一、概述
- Java中异常继承的根类是
Throwable
,Throwable 是根类,但不是异常类。 Error
是严重的错误。Exception
才是异常类,它是是编译或执行过程中可能会出现的问题,应该尽量提前避免。
异常的分类:
- 编译时异常:继承自 Exception 的异常或其子类,编译阶段就会报错。
- 运行时异常:继承自
RuntimeException
的异常或其子类,编译阶段不会报错,它是在运行是阶段才可能出现,建议提前处理。
异常一旦出现,程序就会终止。所以需要避免异常,处理异常。
二、运行时异常
- 运行时异常继承自
RuntimeException
的异常或其子类。 - 编译阶段不会报错,它是在运行是阶段才可能出现。运行时异常在编译阶段无论是否处理,代码编译都能通过。
常见的运行时异常:
ArrayIndexOutOfBoundsException
:数组索引越界异常。
int[] arr = {1,2,3};
System.out.println(arr[3); // 异常
NullPonterException
:空指针异常。
Student stu = null;
System.out.println(stu); // 输出 null
System.out.println(stu.getName); // 异常
ClassCastException
:类型转换异常。
Object obj = "Java";
Integer integer = (Integer) obj; // 异常
NoSuchElementException
:迭代器遍历没有此元素异常。
Collection<String> books= new ArraysList<>();
books.add("JavaSE");
books.add("JavaWeb");
Iterator<String> iterator = books.iterator();
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next()); // 异常
ArithmeticException
:数学操作异常。
int num = 10 / 0; // 异常
NumberFormatException
:数据转换异常。
String num = "123a";
Integer integer = Integer.valueOf(num); // 异常
三、编译时异常
- 编译时异常:继承自 Exception 的异常或其子类,编译阶段就会报错,必须处理。
四、 默认处理机制
- 在运行阶段,某个方法中的某行代码出现,产生异常对象,抛给该方法。
- 该方法再抛出给该方法的调用者,最终抛给main方法。
- main方法最终抛给 JVM,
- JVM 收到异常,输出信息,终止程序。之后的代码不会被执行。
public static void main(String[] args) {
System.out.println("程序开始");
test(10,0);
System.out.println("程序结束");
}
public static void test(int a, int b){
System.out.println("计算" + a + "/" + b);
int c = a /b;
System.out.println("结果为:" + c);
}
输出:
程序开始
计算10/0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at test.MyException.test(MyException.java:13)
at test.MyException.main(MyException.java:7)
默认的异常处理机制并不好,一旦出现异常,程序立即终止死亡。
五、 编译时异常处理
5.1 抛出异常 throws
- 产生的异常依次向上抛,最终由mian方法抛给 JVM。与默认异常处理机制相同。
- 即使会产生多个异常,但是只会抛出一个异常。因为产生了一个异常之后,程序就会终止。
public static void main(String[] args) throws FileNotFoundException {
System.out.println("程序开始");
test();
System.out.println("程序结束");
}
public static void test() throws FileNotFoundException {
InputStream is = new FileInputStream("./logo.jpg");
}
程序开始
Exception in thread "main" java.io.FileNotFoundException: .\logo.jpg (系统找不到指定的文件。)
5.2 try…catch…
- try 块中发生异常,catch 块捕获并打印异常信息。
- try 块中发生异常处之后的代码不会被执行,但是 try 外的代码照常执行。
- 可以处理异常,并且出现异常后程序不会终止。但这种方法不是最好的,上层调用者不知道执行情况。
public static void main(String[] args) {
System.out.println("程序开始");
test();
System.out.println("程序结束");
}
public static void test() {
System.out.println("开始寻找文件");
try {
InputStream is = new FileInputStream("./logo.jpg");
System.out.println("出现异常后,这里不会被执行");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("文件寻找完毕");
}
输出:
程序开始
开始寻找文件
文件寻找完毕
程序结束
java.io.FileNotFoundException: .\logo.jpg (系统找不到指定的文件。)
5.3 抛出给最外层(规范)
- 出现的异常依次抛出给最完成调用者。由最外层调用者集中捕获处理。
- 抛出异常的方法中,产生异常处之后的代码不会被执行。最外层 try 块中 产生异常处之后的代码也不会被执行。
- 这种方式最外层调用者知道底层的执行情况,并且出现异常后程序不会终止。
public static void main(String[] args) {
System.out.println("程序开始");
try {
test();
System.out.println("出现异常,这里不会被执行");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序结束");
}
public static void test() throws Exception {
System.out.println("开始寻找文件");
InputStream is = new FileInputStream("./logo.jpg");
System.out.println("文件寻找完毕");
}
输出:
程序开始
开始寻找文件
程序结束
java.io.FileNotFoundException: .\logo.jpg (系统找不到指定的文件。)
六、运行时异常处理
运行时异常会自动向上抛出,只需要在最外层捕获即可。
public static void main(String[] args) {
System.out.println("程序开始");
try {
test(10,0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序结束");
}
public static void test(int a, int b){
System.out.println("计算" + a + "/" + b);
int c = a /b;
System.out.println("结果为:" + c);
}
七、finally
- finally 关键字用在捕获异常格式中,放在最后面。无论是否出现异常,这里的代码都会被执行。
try…catch…finally 存在个数:
- try:1 次。
- catch:0~n 次(有 finally,可以不需要 catch ) 。
- finally:0~1 次
7.1 关闭资源
- 可在代码执行完毕后,进行资源的释放操作。
- 资源就是实现了
Closeable
接口的,都自带 close()方法。
public static void main(String[] args) {
test();
}
public static void test() {
InputStream is = null;
try {
int a = 10 / 0;
is = new FileInputStream("./logo.jpg");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (is != null) is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.2 延迟 return
- 若 try 和 catch 中有 return,则会延迟到 finally 执行完毕后执行。
- 若 finally 中有return,则会覆盖前面所有的 return。
- 特别注意
System.exit(0);
,在任何地点都可以直接关闭虚拟机。
(1)无异常,try 中的 return 延迟
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
int a = 10 / 2;
return a;
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
System.out.println("finally 执行了");
}
}
finally 执行了
5
(2)有异常,catch 中的 return 延迟
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
int a = 10 / 0;
return a;
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
System.out.println("finally 执行了");
}
}
java.lang.ArithmeticException: / by zero
finally 执行了
-1
(3)finally 中 return,无论是否有异常,都只会返回这里的 return
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
int a = 10 / 0;
return a;
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
System.out.println("finally 执行了");
return 999;
}
}
java.lang.ArithmeticException: / by zero
finally 执行了
999
八、自定义异常
throws
:用在方法上,用于抛出方法中的异常。throw
:用在出现异常的地方,用于创建异常对象,并立即在此处抛出。
8.1 自定义编译时异常
- 自定义一个异常类,继承
Exception
,并重写构造器。
/**
* 自定义编译时异常:年龄非法异常
*/
public class AgeIllegalException extends Exception{
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
public AgeIllegalException(String message, Throwable cause) {
super(message, cause);
}
public AgeIllegalException(Throwable cause) {
super(cause);
}
public AgeIllegalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- 出现异常的地方,抛出自定义异常对象。
public static void main(String[] args) {
try {
checkAge(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void checkAge(int age) throws AgeIllegalException {
if (age < 0 || age > 150){
throw new AgeIllegalException("age is illegal");
}else {
System.out.println("年龄为:" + age);
}
}
8.2 自定义运行时异常
- 自定义一个异常类,继承
RuntimeException
,并重写构造器。
/**
* 自定义运行时异常:年龄非法异常
*/
public class AgeIllegalRuntimeException extends RuntimeException{
public AgeIllegalRuntimeException() {
}
public AgeIllegalRuntimeException(String message) {
super(message);
}
public AgeIllegalRuntimeException(String message, Throwable cause) {
super(message, cause);
}
public AgeIllegalRuntimeException(Throwable cause) {
super(cause);
}
public AgeIllegalRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- 出现异常的地方,抛出自定义异常对象。
public static void main(String[] args) {
checkAge(10);
}
public static void checkAge(int age) throws AgeIllegalRuntimeException {
if (age < 0 || age > 150){
throw new AgeIllegalRuntimeException("age is illegal");
}else {
System.out.println("年龄为:" + age);
}
}
九、异常的作用
- 可以处理代码问题,防止程序出现异常后的死亡。
- 提高了程序的健壮性和安全性。
public static void main(String[] args) {
while (true){
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入年龄:");
int age = scanner.nextInt();
System.out.println("今年" + age + "岁");
break;
}catch (Exception e){
System.err.println("输入错误");
}
}
}