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("除零错误!除数不能为零!\n");
}
System.out.printf("You have excepted the error!\n");
}
}
//输出结果:
除零错误!除数不能为零!
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\n", m);
}catch(Exception e){
}
System.out.printf("m = %d\n", 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("除数不能为零!\n");
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