JavaBasicProblem

一些容易忘记的Java基础知识点:)

Java基本数据类型

类型名称 关键字 占用内存 取值范围
字节型 byte 1字节 -128~127
短整型 short 2字节 -32768~32767
整型 int 4字节 -2147483648~2147483647
长整型 long 8字节 -9223372036854775808L~9223372036854775807L
单精度浮点型 float 4字节 +/-3.4E+38F(6~7 个有效位)
双精度浮点型 double 8字节 +/-1.8E+308 (15 个有效位)
字符型 char 2字节 ISO 单一字符集
布尔型 boolean 1字节 true 或 false

注:当需要将数值范围较大的数值类型赋给数值范围较小的数值类型变量时,此时可能会丢失精度,即强制类型转换。

public static void main(String[] args) {
int a = 233;
byte b = (byte) a;
System.out.println(b);
}
// 输出:-23

原因:233的二进制为:24位0 + 11101001,而byte只有8位,于是从高位开始舍弃,最后剩下:11101001,由于二进制最高位1表示负数,0表示正数,故其十进制输出为-23。

浏览器输入URL发生了什么

  1. 浏览器查找域名的IP地址(DNS解析,查找过程:浏览器缓存,路由器缓存,DNS缓存);
  2. 建立TCP连接,浏览器向web服务器发送一个HTTP请求;
  3. 服务器处理请求并返回HTTP报文;
  4. 浏览器解析渲染页面;
  5. 连接结束;

不论文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么IO操作还要分为字节流操作和字符流操作呢

字符流是由JVM将字节转换得到的,这个过程比较耗时,并且编码类型不确定会出现乱码问题。所以IO流提供了一个直接操作字符的接口,方便对字符进行流操作。如果是音频文件、图片等媒体文件用字节流比较好,涉及到字符的用字符流比较好。

深拷贝和浅拷贝的区别

浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝;
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝;

Spring AOP的实现

当目标对象实现接口,Spring使用JDK动态代理;没有实现接口时,使用CGLIB实现。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
public DefaultAopProxyFactory() {
}

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}

private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
}
}

https://segmentfault.com/a/1190000015797402

JDK动态代理:利用实现了InvocationHandler接口的拦截器加上反射机制生成一个实现代理接口的匿名类,然后通过invoke进行调用;生成类的速度很快,但运行时因为是基于反射,调用后续的类操作会很慢;

CGLIB动态代理:在内存中动态生成子类对原对象进行代理(直接继承原始类),无法代理final类以及方法;底层基于ASM第三方框架,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理,所以生成类的速度慢,后续执行类的操作快;

强制使用CGLIB实现AOP:

<!-- 在spring配置文件中加入 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

ArrayList和LinkedList

  1. LinkedList

    实现了List接口和Deque接口的双端链表。LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:

    List list=Collections.synchronizedList(new LinkedList(...));

    LinkedList类中的内部私有类Node:

    private static class Node<E> {
    E item;//节点值
    Node<E> next;//后继节点
    Node<E> prev;//前驱节点

    Node(Node<E> prev, E element, Node<E> next) {
    this.item = element;
    this.next = next;
    this.prev = prev;
    }
    }

    这个类就代表双端链表的节点Node。这个类有三个属性,分别是前驱节点,本节点的值,后继结点。

  2. ArrayList

    • 实现了RandomAccess接口,可以随机访问;
    • 实现了Cloneable接口,可以克隆;
    • 实现了Serializable接口,可以序列化、反序列化;
    • 实现了List接口,是List的实现类之一;
    • 实现了Collection接口,是Java Collections Framework成员之一;
    • 实现了Iterable接口,可以使用for-each迭代;

    ArrayList底层使用数组实现存储,查询效率高,增删效率低,线程不安全。如果涉及频繁的增删,可以使用LinkedList,线程安全可以使用Vector。

    通过无参构造方法的方式ArrayList()初始化,则赋值底层数Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {},所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。

    扩容

    private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
    }

    原数组的数据,原封不动的复制到新数组中,再把指向原数组的地址换到新数组。

    public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    this.elementData = EMPTY_ELEMENTDATA;
    } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    }
    }

    注意,ArrayList(int initialCapacity)不会初始化数组大小,因为它基于elementData数组而不是大小。

    ArrayList的遍历性能比LinkedList快很多,因为ArrayList是内存连续的,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销,所以ArrayList遍历性能好。

LinkedHashMap和TreeMap

LinkedHashMap内部维护了一个双向链表,保存了记录的插入顺序,因此可以按照插入的顺序从头部或者尾部迭代;

因为TreeMap实现了SortMap接口,将保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器。当用Iterator遍历TreeMap时,得到的记录是排过序的。TreeMap基于红黑树实现,非线程安全。

TreeMap():构建一个空的映像树;

TreeMap(Map m):构建一个映像树,并且添加映像m中所有元素;

TreeMap(Comparator c):构建一个映像树,并且使用特定的比较器对关键字进行排序;

TreeMap(SortedMap s):构建一个映像树,添加映像树s中所有映射,并且使用与有序映像s相同的比较器排序;

hashcode()和equals()

==:比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

equals():是对两个对象的地址值进行的比较(即比较引用是否相同)。但是String 、Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法,从而进行的是内容的比较。当然,基本类型是进行值的比较。

它的性质有:

  • 自反性(reflexive)。对于任意不为null的引用值x,x.equals(x)一定是true
  • 对称性(symmetric)。对于任意不为null的引用值xy,当且仅当x.equals(y)true时,y.equals(x)也是true
  • 传递性(transitive)。对于任意不为null的引用值xyz,如果x.equals(y)true,同时y.equals(z)true,那么x.equals(z)一定是true
  • 一致性(consistent)。对于任意不为null的引用值xy,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false
  • 对于任意不为null的引用值xx.equals(null)返回false

对于Object类来说,equals()方法在对象上实现的是差别可能性最大的等价关系,即,对于任意非null的引用值xy,当且仅当xy引用的是同一个对象,该方法才会返回true

需要注意的是当equals()方法被override时,hashCode()也要被override。按照一般hashCode()方法的实现来说,相等的对象,它们的hash code一定相等。

hashcode():hashCode()方法给对象返回一个hash code值。

Java对于eqauls方法和hashCode方法是这样规定的:

1.如果两个对象相同,那么它们的hashCode值一定要相同;

2.如果两个对象的hashCode相同,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)。
如不按要求去做了,会发现相同的对象可以出现在Set集合中,同时,增加新元素的效率会大大下降。
3.equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

​ 换句话说,equals()方法不相等的两个对象,hashcode()有可能相等(我的理解是由于哈希码在生成的时候产生冲突造成的)。反过来,hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

equals()被覆盖过,则hashcode()也必须被覆盖;

ConcurrentHashMap如何计算size

使用Redis实现延迟消息队列

zset

为什么String是final的

final关键字的好处:

提高了性能,JVM和Java应用都会缓存final变量;

final变量可以安全的在多线程环境下进行共享,不需要额外的同步开销;

使用final关键字,JVM会对方法,变量及类进行优化;

  • 只有当字符串是不可变的,字符串池才有可能实现,字符串池在运行时节约很多堆空间;
  • 线程安全;
  • 因为字符串不可变,创建时hashcode被缓存,不需要重新计算,使得字符串很适合作为Map的key;

count(*) 和 count(1)和count(列名)区别

count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL;

count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL;

count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计;

执行效率上:

列名为主键,count(列名)会比count(1)快 ;
列名不为主键,count(1)会比count(列名)快;
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*);

如果有主键,则 select count(主键)的执行效率是最优的 ;
如果表只有一个字段,则 select count(*)最优;

Springboot启动流程

@SpringBootApplication包括三个注解,功能如下:

@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置;

@SpringBootConfiguration(内部为@Configuration):被标注的类等于在spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境;

@ComponentScan:组件扫描,可自动发现和装配Bean,默认扫描SpringApplication的run方法里的Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下;

public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调用
//starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//2.创建并配置当前应用将要使用的Environment
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//3.打印banner
Banner printedBanner = printBanner(environment);
//4.根据是否是web项目,来创建不同的ApplicationContext容器
context = createApplicationContext();
//5.创建一系列FailureAnalyzer
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//6.初始化ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//7.调用ApplicationContext的refresh()方法,刷新容器
refreshContext(context);
//8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}

1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动了。(SpringApplicationRunListeners其本质上就是一个事件发布者,它在SpringBoot应用启动的不同时间点发布不同应用事件类型(ApplicationEvent),如果有哪些事件监听者(ApplicationListener)对这些事件感兴趣,则可以接收并且处理)
看下SpringApplicationRunListeners源码:

public interface SpringApplicationRunListener {

// 运行run方法时立即调用此方法,可以用户非常早期的初始化工作
void starting();

// Environment准备好后,并且ApplicationContext创建之前调用
void environmentPrepared(ConfigurableEnvironment environment);

// ApplicationContext创建好后立即调用
void contextPrepared(ConfigurableApplicationContext context);

// ApplicationContext加载完成,在refresh之前调用
void contextLoaded(ConfigurableApplicationContext context);

// 当run方法结束之前调用
void finished(ConfigurableApplicationContext context, Throwable exception);

}

SpringApplicationRunListener只有一个实现类:EventPublishingRunListener。①处的代码只会获取到一个EventPublishingRunListener的实例,我们来看看starting()方法的内容:

public void starting() {
// 发布一个ApplicationStartedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

2.创建并配置当前应用将要使用的Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties),不同的环境(eg:生产环境、预发布环境)可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。因此,当Environment准备好后,在整个应用的任何时候,都可以从Environment中获取资源。

  • 判断Environment是否存在,不存在就创建(如果是web项目就创建StandardServletEnvironment,否则创建StandardEnvironment)
  • 配置Environment:配置profile以及properties
  • 调用SpringApplicationRunListener的environmentPrepared()方法,通知事件监听者:应用的Environment已经准备好

3.打印banner(可以自定义)
4.根据是否是web项目,来创建不同的ApplicationContext容器
5.创建一系列FailureAnalyzer,创建流程依然是通过SpringFactoriesLoader获取到所有实现FailureAnalyzer接口的class,然后在创建对应的实例。FailureAnalyzer用于分析故障并提供相关诊断信息。
6.初始化ApplicationContext

  • 将准备好的Environment设置给ApplicationContext
  • 遍历调用所有的ApplicationContextInitializer的initialize()方法来对已经创建好的ApplicationContext进行进一步的处理
  • 调用SpringApplicationRunListener的contextPrepared()方法,通知所有的监听者:ApplicationContext已经准备完毕
  • 将所有的bean加载到容器中
  • 调用SpringApplicationRunListener的contextLoaded()方法,通知所有的监听者:ApplicationContext已经装载完毕

7.调用ApplicationContext的refresh()方法,刷新容器

  • 这里的刷新和spring中刷新原理类似,这里重点关注invokeBeanFactoryPostProcessors(beanFactory);方法,主要完成获取到所有的BeanFactoryPostProcessor来对容器做一些额外的操作,通过源可以进入到PostProcessorRegistrationDelegate类
    的invokeBeanFactoryPostProcessors()方法,会获取类型为BeanDefinitionRegistryPostProcessor的beanorg.springframework.context.annotation.internalConfigurationAnnotationProcessor,对应的Class为ConfigurationClassPostProcessor。ConfigurationClassPostProcessor用于解析处理各种注解,包括:@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean。当处理@import注解的时候,就会调用<自动配置>这一小节中的EnableAutoConfigurationImportSelector.selectImports()来完成自动配置功能

8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。

抽象类和接口

接口:

因为java不支持多重继承,所以有了接口,一个类只能继承一个父类,但可以实现多个接口,接口本身也可以继承多个接口。

接口里面的成员变量默认都是public static final类型的,必须被显示的初始化;接口里面的方法默认都是public abstract类型的,隐式声明;

接口没有构造方法,不能被实例化;

类如果实现了一个接口,那么必须实现接口里面的所有抽象方法,否则类要被定义为抽象类;

抽象类:

如果将一个类声明为abstract,此类不能生成对象,只能被继承使用;

抽象方法必须存在于抽象类中,抽象类中可以有一般的变量和一般的方法;

子类继承抽象类必须实现其中抽象方法,除非子类为抽象类;
private void print(){};此语句表示方法的空实现。
abstract void print(); 此语句表示方法的抽象,无实现。

区别:

接口只能包含抽象方法,抽象类可以包含普通方法;

接口只能定义静态常量属性,抽象类既可以定义普通属性,也可以定义静态常量属性;

接口不包含构造方法,抽象类里可以包含构造方法;

抽象类中可以包含静态方法,接口中不能包含静态方法;

CAP

一个分布式系统不可能同时满足一致性( C:Consistency ),可用性( A: Availability )和分区容错性( P:Partition tolerance )这三个基本需求,最多只能同时满足其中的 2 个

如下:

选项 描述
C(Consistence) 一致性,指数据在多个副本之间能够保持一致的特性(严格的一致性)。
A(Availability) 可用性,指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据。
P(Network partitioning) 分区容错性,分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。

分区:一个分布式系统,网络不通讯,导致连接不通,系统被分割成几个数据区域。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性+分区 的系统,通常性能不是特别高。
AP - 满足可用性+分区 的系统,通常可能对一致性要求低一些。

notify()和notifyAll()区别

notify()只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

notifyAll 会唤醒所有等待(对象的)线程,哪一个线程将会第一个处理取决于操作系统的实现。

如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。

sleep()和wait()区别

  • sleep() 是 Thread 类的静态本地方法;wait() 是Object类的成员本地方法
  • sleep() 方法可以在任何地方使用;wait() 方法则只能在同步方法或同步代码块中使用,否则抛出异常Exception in thread “Thread-0” java.lang.IllegalMonitorStateException
  • sleep() 会休眠当前线程指定时间,释放 CPU 资源,不释放对象锁,休眠时间到自动苏醒继续执行;wait() 方法放弃持有的对象锁,进入等待队列,当该对象被调用 notify() / notifyAll() 方法后才有机会竞争获取对象锁,进入运行状态
  • JDK1.8 sleep() wait() 均需要捕获 InterruptedException 异常

Thread类中interrupt()、interrupted()和isInterrupted()方法详解

interrupt():其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。

interrupted():作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。

isInterrupted():作用是只测试此线程是否被中断 ,不清除中断状态。

@Transactional实现原理

OOP的理解

OOP是面向对象编程,特征是封装、继承、多态、抽象。
封装:是指将对象信息状态通过访问权限修饰符隐藏在对象内部,不允许外部程序直接访问,如果外部程序要访问对象内部,可以调用内部提供的get或set方法。简单来说,封装就是要找出某一类事务的公性然后提取出来。
继承:子类继承了父类所有的成员方法和属性,并且可以拥有自己特性。继承解决了代码的重用问题;
多态:多态存在的三个条件1.继承2.重写3.父类引用指向子类对象
多态的实现方式:接口实现,继承父类方法重写,同一个类中进行重载;
重载:多个同名函数同时存在,具有不同的参数个数/类型,返回值类型可以相同可以不同,调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性,存在于父类和子类、同类中;
重写:

​ 1.参数列表必须完全与被重写的方法相同;
​ 2.返回的类型必须一直与被重写的方法的返回类型相同;
​ 3.访问修饰符的限制一定要大于被重写方法的访问修饰符;
​ 4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常;
​ 5.存在于父类和子类之间,方法被定义为final不能被重写;
抽象:如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。

集合的基类

子类继承父类的初始化顺序

  1. 父类的静态成员变量
  2. 父类的静态代码块
  3. 子类的静态成员变量
  4. 子类的静态代码块
  5. 父类的成员变量
  6. 父类的代码块
  7. 父类的构造函数
  8. 子类的成员变量
  9. 子类的代码块
  10. 子类的构造函数

HTTP和TCP的区别和联系

1、TCP连接

手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。

建立起一个TCP连接需要经过“三次握手”:

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连 接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写 了,就是服务器和客户端交互,最终确定断开)
2、HTTP连接

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。

2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的 做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客 户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

二者的区别和联系:

TCP是底层通讯协议,定义的是数据传输和连接方式的规范;

HTTP是应用层协议,定义的是传输数据的内容的规范;

TCP是传输层,而http是应用层;

HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP也就一定支持TCP;

二叉树 平衡二叉树 红黑树的区别

二叉查找/搜索/排序树 BST (binary search/sort tree)
(1)若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;
(2)若它的右子树上所有结点的值均大于它的根节点的值;
(3)它的左、右子树也分别为二叉排序树。

注意:对二叉查找树进行中序遍历,得到有序集合。

平衡二叉树(Self-balancing binary search tree) 自平衡二叉查找树,又被称为AVL树:
它是一 棵空树或它的左右两个子树的高度差(平衡因子)的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,同时,平衡二叉树必定是二叉搜索树,反之则不一定。

红黑树
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种平衡二叉树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

对象相等与指向他们的引用相等 的区别

对象的相等,比的是内存中存放的内容是否相等;

引用相等,比较的是他们指向的内存地址是否相等;

子类构造方法之前会先调用父类无参构造方法

在子类构造器的第一行会隐式的调用 super(),即调用父类的构造器,如果父类中没有定义空参的构造器,则必须在子类的构造器的第一行显示的调用super(参数),以调用父类中构造器;

帮助子类做初始化工作

死锁

四个条件:

互斥条件:该资源任意一个时刻只由一个线程占用;

请求与保持条件

不剥夺条件

循环等待条件

Servlet生命周期

Servlet的生命周期一般可以用三个方法来表示:

  1. init():仅执行一次,负责在装载Servlet时初始化Servlet对象
  2. service() :核心方法,一般HttpServlet中会有get,post两种处理方式。在调用doGet和doPost方法时会构造servletRequest和servletResponse请求和响应对象作为参数。
  3. destory():在停止并且卸载Servlet时执行,负责释放资源

初始化阶段:Servlet启动,会读取配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,将ServletConfig作为参数来调用init()方法。

接口与抽象类区别

A.抽象类可以有非抽象的方法,而接口中的方法都是抽象方法

B.java中类只能单继承,接口可以‘继承’多个接口

C.抽象类必须有构造方法,接口一定没有构造方法

D.实例化一般指new一个对象,所以抽象类不能实例化

关于抽象类

JDK 1.8以前,抽象类的方法默认访问权限为protected

JDK 1.8时,抽象类的方法默认访问权限变为default

关于接口

JDK 1.8以前,接口中的方法必须是public的

JDK 1.8时,接口中的方法可以是public的,也可以是default的

JDK 1.9时,接口中的方法可以是private的

CountDownLatch 和 CyclicBarrier

CountDownLatch 是等待一组线程执行完,才执行后面的代码。此时这组线程已经执行完。
CyclicBarrier 是等待一组线程至某个状态后再同时全部继续执行线程。此时这组线程还未执行完。

CountDownLatch 允许一个线程或多个线程等待特定情况,同步完成线程中其他任务。举例:百米赛跑,就绪运动员等待发令枪发动才能起步。
CyclicBarrier 和CountDownLatch一样都可以协同多个线程,让指定数量的线程等待期他所有的线程都满足某些条件之后才继续执行。举例:排队上摩天轮时,每到齐四个人,就可以上同一个车厢。

如果finally块中有return语句的话,它将覆盖掉函数中其他return语句。

interface中的方法默认为public abstract 的 ,变量默认为public static final

成员变量有初始值,而局部变量没有初始值得。变量没有初始值就使用了,编译通不过;

实际上这道题考查的是两同两小一大原则:

方法名相同,参数类型相同

子类返回类型小于等于父类方法返回类型,
子类抛出异常小于等于父类方法抛出异常,
子类访问权限大于等于父类方法访问权限。

Socket套接字

就是源Ip地址,目标IP地址,源端口号和目标端口号的组合

服务器端:ServerSocket提供的实例

ServerSocket server= new ServerSocket(端口号)

客户端:Socket提供的实例

Socket soc=new Socket(ip地址,端口号)

异常

异常是指程序运行时(非编译)所发生的非正常情况或错误,当程序违反了语音规则,jvm就会将出现的错误表示一个异常抛出。

异常也是java 的对象,定义了基类 java。lang。throwable作为异常父类。 这些异常类又包括error和exception。两大类

error类异常主要是运行时逻辑错误导致,一个正确程序中是不应该出现error的。当出现error一般jvm会终止。

exception表示可恢复异常,包括检查异常和运行时异常。 检查异常是最常见异常比如 io异常sql异常,都发生在编译阶段。这类通过try、catch捕捉

而运行时异常,编译器没有强制对其进行捕捉和处理。一般都会把异常向上抛出,直到遇到处理代码位置,若没有处理块就会抛到最上层,多线程用thread。run()抛出,单线程用main()抛出。常见的运行异常包括 空指针异常 类型转换异常 数组月结异常 数组存储异常 缓冲区溢出异常 算术异常等。

Java异常

  1. 粉红色的是受检查的异常(checked exceptions),其必须被 try{}catch语句块所捕获,或者在方法签名里通过throws子句声明.受检查的异常必须在编译时被捕捉处理,命名为 Checked Exception 是因为Java编译器要进行检查,Java虚拟机也要进行检查,以确保这个规则得到遵守.

  2. 绿色的异常是运行时异常(runtime exceptions),需要程序员自己分析代码决定是否捕获和处理,比如 空指针,被0除…

  3. 而声明为Error的,则属于严重错误,如系统崩溃、虚拟机错误、动态链接失败等,这些错误无法恢复或者不可能捕捉,将导致应用程序中断,Error不需要捕捉。

Java中的位运算符:

>>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;

>>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。

内部类

(1)把类定义在另一个类的内部,该类就被称为内部类,举例:把类B定义在类A中,类B就被称为内部类。
(2)内部类的访问规则
可以直接访问外部类的成员,包括私有;
外部类要想访问内部类成员,必须创建对象;
(3)内部类的分类
成员内部类;局部内部类;匿名内部类

(4)成员内部类访问规则
成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
成员内部类是静态的:

外部类名.内部类名 对象名 = new 外部类名.内部类名();

(5)局部内部类
局部内部类访问局部变量必须加final修饰,因为局部变量使用完毕就消失,而堆内存的数据并不会立即消失,所以,堆内存还是用该变量,而改变量已经没有了,为了让该值还存在,就加final修饰。
通过反编译工具我们看到了,加入final后,堆内存直接存储的是值,而不是变量名。
(6)匿名内部类(掌握)
是局部内部类的简化形式
前提:存在一个类或者接口
格式:
new 类名或者接口名() {
重写方法;
}
本质:其实是继承该类或者实现接口的子类匿名对象

对于外部类来说,只有两种修饰,public和默认(default),因为外部类放在包中,只有两种可能,包可见和包不可见。

对于内部类来说,可以有所有的修饰,因为内部类放在外部类中,与成员变量的地位一致,所以有四种可能。

Math类

Math类中提供了三个与取整有关的方法:ceil,floor,round,这些方法的作用于它们的英文名称的含义相对应,例如:

ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.6)的结果为-11;

floor的英文是地板,该方法就表示向下取整,Math.floor(11.6)的结果是11,Math.floor(-11.4)的结果-12;

最难掌握的是round方法,他表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果是12,Math.round(-11.5)的结果为-11.

枚举类

枚举类 所有的枚举值都是类静态常量,在初始化时会对所有的枚举值对象进行第一次初始化。

重载就是一句话:同名不同参,返回值无关。

覆盖/重写:同名同参

hibernate

1、什么是延迟加载?
定义:延迟加载(lazy load)是Hibernate3 关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的。就是只有当真正需要数据时,才真正的执行数据加载操作。延迟加载是 hibernate 中用于提高查询效率的一种措施,它的对立面是 立即加载。
2、如何实现延迟加载?
Hibernate 2 实现延迟加载有 2 种方式:
实体对象
集合
Hibernate 3 又引入了一种新的加载方式:属性的延迟加载
一般使用load()的方法来实现延迟加载:
当调用load方法加载对象时,返回***对象,等到真正用到对象的内容时才发出sql语句
3、Hibernate 其他介绍
Hibernate 使用 Java 反射机制,而不是字节码增强程序来实现透明性
Hibernate 的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
4、优化 Hibernate 所鼓励的 7 大措施
1.尽量使用多对一,避免使用单项一对多
2.灵活使用单向一对多
3.不用一对一,使用多对一代替一对一
4.配置对象缓存,不使用集合缓存
5.一对多使用Bag ,多对一使用Set
6.继承使用显示多态 HQL:from object polymorphism=”exlicit” 避免查处所有对象
7.消除大表,使用二级缓存

Author: Jiayi Yang
Link: https://jiayiy.github.io/2020/04/24/JavaBasicProblem/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.