Java异常处理和设计

来源:互联网 发布:电脑上什么看淘宝直播 编辑:程序博客网 时间:2024/05/01 22:54

Java异常处理和设计

  在程序设计中,进行异常处理是非常关键和重要的一部分。一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度。试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻找出错的根源?但是如果一个项目异常处理设计地过多,又会严重影响到代码质量以及程序的性能。因此,如何高效简洁地设计异常处理是一门艺术,本文下面先讲述Java异常机制最基础的知识,然后给出在进行Java异常处理设计时的几个建议。

  若有不正之处,请多多谅解和指正,不胜感激。

  请尊重作者劳动成果,转载请标明转载地址:http://www.cnblogs.com/dolphin0520/p/3769804.html

  以下是本文的目录大纲:

  一.什么是异常
  二.Java中如何处理异常
  三.深刻理解try,catch,finally,throws,throw五个关键字
  四.在类继承的时候,方法覆盖时如何进行异常抛出声明
  五.异常处理和设计的几个建议

一.什么是异常                                                                              

  异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图。错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的。假若程序在运行期间出现了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这不是我们希望看到的结果。因此,如何对运行期间出现的错误进行处理和补救呢?Java提供了异常机制来进行处理,通过异常机制来处理程序运行期间出现的错误。通过异常机制,我们可以更好地提升程序的健壮性。

  在Java中异常被当做对象来处理,根类是java.lang.Throwable类,在Java中定义了很多异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。

  Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。

  Exception,也就是我们经常见到的一些异常情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常。

  Exception类的异常包括checked exception和unchecked exception(unchecked exception也称运行时异常RuntimeException,当然这里的运行时异常并不是前面我所说的运行期间的异常,只是Java中用运行时异常这个术语来表示,Exception类的异常都是在运行期间发生的)。

  unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。

  checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。

  在Java中,异常类的结构层次图如下图所示:

  

  在Java中,所有异常类的父类是Throwable类,Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是所有运行时异常的父类,RuntimeException以外的并且继承Exception的类是非运行时异常。

  典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。

  典型的非RuntimeException包括IOException、SQLException等。

二.Java中如何处理异常                                                                 

   在Java中如果需要处理异常,必须先对异常进行捕获,然后再对异常情况进行处理。如何对可能发生异常的代码进行异常捕获和处理呢?使用try和catch关键字即可,如下面一段代码所示:

1
2
3
4
5
6
7
try {
  File file =newFile("d:/a.txt");
  if(!file.exists())
    file.createNewFile();
} catch(IOException e) {
  // TODO: handle exception
}

  被try块包围的代码说明这段代码可能会发生异常,一旦发生异常,异常便会被catch捕获到,然后需要在catch块中进行异常处理。

  这是一种处理异常的方式。在Java中还提供了另一种异常处理方式即抛出异常,顾名思义,也就是说一旦发生异常,我把这个异常抛出去,让调用者去进行处理,自己不进行具体的处理,此时需要用到throw和throws关键字。 

  下面看一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
    publicstaticvoidmain(String[] args) {
        try{
            createFile();
        }catch(Exception e) {
            // TODO: handle exception
        }
    }
     
    publicstaticvoidcreateFile()throwsIOException{
        File file =newFile("d:/a.txt");
        if(!file.exists())
            file.createNewFile();
    }
}

  这段代码和上面一段代码的区别是,在实际的createFile方法中并没有捕获异常,而是用throws关键字声明抛出异常,即告知这个方法的调用者此方法可能会抛出IOException。那么在main方法中调用createFile方法的时候,采用try...catch块进行了异常捕获处理。

  当然还可以采用throw关键字手动来抛出异常对象。下面看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
    publicstaticvoidmain(String[] args) {
        try{
            int[] data =newint[]{1,2,3};
            System.out.println(getDataByIndex(-1,data));
        }catch(Exception e) {
            System.out.println(e.getMessage());
        }
         
    }
     
    publicstaticintgetDataByIndex(intindex,int[] data) {
        if(index<0||index>=data.length)
            thrownewArrayIndexOutOfBoundsException("数组下标越界");
        returndata[index];
    }
}

  然后在catch块中进行捕获。

  也就说在Java中进行异常处理的话,对于可能会发生异常的代码,可以选择三种方法来进行异常处理:

  1)对代码块用try..catch进行异常捕获处理;

  2)在 该代码的方法体外用throws进行抛出声明,告知此方法的调用者这段代码可能会出现这些异常,你需要谨慎处理。此时有两种情况:

    如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

    如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

  3)在代码块用throw手动抛出一个异常对象,此时也有两种情况,跟2)中的类似:

    如果抛出的异常对象是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

    如果抛出的异常对象是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

  (如果最终将异常抛给main方法,则相当于交给jvm自动处理,此时jvm会简单地打印异常信息)

三.深刻理解try,catch,finally,throws,throw五个关键字                   

   下面我们来看一下异常机制中五个关键字的用法以及需要注意的地方。

1.try,catch,finally

  try关键字用来包围可能会出现异常的逻辑代码,它单独无法使用,必须配合catch或者finally使用。Java编译器允许的组合使用形式只有以下三种形式:

  try...catch...;       try....finally......;    try....catch...finally...

  当然catch块可以有多个,注意try块只能有一个,finally块是可选的(但是最多只能有一个finally块)。

  三个块执行的顺序为try—>catch—>finally。

  当然如果没有发生异常,则catch块不会执行。但是finally块无论在什么情况下都是会执行的(这点要非常注意,因此部分情况下,都会将释放资源的操作放在finally块中进行)。

  在有多个catch块的时候,是按照catch块的先后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配。

  在使用try..catch..finally块的时候,注意千万不要在finally块中使用return,因为finally中的return会覆盖已有的返回值。下面看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
 
 
public class Main {
    publicstaticvoidmain(String[] args) {
        String str =newMain().openFile();
        System.out.println(str);
         
    }
     
    publicString openFile() {
        try{
            FileInputStream inputStream =newFileInputStream("d:/a.txt");
            intch = inputStream.read();
            System.out.println("aaa");
            return"step1";
        }catch(FileNotFoundException e) {
            System.out.println("file not found");
            return"step2";
        }catch(IOException e) {
            System.out.println("io exception");
            return"step3";
        }finally{
            System.out.println("finally block");
            //return "finally";
        }
    }
}

  这段程序的输出结果为:

  可以看出,在try块中发生FileNotFoundException之后,就跳到第一个catch块,打印"file not found"信息,并将"step2"赋值给返回值,然后执行finally块,最后将返回值返回。

  从这个例子说明,无论try块或者catch块中是否包含return语句,都会执行finally块。

  如果将这个程序稍微修改一下,将finally块中的return语句注释去掉,运行结果是:

  

  最后打印出的是"finally",返回值被重新覆盖了。

  因此如果方法有返回值,切忌不要再finally中使用return,这样会使得程序结构变得混乱。

 2.throws和thow关键字

  1)throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,允许throws后面跟着多个异常类型;

  2)一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出现在方法体中,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw出去。throw关键字的一个非常重要的作用就是异常类型的转换(会在后面阐述道)。

  throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由方法去处理异常,真正的处理异常由此方法的上层调用处理。

四.在类继承的时候,方法覆盖时如何进行异常抛出声明                        

   本小节讨论子类重写父类方法的时候,如何确定异常抛出声明的类型。下面是三点原则:

  1)父类的方法没有声明异常,子类在重写该方法的时候不能声明异常;

  2)如果父类的方法声明一个异常exception1,则子类在重写该方法的时候声明的异常不能是exception1的父类;

  3)如果父类的方法声明的异常类型只有非运行时异常(运行时异常),则子类在重写该方法的时候声明的异常也只能有非运行时异常(运行时异常),不能含有运行时异常(非运行时异常)。

  

五.异常处理和设计的几个建议                                                         

   以下是根据前人总结的一些异常处理的建议:

1.只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程

  谨慎地使用异常,异常捕获的代价非常高昂,异常使用过多会严重影响程序的性能。如果在程序中能够用if语句和Boolean变量来进行逻辑判断,那么尽量减少异常的使用,从而避免不必要的异常捕获和处理。比如下面这段经典的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void useExceptionsForFlowControl() {  
  try{  
  while(true) {  
    increaseCount();  
    }  
  }catch(MaximumCountReachedException ex) {  
  }  
  //Continue execution  
}  
    
public void increaseCount() throwsMaximumCountReachedException {  
  if(count >=5000)  
    thrownewMaximumCountReachedException();  
}

  上边的useExceptionsForFlowControl()用一个无限循环来增加count直到抛出异常,这种做法并没有说让代码不易读,而是使得程序执行效率降低。

2.切忌使用空catch块

  在捕获了异常之后什么都不做,相当于忽略了这个异常。千万不要使用空的catch块,空的catch块意味着你在程序中隐藏了错误和异常,并且很可能导致程序出现不可控的执行结果。如果你非常肯定捕获到的异常不会以任何方式对程序造成影响,最好用Log日志将该异常进行记录,以便日后方便更新和维护。

3.检查异常和非检查异常的选择

  一旦你决定抛出异常,你就要决定抛出什么异常。这里面的主要问题就是抛出检查异常还是非检查异常。

  检查异常导致了太多的try…catch代码,可能有很多检查异常对开发人员来说是无法合理地进行处理的,比如SQLException,而开发人员却不得不去进行try…catch,这样就会导致经常出现这样一种情况:逻辑代码只有很少的几行,而进行异常捕获和处理的代码却有很多行。这样不仅导致逻辑代码阅读起来晦涩难懂,而且降低了程序的性能。

  我个人建议尽量避免检查异常的使用,如果确实该异常情况的出现很普遍,需要提醒调用者注意处理的话,就使用检查异常;否则使用非检查异常。

  因此,在一般情况下,我觉得尽量将检查异常转变为非检查异常交给上层处理。

4.注意catch块的顺序

  不要把上层类的异常放在最前面的catch块。比如下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
        FileInputStream inputStream =newFileInputStream("d:/a.txt");
        intch = inputStream.read();
        System.out.println("aaa");
        return"step1";
    }catch(IOException e) {
        System.out.println("io exception");        
         return"step2";
    }catch(FileNotFoundException e) {
        System.out.println("file not found");          
        return"step3";
    }finally{
        System.out.println("finally block");
        //return "finally";
    }

  

  第二个catch的FileNotFoundException将永远不会被捕获到,因为FileNotFoundException是IOException的子类。

5.不要将提供给用户看的信息放在异常信息里

  比如下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
    publicstaticvoidmain(String[] args) {
        try{
            String user =null;
            String pwd =null;
            login(user,pwd);
        }catch(Exception e) {
            System.out.println(e.getMessage());
        }
         
    }
     
    publicstaticvoidlogin(String user,String pwd) {
        if(user==null||pwd==null)
            thrownewNullPointerException("用户名或者密码为空");
        //...
    }
}

  展示给用户错误提示信息最好不要跟程序混淆一起,比较好的方式是将所有错误提示信息放在一个配置文件中统一管理。

6.避免多次在日志信息中记录同一个异常

  只在异常最开始发生的地方进行日志信息记录。很多情况下异常都是层层向上跑出的,如果在每次向上抛出的时候,都Log到日志系统中,则会导致无从查找异常发生的根源。

7. 异常处理尽量放在高层进行

  尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂。

8. 在finally中释放资源

  如果有使用文件读取、网络操作以及数据库操作等,记得在finally中释放资源。这样不仅会使得程序占用更少的资源,也会避免不必要的由于资源未释放而发生的异常情况。                                    

参考资料:http://blessht.iteye.com/blog/908286

     http://www.oschina.net/translate/10-exception-handling-best-practices-in-java-programming

       http://blog.csdn.net/snow_fox_yaya/article/details/1823205

       http://www.iteye.com/topic/72170

       http://www.blogjava.net/gdws/archive/2010/04/25/319342.html

       http://www.2cto.com/kf/201403/284166.html

       http://www.iteye.com/topic/857443

     http://developer.51cto.com/art/200808/85625.htm

     http://www.cnblogs.com/JavaVillage/articles/384483.html

     http://tech.e800.com.cn/articles/2009/79/1247105040929_1.html

     http://blog.csdn.net/zhouyong80/article/details/1907799

     http://blog.csdn.net/luoweifu/article/details/10721543                                  

     《Effective Java中文版》   

出处:http://www.cnblogs.com/dolphin0520/

常见RuntimeException:

ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常
ClassCastException 试图将对象强制转换为不是实例的子类时,抛出该异常
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出
NoSuchElementException 表明枚举中没有更多的元素
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常

为什么Java代码中必须try catch 或者throw异常,不然编译不通过


核心原因:语言设计者觉得这样设计是最好的,O(∩_∩)O~

至于为什么python不是这么设计的,C#不是这么设计的,核心原因也是他们的语言设计者觉得不用catch异常是最好的,O(∩_∩)O~

那到底是显式去处理更好,还是不用显式去处理更好呢?
我来谈一下我的个人见解:

首先我们来定义什么叫异常,java中设计了两种类型的异常:checked exception和unchecked exception(即runtimeException),checked exception是需要显式捕获的,他向上层代码传递了一些信息,比如你校验用户登陆的时候,如果用户名在数据库中查不到,你可以抛出UserNotFoundException(定义为checked exception)来给上层代码传递这个User not found的信息,比如密码不对,可以创建一个PasswordErrorException。

再来说说RuntimeException,这种异常不需要显式捕获,比如IllegalArgumentException(参数不合法),DivideZeroException(除以0的异常),这种异常更像是一个错误的调用方法导致的,它在给上层代码传递的信息是:你用了一个很不对的方法在调用我,这是错误的,后果很严重哦,你如果没有防范措施(没有显式捕获),我可是会让终止这次方法调用链哦

对异常这个概念大家本身就没有一个统一的认识,比如python的设计者觉得:往上层代码传递信息根本不用checked exception,直接用多个方法返回值就可以了,所以他就只设计了第二种异常

Java异常分为两种类型,checked异常和unchecked异常,另一种叫法是异常和错误。

简单地讲,checked是可以在执行过程中恢复的,而unchecked异常则是无法处理的错误。

checked异常:

  • 表示无效,不是程序中可以预测的。比如无效的用户输入,文件不存在,网络或者数据库链接错误。这些都是外在的原因,都不是程序内部可以控制的。
  • 必须在代码中显式地处理。比如try-catch块处理,或者给所在的方法加上throws说明,将异常抛到调用栈的上一层。
  • 继承自java.lang.Exception(java.lang.RuntimeException除外)。

unchecked异常:

  • 表示错误,程序的逻辑错误。是RuntimeException的子类,比如IllegalArgumentException,NullPointerException和IllegalStateException。
  • 不需要在代码中显式地捕获unchecked异常做处理。
  • 继承自java.lang.RuntimeException(而java.lang.RuntimeException继承自java.lang.Exception)。

Java中的checked异常需要在代码中显式地通过try-catch捕获或者再抛出,如果不需要对这个异常做处理,可以简地将异常再次抛出,这种异常存在一点不足,很多人习惯在代码中直接写一个空的catch块,这样不但使代码变得有些冗余和“丑陋”,而且给调试带来麻烦,增加了代码维护的难度。所以有人说checked使代码变的冗长,空的catch块也没什么意义,所以checked异常应该从Java标准中去除,像C#中就没有checked异常的概念,C#中不强制显式地捕获异常。

Java异常之所以会分为这两种,应该是出于如下考虑:

checked异常可以帮助开发人员意识到哪一行有可能会出现异常,因为Java的API已经说明了调用哪些方法可能会抛出异常。如果不做处理编译就不能通过,从某种程度上说,这种做法可以避免程序的一些错误。

两个简单的例子

1. checked异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class Main{
public static voidmain(String[] args) {
Filef = new File("C:\test.txt");
FileReader r = new FileReader(f); //A
BufferedReader br = new BufferedReader(r);
br.readLine(); //B
br.close(); //C
}
}

这段代码不能通过编译,原因是A,B和C行会抛出IOException,必须将这段代码放入try-catch块中,或者给main方法加上throwsIOException才能编译。

2. unchecked异常

1
2
3
4
5
6
7
8
9
10
11
public class Main{
public static voidmain(String[] args) {
int a =0;
int b =100;
int c =b/a;
}
}

可以编译,但执行会报错

Exception in thread “main” java.lang.ArithmeticException: / byzero
at Main.main(Main.java:13)

ArithmeticException是一个unchecked异常。

自定义异常

1. checked异常

自定义异常类InvalidUrlException

1
2
3
4
5
public class InvalidUrlException extends Exception{
public InvalidUrlException(String s){
super(s);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main{
public static voidgetRemoteData(String url)throwsInvalidUrlException{
if(isValidUrl(url)){
//获取远程数据
}
else
throw newInvalidUrlException("Invalid URL: "+url);
}
public static boolean isValidUrl(String url){
.... //验证URL是否有效
}
public static voidmain(String[] args) {
getRemoteData(args[0]);
}
}

如果在main方法里调用getRemoteData,有两种做法,一种是try-catch,另一种则是直接给main加上throwsInvalidUrlException。

2. unchecked异常

如果将InvalidUrlException改成extends RuntimeException,

1
2
3
4
5
public class InvalidUrlException extends Exception{
public InvalidUrlException(String s){
super(s);
}
}

那么main不需要再加throws或者try-catch。

选择checked还是unchecked异常?

一些Java书籍建议使用checked异常处理程序中所有可恢复的异常,而用unchecked异常作为无法恢复的错误。但是实际上那些继承自RuntimeException的Java异常大多也可以在程序中恢复的,比如NullPointerException、IllegalArgumentExceptions、除0异常等等都可以通过捕获处理使程序继续运行。只有一些特殊情况会破坏程序的执行,比如启动的时候读取配置文件,如果配置文件不存在或者有严重错误,程序只好退出。

以下是是支持和反对checked异常的一些观点:

  1. 编译器强制捕获或者抛出unchecked异常使开发人员时刻记着要处理异常。
  2. 抛出checked异常的方法必须声明throws,throws成为了方法或者接口的一部分,给后续版本增加或者修改方法的异常带来不便。
  3. unchecked异常不需要显式地处理反而使异常处理变的困难。
  4. 调用checked异常的方法,就必须处理这个方法的异常,这使得上层的调用者代码混乱。

选择checked还是unchecked都是由你自己决定的,很难说哪种就一定正确,反之就一定错误。目前比较流行的语言中,Java似乎是唯一支持checked异常的语言,其他语言都只有unchecked异常。


异常
现有的程序开发技术和测试技术无法确保程序中不存在错误,很多错误不是程序编译时出现(也就是代码错误),而是在运行时由于各种原因(突然断网、文件不存在了)产生的。
几个概念
编译错误:编译阶段语法检查,代码书写不满足语法规则。
运行时错误:指令执行条件的错误,语义错误。
逻辑错误:设计不满足要求,未能得到期望结果。
异常:程序在运行时出现的错误。(这里的运行时错误这一概念泛指运行期间发生的错误,与java的RuntimeException类所涵盖的范畴不同,后者仅仅是前者的一个子集)
异常分类:(Java提供了Throwable类,只有Throwable类或其子类的对象,才能被虚拟机或throw语句抛出,并被catch语句捕获;Throwable类有两个子类:Error和Exception刻画了不同程度的异常)
Error:比较严重的灾难性错误(不可捕获,任其报错)。如:OutOfMemoryError(内存不足)、ThreadDeath、IOError、LinkageError(库链接错误)等。这些异常发生时,JVM一般会选择线程终止。
Exception:程序本身可以处理的异常(可捕获)。分为以下两种
运行时异常:RuntimeException类及其子类异常,JVM正常操作期间抛出的异常,不需要事先声明。可不捕获,JVM会接管(RuntimeExceptionand its subclasses are uncheckedexceptions.Unchecked exceptions donotneed to be declared in a method or constructor'sthrowsclause if they can be thrown by the execution of the method orconstructor and propagate outside the method or constructorboundary.)如:ArithmeticException(算术异常)、BufferOverflowException(缓冲区上益)、BufferUnderflowException、NullPointerException(空指针)。
非运行时异常:RuntimeException以外的异常。必须用catch子句或throws子句指明可能抛出何种异常,否则编译不通过。如:IOException(IO异常)、AWTException(AWT异常)、InterruptedException(中断异常)、TimeoutException(超市异常)
这是别人博客上写的,感觉很好:(出现运行时异常后,系统会把异常一直往上层抛,一直遇到处理代码。如果没有处理块,到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是Exception的子类,也有一般异常的特点,是可以被Catch块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。如果不想终止,则必须扑捉所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。在这个场景这样处理可能是一个比较好的应用,但并不代表在所有的场景你都应该如此。如果在其它场景,遇到了一些错误,如果退出程序比较好,这时你就可以不太理会运行时异常,或者是通过对异常的处理显式的控制程序退出。)
Java异常处理机制
java将程序中运行时出现的各种错误统一处理成“异常对象”,冰箱外抛出;
捕获并处理这个异常对象,处理策略是:处理或 声明
处理:用try-catch机制实现;用try监控可能出现异常的代码段,用catch捕获和处理异常;
声明:就是声明自己不处理,将异常对象抛给它的调用者,用throws机制实现(在方法声明部分用throws指明本方法可能抛出的异常,以提醒调用者务必采取相应的措施)
(借助JVM的支持,java的异常处理与传统处理方式有很大不同。由于字节码在执行前需要由虚拟机将其翻译成二进制代码,然后执行。JVM可以在执行前根据当时状况对执行效果进行预测。若是发生异常,则由虚拟机创建异常对象并抛出)
JVM的工作模式:监控模式和 正常模式
JVM对执行结果预测将会一定程度上影响代码执行效率,因此JVM只对try-catch-finally语句和throws标记的代码段实施监控预测模式。正常模式下,发生的错误只能被Java虚拟机捕获和处理(比如Error和没有捕获的运行时异常哦,将会终止JVM运行)
异常的声明(throws)和外抛(throw)
格式
返回类型 方法名(参数列表) throws 异常列表 {方法体}
throw异常对象的引用
实例
 
[java]  
import java.io.IOException;  
import java.util.Scanner;  
  
public class Test {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        try {  
            f();  
        } catch (ArrayIndexOutOfBoundsException e) {  
            // TODO Auto-generated catch block  
            System.out.println("数组下标越界异常");  
        }catch (ArithmeticException e) {  
            // TODO: handle exception  
            System.out.println("算术异常");  
        }catch (Exception e) {  
            // TODO: handle exception  
            System.out.println(e.getMessage()+"异常");  
        }  
          
    }  
    public static void f() throws ArithmeticException,ArrayIndexOutOfBoundsException,Exception{  
        int[] x={1,2};  
        int y,z;  
        while(true){  
            System.out.print("请输入一个整数,-1表示结束:");  
            Scanner in=new Scanner(System.in);  
            y=in.nextInt();  
            if(y==-1)   break;  
            if(y==2)    throw new IOException();    // 假如没有声明Exception的话,这里就会编译错误  
            z=x[y]/y;  
        }  
    }  
}  
 
异常传播
来看段代码:
[java] 
public class Test {  
    public static void main(String[] args) {  
        a();  
    }  
    static void a(){  
        b();  
    }  
    static void b(){  
        c();  
    }  
    static void c(){  
        int i=5/0;  
    }  
}  
运行结果:  
Exception in thread "main" java.lang.ArithmeticException: / by zero  
    at test.Test.c(Test.java:36)  
    at test.Test.b(Test.java:33)  
    at test.Test.a(Test.java:30)  
    at test.Test.main(Test.java:27)  
方法调用顺序:  
main → a → b → c  
异常传递顺序:  
c → b → a → main → JVM(JVM终止)  
由于ArithmeticException是运行时异常不需要捕获。  
注意点
1. 当try语句块中有return语句,finally也将执行
[java]  
int f(){  
    try{  
        return 5;  
    }finally{  
        return 10;  
    }  
}//此方法返回的是10  
2.throw有类似return的强力转向功能,即throw后的语句不执行,并且导致编译无法通过。
3. 重写方法中www.2cto.com
 
[java]  
class A{ // 假如这两个异常调换,会报B类中的异常不能跟A类中的兼容  
    void f() throws Exception{  
          
    }  
}  
class B extends A{  // 也就是说这个方法中的异常必须和上面方法中的相同或是其子类  
    void f() throws IOException{  
          
    }  
}  
 


0 0
原创粉丝点击