java异常——异常分类+声明已检查异常+如何抛出异常+自定义异常类

【0】README

0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java异常——异常分类+声明已检查异常+如何抛出异常+自定义异常类 的相关知识;
0.2)异常处理的任务: 就是将控制权从错误产生的地方转移给能够处理这种case 的错误处理器;
0.3)为了能够在程序中处理异常情况, 必须研究程序中可能会出现的错误和问题,以及哪类问题需要关注:

  • 0.3.1)用户输入错误:用户输入 URL , 而其语法却不正确;
  • 0.3.2)设备错误:硬件不总是让它做什么,它就做什么, 比如打印机关掉了;
  • 0.3.3)物理限制:磁盘满了,可用存储空间已被用完;
  • 0.3.4)代码错误:程序方法有可能无法正确执行;

【1】异常分类

1.1)java中, 异常都是派生于 Throwable 类的一个实例: 且如果java内置的异常类不能够满足需求,用户可以自己创建异常类;(下图显示了 java异常层次结构的简化图)
这里写图片描述

对上图的分析(Analysis)

  • A1)Error类层次结构:描述了java运行时系统的内部错误和资源耗尽错误, 这种case 很少出现;
  • A2)Exception层次结构:划分成两个分支的规则是: 由程序错误导致的异常属于 RuntimeException, 而程序本身没有问题, 但由于像 IO错误这类问题导致的异常属于其他异常;

1.2)派生于RuntimeException 的异常包含下面几种 Cases(运行时异常的几种 cases), 这属于未检查类异常(unchecked):

  • C1)错误的类型转换;
  • C2)数组访问越界;
  • C3)访问空指针;

1.3)不是派生于 RuntimeException 的异常包括:

  • C1)试图在文件尾部后面读取数据;
  • C2)试图打开一个不存在的文件;
  • C3)试图根据给定的字符串查找class 对象, 而这个字符串表示的类并不存在;

Attention)

  • A1)如果出现 RuntimeException 异常, 那么就一定是你的问题了, 这是一条相当有道理的规则;
  • A2)应该通过检测数组下标是否越界来避免 ArrayIndexOutofBoundException 异常;应该通过在变量使用前检测是否为空来杜绝 NullPointerException 异常的发生;

1.4)异常分类:

  • 1.4.1)未检查异常(不需要throw 抛出的异常,这是程序运行过程中出现的错误): java语言规范 派生于 Error类 或 RuntimeException类的所有异常称为 未检查异常(unchecked);
  • 1.4.2)已检查异常(需要显式抛出的异常):所有其他的异常称为 已检查异常(checked);
    这里写图片描述

【2】声明已检查异常

2.1)如果遇到了无法处理的case , java抛出一个异常, 这个道理很简单: 一个方法不仅需要告诉编译器将要返回什么值, 还要告诉编译器有可能发生什么错误;例如, 一段读取文件 的代码知道有可能读取的文件不存在, 或者内容为null , 因此, 试图处理文件信息的代码就需要通知编译器可能会抛出 IOException 类的异常;
2.2)方法应该在其首部声明所有可能抛出的 异常, 这样可以从首部反映出这个方法可能抛出哪类已检查异常:如,

public FileInputStream(String name) throws FileNotFoundException 

, 如果上述代码真的抛出了异常, 那么运行时系统就会开始搜索异常处理器, 以便知道如何处理 FileNotFoundException 对象;
2.3)在自己编写方法时, 不必将所有 可能抛出的异常全部都进行声明。 至于什么时候需要在方法中用 throws 子句声明异常, 什么异常必须使用 throws 子句声明, 需要记住下面遇到的 4种 Cases 应该抛出异常:

  • C1)调用一个抛出已检查异常的方法, 例如, FileInputStream 构造器;
  • C2)程序运行过程中发现错误, 并且利用 throw 语句抛出一个已检查异常;
  • C3)程序出现错误, 例如, a[-1]=0 会抛出一个 ArrayIndexOutOfBoundsException 这样的未检查异常;
  • C4) java 虚拟机和运行时库出现的内部错误;

  • 2.3.1)如果出现前两种cases 之一, 则必须告诉调用这个方法的程序员有可能抛出异常。因为任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常, 当前执行的线程就会结束;

  • 2.3.2)对于那些可能被其他人使用的 java方法, 应该根据异常规范, 在方法的首部声明这个方法可能抛出的异常;
class MyClass
{
    public Image loadImage(String s) throws IOException
}
  • 2.3.3)如果一个方法有可能抛出多个已检查异常, 那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开:
class MyClass
{
    public Image loadImage(String s) throws FileNotFoundException, EOFException
}
  • 2.3.4)但是,不需要 声明java 的内部错误, 即从Error 继承的错误。因为任何程序代码都具有抛出那些异常的潜能, 而我们对其没有任何控制能力;
  • 2.3.5)同样, 也不应该声明从 RuntimeException 继承的那些未检查异常:(因为, 这些错误完全在我们的控制之下, 如果特别关注数组下标引发的错误, 就应该将更多的时间花在 修正程序的错误上, 而不是说明这些错误发生的可能性上) //bad style 错误的异常抛出格式(RuntimeException属于未检查类异常,是不需要throw 抛出的异常,这是程序运行过程中出现的错误)
class MyClass
{
    public Image loadImage(String s) throws ArrayIndexOutOfBoundsException //bad style 错误的异常抛出格式
}

Conclusion)

  • C1)总之, 一个方法必须 声明 所有可能抛出的已检查异常, 而未检查异常要么不可控制(Error), 要么就应该避免发生(RuntimeException)。 如果方法没有声明所有可能发生的已检查异常, 编译器就会给出一个错误消息;
  • C2)当然,除了声明异常之外, 还可以捕获异常, 这样会使得异常不被抛出到方法之外,也不需要 throws 规范;(稍后, 我们来讨论如何决定一个异常是被捕获, 还是被抛出让其他的处理器进行处理)

Warning)

  • W1)如果在子类中覆盖了超类中的一个方法, 子类方法中声明的已检查异常不能比超类方法中声明的异常更通用(也就是说, 子类方法中可以抛出更特定的异常, 或者根本不抛出任何异常)
  • W2)特别需要说明的是: 如果超类方法没有抛出任何已检查异常, 子类也不能抛出任何已检查异常;

  • 2.3.6)如果类中的一个方法声明将会抛出一个异常, 而这个异常是某个特定类的实例时, 则这个方法就有可能抛出一个这个类的异常, 或者这个类的任意一个子类的异常;看个荔枝:

    • 例如: FileInputStream 构造器声明将有可能抛出一个 IOException异常,然而并不知道具体是哪种IOException异常, 它既可能是 IOException异常,也可能是其子类的异常,例如, FileNotFoundException;

【3】如何抛出异常

3.1)首先要决定抛出什么类型异常 。如, EOFException异常描述为“在输入过程中, 遇到了一个未预期的EOF后 的信号”;
3.2)下面抛出这个异常的语句:

throw new EOFException();
或者, 
EOFException e = new EOFException();
throw e;
  • 3.2.1)EOFException类还有一个字符串参数的构造器。这个构造器更加详细的描述异常出现的情况:
String gripe = "content-length" + len + ", received: " + n;
throw new EOFException(gripe);
  • 3.2.2)在以下cases 下, 一旦方法抛出异常, 这个方法就不可能返回到调用者了;
    • case1)找到一个合适的异常类;
    • case2)创建这个类的一个对象;
    • case3)将对象抛出;

【4】创建异常类(自定义异常类)

4.1)我们需要做的只是:定义一个派生于Exception的类, 或者派生于Exception子类的类;
4.2)定义的类应该包含两个构造器: 一个是默认的构造器, 另一个是带有详细描述信息的构造器(超类Throwable 的toString 方法将会打印出这些详细信息, 在调试中非常有用)

class FileFormatException extends IOException
{
    public FileFormatException() {} //1、默认构造器
    public FileFormatException(String gripe) //2、带有详细描述信息的构造器
    {
        super(gripe);
    }
}
  • 4.2.1)现在,就可以抛出自定义的异常类型了:
String readData(BufferedReader in) throws FileFormatException
{
    throw new FileFormatException();
}

[API] java.lang.Throwable 1.0

  • Throwable() :构造一个新的 Throwable 对象, 这个对象没有详细的描述信息;
  • Throwable(String msg):构造一个新的 Throwable 对象, 这个对象包含详细的描述信息;习惯上, 所有派生的异常类都支持一个默认构造器和一个带有描述信息的构造器;
  • String getMessage(): 获得Throwable 对象 的详细描述信息;
展开阅读全文

没有更多推荐了,返回首页