在本篇文章开始之前,我想先来回答一个问题:我为什么要写这样一篇关于面试的文章?
原因有三个:第一,我想为每一个为梦想时刻准备着的“有心人”,尽一份自己的力量,提供一份高度精华的 Java 面试清单;第二,目前市面上的面试题不是答案不准确就是内容覆盖面太窄,所以提供一份经典而又准确的面试题是非常有必要的;第三,本文会对部分面试题提供详细解读和代码案例,让读者知其然并知其所以然,从而学到更多的知识。或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信你一定不会“败”的很惨。
凡事预则立,不预则废。能读到这里的人,我相信都是这个世界上的“有心人”,还是那句老话:上天不负有心人!我相信你的每一步努力,都会收获意想不到的回报。
本文分为十九个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、
Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis,RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM ,如下图所示:
JDK:Java Development Kit 的简称,Java开发工具包,提供了Java的开发环境和运行环境。JRE:Java Runtime Environment 的简称,Java运行环境,为Java的运行提供了所需环境。
具体来说JDK包含了JRE,同时还包含了编译Java源码的编译器Javac,还包含了很多Java程序调试和分析工具。简单来说:如果你需要运行Java程序,只需安装JRE就可以了,如果你需要编写Java程序,需要安装JDK。
2.==和equals的区别是什么?
==解读
对于基本类型和引用类型==的作用效果是不同的,如下所示
基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同;
代码示例:
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y);//true
System.out.println(x==z);//false
System.out.println(x.equals(y));//true
System.out.println(x.equals(z));//true
代码解读:因为x和y指向的是同一个引用,所以也是true,而new String()方法则重写开辟了内存空间,所以结果为false,而equals比较的一直是值,所以结果为true。
equals解读
equals本质上就是==,只不过String和Integer等重写了equals方法,把它变成了值比较。
首先看默认情况下equals比较一个有相同值的对象,代码如下:
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args){
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
}
输出结果出乎我们意料,竟然是false?这是怎么回事,看了equals源码就知道了,源码如下:
public boolean equals(Objectobj){
return (this==obj);
}
原来equals本质上就是==。
那问题来了,两个相同值的String对象,为什么返回的是true?代码如下:
String s1 = new String("老王");
String s2 = new String("老王");
System,out.println(s1.equals(s2));//true
同样的当我们进入String的equals方法,找到了答案,代码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
原来是String重写了Object的equals的方法,把引用比较改成了只比较。
总结:==对于基本类型来说值比较,对于引用类型来说比较的是引用;
而equals默认比较的是引用,只是很多类重写了equals方法,比如String,Integer等把它变成了值比较,所以一般情况下equals比较的是值是否相等。
不对,两个对象的hashCode()相同,equals不一定为true。
代码示例:
String str1 = "通话";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));
执行结果:
str1:1179395 | str2:1179395 false
代码解读:很显然“通话”和“重地”的hashCode()相同,然而equals()则为false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等并不一定能得出键值对相等。
4.final在java中有什么作用?final修饰的类叫最终类,该类不能被继承。final修饰的方法不能被重写final修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。 5.Java中的Math。round(-1.5)等于多少?
等于-1,因为在数轴上取值时,中间值(0.5)向右取整,所以正0.5是往上取整,负0.5直接舍弃。
6.String属于基础的数据类型吗?String不属于基础类型,基础类型有八种:byte,boolean,char,short,int,float,long,double,而String属于对象。
7.Java中操作字符串都有哪些类?他们之间有什么区别?操作字符串的类有:String,StringBuilder,StringBuffer。
String和StringBuilder,StringBuffer的区别在于String声明的是不可变的对象,每次操作都会生成新的String对象,然后将指针指向新的String对象,而StringBuilder,StringBuffer可以在原有对象的基础上进行操作,所以经常改变字符串内容的情况下尽量不要用String。StringBuffer和StringBuilder最大的区别在于StringBuffer是线程安全的,而StringBuilder是非线程安全的,但是StringBuilder的性能高于StringBuffer,所以在单线程的情况下建议使用StringBuilder,多线程环境下建议使用StringBuffer。
不一样,因为内存的分配方式不一样。String str = “i”的方式,java虚拟机会将其分配到字符串常量池中;而String str= new String(“i”)会被分配到堆内存中。
9.如何将字符串反转?使用StringBuilder或者StringBuffer的reverse()方法。
示例代码:
// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba
10.String类的常用方法有哪些?
indexOf():返回指定字符串的索引。charAt():返回指定索引处的字符。replace():字符串替换。trim():去除字符串两端空白。split():分割字符串,返回一个分割后的字符串数组。getBytes():返回字符串的byte类型数组。length():返回字符串的长度。toLowerCase():将字符串转成小写字母。toUpperCase():将字符串转成大写字母。subString():截取字符串。equals():字符串比较。 11.抽象类必须要有抽象方法吗?
不需要,抽象类不一定非要有抽象方法。
示例代码:
abstract class Cat {
public static void sayHi() {
System. out. println("hi~");
}
}
上面代码,抽象类并没有抽象方法,但可以正常运行。
12.普通类和抽象类有哪些区别?普通类不能包含抽象方法,抽象类可以包含抽象方法。抽象类不能直接实例化,普通类可以直接实例化。 13.抽象类可以被final修饰吗?
不能,定义抽象类就是让其它类继承的,如果定义为final该类就不能被继承,这样彼此就会产生矛盾,所以final不能修饰抽象类,如下所示,编辑器也会提示错误信息:
实现:抽象类的子类使用extends来继承;接口必须使用implements来实现接口。构造函数:抽象类可以有构造函数,接口不能有。实现数量:类可以实现很多个接口;但是只能继承一个抽象类。访问修饰符:接口种方法默认使用public修饰;抽象类中的方法可以使任意访问修饰符。 15.Java中的IO流分为几种?
按功能来分:输入流(input),输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按8位传输以字节为单位输入输出数据,字符流按16
位传输以字符为单位输入输出数据。
BIO:Block IO同步阻塞式IO,就是我们平常使用的传统是IO,它的特点是模式简单,使用方便,并发处理能力低。NIO:Non IO同步非阻塞式IO,是传统IO的升级,客户端和服务端通过Channel(通道)通讯,实现了多路复用。AIO:Asynchronous IO 是NIO的升级,也叫NIO2,实现了异步非阻塞式IO,异步IO的操作基于时间的回调机制。 17.Files的常用方法有哪些?
exists():检测文件路径是否存在。createFile():创建文件。createDirectory():创建文件夹。delete():删除一个文件和目录。copy():复制文件。move():移动文件。size():查看文件个数。read():读取文件。write():写入文件。 容器 18.Java容器都有哪些?
java容器分为Collection和Map两大类,旗下有有很多子类,如下所示:
CollectionList
- ArrayList
- linkedList
- Vector
- StackSet
- HashSet
- linkedHashSet
- TreeSetMapHashMap
- linkedHashMapTreeMapConcurrentHashMapHashtable
19.Collection和Collection有什么区别?
Collection是一个集合接口,他提供了对集合对象进行基本操作的通用接口方法,所有集合都是他的子类,比如List,Set等。Collections是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections.sort(list)。 20.List,Set,Map之间的区别是什么?
List,Set,Map的区别主要体现在两个方面:元素是否有序,是否允许元素重复。
三者之间的区别,如下表:
存储:HashMap运行Key和value为null而Hashtable不允许。线程安全:Hashtable是线程安全的,而HashMap是线程非安全的。推荐使用:在Hashtable的类注释可以看到,Hashtable是保留类,不建议使用,推荐单线程环境下使用HashMap来替代如果需要多线程使用则用ConcurrentHashMap。 22.如何决定使用HashMap还是TreeMap/
对于Map中插入,删除,定位一个元素这类操作,HashMap是最好的选择,因为相对而言HashMap会插入更快,但如果你要对一个key集合进行有序遍历,那TreeMap是最好的选择。
23.说一下HashMap的实现原理?HashMap基于hash算法实现的,我们通过put(key,value)存储,get(key)来获取。
当传入key是HashMap会更具key,hashCode()计算出hash值,根据hash值将value保存在bucket里,当计算出的hash值相同时,我们称之为hash冲突,HashMap的做法是用链表和红黑树来存储相同hash值的value,当hash冲突的个数比较少时,使用链表否则使用红黑树。
HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关HashSet的操作基本上都是直接调用底层HashMap的相关方法来实现的,HashSet不允许有重复的值。
25.ArrayList和linkedList的区别是什么?数据结构实现:ArrayList是动态数组的数据结构实现,而linkedList是双向链的结构数据实现。随机访问效率:Arraylist比linkedList在随机访问的时候效率高,因为linkedList是线性数据存储方式,所以需要移动指针从前往后依次查找。增加和删除效率:在非首尾的增加和删除操作,linkedList要比ArrayList效率高,因为ArrayList增删操作要影响数组内其他数据的下标。
综合来说在需要频繁读取集合中元素时,更推荐使用ArrayList而在插入和删除操作较多时,更推荐使用linkedList。
26.如何实现数组和List之间的转换?
数组转List:使用Array.asList(array)进行转换。List转数组:使用List自带的toArray方法。
代码示例:
// list to array ListArrayList和Vector的区别是什么?list = new ArrayList (); list. add("王磊"); list. add("的博客"); list. toArray(); // array to list String[] array = new String[]{"王磊","的博客"}; Arrays. asList(array);
线程安全:Vector使用了Synchronized来实现线程同步,是线程安全的,而ArrayList是线程非安全的。性能:ArrayList在性能方面由于Vector。扩容:ArrayList和Vector都会根据实际的需要动态的调整容量,只不过在Vector扩容每次会增加一倍,而ArrayList只会增加50%。 28.Array和ArrayList有何区别?
Array可以存储基本数据类型和对象,ArrayList只能存储对象。Array是指固定大小的,而Arraylist大小是自动扩展的。Array内置方法没有ArrayList多,比如addAll,removeAll,iteration等方法只有ArrayList有。 29.在Queue中poll()和remove()有什么区别
相同点:都是返回一个元素,并在队列中删除返回的对象。不同点:如果没有元素poll()会返回null,而remove()会直接抛出NoSuchElementException异常。
代码示例:
Queue30.那些集合类是线程安全的?queue = new linkedList (); queue. offer("string"); // add System. out. println(queue. poll()); System. out. println(queue. remove()); System. out. println(queue. size());
Vector,Hashtable,Stack都是线程安全的,而像HashMap是线程非安全的,不过在jdk1.5后随着Java.util.concurrent并发包的出现,他们也有了自己对应的线程安全类,比如HashMap对应的线程安全类是ConcurrentHashMap。
31.迭代器Iterator是什么?Iterator接口提供便利任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例,迭代器取代了java集合框架中的Enumeration,迭代器允许调用者在迭代过程中移除元素。
32.Iterator怎么使用?有什么特点?Iterator使用代码如下:
Listlist = new ArrayList<>(); Iterator it = list. iterator(); while(it. hasNext()){ String obj = it. next(); System. out. println(obj); }
Iterator的特点是更加安全,因为它可以确保,在当前遍历集合元素被更改的时候,就会抛出ConcurrentModificationException异常。
33Iterator和ListIterator有什么区别?Iterator可以遍历Set和List集合,而ListIterator只能遍历List。Iterator只能单向遍历,而ListIterator可以双向遍历(向前/向后遍历)。ListIterator从Iterator接口继承然后添加了一些额外的功能,比如添加一个元素,替换一个元素,获取前面元素或后面元素索引的位置。 34.怎么确保一个集合不能被修改?
可以使用CollectionsunmodifiableCollection(Collection c)方法来创建一个只读集合,这样改集合的任何操作都会抛出Java. lang. UnsupportedOperationException异常。
示例代码如下:
List多线程 35.并行和并发有什么区别?list = new ArrayList<>(); list. add("x"); Collection clist = Collections. unmodifiableCollection(list); clist. add("y"); // 运行时此行报错 System. out. println(list. size());
并行:多个处理器或多核处理器同时处理多个任务。并发:多个任务在同一个CPU核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
如下图:
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两台咖啡机。
36.线程和进程的区别?
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度
37.守护线程是什么?守护线程是守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。在java垃圾回收线程就是特殊的守护线程。
38.创建线程有哪几种方式?创建线程有三种方式:
继承Thread重写run()。实现Runnable接口实现Callable接口
说一下Runnable和Callable有什么区别?
runnable没有返回值,Callable可以拿到有返回值,Callable可以看做是Runnable的补充。
40.线程有哪些状态?
线程的状态:
new尚未启动runnable正在执行中blocked阻塞的(被同步锁或者IO锁阻塞)waiting永久等待状态timed_waiting等待指定时间重新被唤醒的状态terminated执行完成 41.sleep()和wait()有什么区别
类的不同:sleep()来自Thread,wait()来自Object。释放锁:sleep()不释放锁,wait()释放锁。用法不同:sleep()时间到会自动恢复,wait()可以使用notify()和notifyAll()直接唤醒 42.notif()y和notifyAll()有什么区别
nitifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll调用后,会将全部线程从等待池移到锁池,然后参与锁的竞争竞争成功则继续执行如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify()只会唤醒一个线程,具体唤醒哪一个有虚拟机控制。
43.现成的run()和start()有什么区别start方法用于启动线程,run()用于执行线程的运行时代码。run可以重复调用,而start只能调用一次。
44.创建线程池有哪几种方式线程池创建有7种方式,最核心的是最后一种:
newSingleThreadExecutor():它的特点在于工作线程数被限制为1,操作一个无界的工作队列,所以他保证了所有任务都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例因此,可以避免其改变其线程数目;



