Java 异常处理机制

一、产生原因

代码运行过程中可能会出现异常,为了保证程序在出现异常后能够正确执行完毕,就需要进行异常处理。

二、异常的继承结构

Throwable是所有异常的父类

 

其有两个子类:Error和Exception

 

 

而对于Exception类来说,它又有两个子类:RuntimeException和IOException

 

 

继承类结构:

 

其中,

Error描述的是Java运行时内部错误和资源耗尽错误。应用程序不抛出此类异常,这种异常一旦发生,除了告知用户并使程序安全终止外,再无能为力。

Exception的两个子类:

RuntimeException描述的是由于程序内部错误导致的异常。比如ArrayIndexOutOfBoundsException【数组下标越界异常】、ArithmeticException【算术异常,比如除数为0的情况】、NullPointerException【空指针异常】、ClassCastException【强制类型转换异常】等。

IOException描述的是程序本身没有问题,而由IO错误导致的异常。比如FileNotFoundException【文件找不到异常】、FileAlreadyExistsException【文件已存在异常】等。

另外,我们把派生于Error类和RuntimeException类的异常称为非受查异常,也叫运行时异常;其它异常称为受查异常,也叫检查异常。

三、异常处理格式

try{

//有可能出现异常的语句

}catch(异常类 对象){

//异常信息

}finally{

//无论是否发生异常,都会执行到

}

三种组合方式:try...catch...、try...finally...、try...catch...finally

import java.util.Scanner;

public class Test {
	public int calculate(int x, int y){
        return x / y;
    }

    public static void main(String[] args) {
        //1. try...catch
//        Test test = new Test();
//        Scanner scanner = new Scanner(System.in);
//        while (scanner.hasNext()) {
//            int x = scanner.nextInt();
//            int y = scanner.nextInt();
//            try {
//                System.out.println(test.calculate(x, y));
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }

        //2. try...finally
//        Test test = new Test();
//        Scanner scanner = new Scanner(System.in);
//        while (scanner.hasNext()) {
//            int x = scanner.nextInt();
//            int y = scanner.nextInt();
//            try {
//                System.out.println(test.calculate(x, y));
//            } finally {
//                System.out.println("计算完毕");
//            }
//        }

         //3.try...catch...finally
        Test test = new Test();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            try {
                System.out.println(test.calculate(x, y));
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                System.out.println("计算完毕");
            }
        }
    }
}

当然,如果出现异常了,不想进行处理的话,就可以使用throws关键字将异常抛出。告诉调用者本方法有可能产生的异常,并且调用者在调用此方法时,必须使用try...catch捕获异常。

import java.util.Scanner;

public class Test {
	public int calculate(int x, int y) throws Exception {
        return x / y;
    }

    public static void main(String[] args) {
		Test test = new Test();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            try {
                System.out.println(test.calculate(x, y));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("计算完毕");
            }
        }
    }
}

注:如果主方法上有throws关键字的话,那么此时如果出现了异常,就会交给JVM去处理。

如果想手动抛出异常但是有时候有些异常在JVM看来不是异常的情况下,就可以使用throw。比如说年龄出现负数的情况,此时在JVM看来并不是异常,但是实际中年龄不可能为负数,所以此时需要我们手动抛出异常。

public class Test {
    int age = 0;

    public void test() {
        age = -1;
        if (age < 0) {
            try {
                throw new Exception("年龄为负数啦!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(age);
        }
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.test();
    }

}

反编译某个java文件:

  1. 右击该文件名,点击Open in terminal
  2. 输入javac 文件名.java(编译)
  3. 输入javap -v 文件名(反编译)
  4. 点击此时生成的文件名.class文件,就可以看到反编译后的文件

四、关于try...catch...finally使用时的一些特殊情况解读【含return语句】

代码1:

public class Test {
	//此时的执行顺序:
	//1.执行try块中的i=1以及return i语句,并暂存i值【1】
	//2.执行finally块中的i=3语句
	//3.返回try块的return语句,并返回此时暂存的i值【1】
    public int test() {
        int i = 0;
        try {
            i = 1;
            return i;
        } catch (Exception e) {
            i = 2;
            return i;
        } finally {
            i = 3;
        }

    }

    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.test());    //1
    }
}

代码2:

public class Test {

    //此时的执行顺序:
	//1.执行try块中的i=1以及return i语句,此时的i=1
	//2.执行finally块中的i=3以及return i语句,此时的i=3
	//3.返回finally块中的return语句,返回此时的i值【i=3】
    public int test() {
        int i = 0;
        try {
            i = 1;
            return i;
        } catch (Exception e) {
            i = 2;
            return i;
        } finally {
            i = 3;
            return i;
        }

    }

    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.test());   //3
    }
}

代码3:

public class Test {

    int age;
    //此时的执行顺序:
	//1. 产生一个对象
	//2. 执行try块中的test.age=10以及return test语句
	//3. 执行finally块中的test=new Test()以及test.age=30语句  此时产生了另一个对象,并对另一个对象的age属性做了修改
	//4. 返回try块的return语句,返回第1个对象
    public static Test test() {
        Test test = new Test();
        try {
            test.age = 10;
            return test;
        }catch (Exception e) {
            test.age = 20;
            return test;
        }finally {
            test = new Test();
            test.age = 30;
        }
    }
	
	public static void main(String[] args) {
        System.out.println(test().age);  //10
    }
}

代码4:

public class Test {

    int age;
    public static void main(String[] args) {
        System.out.println(test().age);  //30
    }
	
	//此时的执行顺序:
	//1. 产生一个对象
	//2. 执行try块中的test.age=10以及return test语句
	//3. 执行finally块中的test=new Test()以及test.age=30语句  此时产生了另一个对象,并对另一个对象的age属性做了修改
	//4. 返回finally块的return语句,返回第2个对象
    public static Test test() {
        Test test = new Test();
        try {
            test.age = 10;
            return test;
        }catch (Exception e) {
            test.age = 20;
            return test;
        }finally {
            test = new Test();
            test.age = 30;
            return test;
        }
    }
}

代码5:

public class Test {

    int age;
    public static void main(String[] args) {
        System.out.println(test().age);  //30
    }
	
	//此时的执行顺序:
	//1. 产生一个对象
	//2. 执行try块中的test.age=10以及return test语句
	//3. 执行finally块中的test.age=30语句  对同一个对象的age属性做了修改
	//4. 返回try块的return语句,返回该对象
    public static Test test() {
        Test test = new Test();
        try {
            test.age = 10;
            return test;
        }catch (Exception e) {
            test.age = 20;
            return test;
        }finally {
            test.age = 30;
        }
    }
}

代码6:

public class Test {

    int age;
    public static void main(String[] args) {
        System.out.println(test().age);  //30
    }
	
	//此时的执行顺序:
	//1. 产生一个对象
	//2. 执行try块中的test.age=10以及return test语句
	//3. 执行finally块中的test.age=30语句  对同一个对象的age属性做了修改
	//4. 返回finally块的return语句,返回该对象
    public static Test test() {
        Test test = new Test();
        try {
            test.age = 10;
            return test;
        }catch (Exception e) {
            test.age = 20;
            return test;
        }finally {
            test.age = 30;
            return test;
        }
    }
}

 

操作的变量是局部变量的情况下

1. try和finally块中都有return语句

不论try块和finally块中改变的是否是同一个局部变量,返回的都是finally块中对该块中局部变量的修改值

2. try中有return语句,finally中没有return语句

不论try块和finally块中改变的是否是同一个局部变量,返回的都是try块中对该块中局部变量的修改值

 

操作的变量是成员变量的情况下

1. try和finally块中都有return语句

不论try块和finally块中改变的是否是同一个对象的成员变量,返回的都是finally块中对对象成员变量的修改值

2. try中有return语句,finally中没有return语句

第一种情况:try块和finally块中改变的是同一个对象的成员变量,那么finally块中对该成员变量的修改会影响try块的return语句

第二种情况:try块和finally块中改变的不是同一个对象的成员变量,那么finally块中对该成员变量的修改不会影响try块的return语句

 

总结:只要finally块中有return语句,那么最终执行的return语句永远是finally块中的;如果finally块中没有return语句,那么此时要看try块和finally块操作的是否是同一个对象的同一个成员变量,如果是,那么finally块对该成员变量的修改会影响到try中的return语句;否则,不论此时finally块中操作的是同一个对象的不同成员变量,还是不同对象的成员变量,还是只是一个局部变量,都不会对try中的return语句产生影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值