一、产生原因
代码运行过程中可能会出现异常,为了保证程序在出现异常后能够正确执行完毕,就需要进行异常处理。
二、异常的继承结构
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文件:
- 右击该文件名,点击Open in terminal
- 输入javac 文件名.java(编译)
- 输入javap -v 文件名(反编译)
- 点击此时生成的文件名.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语句产生影响。