Java基础之异常篇

异常

异常的体系结构

原图片

  • Throwable从Object直接继承而来(这是java系统所强制要求的),并且它实现了 Serializable接口
  • Throwable是java语言中所有错误和异常的超类
  • Error和Exception都继承自Throwable;
Error
  • Error总是不受检查(unchecked)的
  • 用来表示系统错误,如:OutOfMemoryError、ThreadDeath,StackOverFlowError
Exception
  • 程序层次的错误
  • 分为受检查型异常和不受检查型异常

检查型和不受检查型异常

  • 检查型异常(CheckedException),在编译阶段必须使用try….catch(或throws)否则编译不通过。Java中所有继承自java.lang.Exception类的异常都是检查型异常
  • 不受检查型异常(UncheckedException) 这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的。这些异常发生在运行期,具有不确定性,主要是由程序的逻辑问题所引起。
  • RuntimeException主要有:空指针异常(NullPointerException),数组越界异常(ArrayIndexOutOfBoundException)

该问题可能会引出另一个问题,即Java和C的数组有什么不 同之处,因为C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。

问题

  1. 在Java异常处理的过程中,你遵循的那些最好的实践是什么?

    • 调用方法的时候返回布尔值来代替返回null;
    • catch块里别不写代码
    • 在Java中,一定要在数据库连接,数据库查询,流处理后,在finally块中调用close()方法
    • 能抛受控异常(checked Exception)就尽量不抛受非控异常(checked Exception)。通过去掉重复的异常处理代码,可以提高代码的可读性
  2. 既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常?

    • 存在检查型异常是一个设计上的决定,受到了诸如C++等比Java更早的编程语言设计经验的影响。绝大多数检查型异常位于java.io包内,这是合乎情理的,因为在你请求了不存在的系统资源的时候,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,Java 确保了你能够优雅的对异常进行处理。
    • 另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在你使用后尽早得到释放
  3. throw 和 throws这两个关键字在java中有什么不同?

  4. 你曾经自定义实现过异常吗?怎么写的?
    我们绝大多数都写过自定义或者业务异常,AccountNotFoundException。检查型异常

    1、定义一个类继承Throwable或其子类。

    2、添加构造方法(当然也可以不用添加,使用默认构造方法)。

    3、在某个方法类抛出该异常。

    4、捕捉该异常。

/** 自定义异常 继承Exception类 **/
public class MyException extends Exception{
    public MyException(){

    }

    public MyException(String message){
        super(message);
    }
}

public class Test {
    public void display(int i) throws MyException{
        if(i == 0){
            throw new MyException("该值不能为0.......");
        }
        else{
            System.out.println( i / 2);
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.display(0);
            System.out.println("---------------------");
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

5.执行try..catch..finally时,即使catch中有return,最终也会先执行finally中的代码,再执行return返回,只有当catch中调用System.exit()方法,才不会执行finally

Throw和Throws的区别

异常有两个过程,一个是抛出异常;一个是捕捉异常。

抛出异常有三种形式,一是throw,一个throws,还有一种系统自动抛异常

系统自动抛异常

public static void main(String[] args) {  
        String s = "abc";  
        System.out.println(Double.parseDouble(s));  
        //function();  
} 

系统会自动抛出NumberFormatException异常:

throw

一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。如:

public static void main(String[] args) {  
        String s = "abc";  
        if(s.equals("abc")) {  
            throw new NumberFormatException();  
        } else {  
            System.out.println(s);  
        }  
        //function();  
}  

Exception in thread “main” java.lang.NumberFormatException

throws

当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理

public static void function1() throws NumberFormatException{  
//public static void function1(){ 这样也可以
        System.out.println(Double.parseDouble("abc"));  
        System.out.println("第二条语句。");  //该语句不会执行
    }  

    public static void main(String[] args) {  
        try {  
            function1();  
        } catch (Exception e) {  
            System.err.println(e.getMessage());  
            //e.printStackTrace();  
        }  
}

结果如下:

For input string: “abc”

异常链

在设计模式中有一个叫做责任链模式,该模式是将多个对象链接成一条链,客户端的请求沿着这条链传递直到被接收、处理。同样Java异常机制也提供了这样一条链:异常链。

我们有两种方式处理异常,一是throws抛出交给上级处理,二是try…catch做具体处理。但是这个与上面有什么关联呢?try…catch的catch块我们可以不需要做任何处理,仅仅只用throw这个关键字将我们封装异常信息主动抛出来。然后在通过关键字throws继续抛出该方法异常。它的上层也可以做这样的处理,以此类推就会产生一条由异常构成的异常链。

通过使用异常链,我们可以提高代码的可理解性、系统的可维护性和友好性。

同理,我们有时候在捕获一个异常后抛出另一个异常信息,并且希望将原始的异常信息也保持起来,这个时候也需要使用异常链。

在异常链的使用中,throw抛出的是一个新的异常信息,这样势必会导致原有的异常信息丢失,如何保持?在Throwable及其子类中的构造器中都可以接受一个cause参数,该参数保存了原有的异常信息,通过getCause()就可以获取该原始异常信息。

   public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }
public class Test {
    public void f() throws MyException{
        try {
           FileReader reader = new FileReader("G:\\myfile\\struts.txt");  
            Scanner in = new Scanner(reader);  
            System.out.println(in.next());
       } catch (FileNotFoundException e) {
           //e 保存异常信息
           throw new MyException("文件没有找到--01",e);
       }  
   }

   public void g() throws MyException{
       try {
           f();
       } catch (MyException e) {
           //e 保存异常信息
           throw new MyException("文件没有找到--02",e);
       }
   }

   public static void main(String[] args) {
       Test t = new Test();
       try {
           t.g();
       } catch (MyException e) {
           e.printStackTrace();
       }
   }
}
class MyException extends Exception{
    public MyException(){

    }

    public MyException(String message,Throwable cause){
        super(message,cause);
    }
}
/*
java_interview.MyException: 文件没有找到--02
    at java_interview.Test.g(Test.java:42)
    at java_interview.Test.main(Test.java:49)
Caused by: java_interview.MyException: 文件没有找到--01
    at java_interview.Test.f(Test.java:33)
    at java_interview.Test.g(Test.java:39)
    ... 1 more
Caused by: java.io.FileNotFoundException: G:\myfile\struts.txt (系统找不到指定的路径。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at java.io.FileInputStream.<init>(Unknown Source)
    at java.io.FileReader.<init>(Unknown Source)
    at java_interview.Test.f(Test.java:28)
    ... 2 more
*/


如果在程序中,去掉e,也就是:throw new MyException(“文件没有找到–02”);

  那么异常信息就保存不了,运行结果如下:

com.test9.MyException: 文件没有找到–02
at com.test9.Test.g(Test.java:31)
at com.test9.Test.main(Test.java:38)

两者的区别

throws总是出现在一个函数头中,用来标明该成员函数可能抛出的异常,异常的处理交由它的调用者,所以如果一个方法会有异常发生时,但是又不想处理或者没有能力处理,就使用throws吧!

throw 出现在方法体中,抛出一个异常,程序在执行到throw语句时立即停止,它后面的语句都不执行.

两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

最佳实践

  • 尽可能的减小try块!!!
  • 保证所有资源都被正确释放。充分运用finally关键词。
  • catch语句应当尽量指定具体的异常类型,而不应该指定涵盖范围太广的Exception类。 不要一个Exception试图处理所有可能出现的异常。
  • 既然捕获了异常,就要对它进行适当的处理。不要捕获异常之后又把它丢弃,不予理睬。 不要做一个不负责的人。
  • 在异常处理模块中提供适量的错误原因信息,组织错误信息使其易于理解和阅读。
  • 不要在finally块中处理返回值。
  • 不要在构造函数中抛出异常。

参考

http://www.cnblogs.com/chenssy/p/3453039.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值