二、Exception 和 Error 有什么区别?
在Java中,所有的异常都有一个共同的祖先 java.lang 包中的Throwable类。Throwable类有两个重要的子类:Exception和Error。
- Error:程序无法处理的错误。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。一般不编写针对性的代码进行处理。
- Exception:程序本身可以处理的异常,可以通过catch来进行捕获处理。Exception又可以分为Checked Exception(受检查异常,必须处理)和Unchecked Exception(不受检查异常,可以不处理)。
三、Checked Exception 和 Unchecked Exception 有什么区别?
- Checked Exception 即受检查异常(编译时异常),Java 代码在编译过程中,如果受检查异常没有被 catch或者throws 关键字处理的话,就没办法通过编译。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException 、SQLException
- Unchecked Exception 即不受检查异常(运行时异常),Java代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。
RuntimeException及其子类都统称为非受检查异常,常见的有:
- NullPointerException(空指针错误)
- IllegalArgumentException(参数错误比如方法入参类型错误)
- NumberFormatException(字符串转换为数字格式错误,IllegalArgumentException的子类)
- ArrayIndexOutOfBoundsException(数组越界错误)
- ClassCastException(类型转换错误)
- ArithmeticException(算术错误)
- SecurityException(安全错误比如权限不够)
- UnsupportedOperationException(不支持的操作错误比如重复创建同一用户)
四、Throwable 类常用方法有哪些?
- String getMessage(): 返回异常发生时的简要描述;
- String toString(): 返回异常发生时的详细信息;
- String getLocalizedMessage(): 返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()返回的结果相同;
- void printStackTrace(): 在控制台上打印 Throwable 对象封装的异常信息。
五、异常处理机制一:try-catch-finally
-
try块 : 用于捕获异常,将可能出现异常的代码放在try语句块中。
-
catch块 : 用于处理try捕获到的异常。在catch语句块中是对异常对象进行处理的代码。
-
每个try语句块可以伴随0个或多个catch语句,用于处理可能产生的不同类型的异常对象。
如果没有 catch 块,则必须跟一个 finally 块。
-
finally 块 : finally是可选的。无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
-
try-catch-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
-
注意:不要在 finally 语句块中使用 return!
当try语句和finally语句中都有return语句时,try语句块中的return语句会被忽略。这是因为try语句中的return返回值会先被暂存在一个本地变量中,当执行到finally语句中的return之后,这个本地变量的值就变为了finally语句中的return返回值。代码示例:
public void testMethod() { int num = method(); System.out.println(num); } public int method() { try { int[] arr = new int[10]; System.out.println(arr[10]); return 1; } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return 2; } finally { System.out.println("我一定会被执行"); return 3; } }输出:
我一定会被执行 3
六、异常处理机制二:throws
- 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
- 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
public class ExceptionTest2 {
public static void main(String[] args){
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
method3();
}
public static void method3(){
try {
method2();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void method2() throws IOException{
method1();
}
public static void method1() throws FileNotFoundException,IOException{
File file = new File("hello1.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
fis.close();
System.out.println("hahaha!"); // 不执行
}
}
七、开发中如何选择使用try-catch-finally 还是使用throws?
-
如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
-
执行的方法A中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。
- 建议这几个方法使用throws的方式进行处理;
- 而执行的方法A可以考虑使用try-catch-finally方式进行处理。
try-catch-finally:真正的将异常给处理掉了。
throws:只是将异常抛给了方法的调用者,并没有真正将异常处理掉。
八、手动抛出异常 和 自定义异常 1、手动抛出异常
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
- 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境);
- 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
// s.regist(1001);
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
// e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void regist(int id) throws Exception{
if(id > 0){
this.id = id;
}else{
// System.out.println("您输入的数据非法!");
//手动抛出异常
// throw new RuntimeException("您输入的数据非法!");
throw new Exception("您输入的数据非法!");
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
}
2、自定义异常类
//自定义全局异常类
@Data
public class MyException extends RuntimeException {
private Integer code;
//通过状态码和错误消息创建异常对象
public MyException(String message, Integer code) {
super(message);
this.code = code;
}
//接收枚举类型对象
public MyException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
}
@Override
public String toString() {
return "MyException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
异常使用有哪些需要注意的地方?
- 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。
- 抛出的异常信息一定要有意义。
- 建议抛出更加具体的异常比如字符串转换为数字格式错误的时候应该抛出NumberFormatException而不是其父类IllegalArgumentException。
- 使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)。



