一、异常的概念
异常指的是运行期出现的错误,也就是当程序开始执行以后执行期出现的错误。出现错误时观察错误的名字和行号最为重要。
异常发生的原因:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者JVM内存溢出。
二、异常的分类
检查性异常(Exception):用户错误或问题引起的异常,这是程序员无法预见的。是异常的父类,子类需要用户显式声明或捕获;例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常(Runtime Exception): 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。例如,除数为0,数组下标超出范围等。
错误(ERROR):由Java虚拟机生成并抛出,错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,栈溢出,一个错误就发生了,它们在编译也检查不到的;动态链接失败;虚拟机错误。
Exception的层次
所有的异常类是从java.lang.Exception类继承的子类,Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error 。Java程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Throwable类:可以被抛出的,是所有异常类的根基类,所有的异常类都是从它继承。
Error类:是不能处理的错误。Java虚拟机(JVM)运行时出错,用来指示运行时环境发生的错误。例如,JVM内存溢出。一般地,程序不会从错误中恢复。
IOException......类:这些错误必须被处理;
Runtime Exception类:不一定都要处理,可以忽略;
三、异常的捕获和处理
Java异常处理的五个关键字:try、catch、finally、throw、throws 。
捕获异常
使用try和catch关键字可以捕获异常。try/catch/finally代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch/finally的语法如下:
try { // 可能抛出异常的语句 }catch(ExceptionName e1) { //Catch 块 }catch(ExceptionName e1) { //Catch 块 }finally{ ... }
try语句:捕获可能抛出异常的语句,如果有例外异常,交给后面的catch语句进行相应处理,后面可以跟一个或多个catch代码段。
Catch语句:包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try后面的catch块就会被检查。
如果发生的异常包含在catch块中,异常会被传递到该catch块处理。在catch代码块中可以用一些方法获取异常信息
getMessage()方法:用来得到有关异常事件的信息。
printStackTrace()方法:一般使用这种方法,包括了getMessage()方法,用来跟踪异常事件发生时执行堆栈的内容。使用前需要 new 一个错误对象再调用,因为它是专属某个错误对象里面的方法,
finally语句:为异常处理提供的统一出口,无论是否发生异常都执行,可以不写。在finally语句中,可以进行资源的清除工作,例如,打开关闭文件、删除临时文件...
实例
下面的例子中声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候就会抛出一个异常。
import java.io.*; public class ExcepTest{ public static void main(String args[]){ try{ int a[] = new int[2]; System.out.println("Access element three :" + a[3]); //可能抛出异常语句 }catch(Exception e1){ //捕获数组下标越界异常 System.out.println("数组下标越界"); }catch(ArrayIndexOutOfBoundsException e2){ System.out.println("异常发生"); //输出错误信息 } } }
因为Exception类中包含ArrayIndexOutOfBoundsException异常,所以第二个catch异常重复,是不允许的,编译不会通过。
以上代码编译运行输出结果如下:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException:
throws/throw关键字:
如果一个方法没有捕获一个检查性异常,那么该方法必须使用throws 关键字来声明。throws关键字放在方法签名的尾部。
也可以使用throw关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
下面方法的声明抛出一个RemoteException异常:
import java.io.*; public class className { public void deposit(double amount) throws RemoteException { // Method implementation throw new RemoteException(); } //Remainder of class definition }
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
例如,下面的方法声明抛出RemoteException和InsufficientFundsException:
import java.io.*; public class className { public void withdraw(double amount) throws RemoteException, InsufficientFundsException { // Method implementation } //Remainder of class definition }
测试异常
package test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class testException { /** * 任何方法往外抛能处理的异常的时候都有一种简单的写法:“throws Exception”, * 因为Exception类是所有能处理的异常类的根基类,因此抛出Exception类就会抛出所有能够被处理的异常类里了。 * 使用“throws Exception”抛出所有能被处理的异常之后,这些被抛出来的异常就是交给JAVA运行时系统处理了, * 而处理的方法是把这些异常的相关错误堆栈信息全部打印出来。 * @throws Exception */ void fn() throws Exception{ } /** * 已知异常的类型,方法声明时使用throws把异常往外抛 * @param i * @throws ArithmeticException */ void m1(int i) throws ArithmeticException{ } /** * 手动抛出异常 * new了一个异常对象,在构建这个对象的时候还可以指定他相关的信息,如这里指明了异常信息“i不能等于0” * 这个对象抛出去的时候使用getMessage()方法拿到的就是“i不能等于0”这种信息。 * @param i */ void m2(int i) { if (i==0) { throw new ArithmeticException("i不能等于0"); } } /** * 正常情况下如果这里不写try……catch语句那么程序编译时一定会报错, * 因为这里有可能会产生两个个必须要处理的异常:FileNotFoundException和IOException。 * 但由于在声明方法f()时已经使用throws把可能产生的这两个异常抛出了, * 所以这里可以不写try……catch语句去处理可能会产生的异常。 * f()方法把抛出的异常交给下一个要调用它的方法去处理 * @throws FileNotFoundException * @throws IOException */ void f()throws FileNotFoundException,IOException { //这里有可能会产生FileNotFoundException异常 FileInputStream fis=new FileInputStream("1.txt"); //这里有可能会产生IOException异常 int b = fis.read(); while (b!=-1) { System.out.println((char)b); b=fis.read(); } } /** * 在f2()方法里面调用f()方法时必须要处理f()方法抛出来的异常, * 当然,如果f2()方法也没有办法处理f()方法抛出来的异常,那么f2()方法也可以使用throws把异常抛出, * 交给下一个调用了f2()的方法去处理f()方法抛出来的异常。 * 这里f2()调用f()方法时,选择不处理f()方法中可能抛出的异常,将异常继续抛出 * @throws Exception */ void f2() throws Exception { f(); } /** * f3方法调用f方法捕获f()方法抛出的2个异常并进行处理 */ void f3() { try { f(); } catch (FileNotFoundException e) { System.out.println(e.getMessage()); //处理的方法是把错误信息打印出来 } catch (IOException e) { e.printStackTrace(); //处理的方法是使用printStackTrace()方法把错误的堆栈信息全部打印出来。 } } public static void main(String[] args) { FileInputStream fis = null; try { fis = new FileInputStream("MyFile.txt"); int b = fis.read(); //这个有可能会抛出IOException异常 while (b != -1) { System.out.println((char)b); b = fis.read(); } } catch (FileNotFoundException e) { //使用catch捕获FileNotFoundException类异常的异常对象e。并让异常对象e自己调用printStackTrace方法打印出全部的错误信息 e.printStackTrace(); } catch (IOException e) { //再次使用catch捕获IOException类的异常对象e,并让异常对象e自己调用getMessage()方法将错误信息打印出来。 System.out.println(e.getMessage());; }finally{ try { /** * 前面已经把一个文件打开了,不管打开这个文件时有没有错误发生,即有没有产生异常,最后都一定要把这个文件关闭掉, * 因此使用了finally语句,在finally语句里面不管前面这个文件打开时是否产生异常,在finally这里执行in.close()都能把这个文件关闭掉, * 关闭文件也有可能会产生异常,因此在finally里面也使用了try……catch语句去捕获有可能产生的异常。 */ fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }以上代码编译运行输出结果如下:
java.io.FileNotFoundException: MyFile.txt (系统找不到指定的文件。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileInputStream.<init>(FileInputStream.java:93) at test.testException.main(testException.java:88) Exception in thread "main" java.lang.NullPointerException at test.testException.main(testException.java:107)
四、自定义异常
编写自己的异常类时需要记住下面的几点。
继承java.lang.Exception;声明自己的异常类。
如果希望写一个检查性异常类,则需要继承Exception类。
如果你想写一个运行时异常类,那么需要继承RuntimeException 类。
实例:自定义异常
package test; public class MyException extends Exception{ private int id; /** * 自定义异常类的构造方法 * @param message * @param id */ public MyException(String message,int id) { super(message); //调用父类Exception的构造方法 this.id = id; } /** * 获取异常的代码 * @return */ public int getId() { return id; } }
实例:自定义异常测试
package test; import java.text.MessageFormat; public class TestMyException { //throws MyException,抛出我们自定义的MyException类的异常。 public void regist(int num) throws MyException { if (num < 0) { //使用throw手动抛出一个MyException类的异常对象。 throw new MyException("人数为负值,不合理", 1); } /** * 注意:当我们抛出了异常之后, * System.out.println(MessageFormat.format("登记人数:{0}",num));是不会被执行的。 * 抛出异常之后整个方法的调用就结束了。 */ System.out.println(MessageFormat.format("登记人数:{0}",num)); } public void manage() { try { regist(-100); } catch (MyException e) { System.out.println("登记失败,错误码:"+e.getId()); e.printStackTrace(); } System.out.println("操作结束"); } public static void main(String[] args) { TestMyException t = new TestMyException(); t.manage(); } }
登记失败,错误码:1 操作结束 test.MyException: 人数为负值,不合理 at test.TestMyException.regist(TestMyException.java:9) at test.TestMyException.manage(TestMyException.java:21) at test.TestMyException.main(TestMyException.java:32)
登记失败,错误码:1 操作结束 test.MyException: 人数为负值,不合理 at test.TestMyException.regist(TestMyException.java:9) at test.TestMyException.manage(TestMyException.java:21) at test.TestMyException.main(TestMyException.java:32)
文章参考:孤傲苍狼