final, finally, finalize 的区别
1. 在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。
2. finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。
3. finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
int和Interger的区别
1. a == b 吗? 不相等。两个new出来的对象地址不一样。
2. c == d 吗? 都是基本数据类型的值肯定相等。
e == f 吗? g == h 吗?
答案是:e == f; g != h。为什么会出现这种情况?因为ava在进行编译时 Integer g = 130会被编译成 Integer.valueOf(130) ,这个可以通过反编译class文件看到。而通过Integer源码可以得出,Integer.valueOf() 方法会在数值-128~127之间会对Integer进行缓存,不会再重新new一个,所以 e==f ;当数值二大于127或者小于-128的时候则会重新new一个,所以g != h 。
3. c == e 吗, i == j 吗?
答案都是相等的。因为封装类和基本数据类型进行比较的时候,java会自动拆箱,然后比较数值是否相等。
重写与重载的区别
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
抽象类和接口有什么区别
1. 抽象类要被子类继承,接口要被类实现;
2. 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现;
3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量;
4. 接口是设计的结果,抽象类是重构的结果;
5. 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高;
6. 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量;
7. 抽象类主要用来抽象类别,接口主要用来抽象功能;
说说反射的用途
1. 在运行时判断任意一个对象所属的类
2. 在运行时构造任意一个类的对象
3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射设置可以调用 private)
4. 在运行时调用一个对象的方法
说说自定义注解的场景及实现
登陆、权限拦截、日志处理,以及各种 Java 框架,如 Spring,Hibernate,JUnit 提到注解就不能不说反射,Java 自定义注解是通过运行时靠反射获取注解。
session 分布式处理
1. Session Replication 方式管理 (即session复制)
简介:将一台机器上的Session数据广播复制到集群中其余机器上
使用场景:机器较少,网络流量较小
优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问
缺点:广播式复制到其余机器有一定廷时,带来一定网络开销
2. Session Sticky 方式管理
简介:即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
使用场景:机器数适中、对稳定性要求不是非常苛刻
优点:实现简单、配置方便、没有额外网络开销
缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障
3. 缓存集中式管理
简介:将Session存入分布式缓存集群中的某台机器上,当用户访问不同节点时先从缓存中拿Session信息使用场景:集群中机器数多、网络环境复杂
优点:可靠性好
缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入
JDBC 流程
1. 注冊驱动 (仅仅做一次)
2. 建立连接(Connection)
3. 创建运行SQL的语句(Statement)
4. 运行语句
5. 处理运行结果(ResultSet)
6. 释放资源
List 和 Set 区别
1. list方法可以允许重复的对象,而set方法不允许重复对象
2. list可以插入多个null元素,而set只允许插入一个null元素
3. list是一个有序的容器,保持了每个元素的插入顺序。即输出顺序就是输入顺序,而set方法是无序容器,无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序
Arraylist 与 LinkedList 区别
1. ArrayList是依靠数组来存放对象的;
2. LinkedList可以看做为一个双向链表,所有的操作都可以认为是一个双向链表的操作。并不是用普通的数组来存放数据的,而是使用结点来存放数据的,有一个指向链表头的结点first和一个指向链表尾的结点last;
3. 不同于ArrayList只能在数组末尾添加数据,LinkList可以很方便在链表头或者链表尾插入数据,或者在指定结点前后插入数据;
4. 对于数据频繁出入的情况下,并且要求操作要足够灵活,建议使用LinkedList;对于数组变动不大,主要是用来查询的情况下,可以使用ArrayList。
ArrayList 与 Vector 区别
1. Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;
2. ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
3. Vector可以设置capacityIncrement,而ArrayList不可以,从字面理解就是capacity容量,Increment增加,容量增长的参数。
HashMap 和 Hashtable 的区别
1. Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口;
2. Hashtable是线程安全的,而HashMap非线程安全;
3. HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey。Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同;
4. Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null;
5. Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 ;
6. HashTable直接使用对象的hashCode。而HashMap重新计算hash值;
7. HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
HashSet 和 HashMap 区别
HashMap 和 ConcurrentHashMap 的区别
1. ConcurrentHashMap对整个桶数组进行了分段,而HashMap则有;
2. ConcurrentHashMap在每一个分段上都用锁进行保护,从而让锁的粒度更精细一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
HashMap 的工作原理
1. 调用hashCode计算hash从而得到bucket位置;
2. HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍);
3. 如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
ConcurrentHashMap 的工作原理
1. ConcurrentHashMap是Java1.5中引用的一个线程安全的支持高并发的HashMap集合类。
2. ConcurrentHashMap采用了非常精妙的"分段锁"策略(将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。),ConcurrentHashMap的主干是个Segment数组。Segment继承了ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在ConcurrentHashMap,一个Segment就是一个子哈希表,Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的;
sleep() 、join()、yield()有什么区别
1. sleep() 方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是 sleep() 方法不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据;
2. wait() 方法需要和 notify() 及 notifyAll() 两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在 synchronized 语句块内使用,也就是说,调用 wait(),notify() 和 notifyAll() 的任务在调用这些方法前必须拥有对象的锁。注意,它们都是 Object 类的方法,而不是 Thread 类的方法。wait() 方法与 sleep() 方法的不同之处在于,wait() 方法会释放对象的“锁标志”。当调用某一对象的 wait() 方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了 notify() 方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的 notifyAll() 方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。除了使用 notify() 和 notifyAll() 方法,还可以使用带毫秒参数的 wait(long timeout) 方法,效果是在延迟 timeout 毫秒后,被暂停的线程将被恢复到锁标志等待池;
3. yield() 方法和 sleep() 方法类似,也不会释放“锁标志”,区别在于,它没有参数,即 yield() 方法只是使当前线程重新回到可执行状态,所以执行 yield() 的线程有可能在进入到可执行状态后马上又被执行,另外 yield() 方法只能使同优先级或者高优先级的线程得到执行机会,这也和 sleep() 方法不同;
4. join() 方法会使当前线程等待调用 join() 方法的线程结束后才能继续执行。
CountDownLatch
1. 解释一下CountDownLatch概念
a) CountDownLatch是在jdk1.5引入的,在java.util.concurrent包下,允许一个或多个线程等待直到其他线程完成操作;
b) CountDownLatch通过计数器来实现,初始值为线程的数量,每执行完一个线程 计数器减1,直到变成0,调用await方法的线程才能继续执行。
2. 给出一些CountDownLatch使用的例子
a) 核心服务检测
b) 模拟高并发
c) zook连接
3. CountDownLatch 类中主要的方法
a) await 当前线程等待直到计数器为0
b) countDown 计数器减1
CyclicBarrier原理
1. 线程达到屏障点时被阻塞,直到最后一个线程达到屏障点,屏障才会打开;
2. 构造方法为CyclicBarrier(int parties, Runnable barrierAction),barrierAction非必需,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动barrier时执行给定的屏障操作,该操作由最后一个进入barrier的线程执行;
3. 调用await()方法使得线程进入等待状态,直到最后一个线程调用await()方法时,才会启动barrier;
4. 调用await(long timeout, TimeUnit unit)方法也可以使得线程进入等待状态,当等待超时后,该线程调用breakBarrier()方法终止CyclicBarrier,抛出TimeoutException 异常,而所有处于等待状态的线程将会抛出BrokenBarrierException 异常,其他线程调用await()方法时,也会抛出BrokenBarrierException异常,另外,这种情况下,最后一个线程也不会执行barrierAction。
Semaphore原理
1. Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理的使用公共资源;
2. 线程可以通过acquire()方法来获取信号量的许可,当信号量中没有可用的许可的时候,线程阻塞,直到有可用的许可为止。线程可以通过release()方法释放它持有的信号量的许可;
3. "公平信号量"和"非公平信号量"的释放信号量的机制是一样的!不同的是它们获取信号量的机制:线程在尝试获取信号量许可时,对于公平信号量而言,如果当前线程不在CLH队列的头部,则排队等候;而对于非公平信号量而言,无论当前线程是不是在CLH队列的头部,它都会直接获取信号量。该差异具体的体现在,它们的tryAcquireShared()函数的实现不同。
Exchanger 原理
1. Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。因此使用Exchanger的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的;
2. Exchanger类提供了两个方法,String exchange(V x):用于交换,启动交换并等待另一个线程调用exchange;String exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange,并且设置最大等待时间,当等待时间超过timeout便停止等待。
ThreadLocal实现原理与使用场景
1. 实现原理:
每一个Thread对象维护一个ThreadLocalMap,它以ThreadLoal作为key值,Object作为value值。当调用ThreadLocal对象的set方法来设置值时,ThreadLocal先获取到当前Thread对象,然后获取到该Thread对象的ThreadLocalMap对象,通过ThreadLocalMap对象的set方法,以ThreadLocal对象作为key值,ThreadLocal对象set方法传入的Object对象作为value值,将数据加入到ThreadLocalMap对象中。
2. 使用场景:
a) 每个线程需要有自己单独的实例
b) 实例需要在多个方法中共享,但不希望被多线程共享