春招网上开始出现大量面试题,看到这个面试题比较全面,但是没看到答案,自己也跟着做一下并梳理答案。

这份面试题,包含的内容了十九了模块:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql、Redis、JVM 。如下图所示:

Java 最常见面试题 200+ 清单及自己整理的答案(上)-明亮

下面一起来看 208 道面试题,具体的内容。

一、Java 基础

1.JDK 和 JRE 有什么区别?

JDK可以支持Java程序的开发,包括编译器(javac.exe)、开发工具(javadoc.exe、jar.exe、keytool.exe、jconsole.exe)和更多的类库(如tools.jar)等。

JRE为java的开发环境,包括JVM虚拟机(java.exe等)和基本的类库(rt.jar等),是java程序运行所需要的软件环境。

JKD包含JRE。如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

JRE根据不同操作系统(如:windows,linux等)和不同JRE提供商(IBM,ORACLE等)有很多版本,最常用的是Oracle公司收购SUN公司的JRE版本。如果你想查看更官方的解释,可以前往Oracle官网:https://www.oracle.com/cn/java/

2.== 和 equals 的区别是什么?

一般面试中问到的== 和 equals 的区别指的是字符串的比较,答案如下:

==比较的是两个字符串内存地址(堆内存)的数值是否相等,属于数值比较。

equals():比较的是两个字符串的内容,属于内容比较。

更详细的解释可以参考:https://www.cnblogs.com/smyhvae/p/3929585.html

3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

 不对,Java 对于 eqauls 方法和 hashCode 方法的规定是如果两个对象 equals 方法相等则它们的 hashCode 值一定要相同,如果两个对象的 hashCode 相同则它们的 equals 方法并不一定相同。

更详细的解释可以参考:https://www.cnblogs.com/Qian123/p/5703507.html

4.final 在 java 中有什么作用?

final修饰的类,叫final类,不可被继承,

final修饰的方法,不可被重写,

final修饰的变量,不可被修改,final经常和static组合使用来定义常量。

更详细的解释可以参考:http://www.importnew.com/7553.html

5.java 中的 Math.round(-1.5) 等于多少?

-1

Math.round()方法准确说是“四舍六入”,5要进行判断对待。

Math.round()的原理是对传入的参数+0.5之后,再向下取整得到的数就是返回的结果,返回值为long型。这里的向下取整是说取比它小的第一个整数或者和它相等的整数。

因此Math.round(-1.5)的结果是-1.5 + 0.5 再向下取整,即-1.0取整,结果是-1.
Math.round(-1.4)的结果是 -1.4 + 0.5 即-0.9 向下取整,结果是-1。
同理,Math.round(1.5)即为 1.5 + 0.5 再向下取整,结果是2。

明细参考:https://blog.csdn.net/fenggering/article/details/55806408

6.String 属于基础的数据类型吗?

不属于。

String不是基本的数据类型,是final修饰的java类,java中的基本类型一共有8个,它们分别为:

1 字符类型:byte,char

2 基本整型:short,int,long

3 浮点型:float,double

4 布尔类型:boolean

7.java 中操作字符串都有哪些类?它们之间有什么区别?

StringBuffer,StringBuilder,StringUtil?

区别:buffer是线程安全,效率低;builder是非线程安全,效率高。

存疑待补充。

8.String str=”i”与 String str=new String(“i”)一样吗?

两者看似都是创建了一个字符串对象,但在内存中确是各有各的想法。

String str= “i”; 在编译期,JVM会去常量池来查找是否存在“i”,如果不存在,就在常量池中开辟一个空间来存储“i”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str的空间,来存储“i”在常量池中的地址值。

String str = new String(“i”) ;在编译阶段JVM先去常量池中查找是否存在“i”,如果过不存在,则在常量池中开辟一个空间存储“i”。在运行时期,通过String类的构造器在堆内存中new了一个空间,然后将String池中的“i”复制一份存放到该堆空间中,在栈中开辟名字为str的空间,存放堆中new出来的这个String对象的地址值。

也就是说,前者在初始化的时候可能创建了一个对象,也可能一个对象也没有创建;后者因为new关键字,至少在内存中创建了一个对象,也有可能是两个对象

参考:blog.csdn.net/qq_334174

9.如何将字符串反转?

常用StringBuilder的reverse方法。

更多方法参考:https://josh-persistence.iteye.com/blog/2205772

10.String 类的常用方法都有那些?

String类常用方法可以查看 Java String API 文档中的方法摘要。

11.抽象类必须要有抽象方法吗?

不一定

抽象类中不一定要有抽象方法,但是抽象方法在的类必须是抽象类。

12.普通类和抽象类有哪些区别?

  1. 抽象类不能用new调出构造方法创建对象(不能被实例化),普通类可以new出来。
  2. 抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。
  3. 抽象方法只需申明,而无需实现,留给继承它的子类去实现,抽象类中可以允许普通方法有主体
  4. 含有抽象方法的类必须申明为抽象类。
  5. 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类。

构造函数就是在new的同时赋值,抽象类的构造函数可以为继承它的子类使用。

抽象类不能使用static修饰词,因为抽象类是不能实例化的,即不能被分配内存,而static修饰的方法在类实例化之前就已经别分配了内存,这样一来矛盾就出现了:抽象类不能被分配内存,而static方法必须被分配内存。所以抽象类中不能有静态的抽象方法。

13.抽象类能使用 final 修饰吗?

不能

抽象类生来就是要被继承使用的,而final修饰的类是不允许被继承,重写。

14.接口和抽象类有什么区别?

  1. 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  2. 抽象类要被子类继承,接口要被类实现。
  3. 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。(经评论指出,在Java8中使用default 关键字可以为接口定义默认方法,接口的子类也可以实现此默认方法)
  4. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  5. 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
  6. 抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
  7. 抽象类里可以没有抽象方法
  8. 如果一个类里有抽象方法,那么这个类只能是抽象类
  9. 抽象方法要被实现,所以不能是静态的,也不能是私有的。
  10. 接口可继承接口,并可多继承接口,但类只能单根继承。

详细参考:http://www.importnew.com/12399.html

更多关于抽象类的衍生问题:

  1. java中为什么要用抽象类?
  2. java中没有抽象方法的抽象类有什么意义?
  3. 什么是抽象类,抽象方法?

15.java 中 IO 流分为几种?

参考下图,明细参考博文:https://www.cnblogs.com/QQ846300233/p/6046388.html

Java 最常见面试题 200+ 清单及自己整理的答案(上)-明亮
16.BIO、NIO、AIO 有什么区别?

参考博文:

https://blog.csdn.net/skiof007/article/details/52873421

https://blog.csdn.net/u010310183/article/details/81700405

17.Files的常用方法都有哪些?

常用方法可以参考JAVA File API文档中的方法摘要

二、容器

18.java 容器都有哪些?

常用容易为list,set,map,明细参考:https://www.jianshu.com/p/047e33fdefd2

19.Collection 和 Collections 有什么区别?

  1. java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
  2. java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

参考:https://www.jianshu.com/p/ff1b747d2a5c

20.List、Set、Map 之间的区别是什么?

把第18题的答案说出来了。

List特点:元素有放入顺序,元素可重复 
Map特点:无放入顺序 ,元素按键值对存储,
Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的) 

总结参考:

https://blog.csdn.net/qq_33036887/article/details/79503448

明细参考:

https://j2eemylove.iteye.com/blog/1195823

21.HashMap 和 Hashtable 有什么区别?

HashMap是非线程安全的,单线程效率高,多线程不安全。

Hashtable是线程安全的,单线程慢,多线程安全。

明细参考:http://www.importnew.com/7010.html

22.如何决定使用 HashMap 还是 TreeMap?

TreeMap是按照字母顺序排序的,HashMap没有,如果要排序就用TreeMap,如果不用排序就用HashMap,因为它快。

明细参考:https://blog.csdn.net/IT_ZJYANG/article/details/51867143

23.说一下 HashMap 的实现原理?

简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据 hash 算法来决定其在数组中的存储位置,在根据 equals 方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry 时,也会根据 hash 算法找到其在数组中的存储位置,再根据 equals 方法从该位置上的链表中取出该Entry。

参考:

https://www.jianshu.com/p/bfcbad059ba5

https://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/

着重参考:

http://www.cnblogs.com/chengxiao/p/6059914.html

24.说一下 HashSet 的实现原理?

  1. HashSet底层由HashMap实现
  2. HashSet的值存放于HashMap的key上
  3. HashMap的value统一为PRESENT

参考:https://yikun.github.io/2015/04/08/Java-HashSet%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/

25.ArrayList 和 LinkedList 的区别是什么?

  1. ArrayList无序,LinkedList有序
  2. ArrayList的查询效率高,LinkedList删除、插入效率高。

参考:https://pengcqu.iteye.com/blog/502676

26.如何实现数组和 List 之间的转换?

参考:https://blog.csdn.net/zjx2016/article/details/78273192

27.ArrayList 和 Vector 的区别是什么?

(1)同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
备注:对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。
(2)数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。

简而言之:

  1. ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
  2. Vector提供indexOf(obj, start)接口,ArrayList没有。
  3. Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销。

28.Array 和 ArrayList 有何区别?

参考:http://blog.sina.com.cn/s/blog_5ce1fe770100b0ay.html

29.在 Queue 中 poll()和 remove()有什么区别?

remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。

参考:https://blog.csdn.net/u012050154/article/details/60572567

30.哪些集合类是线程安全的?

  1. Vector
  2. HashTable
  3. ConcurrentHashMap
  4. CopyOnWriteArrayList
  5. CopyOnWriteArraySet

参考:https://blog.csdn.net/lixiaobuaa/article/details/79689338

31.迭代器 Iterator 是什么?

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

参考:https://www.nowcoder.com/questionTerminal/8863f297b1fc4bbca6de95528b6051e1

32.Iterator 怎么使用?有什么特点?

Java中的Iterator功能比较简单,并且只能单向移动:

  1. 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
  2. 使用next()获得序列中的下一个元素。
  3. 使用hasNext()检查序列中是否还有元素。
  4. 使用remove()将迭代器新返回的元素删除。

Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

33.Iterator 和 ListIterator 有什么区别?

Iterator只能向前,listiterator可前可后, ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

参考:https://www.nowcoder.com/questionTerminal/9dbbd35ff35e4e008fdd792c2b539940

34.怎么确保一个集合不能被修改?

jdk提供的方法:Collections.unmodifiableList(list);

Guava提供的方法:ImmutableList.copyOf(list);

Apache Commons Collections提供的方法:ListUtils.unmodifiableList(list);

参考:https://blog.csdn.net/neweastsun/article/details/79782674

三、多线程

35.并行和并发有什么区别?

  1. 并发:一个处理器同时处理多个任务。
  2. 并行:多个处理器或者是多核的处理器同时处理多个不同的任务.

36.线程和进程的区别?

做个简单的比喻:进程=火车,线程=车厢

  1. 线程在进程下行进(单纯的车厢无法运行)
  2. 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  3. 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  4. 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  5. 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  6. 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  7. 进程可以拓展到多机,线程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  8. 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁”
  9. 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

参考:https://www.zhihu.com/question/25532384/answer/411179772

37.守护线程是什么?

Java的线程分为两种:User Thread(用户线程)、DaemonThread(守护线程)。

只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束是,守护线程随着JVM一同结束工作,Daemon作用是为其他线程提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),他就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

参考:http://www.importnew.com/26834.html

38.创建线程有哪几种方式?

  1. 继承Thread类(真正意义上的线程类),是Runnable接口的实现。
  2. 实现Runnable接口,并重写里面的run方法。
  3. 使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。

参考:https://www.nowcoder.com/questionTerminal/e33c72bceb4343879948342e2b6e3bca

39.说一下 runnable 和 callable 有什么区别?

  1. Callcble是可以有返回值的,具体的返回值就是在Callable的接口方法call返回的,并且这个返回值具体是通过实现Future接口的对象的get方法获取的,这个方法是会造成线程阻塞的;而Runnable是没有返回值的,因为Runnable接口中的run方法是没有返回值的;
  2. Callable里面的call方法是可以抛出异常的,我们可以捕获异常进行处理;但是Runnable里面的run方法是不可以抛出异常的,异常要在run方法内部必须得到处理,不能向外界抛出;

参考:https://blog.csdn.net/hzw19920329/article/details/52383287

40.线程有哪些状态?

  1. 创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态;
  2. 就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态
  3. 运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
  4. 阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend等方法都可以导致线程阻塞。
  5. 死亡状态。如果一个线程的run方法执行结束,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪状态。

41.sleep() 和 wait() 有什么区别?

  1. wait只能在同步(synchronize)环境中被调用,而sleep不需要。详见Why to wait and notify needs to call from synchronized method
  2. 进入wait状态的线程能够被notify和notifyAll线程唤醒,但是进入sleeping状态的线程不能被notify方法唤醒。
  3. wait通常有条件地执行,线程会一直处于wait状态,直到某个条件变为真。但是sleep仅仅让你的线程进入睡眠状态。
  4. wait方法在进入wait状态的时候会释放对象的锁,但是sleep方法不会。
  5. wait方法是针对一个被同步代码块加锁的对象,而sleep是针对一个线程。更详细的讲解可以参考《Java核心技术卷1》,里面介绍了如何使用wait和notify方法。

参考:https://www.jianshu.com/p/25e959037eed

42.notify()和 notifyAll()有什么区别?

  1. 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
  2. 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
  3. 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

参考:https://www.zhihu.com/question/37601861/answer/145545371

另附一个简单的解释:

比如说,你是你家挣钱的,儿子和女儿是花钱的。儿子给家里要100,女儿要30。可是家里没钱,他们只能等。后来你出去打工,赚钱了,赚了50,这时你要在儿子和女儿之间选择一个人叫醒。

如果不凑巧,你把儿子叫醒了,儿子发现钱还是不够,又去等。因为你只能叫一次,女儿就错过了使用这50块钱的机会。所以,你决定把所有的人都叫醒,虽然费劲一点。这样一来,儿子发现不够,接着等,女儿发现够了,就用了。

参考:https://www.zhihu.com/question/37601861/answer/308613735

43.线程的 run()和 start()有什么区别?

  1. start() 可以启动一个新线程,run()不能
  2. start()不能被重复调用,run()可以
  3. start()中的run代码可以不执行完就继续执行下面的代码,即进行了线程切换。直接调用run方法必须等待其代码全部执行完才能继续执行下面的代码。
  4. start() 实现了多线程,run()没有实现多线程。

参考:https://www.cnblogs.com/renhui/p/6066750.html

44.创建线程池有哪几种方式?

  1. newFixedThreadPool:创建固定大小的线程池。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  2. newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  3. newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  4. newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
  5. newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

参考:https://blog.csdn.net/u010412719/article/details/52489843

45.线程池都有哪些状态?

线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated

46.线程池中 submit()和 execute()方法有什么区别?

  1. 接收的参数不一样;
  2. submit()有返回值,而execute()没有;
  3. execute 会抛出异常,而submit 如果不用Future.get()获取执行结果的话会“吞掉”异常。

execute源码:

public interface Executor {
    void execute(Runnable command);
}

submit源码

public interface ExecutorService extends Executor {
  ...
  <T> Future<T> submit(Callable<T> task);

  <T> Future<T> submit(Runnable task, T result);

  Future<?> submit(Runnable task);
  ...
}

参考:https://www.cnblogs.com/handsomeye/p/6225033.html

47.在 java 程序中怎么保证多线程的运行安全?

  1. synchronized
  2. Lock

明细参考:https://www.jianshu.com/p/610dcc51d5fd

48.多线程锁的升级原理是什么?

Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

明细参考:https://www.cnblogs.com/paddix/p/5405678.html

存疑,待补充?

49.什么是死锁?

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

明细参考:http://www.runoob.com/java/thread-deadlock.html

50.怎么防止死锁?

  1. 设置加锁顺序
  2. 设置加锁时限
  3. 死锁检测

明细参考:https://www.cnblogs.com/vinozly/p/5240204.html

51.ThreadLocal 是什么?有哪些使用场景?

ThreadLocal提供一个线程(Thread)局部变量,访问到某个变量的每一个线程都拥有自己的局部变量。说白了,ThreadLocal就是想在多线程环境下去保证成员变量的安全。

使用场景:解决数据库连接、Session管理

明细参考:https://blog.csdn.net/sonny543/article/details/51336457

52.说一下 synchronized 底层实现原理?

从JVM规范中可以看到Synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit。

Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

明细参考:http://www.cnblogs.com/paddix/p/5367116.html

53.synchronized 和 volatile 的区别是什么?

  1. volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  4. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  5. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

参考:https://www.cnblogs.com/tf-Y/p/5266710.html

54.synchronized 和 Lock 有什么区别?

  1. 首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
  2. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  3. synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  4. 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
  5. synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
  6. Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

明细:https://blog.csdn.net/natian306/article/details/18504111

55.synchronized 和 ReentrantLock 区别是什么?

两者的共同点:

  1. 协调多线程对共享对象、变量的访问
  2. 可重入,同一线程可以多次获得同一个锁
  3. 都保证了可见性和互斥性

两者的不同点:

  1. ReentrantLock显示获得、释放锁,synchronized隐式获得释放锁
  2. ReentrantLock可响应中断、可轮回,synchronized是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
  3. ReentrantLock是API级别的,synchronized是JVM级别的
  4. ReentrantLock可以实现公平锁
  5. ReentrantLock通过Condition可以绑定多个条件
  6. 底层实现不一样, synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略

参考:https://www.jianshu.com/p/650498242d67

56.说一下 atomic 的原理?

通过CAS乐观锁保证原子性,通过自旋保证当次修改的最终修改成功,通过降低锁粒度(多段锁)增加并发性能。

参考:https://mp.weixin.qq.com/s/aw6OXC9wkxH42rCywNd7yQ

其它参考:https://blog.csdn.net/zhangerqing/article/details/43057799

四、反射

57.什么是反射?

反射机制指的是程序在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。反射

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  4. 在运行时调用任意一个对象的方法

参考:深入解析Java反射(1) – 基础

58.什么是 java 序列化?什么情况下需要序列化?

Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。

什么情况下需要序列化?

  1. 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
  2. 当你想用套接字在网络上传送对象的时候;
  3. 当你想通过RMI传输对象的时候;

参考:https://blog.csdn.net/sheepmu/article/details/27579895

59.动态代理是什么?有哪些应用?

动态代理是什么?

  1. 譬如你有很多套房子要中介帮忙出租,那么中介就相当于java中的代理。
  2. 假如你每套房子找一个中介帮忙出租,那么这每一个中介就相当于java中的静态代理。
  3. 假如你的所有房子委托一个中介出租,那么这个中介就是相当于java中的动态代理。

Java动态代理其实内部也是通过Java反射机制来实现的。明细参考:https://www.jianshu.com/p/dbce090d5c3e

动态代理的应用场景

  1. 设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。
  2. 我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。
  3. Spring的AOP机制就是采用动态代理的机制来实现切面编程。

明细参考:https://blog.csdn.net/u011784767/article/details/78281384

60.怎么实现动态代理?

jdk自带的,继承InvocationHandler接口调用invoke方法。

CGLIB,继承MethodInterceptor调用invokeSuper。

明细参考:https://blog.csdn.net/HEYUTAO007/article/details/49738887

五、对象拷贝

61.为什么要使用克隆?

一般克隆指的是对对象的克隆,8中基本类型的赋值用=就可以了。如果一个对象(实体类)有很多个属性并且属性已经赋值,而我们需要额外使用一个相同值的对象时,用new的方法创建再一个一个赋值肯定是不合适的,这个时候用克隆就很快捷方便了。

参考:https://zhuanlan.zhihu.com/p/27898913

62.如何实现对象克隆?

  1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
  2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)

参考:https://www.cnblogs.com/Qian123/p/5710533.html

63.深拷贝和浅拷贝区别是什么?

如果对象A里包含对象B,在重写A对象clone方法的时候如果不对所包含的对象B进行clone那么就是浅拷贝,反之在重写clone方法的时候对对象B也进行clone,那么就是深拷贝。

抽象描述:浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

参考:https://www.cnblogs.com/Qian123/p/5710533.html

衍生问题,克隆为什么要被重写?

六、Java Web

64.jsp 和 servlet 有什么区别?

jsp就是在html里面写java代码,servlet就是在java里面写html代码

jsp更注重前端显示,servlet更注重模型和业务

抽象描述:

  1. Servlet在Java代码中通过HttpServletResponse对象动态输出HTML内容
  2. JSP在静态HTML内容中嵌入Java代码,Java代码被动态执行后生成HTML内容

引自: https://www.zhihu.com/question/37962386/answer/74906895

65.jsp 有哪些内置对象?作用分别是什么?

  1. request 用户端请求,此请求会包含来自GET/POST请求的参数
  2. response 网页传回用户端的回应
  3. pageContext 网页的属性是在这里管理
  4. session 与请求有关的会话期
  5. application servlet 正在执行的内容
  6. out 用来传送回应的输出
  7. config  servlet的构架部件
  8. page JSP网页本身
  9. exception 针对错误网页,未捕捉的例外

明细:https://blog.csdn.net/machinecat0898/article/details/23380843

66.说一下 jsp 的 4 种作用域?

  1. request域
  2. session域
  3. application域
  4. page域

明细:https://blog.csdn.net/machinecat0898/article/details/23380843

67.session 和 cookie 有什么区别?

  1. 存放位置不同
  2. 存取方式的不同
  3. 安全性(隐私策略)的不同
  4. 有效期上的不同
  5. 对服务器造成的压力不同
  6. 跨域支持上的不同

参考:https://blog.csdn.net/guoweimelon/article/details/50886092

68.说一下 session 的工作原理?

session专业术语叫会话控制,在用户第一次访问系统的时候,服务器生成一个对应的session id和相应的信息保持在会话中。

第二次访问的时候,会话就会带上这个session id ,服务器可以根据session id知道当前用户的状态。

明细参考:https://www.cnblogs.com/liyasong/p/6387833.html

69.如果客户端禁止 cookie 能实现 session 还能用吗?

可以。url重写,URL重写要求将站点中的所有超链接都进行改造,在超链接后用一个特殊的参数JSESSIONID保存当前浏览器对应session的编号,这样一来,当用户点击超链接访问服务器时,服务器可以从URL后的参数中分析出JSESSIONID,从而找到对应的sesison使用.

明细参考:https://www.cnblogs.com/pxffly/p/7488139.html

70.spring mvc 和 struts 的区别是什么?

  1. Struts2是类级别的拦截,SpringMVC是方法级别的拦截
  2. Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
  3. 由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
  4. 由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。
  5. 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。
  6. SpringMVC的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
  7. SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
  8. SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
  9. Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
  10. 设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。
  11. SpringMVC开发效率和性能高于Struts2。
  12. SpringMVC可以认为已经100%零配置。

参考:https://www.cnblogs.com/huajiezh/p/6415444.html

71.如何避免 sql 注入?

  1. 使用preparestatement预编译
  2. 使用正则表达式将包含有 单引号(‘),分号(;) 和 注释符号(–)的语句给替换掉来防止SQL注入
  3. mybatis框架尽量使用#{}的预编译写法,少用${}的写法。

参考:https://www.cnblogs.com/2016-10-07/p/6785106.html

72.什么是 XSS 攻击,如何避免?

也是struts2的著名漏洞之一,另外一个漏洞是容易被重定向。

XSS(Cross Site Script,跨站脚本攻击)是向网页中注入恶意脚本在用户浏览网页时在用户浏览器中执行恶意脚本的攻击方式。跨站脚本攻击分有两种形式:反射型攻击(诱使用户点击一个嵌入恶意脚本的链接以达到攻击的目标,目前有很多攻击者利用论坛、微博发布含有恶意脚本的URL就属于这种方式)和持久型攻击(将恶意脚本提交到被攻击网站的数据库中,用户浏览网页时,恶意脚本从数据库中被加载到页面执行,QQ邮箱的早期版本就曾经被利用作为持久型跨站脚本攻击的平台)。XSS虽然不是什么新鲜玩意,但是攻击的手法却不断翻新,防范XSS主要有两方面:消毒(对危险字符进行转义)和HttpOnly(防范XSS攻击者窃取Cookie数据

参考:https://www.freebuf.com/articles/web/185654.html

  1. 使用 XSS Filter
  2. html 实体
  3. JavaScript编码
  4. Http Only cookie

参考:https://zhuanlan.zhihu.com/p/55394411

73.什么是 CSRF 攻击,如何避免?

跨站请求伪造,跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的

用例子来说明:

假如一家银行用以运行转账操作的URL地址如下:

 http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName

那么,一个恶意攻击者可以在另一个网站上放置如下代码:

<img src=”http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman”>

如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。

参考:维基百科

  1. 验证 HTTP Referer 字段
  2. 使用验证码
  3. 在请求地址中添加token并验证
  4. 在HTTP 头中自定义属性并验证
  5. AngularJS提供的CSRF方案

参考:https://blog.csdn.net/prstaxy/article/details/68940360

衍生问题:什么是token?struts2漏洞?

七、异常

74.throw 和 throws 的区别?

  1. Throw用于方法内部,Throws用于方法声明上
  2. Throw后跟异常对象,Throws后跟异常类型
  3. Throw后只能跟一个异常对象,Throws后可以一次声明多种异常类型

参考:https://www.nowcoder.com/questionTerminal/b769424fb8bf49d9bff97353c75d6f06

75.final、finally、finalize 有什么区别?

  1. final:用于申明属性,方法和类,表示属性不可变,方法不可以被覆盖,类不可以被继承。
  2. finally:是异常处理语句结构中,表示总是执行的部分。
  3. finallize:表示是object类一个方法,在垃圾回收机制中执行的时候会被调用被回收对象的方法。允许回收此前未回收的内存垃圾。所有object都继承了finalize()方法

参考:https://www.cnblogs.com/xwdreamer/archive/2012/04/17/2454178.html

76.try-catch-finally 中哪个部分可以省略?

catch和finally至少保留一个。

77.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

会。

参考:https://blog.csdn.net/qq_39135287/article/details/78455525

78.常见的异常类有哪些?

  1. java.lang.NullPointerException(空指针异常)
  2. java.lang.ClassNotFoundException
  3. java.lang.NumberFormatException
  4. java.lang.IndexOutOfBoundsException
  5. java.lang.ClassCastException
  6. java.lang.NoSuchFiledException
  7. java.lang.OutOfMemoryException

参考:http://www.cnblogs.com/ITtangtang/archive/2012/04/22/2465382.html

八、网络

79.http 响应码 301 和 302 代表的是什么?有什么区别?

  1. 301 redirect: 301 代表永久性转移(Permanently Moved)
  2. 302 redirect: 302 代表暂时性转移(Temporarily Moved )

301重定向的同时浏览器会修改url地址,302重定向的时候浏览器不会修改url地址。

参考:https://blog.csdn.net/grandPang/article/details/47448395

80.forward 和 redirect 的区别?

forward转发是服务器行为,redirect重定向是客户端行为。

1. 从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.

redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.

2. 从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.

3. 从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等

4. 从效率来说
forward:高.
redirect:低.

参考:https://www.cnblogs.com/Qian123/p/5345527.html

81.简述 tcp 和 udp的区别?

1:TCP基于连接,UDP基于无连接。
2:TCP对系统资源要求高,UDP少。
3:TCP是基于字节流的,UDP是数据报文模式。
4:TCP复杂,UDP简单。

tcp常用于文件传输,邮件等对数据准确度要求高的网络服务,udp常用于网络视频,网络电话等,数据偶尔丢包不影响,但速度必须快。

参考:https://zhuanlan.zhihu.com/p/24860273

82.tcp 为什么要三次握手,两次不行吗?为什么?

三次握手的目的:是为了确认双方都有收发数据的能力。

第一次: A->B,A说我要发消息了,证明A有发消息的能力。

第二次: B->A,B说成,你发吧,证明B有收消息,并且有发消息的能力。

第三次: A->B,A说,行,那我准备开始了。证明A有收消息的能力。

衍生问题:tcp的四次挥手

第一次:A->B,A说我要断开连接了。

第二次:B->A,B回A你前面发的文件我还没收完,你等我一下。

第三次:B->A,B再回A,我收完文件了,可以断开了。

第四次:A->B,OK,那断开吧。此时A和B互相确认完成,断开连接。

以上步骤缺少任何一步都会出现问题。

83.说一下 tcp 粘包是怎么产生的?

  1. 发送端需要等缓冲区满才发送出去,造成粘包
  2. 接收方不及时接收缓冲区的包,造成多个包接收

参考:https://blog.csdn.net/tiandijun/article/details/41961785

除了知道怎么产生的,也要知道如何解决。

84.OSI 的七层模型都有哪些?

第7层 应用层

第6层 表达层

第5层 会话层

第4层 传输层

第3层 网络层

第2层 数据链路层

第1层 物理层

参考:https://blog.csdn.net/yaopeng_2005/article/details/7064869

85.get 和 post 请求有哪些区别?

  1. 参数的位置、参数传递的安全性、参数是否可缓存、参数的编码方式……这些都是在使用习惯上的区别;
  2. 在HTTP协议的RPC(互联网规范)说明中,规范下的区别主要在于语义下的区别:GET用于获取资源,POST用于处理资源;
  3. 实质上,这也引出两者在HTTP协议下的METHOD特性方面的区别:1.安全(是否改变服务器状态)2.幂等(重复提交是否造成意外效果)3.可缓存性。

参考:https://www.zhihu.com/question/28586791/answer/145424285 中的评论

86.如何实现跨域?

在浏览器上当前访问的网站向另一个网站发送请求获取数据的过程就是跨域请求。

什么情况下的请求属于跨域参考:http://www.cnblogs.com/hustskyking/archive/2013/03/31/CDS-introduce.html

常用实现跨域:

  1. 图片ping或script标签跨域
  2. JSONP跨域
  3. CORS(跨域资源共享)
  4. window.name+iframe
  5. window.postMessage()

更多参考:https://blog.csdn.net/ligang2585116/article/details/73072868

87.说一下 JSONP 实现原理?

通过HTTP来动态添加<script>标签来调用服务器提供的js脚本,jquery封装的ajax jsonp请求也是通过动态添加script标签来实现的

明细参考:http://www.cnblogs.com/yunfeifei/p/4138632.html

九、设计模式

88.说一下你熟悉的设计模式?

这个是重点,几乎所有的面试都会问。

Java 最常见面试题 200+ 清单及自己整理的答案(上)-明亮

明细参考:https://www.cnblogs.com/pony1223/p/7608955.html

89.简单工厂和抽象工厂有什么区别?

也是重点。

工厂返回实体类实例,抽象工厂返回接口类型。

参考:https://www.jianshu.com/p/6d447cea14c7