Java基础知识(8)- Java 异常处理(二)| 自动资源管理、Java 7多异常捕获、自定义异常、异常跟踪栈


1. 自动资源管理(Automatic Resource Management)

    在 try catch finally 语句使用文件资源,需要在 finally 块中关闭文件资源,代码如下:

复制代码
 1 public static void main(String[] args) {
 2     FileInputStream fis = null;
 3     try {
 4         fis = new FileInputStream("a.txt");
 5     } catch (FileNotFoundException e) {
 6         e.printStackTrace();
 7     } finally {
 8         // 关闭磁盘文件,回收资源
 9         if (fis != null) {
10             try {
11                 fis.close();
12             } catch (IOException e) {
13                 e.printStackTrace();
14             }
15         }
16     }
17 } 
复制代码

 

   1) Java 7自动资源管理

        上面代码中的 finally 代码块是不得不写的 “臃肿代码” ,为了解决这种问题,Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件,被称为自动资源管理(Automatic Resource Management)。

        自动资源管理替代了 finally 代码块,并优化了代码结构和提高程序可读性。语法如下:

            try (声明或初始化资源语句) {
                // 可能会生成异常语句
            } catch(Throwable e1){
                // 处理异常e1
            } catch(Throwable e2){
                // 处理异常e1
            } catch(Throwable eN){
                // 处理异常eN
            }

        当 try 代码块结束时,自动释放资源。不再需要显式的调用 close() 方法,该形式也称为 “带资源的 try 语句”。

        注意:
            a) try 语句中声明的资源被隐式声明为 final,资源的作用局限于带资源的 try 语句。
            b) 可以在一条 try 语句中声明或初始化多个资源,每个资源以;隔开即可。
            c) 需要关闭的资源必须实现了 AutoCloseable 或 Closeable 接口。

        Closeable 是 AutoCloseable 的子接口,Closeable 接口里的 close() 方法声明抛出了 IOException,因此它的实现类在实现 close() 方法时只能声明抛出 IOException 或其子类;AutoCloseable 接口里的 close() 方法声明抛出了 Exception,因此它的实现类在实现 close() 方法时可以声明抛出任何异常。

        下面示范如何使用自动关闭资源的 try 语句。

复制代码
 1 public static void main(String[] args) throws IOException {
 2     try (
 3             // 声明、初始化两个可关闭的资源
 4             // try 语句会自动关闭这两个资源
 5             BufferedReader br = new BufferedReader(new FileReader("d:\\temp\\test.java"));
 6             PrintStream ps = new PrintStream(new FileOutputStream("d:\\temp\\data.txt"))) {
 7         // 使用两个资源
 8         System.out.println(br.readLine());
 9         ps.println("Java 7自动资源管理");
10     }
11 }
复制代码

        上面程序中粗体字代码分别声明、初始化了两个 IO 流,BufferedReader 和 PrintStream 都实现了 Closeable 接口,并在 try 语句中进行了声明和初始化,所以 try 语句会自动关闭它们。

        自动关闭资源的 try 语句相当于包含了隐式的 finally 块(这个 finally 块用于关闭资源),因此这个 try 语句可以既没有 catch 块,也没有 finally 块。

        Java 7 几乎把所有的“资源类”(包括文件 IO 的各种类、JDBC 编程的 Connection 和 Statement 等接口)进行了改写,改写后的资源类都实现了 AutoCloseable 或 Closeable 接口。

        如果程序需要,自动关闭资源的 try 语句后也可以带多个 catch 块和一个 finally 块。


    2) Java 9增强的自动资源管理

        Java 9 再次增强了这种 try 语句。Java 9 不要求在 try 后的圆括号内声明并创建资源,只需要自动关闭的资源有 final 修饰或者是有效的 final (effectively final),Java 9 允许将资源变量放在 try 后的圆括号内。
        
        上面程序在 Java 9 中可改写为如下形式。

复制代码
 1 public static void main(String[] args) throws IOException {
 2     // 有final修饰的资源
 3     final BufferedReader br = new BufferedReader(new FileReader("d:\\temp\\test.java"));
 4     // 没有显式使用final修饰,但只要不对该变量重新赋值,该变量就是有效的
 5     final PrintStream ps = new PrintStream(new FileOutputStream("d:\\temp\\data.txt"));
 6     
 7     // 只要将两个资源放在try后的圆括号内即可
 8     try (br; ps) {
 9         // 使用两个资源
10         System.out.println(br.readLine());
11         ps.println("Java 9增强的自动资源管理");
12     }
13 }
复制代码

 

2. Java 7多异常捕获

    多 catch 代码块虽然客观上提高了程序的健壮性,但是也导致了程序代码量大大增加。如果有些异常种类不同,但捕获之后的处理是相同的,例如以下代码。

        try {
            // 可能会发生异常的语句
        } catch (FileNotFoundException e) {
            // 调用方法methodA处理
        } catch (IOException e) {
            // 调用方法methodA处理
        } catch (ParseException e) {
            // 调用方法methodA处理
        }

    三个不同类型的异常,要求捕获之后的处理都是调用 methodA 方法。为了解决这种问题,Java 7 推出了多异常捕获技术,可以把这些异常合并处理。上述代码修改如下:

        try {
            // 可能会发生异常的语句
        } catch (IOException | ParseException e) {
            // 调用方法methodA处理
        }

    注意:由于 FileNotFoundException 属于 IOException 异常,IOException 异常可以捕获它的所有子类异常。所以不能写成 FileNotFoundException | IOException | ParseException 。

    使用一个 catch 块捕获多种类型的异常时需要注意如下两个地方。
        1) 捕获多种类型的异常时,多种异常类型之间用竖线|隔开。
        2) 捕获多种类型的异常时,异常变量有隐式的 final 修饰,因此程序不能对异常变量重新赋值。

    下面程序示范了 Java 7 提供的多异常捕获。

复制代码
 1 public static void main(String[] args) {
 2     try {
 3         int a = Integer.parseInt(args[0]);
 4         int b = Integer.parseInt(args[1]);
 5         int c = a / b;
 6         System.out.println("您输入的两个数相除的结果是:" + c);
 7     } catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException e) {
 8         System.out.println("程序发生了数组越界、数字格式异常、算术异常之一");
 9         // 捕获多异常时,异常变量默认有final修饰
10         // 所以下面代码有错
11         e = new ArithmeticException("test");    // Line 11
12     } catch (Exception e) {
13         System.out.println("未知异常");
14         // 捕获一种类型的异常时,异常变量没有final修饰
15         // 所以下面代码完全正确
16         e = new RuntimeException("test");       // Line 16
17     }
18 }
复制代码


    上面程序中第一行粗体字代码使用了 IndexOutOfBoundsException | NumberFormatException | ArithmeticException来定义异常类型,这就表明该 catch 块可以同时捕获这 3 种类型的异常。
    
    捕获多种类型的异常时,异常变量使用隐式的 final 修饰,因此上面程序的第 11 行代码将产生编译错误;捕获一种类型的异常时,异常变量没有 final 修饰,因此上面程序的第 16 行代码完全正确。

3. 自定义异常

    如果 Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。

    自定义异常的语法形式为:

        <class> <自定义异常名> <extends> <Exception>

    在编码规范上,一般将自定义异常类的类名命名为 XXXException,其中 XXX 用来代表该异常的作用。

    自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。

    例如,以下代码创建一个名称为 IntegerRangeException 的自定义异常类:

        class IntegerRangeException extends Exception {
            public IntegerRangeException() {
                super();
            }
            public IntegerRangeException(String s) {
                super(s);
            }
        }

    以上代码创建的自定义异常类 IntegerRangeException 类继承自 Exception 类,在该类中包含两个构造方法。

    提示:因为自定义异常继承自 Exception 类,因此自定义异常类中包含父类所有的属性和方法。

4. 异常跟踪栈

    异常对象的 printStackTrace() 方法用于打印异常的跟踪栈信息,根据 printStackTrace() 方法的输出结果,开发者可以找到异常的源头,并跟踪到异常一路触发的过程。

    看下面用于测试 printStackTrace 的例子程序。

复制代码
 1 class SelfException extends RuntimeException {
 2     SelfException() {
 3     }
 4     SelfException(String msg) {
 5         super(msg);
 6     }
 7 }
 8 public class PrintStackTraceTest {
 9     public static void main(String[] args) {
10         firstMethod();
11     }
12     public static void firstMethod() {
13         secondMethod();
14     }
15     public static void secondMethod() {
16         thirdMethod();
17     }
18     public static void thirdMethod() {
19         throw new SelfException("自定义异常信息");
20     }
21 }
复制代码

 

    上面程序中 main 方法调用 firstMethod,firstMethod 调用 secondMethod,secondMethod 调用 thirdMethod,thirdMethod 直接抛出一个 SelfException 异常。
    
    运行上面程序,会看到如下所示的结果:

        Exception in thread "main" Test.SelfException: 自定义异常信息
                at Test.PrintStackTraceTest.thirdMethod(PrintStackTraceTest.java:26)
                at Test.PrintStackTraceTest.secondMethod(PrintStackTraceTest.java:22)
                at Test.PrintStackTraceTest.firstMethod(PrintStackTraceTest.java:18)
                at Test.PrintStackTraceTest.main(PrintStackTraceTest.java:14)

    上面运行结果的第 2 行到第 5 行之间的内容是异常跟踪栈信息,从打印的异常信息我们可以看出,异常从 thirdMethod 方法开始触发,传到 secondMethod 方法,再传到 firstMethod 方法,最后传到 main 方法,在 main 方法终止,这个过程就是 Java 的异常跟踪栈。

实例:

复制代码
 1 import java.io.*;
 2 import java.util.Scanner;
 3 
 4 public class App {
 5     public static void main( String[] args ) {
 6 
 7         // Java 7自动资源管理
 8         try (
 9                 // 声明、初始化两个可关闭的资源
10                 // try 语句会自动关闭这两个资源
11                 BufferedReader br = new BufferedReader(new FileReader("d:\\temp\\test.java"));
12                 PrintStream ps = new PrintStream(new FileOutputStream("d:\\temp\\data.txt"))) {
13 
14             // 使用两个资源
15             System.out.println(br.readLine());
16             ps.println("Java 7自动资源管理");
17 
18         } catch (IOException e) {
19             e.printStackTrace();
20         }
21 
22         // 多异常捕获
23         Scanner scanner = new Scanner(System.in);
24         int num = 0;
25         System.out.println("请输入一个整数:");
26         try {
27             num = scanner.nextInt();    // 输入字母时有异常
28             num = num / 0;              // 除零异常
29         } catch (InputMismatchException | ArithmeticException e) {
30             e.printStackTrace();
31         }
32 
33         // Test SelfException
34         firstMethod();
35     }
36 
37     public static void firstMethod() {
38         secondMethod();
39     }
40     public static void secondMethod() {
41         thirdMethod();
42     }
43     public static void thirdMethod() {
44         throw new SelfException("自定义异常信息");
45     }
46    
47 }
48 
49 class SelfException extends RuntimeException {
50     SelfException() {
51     }
52     SelfException(String msg) {
53         super(msg);
54     }
55 }
复制代码


    *注:  d:\\temp\\test.java 里的内容是 “package com.example;”(只有一行),需要手动创建。d:\\temp\\data.txt 是程序自动创建,目录 d:\\temp\\ 可自行调整。

输出:

    package com.example;
    请输入一个整数:
    5
    java.lang.ArithmeticException: / by zero
        at com.example.ExceptionApp.main(ExceptionApp.java:55)
    Exception in thread "main" com.example.SelfException: 自定义异常信息
        at com.example.ExceptionApp.thirdMethod(ExceptionApp.java:74)
        at com.example.ExceptionApp.secondMethod(ExceptionApp.java:71)
        at com.example.ExceptionApp.firstMethod(ExceptionApp.java:68)
        at com.example.ExceptionApp.main(ExceptionApp.java:61)


posted @   垄山小站  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示