一、异常的概念
异常指的是运行期出现的错误,也就是当程序开始执行以后执行期出现的错误。出现错误时观察错误的名字和行号最为重要。
异常发生的原因:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者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)
文章参考:孤傲苍狼