一、异常的概念
异常的概念:在java程序运行过程中,出现的不正常的情况,出现的错误,称为异常。
异常就是一个对象,描述了那些不符合正常运行的情况,包含了这些情况的原因、类型、描述以及位置,这些内容都封装到异常对象中。
Java程序运行过程中所发生的异常事件可分为两类:
Error:JAM系统内部错误、资源耗尽等严重情况
Exception:其它因编程错误或偶然的外在因素导致的一般性问题。比如:空指针访问、试图读取不存在的文件等
二、异常的处理机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
捕捉异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
在jvm中默认处理异常的机制
1.在代码的某个位置,出现和正常情况不同的情况,就将异常封装到一个异常对象。
2.将异常对象抛给调用该方法的方法。
3.某个方法接收到抛过来的异常,也没有办法自己处理,继续向上抛出,最终抛给主方法,主方法也没有办法处理,抛给调用自己的jvm虚拟机。
4.JVM只能将错误信息通过错误流打印出来。
手动处理异常的方式(两种方式)
异常的声明:某个方法有编译时异常,编译就会无法通过,需要在异常所在的方法声明上,声明该方法可能出现的编译异常。
异常的处理:出现异常之后,可以通过某些格式来捕获和处理异常,可以定义自己处理异常的逻辑。
捕获处理异常的格式:
try...catch
try...catch...finally
try...finally
(1)异常捕获处理的第一种格式(方式一)
格式:
try{
可能发生异常的代码
}catch(){
这种异常出现之后的处理方式
}
运行机制:
1.运行try语句中的代码,检测。
2.如果没有发生异常,那么不再运行catch块中的内容。
3.如果发生了catch块中声明的异常,那就会捕获到这个异常,并且进行处理,后面的程序可以继续执行。
4.如果发生catch中没有声明的异常,那么就无法捕获异常,该异常的处理机制就是jvm的默认处理机制。
代码示例
public class Test {
public static void main(String[] args) {
test01(); //长点心吧,不要再瞎搞的
}
private static void test01() {
test02();
}
private static void test02() {
try {
//int i = 1/0;
int[] arr = new int[5];
arr[6] = 7;
}catch(ArrayIndexOutOfBoundsException e) {
//处理异常的代码
//System.out.println("除零异常发生,请重新运行程序");
System.out.println("长点心吧,不要再瞎搞的");
}
}
}
(2)异常捕获处理的第一种格式(方式二)
格式:
try{
}catch(异常类型1 异常对象){
}catch(异常类型2 异常对象){
}
在jdk1.7之后可使用此方式
catch(异常类型1 | 异常类型2 异常对象名){
异常类型1 | 异常类型2的共同处理方式
}
catch(Exception e)模块一般都写在catch块的最下面。
代码示例
public class Test {
public static void main(String[] args) {
try {
//int i = 1/0;出现算术异常了...
//int[] arr = new int[5];
//arr[7] = 10;出现算术异常了...
Object obj = null;
Class clazz = obj.getClass();//出现空指针异常了...
}catch(ArithmeticException | ArrayIndexOutOfBoundsException e) {
System.out.println("出现算术异常了...");
}catch(NullPointerException e) {
System.out.println("出现空指针异常了...");
}catch(Exception e) {
}
}
}
(3)异常捕获的第二种格式
格式:
try{
可能发生异常的代码
}catch(Exception e){
当前异常的处理方式
}finally{
一定要执行的代码
}
代码示例
public class Test {
public static void main(String[] args) {
try {
//int i = 1 / 1; finally中一定会执行的语句
/*int[] arr = new int[5];
arr[7] = 10;*/ //出现异常了...
//finally中一定会执行的语句
} catch (Exception e) {
System.out.println("出现异常了...");
}finally {
//一定会执行的代码块 释放资源
System.out.println("finally中一定会执行的语句");
}
}
}
(4)异常处理的第三种格式
格式:
try{
可能发生异常的代码;
}finally{
一定会执行的代码
}
作用:如果有两句代码,都需要执行,不希望第一句代码执行成功与否影响第二句的执行,那么就把这两句代码分别放在try和finally中。
代码示例
public class Test {
public static void main(String[] args) {
try {
int i = 1 / 0;
} finally {
System.out.println("上面报错不影响finally中的输出");
}
}
}
/*上面报错不影响finally中的输出
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:5)
*/
final, finally, finalize的区别-
final:修饰类,类不能被继承。修饰方法,方法不能被重写。修饰变量:基本数据类型,值不能被改变;引用数据类型,地址不能被修改(new...),变量值可以修改
finally:try-catch语句中用的,最后执行的语句。
finalize:垃圾回收(对象,长时间不适用,垃圾回收。静态变量除外。)。可以显示的调用,但是什么时候回收不由我们决定。
三、异常的体系
Throwable:有两个重要的子类:Error(错误)和Exception(异常)。二者都是java异常处理的重要子类,各种都包含大量子类。
Error:用于描述那些无法捕获和处理的错误情况,属于非常严重的错误。(内存溢出,栈溢出)
Exception:用于描述那些可以捕获和处理的异常情况,属于不太严重的错误。
RuntimeException:运行时异常,在编译阶段不做检查的一个体系。
注:非运行时异常(受检查异常)
Throwable类
toString() 输出该异常的类名。
getMessage() 输出异常的信息,需要通过构造方法传入异常信息(例如病态信息)。
printStackTrace() 打印栈信息。
示例代码
public class Test {
public static void main(String[] args) {
Throwable able = new Throwable("异常");
System.out.println(able.toString()); // 输出该异常的类名
System.out.println(" ");
System.out.println(able.getMessage()); // 输出异常的信息
System.out.println(" ");
able.printStackTrace(); // 打印栈
}
}
java的异常分为可检查的异常和不可检查的异常
可检查的异常(编译器要求必须处置的异常)
编译器要求你必须要对这段代码try...catch,或者throws exception,这种异常的特点是java编译器会检查它。
除了RuntimeException与其子类,以及错误(Error),其他的都是可检查的异常。
四、throws和throw关键字
1.throws
当程序中发生异常时,无论是程序运行出现的运行时异常,编译阶段的非运行时异常,还是手工抛出的异常,都可以在程序中使用try{}catch进行处理。但也可以在当前的程序(也就是当前方法中),不做任何处理。使用throws关键字在方法声明时,进行异常的抛出,交给方法的调用者去处理。当方法的调用者也不知道如何处理,或不想处理时,可以继续向上抛出。
格式
类型 方法名(参数列表) throws 异常列表{
代码
}
注:多个异常之间,用英文逗号隔开
代码示例
public class Test {
public static void main(String[] args) throws Exception{
test01();
}
private static void test01() throws FileNotFoundException,IOException{
InputStream is = new FileInputStream("1.txt");
}
}
throws抛出异常
如果是不可查异常,即Error、RuntimeException或它们的子类,可以不使用throws关键字来声明要抛出的异常。
必须声明方法可抛出的任何可查异常
仅当抛出了异常,该方法的调用者才必须处理或者更新抛出该异常。检查异常,如果在方法体上throws,在调用此方法的方法内try-catch,要没在方法体上继续throws,throws到main方法上就到头了。
调用方法必须遵循任何可减产异常的处理和声明规则。如果catch的异常很多,直接RuntimeException,Exception。
2.throw关键字
手工抛出异常,其效果和程序抛出异常是一样的,只是它是人为地抛出。
格式:throw ThrowableObject
示例代码
public class Test {
public static void main(String args[]) {
System.out.print("Now");
try {
System.out.print("is");
throw new NullPointerException(); // 直接抛出一个异常 ;
// System.out.print("This will not execute!"); //此句不被执行
} catch (NullPointerException m) {
System.out.print("the");
}
System.out.print("time. ");
}
}
//Nowisthetime.
3.throws和throw的区别
throw是对异常对象的抛出,throws是对异常的声明。
throw是实实在在地抛出对象,一旦使用throw,一定会产生异常对象,throw只能跟一个异常对象。
throws后面跟的异常的类名,可以跟多个,使用逗号隔开。
五、自定义异常
问题:现实中会出现新的病,就需要新的描述。
分析: Java的面向对象思想将程序中出现的特有问题进行封装。
当jdk内置异常类型不足以满足系统需求时,我们需要自定义异常类型。
使用自定义异常和使用jdk内置异常是一回事。
自定义异常的方式: 继承Exception 或 RuntimeException
示例代码
//自定义的运行时异常
public class MyException extends RuntimeException{
/*
* 在自定义异常时,完全可以自定义一些属性和方法。但多数时间不需要这样做
* */
public MyException(){
}
public MyException(String message){
super(message);
}
}
示例代码
// 自定义一个非运行时异常
public class MyException2 extends Exception {
public MyException2(){
}
public MyException2(String message){
super(message);
}
}
案例:模拟吃饭没带钱的问题
定义吃饭功能,需要钱。(例如:eat(double money))
如果钱不够是不能吃饭,有异常。
自定义NoMoneyException();继承Exception 提供有参无参构造,调用父类有参构造初始化。eat 方法进行判断,小于10块,throw NoMoneyException("钱不够");
eat 方法进行声明,throws NoMoneyException;
如果钱不够老板要处理。调用者进行处理。try{}catch(){} 。
class NoMoneyException extends Exception {
NoMoneyException() {
}
NoMoneyException(String message) {
super(message);
}
}
class Test {
public static void main(String[] args) {
try {
//eat(0);//跟我干活吧
// eat(5);//跟我干活吧
//eat(11);//吃桂林米粉
} catch (NoMoneyException e) {
System.out.println("跟我干活吧。");
}
}
public static void eat(double money) throws NoMoneyException {
if (money < 10) {
throw new NoMoneyException("钱不够");
}
System.out.println("吃桂林米粉");
}
}
六、注意事项
1.必须在try之后至少添加catch或finally块,也可以都有。
2.必须遵循块顺序:若代码同时使用catch和finall块,则必须将catch块放在try之后。
3.catch块与相应的异常类的类型相关。不用都写Exception。
4.一个try块可能有多个catch。多个catch块异常,应该由子类到父类,或者兄弟之间可以并列,父类放到最后