目录
1. 拆箱与装箱
2.Map类
3.面向对象
3.1 hashCode()和equals()方法有什么联系?
3.2说明Query接口的list方法和iterate方法有什么区别?
3.3 面向对象的"六原则一法则"
3.4 讲讲abstract class和interface有什么区别?
3.5 请说明一下final, finally, finalize的区别
3.6 请说明Comparable和Comparator接口的作用以及它们的区别
3.7 Java泛型中extends和super的区别?
3.7 请说明Collection 和 Collections的区别。
3.8 List、Set、Map是否继承自Collection接口?
3.8.1 TreeMap的原理
3.8.x1 ArrayList是否会越界:
3.8.x concurrenthashmap有什么优势?
3.9 请你说说Iterator和ListIterator的区别?
3.10 为什么集合类没有实现Cloneable和Serializable接口?
4 线程与锁
4.1 如何保证线程安全?
4.2 简要说明一下线程的基本状态以及状态之间的关系
4.3 线程池的原理及实现
4.4 简述一下线程的sleep()方法和yield()方法有什么区别?
4.5 java中有几种方法可以实现一个线程? 第二个问题:用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用,请说明原因?
4.6 多线程和同步有几种实现方法,并且这些实现方法具体内容都是什么?
4.7 请说明一下sleep() 和 wait() 有什么区别?
4.8 请你说明一下在监视器(Monitor)内部,是如何做到线程同步的?在程序又应该做哪种级别的同步呢?
4.9 请分析一下同步方法和同步代码块的区别是什么?
4.10 请详细描述一下线程从创建到死亡的几种状态都有哪些?
4.11 请列举一下启动线程有哪几种方式,之后再说明一下线程池的种类都有哪些?
4.12 简要说明一下JAVA中cyclicbarrier和countdownlatch的区别分别是什么?
5 锁机制
5.1 AtomicLong与LongAdder(jdk1.8新增)
6 反射
JAVA中反射的实现过程和作用?
7. JVM(java虚拟机)
8. GC(垃圾回收机制)
8.1 在JAVA虚拟机中,哪些对象可作为 GC ROOT对象?、
很容易错,考的很细节
拆箱,装箱详解
HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL。
HashMap不支持线程同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致性。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
LinkedHashMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。ap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
➀相等(相同)的对象必须具有相等的哈希码(或者散列码)。
➁如果两个对象的hashCode相同,它们并不一定相同。
①list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的前提下使用查询缓存;iterate()方法可以充分利用缓存,如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销。 ② list()方法不会引起N+1查询问题,而iterate()方法可能引起N+1查询问题
- 单一职责原则:一个类只做它该做的事情。("高内聚、低耦合"
- 开闭原则:软件实体应当对扩展开放,对修改关闭。(①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性)
- 依赖倒转原则:面向接口编程。
-里氏替换原则:任何时候都可以用子类型替换掉父类型。(简单的说就是能用父类型的地方就一定能使用子类型,即子类一定是增加父类的能力而不是减少父类的能力)
- 接口隔离原则:接口要小而专,绝不能大而全
- 合成聚合复用原则:优先使用聚合或合成关系复用代码。(合成聚合复用原则想表达的是优先考虑Has-A关系而不是Is-A关系复用代码)
- 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。
思路是先从总体解释抽象类和接口的基本概念,然后再比较两者的语法细节,最后再说两者的应用区别。比较两者语法细节区别的条理是:先从一个类中的构造方法、普通成员变量和方法(包括抽象方法),静态变量和方法,继承性等6个方面逐一去比较回答,接着从第三者继承的角度的回答,特别是最后用了一个典型的例子来展现自己深厚的技术功底。
抽象类和接口区别(JDK8以后接口中可以定义静态方法)
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 finally是异常处理语句结构的一部分,表示总是执行。 finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源 回收,例如关闭文件等
- Comparable 是排序接口,是内部比较器;Comparator 是比较器接口,是外部比较器。
-Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
-Comparable通过 重写x.compareTo(y) 来“比较x和y的大小”。
而Comparator通过重写int compare(T o1, T o2)来比较大小。
extend与super
(1)
上界<? extend Fruit>
下界<? super Apple>
(2)上界和下界的特点
上界的list只能get,不能add(确切地说不能add出除null之外的对象,包括Object)
下界的list只能add,不能get
(3)
PECS(Producer Extends Consumer Super)原则,已经很好理解了:
频繁往外读取内容的,适合用上界Extends。经常往里插入的,适合用下界Super。
Collection是集合类的上级接口,继承与他的接口主要有Set 和List. Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
List存储数据结构是数组或链表,Set和Map是哈希存储和排序树
List、Set 是,Map 不是。Map是键值对映射容器。
Java--集合大全List,Set,Map超详细讲解
Collection 框架
红黑树的原理
3.8.2 TreeMap的底层实现
ArrayList是实现了基于动态数组的数据结构;ArrayList并发add()可能出现数组下标越界异常。
concurrenthashmap是线程安全的,JDK1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据或则删除数据时,会通过addCount()方法更新baseCount,通过累加baseCount和CounterCell数组中的数量,即可得到元素的总个数;
3.8.x HashMap的容量为什么是2的n次幂?
负载因子默认是0.75, 2^n是为了让散列更加均匀,例如出现极端情况都散列在数组中的一个下标,那么hashmap会由O(1)复杂退化为O(n)的。
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。 Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。 ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
克隆:克隆是把一个对象里面的属性值,复制给另一个对象,而不是对象引用的复制。
串行化/序列化:
1.将对象的状态保存在存储媒体中一边可以在以后重写创建出完全相同的副本
2.按值将对象从一个应用程序域法相另一个应用程序域
理由:
1. 集合类接口Collection,List,Set,Map定义了自己集合类的抽象即可,如果接口的设计也要考虑是否可以克隆,串行化等一堆额外特性,那是不是还要额外考虑是否可以Closeable, 接口就不是基于抽象,不是纯粹的接口了。
2.于具体的实现类, java.util.ArrayList,LinkedList,HashMap,HashSet, 有什么特性就实现什么接口,可以实现多个接口即可。实际这些类都实现了Cloneable和Serializable接口,因为实际应用中集合类很常用,串行化和克隆也常用。
Java保证线程安全
一、线程安全在三个方面体现
1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
2.可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)
Running表示运行状态,Runnable表示就绪状态(万事俱备,只欠CPU),Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了sleep()方法或join()方法等待休眠或其他线程结束,或是因为发生了I/O中断。
线程池
①sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态; ③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; ④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
--6种方法实现一个线程:实现线程的方法
1)继承Thread类创建线程 2)实现Runnable接口创建线程 3)使用Callable和FutureTask创建线程 4)使用线程池,例如用Executor框架 5)Spring实现多线程(底层是线程池) 6 )定时器Timer (底层封装了一个TimerThread对象)
--用synchronized关键字修饰同步方法
--反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
多线程有两种实现方法。一种是继承Thread类,一种是实现Runnable接口! 同步有两种方法。一种同步方法,一种同步代码!分别是synchronized,wait与notify
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep会释放CPU资源,不会释放对象锁。wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。调用wait方法会释放CPU资源以及对象锁
在Java虚拟机中,每个对象(object和class)通过某种逻辑关联监视器,每个监视器和一个对象引用相关联,为了实现监视器的互斥功能,每个对象都关联着一把锁。
一旦方法或者代码块被synchronized修饰,那么这个部分就放入了监视器的监视区域,确保一次只有一个线程执行该部分代码,线程在获取锁之前,不允许执行该部分代码。
Java提供了显示监视器(Lock)和隐式监视器(synchronized)两种锁方案。
同步方法与同步代码块
线程的5种状态
启动线程有哪几种方式,之后再说明一下线程池的种类都有哪些
countdownlatch:闭锁
cyclicbarrier:CyclicBarrier使用与原理
两者区别
AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于AtomicLong,LongAdder有着更高的性能和更好的表现,可以完全替代AtomicLong的来进行原子操作。 在32位操作系统中,64位的long 和 double 变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。 AtomicLong的实现方式是内部有个value 变量,当多线程并发自增,自减时,均通过cas 指令从机器指令级别操作保证并发的原子性。 唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高, 重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低。 那怎么解决? LongAdder给了我们一个非常容易想到的解决方案: 减少并发,将单一value的更新压力分担到多个value中去,降低单个value的 “热度”,分段更新!!! 这样,线程数再多也会分担到多个value上去更新,只需要增加value就可以降低 value的 “热度” AtomicLong中的 恶性循环不就解决了吗? cells 就是这个 “段” cell中的value 就是存放更新值的, 这样,当我需要总数时,把cells 中的value都累加一下不就可以了么!! 当然,聪明之处远远不仅仅这里,在看看add方法中的代码,casBase方法可不可以不要,直接分段更新,上来就计算 索引位置,然后更新value? 答案是不好,不是不行,因为,casBase操作等价于AtomicLong中的cas操作,要知道,LongAdder这样的处理方式是有坏处的,分段操作必然带来空间上的浪费,可以空间换时间,但是,能不换就不换,看空间时间都节约~! 所以,casBase操作保证了在低并发时,不会立即进入分支做分段更新操作,因为低并发时,casBase操作基本都会成功,只有并发高到一定程度了,才会进入分支,所以,Doug Lead对该类的说明是: 低并发时LongAdder和AtomicLong性能差不多,高并发时LongAdder更高效! 但是,Doung Lea 还是没这么简单,聪明之处还没有结束…… 如此,retryUpdate中做了什么事,也基本略知一二了,因为cell中的value都更新失败(说明该索引到这个cell的线程也很多,并发也很高时) 或者cells数组为空时才会调用retryUpdate, 因此,retryUpdate里面应该会做两件事: 1. 扩容,将cells数组扩大,降低每个cell的并发量,同样,这也意味着cells数组的rehash动作。 2. 给空的cells变量赋一个新的Cell数组。 LongAdder确实用了很多心思减少并发量,并且,每一步都是在”没有更好的办法“的时候才会选择更大开销的操作,从而尽可能的用最最简单的办法去完成操作。追求简单,但是绝对不粗暴。 AtomicLong可不可以废掉? 我的想法是可以废掉了,因为,虽然LongAdder在空间上占用略大,但是,它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上,AtomicLong基本没有优势了。
1. JAVA反射机制原理及用途
2.简要说明java反射机制实现过程
java内存模型与线程:java内存模型与线程
原子性:原子操作,锁或synchronized来保证
可见性volatile:缓存一致性问题,synchronized 和 final 能保证可见性
顺序性:指令重排,volatile synchronized能保证有序性
a. java虚拟机栈中的引用的对象。
b.方法区中的类静态属性引用的对象。 (一般指被static修饰的对象,加载类的时候就加载到内存中。)
c.方法区中的常量引用的对象。
d.本地方法栈中的JNI(native方法)引用的对象
参考:JVM中的GC对象
深入理解 JVM 垃圾回收机制及其实现原理