java面试题
1、hashmap实现原理?
1.7的底层数据结构是数组+链表。1.8之后是数组+链表+红黑树。保存的数据类型为key-value对。允许key和value的值为null。
hashmap的存储实现:
①对key求hash值,计算所处底层数组位置
②若key的hash值不相同,直接存入桶中。
③若key的hash值相同,以链表的形式连接到后面
④当链表的长度超过8,那么数据结构转换为红黑树。
⑤当元素数量超过容量*加载因子,会进行自动扩容机制,resize
扩容机制:
容量的初始值为16,指的是底层的entry数组大小,扩容因子默认为0.75。hashmap是通过key的hashcode值的高16位和低16位异或后和桶的数量取模后取得索引位置。因为每次的长度是之前的两倍,那么重新计算出来的索引只有两种情况,一种索引不变,一种索引+oldcap。
2、hashcode与equals
hashcode的返回值是一个int值。当hashcode相同时,equals可能不相同。equals相同时,hashcode一定相同。
3、hashmap和hashtable
不同点:hashmap的key和value都可以是null。hashtable的key和value都不可以是null。hashmap线程不安全,hashtable线程安全。
相同点:hashmap和hashtable中的元素key相同时,value都会被覆盖,且不会报错。
4、java中的重写和重载
5、string、stringbulider、stringbuffer
string是常量。
stringbuilder线程不安全的可变字符串。
stringbuffer线程安全的可变字符串。
6、arraylist、linkedlist、vector
arraylist:底层数组。查询快、修改快、增删慢。
linkedlist:底层链表。查询慢、修改慢、增删快
7、数组和链表
数组在栈中分配空间。查询、修改快,增删慢
链表在堆中分配空间。查询、修改慢。增删快。
vector:底层数组实现。线程安全
8、list、map、set
list:有序容器。可以存放多个null值
map:是一个接口,存放k、v键值对
set:无序容器。数据唯一
9、判断算法效率
时间复杂度:n、log(n)、nlog(n)、n²、n+k
空间复杂度:1、n、log n、k、n+k
稳定性:a在b之前,排序后a还在b之前。
10、java的重载和重写
重载:方法名一样,但是参数类型、参数数量、返回值类型可以不一样。
重写:子类继承父类的时候对父类方法进行重写。两个方法之间的参数类型、参数数量和返回值类型要一致
11、java的设计模式
3类23种。
第一类:创建型模式--单例、工厂、建造者
第二类:结构型模式--代理模式
第三类:行为型模式--观察者模式
12、单例模式:
分为懒汉模式、饿汉模式和双重锁模式
实现:
特点:保证一个类仅有一个实例对象,提供一个全局访问点
应用场景:程序计数器、spark的共享变量。
13、jvm的类加载机制
https://www.jianshu.com/p/3556a6cca7e5
什么是类的加载:
jvm将.class文件加载到内存,对字节码数据进行校验、解析、初始化等操作,最终形成可以被jvm使用的java类型。
类的生命周期?
加载----校验----准备----解析----初始化----使用----卸载
类加载器是什么?
通过类的全称来获取类的二进制字节流的工作交给虚拟机以外的类加载器来完成,则可以完成灵活的类加载国过程。系统自带三种类加载器:根类加载器、扩展类加载器、应用程序类加载器。
双亲委派机制是什么?
当一个类收到了类加载器的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父加载器去完成。因此所有的加载请求最后都会落到bootstrap(根类加载器)。只有当父类反馈自己无法完成该请求,才会让子类自己去加载。这样做的好处是完成类加载的层级关系,避免类的重复加载和核心类被篡改。
类的加载顺序:启动类加载器---》扩展类加载器---》应用程序加载器---》自定义加载器
14、jvm内存模型
堆、虚拟机栈、本地方法栈、程序计数器、元空间
15、能描述下jvm的垃圾回收机制吗?
①、先判断对象是否进行回收,涉及到对象的判定问题。java采用的是可达性分析。通过从gcroots根节点向下溯源,如果该节点与gcroots没有相关的引用链时,可以判定为对象不可达。一般经过两次的判定,会将对象回收。引用链分为强引用、软引用、弱引用、虚引用
②、回收算法有哪些?
常见的有:a、标记-清除算法。这是最简单且容易实现的算法。通过可达性分析,将该内存区域标记为可回收、存活、未使用。然后清除掉可回收区域的对象。但是容易产生内存碎片。当需要给一个较大的对象分配内存时,由于不连续的碎片较多,会触发gc。而且标记和清除的过程、效率都不算太高。
b、复制算法。为了解决内存碎片较多的问题,出现了复制算法。该算法的核心是将内存区域一分为二。当进行一个区域的标记回收后,将存活对象复制到另外一块区域。这样,解决了内存碎片的产生。缺点,对于内存空间的使用效率较低。
c、标记-整理算法。是将整块内存进行标记,但是立刻进行回收,而是将存活对象移动到内存边界。然后清理掉边界以外的内存。
d、分代回收算法。是目前主流的jvm使用的垃圾回收算法。将整块内存区域分为年轻代、老年代。1.8之后没有永久代。年轻代的特点是每一次垃圾回收都会有大量的对象死亡,所以采用复制算法,只需付出少量存活对象的复制成本即可,年轻代的回收称为minorGC。年轻代的空间分为eden区、s0区、s1区。新分配的对象会进入到eden区,当eden区满了之后,会进行一次minorGC,将存活的对象放入s0区,当s0区满了之后,再触发一次minorGC。将存活对象保存到s1区。当s1区满了之后,再进行GC,如果仍然存活则进入老年代。
如何判定是否进入老年代?
--有三种方式,Ⅰ:通过对年龄进行设限。每触发一次GC,对象的年龄计数器会加1,如果超过设定的值,即进入老年代。
Ⅱ、动态判定。当s内存区域中年龄相同的对象超过半数,那么大于该年龄的其他对象则进入老年代
Ⅲ、大对象直接进入老年代。当一次minorGC的存活对象大于s区所能分配的内存空间,则通过担保机制进入老年代。
gc的担保机制?
在进行一次minorGC前,会判断新生代所有对象的大小是否大于老年代能分配的所有内存空间。当不大于时,可以进行安全的minorGC。当大于时,担保分为允许担保失败和不允许担保失败。当允许担保失败,会判断历次新生代进入老年代对象的平均大小与老年代能分配的内存空间进行比较。如果小于,那么进行一次冒险的minorGC。如果大于,会触发fullGC。先进行老年代的majorGC,再进行minorGC。如果不允许担保失败,就不会进行二次判断,直接触发fullGC。