Java学习笔记005——异常、断言

异常

Java使用“异常处理的错误捕获机制”处理异常,异常处理的任务是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。

1、异常对象都是派生于Throwable类的一个实例

下面是Java异常层次结构的示意图:

 

① Error类层次描述了Java运行时系统的内部错误和资源耗尽错我,应用程序不应该跑出这种类型的对象,如果出现了这样的内部错误,除了通告给用户并尽力使程序安全地终止之外别无他法

② 由程序错误导致的异常属于RuntimeException;而程序本身没问题,但由于像I/O错误这类问题导致的异常属于其他异常

派生于RuntimeException的异常包含以下情况:

· 错误的类型转换

· 数组访问越界

· 访问空指针

——注意:出现RuntimeException异常往往是程序本身的问题,应该在写程序的时候通过检测来杜绝这种异常的发生

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

· 试图在文件尾部后面读取数据

· 试图打开一个错误格式的URL

· 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在

Java语言规范将派生于Error类或RuntimeException类的所有异常成为“未检查异常”,所有其他的异常成为“已检查异常

2、如果遇到无法处理的情况,那么Java的方法可以抛出一个异常,即:一个方法不仅需要高数编译器将要返回什么值,还要高数编译器有可能发生什么错误,方法应该在其首部声明所有可能抛出的异常,这样可以冲首部反映出这个方法可能抛出哪类已检查异常。如:public FileInputStream(String name) throws FileNotFoundException在自己编写方法时,不必将所有可能抛出的异常都进行声明,在下面4种情况下应该抛出异常

① 调用一个抛出已检查异常的方法

② 程序运行过程中发现错误,并且利用throw语句抛出一个已检查异常

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

③ 程序出现错误

④ Java虚拟机和运行时库出现的内部异常

如果一个方法可能抛出多个已检查异常,那么必须在方法的首部列出所有的异常类,每个异常类之间用逗号隔开,如:public Image loadImage(String s) throws EOFException, MalformedURLException {  }

—— 不需要声明Java的内部错误,即从Error继承的错误

—— 不应该声明从RuntimeException继承的那些未检查异常,因为这些运行时错误完全在我们的控制之下。

—— 总之,一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么就应该避免发生(RuntimeException),如果一个方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误信息

—— 如果在子类中覆盖了超类的一个方法,子类方法中声明的已检查异常不能超过超类方法中声明的异常范围,特别地,如果超类方法没有抛出任何已检查异常,子类也不能抛出任何已检查异常

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

3、抛出异常的语句:throw new EOFException(); 或者 EOFException e = new EOFEception(); throw e; 如:

void abc(int i) throws EOFException {

if (i < 3) throw new EOFException();

}

对于一个已经存在的异常类,将其抛出是非常容易的,步骤是:① 找到一个合适的异常类 ② 创建这个类的一个对象 ③ 将对象抛出 一旦方法抛出了异常,这个方法就不可能返回到调用者

4、创建自己的异常类

有时候任何标准异常类都没能够充分描述清楚问题,在这种情况下,就需要创建自己的异常类了,需要做的是定义一个派生于Exception的类,或者派生于Exception子类的类,如IOException类,习惯上定义的类应该包含两个构造器,一个是默认的构造器,一个是带有详细描述信息的构造器,如下例子:

class FileFormatException extends IOException{

public FileFormatException() { };

public FileFormatException(String gripe) { super(gripe); }

}  //自定义的异常类

void abc(int i) throws FileFormatException {

if (i < 3) throw new FileFormatException();

}

5、捕获异常,如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容,要想捕获一个异常,必须设置try/catch语句块,如:

try{ code }

catch(ExceptionType e){ code }

① 如果在try语句块中的任何代码抛出了一个在cathc子句中说明的异常类,那么

· 程序将跳过try语句块的其余代码

· 程序将执行catch子句中的处理代码

② 如果在try语句块中的代码没有抛出任何异常,程序将跳过catch子句

③ 如果方法中的任何代码抛出一个在catch子句中没有声明的异常类型,那么这个方法就会立即退出,期待调用该方法的人为这种类型的异常设计了catch子句

④ 如果A异常在方法中的catch子句已经处理了,那么就不需要抛出到方法外,没被处理的异常才需要使用throws抛出到方法外让调用者来处理

如果调用一个抛出已检查异常的方法,应该捕获那些知道如何处理的异常,而将那些不知道怎么处理的异常传递出去,如果想将异常传递出去,就必须在方法的首部添加throws说明符,以便告知调用者这个方法可能会抛出异常

6、捕获多个异常,如下所示:

try{ code }

catch(MalformedURLException e1) { code }

catch(UnknownHostException e2) { code }

catch(IOException e3) { code }

异常对象可能包含与异常本身有关的信息,要想获得对象的更多信息,可以试着使用e1.getMassage();得到详细的错误信息(如果有的话),或者使用e1.getClass().getName();得到异常对象的实际类型

7、finally子句,如下所示:

try{ code }

catch(IOException e){ code }

finally{ code }

finally子句里的代码无论在有或者没用抛出异常的情况下都会被执行,分下面三种情况:

① 代码没有抛出异常的情况下,程序首先执行try语句块中的全部代码,然后执行finally子句中的代码

② 抛出一个在catch子句中捕获的异常的情况下,程序将执行try语句直到发生异常处,跳过try语句块中的剩余代码,转去执行catch子句中的代码,最后执行finally子句的代码

③ 抛出一个没用catch子句捕获的异常的情况下,程序将执行try语句直到发生异常处,跳过try语句块中的剩余代码,转去执行finally子句的代码

try语句中可以只有finally子句而没用catch子句,如:

try{ code }

finally{ code }

在需要关闭资源时使用finally子句是不错的选择

—— 当finally子句包含return语句时,将会出现一种意想不到的结果,假设利用return语句从try语句块中退出,在方法返回前,finally子句的内容将被执行,如果finally子句中也有一个return语句,这个返回值将会覆盖原始的返回值

8、分析堆栈跟踪元素

① 堆栈跟踪是一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置,可以调用getStackTrace方法获得一个StatckTraceElement对象的数组,并在程序中对其进行分析,如:

Throwable t = new Throwable();

StackTraceElement[] frames = t.getStackTrace();

for(StackTraceElement frame : frames) {

analyze frame

}

StackTraceElement类包含能够获得文件名和当前执行的代码行号的方法,同时还含有能够获得类名和方法名的方法,toString方法将产生一个格式化的字符串,其中包含所获得的信息,在API的java.lang.StackTraceElement下可以找到

② 在Java SE 5.0中增加了一个静态的Thread.getAllStackTrace方法,可以产生所有线程的堆栈跟踪,如:

Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();

for(Thread t : map.keySet()){

StackTraceElement[] frames = map.get(t);

analyze frames

}

断言

断言机制允许在测试期间向代码中插入一些检查语句,当代码发布时,这些插入的检测语句将会被自动移走

1、Java语言引入了关键字assert,有两种形式:

① assert 条件 ;

② assert 条件 : 表达式 ;

这两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常,在第二种形式中,表达式将被传入AssertionError的构造器,并转换成一个消息字符串

—— “表达式”部分的唯一目的是产生一个消息字符串,AssertionError对象并不存储表达式的值,如:要想断言x是一个非负数值,只需要简单地使用:assert x >= 0 ; 或者将x的实际值传递给AssertionError对象,从而可以在后面显示出来:assert x >= 0 : x ; 如果希望看到这个条件,就必须将它以字符串的形式传递给AssertionError对象:assert x >= 0 : “x >= 0”

2、启用和禁用断言

① 默认情况下,断言被禁用,可以在运行程序时用 –enableassertions或 –ea选项启用它:java –enableassertions MyApp

—— 在启用或禁用断言时不必重新编译程序,启用或禁用断言是类加载器的功能,当断言被禁用时类加载器将跳过断言代码;

② 也可以在某个类或某个包中使用断言,如:java –ea:MyClass –ea:com.mycompany.mylib... MyApp,这条命令将开启MyClass类以及在com.mycompany.mylib包和它的子包中的所有类的断言,选项-ea将开启默认包中的所有类的断言

③ 也可以使用-disableassertions或-da禁用某个特定类和包的断言:java –ea:... –da:MyClass MyApp

—— 有些类不是由类加载器加载,而是直接由虚拟机加载,可以使用这些开关有选择性地启用或禁用那些类中的断言,然而,启用和禁用所有断言的-ea和-da开关不能应用到那些没有类加载器的“系统类”上,对于这些系统类来说,需要使用-enablesystemassertions/-esa开关启用断言

3、断言检查只用于开发和测试阶段,只应该用于在测试阶段确定程序内部的错误位置,如测试一段代码,有一个程序员默认的条件,这时候需要使用断言来保证这个条件的成立,否则会影响测试结果

原文地址:https://www.cnblogs.com/oushihua/p/2935829.html