8.1异常的继承体系
在Java中,使用类 Throwable 来描述所有的不正常的情况。 Throwable有两个子类,Error 和 Exception 。其中 Error 用来描述发生在JVM级别的错误信息,这些错误,无法被处理。 Exception 用来描述程序在编译或者运行的过程中遇到的异常信息,这些异常一旦处理了,对程序的编译和运行是没有影响的。
Throwable:Error\Exception
Error:VirtualMachineError(虚拟机错误)、OutOfMemoryError(内存溢出)、ThreadDeath(线程死锁)
Exception:RuntimeException、IOException(IO异常)、SQLException(SQL异常)
RuntimeException:NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组下标越界异常)、ArithmeticException(算数异常)、ClassCastException(类型转换异常)
异常可以分为 运行时异常 和 编译时异常
8.2异常处理的语法
try { // 将可能会出现异常的代码放到这里执行 // 一旦try中的代码出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。 }catch (需要捕获的异常的类型 标识符) { // 如果try中的代码出现了异常,并且异常对象的类型和捕获的类型一致 // 这里的代码将会执行 // 一旦一个异常被捕获了,那么这个异常将不再影响程序的编译和执行 }
-
将可能出异常的代码片段放入try的语句块中,
-
jvm在运行程序时,如果出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。
-
jvm在运行程序时,如果出现了异常,会自动创建一个具体的异常对象
-
catch(需要捕获的异常的类型 标识符){}用于捕获异常操作
-
jvm创建的异常对象如果与catch捕获的异常对象类型匹配(或者向上造型没问题),就会就将地址值赋值给异常类型变量。
-
catch捕获到异常对象后,就会执行对应的{}里的代码。这个异常将不再影响程序的编译和执行
-
catch模块如何处理异常呢? 比如打印异常信息,供程序员查看,然后进行调试,或者继续抛给调用者
案例:
try{ int[] nums = {1,2,3,4,5}; for (int i = 0; i <= nums.length; i++) { int num = nums[i]; System.out.println("num = " + num); } }catch (Exception e){ //System.out.println("发生了异常"); e.printStackTrace(); } System.out.println("main方法结束"); } }
8.3多种异常的处理
如果多个catch的异常之间不存在继承关系,不需要考虑书写顺序。 如果多个catch的异常之间存在继承关系,则必须子类异常在前,父类异常在后。
案例:
try{ int[] nums = new int[5]; nums[5] = 100; String str = null; int length = str.length(); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("--数组下标越界--"); }catch (NullPointerException e){ System.out.println("--空指针异常--"); }catch (Exception e){ System.out.println("--异常--"); } //简化版本1:没有继承关系的异常类型,可以写在一个catch中,使用|分开 // 前提条件:处理逻辑一样 try{ int[] nums = new int[5]; nums[5] = 100; String str = null; int length = str.length(); }catch (ArrayIndexOutOfBoundsException|NullPointerException e){ e.printStackTrace(); } //简化版本2: 使用这些异常的共同父类型即可 前提条件:处理逻辑一样 try{ int[] nums = new int[5]; nums[5] = 100; String str = null; int length = str.length(); }catch (Exception e){ e.printStackTrace(); }
8.4 finally模块
finally用在try后面,或者catch后面,作为异常捕获的结尾。
特点:finally中的语句始终会执行。(无论try中的代码是否出现了异常,这里的代码都会执行)
使用场景:会在finally中做资源释放、流的关闭等操作。
String[] names = null; try{ names = new String[3]; String name = names[1]; int length = name.length(); }catch (Exception e){ e.printStackTrace(); }finally { names[1] = "zhangsan"; } System.out.println(Arrays.toString(names)); System.out.println("--main方法结束--"); // finally的应用场景: 一般用于流的关闭操作。 InputStream is = null; try{ is = Exception03.class.getClassLoader().getResourceAsStream(""); BufferedImage image = ImageIO.read(is); }catch (Exception e){ e.printStackTrace(); }finally { try { is.close(); } catch (IOException e) { throw new RuntimeException(e); } } 结果 java.lang.NullPointerException at com.se.day01.aException.Exception03.main(Exception03.java:21) [null, zhangsan, null] --main方法结束-- Process finished with exit code 0
8.5研究一下finally和return的特点
-
当try里有return关键字,以及finally模块没有return 先执行finally模块代码,然后再执行try里的return关键字
-
try和finally里都有return, 一定执行的finally里的return.
案例:
public static void main(String[] args){ int result = test4(); System.out.println(result); } public static int test4(){ //finally 语句块中有 return 语句 int i = 1; try { i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i++; System.out.println("catch block i = " + i); return i; } finally { i++; System.out.println("finally block i = " + i); return i; } } public static int test3(){ //try 语句块中有 return 语句时的整体执行顺序 int i = 1; try{ i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i ++; System.out.println("catch block i = " + i); return i; } finally { i = 10; System.out.println("finally block i = " + i); } } public static int test2(){ int i = 1; try { i++; throw new Exception(); } catch (Exception e) { i--; System.out.println("catch block i = " + i); } finally { i = 10; System.out.println("finally block i = " + i); } return i; } public static int test1(){ int i = 1; try { i++; System.out.println("try block, i = "+i); } catch (Exception e) { i--; System.out.println("catch block i = "+i); } finally { i = 10; System.out.println("finally block i = "+i); } return i; } try block, i = 2 finally block i = 3 3
8.6如何自定义异常类型:
-
继承Exception 或者继承RuntimeException ,定义两个构造器即可。模拟已经存在的子类异常
-
继承Exception的自定义异常,是编译时异常
-
继承RuntimeException的自定义异常,是运行时异常 throw和throws的特点:
-
throw是用在方法里,用于将一个异常对象抛出,自己不处理,抛给调用者,谁调用这个方法,谁就是调用者。
-
throws是用在方法的定义上。表示告诉调用者需要处理的异常类型。
-
throw的如果是编译时异常,必须throws(必须告诉调用者) 4.throw的如果是runtimeException, 就没有必要throws了。
案例:
public static void main(String[] args) { try { Person p = new Person("小明",130); }catch (AgeIllegalException e){ e.printStackTrace(); } } } class Person{ private String name; private int age; public Person(String name, int age) throws AgeIllegalException{ this.name = name; if (age<1 || age>120){ throw new AgeIllegalException("年龄不合理,不应该<1或者大于120"); } this.age = age; } } class AgeIllegalException extends Exception { public AgeIllegalException() { super(); } public AgeIllegalException(String message) { super(message); } } com.se.day01.aException.AgeIllegalException: 年龄不合理,不应该<1或者大于120 at com.se.day01.aException.Person.<init>(Exception05.java:30) at com.se.day01.aException.Exception05.main(Exception05.java:18)
8.7面试题 : 简述 final、finally、finalize 的区别。
性质不同:(1)final为关键字;(2)finalize()为方法;(3)finally为为区块标志,用于try语句中;
作用不同:(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中((2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象 进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行 I/0操作); (3)finally{}用于标识代码块,与try{ }进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行 案例