目录
1.异常概述、体系
什么是异常?
- 异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
- 比如:数组索引越界、空指针异常、 日期格式化异常,等…
为什么要学习异常?
- 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
- 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。
异常体系
Error: 系统级别问题、JVM退出等,代码无法控制。
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
- RuntimeException及其子类:运行时异常,编译阶段不会报错。 (空指针异常,数组索引越界异常)
- 除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)。
编译时异常和运行时异常
简单来说:
- 编译时异常就是在编译的时候出现的异常
- 运行时异常就是在运行时出现的异常。
2.常见运行时异常
直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
常见的运行时异常包括:
-
NullPointerException(空指针异常):当试图访问一个空对象的方法或属性时,抛出该异常。
-
ArrayIndexOutOfBoundsException(数组下标越界异常):当尝试访问数组中不存在的索引位置时,抛出该异常。
-
ClassCastException(类型转换异常):当试图将一个对象强制转换为不相关的类时,抛出该异常。
-
IllegalArgumentException(非法参数异常):当提供的参数不符合方法的预期时,抛出该异常。
-
ArithmeticException(算术异常):当出现除以零等算术错误时,抛出该异常。
-
IndexOutOfBoundsException(索引越界异常):当访问某个实现了索引接口的对象时,访问了超出其范围的索引位置,抛出该异常。
-
SecurityException(安全异常):当试图执行非法操作时,如访问受保护的资源或从安全管理器中请求不被允许的权限,抛出该异常。
-
ConcurrentModificationException(并发修改异常):当在迭代器遍历集合的同时,集合被修改时,抛出该异常。
-
OutOfMemoryError(内存不足异常):当没有足够的内存可用时,抛出该异常。
运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误, 自己的水平有问题!
3.常见编译时异常
不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
编译时异常示例
编译时异常的作用是什么:
- 是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒不要出错!
- 编译时异常是可遇不可求。遇到了就遇到了呗。
编译时异常的特点
- 编译时异常:继承自Exception的异常或者其子类
- 编译阶段报错,必须处理,否则代码不通过。
Java 常见的编译时异常包括:
-
FileNotFoundException:表示指定的文件不存在。
-
IOException:表示输入/输出操作失败或被中断。
-
ClassNotFoundException:表示无法找到类。
-
NoSuchMethodException:表示调用了不存在的方法。
-
NoSuchFieldException:表示访问了不存在的字段。
-
IllegalAccessException:表示试图访问私有字段或方法。
-
IllegalArgumentException:表示传递给方法的参数不合法。
-
NullPointerException:表示试图访问空对象引用。
-
ArrayIndexOutOfBoundsException:表示访问数组越界。
-
InterruptedException:表示线程被中断。
4.异常的默认处理流程
- 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
- 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
- 直接从当前执行的异常点干掉当前程序。
- 后续代码没有机会执行了,因为程序已经死亡。
public class test {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
chu(10,0);
System.out.println("程序结束。。。。");
}
private static void chu(int a, int b) {
System.out.println(a);
System.out.println(b);
int c=a/b;
System.out.println("结果是"+c);
}
}
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
5.编译时异常的处理机制
编译时异常是编译阶段就出错的,所以必须处理,否则代码根本无法通过
1.抛出异常
编译时异常可以通过在方法声明中使用“throws”关键字抛出给调用方,在调用方处进行处理。如果调用方未能捕获该异常,则必须将异常向上抛出,直到被捕获或程序终止。
例如:
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
String line = br.readLine();
System.out.println(line);
}
在以上代码中,读取文件时可能会发生IOException异常,因此我们将其抛出给main方法的调用方进行处理。
2.捕获异常并处理
- 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
- 这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。
例如:
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
String line = br.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("文件读取失败!");
e.printStackTrace();
}
}
在以上代码中,我们使用try-catch语句捕获了IOException异常,并在catch块中输出了错误信息。
总之,编译时异常的处理方式取决于异常的类型和代码实现的需求。通常,我们应该遵循“抛出异常而不是吞噬异常”的原则,以确保代码的健壮性和可维护性。
6.运行时异常的处理机制
Java运行时异常是指在程序运行期间发生的异常,它们通常与程序员编写的代码有关,如除以零、数组越界、空指针引用等。与受检异常不同,运行时异常可以不用显式地在方法签名或调用处进行声明或处理。
在Java中,运行时异常的处理机制是通过try-catch语句来捕获并处理异常。当运行时异常发生时,程序会跳转到与异常匹配的catch块中,并执行其中的代码。如果没有匹配的catch块,程序会终止并打印出异常信息。
以下是一个示例代码,演示了如何捕获并处理运行时异常:
public class Example {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
int num = arr[3]; // 数组越界,抛出ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常:" + e.getMessage());
}
}
}
在上述示例中,当程序尝试访问索引为3的数组元素时,会发生数组越界异常,抛出ArrayIndexOutOfBoundsException。由于在try块中捕获了该异常,程序会跳转到catch块中,并输出异常信息。
总而言之,Java提供了try-catch语句来捕获和处理运行时异常,程序员可以根据自己的需要选择是否处理这些异常。但应该注意,过多地捕获和处理异常会影响程序的性能和可读性,应该谨慎处理。
7.finally
"finally"是Java中的一种关键字。它是一个可选的代码块,在try-catch语句块中使用,无论是否发生异常,都会执行。"finally"用于确保有些代码需要在异常情况下被执行,如关闭文件或释放资源等。例如:
try {
// 一些代码
} catch (Exception e) {
// 处理异常
} finally {
// 无论是否发生异常,都会执行
// 关闭文件或释放资源等操作
}
在使用"finally"时需要注意以下几点:
- "finally"所包含的代码块一定会被执行,除非程序在"finally"块之前被强制退出或死机。
- 如果在try或catch块中使用了return语句,那么"finally"块仍然会被执行,在"finally"块执行完后才会真正返回。
- "finally"块也可以与try或catch块独立使用。
8.自定义异常
自定义异常的必要?
Java无法为这个世界上全部的问题提供异常类。 如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。
自定义异常的好处:
- 可以使用异常的机制管理业务问题,如提醒程序员注意。
- 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。
自定义异常可以分为两类:
1、自定义编译时异常
- 定义一个异常类继承Exception.
- 重写构造器。
- 在出现异常的地方用throw new 自定义对象抛出
- 作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
2、自定义运行时异常
- 定义一个异常类继承RuntimeException.
- 重写构造器。
- 在出现异常的地方用throw new 自定义对象抛出!
- 作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
下面是一个简单的自定义异常的 Java 例子:
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
这个例子定义了一个名为 MyException
的自定义异常,它继承了 Java 内置的 Exception
类。在这个自定义异常类中,我们只是重写了父类的构造函数,为其添加了一个带有字符串参数的构造函数。
这个自定义异常类可以用于抛出我们自己定义的异常信息,例如:
public void myMethod(int x) throws MyException {
if (x < 0) {
throw new MyException("Number cannot be negative");
}
// do something with x
}
在这个例子中,myMethod
方法会抛出 MyException
异常,当参数 x
是负数的时候。在异常对象创建时,我们使用带有一个字符串参数的构造函数来传递异常信息,这个信息将在异常被捕获时显示出来。