Java基础常见面试题-异常-泛型
1 Exception 和 Error 有什么区别?
1**Exception
** :程序本身可以处理的异常,可以通过 catch
来进行捕获。Exception
又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。
2**Error
** :Error
属于程序无法处理的错误 。例如 Java 虚拟机运行错误(Virtual MachineError
)、虚拟机内存不够错误(OutOfMemoryError
)、类定义错误(NoClassDefFoundError
)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
2 Checked Exception 和 Unchecked Exception 有什么区别?
Checked Exception 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch
或者throws
关键字处理的话,就没办法通过编译。
Unchecked Exception 即 不受检查异常 ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。
3Throwable 类常用方法有哪些?
1String getMessage()
: 返回异常发生时的简要描述
2String toString()
: 返回异常发生时的详细信息
3String getLocalizedMessage()
: 返回异常对象的本地化信息。使用 Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()
返回的结果相同
4void printStackTrace()
: 在控制台上打印 Throwable
对象封装的异常信息
4 try-catch-finally 如何使用?
try
块 : 用于捕获异常。其后可接零个或多个 catch
块,如果没有 catch
块,则必须跟一个 finally
块。
catch
块 : 用于处理 try 捕获到的异常。
finally
块 : 无论是否捕获或处理异常,finally
块里的语句都会被执行。当在 try
块或 catch
块中遇到 return
语句时,finally
语句块将在方法返回之前被执行。
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
} finally {
System.out.println("Finally");
}
输出:
Try to do something
Catch Exception -> RuntimeException
Finally
5 finally 中的代码一定会执行吗?
不一定的!在某些情况下,finally 中的代码不会被执行。
就比如说 finally 之前虚拟机被终止运行的话,finally 中的代码就不会被执行。
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
// 终止当前正在运行的Java虚拟机
System.exit(1);
} finally {
System.out.println("Finally");
}
输出结果:
Try to do something
Catch Exception -> RuntimeException
还有在以下 2 种特殊情况下,finally
块的代码也不会被执行:
- 程序所在的线程死亡。
- 关闭 CPU。
6 异常使用有哪些需要注意的地方?
1 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。
2 抛出的异常信息一定要有意义。
3 建议抛出更加具体的异常比如字符串转换为数字格式错误的时候应该抛出NumberFormatException
而不是其父类IllegalArgumentException
。
4 使用日志打印异常之后就不要再抛出异常了
二 、泛型
1 什么是泛型?有什么作用?
Java泛型是JDK5中引入的一个新特性。使用泛型参数可以增强代码的可读性以及稳定性。
泛型就是参数化类型或类型参数化
2 泛型的使用方式有哪几种?
泛型一般有三种使用方式:泛型类、泛型接口、泛型方法。
1.泛型类:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
实例化泛型类:
Generic<Integer> genericInteger = new Generic<Integer>(123456);
2.泛型接口 :
public interface Generator<T> {
public T method();
}
3.泛型方法 :
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
3 项目中哪里用到了泛型?
1 自定义接口通用返回结果 CommonResult<T>
通过参数 T
可根据具体的返回类型动态指定结果的数据类型
2 定义 Excel
处理类 ExcelUtil<T>
用于动态指定 Excel
导出的数据类型
3 构建集合工具类(参考 Collections
中的 sort
, binarySearch
方法)。
三、反射
1 什么是反射?
反射是指能访问、检测、修改其本身状态的能力;
2 反射的优缺点?
1 可以让代码编写更加灵活,为各种框架提供开箱即用的功能便利。
2 反射让我们在运行时有了分析操作类的能力的同时,但增加了安全问题。
3 反射性能较差(影响不大)
3 反射的应用场景有哪些?
Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。
四、注解
1 what is 注解?
Annotation
(注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。
2 注解的解析方法有哪几种?
编译期直接扫描 :编译器在编译 Java 代码的时候扫描对应的注解并处理。
运行期通过反射处理 :像框架中自带的注解(比如 Spring 框架的 @Value
、@Component
)都是通过反射来进行处理的。
五、SPI
1 什么是SPI
SPI=Service Provider Interface,字面意思服务提供者的接口
2 SPI 和 API 有什么区别?
API:实现方提供实现和接口,调用方进行调用。
SPI:接口存在于调用方,调用方确定接口规则,实现方根据规则进行实现。
3 SPI 的优缺点?
1 需要遍历加载所有的实现类,性能低;
2 当多个ServiceLoader 同时 load 时,会出现并发问题;
六、序列化和反序列化
1 什么是序列化和反序列化?
序列化:就是将数据结构或者对象转化为二进制字节流的过程。
反序列化:将在序列化中产生的二进制字节流转换成数据结构或者对象。
序列化和反序列化常见应用场景:
-
对象在进行网络传输;
-
将对象储存到文件前需要进行序列化,将对象从文件中读取出来需要进行反序列化。
-
对象存储到数据库需要序列化,数据库读出对象需要反序列化。
-
对象到内存-序列化,内存读出对象-反序列化。
总结:序列化的目的就是在网络传输对象或者将对象存储到文件系统、数据库、内存中。
2 序列化协议对应于 TCP/IP 四层模型的哪一层?
-
应用层
-
传输层
-
网络层
-
网络接口层
3 如果有些字段不想进行序列化怎么办?
使用 transient
关键字修饰阻止序列化
transient
几点注意:
1transient
只能修饰变量,不能修饰类和方法。
2transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int
类型,那么反序列后结果就是 0
。
3static
变量因为不属于任何对象(Object),所以无论有没有 transient
关键字修饰,均不会被序列化。
比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议。
4 为什么不推荐使用 JDK 自带的序列化?
- 不支持跨语言调用;
- 性能差;
- 存在安全问题;
七、I/O
1 Java I/O流是什么?
IO 即 Input/Output
,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。
InputStream
/Reader
: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。OutputStream
/Writer
: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
2 I/O 流为什么要分为字节流和字符流呢?
- 字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时;
- 如果我们不知道编码类型的话,使用字节流的过程中很容易出现乱码问题。
3 什么是语法糖?
语法糖(Syntactic sugar) 代指的是编程语言为了方便程序员开发程序而设计的一种特殊语法,这种语法对编程语言的功能并没有影响
Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等。