异常
1. 异常类型
五个关键字:
try
、catch
、finally
、throw
、throws
2. 抛出和捕获异常
快捷键:Ctrl + Alt + T
2.1 try 和 catch
import java.util.Random;
class HandleError {
public static void main(String[] args) {
int a=0, b=0, c=0;
Random r = new Random();
for (int i = 0; i < 32000; i++) {
try { // try 监控区域
// 生成随机有效整数(不带参数则没有范围)
b = r.nextInt();
c = r.nextInt();
a = 12345 / (b/c);
} catch (ArithmeticException e) {// catch(想要捕获的异常类型) 捕获异常
// 显示异常的描述信息
System.out.println("Exception: " + e);
a = 0; // 将 a 置为0,继续
}
System.out.println("a: " + a);
}
}
}
Output:
a: 12345
Exception: java.lang.ArithmeticException: / by zero
a: 0
Exception: java.lang.ArithmeticException: / by zero
a: 0
Exception: java.lang.ArithmeticException: / by zero
a: 0
a: -12345
a: -726
a: 12345
...
2.2 多条 catch 子句
有些情况下,单块代码可能会引发多个异常
package com.feiye.Demo02;
class MultipleCatches {
public static void main(String[] args) {
try {
int a = args.length;
System.out.println("a = " + a);
int b = 42 / a;
int[] c = {1};
c[42] = 99;
} catch (ArithmeticException e) {
System.out.println("Divide by 0: " + e);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index oob: " + e);
}
System.out.println("After try/catch blocks...");
}
}
Output1:
a = 0
Divide by 0: java.lang.ArithmeticException: / by zero
After try/catch blocks...
Output2:
注:
- 异常子类必须位于其所有超类之前,因为使用了某个超类的catch语句会捕获这个超类及其所有子类的异常**(先小后大)**
public class SuperSubCatch {
public static void main(String[] args) {
try {
int a = 0;
int b = 42 / a;
} catch (Exception e) {
System.out.println("Generic Exception catch.");
}
// 以下语句报错!!
catch (ArithmeticException e) {
System.out.println("这条语句不会到达,ArithmeticException已经在Exception中被捕获");
}
}
}
更换两条catch即可:
package com.feiye.Demo03;
public class SuperSubCatch {
public static void main(String[] args) {
try {
int a = 0;
int b = 42 / a;
}
catch (ArithmeticException e) {
System.out.println("ArithmeticException: " + e);
}
catch (Exception e) {
System.out.println("Exception: " + e);
}
}
}
2.3 嵌套的 try 语句
class NestTry {
public static void main(String[] args) {
try {
int a = args.length;
int b = 42 / a;
System.out.println("a = " + a);
// 嵌套 try
try {
if (a==1)
a=a/(a-a);
if (a==2) {
int[] c = {1};
c[42] = 99;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index out-of-bounds: " + e);
}
} catch (ArithmeticException e) {
System.out.println("Divide by 0: " + e);
}
}
}
Output:
2.4 主动抛出异常 throw throws
- throw
class ThrowDemo {
static void demoproc() {
try {
throw new NullPointerException("demo");
} catch (NullPointerException e) {
System.out.println("捕获内部 demoproc.");
throw e; // 重新抛出异常
}
}
public static void main(String[] args) {
try {
demoproc();
} catch (NullPointerException e) {
System.out.println("重新捕获: " + e);
}
}
}
Output:
捕获内部 demoproc.
重新捕获: java.lang.NullPointerException: demo
- throw 语句之后的执行流会立即停止,所有后续语句都不会执行
- throws
class ThrowsDemo {
static void throwOne() throws IllegalAccessException {
System.out.println("throwOne内部");
throw new IllegalAccessException("demo");
}
public static void main(String[] args) {
try {
throwOne();
} catch (IllegalAccessException e) {
System.out.println("捕获: " + e);
}
}
}
Output:
throwOne内部
捕获: java.lang.IllegalAccessException: demo
public class Test {
public static void main(String[] args) {
try {
new Test().test(1, 0);
} catch (ArithmeticException e) {
System.out.println("捕获: " + e);
}
}
// 假设在方法中处理不了这个异常,就在方法中抛出异常
public void test(int a, int b) throws ArithmeticException{
if (b == 0) { // 主动抛出异常 throw throws, 一般在方法中使用
throw new ArithmeticException("除数不能为0");
}
System.out.println(a / b);
}
}
Output:
捕获: java.lang.ArithmeticException: 除数不能为0
2.5 finally
class FinallyDemo {
static void procA() {
try {
System.out.println("procA内部");
throw new RuntimeException("demo");
} finally {
System.out.println("procA's finally");
}
}
static void procB() {
try {
System.out.println("procB内部");
return;
} finally {
System.out.println("procB's finally");
}
}
static void procC() {
try {
System.out.println("procC内部");
} finally {
System.out.println("procC's finally");
}
}
public static void main(String[] args) {
try {
procA();
} catch (Exception e) {
System.out.println("捕获异常");
}
procB();
procC();
}
}
Output:
procA内部
procA's finally
捕获异常
procB内部
procB's finally
procC内部
procC's finally
如果finally代码块和某个try代码块相关联,那么finally代码块会在这个try代码块结束后执行
3. 自定义异常
MyException.java
package oop.Demo13;
// 自定义的异常类
public class MyException extends Exception{
// 传递数字 > 10
private int detail;
public MyException(int a) {
this.detail = a;
}
// toString: 异常的打印信息
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
Test.java
package oop.Demo13;
public class Test {
// 可能会存在异常的方法
static void test(int a) throws MyException{
System.out.println("传递的参数为: " + a);
if (a > 10) {
throw new MyException(a);
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
// 增加一些处理异常的代码
System.out.println("MyException -> " + e);
}
}
}
Output:
传递的参数为: 11
MyException -> MyException{detail=11}
Throwable 定义的方法
方法 | 描述 |
---|---|
final void addSuppressed(Throwable exc) | 将exc添加到与调用异常关联的suppressed异常列表中,主要用于新的带资源的try语句 |
Throwable fillInStackTrace() | 返回一个包含完整堆栈踪迹的Throwable对象,可重新抛出该对象 |
Throwable getCause() | 返回引起当前异常的异常。如果不存在引起当前异常的异常,就返回null |
String getLocalizedMessage() | 返回异常的本地化描述信息 |
String getMessage() | 返回异常的描述信息 |
StackTraceElement[] getStackTrace() | 返回一个包含堆栈踪迹的数组,数组元素的类型为StackTraceElement,每次一个元素。堆栈顶部的方法是抛出异常之前调用的最后一个方法,可以在数组的第一个元素中找到该方法。通过StackTraceElement类,程序可以访问与踪迹中每个元素相关的信息,如方法名 |
final Throwable[] getSuppressed() | 获取与调用异常关联的suppressed异常,并返回一个包含结果的数组。suppressed异常主要由带资源的try语句生成 |
Throwable initCause(Throwable causeExc) | 将causeExc与调用异常关联到一起,作为调用异常的原因。返回对异常的引用 |
void printStackTrace() | 显示堆栈踪迹 |
void printStackTrace(PrintStream stream) | 将堆栈踪迹发送到指定的流中 |
void setStackTrace(StackTraceElement[] elements) | 将elements中传递的元素设置为堆栈踪迹。该方法用于特殊的应用程序,在常规情况下不使用 |
String toString() | 返回包含异常描述信息的String对象,当通过println() 输出Throwable 对象时,会调用该方法 |