Skip to content

java 的异常体系

一、java 中的异常分为哪几类?

1617069809797-159463f8-89cb-4a70-9ee4-049552f304c3.png

1.Exception:出现的问题是可以被捕获的;

1.check 异常: 直接派生自 Exception 的异常类,必须被捕获或再次声明抛出

2.runtime 异常:派生自 RuntimeException 的异常类。使用 throw 语句可以随时抛出这种异常对象

2.Error:系统错误,通常由 JVM 处理。

二、异常的处理机制有几种?

1、异常捕捉

try-catch 语句或者 try-catch-finally 语句实现

2、异常抛出

throws 异常抛出:

throws 语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是 Exception 异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。throws 语句的语法格式为:

java
    methodname throws Exception1,Exception2,..,ExceptionN
    {
    }

Throws 抛出异常的规则:

1) 如果是不可查异常(unchecked exception),即 Error、RuntimeException 或它们的子类,那么可以不使用 throws 关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。(不需要 catch 或者 throws 处理抛出的异常就是 unchecked 异常)

2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用 try-catch 语句捕获,要么用 throws 子句声明将它抛出,否则会导致编译错误

3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。

4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。

结论:

finally 如果有 return 会覆盖 catch 里的 throw,同样如果 finally 里有 throw 会覆盖 catch 里的 return。

进而如果 catch 里和 finally 都有 return, finally 中的 return 会覆盖 catch 中的。throw 也是如此。

三、如何自定义一个异常

1、第一步:编写一个类,继承 Exception,或者继承 RuntimeException,看你是自定义编译时异常,还是自定义运行时异常;

2、第二步:提供两个构造方法,一个无参数构造方法,一个带有 String s 参数的。有参数方法体中写 super(s) 即可。

例:

java
public static void main(String[] args) {
		// 创建自定义异常类的实例,并不抛出
		MyStackOperatorException e = new MyStackOperatorException("栈操作有误");
		String msg = e.getMessage();
		System.out.println(msg); // 栈操作有误
		e.printStackTrace(); // exception.MyStackOperatorException: 栈操作有误 }
	}

	// 自定义异常类
	public static class MyStackOperatorException extends Exception {
		public MyStackOperatorException() {

		}

		public MyStackOperatorException(String s) {
			super(s);
		}
	}

四、Throw 与 Throws 的区别

1、Throw 用于方法内部,Throws 用于方法声明上

2、Throw 后跟异常对象,Throws 后跟异常类型

3、Throw 后只能跟一个异常对象,Throws 后可以一次声明多种异常类型

五、Error 与 Exception 区别

1、Exception 和 Error 继承 Throwable 类,在 java 中只有 Throwable 类型的实例才能被抛出 (throw) 或者捕获 (catch),他是异常处理机制的基本类型。


2、Exception 和 Error 体现了 java 平台针对不同异常情况的分类。Exception 是程序正常运行过程中,可以预料的意外情况,可能并且应该被捕获,并进行处理。Error 正常情况下不大可能出现的情况,绝大部分的 Error 都会导致程序状态不正常,不可恢复,既然是非正常情况,所以不便也不需要处理,例如 OutOfMemoryError 之类都是 Error 的子类


3、Exception 分为检查型异常和非检查型异常。检查型异常必须在源码中进行捕获处理,这是编译检查的一部分。除了 RuntimeExceion 及其子类之外的异常都是检查型异常。非检查型异常就是所谓的 RuntimeExceion,类似 NullPointerException,ArrayIndexOfBoundException 就是非检查型异常,通常是可以通过编码避免的逻辑错误,具体根据需要判断是否需要捕获,编译期不检查,如果抛出了非检查型异常,那就是编码逻辑有问题,要解决。

六、如何处理异常

1、直接抛出

java

public static void main(String[] args) throws FileNotFoundException {
    try {
        InputStream inputStream = new FileInputStream(new File(""));
    }catch (Exception e){

    }

}

2、异常处理两种方式:catch 住本身( 对于本身可预测的异常 你就可以 catch 住具体的异常 ) 或 catch 父类(无法预测抛出什么异常 ,就可以 cathc 主异常的父类 Exception)

java

public void handleExceptionOne() throws FileNotFoundException {
    //抛出异常 交由调用此方法的人处理
    InputStream inputStream = new FileInputStream(new File(""));
}

public void handleExceptionTwo() {
    //catch主异常本身
    try {
        InputStream inputStream = new FileInputStream(new File(""));
    }catch (FileNotFoundException e){
        //务必打印异常信息 项目中log.error 或者是 log.warn() 根据具体业务区分
        //
        e.printStackTrace(); //本地测试可用这个
        log.error("FileNotFoundException:", e);
    }

    
   //catch主异常的父类
    try {
        InputStream inputStream = new FileInputStream(new File(""));
    }catch (Exception e){
        //务必打印异常信息 项目中log.error 或者是 log.warn() 根据具体业务区分
        e.printStackTrace(); //本地测试可用这个
        log.error("Exception:", e);
    }
}

// 假如说我要对外暴露一个接口或者是给页面提供的一个接口
//对于使用你接口的人,他肯定不希望看到一堆异常信息,对于网站的使用者更不希望看到 体验性很差
//所以我们会在最外层 catch主所有异常
public void method(){
    try{
        handleOne();
        handleTwo();
        handleThree();
    }catch (Exception e){

    }
}

public void handleOne() {
    //逻辑处理
}
public void handleTwo() {
    //逻辑处理
}
public void handleThree() {
    //逻辑处理
}
最近更新