异常 ( Exception ) 也叫例外;在 Java 编程语言中,异常就是程 序在运行过程中由于硬件设备问题、软件设计错误、缺陷等导致的 程序错误,比如: 想打开的文件不存在;网络连接中断;操作数超 出预定范围;正在装载的类文件丢失;访问的数据库打不开等操作。
异常是指程序执行过程中出现的非正常事件,如: 字符串无法正确 格式化为数字数字(NumberFormatException);在一个取值是 null 的元素上调用方法或属性(NullPointerException);使用了 0 做 除 数 ( 算 术 异 常 ArithmeticException) ; 数 组 下 标 越 界 ( ArrayIndexOutOfBoundsException )......
使用异常的好处
可以实现对已知或未知的"特殊情况"的控制; 可以实现"核心程序" 和 处理"特殊情况"的代码相分离
public class ExceptionHandling1 {
public static void main(String[] args) {
int a = 100 ;
int b = 0 ;
// 因为除数不能是 0,因此 a/b 会发生异常
// 当异常发生之后,会创建一个该异常对应的对象
// 即 java.lang.ArithmeticException 类型的对象
int r = a/b ; // 该行会导致产生java.lang.ArithmeticException 类型的对象
System.out.println( r );
}
}
异常处理机制
程序运行过程中发生了异常, 则会生成一个代表该异常的对象。这个异常有可能是当前正在执行的程序造成的,也有可能是虚拟机生成的;同时当前程序会把这个异常对象交给运行时系统,我们把生成异常对象并把它提交给运行时系统的过程称为抛出异常。抛出异常使用 throw 关键字。运行时系统, 通常使用 Runtime 类的实例来表示。运行时系统寻找相应代码来处理该异常。
这里相应的代码有两种情况: 捕获该异常对象, 并作出相应处理; 对该异常对象不做任何处理, 继续往外抛出(比较消极)
捕获异常
捕获异常是一种积极的异常处理机制,在 Java 程序运行过程中系统得到一个异常对象时, 它将会沿着方法的调用栈逐层回溯, 寻找处理这一异常的代码。找到能够处理这种类型异常的代码后, 运行时系统把当前异常对象交给该代码进行处理, 这一过程称作捕获异常。
捕获异常使用 catch 关键字( 需要跟 try 关键字连用)
如果 Java 运行时系统找不到可以捕获异常的代码, 则运行时系统将终止, 相应的 Java 程序也将退出
public class ExceptionHandling2 {
public static void main(String[] args) {
int a = 100 ;
int b = 0 ;
try{ /** 尝试执行后边的代码块,可能有异常发生*/
int r = a/b ; // 1、该行会导致产生java.lang.ArithmeticException类型的对象
// 2 、 当前程序将产生的异常对象【抛出】 ( throw )给“运行时系统”( Runtime 对象)
System.out.println( r );
}// 运行时系统把当前异常对象交给该代码进行处理,这一过程叫做【捕获】 (catch)异常
catch(ArithmeticException e){ /**捕获异常*/
// 3 、运行时系统( Runtime对象) 寻找相应的代码来处理该异常
System.out.println("捕获到的异常: " +e);
}
}
}
抛出异常
public class ExceptionHandling3 {
/**
* 在方法声明上采用throws关键字来声明可能抛出哪种类型
的异常
* @throws ArithmeticException
*/
public static void main(String[] args) throws
ArithmeticException{
int a = 100 ;
int b = 0 ;
int r = a/b ; // 1、该行会导致产生java.lang.ArithmeticException类型的对象
// 2 、 当前程序将产生的异常对象【抛出】 ( throw )给“运行时对象”( Runtime 对象)
// 抛出的异常对象 : throw e
// 3 、运行时系统( Runtime对象) 寻找相应的代码来处理该异常
System.out.println( r );
}
}
Java 中的异常分类
Java 中的异常被分为两大类:
Runtime 异常 (运行时异常)
RuntimeException 类及其子类的异常实例被称为 Runtime 异常
Runtime 异常无需显式声明抛出
如果程序需要捕获 Runtime 异常, 也可以使用 try ... catch 块来捕获
Checked 异常 (受检查异常,检查工作是交给编译器处理的)
不是 RuntimeException 类及其子类的异常实例被称为 Checked异常
Java 程序必须显式处理 Checked 异常
如果程序没有处理 Checked 异常, 该程序在编译时就会发生错误
对于 Checked 异常的处理方式有两种
使用 try ... catch 块来捕获该异常, 然后在对应的 catch 块中修补该异常
当前方法不知道如何处理这种异常, 则定义该方法时声明抛出该异常
区别:
运行时异常不受编译器检查;受检查异常,接受编译器检查,在编译器对源代码编译成字节码的过程中,就要检查程序员是否对“受检查异常”做出异常(捕获或抛出),如果没有处理这些异常,则编译器拒绝编译
/**
* 异常(Exception)的两个分类
* 运行时异常:继承过java.lang.RuntimeException类的那些异
常类的实例,不接受编译器的检查
* 受检查异常:从来没有过继承RuntimeException类的那些异
常类的实例,对于该类型的异常,编译器在编译阶段会检查程序
员是否对这些异常做出处理
* 【注意】:到了运行阶段,大家都一样,该怎么办就怎么办
*/
public class ExceptionHandling4 {
public static void main(String[] args) {
int a = 100 ;91
int b = 0 ;
try {
int r = a /b ; // 可能会导致ArithmeticException异常发生(运行时异常)
System.out.println("r = "+ r);
} catch (ArithmeticException e) {
System.out.println(e);
}
Properties pro = new Properties();
InputStream in = ExceptionHandling4.class.getResourceAsStream("ExceptionHandling3.java");
try {
pro.load(in);
} catch (IOException e) {
System.out.println(e);
}
}
}
异常转译
在开发中,经常出现异常,发现 Java 提供的异常不能满足我们的需求,也就是说看不懂,并不清楚发生了什么事情,所以我们要进行异常转译。下面是我们自己写的异常
/**
* 自己定义一个异常
* 1、如果继承RuntimeException或其子类,则是运行时异常
* 2、如果从来没有继承RuntimeException类,则是受检查异常
*/
public class SuanShuException extends Exception{
public SuanShuException() {
super(); // 调用父类的无参构造
}
public SuanShuException(String message, Throwable cause) {
super(message, cause);// 调用父类带String ,Throwable参数的构造
}
public SuanShuException(String message) {
super(message);// 调用父类带String参数的构造
}
}
/**
* 异常“转译”
* 1、先自己定义一个异常,以便于封装我们的信息
* 2、抓去一个可能存在的异常(作为cause传递给下一个新创建
的异常对象)
* 3、创建另外一个异常类(比如我们自己定义的那个类),并
为它指定(message、 cause)
* 4、将这个新创建的异常抛出(throw)
*/
public class ExceptionHandling5 {
/*如果有受检查异常没有捕获,则可以在它所在的方法上用throws关键字来声明抛出*/
public static void main(String[] args) throws SuanShuException {
int a = 100 ;
int b = 0 ;
try {
int r = a /b ; //创建 ArithmeticException对象时,会将发生异常的信息传递进去
System.out.println("r = "+ r);
} catch (ArithmeticException e) {
//e.printStackTrace();//打印堆栈的信息
//System.out.println(e);//java.lang.ArithmeticException: / by zero
//System.out.println(e.getMessage());// / by zero
// 带两个参数表示第一个是提示信息,第二个参数表示的异常的原因
SuanShuException suan = new SuanShuException("除数不能为0你都不知道啊" , e);
// 向外界抛出一个异常用throw关键字
throw suan; // 抛出一个具体的异常对象(实例)
}
}
}
总结
1、当异常发生时,会产生一个与该异常对应的对象(实例)
2、当前程序会将该异常对象(实例)抛出给当前的“运行时系统”(Runtime类的一个实例)
3、 “运行时系统”会寻找相应的代码来处理该异常处理异常
积极:
try{
// 可能会引发异常的代码
}catch( 异常类型A 变量名){
// 对异常做出处理
}catch( 异常类型B 变量名){
// 对异常做出处理
}finally{
// 无论如何都要执行的代码
}
catch之后的()中的类型是有要求的,如果catch之后()内声明的是IOException,而代码中产生的是ArithmeticException类型的异常,则这个catch无法处理这个异常,需要运行时系统再寻找其他处理代码
无论是正常执行try中的代码,还是发生异常,程序都要继续向下执行,因此就会有一些代码,是无论发生异常还是不发生异常都要执行,这些代码在finally中声明
消极: 可以接着将异常抛出(可能会导致程序退出)
对于受检查异常来说,则必须在相应的方法上使用throws来声明抛出异常
public void hello() throws 异常类型A , 异常类型B{
}
public void hello() throws 受检查异常{
throw new 受检查异常; // 编译失败
}
对于运行时异常来说,可以在相应的方法上使用
throws声明抛出,也可以不写