本文包含了String、包装类型、反射、泛型、序列化、异常和IO的常见面试题。
本文收录于《面试小抄》系列,Github地址:https://github.com/cosen1024/Java-Interview
包含了Java基础、Java并发、JVM、MySQL、Redis、Spring、MyBatis、Kafka、操作系统和计算机网络等的常考面试题。
这是本期的 Java 基础面试题目录,不会的快快查漏补缺~
- 1. 字符型常量和字符串常量的区别?
- 2. 什么是字符串常量池?
- 3. String str="aaa"与 String str=new String(“aaa”)一样吗?
new String(“aaa”);
创建了几个字符串对象? - 4. String 是最基本的数据类型吗?
- 5. String有哪些特性?
- 6. 在使用 HashMap 的时候,用 String 做 key 有什么好处?
- 7. 包装类型是什么?基本类型和包装类型有什么区别?
- 8. 解释一下自动装箱和自动拆箱?
- 9. int 和 Integer 有什么区别?
- 10. 两个new生成的Integer变量的对比
- 11. Integer变量和int变量的对比
- 12. 非new生成的Integer变量和new Integer()生成变量的对比
- 13. 两个非new生成的Integer对象的对比
- 14. 什么是反射?
- 15. 反射机制的优缺点有哪些?
- 16. 如何获取反射中的Class对象?
- 17. Java反射API有几类?
- 18. 反射使用的步骤?
- 19. 为什么引入反射概念?反射机制的应用有哪些?
- 20. 反射机制的原理是什么?
- 21. Java中的泛型是什么 ?
- 22. 使用泛型的好处是什么?
- 23. Java泛型的原理是什么 ? 什么是类型擦除 ?
- 24. 什么是泛型中的限定通配符和非限定通配符 ?
- 25. List<? extends T>和List <? super T>之间有什么区别 ?
- 26. 可以把List
<String>
传递给一个接受List<Object>
参数的方法吗? - 27. 判断
ArrayList<String>
与ArrayList<Integer>
是否相等? - 28. Java序列化与反序列化是什么?
- 29. 为什么需要序列化与反序列化?
- 30. 序列化实现的方式有哪些?
- 31. 什么是serialVersionUID?
- 32. 为什么还要显示指定serialVersionUID的值?
- 33. serialVersionUID什么时候修改?
- 34. Java 序列化中如果有些字段不想进行序列化,怎么办?
- 35. 静态变量会被序列化吗?
- 36. Error 和 Exception 区别是什么?
- 37. 非受检查异常(运行时异常)和受检查异常(一般异常)区别是什么?
- 38. throw 和 throws 的区别是什么?
- 39. NoClassDefFoundError 和 ClassNotFoundException 区别?
- 40. Java常见异常有哪些?
- 41. try-catch-finally 中哪个部分可以省略?
- 42. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
- 43. JVM 是如何处理异常的?
- 44. Java的IO 流分为几种?
- 45. 字节流如何转为字符流?
- 46. 字符流与字节流的区别?
- 47. 什么是阻塞IO?什么是非阻塞IO?
- 48. BIO、NIO、AIO的区别?
- 49. Java IO都有哪些设计模式?
- 参考
坐稳,要发车了~
1. 字符型常量和字符串常量的区别?
-
形式上: 字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符;
-
含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算;字符串常量代表一个地址值(该字符串在内存中存放位置,相当于对象;
-
占内存大小:字符常量只占2个字节;字符串常量占若干个字节(至少一个字符结束标志) (注意: char 在Java中占两个字节)。
2. 什么是字符串常量池?
java中常量池的概念主要有三个:全局字符串常量池
,class文件常量池
,运行时常量池
。我们现在所说的就是全局字符串常量池
,对这个想弄明白的同学可以看这篇Java中几种常量池的区分。
jvm为了提升性能和减少内存开销,避免字符的重复创建,其维护了一块特殊的内存空间,即字符串池,当需要使用字符串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用,如果不存在,初始化,并将该字符串放入字符串常量池中。
字符串常量池的位置也是随着jdk版本的不同而位置不同。在jdk6中,常量池的位置在永久代(方法区)中,此时常量池中存储的是对象。在jdk7中,常量池的位置在堆中,此时,常量池存储的就是引用了。在jdk8中,永久代(方法区)被元空间取代了。
3. String str="aaa"与 String str=new String(“aaa”)一样吗?new String(“aaa”);
创建了几个字符串对象?
- 使用
String a = “aaa” ;
,程序运行时会在常量池中查找”aaa”字符串,若没有,会将”aaa”字符串放进常量池,再将其地址赋给a;若有,将找到的”aaa”字符串的地址赋给a。 - 使用String b = new String(“aaa”);`,程序会在堆内存中开辟一片新空间存放新对象,同时会将”aaa”字符串放入常量池,相当于创建了两个对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。
具体分析,见以下代码:
@Test
public void test(){
String s = new String("2");
s.intern();
String s2 = "2";
System.out.println(s == s2);
String s3 = new String("3") + new String("3");
s3.intern();
String s4 = "33";
System.out.println(s3 == s4);
}
运行结果:
jdk6
false
false
jdk7
false
true
这段代码在jdk6中输出是false false
,但是在jdk7中输出的是false true
。我们通过图来一行行解释。
先来认识下intern()函数:
intern函数的作用是将对应的符号常量进入特殊处理,在JDK1.6以前 和 JDK1.7以后有不同的处理;
在JDK1.6中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,则将该字符串常量加入到字符串常量区,也就是在字符串常量区建立该常量;
在JDK1.7中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,说明该字符串常量在堆中,则处理是把堆区该对象的引用加入到字符串常量池中,以后别人拿到的是该字符串常量的引用,实际存在堆中
JDK1.6
String s = new String("2");
创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“2”对象。
s.intern();
在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象2的地址。
String s2 = "2";
使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"2"的地址。
System.out.println(s == s2);
从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false
String s3 = new String("3") + new String("3");
创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“3”对象。中间还有2个匿名的new String(“3”)我们不去讨论它们。
s3.intern();
在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,在常量池中创建“33”对象,返回“33”对象的地址。
String s4 = "33";
使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"33"的地址。
System.out.println(s3 == s4);
从上面可以分析出,s3变量和s4变量地址指向的是不同的对象,所以返回false
JDK1.7
String s = new String("2");
创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“2”对象,并在常量池中保存“2”对象的引用地址。
s.intern();
在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象“2”的引用地址。
String s2 = "2";
使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象“2”的引用地址。
System.out.println(s == s2);
从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false
String s3 = new String("3") + new String("3");
创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“3”对象,并在常量池中保存“3”对象的引用地址。中间还有2个匿名的new String(“3”)我们不去讨论它们。
s3.intern();
在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,将s3对应的StringObject对象的地址保存到常量池中,返回StringObject对象的地址。
String s4 = "33";
使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回其地址,也就是StringObject对象的引用地址。
System.out.println(s3 == s4);
从上面可以分析出,s3变量和s4变量地址指向的是相同的对象,所以返回true。
4. String 是最基本的数据类型吗?
不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。
5. String有哪些特性?
-
不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性;
-
常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用;
-
final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。
6. 在使用 HashMap 的时候,用 String 做 key 有什么好处?
HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。
7. 包装类型是什么?基本类型和包装类型有什么区别?
Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,把基本类型转换成包装类型的过程叫做装箱(boxing);反之,把包装类型转换成基本类型的过程叫做拆箱(unboxing),使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
基本类型和包装类型的区别主要有以下 几点:
-
包装类型可以为 null,而基本类型不可以。它使得包装类型可以应用于 POJO 中,而基本类型则不行。那为什么 POJO 的属性必须要用包装类型呢?《阿里巴巴 Java 开发手册》上有详细的说明, 数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛出
NullPointerException
的异常。 -
包装类型可用于泛型,而基本类型不可以。泛型不能使用基本类型,因为使用基本类型时会编译出错。
List<int> list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType List<Integer> list = new ArrayList<>();
因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例。
-
基本类型比包装类型更高效。基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。 很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。
8. 解释一下自动装箱和自动拆箱?
自动装箱:将基本数据类型重新转化为对象
public class Test {
public static void main(String[] args) {
// 声明一个Integer对象,用到了自动的装箱:解析为:Integer num = Integer.valueOf(9);
Integer num = 9;
}
}
9是属于基本数据类型的,原则上它是不能直接赋值给一个对象Integer的。但jdk1.5 开始引入了自动装箱/拆箱机制,就可以进行这样的声明,自动将基本数据类型转化为对应的封装类型,成为一个对象以后就可以调用对象所声明的所有的方法。
自动拆箱:将对象重新转化为基本数据类型
public class Test {
public static void main(String[] args) {
/ /声明一个Integer对象
Integer num = 9;
// 进行计算时隐含的有自动拆箱
System.out.print(num--);
}
}
因为对象时不能直接进行运算的,而是要转化为基本数据类型后才能进行加减乘除。
9. int 和 Integer 有什么区别?
- Integer是int的包装类;int是基本数据类型;
- Integer变量必须实例化后才能使用;int变量不需要;
- Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
- Integer的默认值是null;int的默认值是0。
10. 两个new生成的Integer变量的对比
由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(10000);
Integer j = new Integer(10000);
System.out.print(i == j); //false
11. Integer变量和int变量的对比
Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
int a = 10000;
Integer b = new Integer(10000);
Integer c=