【转】 JavaSE学习笔记(6)---异常

【转】 JavaSE学习笔记(6)---异常

异常

​ 软件程序在运行过程中,非常可能遇到问题,我们称之为异常,英文是:Exception,意思是例外。遇到这些例外情况,或者叫异常,我们怎么让写的程序做出合理的处理,安全的退出,而不至于程序崩溃呢?

1.异常产生的原因及使用原则

在 Java 中一个异常的产生,主要有如下三种原因:

  1. Java 内部错误发生异常,Java 虚拟机产生的异常。
  2. 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,一般需要在某些类中集中处理这些异常。
  3. 通过 throw 语句手动生成的异常,这种异常称为检査的异常,一般用来告知该方法的调用者一些必要的信息。

Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。

我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常

Java 异常强制用户考虑程序的强健性和安全性。异常处理不应用来控制程序的正常流程,其主要作用是捕获程序在运行时发生的异常并进行相应处理。编写代码处理某个方法可能出现的异常,可遵循如下三个原则:

  1. 在当前方法声明中使用 try catch 语句捕获异常。
  2. 一个方法被覆盖时,覆盖它的方法必须拋出相同的异常或异常的子类。
  3. 如果父类抛出多个异常,则覆盖方法必须拋出那些异常的一个子集,而不能拋出新异常。

2.异常的类型

在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 ExceptionError,如图所示。

img

Throwable 类是所有异常和错误的父类,下面有 Error 和 Exception 两个子类分别表示错误和异常。其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

  • Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
  • Error 定义了在通常环境下不希望被程序捕获的异常。Error 类型的异常用于 Java 运行时由系统显示与运行时系统本身有关的错误。堆栈溢出是这种错误的一例。
  • 本章不讨论关于 Error 类型的异常处理,因为它们通常是灾难性的致命错误,不是程序可以控制的。本章接下来的内容将讨论 Exception 类型的异常处理。

运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。

非运行时异常是指 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、ClassNotFoundException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。下表中列出了一些常见的异常类型及它们的作用。

异常类型说明
Exception 异常层次结构的根类
RuntimeException 运行时异常,多数 java.lang 异常的根类
ArithmeticException 算术谱误异常,如以零做除数
ArraylndexOutOfBoundException 数组大小小于或大于实际的数组大小
NullPointerException 尝试访问 null 对象成员,空指针异常
ClassNotFoundException 不能加载所需的类
NumberF ormatException 数字转化格式异常,比如字符串到 float 型数字的转换无效
IOException I/O 异常的根类
F ileN otF oundException 找不到文件
EOFException 文件结束
InterruptedException 线程中断
IllegalArgumentException 方法接收到非法参数
ClassCastException 类型转换异常
SQLException 操作数据库异常

3.异常的处理

1.捕获异常

捕获异常是通过3个关键词来实现的:try-catch-finally。用try来执行一段程序,如果出现异常,系统抛出一个异常,可以通过它的类型来捕捉(catch)并处理它,最后一步是通过finally语句为异常处理提供一个统一的出口,finally所指定的代码都要被执行(catch语句可有多条;finally语句最多只能有一条,根据自己的需要可有可无)。如图所示。

图6-10 异常处理.png

上面过程详细解析:

1. try:

try语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做相应的处理。

一个try语句必须带有至少一个catch语句块或一个finally语句块 。

注意事项

当异常处理的代码执行结束以后,不会回到try语句去执行尚未执行的代码。

2. catch:

n-每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。

n-常用方法,这些方法均继承自Throwable类 。

​ u-toString ()方法,显示异常的类名和产生异常的原因

​ u-getMessage()方法,只显示产生异常的原因,但不显示类名。

​ u-printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容。

n-catch捕获异常时的捕获顺序:

​ u-如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面,再不然就直接把多余的catch省略掉。 也就是先捕获子类异常再捕获父类异常。

2. finally:

n-有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到finally语句块中。

n-通常在finally中关闭程序块已打开的资源,比如:关闭文件流、释放数据库连接等。

try-catch-finally语句块的执行过程:

try-catch-finally程序块的执行流程以及执行结果比较复杂。

基本执行过程如下:

程序首先执行可能发生异常的try语句块。如果try语句没有出现异常则执行完后跳至finally语句块执行;如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。catch语句块可以有多个,分别捕获不同类型的异常。catch语句块执行完后程序会继续执行finally语句块。finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。

注意事项

  1. 即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出
  2. finally语句块只有一种情况是不会执行的,那就是在执行finally之前遇到了System.exit(0)结束程序运行

典型代码

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Test8 {
    public static void main(String[] args) {
        FileReader reader = null;
        try {
            reader = new FileReader("d:/a.txt");
            char c = (char) reader.read();
            char c2 = (char) reader.read();
            System.out.println("" + c + c2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2.抛出异常

当CheckedException产生时,不一定立刻处理它,可以再把异常throws出去。

在方法中使用try-catch-finally是由这个方法来处理异常。但是在一些情况下,当前方法并不需要处理发生的异常,而是向上传递给调用它的方法处理。

如果一个方法中可能产生某种异常,但是并不能确定如何处理这种异常,则应根据异常规范在方法的首部声明该方法可能抛出的异常。

如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。

注意事项

方法重写中声明异常原则:子类重写父类方法时,如果父类方法有声明异常,那么子类声明的异常范围不能超过父类声明的范围。

典型代码

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
 
public class Test9 {
    public static void main(String[] args) {
        try {
            readFile("joke.txt");
        } catch (FileNotFoundException e) {
            System.out.println("所需文件不存在!");
        } catch (IOException e) {
            System.out.println("文件读写错误!");
        }
    }  
    public static void readFile(String fileName) throws FileNotFoundException,          
    IOException {
        FileReader in = new FileReader(fileName);
        int tem = 0;
        try {
            tem = in.read();
            while (tem != -1) {
                System.out.print((char) tem);
                tem = in.read();
            }
        } finally {
            in.close();
        }
    }
}

4.自定义异常

  1. 在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。

  2. 自定义异常类只需从Exception类或者它的子类派生一个子类即可。

  3. 自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。

  4. 习惯上,自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。

示例

自定义异常:

/**IllegalAgeException:非法年龄异常,继承Exception类*/
class IllegalAgeException extends Exception {
    //默认构造器
    public IllegalAgeException() {
     
    }
    //带有详细信息的构造器,信息存储在message中
    public IllegalAgeException(String message) {
        super(message);
    }
}

自定义异常的使用:

class Person {
    private String name;
    private int age;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setAge(int age) throws IllegalAgeException {
        if (age < 0) {
            throw new IllegalAgeException("人的年龄不应该为负数");
        }
        this.age = age;
    }
 
    public String toString() {
        return "name is " + name + " and age is " + age;
    }
}
 
public class TestMyException {
    public static void main(String[] args) {
        Person p = new Person();
        try {
            p.setName("Lincoln");
            p.setAge(-1);
        } catch (IllegalAgeException e) {
            e.printStackTrace();
            System.exit(-1);
        }
        System.out.println(p);
    }
}

执行结果:

图6-11 示例6-11运行效果图.png

5.使用异常机制的建议

1.要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。

2.处理异常不可以代替简单测试---只在异常情况下使用异常机制。

3.不要进行小粒度的异常处理---应该将整个任务包装在一个try语句块中。

4.异常往往在高层处理(先了解!后面做项目会说!) 。

posted @ 2020-10-12 06:12  CLASSIC~W  阅读(114)  评论(0编辑  收藏  举报