1、异常概述
Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch子句中的参数类型。
两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。
1.1异常分类:
异常包括Exception和Error,它们的特点如下:
Exception:
- 1.可以是受检(checked) 或非受检的(unchecked)。
- 2.表示一个由程序代码导致的错误。
- 3.应该在应用程序级被处理。
- 4.分为编译时异常(必须处理)与运行时异常
Error:
- 1.总是不可控制的(unchecked)。
- 2.经常用来用于表示系统错误或底层资源的错误。
- 3.若可能的话,应该在系统级被捕捉。
受检异常和非受检异常:
非受检异常(unchecked exception)指的是java.lang.RuntimeException和java.lang.Error类及其子类,除此之外的所有其他的异常类都称为受检异常(checked exception)。两者的唯一区别在于,使用受检异常时,其合法性在编译时刻就会被编译器检查。
1.2 继承体系
下图粗线条的展示了异常的体系:
2、JVM的默认处理方案
1.把异常的名称,错误原因及异常出现的位置等信息输出到控制台
2.程序停止执行
3、编译时异常和运行时异常
Java中的异常被分为两大类:编译时异常和运行时异常。在Exception继承体系下,所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
1.编译时异常
Java程序必须显示处理,否则程序就会发生错误,无法通过编译扫描二维码关注公众号,回复: 153183 查看本文章2.运行时异常
不必显示处理,也可以和编译时异常一样处理
4、异常(Exception)处理
4.1 处理方案
1、try…catch…finally (finally不是必须的)
注意细节
1.try块出现异常处之后的语句不再执行,一旦发现有匹配的异常类型,就转到相应的catch块去执行相关逻辑,然后try…catch就结束了
2.catch块可以捕获多种异常,但范围必须从小到大进行捕获,异常能明确的尽量明确,尽量不要用大范围的来处理。
3.非特殊情况下,finally一定会执行,因此可以在该方法做一些资源回收的工作
示例1:
try {
try块,正常执行的代码
} catch (Exception e) {
catch块,进行一场捕获,可以有多个catch块
} finally {
finally块,通常一定会执行,一般用于释放资源
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2、 throws(使用在方法声明上)
示例2:
public void getBitmapFromUrl() throws IOException{
}
- 1
- 2
- 3
- 4
- 5
如果将异常抛出,那么调用者必须处理或抛出此异常
4.2 finally
4.21.finally的特点
- 被finally控制的语句体一定会执行
- 特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))。
注意:如果在执行到finally之前jvm退出了,就不能保证finally块执行了。
4.22.finally的作用
- 用于释放资源,在IO流操作和数据库操作中会见到
4.23.final、finally和finalize的区别
final:最终的意思,可以修饰类,成员变量,成员方法
- 1.修饰类,类不能被继承
- 2.修饰变量,变量成为常量
- 3.修饰方法,方法不能被重写
finalize:是Object类的一个方法,用于垃圾回收
finally:是异常处理的一部分,用于释放资源。
一般来说,代码肯定会执行。特殊情况:在执行到finally之前jvm退出了
- 1
- 2
思考:如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后?
会执行。并且在前。
5、Throwable中的方法
5.1.方法摘要
Throwable getCause()
返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
String getMessage()
返回此 throwable 的详细消息字符串。
String getLocalizedMessage()
创建此 throwable 的本地化描述。 子类可以重写此方法,以便生成特定于语言环境的消息。
对于不重写此方法的子类,默认实现返回与 getMessage() 相同的结果。
StackTraceElement[] getStackTrace()
提供编程访问由 printStackTrace() 输出的堆栈跟踪信息。
void printStackTrace()
将此 throwable 及其追踪输出至标准错误流。
void printStackTrace(PrintStream s)
将此 throwable 及其追踪输出到指定的输出流。
void printStackTrace(PrintWriter s)
将此 throwable 及其追踪输出到指定的 PrintWriter。
String toString()
返回此 throwable 的简短描述。结果是以下字符串的串联:
1.此对象的类的 name
2.": "((冒号和一个空格)
3、调用此对象 getLocalizedMessage方法(默认与getMessage()返回相同的结果)的结果
如果 getLocalizedMessage 返回 null,则只返回类名称
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
5.2. 使用示例
public class ExceptionDemo {
public static void main(String[] args) {
DateFormat formatter=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
String date = formatter.format(new Date("2016-1-1"));
System.out.println(date);
} catch (Exception e) {
e.printStackTrace();
//System.out.println(e.toString());
//System.out.println(e.getMessage());
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
6、区分throws和throw
6.1 throws
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在方法上标识。特点如下:
- 1.用在方法声明的方法名后面,跟的是异常类名
- 2.可以声明抛出多个异常,后面跟多个异常类名,用逗号隔开
- 3.表示抛出异常,由该方法的调用者来处理
- 4.throws表示出现异常的一种可能性,并不一定会发生这些异常
6.2 throw
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。特点如下:
- 1.用在方法体内,表示抛出异常对象,且每次只能抛出一个异常对象
- 2.能执行throw则一定抛出了某种异常,throw语句之后的代码不再执行
6.3 处理
- 对于编译期异常,调用者必须处理。
- 对于运行期异常,调用者可以不用处理。
6.4 异常注意事项
- 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。
- 如果父类的方法抛出了多个异常,子类重写父类的方法时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常。
- 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
注意:此处所说的异常均为受检异常
7、自定义异常类
自定义异常类:要继承自一个异常类。 继承自Exception的异常为编译期异常,继承自RuntimeException的异常为运行期异常。
如下示例:
class CustomException extends Exception{
private static final long serialVersionUID = -2008123398676394901L;
public CustomException(String msg) {
super(msg);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
8、JDK7对异常处理的新特性
8.1 处理多个异常
在jdk7中,可以在一个异常等级同时处理多个异常,形式如下:
try{
}catch(异常名1 | 异常名2 | ... 变量 ) {
...
}
- 1
- 2
- 3
- 4
- 5
- 6
示例
try {
// to do
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
// deal with exception
}
- 1
- 2
- 3
- 4
- 5
注意事项:
1 .处理方式是针对同类型的问题,给出同一个处理。
2.多个异常间必须是平级关系。
8.2 addSuppressed
对于jdk之前的版本,如果在try块出了异常,同时当finally方法的逻辑也出了异常,那么会导致前面部分异常信息的丢失。即当finally方法异常被抛出的时候,会导致之前的异常因为该异常而被抑制住,从而无法正常抛出,造成了部分异常信息丢失。jdk7提供了addSuppressed方法把这些被抑制的异常信息记录下来。被抑制的异常会出现在抛出的异常的堆栈信息中,也可以通过getSuppressed方法来获取这些异常。使用addSuppressed方法可以让我们得到完整的异常堆栈信息,以方便开发调试。
如下示例:
/**
* @param input
* @param def
* @throws Exception
*/
public static void useAddSuppressed(int input, String def) throws Exception {
java.lang.Exception exception = null;
int result = 0;
try {
result = 2 / input;
} catch (ArithmeticException e) {
exception = e;
throw e;
} finally {
if (result == 0) {
try {
result = Integer.parseInt(def);
} catch (NumberFormatException e) {
if (exception != null) {
exception.addSuppressed(e); // 将信息添加到异常的堆栈信息
} else {
exception = e;
}
}
if (exception != null)
throw exception;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
当调用方法如下:
try {
useAddSuppressed(0, "hello");
} catch (Exception e) {
e.printStackTrace();
}
- 1
- 2
- 3
- 4
- 5
输出异常信息如下,可见输出了完整的异常信息:
java.lang.ArithmeticException: / by zero
at com.yu.kotlin.oop.ArrayTest.useAddSuppressed(ArrayTest.java:50)
at com.yu.kotlin.oop.ArrayTest.main(ArrayTest.java:31)
Suppressed: java.lang.NumberFormatException: For input string: "hello"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at com.yu.kotlin.oop.ArrayTest.useAddSuppressed(ArrayTest.java:57)
... 1 more
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
8.3 try-with-resources语句
Java 7中引入了使用try语句进行资源管理的新用法,简化try…catch…finally的结构,同时保证使用的资源可以得到释放。如下代码示例:
public static String readFile(String path) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line); // 添加内容
sb.append(String.format("%n")); // 换行
}
return sb.toString();
}
// 不需要finally块来进行手动回收
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
资源的申请是在try子句中进行的,而资源的释放则是自动完成的。
使用使用try-with-resources语句异常抛出情况:
- 异常可能发生在try语句,抛出try块相关异常
- 可能发生在释放资源时,抛出资源释放相关异常
- 上述两种同时发生,抛出try块相关异常,并将释放资源时出现的异常会作为被抑制的异常添加进去,即通过Throwable.addSuppressed方法来实现。得到所有异常信息。
注意:
能够被try语句所管理的资源Java类需要满足一个条件,它必须实现java.lang.AutoCloseable接口,否则会出现编译错误。当需要释放资源的时候,其接口实现的close方法会被自动调用。
try-with-resources也可以对多个资源进行管理。当对多个资源进行管理的时候,在释放每个资源时都可能会产生异常。所有这些异常都会被加到资源初始化异常或try块中抛出的异常的被抑制异常列表中。
/**
* 复制文件
* @param src 源
* @param dest 目的
* @throws IOException
*/
public static void copyFile(String src, String dest) throws IOException {
try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(dest))) {
byte[] buffer = new byte[1024 * 4];
int length = -1;
while ((length = input.read(buffer)) != -1) { // read
output.write(buffer, 0, length); // write
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在try-with-resource语句中也可以使用catch和finally语句,在catch子句中可以捕获try块和释放资源时可能发生的各种异常。