2021年2月10日21点59分 JAVA自学课程笔记5: 异常(Exception) 当Java程序运行出现问题时,系统会自动检测到该错误,并立即生成一个与该错误对应的异常对象。 异常(Exception)的分类: Throwable(可抛出的): Error(系统错误,程序员无法处理这些异常) Exception(程序员可以捕获并处理的异常) RuntimeException(其子类所有的异常处理与否按需): (其子类异常可处理可不处理) 重写方法抛出异常的范围不能大于被重写方法抛出的异常的范围。(即子类抛出异常范围不能比父类大。) 通过try-catch语句去捕获更正错误: class A{ public int divide(int a, int b){ int ans = a / b; return ans; } } public class Test1{ public static void main(String[] args){ A aa = new A(); try{ aa.divide(6, 0); }catch(ArithmeticException e){ //e用来接收18行抛出的异常对象(算术异常) System.out.printf("除零错误!除数不能为零! "); } System.out.printf("You have excepted the error! "); } } //输出结果: 除零错误!除数不能为零! You have excepted the error! try语句只用于尝试不确定的语句,正确则运行,错误则被catch语句捕获并执行。若在try语句外出现错误,则仍会报错(相当于无视try后的源代码存在错误)。 public class Test2{ public static void main(String[] args){ int m; try{ m = 2; System.out.printf("m = %d ", m); }catch(Exception e){ } System.out.printf("m = %d ", m); //若该行存在,则显示m未初始化;若该行不存在,则显示41行的m = 2。 } } 通过e.printStackTrace();语句可以将编译错误信息显示出来。 ArrayIndexOutOfBoundsException(数组下标越界异常)。 throw和throws: 若出现异常(假设这里由throw抛出异常),则可用throws将异常抛出给上层引用者。 import java.io.*; class A{ public void f() throws IOException{ throw new IOException(); //throw抛出异常 } } public class Test3{ public static void main(String[] args){ //若在主函数后添加"throws IOException"则不会出现68行情况。 而会“直接报错”。因为异常从主函数抛给了虚拟机。 A aa = new A(); //aa.f(); 若使用对象a的f()函数,则会直接报错说明“抛出的异常未被处理”。 } } try-catch语句中存在短路,若try有多条语句,则直接结束try语句跳转到catch。 finally语句无论try语句中是否抛出异常,最终一定会执行。 自定义异常: class NameException extends Exception{ public NameException(String name){ super(name); } } class A{ public int divide(int a, int b) throws NameException{ int m = 0; if(b == 0) throw new NameException("除数不能为零! "); else m = a / b; return m; } } public class Test4{ public static void main(String[] args){ A aa = new A(); try{ aa.divide(6, 0); }catch(Exception e){ e.printStackTrace(); } } } //运行结果: NameException: 除数不能为零! at A.divide(test1.java:11) at test1.main(test1.java:22) 建议在调用f()方法时对可能抛出的A异常进行捕捉(void f() throws A{}),其只是未雨绸缪的作用,且就算出现异常也不一定会对A异常进行捕捉(若异常为RuntimeException子类异常则不进行处理)。 所有的catch语句只能有一个被执行。所以要遵循先catch子类异常再catch父类异常的规则的话,把catch子类语句写在catch父类语句之前。两条catch语句之间不能有任何别的代码。 常见异常类型和说明: Exception 异常层次结构的父类 ArithmeticException 算术错误情形,如以零作除数 ArrayIndexOutOfBoundsException 数组下标越界 NullPointerException 尝试访问null对象成员 ClassNotFoundException 不能加载所需的类 IllegalArgumentException 方法接收到非法参数 ClassCastException 对象强制类型转换出错 NumberFormatException 数字格式转换异常,如把"abc"转换成数字 throw和throws的使用: public void dothing(int a,int b) throws Exception1,Exception3 { try{ //...... }catch(Exception1 e){ throw e; }catch(Exception2 e){ System.out.println("自己打印提示,不抛出"); } if(a!=b) throw new Exception3("自定义异常"); } //代码块中try语句可能会产生3个异常,(Exception1,Exception2,Exception3)。 //如果产生Exception1异常,则捕获之后再抛出,由该方法的调用者去处理。 //如果产生Exception2异常,则该方法自己处理了(即打印出字符串:自己打印提示,不抛出)。所以该方法就不会再向外抛出Exception2异常了,void dothing() throws Exception1,Exception3 里面的Exception2也就不用写了(当然你写了也不会报错的),throws 就是声明可能抛出的错误,而Exception2 并未做出抛出操作。 //而Exception3异常是该方法的某段逻辑出错,程序员自己做了处理,在该段逻辑错误的情况下抛出异常Exception3,则该方法的调用者也要处理此异常。 class A extends Exception{} class B extends Exception{} class C extends Exception{} class M{ void f() throws A,B{} } class N extends M{ void f() throws A,B{} } class Test{ public void k(M mm) throws A,B{ mm.f(); } } class Test5{ public static void main(String[] args) throws A,B{ M m = new M(); N n = new N(); System.out.println("this"); Test aa = new Test(); aa.k(m); } } //运行结果: this