1、面向对象:封装、继承、多态
2、JDK、JRE、JVM之前的区别
JDK:java标准包、包含java编译器、java运行环境以及常用的java类库
JRE:java运行环境,用于运行java的字节码文件
JVM:java虚拟机,JRE的一部分,它是整个java实现跨平台的最核心部分,负责运行字节文件
3、hashcode()与equals()之间的关系
两个对象的hashcode不同,两个对象肯定不同
两个对象的hashcode相同,两个对象可能相同,可能不同
两个对象相同,两个对象的hashcode肯定相同
4、==和equals方法的区别
==:如果是基本数据类型,比较值,如果是引用对象,比较的是引用地址
equals:具体要看各个类重写equals方法之后的比较逻辑,方法内部比较的是字符串中各个字符是否全部相等
5、List和Set
List:有序,可重复,可以存储多个null,可以用迭代器(Iterator)获取全部元素,再遍历,也可以get(下标)获取元素
Set:无序,不可重复,只能存在一个null,只能用迭代器(Iterator)获取全部元素,再遍历
6、ArrayList、LinkedList、Vector区别
ArrayList、Vector底层基于数组,LinkedList底层基于链表
ArrayList适用场景:随机查找,LinkedList适用场景:增删改查
ArrayList和LinkedList都实现了List接口,LinkedList还实现了Deque接口,所以LinkedList还可以用来当做队列使用
Vector是线程安全的,使用了synchronized,实现同步要消耗时间,所以速度要慢
7、ConcurrentHashMap扩容机制
JDK1.7 基于Segment分段实现,每个Segment就是一个小型的HashMap
JDK1.8 不在基于Segment分段实现
8、JDK1.7到1.8发生了什么变化
1. 1.7中底层是数组+链表,1.8中底层是数组+链表+红黑树,加入红黑树的目的是提高HashMap插入和查询整体效率
2. 1.8中,只有在链表节点个数大于8个,HashMap长度大于64个的情况下,才会使用红黑树
3. 1.7中链表插入使用的是头插法,1.8中链表插入使用的是尾插法,因为1.8中插入key和value时需要判断链表元素个数,所以需要遍历链表元素个数,所以就正好使用尾插法
4. 1.7中哈希算法复杂,1.8中有了红黑树,所以就简化哈希算法,从而提高效率,节省CPU资源
9、说一下HashMap的Put方法
1. 首先根据传入的Key进行哈希算法与与运算计算出数组下标
2. 如果数组下标的位置元素为空,则将Key和Value封装成Entry对象(1.8封装成Node对象)
3. 如果数组下标的位置元素不为空,则分情况讨论
? 3.1 如果是1.7,先判断是否扩容,若果需要就扩容,如果不需要就将Key和Value封装成Entry对象,并使用头插法插入到链表当中? ? ? ? ? ? ? ?
? 3.2 如果是1.8,当前node节点是链表还是红黑树
? ? a. 如果是红黑树node,则将Key和Value封装成红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value
? ? b. 如果是链表node,则将Key和Value封装成链表节点,并使用尾插法插入到链表的最后位置,因为是尾插法,所以要遍历链表,在遍历的过程当中,会查询当前key是否存在,如果存在则更新value,如果不存在,当遍历完链表后,将新链表插入到链表后,会得到当前链表的节点个数,如果节点个数大于等于8,则会将链表转换为红黑树,
? ? c. 将Key和Value封装成node插入到红黑树或链表中之后,再进行判断是否需要扩容,如果需要就扩容,如果不需要就结束PUT方法
10、深拷贝和浅拷贝
深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型属性,一种是基本数据类型,一种是实例对象引用
1. 浅拷贝就只会拷贝基本数据类型的值,以及实例对象的引用地址
2. 深拷贝既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的属性指向的不是一个对象
11、HashMap的扩容机制原理
1.7版本
1.先生成一个新的数组
2.遍历老数组中每个位置上的链表的每个元素
3.取每个元素的key,并根据新数组的长度,计算出每个元素在新数组中的下标
4.将元素添加到新数组当中
5.所有元素转移完成之后,将新数组赋值给HashMap的table属性
1.8版本
1.先生成一个新的数组
2.遍历老数组中每个位置上的链表和红黑树
3.如果是链表,则计算链表中每个元素的数组下标,然后添加到新数组中去
4.如果是红黑树,则先遍历红黑树,计算每个元素在新数组中下标位置
? 4.1 统计每个位置下的元素个数
? 4.2 如果该位置下的元素大于8,则生成一个新的红黑树,并将根节点添加到新数组的对应位置
? 4.3 如果该位置下的元素不大于8,则生成一个新的链表,并将链表的头节点添加到新数组的对应位置
5.所有元素转移完成之后,将新数组赋值给HashMap的table属性
12、CopyOnWriteArrayList的底层原理是怎样的
因为ArrayList是线程不安全的,所以要有一个线程安全的,因此它出现了
CopyOnWriteArrayList的底层也是通过数组实现的,先进行加锁,然后复制一份新的数组,写的时候往新的数组写,读的时候读老的数组,最后写完之后,将老数组指向新数组,然后清除老数组,适用场景:写少读多
13、什么是字节码,采用字节码的好处是什么?
实现了跨平台,提高了代码的执行性能
14、Java中的异常体系
Java中所有异常都来自于顶级父类Throwable
Throwable下有两个子类Exception和Error
Exception又分为运行时异常和非运行时异常
15、什么是反射机制?
Java反射机制是在Java运行状态时,对任意一个类,都能知道他的方法和属性,对任意一个对象,都能够调用他的方法和属性
16、Java反射机制的优缺点?
优点:运行期类型的判断,动态加载类,提高代码灵活性
缺点:性能慢,通知JVM需要做的事儿,比直接运行Java代码慢多了
17、Java反射机制使用场景?
框架设计的核心,动态代理的核心
18、实现Java反射的方式?
1.通过new对象? new AA()
2.通过路径? Class.forName()
3.通过通过类名? AA.Class.getName()
19、HashMap在多线程情况下,会有什么问题?
HashMap在多线程情况下,JDK1.7版本时,会引起死链问题和对象丢失问题,JDK1.8通过尾插法解决了死链问题,但仍存在对象丢失问题,所以多线程情况下尽量使用HashTable或ConCurrentHashMap线程安全的类
20、ArrayList底层原理
1.ArrayList构造的时候,如果没有传指定容量大小,则会生成一个空数组,如果指定了容量,则构造出对应容量大小的数组
2.在添加元素时,会先判断容量是否足够,默认是10,如果不够则会扩容,扩容按1.5倍扩容,容量足够后,再把元素加到数组中
3.在添加元素时,如果指定了下标,则先检查下标是否越界,然后再判断容量是否足够,不够则扩容,然后再把新元素添加到指定位置,如果该位置后面还有元素,则后移
4.在获取元素时,先检查数组下标是否越界,然后从数组中取出对应位置的元素