所有的异常类型都是它的子类,它派生两个子类Error、Exception。
表示仅靠程序本身无法恢复的严重错误(内存溢出动态链接失败、虚拟机错误),应用程序不应该抛出这种错误(一般由虚拟机抛出)
由Java应用程序抛出和处理的非严重错误
除了运行时异常外的其它由Exception继承来的异常类
异常 | 说明 |
---|---|
Exception | 异常层次结构的根类(父类) |
ArithmeticException | 算术错误 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
NullPointerException | 尝试访问Null对象成员 |
ClassNotFoundException | 不能加载所需的类 |
InputMismatchException | 欲的到的数据类型与实际不匹配 |
IllegaArgumentException | 方法接收到非法参数 |
ClassCastException | 对象强制转换出错 |
NumberFormatException | 数字格式化异常 |
异常 | 说明 |
---|---|
ClassCastException | 类型转换出错 |
NullPointerException | 空指针异常 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
ArithmeticException | 算术异常 |
ArrayStoreException | 数组中包含不兼容的值抛出的异常 |
NumberFormatException | 字符串转换为数字抛出的异常 |
IllegaArgumentException | 方法接收到非法参数 |
FileSystemNotFoundException | 文件系统未找到异常 |
SecurityException | 安全性异常 |
StringIndexOutOfBoundsException | 字符串索引超出范围 |
NegativeArraySizeException | 数组长度为负异常 |
RuntimeException类及其子类异常以外的异常,是必须进行处理的异常,如果不处理,程序就不能编译通过
异常 | 说明 |
---|---|
ClassNotFoundException | 未找到相应类异常 |
SQLException | 操作数据库异常类 |
IOException | 输入/输出流异常 |
TimeoutException | 操作超时异常 |
FileNotFoundException | 文件未找到异常 |
凡是可能抛出异常的语句,都可以用try-catch捕获。把可能发生异常的语句放在try { }中,然后使用catch捕获对应的Exception及其子类
try-catch-finally
try:可能出现异常的代码块,出现异常,则跳转到catch(必须)
catch:输出异常信息(可选)
finally:必须执行的代码(可选)
catch块中,调用异常对象的方法输出异常信息
(1)void printStackTrace():输出异常的堆栈信息
(2)String getMessage():返回异常信息描述字符串
try-catch-catch-finally
JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配
(1)使用多重catch时,catch块的排列顺序必须从子类到父类,最后一个一般是Exception
(2)运行时,只执行一个catch块
(3)存在多个catch的时候,catch的顺序非常重要:子类必须写在前面
finally语句保证了有无异常都会执行,它是可选的
(1)finally语句不是必须的,可写可不写
(2)finally总是最后执行
如果没有发生异常,就正常执行try {}语句块,然后执行finally
如果发生了异常,就中断执行try {}语句块,然后跳转执行匹配的catch语句块,最后执行finally
try {
return 1;
} finally {
System.out.println("finally模块被执行");
}
finally语句在return语句执行之后return返回结果之前执行
执行结果:先输出finally模块被执行,然后执行在return 1;
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("finally模块被执行");
}
程序是从try代码块或者catch代码块中返回时,finally中的代码总会执行
而且finally语句在return语句执行之后return返回之前执行的。可以使用编译器的Debug功能查看详细过程
执行结果:try语句报错,执行输出finally模块被执行,catch中的return 2
try {
int a = 8 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.println("finally模块被执行");
return 2;
}
当finally有返回值时,会直接返回。不会再去返回try或者catch中的返回值
public int show() {
int result = 0;
try {
return result;
} finally {
System.out.println("finally模块被执行");
result = 1;
}
}
并不会改变返回的内容
当返回的变量的类型是引用类型时,结果也是一样的
public Object show() {
Object obj = new Object();
try {
return obj;
} finally {
System.out.println("finally模块被执行");
obj = null;
}
}
如果try和catch的return是一个变量时且函数的是从其中一个返回时,后面finally中语句即使有对返回的变量进行赋值的操作时,也不会影响返回的值。
(1)通过try-catch捕获并处理异常
(2)通过throws继续声明异常。如果调用者不打算处理异常,则可以继续通过throws声明异常,让上一级调用者处理异常,main()方法声明的异常将由Java虚拟机来处理
在代码中需要抛出异常时,尽量使用JDK已定义的异常类型。例如,参数检查不合法,应该抛出IllegalArgumentException:
static void process1(int age) {
if (age <= 0) {
throw new IllegalArgumentException();
}
}
class MyException extends Exception { // 创建自定义异常类
public MyException(String ErrorMessagr) { // 父类方法
super(ErrorMessagr);
}
}
// 使用自定义异常
throw new MyException("发生了自定义的异常");
throw | throws | |
---|---|---|
作用不同 | 用于程序中抛出异常 | 用于声明在该方法内可能抛出的异常 |
使用位置不同 | 位于方法体内部,可以作为单独语句使用 | 必须跟在方法参数列表的后面,不能单独使用 |
内容不同 | 抛出一个异常对象,而且只能是一个 | 后面跟异常类,而且可以跟多个异常类 |
自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的
一个常见的做法是自定义一个BaseException作为“根异常”,然后,派生出各种业务类型的异常自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的
BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生,自定义的BaseException应该提供多个构造方法
public class BaseException extends RuntimeException {
public BaseException() {
super();
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(String message) {
super(message);
}
public BaseException(Throwable cause) {
super(cause);
}
}
上述构造方法实际上都是原样照抄RuntimeException。这样,抛出异常的时候,就可以选择合适的构造方法。通过IDE可以根据父类快速生成子类的构造方法
其他业务类型的异常就可以从BaseException派生:
public class UserNotFoundException extends BaseException {}
public class LoginFailedException extends BaseException {}
(1)抛出异常时,尽量复用JDK已定义的异常类型
(2)自定义异常体系时,推荐从RuntimeException派生“根异常”,再派生出业务异常
(3)自定义异常时,应该提供多种构造方法
自定义异常类继承自Exception类;需要检查编译期异常和运行期异常
// 或者继承RuntimeException(运行时异常)
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
public MyException() { // 提供无参数的构造方法
}
// 提供参数构造,把参数传递给Throwable的带String参数的构造方法
public MyException(String message) {
super(message);
}
}
serialVersionUID作用:序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性
写一个测试分数的方法类:这里面是抛出一个自己写的异常类
public class CheckScore {
// 检查分数合法性的方法check() 如果定义的是运行时异常就不用抛异常了
public void check(int score) throws MyException {
if (score > 120 || score < 0) {
// 分数不合法时抛出异常,new一个自定义异常类
throw new MyException("分数不合法,分数应该是0--120之间");
} else {
System.out.println("分数合法,你的分数是" + score);
}
}
}
写一个测试分数,如果有异常,要捕获,不要抛出了
public class Student {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int score = sc.nextInt();
CheckScore check = new CheckScore();
try {
check.check(score);
} catch (MyException e) {
// 用自定义异常类来捕获异常
e.printStackTrace();
}
}
}
自定义异常类继承自RuntimeException类;只需要检查运行期异常
仅仅为了提示,又不想自定义一个Exception,可以用RuntimeException。这个可以抛出异常,并准确定位,缺点是不能处理这个异常,自定义异常的话可以捕获并且处理。
断言(Assertion)一种调试程序的方式。在Java中,使用assert关键字来实现断言
(1)断言是一种调试方式,断言失败会抛出AssertionError,只能在开发和测试阶段启用断言
(2)对可恢复的错误不能使用断言,而应该抛出异常
(3)断言很少被使用,更好的方法是编写单元测试
语句assert x >= 0;即为断言,断言条件x >= 0预期为true。如果计算结果为false,则断言失败,抛出AssertionError
使用assert语句时,还可以添加一个可选的断言消息
assert x >= 0 : “x must >= 0”;
这样,断言失败的时候,AssertionError会带上消息x must >= 0,更加便于调试
Java断言的特点:断言失败时会抛出AssertionError,导致程序结束退出。因此,断言不能用于可恢复的程序错误,只应该用于开发和测试阶段
对于可恢复的程序错误,不应该使用断言
void sort(int[] arr) {
assert arr != null;
}
应该抛出异常并在上层捕获
void sort(int[] arr) {
if (arr == null) {
throw new IllegalArgumentException("array cannot be null");
}
}
一个简单的断言
public static void main(String[] args) {
int x = -1;
assert x > 0;
System.out.println(x);
}
JVM默认关闭断言指令,即遇到assert语句就自动忽略,不执行;也就是上方断言不会抛出异常AssertionError
选择地对特定地类启用断言,命令行参数是:-ea:com.itranswarp.sample.Main,表示只对com.itranswarp.sample.Main这个类启用断言。
或者对特定地包启用断言,命令行参数是:-ea:com.itranswarp.sample…(注意结尾有3个.),表示对com.itranswarp.sample这个包启动断言。实际开发中,很少使用断言。更好的方法是编写单元测试
要执行assert语句,必须给Java虚拟机传递-enableassertions(可简写为-ea)参数启用断言
java -ea Main.java