第十八章 Java中的异常处理
提纲
- 18.1 异常的概述
- 18.2 编写时异常
- 18.3 运行时异常
- 18.3.1 什么是运行时异常
- 18.3.2 异常举例
- 18.3.3 捕捉异常
- 18.4 Java常见的异常
- 18.4.1 Java异常类层次结构图
- 18.4.2 常见异常及解释举例
- 18.4.3 举例
- 18.5 使用throws关键字抛出异常
- 18.6 自定义异常与thorw关键字抛出异常
- 18.7 作业
18.1 异常的概述
- 在程序中,错误可能产生于程序员没有预料到的各种情况,或者是超出了程序员可控范围的环境因素,如用户的坏数据、视图打开一个根本不存在的文件等。
- 异常分为两种:一种是在编写的过程中产生的语法错误,代码编译器会用红线进行提示。第二种是运行过程中所产生的错误。
18.2 编写时异常
-
什么是编写时异常:编写的过程中产生的语法错误,代码编译器会用红线进行提示。
-
举例:
public class Test { public static void main(String[] args) { //当我用字符串给int类型赋值时,编译器直接报错。这个就是编写时异常 int a = "AAA"; } }
-
总结:编写时所产生的错误是必须在程序运行前解决的,否则程序是运行不了的。一般情况下可以把鼠标放在报红线的地方,编译器会给出英文提示。可以将英文提示用翻译器翻译出来。
18.3 运行时异常
-
18.3.1 什么是运行时异常:程序编写后编译器不会报出任何错误,但是在程序运行的过程中会因为某些错误导致JVM虚拟机无法执行而产生异常,如果异常产生不做任何处理,程序就会被终止。
-
18.3.2 异常举例:
public class Test { public static void main(String[] args) { int result = 3 / 0;//定义一个整型变量 System.out.println("result="+result);//输出结果 } } //执行结果 Exception in thread "main" java.lang.ArithmeticException: / by zero at com.zxjy.day1012.Test.main(Test.java:5)
结论:1.结果中并未打印result=语句。证明程序终止在了错误的那一句。int result = 3 / 0;2.在打印的结果中有一个java.lang.ArithmeticException异常,并且打印了错误信息:/ by zero,即除数为0的错误。
-
18.3.3 捕捉异常
-
什么叫捕捉异常:当程序员觉得某处代码块可能会出现异常,则通过一个语句块来处理可能产生异常的这部分。
-
Java语言的异常捕获结构由try、catch和finally3部分组成。其中,try语句块存放的是可能发生异常的Java语句;catch程序快在try语句块之后,用来激发被捕获的异常;finally语句块是异常处理结构的最后执行部分,无论try语句块中的代码如何退出,都将执行finally语句块。try不能单独存在。有try-catch块,有try-finally块。
//捕捉异常语法 try { } catch (异常类型1 e) { //对异常类型1进行处理 } catch (异常类型2 e) { //对异常类型2进行处理 } .... finally{ //程序块 }
-
举例:单个catch块
public static void main(String[] args) { try { System.out.println("计算结果是"); int result = 3 / 0; System.out.println("result="+result); } catch (Exception e) { System.out.println("这里是异常处理模块!"); System.out.println("e.getMessage():"+e.getMessage()); System.out.println("e.toString():"+e.toString()); e.printStackTrace(); } finally { System.out.println("finally语句块!"); } System.out.println("程序结束!"); } //执行结果 计算结果是 这里是异常处理模块! e.getMessage():/ by zero e.toString():java.lang.ArithmeticException: / by zero java.lang.ArithmeticException: / by zero at com.zxjy.day1012.Test.main(Test.java:7) finally语句块! 程序结束!
- 结论:
- 发生异常的语句之前代码可以执行,try-catch语句块中,异常语句之后的代码没有执行,而是直接运行了catch中的语句。
- 其中,Exception是一个异常对象类型,e是变量名,这个类中的主要方法:e.getMessage():得到具体错误的性质,e.toString():给出异常的类型与性质,e.printStackTrace();指出异常的类型、性质及出现在程序中的位置。
- 程序没有终止,try-catch语句块外的语句继续在执行。
- Java的异常处理是结构化的,不会因为一个异常影响整个程序的执行。
- finally语句块:无论程序中有无异常发生,并且无论之间的try-catch是否顺利执行完毕,都会执行finally语句。但是有以下五种特殊情况下,finally块不会被执行:
- 在finally语句块中发生了异常
- 在前面的代码中使用了System.exit(0)退出程序。这个System.exit(0)代码是终止Java虚拟机的运行,即退出结束当前的程序。
- 程序所在的线程死亡。
- 关闭CPU。
- 如果一个方法内在执行try{}语句之前就已经return了。只有与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块才会执行。
- 结论:
-
18.4 Java常见的异常
-
18.4.1 Java异常类层次结构图(见图解)
-
18.4.2 常见异常及解释举例:Java中提供了一些异常用来描述经常发生的错误,其中,有的需要程序员进行捕获处理或声明抛出,有的是由Java虚拟机自动进行捕获处理。
- 运行时异常RuntimeException
-
ArithmeticException:算术异常。(当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。)
-
ArrayIndexOutOfBoundsException:数组下标越界异常。(用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。)
-
NullPointerException:空指针异常。(当应用程序试图在需要对象的地方使用 null 时,抛出该异常。)
这种情况包括: 调用 null 对象的实例方法。 访问或修改 null 对象的字段。 将 null 作为一个数组,获得其长度。
-
ArrayStoreException:数组中包含不兼容的值抛出的异常。(试图将错误类型的对象存储到一个对象数组时抛出的异常。)
Object x[] = new String[3]; x[0] = new Integer(0);
-
IllegalArgumentException:非法参数异常。(抛出的异常表明向方法传递了一个不合法或不正确的参数。)
-
SecurityException:安全性异常。(由安全管理器抛出的异常,指示存在安全侵犯。)
-
NegativeArraySizeException:数组元素个数为负数抛出的异常。(如果应用程序试图创建大小为负的数组,则抛出该异常。)
-
- 其他异常
-
ClassCastException:类型转换异常。(API官方解释:当试图将对象强制转换为不是实例的子类时,抛出该异常)
Object x = new Integer(0); System.out.println((String)x);
-
ClassNotFoundException:未找到相应类异常。(当应用程序试图使用以下方法通过字符串名加载类时,抛出该异常)
Class 类中的 forName 方法。 ClassLoader 类中的 findSystemClass 方法。 ClassLoader 类中的 loadClass 方法。
-
SQLException:操作数据库异常类。(提供关于数据库访问错误或其他错误信息的异常。 )
-
NoSuchFieldException:字段未找到异常。(类不包含指定名称的字段时产生的信号。 )
-
NoSuchMethodException:方法未找到抛出的异常。(无法找到某一特定方法时,抛出该异常。)
-
NumberFormatException:字符串转换为数字抛出异常。(当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。)
-
StringIndexOutOfBoundsException:字符串索引超出范围抛出的异常。(此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。)
-
IOException:输入输出异常。(当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操作生成的异常的通用类。)
-
IllegalAccessException:不允许访问某类异常。(当应用程序试图反射性地创建一个实例(而不是数组)、设置或获取一个字段,或者调用一个方法,但当前正在执行的方法无法访问指定类、字段、方法或构造方法的定义时,抛出 IllegalAccessException。)
-
InstantiationException:当应用程序试图使用Class类中的newInstance()方法创建一个类的实例时,而指定的类的对象无法被实例化,抛出该异常。
-
EOFException:文件已结束异常。(当输入过程中意外到达文件或流的末尾时,抛出此异常。)
-
FileNotFoundException:文件未找到异常。(当试图打开指定路径名表示的文件失败时,抛出此异常。)
-
- 运行时异常RuntimeException
-
18.4.3 举例:
-
18.4.3.1 例1:要求实现,要求输入一个int类型的整数,如果输入的不是整数则要求重新输入。
public static int getInt() { int a=0; Scanner scan=new Scanner(System.in); try { a=scan.nextInt(); } catch (Exception e) { System.out.println("只能输入数字,请重新输入"); a=getInt();//输入错误,继续调自己 } return a; }
-
18.4.3.2 例2:嵌套try-catch块
//1.嵌套try-catch块如果内层没有捕捉到则会继续到外层判断 public class Test { public static void main(String[] args) { try { try { int a=10; int b=0; int c=a/b; } catch(ArrayIndexOutOfBoundsException e) {//内层数组越界异常,但是这个是算术异常 System.out.println("内层异常"); } } catch (Exception e) { System.out.println("外层异常"); } } } //执行结果 外层异常 //2. 嵌套try-catch块如果内层捕捉到了之后就不会进行外层判断 public class Test { public static void main(String[] args) { try { try { int a=10; int b=0; int c=a/b; } catch(ArithmeticException e) {//内层算术异常 System.out.println("内层异常"); } } catch (Exception e) { System.out.println("外层异常"); } } } //执行结果 内层异常
-
18.4.3.3 例3:多个try-catch块
public class Test { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.println("请输入第一个数:"); int a = scan.nextInt(); System.out.println("请输入第二个数:"); int b = scan.nextInt(); int[] arry = new int[a]; String str = null; try { int c = a / b; arry[b] = a; System.out.println(str.equals(a)); System.out.println(c); } catch (ArithmeticException e) { System.out.println("算术异常"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("数组下标越界了"); } catch (RuntimeException e) { System.out.println("运行时异常"); } } } //执行结果 1. 请输入第一个数: 10 请输入第二个数: 0 算术异常 2. 请输入第一个数: 10 请输入第二个数: -3 数组下标越界了 3. 请输入第一个数: 10 请输入第二个数: 5 运行时异常
结论:1.多个catch块时越是父类,越要往后写,否则会报错,可平行写。因为越是父类,包含的错误信息越多。2.如果第一层没有捕捉到则会继续往下一层判断,直到判断到可捕捉的异常时停止。
-
18.4.3.4 例4:try-finally块:即将异常向外抛出,自己并不处理
public class Test { public static void main(String[] args) { try { int a = 11/0; } finally { System.out.println("finally语句块"); } } } //执行结果 finally语句块 Exception in thread "main" java.lang.ArithmeticException: / by zero at com.zxjy.day1012.Test.main(Test.java:7)
结论:1.先执行try代码块,再执行finally代码块,最后将异常抛出。2.好处:它做了自己必须要做的事(finally),并向外抛出自己无法处理的异常;对于调用者来说,能够感知出现的异常,并可以按照需要进行处理。也就是说这种结构实现了职责的分离,实现了异常处理(throw)与异常清理(finally)的解耦,让不同的方法专注于自己应该做的事。
-
18.4.3.5 例5:finally在return之前执行还是在return之后执行:在return中间执行。
public class Test { public static void main(String[] args) { System.out.println(Test.method()); } public static int method(){ try { return 1; } catch (Exception e) { return 0; } finally { return 2; } } } //执行结果 2
**结论:**1.执行过程:这里仅仅需要注意的是在try{}语句中执行到return 1 会在临时栈中存储值为1的变量。接着回去执行finally里面的内容,这时执行finally中的return 2;方法,这时临时栈中的值就是变为 2,会覆盖原来临时栈中的值1.所以它的返回值为2。2.finally 语句块在 try 语句块中的 return 语句之前执行。3.finally 语句块在 catch 语句块中的 return 语句之前执行。
-
18.5 使用throws关键字抛出异常
-
什么是throws:在某个方法可能会发生异常,但不想在当前方法中处理这个异常,则可以使用throws、throw关键字在方法中抛出异常。throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常。向外抛出异常。
-
举例:
public class Shoot { static void pop() throws NegativeArraySizeException{ //定义方法并抛出NegativeArraySizeException异常 int[] arr = new int[-2];//创建数组 } public static void main(String[] args) { try {//try语句处理异常信息 Shoot.pop();//调用pop()方法 } catch (NegativeArraySizeException e) { System.out.println("pop()方法抛出的异常"); e.printStackTrace(); } } } //执行结果 pop()方法抛出的异常
结论:1.使用throws关键字将异常抛给上一级后,如果不想处理该异常,可以继续向上抛出,但是最终要有能够处理该异常的代码。2.如果是Error、RuntimeException或他们的子类,可以不用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
18.6 自定义异常与thorw关键字抛出异常
-
为什么使用自定义异常:使用Java内置的异常类可以描述在编程时出现的大部分异常情况。但是在出现我们认为的逻辑错误时,会使用到自定义异常。比如,一般情况下一个人的年龄不能小于0,不能大于200。如果设置了年龄在这个区间外程序不会报错,但是我们认为是错误的。
-
如何创建一个自定义异常类:用户只需要继承Exception类即可自定义异常类。
-
使用自定义异常类的几个步骤:
- 创建自定义异常类。
- 在方法中通过throw关键字抛出异常对象。
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句块捕获并处理,否则在方法的声明处通过thorws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常。
-
举例:如何创建一个自定义异常类。
//创建自定义异常,继承Exception类 public class AgeException extends Exception{ String msg; //构造方法,errorMsg是要输出的错误信息 public AgeException(String errorMsg) { super(errorMsg);//给父类的构造方法传参 this.msg = errorMsg;//将错误信息赋值给msg } //覆盖getMessage()方法 public String getMessage() { return msg; } }
-
throw关键字:
-
什么是throw:thorw关键字通常用于方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即终止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法的声明中要抛出的异常;如果要捕捉throw抛出的异常。则必须使用try-catch语句块。
-
举例:解决年龄所出现的逻辑问题。
//1.创建自定义异常,继承Exception类 public class AgeException extends Exception{ String msg; //构造方法,errorMsg是要输出的错误信息 public AgeException(String errorMsg) { super(errorMsg);//给父类的构造方法传参 this.msg = errorMsg;//将错误信息赋值给msg } //覆盖getMessage()方法 public String getMessage() { return msg; } } //2.创建一个人类Person public class Person { private int age; public int getAge() { return age; } //设置年龄,如果年龄不在0-200则抛出相应异常,如果不捕捉处理则必须用throws抛出。 public void setAge(int age) throws AgeException { if (age > 200) { //手动抛出一个自定义异常,也可以抛出内置的异常,但是throws必须抛出 throw new Exception("年龄不能大于200!"); throw new AgeException("年龄不能大于200!"); } else if (age < 0) { throw new AgeException("年龄不能小于0!"); } else { this.age = age; } } } //3.测试类测试 public class Test { public static void main(String[] args) { Person person = new Person(); try { person.setAge(500); } catch (AgeException e) { e.printStackTrace(); } finally { System.out.println("年龄为:"+person.getAge()); } } } //4.执行结果 com.zxjy.day1012.AgeException: 年龄不能大于200! at com.zxjy.day1012.Person.setAge(Person.java:12) at com.zxjy.day1012.Test.main(Test.java:7) 年龄为:0
-
18.7 作业
- 编写如下一段程序:
-
定义一个长度为10的数组,并给默认值。
-
然后由控制台输入两个整数,第一个表示要访问的数组的索引,第二个表示获得的元素要除以的数字。
-
结果为将第一个数字对应索引的数组元素值除以输入的第二个数字。
5 10 2 100 87 index input: 2 num input: 10 sysout(arr [index]/num)
-
考虑 以上代码会可能会出现多少种异常,并用异常处理的代码块来处理异常。
-
- 编写如下一段程序:
- 实现从控制台输入三个数值作为三角形的三条边,以此构造一个三角形。程序应该实现如下功能:
- 检查输入的命令行参数是否为数值格式,如果输入的数值参数格式错误,则程序可能产生InputMismatchException异常(java.util.InputMismatchException 需要导入这个类)。
- 检查输入的命令行参数中是否有负值或0,如果有负值或0,则程序可能产生NumberValueException异常( NumberValueException为自定义异常)。
- 检查输入的命令行参数的三个数值是否能够构造一个三角形,如果不能构造一个三角形,则程序可能产生TriangleException异常(TriangleException为自定义异常)。
- 没出现异常则显示三个数字。
- 提示:三角形任意两边之和必须大于第三边。
- (课后)编写继承体系: 宠物(eat(),cry()),猫,狗(重写父类方法)。
- 编写宠物店类 提供方法:出售宠物 Pet sellPet(int i)。传入1 则返回猫对象,2 则返回狗对象。
- 若传入的参数不是1或2,则抛出异常,显示:类型错误!!!
- 在方法内使用try catch 处理异常,注意:思考出现异常时如何处理返回值。
- 无论是否有异常 都要能输出一句话:“欢迎下次光临”。
- 测试sellPet() 方法,调用返回的对象的cry方法。
berValueException为自定义异常)。 - 检查输入的命令行参数的三个数值是否能够构造一个三角形,如果不能构造一个三角形,则程序可能产生TriangleException异常(TriangleException为自定义异常)。
- 没出现异常则显示三个数字。
- 提示:三角形任意两边之和必须大于第三边。
- (课后)编写继承体系: 宠物(eat(),cry()),猫,狗(重写父类方法)。
- 编写宠物店类 提供方法:出售宠物 Pet sellPet(int i)。传入1 则返回猫对象,2 则返回狗对象。
- 若传入的参数不是1或2,则抛出异常,显示:类型错误!!!
- 在方法内使用try catch 处理异常,注意:思考出现异常时如何处理返回值。
- 无论是否有异常 都要能输出一句话:“欢迎下次光临”。
- 测试sellPet() 方法,调用返回的对象的cry方法。