上图是我整理的Java面试题合集,包揽了基本所有技术栈:完整版Java面试题合集附答案,高清PDF下载
1. 为什么要有包装类型?
让基本数据类型也具有对象的特征
基本类型 | 包装器类型 |
---|---|
boolean | Boolean |
char | Character |
int | Integer |
byte | Byte |
short | Short |
long | Long |
float | Float |
double | Double |
为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型)因为容器都是装object的,这是就需要这些基本类型的包装器类了。
自动装箱:new Integer(6);
,底层调用:Integer.valueOf(6)
自动拆箱: int i = new Integer(6);
,底层调用i.intValue();
方法实现。
Integer i = 6;
Integer j = 6;
System.out.println(i==j);
答案在下面这段代码中找:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
二者的区别:
- 声明方式不同:基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
- 存储方式及位置不同:基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
- 初始值不同:基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
- 使用方式不同:基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
2. 成员变量与局部变量的区别有哪些
-
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域
-
成员变量:方法外部,类内部定义的变量
-
局部变量:类的方法中的变量。
-
成员变量和局部变量的区别
作用域
- 成员变量:针对整个类有效。
- 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
存储位置
- 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
- 局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。
生命周期
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:当方法调用完,或者语句结束后,就自动释放。
初始值
- 成员变量:有默认初始值。
- 局部变量:没有默认初始值,使用前必须赋值。
3. 访问修饰符 public,private,protected,以及不写(默认)时的区别
-
定义:Java中,可以使用访问修饰符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
-
分类
-
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
-
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
-
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
-
public : 对所有类可见。使用对象:类、接口、变量、方法
-
访问修饰符图
4. 构造方法有哪些特性?
-
名字与类名相同;
-
没有返回值,但不能用void声明构造函数;
-
生成类的对象时自动执行,无需调用。
5. 什么是受检异常
异常表示程序运行过程中可能出现的非正常状态。
运行时异常表示 虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。
受检异常 跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。
Java 编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,在 Effective Java 中对异常的使用给出了以下指导原则:
1、 不要将异常处理用于正常的控制流(设计良好的 API 不应该强迫它的调用者为了正常的控制流而使用异常)
2、 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
3、 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
4、 优先使用标准的异常
5、 每个方法抛出的异常都要有文档
6、 保持异常的原子性
7、 不要在 catch 中忽略掉捕获到的异常
6. Java的四种引用,强弱软虚
-
强引用
强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:
String str = new String("str");
-
软引用
软引用在程序内存不足时,会被回收,使用方式:
// 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的,
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。
-
弱引用
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference<String> wrf = new WeakReference<String>(str);
可用场景: Java源码中的java.util.WeakHashMap
中的key
就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。
-
虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入
ReferenceQueue
中。注意哦,其它引用是被JVM回收后才被传入ReferenceQueue
中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有ReferenceQueue
,使用例子:
PhantomReference<String> prf = new PhantomReference<String>(new String("str"), new ReferenceQueue<>());
可用场景: 对象销毁前的一些操作,比如说资源释放等。**Object.finalize()
虽然也可以做这类动作,但是这个方式即不安全又低效
上诉所说的几类引用,都是指对象本身的引用,而不是指Reference
的四个子类的引用(SoftReference
等)。
7. 数组有没有 length()方法?String 有没有 length()方法
- 数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。
8. Java 中有几种类型的流?
从输入输出方面来讲: Java中有输入流和输出流
从流的编码方式上来讲: Java中有字节流和字符流
对于字节流而言:主要继承的抽象类为 InputStream和OutputStream
对于字符流而言:主要继承的抽象类为 InputStreamReader和OutputStreamReder
9. 反射机制的应用场景有哪些?
-
反射是框架设计的灵魂。
-
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
-
举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
10. final有哪些用法?
final也是很多面试喜欢问的地方,但我觉得这个问题很无聊,通常能回答下以下5点就不错了:
- 被final修饰的类不可以被继承
- 被final修饰的方法不可以被重写
- 被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
- 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
- 被final修饰的常量,在编译阶段会存入常量池中.
除此之外,编译器对final域要遵守的两个重排序规则更好:
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序.
11. 讲讲NIO
看了一些文章,传统的IO流是阻塞式的,会一直监听一个ServerSocket,在调用read等方法时,他会一直等到数据到来或者缓冲区已满时才返回。
调用accept也是一直阻塞到有客户端连接才会返回。每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。并且多线程处理多个连接。每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部未准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。
对于NIO,它是非阻塞式,核心类:
1、 Buffer为所有的原始类型提供 (Buffer)缓存支持。
2、 Charset字符集编码解码解决方案
3、 Channel一个新的原始 I/O抽象,用于读写Buffer类型,通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。
12. Java有哪些数据类型
Java中有 8 种基本数据类型,分别为:
-
6 种数字类型 (四个整数形,两个浮点型):byte、short、int、long、float、double
-
1 种字符类型:char
-
1 种布尔型:boolean。
byte:
- byte 数据类型是8位、有符号的,以二进制补码表示的整数;
- 最小值是 -128(-2^7);
- 最大值是 127(2^7-1);
- 默认值是 0;
- byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
- 例子:byte a = 100,byte b = -50。
short:
- short 数据类型是 16 位、有符号的以二进制补码表示的整数
- 最小值是 -32768(-2^15);
- 最大值是 32767(2^15 - 1);
- Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
- 默认值是 0;
- 例子:short s = 1000,short r = -20000。
int:
- int 数据类型是32位、有符号的以二进制补码表示的整数;
- 最小值是 -2,147,483,648(-2^31);
- 最大值是 2,147,483,647(2^31 - 1);
- 一般地整型变量默认为 int 类型;
- 默认值是 0 ;
- 例子:int a = 100000, int b = -200000。
long:
-
注意:Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析
-
long 数据类型是 64 位、有符号的以二进制补码表示的整数;
-
最小值是 -9,223,372,036,854,775,808(-2^63);
-
最大值是 9,223,372,036,854,775,807(2^63 -1);
-
这种类型主要使用在需要比较大整数的系统上;
-
默认值是 0L;
-
例子: long a = 100000L,Long b = -200000L。
"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float:
- float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
- float 在储存大型浮点数组的时候可节省内存空间;
- 默认值是 0.0f;
- 浮点数不能用来表示精确的值,如货币;
- 例子:float f1 = 234.5f。
double:
- double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;
- 浮点数的默认类型为double类型;
- double类型同样不能表示精确的值,如货币;
- 默认值是 0.0d;
- 例子:double d1 = 123.4。
char:
- char类型是一个单一的 16 位 Unicode 字符;
- 最小值是 \u0000(即为 0);
- 最大值是 \uffff(即为 65535);
- char 数据类型可以储存任何字符;
- 例子:char letter = ‘A’;(单引号)
boolean:
- boolean数据类型表示一位的信息;
- 只有两个取值:true 和 false;
- 这种类型只作为一种标志来记录 true/false 情况;
- 默认值是 false;
- 例子:boolean one = true。
这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean
类型名称 | 字节、位数 | 最小值 | 最大值 | 默认值 | 例子 |
---|---|---|---|---|---|
byte字节 | 1字节,8位 | -128(-2^7) | 127(2^7-1) | 0 | byte a = 100,byte b = -50 |
short短整型 | 2字节,16位 | -32768(-2^15) | 32767(2^15 - 1) | 0 | short s = 1000,short r = -20000 |
int整形 | 4字节,32位 | -2,147,483,648(-2^31) | 2,147,483,647(2^31 - 1) | 0 | int a = 100000, int b = -200000 |
lang长整型 | 8字节,64位 | -9,223,372,036,854,775,808(-2^63) | 9,223,372,036,854,775,807(2^63 -1) | 0L | long a = 100000L,Long b = -200000L |
double双精度 | 8字节,64位 | double类型同样不能表示精确的值,如货币 | 0.0d | double d1 = 123.4 | |
float单精度 | 4字节,32位 | 在储存大型浮点数组的时候可节省内存空间 | 不同统计精准的货币值 | 0.0f | float f1 = 234.5f |
char字符 | 2字节,16位 | \u0000(即为0) | \uffff(即为65,535) | 可以储存任何字符 | char letter = ‘A’; |
boolean布尔 | 返回true和false两个值 | 这种类型只作为一种标志来记录 true/false 情况; | 只有两个取值:true 和 false; | false | boolean one = true |
13. 静态变量和实例变量区别
-
静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。
-
实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
14. error和exception有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
15. 有哪些可用的Filter流?
在java.io包中主要由4个可用的filter Stream组成。两个字节filter stream,两个字符filter stream。分别是:FilterInputStream
、FilterOutputStream
、FilterReader
和FilterWriter
。