JAVA面试题

面试题模块介绍

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

在这里插入图片描述

一、Java 基础
1.JDK 和 JRE 有什么区别?

答:JDK:Java Development Kit
JRE: Java Runtime Environment
JRE是运行时环境,包含java虚拟机,java基础类库。
JDK是java工具包,是程序员使用java语言编写java程序所需要的开发工具包,Jdk包含了jre,同时还包含了编辑java源码的编译器javac。
需要运行java程序,只需要安装JRE就可以了;如果需要编写java程序,需要安装JDK

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

答:"=="比较的事两个对象的地址, “equals”比较的是两个对象的内容

String str1=new String("apple");
String str2=new String("apple");
;;现在又两个boolean表达式
str1==str2            =>false
str1.equals(str2)  =>true
3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

答:不一定,hashcode()返回该对象的哈希码值,equals()返回两个对象是否相等。

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

final关键字可以用于三个地方:用于修饰类、类属性、类方法。

  • 被final修饰的类不能在被继承
    使用:除非真的该类需要设置为不能被继承,否则需要谨慎设置final类

  • 被final修饰的类属性
    无论属性是基本类型还是引用类型,final所起的作用都是变量里面存放的“值”不能变
    这个值,对于基本类型来说,变量里面放的就是实实在在的值,如1,“abc”等。
    而引用类型变量里面放的是个地址,所以用final修饰引用类型变量指的是它里面的地址不能变,并不是说这个地址所指向的对象或数组的内容不可以变,这个一定要注意
    例如:类中有一个属性是final Person p=new Person(“name”); 那么你不能对p进行重新赋值,但是可以改变p里面属性的值,p.setName(‘newName’)
    final修饰属性,声明变量时可以不赋值,而且一旦赋值就不能被修改了。对final属性可以在三个地方赋值:声明时、初始化块中、构造方法中。总之一定要赋值。

  • 被final修饰的类方法
    作用:可以被继承,但是继承后不能被重写

思考一个有趣的现象:

   byte b1=1;

   byte b2=3;

   byte b3=b1+b2;//当程序执行到这一行的时候会出错,因为b1、b2可以自动转换成int类型的变量,运算时java虚拟机对它进行了转换,结果导致把一个int赋值给byte-----出错

   如果对b1 b2加上final就不会出错

   final byte b1=1;

   final byte b2=3;

   byte b3=b1+b2;//不会出错,相信你看了上面的解释就知道原因了
5.java 中的 Math.round(-1.5) 等于多少?

答:Math.round(-1.5) 的返回值是-1。 但是Math.round(1.5)的返回值是2。
注:四舍六入五成双:
当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,”六”是指≥6时进上,”五”指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:①5前为奇数,舍5入1;②5前为偶数,舍5不进。(0是偶数)

Math.round(-1.5)   //=> 1
Math.round(1.5)  //=>2
6.String 属于基础的数据类型吗?

答:不是基础的数据类型。java中String是个对象,是引用类型。基础类型和引用类型的区别是:基础类型值表示简单的字符或者数字,引用类型可以是任何复杂的数据结构。java虚拟机处理基础类型与引用类型的方式是不一样的,对于基本类型,java虚拟机会为其分配数据类型实际占用的内存空间,而对于引用类型,他仅仅是一个指向堆区中某个实例的指针

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

答:String、StringBuffer、StringBuilder
String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。
StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。

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

答:不一样 ,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

9.如何将字符串反转?

答:1.通过jdk自带的reverse方法,注:String类本事么有反转类,需要包装成StringBuilder或者是StringBuffer

public class InvertString{
	public static void main(String [] args){
		String a="abcde";
		StringBuilder b=new StringBuider(a);
		Systom.out.print(b.reverse().toString());
}
}

思路2.运用递归的方法进行反转
假设反转的方法为reverseString(String str)
当字符串为空或者只有一个字符时,返回原字符,当字符串有两个以上(长度为len)的字符时,反转后的字符串为第二个字符开始的字符串+第一个字符串,即reverseString(str.subString(1))+str.charAt(0);
代码实现如下:

   public String reverseStringRecur(String str) {
        if ((str == null) || str.length() <2) return str;
        return  reverseString(str.subString(1))+str.charAt(0);
     }

思路3:非递归的方法
当字符串长度大于1时,把第一个字符和最后一个字符交换,把第二个字符和倒数第二个字符交换
需要设置两个标识符:begin, end。begin指向第一个字符,end指向最后一个字符
当begin<end, 交换第begin个字符和第end个元素的字符, 然后begin向后移动,end向前移动

代码实现如下:

public static String reverseString(String str) {
    if ((str == null) || str.length() <2) return str;
    char cArray[] = str.toCharArray();
    int begin = 0;
    int end = cArray.length-1;
    while(begin<end){
        char temp=cArray[begin];
        cArray[begin] = cArray[end];
        cArray[end] = temp;
        begin ++;
        end --;
    }
return new String(cArray);
}
10.String 类的常用方法都有那些?
  • 求字符串的长度:
String str = new String("asdfzxc");
int strlength = str.length();//strlength = 7
  • 提取字符串
    substring(int beginIndex),从当前字符串中取出剩余的字符作为一个新的字符串返回;
    substring(int beginIndex, int endIndex),从当前字符串中取出到endIndex-1位置的字符作为一个新的字符串返回。
String str1 = new String("asdfzxc");
String str2 = str1.substring(2);//str2 = "dfzxc"
String str3 = str1.substring(2,5);//str3 = "dfz"
  • 字符串比较
    int compareTo(String anotherString)/该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
    int compareToIgnore(String anotherString)//与compareTo方法相似,但忽略大小写
    boolean equalsIgnoreCase(String anotherString)//与equals方法相似,但忽略大小写
String str1 = new String("abc");
String str2 = new String("ABC");
int a = str1.compareTo(str2);//a>0
int b = str1.compareToIgnoreCase(str2);//b=0
boolean c = str1.equals(str2);//c=false
boolean d = str1.equalsIgnoreCase(str2);//d=true
  • 字符串连接
    String concat(String str)//将参数中的字符串str连接到当前字符串的后面,效果等价于"+"。
String str = "aa".concat("bb").concat("cc");
相当于String str = "aa"+"bb"+"cc";
  • 求字符串某一位置字符(根据位置查找字符)
    public char charAt(int index)//返回字符串中指定位置的字符;注意字符串中第一个字符索引是0,最后一个是length()-1。
String str = new String("asdfzxc");
char ch = str.charAt(4);//ch =z
  • 字符串中单个字符查找(根据字符查找位置)
    1)public int indexOf(int ch/String str)//用于查找当前字符串中字符或子串,返回字符或子串在当前字符串中从左边起首次出现的位置,若没有出现则返回-1。
    2)public int indexOf(int ch/String str, int fromIndex)//改方法与第一种类似,区别在于该方法从fromIndex位置向后查找。
    3)public int lastIndexOf(int ch/String str)//该方法与第一种类似,区别在于该方法从字符串的末尾位置向前查找。
    4)public int lastIndexOf(int ch/String str, int fromIndex)//该方法与第二种方法类似,区别于该方法从fromIndex位置向前查找。
String str = "I am a good student";
int a = str.indexOf('a');//a = 2
int b = str.indexOf("good");//b = 7
int c = str.indexOf("w",2);//c = -1
int d = str.lastIndexOf("a");//d = 5
int e = str.lastIndexOf("a",3);//e = 2
  • String[] split(String str)//将str作为分隔符进行字符串分解,分解后的字字符串在字符串数组中返回。
String str = "asd!qwe|zxc#";
String[] str1 = str.split("!|#");//str1[0] = "asd";str1[1] = "qwe";str1[2] = "zxc";

其他的请参考:https://blog.csdn.net/Amdrose/article/details/89459782

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

答:不一定。抽象类可以不包含抽象方法,但是有抽象方法的类必须是抽象类
抽象类是指用abstract关键字定义的,不允许被实例化的类;抽象方法是没有方法体的方法。

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

答:1)抽象类用abstract关键字声明,而普通类是public、parivate声明
2)抽象类里的方法不能有方法的主体, 只能是方法的声明,例如 abstract void AMetho,而普通类的方法可以有主体。
3)抽象类被继承时、子类必须实现它的所有方法,而普通类不需要;
4)抽象类的方法在扩展性和延伸性方面要比普通类的好;
5)抽象类可以应用多态,但是普通类不可以。

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

答:不能,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。

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

共同点:
1)都不能实例化,都位于继承树的顶端,用于被其他的类实现和继承
2)都包含抽象方法,实现接口和继承抽象类的普通子类都必须实现抽象方法

区别:
1)在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象类的优势;接口中只能有抽象的方法。所以接口里不能定义静态方法,而抽象类中可以。
2)一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接口,可以弥补Java单继承的不足。另外一个接口可已继承多个接口。
3)接口里只能定义静态常量属性,不能定义普通属性;抽象类里二者都可以定义。
4)接口不能包含构造器和初始化块,抽象类中可以包含。
5)二者在设计目的上存在较大差异,接口体现的是一种规范和实现分离的思想,充分利用接口,可以极好地降低程序各模块之间的耦合,从而提高系统的可扩展性和可维护性。接口相当于是一种规范,他制订了程序各模块需要遵循的标准,因此系统中的一个接口不应该经常改变,一旦接口改变,对整个系统的影响是辐射型的,导致系统大部分类都需要改写。
抽象类则不同,抽象类作为系统中多个子类的父类,它体现的是一种模板式设计。抽象类作为多个子类的父类,可以被认为是一种中间产品,这种中间产品已经实现个部分功能,但是仍不能当成最终产品,需要进一步的完善,这种完善可以有几种不同的实现。

15.java 中 IO 流分为几种?

答:1)按照流的流向分,可以分为输入流和输出流;
2)按照操作单元划分,可以划分为字节流和字符流;
3)按照流的角色划分为节点流和处理流。
4)InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
5)OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

  • 字符流和字节流的区别
    1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
    3)字节流:一次读入或读出是8位二进制。通过字节的形式一个字节一个字节或者字节数组来操作文件中内容,可以操作一切文件。
    4)字符流:一次读入或读出是16位二进制。通过单个字符或者是字符数组的形式来操作文件的,存在一定的局限性,是专门用于对文本文件操作的,默认的版本为GBK
16.BIO、NIO、AIO 有什么区别?

IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
BIO:
数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,
当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的(高并发问题不好处理)
NIO:

详情见:https://blog.csdn.net/skiof007/article/details/52873421

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

Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。

二、容器

容器的相关基础知识见:https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E5%AE%B9%E5%99%A8.md
推荐一个java面试题,很不错 :https://github.com/JGPY/JavaGuideBooster

18.java 容器都有哪些?

答:java容器主要包括Cpllection和Map两种,Collection存储着对象的集合,Map存储着键值对(两个对象)的映射表。

  • Collection:

在这里插入图片描述

  • Map:

在这里插入图片描述

19.Collection 和 Collections 有什么区别?

答:1)Collection是集合类的上级接口,继承与他相关的接口都有List和Set
2)Collections是针对集合类的一个帮助类,提供了一系列方法实现对各种集合的搜索、排序、线程安全等操作
Collections的主要方法有:混排(Shuffling)、反转(Reverse)、替换所有的元素(fill)、拷贝(copy)、返回Collections中最小元素(min)、返回Collections中最大元素(max)、返回指定源列表中最后一次出现指定目标列表的起始位置(lastIndexOfSubList)、返回指定源列表中第一次出现指定目标列表的起始位置(IndexOfSubList)、根据指定的距离循环移动指定列表中的元素(Rotate)

20.List、Set、Map 之间的区别是什么?(其实面试的时候听到这个问题的时候,你要知道,面试官是想考察List,Set

答:
List:
1)可以允许重复的对象
2)可以允许多个null元素
3)是一个而有序容器,保持了每个元素插入的顺序,输出的顺序就是插入的顺序
4)常用的而实现类是ArrayList、LinkedList、Vector。ArrayList最为流行,提供了使用索引随意的访问,而LinkedList则对于经常需要从List进行插入和删除元素的场合更合适。
Set:
1)不允许重复对象
2)只允许一个null元素
3)是一个无序容器,treeSet通过Comparator或者Comparable维护了一个排序顺序
4)常用的额几个实现类是HashSet、LinkedHashSet、TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器
Map:
1)Map不是Collection的子接口或者实现类,Map是一个接口
2)Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
3)TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4)Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
5)Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

详情见:https://www.cnblogs.com/IvesHe/p/6108933.html

21.HashMap 和 Hashtable 有什么区别?

答:1)作者不同:HashMa多了一个并发大神Doug Lea
2)产生时间不同:Hashtable是java一开始发布时就提供的键值映射的数据结构,而HashMap产生于JDK1.2。现在更多的是HashMap
3)继承的父类不同:HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
4)对外提供的接口不同:Hashtable比HashMap多提供了elments() 和contains() 两个方法
5)对Null key 和Null value的支持不同:HashTable既不支持null键也不支持null值,HashMap可以允许null为键,但是这样的键只能有一个,null值可以有多个。
6)线程安全性不同:HashTable是线程安全的,它的每个方法中都加入了Synchronize方法;HashMap是非线程安全的,在多线程并发的情况下回产生死锁问题。
7)遍历方式的内部实现上不同:Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式
8) 初始容量大小和每次扩充容量大小的不同:Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
详情见:https://blog.csdn.net/wangxing233/article/details/79452946

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

答:HashMap的底层是哈希数据结构,TreeMap底层是二叉树数据结构
TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现也是基于红黑树结构。
而HashMap<K,V>的Key值实现散列hashCode(),分布是散列的均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。
大多情况下HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap.

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

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。

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

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值

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

答:相同:ArrayList与LinkedList都是List接口的实现类
区别:
1)List接口的实现方式不同:ArrayList是以数组 方式实现的,LinkedList是以链表的方式实现的。
2)随机访问ge()t和set()操作效率,ArrayList比LinkedList效果好,因为LinkedList要移动指针
3)数据的插入和删除操作效率,LinkedList比ArrayList效果好,因为ArrayList要移动数据

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

答:
1)List转换成数组可以使用List的toArray()方法,返回一个Object数组

 List list = new ArrayList();
      // 当list中的数据类型都一致时可以将list转化为数组
      Object[] array = list.toArray();
       // 分配一个长度与list的长度相等的字符串数组
      String[] array2 = (String[]) list.toArray(new String[list.size()])

2)数组转换成List可以使用Array的asList()方法,返回一个List

	 List list = new ArrayList();
     // 将数组转换成list
      for (int i = 0; i < array.length; i++) {
         list.add(array[i]);
      }
      // 直接使用Arrays的asList方法
      list = Arrays.asList(array);

3)Set转换成数组可以使用Set的toArray方法,返回一个Object数组

      Set set = new HashSet();
      set.add("a");
      set.add("b");
      // 将set转换为数组
      array = set.toArray();
      array1 = (String[]) set.toArray(new String[0]);
      array2 = (String[]) set.toArray(new String[set.size()]);

4)数组转化成Set时,需要先将数组转化成List再用List构造Set

      // 数组转换成Set
      // 将数组转换成List后,再用List构造Set
      set = new HashSet(Arrays.asList(array));
      //把数组转换成的list全部add
       set.addAll(Arrays.asList(array1));
27.ArrayList 和 Vector 的区别是什么?

答:1)Vector是线程安全的,ArrayList不是线程安全的,Vector的方法都多了synchronized关键字,来保证线程的安全性。
2)ArrayList在底层数组不够用时在原来的基础上扩展0.5倍,Vector是扩展1倍。
详情见:https://zhuanlan.zhihu.com/p/28241176

28.Array 和 ArrayList 有何区别?

答:1)存储内容不同:Array包含基础类型和对象类型,Arraylist包含对象类型
2)空间大小不同:Array大小是固定的,ArrayList的大小是动态变化的。
4)方法上的比较:ArrayList作为Array的增强版,当然是在方法上比Array更多样化,比如添加全部addAll()、删除全部removeAll()、返回迭代器iterator()等
适用场景:若程序运行期间数据永远保持不变的,放到一个全局数组里,选择Array;
若只是单纯以数组的形式保存数据,并且不对数据进行增加、删除操作,只是方便查找的话,选择Arraylist;
若是需要对元素进行频繁的增加和删除操作的话,或者处理超大量的数据的时候,最好不要选择ArrayList,因为它的效率低。

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

答:相同点:都是返回第一个元素,并在队列中删除返回的对象。
不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常

Queue<String> queue = new LinkedList<String>();
queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());
30.哪些集合类是线程安全的?

答:vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全,现在很少用
enumeration:枚举,相当于迭代器
注:线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低。

31.迭代器 Iterator 是什么?

答:迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

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

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

 list l = new ArrayList();
 l.add("aa");
 l.add("bb");
 l.add("cc");
 for (Iterator iter = l.iterator(); iter.hasNext();) {
  String str = (String)iter.next();
  System.out.println(str);
 }
 /*迭代器用于while循环
 Iterator iter = l.iterator();
 while(iter.hasNext()){
  String str = (String) iter.next();
  System.out.println(str);
 }
 */
33.Iterator 和 ListIterator 有什么区别?

答:
相同:
1)都可以取得迭代器,都有hasNext()和next()方法,可以实现顺序向后遍历
2)都可以删除对象。
区别:
1)ListIterator可以用add()方法向list中添加对象,但Iterator不可以
2)ListIterator可以用set()方法实现对象的修改,但Iterator不可以
3)ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
4) ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能

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

答:集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。
可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。

利用Collections和Guava提供的类可实现的不可变对象
Collections.unmodifiableXXX:Collection、List、Set、Map…;
Guava:ImmutableXXX:Collection、List、Set、Map…

Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
三、多线程

线程同步问题:https://blog.csdn.net/qq_35114086/article/details/52621841

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

答:1)并行是同一个时刻执行多个事件,并发是同一个时间段内执行多个事件
2)并行发生在不同的实体上,并发发生在同一个实体上。
注:
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。

你吃饭吃到一半,电话来了,你一边打电话一边吃饭表示同一时刻发生的事情,这是并行;
你吃饭吃到一半,电话来了,你接完点好,再回来吃饭表示同一时间段内发生的事情,这是并发;

36.线程和进程的区别?

答:
1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
详情见:https://www.jianshu.com/p/a4fa4edbeb8a

37.守护线程是什么?

答:在java中有两类线程,一种是用户线程(User Thread),一种是守护线程(Daemon Thread)。用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆。

  • 1)定义

守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。

  • 2)优先级

守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。

  • 3)设置

通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为
守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。
example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的
Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是
JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于
实时监控和管理系统中的可回收资源。

  • 4)生命周期

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且
周期性地执行某种任务或等待处理某些发生的事件。也就是
说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是
什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个
或以上的非守护线程则JVM不会退出。

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

答:有三种实现线程的方法:

实现Runnable接口、实现 Callable 接口、继承 Thread 类

实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的

1)实现Runnable接口
需要实现 run() 方法。
通过 Thread 调用 start() 方法来启动线程

public class MyRunnable implements Runnable {
    public void run() {
        // ...
    }
}
public static void main(String[] args) {
    MyRunnable instance = new MyRunnable();
    Thread thread = new Thread(instance);
    thread.start();
}

2)实现 Callable 接口
与 Runnable 相比,需要实现call()方法,Callable 可以有返回值,返回值通过 FutureTask 进行封装。

public class MyCallable implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable mc = new MyCallable();
    FutureTask<Integer> ft = new FutureTask<>(mc);
    Thread thread = new Thread(ft);
    thread.start();
    System.out.println(ft.get());
}

3)继承 Thread 类
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。

public class MyThread extends Thread {
    public void run() {
        // ...
    }
}
public static void main(String[] args) {
    MyThread mt = new MyThread();
    mt.start();
}

实现接口 VS 继承 Thread

实现接口会更好一些,因为:

  • Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
  • 类可能只要求可执行就行,继承整个 Thread 类开销过大
39.说一下 runnable 和 callable 有什么区别?

答:区别:
1)runable的实现方法是start(),callable的实现方法是call()
2)runable不可能有返回值,但callable可以有返回值,返回值通过 FutureTask 进行封装
3)run()方法不可以抛出异常,但call()方法可以抛出异常

//run()接口
public void run();
//call()接口
public Object call() throws Exception;
40.线程有哪些状态?

答:线程状态有新建(new)、可运行(runable)、阻塞(block)、无限期等待(waiting)、限时等待(time waiting)、死亡(terminated)
在这里插入图片描述

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

答:1)最本质区别是:sleep()不释放同步锁,wait()释放同步锁
2)使用位置不同:sleep()可以在任何地方使用,但是wait()、notify()、notifyAll()只能在同步控制方法或者同步控制块中使用
3)来自的类不同:sleep()是Thread类的方法,wait()是Object类的方法

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

答:先了解概念:锁池和等待池
锁池:若线程a已经拥有了某个对象的锁,而其他的线程想要访问这个对象的某个synchronized方法或者synchronized块。由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

注:等待池的线程不会去竞争该对象的锁,只有锁池中的线程才会去竞争对象的锁

notify()和notifyAll作用:把等待池的线程唤醒到锁池中。 但是有个问题,每次从等待池中唤醒几个线程到锁池呢?所以notify和notifyAll就有区别了

notyfy()一次只能唤醒一个线程,notyfyAll一次唤醒所有线程

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

答:调用 start() 方法才会启动新线程;如果直接调用 Thread 的 run() 方法,它的行为就会和普通的方法一样

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

答:1)两种显式创建线程池的方式

  • 直接使用ThreadPoolExecutor类直接创建一个线程池,这种线程池需要设置具体的参数
  • 可以使用Executor类创建,Executor类是java.util.concurrent提供的的一个线程吃的工厂类,使用该类可以方便的创建线程池,此类提供的几种方法,支持创建四中类型的线程池,分别是:newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor.

为什么很多 Java 规范都建议不要显式的创建 Thread,而使用线程池?

1)因为使用线程池的好处是减少在创建和销毁线程上所消耗的时间和系统资源,解决资源不足的问题,如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者过渡切换问题

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

答:1)五种线程池状态
running运行状态:处于运行转态,可以接受新任务并处理
shutdown关闭状态:处于关闭转态,不会接受新的任务,但是会处理队列中还存在的任务
stop停止状态:处于停止状态,不会接受新的任务 ,也不处理队列任务,直接中断
tidying:表示所有任务都已经终止了
terminated:表示terminated()方法已经执行完成

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

答:1)接收参数不一样
2)返回值不一样,submit()有返回值,但execute()没有返回值

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

答:1)原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized)

2)可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile)

3)有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)

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

答:锁的级别:
无锁–>偏向锁–>轻量级锁–>重量级锁

锁分级别的原因:
没有优化前,sychroniezed是重量级锁(悲观锁),使用wait、notify、notifyAll来切换线程状态非常消耗系统资源,线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以JVM对sychronized关键字进行了优化,把锁分为无锁、偏向锁、轻量级锁、重量级锁

一、无锁

没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其它修改失败的线程会不断重试直到修改成功。

二、偏向锁

对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续执行中自动获取锁,降低获取锁带来的性能开销。偏向锁,指的是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。

偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停偏向锁的线程,然后判断锁对象是否处于被锁定状态,如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁。

如果线程处于活动状态,升级为轻量级锁的状态。

三、轻量级锁

轻量级锁是指当锁是偏向锁的时候,被第二个线程B访问,此时偏向锁就会升级为轻量级锁,线程B会通过自旋的形式尝试获取锁,线程不会阻塞,从未提升性能。

当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定次数时,轻量级锁边会升级为重量级锁,当一个线程已持有锁,另一个线程在自旋,而此时第三个线程来访时,轻量级锁也会升级为重量级锁。

注:自旋是什么?

自旋(spinlock)是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

四、重量级锁

指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁通过对象内部的监听器(monitor)实现,而其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

五、锁状态对比:
在这里插入图片描述

六、CAS是什么呢?

CAS即即比较和替换,当使用CAS替换新值不成功时,自旋,重新获得原值和新值再试一次直到成功为止。
CAS通过无锁操作提高了系统的吞吐率,高效的解决了原子操作问题。

49.什么是死锁?

答:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

50.怎么防止死锁?

答:产生死锁的四个必要条件:

1)互斥条件:指进程对锁分配到的资源进行排他性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其他进程请求资源,则请求者只能等待,知道占有资源的进程用毕释放。

2)请求和保持条件:指进程已经至少一个资源,但是又提出了新的资源请求,而该资源已经被其他进程占用,此时请求进程阻塞,但对自己已获得的其他资源保持不放

3)不剥夺条件:指进程已获得资源,在未使用之前,不能被剥夺,只能在使用完成时由自己释放

4)环路等待条件:指发送死锁时,必然存在一个进程-----资源的环形链,即进程集合{p0,p1,p2,…,pn}中的p0正在等待p1占用的资源;p1等待p2占用的资源,…,pn正在等待已经被p0占用的资源

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

我们可以通过破坏死锁产生的4个必要条件来预防死锁:

1)由于资源互斥是资源使用的固有特性是无法改变的。

2)破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。

3)破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。

4)破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。

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

答:ThreadLocal是线程Thread中属性threadLocals的管理者。

52.说一下 synchronized 底层实现原理?
53.synchronized 和 volatile 的区别是什么?
54.synchronized 和 Lock 有什么区别?
55.synchronized 和 ReentrantLock 区别是什么?
56.说一下 atomic 的原理?
四、反射
57.什么是反射?

答:反射是:指程序可以访问、检测和修改它本身状态或行为一种能力

反射是一种能力,所以给的定义就是说明了它能干嘛。

我们平时用反射主要做:

  • 获取类型的相关信息
  • 动态调用方法
  • 动态构造对象
  • 从程序集中获得类型。
    详情见:https://www.cnblogs.com/zhaopei/p/reflection.html
58.什么是 java 序列化?什么情况下需要序列化?

答:1)序列化:将 Java 对象转换成字节流的过程。

反序列化:将字节流转换成 Java 对象的过程。

2)当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化
注意事项:

  • 某个类可以被序列化,则其子类也可以被序列化
  • 声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient
    表示临时数据
  • 反序列化读取序列化对象的顺序要保持一致

详情见:https://blog.csdn.net/meism5/article/details/90413987

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

答:1)代理类在程序运行时创建的代理方式被成为 动态代理。在静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法

2)动态代理的应用:Spring的AOP,加事务,加权限,加日志。

60.怎么实现动态代理?

答:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
详情见:https://www.cnblogs.com/gonjan-blog/p/6685611.html

五、对象拷贝

61.为什么要使用克隆?

62.如何实现对象克隆?

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

六、Java Web

64.jsp 和 servlet 有什么区别?

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

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

67.session 和 cookie 有什么区别?

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

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

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

71.如何避免 sql 注入?

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

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

七、异常

74.throw 和 throws 的区别?

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

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

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

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

八、网络

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

80.forward 和 redirect 的区别?

81.简述 tcp 和 udp的区别?

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

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

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

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

86.如何实现跨域?

87.说一下 JSONP 实现原理?

九、设计模式

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

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

十、Spring/Spring MVC

90.为什么要使用 spring?

91.解释一下什么是 aop?

92.解释一下什么是 ioc?

93.spring 有哪些主要模块?

94.spring 常用的注入方式有哪些?

95.spring 中的 bean 是线程安全的吗?

96.spring 支持几种 bean 的作用域?

97.spring 自动装配 bean 有哪些方式?

98.spring 事务实现方式有哪些?

99.说一下 spring 的事务隔离?

100.说一下 spring mvc 运行流程?

101.spring mvc 有哪些组件?

102.@RequestMapping 的作用是什么?

103.@Autowired 的作用是什么?

十一、Spring Boot/Spring Cloud

104.什么是 spring boot?

105.为什么要用 spring boot?

106.spring boot 核心配置文件是什么?

107.spring boot 配置文件有哪几种类型?它们有什么区别?

108.spring boot 有哪些方式可以实现热部署?

109.jpa 和 hibernate 有什么区别?

110.什么是 spring cloud?

111.spring cloud 断路器的作用是什么?

112.spring cloud 的核心组件有哪些?

十二、Hibernate

113.为什么要使用 hibernate?

114.什么是 ORM 框架?

115.hibernate 中如何在控制台查看打印的 sql 语句?

116.hibernate 有几种查询方式?

117.hibernate 实体类可以被定义为 final 吗?

118.在 hibernate 中使用 Integer 和 int 做映射有什么区别?

119.hibernate 是如何工作的?

120.get()和 load()的区别?

121.说一下 hibernate 的缓存机制?

122.hibernate 对象有哪些状态?

123.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

124.hibernate 实体类必须要有无参构造函数吗?为什么?

十三、Mybatis

125.mybatis 中 #{}和 ${}的区别是什么?

126.mybatis 有几种分页方式?

127.RowBounds 是一次性查询全部结果吗?为什么?

128.mybatis 逻辑分页和物理分页的区别是什么?

129.mybatis 是否支持延迟加载?延迟加载的原理是什么?

130.说一下 mybatis 的一级缓存和二级缓存?

131.mybatis 和 hibernate 的区别有哪些?

132.mybatis 有哪些执行器(Executor)?

133.mybatis 分页插件的实现原理是什么?

134.mybatis 如何编写一个自定义插件?

十四、RabbitMQ

135.rabbitmq 的使用场景有哪些?

136.rabbitmq 有哪些重要的角色?

137.rabbitmq 有哪些重要的组件?

138.rabbitmq 中 vhost 的作用是什么?

139.rabbitmq 的消息是怎么发送的?

140.rabbitmq 怎么保证消息的稳定性?

141.rabbitmq 怎么避免消息丢失?

142.要保证消息持久化成功的条件有哪些?

143.rabbitmq 持久化有什么缺点?

144.rabbitmq 有几种广播类型?

145.rabbitmq 怎么实现延迟消息队列?

146.rabbitmq 集群有什么用?

147.rabbitmq 节点的类型有哪些?

148.rabbitmq 集群搭建需要注意哪些问题?

149.rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?

150.rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?

151.rabbitmq 对集群节点停止顺序有要求吗?

十五、Kafka

152.kafka 可以脱离 zookeeper 单独使用吗?为什么?

153.kafka 有几种数据保留的策略?

154.kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?

155.什么情况会导致 kafka 运行变慢?

156.使用 kafka 集群需要注意什么?

十六、Zookeeper

157.zookeeper 是什么?

158.zookeeper 都有哪些功能?

159.zookeeper 有几种部署模式?

160.zookeeper 怎么保证主从节点的状态同步?

161.集群中为什么要有主节点?

162.集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?

163.说一下 zookeeper 的通知机制?

十七、MySql

164.数据库的三范式是什么?

165.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?

166.如何获取当前数据库版本?

167.说一下 ACID 是什么?

168.char 和 varchar 的区别是什么?

169.float 和 double 的区别是什么?

170.mysql 的内连接、左连接、右连接有什么区别?

171.mysql 索引是怎么实现的?

172.怎么验证 mysql 的索引是否满足需求?

173.说一下数据库的事务隔离?

174.说一下 mysql 常用的引擎?

175.说一下 mysql 的行锁和表锁?

176.说一下乐观锁和悲观锁?

177.mysql 问题排查都有哪些手段?

178.如何做 mysql 的性能优化?

十八、Redis

179.redis 是什么?都有哪些使用场景?

180.redis 有哪些功能?

181.redis 和 memecache 有什么区别?

182.redis 为什么是单线程的?

183.什么是缓存穿透?怎么解决?

184.redis 支持的数据类型有哪些?

185.redis 支持的 java 客户端都有哪些?

186.jedis 和 redisson 有哪些区别?

187.怎么保证缓存和数据库数据的一致性?

188.redis 持久化有几种方式?

189.redis 怎么实现分布式锁?

190.redis 分布式锁有什么缺陷?

191.redis 如何做内存优化?

192.redis 淘汰策略有哪些?

193.redis 常见的性能问题有哪些?该如何解决?

十九、JVM

194.说一下 jvm 的主要组成部分?及其作用?

195.说一下 jvm 运行时数据区?

196.说一下堆栈的区别?

197.队列和栈是什么?有什么区别?

198.什么是双亲委派模型?

199.说一下类加载的执行过程?

200.怎么判断对象是否可以被回收?

201.java 中都有哪些引用类型?

202.说一下 jvm 有哪些垃圾回收算法?

203.说一下 jvm 有哪些垃圾回收器?

204.详细介绍一下 CMS 垃圾回收器?

205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

206.简述分代垃圾回收器是怎么工作的?

207.说一下 jvm 调优的工具?

208.常用的 jvm 调优的参数都有哪些?

原文:https://blog.csdn.net/sufu1065/article/details/88051083

github 面试题:https://github.com/Snailclimb/JavaGuide

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值