JAVA6错误_Java8基础知识(六)异常

异常

所有的异常都是由Throwable继承而来的子类,分为Error和Exception两种:

Error类描述了Java运行时系统的内部错误和资源耗尽错误,应将这种异常通告给用户并尽力使程序安全地终止。

Exception分为RuntimeException和IOException两种:

RuntimeException描述由于程序错误导致的异常,例如:错误的类型转换、数组访问越界、访问空指针等等。

IOException描述非程序错误导致的异常(通常是用户输入错误导致的),例如:试图在文件尾部之后读取数据、试图打开一个不存在的文件、试图根据给定的字符串查找类对象,而字符串表示的类不存在等等。

将Error和RuntimeException称为非受查异常,而IOException称为受查异常。

声明受查异常

一个方法必须声明所有可能抛出的受查异常。而非受查异常或为不可控制的Error,或为应由程序避免发生的RuntimeException,不应当抛出。抛出异常的规范如下:

class SomeClass {

// some fields

// ...

// some methods

// ...

// method which may throw exception

public void method() throws someExcpetion {

// some code

// ...

}

}

注意:若子类覆盖了超类的某个方法,则子类中该方法不可以声明比超类更通用的异常。若超类方法没有抛出任何受查异常,则子类方法也不能抛出受查异常。

抛出异常

当一个方法声明了某个异常时,应当在异常出现时将其抛出。

class someClass {

public void method throws someException {

if (exception statements) {

throw new someException();

}

}

}

如果存在合适的异常来描述异常情况,就可以直接使用这个异常类。否则,需要自己定义异常来满足需求。

习惯上,定义的异常类应包含两个基础的构造器:默认构造器和带有详细描述信息的构造器。Throwable类的toString方法会打印这些详细信息,便于调试。

class FileFormatException extends IOException {

public FileFormatException() {}

public FileFormatException(String gripe) {

super(gripe);

}

}

捕获异常

当调用了一个抛出受查异常的方法时,必须对其进行处理,使程序能够捕获其抛出的异常或继续向外抛出该异常。

try {

// method throws exception

// ...

}catch(Exception e) {

// do something

// ...

}

注意:若超类的方法没有抛出异常,覆盖的方法也不能抛出异常,则必须捕获覆盖方法中可能出现的每一个受查异常。

异常链

当方法捕获异常而再次抛出时,应该将其包装为新的高级异常,并注意保留原始异常的细节。

以数据库异常为例:

try {

// access the database

}catch(SQLException e) {

Throwable se = new ServletException("database error");

se.initCause(e);

throw se;

}

// 捕获se时可以通过Throwable e = se.getCause()来得到原始异常

finally子句

显然try代码块与catch代码块在尾部经常包含重叠的代码。例如:资源回收处理等。可以将这些代码转移到finally子句中,这样无论是否捕获异常,这些代码都能很好地执行,且不需要重复编写。

可以通过解耦合的方式提高try/catch/finally语句块的清晰度,同时能报告finally子句中出现的错误。

InputStream in = ...;

try {

try {

// code that might throw exceptions

}

finally {

in.close();

}

}catch(Exception e) {

// show error message

}

注意:finally子句会在return 语句前执行,所以不应当在finally子句中使用return语句。

当try语句涉及资源(实现了AutoCloseable接口的类)时,try块退出或出现异常时需要在finally子句中调用AutoCloseable的close方法。但这个方法声明了异常,处理起来就会比较困难。使用带资源的try语句能很好地解决这一问题。

try(Resource res = ...) {

// work with res

}

通过上述方法编写try块,当try块正常退出或出现异常时,都会自动调用close方法,不需要使用嵌套。

否则若try块和close方法均发生异常,后者就会被自动捕获,由addSuppressed方法添加到前者中。需要调用getSuppressed方法得到从close方法抛出的被抑制的异常列表才能对这些异常进行观察,十分麻烦。

分析堆栈轨迹元素

堆栈轨迹是一个方法调用过程的列表,记录了方法调用的顺序。

可以调用Throwable类的printStackTrace方法访问堆栈轨迹的文本描述信息。

Throwable t = new Throwable();

StringWriter out = new StringWriter();

t.printStackTrace(new PrintWriter(out));

String description = out.toString();

更灵活的方法是使用getStackTrace方法,得到堆栈轨迹元素的数组。

Throwable t= new Throwable();

StackTraceElement[] frames = t.getStackTrace();

for (StackTraceElement frame : frames) {

// analyze frame

// ...

}

更全面地,可以使用静态方法Thread.getAllStackTrace来产生所有线程的堆栈轨迹。

Map map = Thread.getAllStackTrace();

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

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

// analyze frames

// ...

}

以递归阶乘方法为例:

package exception;

import java.util.Scanner;

public class StackTraceTest {

public static int factorial(int n) {

System.out.println("factorial(" + n + "):");

Throwable t = new Throwable();

StackTraceElement[] frames = t.getStackTrace();

for(StackTraceElement frame : frames) {

System.out.println(frame);

}

int r;

if(n <= 1) r = 1;

else r = n * factorial(n-1);

System.out.println("return " + r);

return r;

}

public static void main(String[] args) {

Scanner in = new Scanner(System.in);

System.out.println("Enter n: ");

int n = in.nextInt();

factorial(n);

}

}

输入结果如下:

factorial(3):

exception.StackTraceTest.factorial(StackTraceTest.java:9)

exception.StackTraceTest.main(StackTraceTest.java:25)

factorial(2):

exception.StackTraceTest.factorial(StackTraceTest.java:9)

exception.StackTraceTest.factorial(StackTraceTest.java:16)

exception.StackTraceTest.main(StackTraceTest.java:25)

factorial(1):

exception.StackTraceTest.factorial(StackTraceTest.java:9)

exception.StackTraceTest.factorial(StackTraceTest.java:16)

exception.StackTraceTest.factorial(StackTraceTest.java:16)

exception.StackTraceTest.main(StackTraceTest.java:25)

return 1

return 2

return 6

异常机制技巧

不应用异常处理代替简单的测试,会造成性能的浪费。

不应过分细化异常,会导致代码量膨胀,难以排除错误。

应使用最合适的异常类型,积极使用子类异常和自定义类异常。

不应压制异常,可能导致更加严重的错误。

应当尽早抛出存在的异常。

应灵活地包装并传递异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值