java异常处理详解!!

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java异常处理详解!!相关的知识,希望对你有一定的参考价值。

参考技术A 异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出现,那么你每个地方都要做相同处理,感觉相当的麻烦! Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个类型来表示,不同类型的异常对应不同的子类异常(这里的异常包括错误概念),定义异常处理的规范,在1.4版本以后增加了异常链机制,从而便于跟踪异常!这是Java语言设计者的高明之处,也是Java语言中的一个难点,下面是我对Java异常知识的一个总结,也算是资源回收一下。
一、Java异常的基础知识
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异常。 有些异常需要做处理,有些则不需要捕获处理,后面会详细讲到。 天有不测风云,人有旦夕祸福,Java的程序代码也如此。在编程过程中,首先应当尽可能去避免错误和异常发生,对于不可避免、不可预测的情况则在考虑异常发生时如何处理。 Java中的异常用对象来表示。Java对异常的处理是按异常分类处理的,不同异常有不同的分类,每种异常都对应一个类型(class),每个异常都对应一个异常(类的)对象。 异常类从哪里来?有两个来源,一是Java语言本身定义的一些基本异常类型,二是用户通过继承Exception类或者其子类自己定义的异常。Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。 异常的对象从哪里来呢?有两个来源,一是Java运行时环境自动抛出系统生成的异常,而不管你是否愿意捕获和处理,它总要被抛出!比如除数为0的异常。二是程序员自己抛出的异常,这个异常可以是程序员自己定义的,也可以是Java语言中定义的,用throw 关键字抛出异常,这种异常常用来向调用者汇报异常的一些信息。 异常是针对方法来说的,抛出、声明抛出、捕获和处理异常都是在方法中进行的。 Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理。基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出。你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象。finally语句块会在方法执行return之前执行,一般结构如下: try 程序代码 catch(异常类型1 异常的变量名1) 程序代码 catch(异常类型2 异常的变量名2) 程序代码 finally 程序代码 catch语句可以有多个,用来匹配多个异常,匹配上多个中一个后,执行catch语句块时候仅仅执行匹配上的异常。catch的类型是Java语言中定义的或者程序员自己定义的,表示代码抛出异常的类型,异常的变量名表示抛出异常的对象的引用,如果catch捕获并匹配上了该异常,那么就可以直接用这个异常变量名,此时该异常变量名指向所匹配的异常,并且在catch代码块中可以直接引用。这一点非常非常的特殊和重要! Java异常处理的目的是提高程序的健壮性,你可以在catch和finally代码块中给程序一个修正机会,使得程序不因异常而终止或者流程发生以外的改变。同时,通过获取Java异常信息,也为程序的开发维护提供了方便,一般通过异常信息就很快就能找到出现异常的问题(代码)所在。 Java异常处理是Java语言的一大特色,也是个难点,掌握异常处理可以让写的代码更健壮和易于维护。
二、Java异常类类图
下面是这几个类的层次图: java.lang.Object java.lang.Throwable java.lang.Exception java.lang.RuntimeException java.lang.Error java.lang.ThreadDeath
下面四个类的介绍来自java api 文档。
1、Throwable Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。 两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。通常,这些实例是在异常情况的上下文中新近创建的,因此包含了相关的信息(比如堆栈跟踪数据)。
2、Exception Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件,表示程序本身可以处理的异常。
3、Error Error 是 Throwable 的子类,表示仅靠程序本身无法恢复的严重错误,用于指示合理的应用程序不应该试图捕获的严重问题。 在执行该方法期间,无需在方法中通过throws声明可能抛出但没有捕获的 Error 的任何子类,因为Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。
4、RuntimeException RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过,这种异常可以通过改进代码实现来避免。
5、ThreadDeath 调用 Thread 类中带有零参数的 stop 方法时,受害线程将抛出一个 ThreadDeath 实例。 仅当应用程序在被异步终止后必须清除时才应该捕获这个类的实例。如果 ThreadDeath 被一个方法捕获,那么将它重新抛出非常重要,因为这样才能让该线程真正终止。 如果没有捕获 ThreadDeath,则顶级错误处理程序不会输出消息。 虽然 ThreadDeath 类是“正常出现”的,但它只能是 Error 的子类而不是 Exception 的子类,因为许多应用程序捕获所有出现的 Exception,然后又将其放弃。
以上是对有关异常API的一个简单介绍,用法都很简单,关键在于理解异常处理的原理,具体用法参看Java API文档。
三、Java异常处理机制
对于可能出现异常的代码,有两种处理办法: 第一、在方法中用try...catch语句捕获并处理异常,catach语句可以有多个,用来匹配多个异常。例如: public void p(int x) try ... catch(Exception e) ... finally ...
第二、对于处理不了的异常或者要转型的异常,在方法的声明处通过throws语句抛出异常。例如: public void test1() throws MyException ... if(....) throw new MyException(); 如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调用中,Java虚拟机会从出现异常的方法代码块中往回找,直到找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理。如果Java虚拟机追溯到方法调用栈最底部main()方法时,如果仍然没有找到处理异常的代码块,将按照下面的步骤处理: 第一、调用异常的对象的printStackTrace()方法,打印方法调用栈的异常信息。 第二、如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。 通过分析思考可以看出,越早处理异常消耗的资源和时间越小,产生影响的范围也越小。因此,不要把自己能处理的异常也抛给调用者。 还有一点,不可忽视:finally语句在任何情况下都必须执行的代码,这样可以保证一些在任何情况下都必须执行代码的可靠性。比如,在数据库查询异常的时候,应该释放JDBC连接等等。finally语句先于return语句执行,而不论其先后位置,也不管是否try块出现异常。finally语句唯一不被执行的情况是方法执行了System.exit()方法。System.exit()的作用是终止当前正在运行的 Java 虚拟机。finally语句块中不能通过给变量赋新值来改变return的返回值,也建议不要在finally块中使用return语句,没有意义还容易导致错误。
最后还应该注意一下异常处理的语法规则: 第一、try语句不能单独存在,可以和catch、finally组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。 第二、try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。 第三、多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。 第四、throw语句后不允许有紧跟其他语句,因为这些没有机会执行。 第五、如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。
那怎么判断一个方法可能会出现异常呢?一般来说,方法声明的时候用了throws语句,方法中有throw语句,方法调用的方法声明有throws关键字。
throw和throws关键字的区别 throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。 throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2...异常类型n。

四、如何定义和使用异常类
1、使用已有的异常类,假如为IOException、SQLException。 try 程序代码 catch(IOException ioe) 程序代码 catch(SQLException sqle) 程序代码 finally 程序代码
2、自定义异常类 创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如: public class MyException extends Exception public MyException() public MyException(String smg) super(smg);
3、使用自定义的异常 用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如: 在某种条件抛出异常 public void test1() throws MyException ... if(....) throw new MyException();
将异常转型(也叫转译),使得异常更易读易于理解 public void test2() throws MyException ... try ... catch(SQLException e) ... throw new MyException();
还有一个代码,很有意思: public void test2() throws MyException ... try ... catch (MyException e) throw e;
这段代码实际上捕获了异常,然后又和盘托出,没有一点意义,如果这样还有什么好处理的,不处理就行了,直接在方法前用throws声明抛出不就得了。异常的捕获就要做一些有意义的处理。
五、运行时异常和受检查异常
Exception类可以分为两种:运行时异常和受检查异常。 1、运行时异常 RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。例如,当除数为零时,就会抛出java.lang.ArithmeticException异常。 2、受检查异常 除了RuntimeException类及其子类外,其他的Exception类及其子类都属于受检查异常,这种异常的特点是要么用try...catch捕获处理,要么用throws语句声明抛出,否则编译不会通过。 3、两者的区别 运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。 受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的,比如除数为零的运行时异常: public class HelloWorld public static void main(String[] args) System.out.println("Hello World!!!"); try System.out.println(1/0); catch(ArithmeticException e) System.out.println("除数为0!"); System.out.println("除数为零后程序没有终止啊,呵呵!!!");
运行结果:
Hello World!!! 除数为0! 除数为零后程序没有终止啊,呵呵!!!
4、运行时错误 Error类及其子类表示运行时错误,通常是由Java虚拟机抛出的,JDK中与定义了一些错误类,比如VirtualMachineError 和OutOfMemoryError,程序本身无法修复这些错误.一般不去扩展Error类来创建用户自定义的错误类。而RuntimeException类表示程序代码中的错误,是可扩展的,用户可以创建特定运行时异常类。 Error(运行时错误)和运行时异常的相同之处是:Java编译器都不去检查它们,当程序运行时出现它们,都会终止运行。
5、最佳解决方案 对于运行时异常,我们不要用try...catch来捕获处理,而是在程序开发调试阶段,尽量去避免这种异常,一旦发现该异常,正确的做法就会改进程序设计的代码和实现方式,修改程序中的错误,从而避免这种异常。捕获并处理运行时异常是好的解决办法,因为可以通过改进代码实现来避免该种异常的发生。 对于受检查异常,没说的,老老实实去按照异常处理的方法去处理,要么用try...catch捕获并解决,要么用throws抛出! 对于Error(运行时错误),不需要在程序中做任何处理,出现问题后,应该在程序在外的地方找问题,然后解决。
六、异常转型和异常链 异常转型在上面已经提到过了,实际上就是捕获到异常后,将异常以新的类型的异常再抛出,这样做一般为了异常的信息更直观!比如: public void run() throws MyException ... try ... catch(IOException e) ... throw new MyException(); finally ...
异常链,在JDK1.4以后版本中,Throwable类支持异常链机制。Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。最后,它还可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。它也称为异常链 设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。 通俗的说,异常链就是把原始的异常包装为新的异常类,并在新的异常类中封装了原始异常类,这样做的目的在于找到异常的根本原因。
通过Throwable的两个构造方法可以创建自定义的包含异常原因的异常类型: Throwable(String message, Throwable cause) 构造一个带指定详细消息和 cause 的新 throwable。 Throwable(Throwable cause) 构造一个带指定 cause 和 (cause==null ? null :cause.toString())(它通常包含类和 cause 的详细消息)的详细消息的新 throwable。 getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。 initCause(Throwable cause) 将此 throwable 的 cause 初始化为指定值。 在Throwable的子类Exception中,也有类似的指定异常原因的构造方法: Exception(String message, Throwable cause) 构造带指定详细消息和原因的新异常。 Exception(Throwable cause) 根据指定的原因和 (cause==null ? null : cause.toString()) 的详细消息构造新异常(它通常包含 cause 的类和详细消息)。 因此,可以通过扩展Exception类来构造带有异常原因的新的异常类。
七、Java异常处理的原则和技巧
1、避免过大的try块,不要把不会出现异常的代码放到try块里面,尽量保持一个try块对应一个或多个异常。 2、细化异常的类型,不要不管什么类型的异常都写成Excetpion。 3、catch块尽量保持一个块捕获一类异常,不要忽略捕获的异常,捕获到后要么处理,要么转译,要么重新抛出新类型的异常。 4、不要把自己能处理的异常抛给别人。 5、不要用try...catch参与控制程序流程,异常控制的根本目的是处理程序的非正常情况。
参考技术B 异常栈都没有,究竟想知道啥呀?

《java基础知识》Java异常处理详解

1. Java 中的异常

前言:Java 中的异常处理是处理程序运行错误时的强大机制之一,它可以保证应用程序的正常流程。

首先我们将了解java异常、异常的类型以及受查和非受查异常之间的区别。

1.1 什么是异常?

字面意义:异常是一种不正常的情况。

在 java 中,异常是扰乱程序正常流程的事件,它是在程序运行时抛出的对象。

1.2 什么是异常处理?

异常处理一种在运行时解决程序错误的机制,例如 ClassNotFound、IO、SQL、Remote 等。

1.2.1 异常处理的优势

异常通常会干扰程序的正常流程,而异常处理的核心优势是维护程序的正常流程。现在让我们假设一下:

statement 1;  
statement 2;  
statement 3;  
statement 4;  
statement 5;//发生异常
statement 6;  
statement 7;  
statement 8;  
statement 9;  
statement 10;  

假设你的程序中有10条语句,如果在第5条中出现了一个异常,那么语句6-10将不会继续执行。如果你使用了异常处理,那么语句6-10的部分将正常执行,这就是我们为什么需要在程序中使用异常处理的原因。

1.3 Java 异常类的层次结构

 技术图片

1.4 异常类型

主要有两种类型的异常:受查和非受查异常,Error被视为非受查异常。Sun公司认为有三种异常类型:

  • 受查异常(Checked Exception)
  • 非受查异常(UnChecked Exception)
  • 错误(Error)

1.5 受查和非受查异常之间的区别

1)受查异常

除了RuntimeExceptionError外,继承自Throwable类的类称为受查异常,例如:IOException、SQLException 等。受查异常在编译时进行检查。

常见的有以下几个方面:

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

2)非受查异常

继承自RuntimeException类的异常被称为非受查异常,例如:ArithmeticException、 NullPointerException、 ArrayIndexOutOfBoundsException 等。非受查异常不会在编译时检查,而是在运行时进行检查。

常见的有以下几个方面:

  • 错误的类型转换
  • 数组访问越界
  • 访问null指针

“如果出现了RuntimeException异常,那么一定是你自身的问题”,是一条相当有道理的规则。

3)错误(Error)

错误是一种无法恢复的异常类型,通常是在java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力的使得程序安全的终止之外,再也无能为力了。这种情况很少出现。

1.6 可能出现异常的常见场景

在某些情况下,可能出现未检查的异常,它们如下:

1)发生ArithmeticException的场景

如果我们将任何数字除以0,就会出现一个 ArithmeticException 异常。

int a = 50/0;//ArithmeticException  

2)发生NullPointerException的场景

如果变量的值为null,那么调用此变量将会出现 NullPointerException 异常。

String s = null;  
System.out.println(s.length());//NullPointerException  

3)发生NumberFormatException的场景

任何值的格式错误,都有肯能发生 NumberFormatException 异常。假设一个字符串变量,其中包含了字符,若将此变量转换为数字类型,将会发生 NumberFormatException 异常。

String s = "abc";  
int i = Integer.parseInt(s);//NumberFormatException

4)发生ArrayIndexOutOfBoundsException的场景

如果你在一个不存在的的数组索引中插入任何值,则会导致 ArrayIndexOutOfBoundsException 异常。

int a[] = new int[5];  
a[10] = 50; //ArrayIndexOutOfBoundsException  

1.7 Java 异常处理关键字

下面是 Java 异常处理中的5个关键字:

trycatchfinallythrowthrows

1.8 创建自定义异常类

在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于 Exception 的类,或者派生于 Exception 子类的类。例如,定义一个派生于 IOException 的类。

习惯上,定义的类应该包含两个构造器,一个是默认构造器,一个是描述详细信息的的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息,这在调试中非常有用。)

示例如下:

class FileFormatException extends IOException 
    public FileFormatException() 
    public FileFormatException(String gripe) 
        super(gripe);
    
String readData(BufferedReader in) throws FileFormatException 
    ...
    while (...) 
        // EOF encountered
        if (ch == -1) 
            if (n < len)
                throw new FileFormatException();
        
        ...
    
    return s;

2. Java try-catch

将可能发生异常的代码放在try块中,且必须在方法中才能使用。try 块后必须使用catch块或finally块。

2.1 Java try 块

1)try-catch 语法

try  
// 可能抛出异常的代码
catch(Exception_class_Name ref)  

2)try-finally 语法

try  
// 可能抛出异常的代码
finally 

2.2 Java catch 块

Java catch块被用于处理异常,必须在try块后使用。

你可以在一个try块后使用多个catch

2.3 未使用异常处理的问题

如果我们不使用try-catch处理异常,看看会发生什么。

public class Testtrycatch1   
    public static void main(String args[])   
        int data=50/0;// 可能抛出异常
        System.out.println("代码的其余部分...");  
      
  

运行结果:

技术图片

如上面的示例所示,代码的其余部分并没有执行。("代码的其余部分..."未打印)

2.4 使用异常处理解决问题

让我们通过try-catch块来查看上述问题的解决方案。

public class Testtrycatch2   
    public static void main(String args[])   

        try   
            int data = 50/0;  
        
        catch(ArithmeticException e) 
            System.out.println(e);
          

        System.out.println("代码的其余部分...");  
      
  

运行结果:

技术图片

现在,正如上面的示例所示,代码的其余部分执行了.(也就是"代码的其余部分..."被打印)

Java 虚拟机首先检查异常是否被处理,如果异常未处理,则执行的一个默认的异常处理程序:

  • 打印异常描述
  • 打印堆栈跟踪(异常发生方法的层次结构)
  • 终止程序

如果程序员处理了异常,则应用程序按照正常流程执行。

3. 使用多个 catch 块

如果需要在发生不同异常时执行不同的任务,则需要使用多个 catch 块。

查看下面一个简单的多 catch 块示例。

public class TestMultipleCatchBlock  
    public static void main(String args[])   

        try  
            int a[] = new int[5];  
            a[5] = 30/0;  
          
        catch(ArithmeticException e) 
            System.out.println("任务1已完成");
          
        catch(ArrayIndexOutOfBoundsException e) 
            System.out.println("任务2已完成");
          
        catch(Exception e) 
            System.out.println("已完成通用任务");
        

        System.out.println("代码的其余部分...");  

      
 

输出:

技术图片

规则:一次只有一个异常发生,并且一次只执行一个catch块。

规则: 所有异常必须从最具体到最通用的顺序排序,即捕获ArithmeticException必须在捕获Exception之前发生。

class TestMultipleCatchBlock1   
public static void main(String args[])   

        try  
            int a[]=new int[5];  
            a[5]=30/0;  
        
        catch(Exception e) 
            System.out.println("已完成通用任务");
        
        catch(ArithmeticException e) 
            System.out.println("任务1已完成");
        
        catch(ArrayIndexOutOfBoundsException e) 
            System.out.println("任务2已完成");
          
        System.out.println("代码的其余部分...");  
      
  

编译报错。

4. Java 嵌套 try 块

Java try块中的try块被称为try嵌套块。

4.1 为什么使用 try 嵌套块?

有时可能会出现一种情况,一个块的某个部分可能导致一个错误,而整个块的本身可能会导致另一个错误。在这种情况下,必须使用嵌套异常处理程序。

语法:

....  
try  
  
    statement 1;  
    statement 2;  
    try  
      
        statement 1;  
        statement 2;  
      
    catch(Exception e)  
      
        ...
      
  
catch(Exception e) ...  
....  

4.2 Java try 嵌套块示例

class Excep6 
    public static void main(String args[]) 
        try 
            // try 嵌套块1
            try 
                System.out.println("try 嵌套块1");
                int b = 39 / 0;
            
            catch(ArithmeticException e) 
                System.out.println(e);
            
            // try 嵌套块2
            try 
                int a[] = new int[5];
                a[5] = 4;
            
            catch(ArrayIndexOutOfBoundsException e) 
                System.out.println(e);
            
            System.out.println("try外部块其他语句...");
        
        catch(Exception e) 
            System.out.println("handeled");
        
        System.out.println("正常流...");
    

输出:

技术图片

5. Java finally 块

Java finally 块是用来执行重要代码的块(如关闭连接、流等)。

无论是否处理异常,最终都会执行 finally 块。

finally 块紧跟 try 或 catch 块后

注意:无论你是否处理异常,在终止程序之前,JVM都将执行finally块(如果存在的话)

5.1 为什么要使用 finally 块

finally 块可以用于放置"clear"代码,例如关闭文件,关闭连接等。

5.2 使用 finally 块案例

接下来让我们来看看在不同情况下使用 finally 块。

案例:

public class TestFinallyBlock2 
    public static void main(String args[]) 
        try 
            int data = 25 / 0;
            System.out.println(data);
        
        catch(ArithmeticException e) 
            System.out.println(e);
        
        finally 
            System.out.println("finally 块总是执行");
        
        System.out.println("代码的其余部分...");
    

输出:

技术图片

规则:对于 try 块可以有0个或多个 catch 块,但仅仅只能有一个 finally 块。

规则:如果程序退出(通过调用 System.exit() 或通过导致进程中止的致命错误),finally块将不会被执行。

6. Java 抛出异常

6.1 Java throw 关键字

Java throw 关键字用于显示的抛出异常。

我们可以使用 throw 关键字在 Java 中抛出检查(Checked)或未检查(UnChecked)异常。throw 关键字主要用于抛出自定义异常。

Java throw 语法如下:

throw exception;  

抛出IOException异常的例子:

throw new IOException("sorry device error");  

6.2 Java throw 示例

在本例中,我们创建了一个将整数值作为参数的 validate 方法。如果年龄小于18岁,我们将抛出一个ArithmeticException异常,否则打印一条消息"欢迎投票"。

public class TestThrow1 
    static void validate(int age) 
        if(age < 18)  
            throw new ArithmeticException("无效");
        else  
            System.out.println("欢迎投票");
    

    public static void main(String args[]) 
        validate(13);
        System.out.println("代码的其余部分...");
    

运行结果:

技术图片

7. Java 异常传递

异常首先从堆栈顶部抛出,如果未捕获,则将调用堆栈下降到前一个方法,如果没有捕获,则将异常再次下降到先前的方法,以此类推,知道它们被捕获或到达调用堆栈底部为止。以上称为异常传递。

规则:默认情况下,非受查异常在调用链中(传递)转发。

异常传递示例:

class TestExceptionPropagation1 
    void m()
        int data = 50 / 0;
    
    void n() 
        m();
    
    void p() 
        try
            n();
        
        catch(Exception e) 
            System.out.println("异常处理器");
        
    

    public static void main(String args[]) 
        TestExceptionPropagation1 obj = new TestExceptionPropagation1();
        obj.p();
        System.out.println("正常流...");
    

运行结果:

技术图片

在上面的示例中。异常发生在 m() 方法中,如果未对其进行处理,则将其传递到未处理它的前 n() 方法,再次将其传递到处理异常的 p() 方法。

可以在 main()、p()、n()、p()、 m() 中的任何方法中处理异常。

规则:默认情况下,受查异常不会在调用链中(传递)转发。

用于描述受查异常不会在程序中传递的示例:

class TestExceptionPropagation2
    void m()
        throw new java.io.IOException("设备异常"); // 受查异常
    
    void n()
        m();
    
    void p()
        try
            n();
        
        catch(Exception e)
            System.out.println("异常处理器");
        
    
    public static void main(String args[])
        TestExceptionPropagation2 obj=new TestExceptionPropagation2();
        obj.p();
        System.out.println("正常流...");
    

编译报错

8. Java throws 关键字

Java throws 关键字被用于声明一个异常。它给程序员提供了一个信息,说明可能会发生异常,所以程序员最好提供异常处理代码,以保证程序正常的流程。

异常处理主要用于处理受查异常,如果出现任何非受查异常,如"NullPointerException",都是程序员自身的错误,请认真检查你的代码。

8.1 Java throws 语法

return_type method_name() throws exception_class_name   
    // method code  
 

8.2 应该声明哪个异常?

仅仅声明受查异常,因为:

  • 非受查异常:程序员应该更正代码以确保代码正确无误。
  • Error:无法控制,如果出现了 VirtualMachineError 或 StackOverflowError等异常,将无法进行任何操作。

8.3 Java throws 优势

使用 throws 声明受查异常后,使得受查异常可以在调用堆栈中进行(传递)转发。它向处理该异常的方法提供异常信息。

8.4 Java throws 示例

下面的示例描述了受查异常可以通过throws关键字进行传递:

import java.io.IOException;
class Testthrows1
    void m() throws IOException
        throw new IOException("设备异常"); // 受查异常
    
    void n()throws IOException
        m();
    
    void p()
        try
            n();
        
        catch(Exception e)
            System.out.println("异常处理器");
        
    
    public static void main(String args[])
        Testthrows1 obj=new Testthrows1();
        obj.p();
        System.out.println("正常流...");
    

运行结果:

技术图片

规则:如果你正在调用一个声明了异常的方法,则必须捕获或声明异常。

现在有两种情况:

  • 情况1:你遇到了一个异常,使用 try-catch 处理了异常。
  • 情况2:你声明了异常,使用方法指定抛出。

1) 情况1:处理了异常

  • 在这种情况下,如果你处理了异常,则不管程序是否出现了异常,程序都将继续执行。
import java.io.*;
class M
    void method() throws IOException
        throw new IOException("设备异常");
    

public class Testthrows2
    public static void main(String args[])
        try
            M m = new M();
            m.method();
        
        catch(Exception e)
            System.out.println("异常处理器");
        
        System.out.println("正常流...");
    

运行结果:

技术图片

2) 情况2:声明了异常

  • A)如果声明了异常,但代码未出现异常,程序将正常执行。
  • B)如果声明了异常且发生了异常,则在运行时抛出异常,因为程序会抛出不处理的异常。

A)声明了异常但未发生异常:

import java.io.*;
class M
    void method()throws IOException
        System.out.println("执行设备操作");
    

class Testthrows3
    public static void main(String args[])throws IOException
        // 声明了异常
        M m=new M();
        m.method();
        System.out.println("正常流...");
    

运行结果:

技术图片

B)声明了异常且发生了异常:

import java.io.*;
class M
    void method()throws IOException
        throw new IOException("设备错误");
    

class Testthrows4
    public static void main(String args[])throws IOException
        // 声明了异常  
        M m=new M();
        m.method();
        System.out.println("正常流...");
    

运行结果:

技术图片

8.5 throw 与 throws 区别

No.  throwthrows
1) Java throw 关键字用于显示的抛出异常     Java throws 关键字用于声明一个异常
2) 受查异常不能只使用 throw 进行传递 受查异常可以通过 throws 进行传递
3) Throw 后面跟着一个异常实例 Throws 后面跟着一个异常类
4) 在方法中使用 Throw Throws 与方法签名一起使用
5) 你不能抛出多个异常 你可以声明多个异常,例如public void method() throws IOException,SQLException   

1)Java throw 示例:

void m()  
    throw new ArithmeticException("sorry");  
 

2)Java throws 示例:

void m()throws ArithmeticException  
    // method code  
  

3)Java throw 和 throws 示例:

void m()throws ArithmeticException  
    throw new ArithmeticException("sorry");  
 

10. 异常处理方法的重写

关于重写异常处理方法的规则如下:

  • 超类方法没有声明异常:
    如果超类方法没有声明异常,则子类重写方法不能声明受查异常,但可以声明非受查异常。
  • 超类方法声明了异常:
    如果超类方法声明了异常,则子类重写方法可以声明与超类方法相同的异常,也可以不声明异常。若父类方法声明父类异常,子类重写方法声明子类异常也可以,反之不可以。

1)如果超类方法没有声明异常

超类方法未声明异常,子类重写方法声明受查异常的示例:

import java.io.*;
class Parent
    void msg()
        System.out.println("parent");
    

class TestExceptionChild extends Parent
    void msg() throws IOException
        System.out.println("Child");
    
    public static void main(String args[])
        Parent p = new TestExceptionChild();
        p.msg();
    

编译报错

超类方法未声明异常,子类重写方法声明非受查异常的示例:

import java.io.*;
class Parent
    void msg()
        System.out.println("parent");
    

class TestExceptionChild1 extends Parent
    void msg() throws ArithmeticException
        System.out.println("child");
    
    public static void main(String args[])
        Parent p = new TestExceptionChild1();
        p.msg();
    

运行结果:

技术图片

2)如果超类方法声明了异常

A)超类方法声明了异常,子类重写方法声明不相同父类异常的示例:

import java.io.*;
class Parent
    // 声明了子类异常
    void msg() throws ArithmeticException
        System.out.println("parent");
    

class TestExceptionChild2 extends Parent
    // 声明了父类异常
    void msg() throws Exception
        System.out.println("child");
    
    public static void main(String args[])
        Parent p = new TestExceptionChild2();
        try
            p.msg();
        
        catch(Exception e)
        
    

运行结果:

编译报错

B)超类方法声明了异常,子类重写方法声明相同异常的示例:

import java.io.*;
class Parent
    void msg()throws Exception
        System.out.println("parent");
    

class TestExceptionChild3 extends Parent
    void msg()throws Exception
        System.out.println("child");
    
    public static void main(String args[])
        Parent p=new TestExceptionChild3();
        try
            p.msg();
        
        catch(Exception e)
        
    

运行结果:

技术图片

C)超类方法声明了异常,子类重写方法声明不相同子类异常的示例:

import java.io.*;
class Parent
    // 声明了父类异常
    void msg()throws Exception
        System.out.println("parent");
    

class TestExceptionChild4 extends Parent
    // 声明了子类异常
    void msg()throws ArithmeticException
        System.out.println("child");
    
    public static void main(String args[])
        Parent p=new TestExceptionChild4();
        try
            p.msg();
        
        catch(Exception e)
        
    

运行结果:

技术图片

D)超类方法声明了异常,子类重写方法未声明异常的示例:

import java.io.*;
class Parent
    void msg()throws Exception
        System.out.println("parent");
    

class TestExceptionChild5 extends Parent
    void msg()
        System.out.println("child");
    
    public static void main(String args[])
        Parent p=new TestExceptionChild5();
        try
            p.msg();
        
        catch(Exception e)
        
    

运行结果:

技术图片

注意点:

  • 不要在fianlly中使用return。
  • 不要在finally中抛出异常。
  • 减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。
  • 将尽量将所有的return写在函数的最后面,而不是try ... catch ... finally中。

参考:https://www.cnblogs.com/lulipro/p/7504267.html

参考:https://www.cnblogs.com/nwgdk/p/8862353.html

以上是关于java异常处理详解!!的主要内容,如果未能解决你的问题,请参考以下文章

《java基础知识》Java异常处理详解

java异常处理机制详解

详解Java中的异常和处理

Java中的异常和处理详解

异常处理机制详解

JAVA异常与异常处理详解