java异常机制详细说明
基本概念
- 异常就是"不正常"的含义,在Java语言中主要指程序执行中发生的不正常情况
- java.lang.Throwable类是Java语言中错误(Error)和异常(Exception)的超类
- 其中Error类主要用于描述Java虚拟机无法解决的严重错误,通常无法编码解决,如:JVM挂掉了 等
- 其中Exception类主要用于描述因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决, 如:0作为除数等。
异常的分类
-
java.lang.Exception类是所有异常的超类,主要分为以下两种:
- RuntimeException - 运行时异常,也叫作非检测性异常
- IOException- 输入输出异常和其它异常 - 其它异常,也叫作检测性异常,所谓检测性异常就是指在编译阶段都能 被编译器检测出来的异常
-
其中RuntimeException类的主要子类
- ArithmeticException类 - 算术异常
- ArrayIndexOutOfBoundsException类 - 数组下标越界异常
- NullPointerException - 空指针异常
- ClassCastException - 类型转换异常
- NumberFormatException - 数字格式异常
-
注意:
当程序执行过程中发生异常但又没有手动处理时,则由Java虚拟机采用默认方式处理异常,而默认处理方式就是:打印异常的名称、异常发生的原因、异常发生的位置以及终止程序。
异常的机构图
异常的避免
-
在以后的开发中尽量使用if条件判断来避免异常的发生
// 算术异常 int ia = 10; int ib = 0; if (0 != ib){ // 判断除数不为0 执行下面代码 System.out.println(ia / ib); } // 数组越界异常 int[] arr = new int[10]; int pos = 10; if (pos >= 0 && pos < arr.length){ // 判断数组索引值是否合法 System.out.println(arr[pos]); }
-
但是过多的if条件判断会导致程序的代码加长、臃肿,可读性差
异常的捕获
-
语法格式
try { 编写可能发生异常的代码; }catch(异常类型 引用变量名) { 编写针对该类异常的处理代码; }吧 ... finally { 编写无论是否发生异常都要执行的代码; }
-
注意事项
-
.当需要编写多个catch分支时,切记小类型应该放在大类型的前面;子类在父类的前面
-
懒人的写法:
catch(Exception e) {} 不建议 因为代码的可读性差
-
finally通常用于进行善后处理,如:关闭已经打开的文件等。
-
-
执行流程
try { a; b; - 可能发生异常的语句 c; }catch(Exception ex) { d; }finally { e; } // 当没有发生异常时的执行流程:a b c e; // 当发生异常时的执行流程:a b d e;
-
关于finally类的笔试考点
private static int test() { try { String str = null; // 定义一个空字符串 // 将字符串信息转大写操作 由于字符串是空 所以会发生空指针异常 异常发生后 return 0 不会执行 System.out.println(str.toLowerCase()); return 0; }catch (NullPointerException e){ // 进行异常处理 打印异常信息 打印完之后将结束方法的执行 但是finally未执行 所以执行finally之后 返回 e.printStackTrace(); return 1; }finally { // 提前结束方法 return 2; } }
注意事项 如果finally 未结束方法的执行 先执行finally内部的代码块 然后执行return 1
异常的抛出
-
基本概念
在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者, 这种方法就叫异常的抛出。当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代 码后续的代码就不再执行。
-
语法格式
访问权限 返回值类型 方法名称(形参列表) throws 异常类型1,异常类型2,...{ 方法体; }
-
代码演示
-
方法throws异常
private static void test() throws IOException { FileInputStream fi = new FileInputStream("e:/a.txt"); // 此处会发生 IOException fi.close(); }
-
调用发法依旧 throws 且发生异常位置不会进行
public static void main(String[] args) throws IOException { test(); // 此处会发生 IOException System.out.println("------"); // 因为交给虚拟机处理 所以中断处理 所以此处及之后的代码不会执行 }
-
-
注意事项
main方法 不建议把异常throws 因为交给虚拟机处理 所以中断处理
-
方法重写的原则
-
要求方法名相同、参数列表相同以及返回值类型相同,从jdk1.5开始支持返回子类类型;
-
要求方法的访问权限不能变小,可以相同或者变大;
public > default > protected > private
-
要求方法不能抛出更大的异常;
-
-
注意:
子类重写的方法不能抛出更大的异常、不能抛出平级不一样的异常,但可以抛出一样的异常、更小 的异常以及不抛出异常
-
经验分享
- 若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理
- 若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出 的方法处理到最后一层进行捕获方式处理
自定义异常
-
基本概念
当需要在程序中表达年龄不合理的情况时,而Java官方又没有提供这种针对性的异常,此时就需要 程序员自定义异常加以描述
-
实现流程
-
自定义xxxException异常类继承Exception类或者其子类。
-
提供两个版本的构造方法,一个是无参构造方法,另外一个是字符串作为参数的构造方法。
-
代码实现
public class NameException extends Exception{ static final long serialVersionUID = -3387516993124229948L; // 序列化版本号 有序列化操作有关 public NameException() { } public NameException(String message) { super(message); } }
-
-
异常的产生
throw new 异常类型(实参);
代码演示
public class Student { private String name; public Student() { } public Student(String name) throws NameException { setName(name); } public void setName(String name) throws NameException { if (name.matches(".{2,8}")){ // 判断用字输入 2 - 8个字符 this.name = name; }else throw new NameException("请输入2-8位的汉字或英语字符"); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } }
创建之后的调用
public class StudentTest { public static void main(String[] args) { Student s = null; try { s = new Student("张三三三三三三三三"); } catch (NameException e) { e.printStackTrace(); } System.out.println(s); // null } }