常见面试题及解答

1Java 基础

1、HashMap的源码,实现原理,JDK8中对HashMap做了怎样的优化。

1) HashMap实现Map接口,元素以键值对的方式存储,并且允许使用null  作为key和value,因为HashMap的key不允许重复,所以只能有一个键是 null,并不推荐使用,因为容易出问题并且很难排查。

2)HashMap不能保证放入元素的顺序,所以是无序的,HashMap初始长度16,每次扩充翻倍。

JDK1.7的HashMap是使用数组加链表进行存储,每一个键值对用一个Entry来存储,Entry中还有hash和next属性,hash是key的hash值,next则是指向下一个Entry的指针。HashMap的初始容量是16,即有一个长度为16的数组,用来存储每个Entry链表的头结点。每一个Entry存储的位置是由key.hashCode()%len获得,假设有A,B,C三个键值对并且每个key的hash值对16取模都是1,A先进来就存储在Entry[1],再进来B则B.next=A、Entry[1]=B。C相同。也就是说数组中存储的是最后插入的元素。所以说放入元素是无序的。

JDK1.8的HashMap加入了红黑树,当每个单链表的长度大于阈值(8)时,则将链表转化为红黑树。并且1.8之后,新插入的元素都放在了链表的尾部。

3) HashMap是线程不安全的,可以使用Collections.synchronizedMap()获 取一个线程安全的集合,Collections.synchronizedMap()实际上是定义一个synchronizedMap的内部类,这个内部类实现了Map接口并在方法上加上synchronized关键字。就是操作的HashMap的时候自动添synchronized。

2、HaspMap扩容是怎样扩容的,为什么都是2的N次幂的大小。

1) 如果超过阈值,会自动扩充两倍,然后重新计算HashMap中各个Entry的位置。

2) 都是2的N次是因为在新插入操作的时候,位置计算是由(n-1)&hash决定的,(n-1)&hash实际上相当于hash%(n-1),只不过&运算更快速。HashMap中的hash=((h = key.hashCode()) ^ (h >>> 16),hashCode是9位十进制也就是36位二进制。右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。这样大多数的hashCode的分布已经很不错了。

3、HashMap,HashTable,ConcurrentHashMap的区别。

   HashMap的key和value都可以为空,线程不安全,初始size16,每次扩容翻倍,先插入后扩容,插入后长度达到总数的75%则扩容,计算index(n-1)&hash,迭代器是fail-fast的(在遍历一个集合时,当集合结构被修改,会抛出Concurrent Modification Exception)

HashTable的key和value都不能为空,线程不安全,初始size11,每次扩容size=oldSize*2+1,计算index方法 (hash & 0x7FFFFFFF) % tab.length,迭代器不是fail-fast

ConcurrentHashMap线程安全,分段加锁,段内扩容。需锁定整个map时按顺序锁定各个分段,然后按顺序释放锁。

4、极高并发下HashTable和ConcurrentHashMap哪个性能更好,为什么,如何实现的。

ConcurrentHashMap更好,分段锁,HashTable锁整个table。

5、HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么。

1)put是会见检查Map容量是否足够,不够则扩充,扩充后会把数组从老的hash表移到新的hash表,多个线程同时同时操作可能会形成循环链表,所以get()时会出现无限循环

2)造成迭代的fail-fast

6、java中四种修饰符的限制范围。

7、Object类中的方法。

 hash,finalize,notify,notifyAll,wait....

8、接口和抽象类的区别,注意JDK8的接口可以有实现。

1)抽象类可以有构造方法,接口中不能有构造方法。

2)抽象类中可以有普通成员变量,接口中没有普通成员变量

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

4) 一个类可以实现多个接口,但只能继承一个抽象类。

5)接口可以被多重实现,抽象类只能被单一继承

6)如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法

9、动态代理的两种方式,以及区别。

 (1)JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

 (2)JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口

10、Java序列化的方式。

11、传值和传引用的区别,Java是怎么样的,有没有传值引用。

12、一个ArrayList在循环过程中删除,会不会出问题,为什么。

会遗漏元素,例如删除了a[2],删除后a[3]就到了a[2]的位置上,继续遍历是从a[3]开始,那么如果之前的a[3]符合删除条件也不会被删除。反向遍历删除是可以的,用迭代器删除单线程可以多线程有可能触发fail-fast。

2JVM

1、JVM的内存结构。

 程序计数器:当前线程所执行的字节码的行号指示器,是一块较小的内存空间

 Java虚拟机栈:线程私有的,生命周期同线程相同。虚拟机栈描述的是java方法执行的内存模型--每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈,动态链接、方法出口等信息。

 本地方法栈:与虚拟机栈类似,只是本地方法栈只服务于Native方法。

 java堆:线程共享,占用内存最大,GC的主要区域。所有的对象实例都要在这里分配内存

 方法区:线程共享,Java虚拟机规范把方法区描述为堆的的一个逻辑部分,但别名是非堆,与堆区分。方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译             后的代码等数据。

2、JVM方法栈的工作过程,方法栈和本地方法栈有什么区别。

   见1

3、JVM的栈中引用如何和堆中的对象产生关联。

 对象的实例保存在堆上,对象的元数据(instantKlass)保存在方法区,对象的引用保存在栈上。

4、可以了解一下逃逸分析技术。

5、GC的常见算法,CMS以及G1的垃圾回收过程,CMS的各个阶段哪两个是Stop the world的,CMS会不会产生碎片,G1的优势。

 (1)判断对象已死:可达性分析算法,从root对象开始到当前对象没有任何调用链,则证明当前对象不可用。不可用不一定“非死不可”,需要至少经历两次标记过程,然后才会被虚拟机自动建立的、低优先级的Finalizer线程去执行(只是执行并不等待执行结束)。

 (2)清除算法:

 标记-清除算法:标记所有需要回收的对象,统一回收。效率不高并且会产生大量不连续的内存碎片

 复制算法:将内存分为两块,每次只使用一块,将当前使用块中的存活对象复制到另一块中,然后在把当前块清理掉。可用内存缩小严重

 标记-整理算法:标记存活的对象都向一端移动,然后清理掉端边界以外的内存。

  (3)堆一般分为年轻代和老年代:

    年轻代一般采用复制算法,有一个eden区和两个survior区(From和To),大小比例通常是8:1(年轻代中80%的对象都是朝生夕死)。每次只使eden区和一个survior(From)区。每个GC周 期都会将eden区的所有存活对象复制到To中,而From中仍然存活的并且年龄未到达阈值的对象会被复制到To中,超龄的复制到老年代。这时eden和From是空的,From就会转换成“To”,To"则当做“From”。知道To中区域满了则会移动所有存活对象到老年代。

    (4)垃圾收集器

       CMS和G1

  

6、标记清除和标记整理算法的理解以及优缺点。

 见上

7、eden survivor区的比例,为什么是这个比例,eden survivor的工作过程。

 见上

8、JVM如何判断一个对象是否该被GC,可以视为root的都有哪几种类型。

 被启动类(bootstrap 加载器)加载的类和创建的对象;
 JavaStack 中的引用的对象 (栈内存中引用的对象);
 方法区中静态引用指向的对象;
 方法区中常量引用指向的对象;
 Native 方法中 JNI 引用的对象。

9、强软弱虚引用的区别以及GC对他们执行怎样的操作。

 不想打字了,见《深入理解java虚拟机》第2版第65页3.2.3再谈引用

10、Java是否可以GC直接内存。

 直接内存其实是jvm自定义的空间,直接内存本身不受gc的影响,但是由于有对象在堆引用这这块内存,那么受到gc的间接影响,典型的是java的代码里有system.gc去回收。

 堆外内存不同于直接内存,堆外内存会溢出,并且其垃圾回收依赖于代码显式调用System.gc()

11、Java类加载的过程。

 加载-验证-准备-解析-初始化

12、双亲委派模型的过程以及优势。

13、常用的JVM调优参数。

14、dump文件的分析。

15、Java有没有主动触发GC的方式(没有)。

3数据结构与算法

1、B+树

2、快速排序,堆排序,插入排序(八大排序算法)

3、一致性Hash算法,一致性Hash算法的应用

4多线程()

1、Java实现多线程有哪几种方式。

  继承Thread或者实现Runnable接口

2、Callable和Future的了解。

  Callable是跟Runnable类似的接口。不过Callable接口需要实现call方法,是有返回值的并且可以抛出异常。而实现Runnable接口需要实现run方法是没有返回值的并无法抛出异常。

  Future是控制Callable的运行过程,参数是Callable的一个实例。

3、线程池的参数有哪些,在线程池创建一个线程的过程。

  核心线程数,最大线程数,空闲时间,时间单位,阻塞队列,拒绝机制

4、volitile关键字的作用,原理。

 (1)Lock前缀的指令会引起处理器缓存写回内存;

 (2)一个处理器的缓存回写到内存会导致其他处理器的缓存失效;

 (3)当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值

5、synchronized关键字的用法,优缺点。

6、Lock接口有哪些实现类,使用场景是什么。

7、可重入锁的用处及实现原理,写时复制的过程,读写锁,分段锁(ConcurrentHashMap中的segment)。

8、悲观锁,乐观锁,优缺点,CAS有什么缺陷,该如何解决。

9、ABC三个线程如何保证顺序执行。

10、线程的状态都有哪些。

11、sleep和wait的区别。

12、notify和notifyall的区别。

13、ThreadLocal的了解,实现原理。

5分布式

1、分布式事务的控制。分布式锁如何设计。

2、分布式session如何设计。

3、dubbo的组件有哪些,各有什么作用。

4、zookeeper的负载均衡算法有哪些。

5、dubbo是如何利用接口就可以通信的。

6框架相关

1、SpringMVC的Controller是如何将参数和前端传来的数据一一对应的。

2、Mybatis如何找到指定的Mapper的,如何完成查询的。

3、Quartz是如何完成定时任务的。自定义注解的实现。

4、Spring使用了哪些设计模式。Spring的IOC有什么优势。

5、Spring如何维护它拥有的bean。

6、一些较新的东西JDK8的新特性,流的概念及优势,为什么有这种优势。

7、区块链了解如何设计双11交易总额面板,要做到高并发高可用

原文地址:https://www.cnblogs.com/ryan304/p/11578691.html