文章目录
Throwable类
什么是Throwable?
Throwable 类是 Java 语言中所有错误或异常的超类,是对所有异常进行整合的一个普通类。它的作用就是能够提取保存在堆栈中的错误信息。
成员方法:
public String getMessage():返回此 throwable 的详细消息字符串
public String toString():获取异常类名和异常信息。
public void printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置。
public void printStackTrace(PrintStream s):通常用该方法将异常内容保存在日志文件中,以便查阅。
异常的分类
-
异常(Exception)
-
编译时异常: 凡是Exception或者是Exception的子类都成为编译时异常,这种是可以解决的,一般编译不通过
-
运行时异常: 凡是RuntimeException或者是RuntimeException的子类都成为运行时异常,这种也是可以解决的,一般都是代码不够严谨或者逻辑错误
-
错误 (Error)
我们无法解决,这不是我们所关心
为什么需要将Throwable设计成普通类?
大家可能和我一样会产生疑问,为什么不将Throwable类直接设置为接口或者抽象类呢?
如果是Throwable一个接口,那么异常处理会不会更容易?
对于我个人的理解,因为Throwable里面存储的是错误描述的对象体现,这些错误描述的对象体现指的是字符串,只不过把这些字符串表示为一个对象,那么既然是对象,里面是没有实现任何功能的方法的,我查看了一下源码,发现Throwable类中的子类的解决异常处理方法里都是构造方法,一个是无参构造,另外一个就是带字符串参数(对象)的带参构造,既然有了构造方法,那就可以直接从父类中直接提取所需要的异常处理信息从而来构造一个方法,这样可以省略子类很多代码,注意普通类继承的话是直接继承,不能够改变,如果改变的话就会影响到父类,根据这个特点也可以很清楚的说明Throwable为什么是普通类,因为它压根就不需要子类重写方法,只需要提取信息便可。如果将Throwable定义为接口类或者是抽象类的话,还要创建子类将Throwable实例化并且要继承父类Throwable的所有方法,如果是接口,如果需要使用异常处理方法的时候,还要将所需要的异常处理信息填进去,改写接口,这样只会导致代码量增多,会让异常处理更加麻烦。
问题引入:为什么需要处理异常?
因为在程序出现问题(系统判定的异常)后,通过异常处理,能够让程序继续执行
JVM处理异常的方式
1.打印错误信息
a.异常的类名
b.异常信息
c.异常的位置
d.错误的行号
2.将程序停止
JVM处理异常的方式不能够满足我们的需求,因为我们想要对异常处理之后,程序还能够继续运行下去,所以需要自己来处理异常
我们处理异常的方式
1.try…catch…finally
2.throws
try…catch…finally异常处理方法
try…catch…finally异常处理的格式:
try {
可能出现问题的代码;
} catch(异常类名 异常对象) {
异常处理的代码
} finally {
释放未关闭的资源
}
try…catch…finally执行流程:
1.程序执行到错误代码的地方,系统会抛出一个异常对象
ArithmeticException ae = new ArithmeticException("/by zero");
throw ae;
2.程序转入catch块进行逐个匹配
3.匹配成功,程序执行 catch代码
4.匹配失败,程序还给JVM处理
注意事项
-
try块代码尽量越少越好
-
一旦try块代码中出现异常,无论try块后面有多少行代码,都不会被执行
-
catch块中异常参数匹配同样满足多态 Exception e = new ArithmeticException("/by zero");
-
try块只能够有一个,catch可以有多个,try可以和finally组合,try可以和catch组合,try可以和catch和finally组合
-
一般会把Exception作为catch的参数类型放在异常处理格式的最后
-
Execption作为父类异常参数只能够出现在异常的最后面,先子类后父类
-
finally修饰的代码块一定会被执行,除非在执行到finally之前程序异常退出或者调用了系统退出的方法。
-
在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,在从中取出返回结果,因此,即使finally中对变量x进行了改变,但是不会影响返回结果。它应该使用栈保存返回值,针对return的相关知识点的参考代码如下:
public class ExceptionDemo01 { public static void main(String[] args) { System.out.println(test()); //输出结果为3 2 } public static int test() { int x = 1; try { x++; // 2 return x++; // return 2; } finally { ++x; // 3 System.out.println(x); } } }
public class ThrowableDemo02 { public static void main(String[] args) { foo(0); System.out.println(output); // 134 foo(1); System.out.println(output); // 13423 } public static String output = ""; // 13423 public static void foo(int i) { try { if (i == 1){ throw new Exception(); } output += "1"; } catch (Exception e) { output += "2"; return; //如果该行注释掉的打印结果134234,说明在运行finally之后直接返回主方法,不会实现后面的函数 } finally { output += "3"; } output += "4"; }
}
此外,return语句还可以进行运算,从而影响后面的代码,代码如下:
public class ThrowableDemo03 {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
int x = 1;
try {
x++;
return x++; //2
//throw new Exception();
} catch (Exception e) {
return x;
} finally {
System.out.println(x); //3,此处为3,说明return参与运算
++x;
System.out.println(x); //4
//return x++; //如果打开这行代码,会发现会覆盖掉上面的return的结果
}
}
}
总结:return可以参与运算并且影响后面的代码运算,可以调出方法体而不执行后面的语句
异常处理的标准格式:
1.能够显示声明的异常尽量显示声明,提高程序的可读性
2.在最后都必须加上 Exception 作为父类异常,防止程序异常停止,提高程序的安全性
Throws异常处理方法
在开发中,有的时候我们没有权限处理该异常,我们不知道该如何处理异常,或者不想处理异常,这种情况下我们可以将异常抛出,抛出给调用者处理
throws处理异常的格式:
[访问权限修饰符] 返回值类型 方法名(参数列表) [throws 异常类名]{
方法体;
[return 返回值];
}
注意事项
-
抛出异常的处理方法千万不能抛出给JVM处理[主方法]
-
如果一个方法抛出的是一个编译时异常,那么调用者必须处理
-
如果一个方法抛出的是一个运行时异常,可以处理也可以不处理,建议处理,提高程序的安全性
-
子类重写的方法声明的异常不能够被扩大
-
throws表示一种异常发生的可能性,可以声明多个异常类.
Throw异常处理方法
格式:
throw 异常对象;
注意事项:
可以发现其实throw和throws的作用都是将异常抛出给调用者或者虚拟机来处理,但是两者有个根本区别就是throw的是异常对象,而throws的是异常类,具体的区别如下所示:
throw 和 throws 区别:
-
throw抛出的是异常对象,throws声明的是异常类
-
throw只能够抛出一个对象,throws可以声明多个异常类
-
throw表示异常已经发生,throws是一种异常的可能性
-
throw在方法体内出现,throws在方法的声明上
自定义异常
问题引入:为什么要自定义异常?
异常的本质就是Java当中对于错误信息描述的一种对象体现,既然是错误信息对象,错误信息有多少?
系统提供了常用的错误异常对象,但是不能够表示现实生活中所有的错误信息
比如说 分数必须在0~100分之间 身份证异常
自定义异常
1.运行时异常 继承 RuntimeException
编译时异常 继承 Exception
2.书写两个构造方法
无参构造方法
带 message 参数的构造方法
参考代码如下:
public class ExceptionDemo02 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Teacher t = new Teacher();
System.out.print("请输入分数: ");
double score = input.nextDouble();
try {
System.out.println(t.checkScore(score) ? "分数合格" : "分数不合格");
} catch (ScoreException e) {
e.printStackTrace();
} finally {
if (input != null) {
input.close();
}
}
System.out.println("批改结束");
}
}
class Teacher {
public boolean checkScore(double score) throws ScoreException {
// 方式一:
/*if (score > 100 || score < 0) {
return false;
} else {
return true;
}*/
// 方式二:
if (score > 100 || score < 0) {
throw new ScoreException("分数必须在0~100分之间!!!");
}
return true;
}
}
class ScoreException extends Exception {
private static final long serialVersionUID = -3114508965246281767L;
public ScoreException() {}
public ScoreException(String message) {
super(message);
}
}
查找错误根源的顺序
从Throwable类提取到的错误信息中,从下往上依次看,先看你能够看懂的,再看别人写的或者是源码的,为什么不从第一句直接查找错误的根源呢?因为我们查看别人的代码的时候。当发现了异常,我们并不知道这个异常是怎么产生的,由谁调用,甚至我们有时候还看不懂根源代码,所以我们应该从外部出发,逐层慢慢的了解代码的调用关系,然后就会很清楚的知道异常的来源了。