【Java基础】Java异常的捕获及处理

《第一行代码:Java》第6章、异常的捕获及处理 读书笔记

第6章、异常的捕获及处理

6.1 认识异常

异常是程序中导致程序中断的一种指令流,例:

package com.yootk.demo;
public class TestDemo {
	public static void main(String args[]) {
		System.out.println("1. 除法计算开始。") ;
		System.out.println("2. 除法计算:" + (10 / 0)) ; 	// → 此处产生异常
		// 出现异常并且没有正确处理后,异常之后的语句将不再执行
		System.out.println("3. 除法计算结束。") ; 
	}
}

本程序将产生“ArithmeticException”异常,此时如果没有正确的处理异常,程序就会出现中断执行的情况,为了让程序在出现异常后依然可以正常执行完毕,需要引入异常处理语句来完善代码编写。

6.2 异常处理

*Java针对异常的处理提供了3个核心的关键字:try、catch、finally,利用这3个关键字就可以组成以下异常处理格式:

try {
    // 有可能出现异常的语句
}[catch(异常类型 对象) {
    // 异常处理
}catch(异常类型 对象) {
    
}catch(异常处理 对象) {
    // 异常处理
}...][finally{
    // 不管是否出现异常,都执行统一的代码
}]
  • 如果在try中代码产生了异常,则程序会自动跳转到catch 语句中找到匹配的异常类型进行相应的处理。最后不管程序是否会产生异常,都会执行到finally 语句,finally 语句就作为异常的统一出口。异常基本处理流程如图6-1所示:

在这里插入图片描述

  • 注意:finally和catchk块是可以省略的,但只能省略其中之一,异常处理格式一般有3种:try…catch…finally 、 try…catch 、 try…finally

  • 例:

    package com.yootk.demo;
    public class TestDemo {
    	public static void main(String args[]) {
    		System.out.println("1. 除法计算开始。");
    		try {
    			System.out.println("2. 除法计算:" + (10 / 0));		// 此处产生异常
    			// 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行 
    			System.out.println("更多课程请访问:www.yootk.com");
    		} catch (ArithmeticException e) {						// 处理算术异常
    			System.out.println("******** 出现异常了 *********");
    		}
    		System.out.println("3. 除法计算结束。");
    	}
    }
    /*
    程序执行结果:
    	1. 除法计算开始。
    	******** 出现异常了 *********
    	3. 除法计算结束。
    */
    
printStackTrace

上面代码虽然处理了异常,但它输出的异常描述不够准确,使用异常类中提供的 printStackTrace() 方法进行异常信息的输出。

package com.yootk.demo;
public class TestDemo {
	 public static void main(String args[]) {
		System.out.println("1. 除法计算开始。");
		try {
			System.out.println("2. 除法计算:" + (10 / 0));	// 此处产生异常
			// 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行 
			System.out.println("更多课程请访问:www.yootk.com");
		} catch (ArithmeticException e) {					// 处理算术异常
			e.printStackTrace();							// 输出异常的完整信息
		}
		System.out.println("3. 除法计算结束。");
	}
}
/*
程序执行结果:
	1. 除法计算开始。
	java.lang.ArithmeticException: / by zero
		at com.yootk.demo.TestDemo.main(TestDemo.java:7)
	3. 除法计算结束。
*/

6.3 异常的处理流程

为了能够正确地处理异常,就必须清楚异常的继承结构以及处理流程。

  • 为了解释异常的继承结构,首先来观察以下两个异常类的继承关系:

在这里插入图片描述

通过这两个异常类可以发现所有的异常类的最高的继承类都是Throwable,并且在Throwable下有两个子类:

* Error:指的是JVM错误,这时的程序并没有执行,无法处理
* Exception:指的是程序运行中产生的异常,用户可以使用异常处理格式处理
  • 清楚了异常类的继承关系后,下面了解一下Java中的异常的处理完整流程:

在这里插入图片描述

整个过程就好比方法传递参数一样,只是根据catch后面的参数类型进行匹配。既然异常捕获只是一个异常类对象的传递过程,那么依据Java 中对象自动向上转型的概念来讲,所有异常类对象都可以向父类对象转型,也就证明所有的异常类对象都可以使用 Exception来接收,这样就可以简单地实现异常处理了。
  • 使用Exception来接收异常,因为所有异常类都是Exception的子类,所以可以使用 Exception来接收,这样就可以简单地实现异常处理了。

    package com.yootk.demo;
    public class TestDemo {
    	public static void main(String args[]) {
    		System.out.println("1. 除法计算开始。");
    		try {
    			int x = Integer.parseInt(args[0]);				// 接收参数并且转型
    			int y = Integer.parseInt(args[1]);				// 接收参数并且转型
    			System.out.println("2. 除法计算:" + (x / y));  	 // 此处产生异常
    			// 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行 
    			System.out.println("更多课程请访问:www.yootk.com");
    		} catch (Exception e) {								// 使用Exception接收异常,使其能处理所有异常类型
    			e.printStackTrace();
    		} finally {
    			System.out.println("### 不管是否出现异常我都执行!") ;
    		} 
    		System.out.println("3. 除法计算结束。");
    	}
    }
    
  • 为什么不用Throwable来接收异常?

    本程序使用Throwable来处理异常是没有语法错误的,因为Throwable是Exception的父类,自然能接收所有异常。但同样也会接收Error,但用户是没法处理Error错误的,所以开发中用户处理的异常都是以Exception类为主。

  • 处理多个异常时,捕获范围小的异常要放在捕获范围大的异常之前处理

    如:ArithmeticException要放在Exception前面处理,否则将出现语法错误。

6.4 throws关键字

throws关键字主要在方法定义时使用,表示此方法中不进行异常处理,而是交给被调用处处理。且调用这个方法的地方一定要做异常处理操作。

  • 举例:

    class MyMath {
    	public static int div(int x, int y) throws Exception { // 表示此方法不处理异常
    		return x / y;
    	}
    }
    

    本方法中所产生的任何异常本方法都可以不用处理,而是直接交给程序的调用处进行处理。由于div()方法上存在throws抛出的Exception异常,则当调用此方法时必须明确地处理可能会出现的异常。

  • 调用以上方法:

    public class TestDemo {
    	public static void main(String args[]) {
    		try {				// div()方法抛出异常,此处必须明确进行异常处理
    			System.out.println(MyMath.div(10, 2));
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
  • 主方法上也可以使用throws抛出

    主方法本身也属于一个Java方法,所以主方法上使用throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现异常,将交给JVM进行默认处理,则此时会导致程序中断执行。

    public class TestDemo {
    	public static void main(String args[]) throws Exception {
    		// 表示此异常产生后会直接通过主方法抛出,代码中可以不强制使用异常处理
    		System.out.println(MyMath.div(10, 0));
    	}
    }
    

6.5 throw关键字

之前的所有异常类对象都是由JVM自动进行实例化操作得,而现在用户也可以自己手动地抛出一个实例化对象(手工调用异常类的构造方法),就需要通过throw完成。

  • 手工抛出异常:

    package com.yootk.demo;
    public class TestDemo {
    	public static void main(String args[]) {
    		try {				// 直接抛出一个自定义的异常类对象
    			throw new Exception("自己定义的异常!");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    /*
    程序执行结果:
    	java.lang.Exception: 自己定义的异常
    		at com.yootk.demo.TestDemo.main(TestDemo.java:5)
    */
    

    本程序首先实例化了一个Exception异常类对象,然后利用throw进抛出,这里就必须明确进行异常处理了。

  • throws和throw的区别:

    • throws:在方法的声明上使用,表示此方法在定义时不抛出异常,而是在调用处抛出,且在调用处必须进行异常处理。
    • throw:指的是在方法中人为抛出一个异常类对象(这个异常类对象可能是自己实例化的或者是抛出已存在的)

6.6 异常处理的标准格式

结合“try、catch、finally、throw、throws”一起使用的异常处理格式。

  • 现要求定义一个div()方法,这个方法的要求为:

    • 在进行除法操作之前,输出一行提示信息
    • 在除法操作执行完毕后,输出一行提示信息
    • 如果中间产生异常,则应该交给调用处进行处理
    package com.yootk.demo;
    class MyMath {
    	public static int div(int x, int y) throws Exception {		// 出现异常要交给被调用处输出
    		System.out.println("===== 计算开始 ====="); 		// 等价于:资源打开
    		int result = 0;
    		try {
    			result = x / y; 										// 除法计算
    		} catch (Exception e) {
    			throw e; 											// 向上抛
    		} finally {
    			System.out.println("===== 计算结束 ====="); 	// 等价于:资源关闭
    		}
    		return result;
    	}
    }
    public class TestDemo {
    	public static void main(String args[]) {
    		try {
    			System.out.println(MyMath.div(10, 0));			// 被调用处处理异常
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    /*
    程序执行结果:
    	===== 计算开始 =====
        ===== 计算结束 =====
        java.lang.ArithmeticException: / by zero
            at com.yootk.demo.MyMath.div(TestDemo.java:7)
            at com.yootk.demo.TestDemo.main(TestDemo.java:19)
    */
    

6.7 RuntimeException类

在Exception类中有一个RuntimeException子类,而对于所有的RuntimeException子类的对象,用户可以进行有选择性的处理,所以调用处即使不处理,也不会有任何编译语法错误。但如果没进行异常处理又发生了异常,将交由JVM进行默认处理。

  • 比如Integer.parseInt()方法就会抛出一个NumberFormatException异常类对象,而这个异常类就属于RuntimeException子类。

    package com.yootk.demo;
    public class TestDemo {
    	public static void main(String args[]) {
    		int temp = Integer.parseInt("100");			// 直接将字符串变为int型
    		System.out.println(temp);
    	}
    }
    // 程序执行结果:	100
    

    本程序没有处理Integer.parseInt()抛出的异常也能正常的编译和运行,但此时一旦出现异常,将交由JVM进行默认处理

6.8 assert关键字

assert关键字是JDK1.4引入的,其主要功能就是进行断言。

  • 格式:

    assert 判断语句:"异常说明";
    

    如果判断语句的结果为false,则发生异常,并在错误信息处打印”异常说明“。

  • 注意:Java默认是不开启断言的,需要用户手动开启

6.9 自定义异常

除了Java中已有的异常类型,还可以自己定义异常类型,只需要继承Exception类(强制性异常处理类)或RuntimeException类(选择性异常处理类)即可。

  • 例,定义AddException:

    package com.yootk.demo;
    class AddException extends Exception {			// 此异常类要强制处理
    	public AddException(String msg) {
    		super(msg);							// 调用父类构造
    	}
    }
    public class TestDemo {
    	public static void main(String args[]) {
    		int num = 20;
    		try {
    			if (num > 10) { 					// 出现了错误,应该产生异常
    				throw new AddException("数值传递的过大!");
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    /*
    程序执行结果:
    	com.yootk.demo.AddException: 数值传递的过大!
            at com.yootk.demo.TestDemo.main(TestDemo.java:13)
    */
    

本章小结

  • 异常是导致程序中断运行的一种指令流,当异常发生时,如果没有进行良好的处理,则程序将会中断执行。
  • 异常处理可以使用try…catch 进行处理,也可以使用try…catch…finally进行处理,在 try语句中捕捉异常,之后在catch 中处理异常,finally作为异常的统一出口,不管是否发生异常都要执行此段代码。
  • 异常的最大父类是Throwable,其分为两个子类:Exception、Error。Exception 程序处理的异常;而 Error表示JVM错误,一般不是由程序开发人员处理的。
  • 发生异常之后,JVM 会自动产生一个异常类的实例化对象,并匹配相应的 catch语句中的异常类型,也可以利用对象的向上转型关系,直接捕获Exception。
  • throws用在方法声明处,表示本方法不处理异常。
  • throw表示在方法中手工抛出一个异常。
  • 自定义异常类时,只需要继承Exception类或RuntimeException类。
    y…catch…finally进行处理,在 try语句中捕捉异常,之后在catch 中处理异常,finally作为异常的统一出口,不管是否发生异常都要执行此段代码。
  • 异常的最大父类是Throwable,其分为两个子类:Exception、Error。Exception 程序处理的异常;而 Error表示JVM错误,一般不是由程序开发人员处理的。
  • 发生异常之后,JVM 会自动产生一个异常类的实例化对象,并匹配相应的 catch语句中的异常类型,也可以利用对象的向上转型关系,直接捕获Exception。
  • throws用在方法声明处,表示本方法不处理异常。
  • throw表示在方法中手工抛出一个异常。
  • 自定义异常类时,只需要继承Exception类或RuntimeException类。
  • 断言是JDK 1.4之后提供的新功能,可以用来检测程序的执行结果,但开发中并不提倡使用断言进行检测。

第7章、Eclipse开发工具