🐵本篇文章将对异常相关知识进行讲解
一、异常的结构
在程序执行的过程中出现的一些问题叫做异常,异常其实是一个一个类,每一种异常都代表一个类
1.1 几种常见的异常
System.out.println(10/0); //算数异常
//Exception in thread "main" java.lang.ArithmeticException: / by zero
int[] arr = new int[]{1,2,3,4};
System.out.println(arr[10]); //数组越界异常
//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
int[] arr = null;
System.out.println(arr[10]); //空指针异常
//Exception in thread "main" java.lang.NullPointerException
1.2 异常的结构
图中的Exception就是我们说的异常,它继承自Throwable类,同时又派生出IOException和RunTimeException类,它们分别是编译时异常(受查异常)和运行时异常(非受查异常)
二、异常的处理
关于异常的处理有5个关键字:throw,throws,try,catch,finally;下面会逐个讲解
2.1 异常的抛出:throw
int[] arr = new int[]{1,2,3,4};
System.out.println(arr[10]); //数组越界异常
在下面的代码中会抛出一个数组越界异常,如下:
这时编译器自己抛出的,在一些特定的环境下,我们也可以自己主动抛异常,来让程序终止,这时就可以用到throw关键字,用法如下:
throw new ...Exception("数组越界异常");
将上面的代码改成下面的:
int pos = 6;
int[] arr = new int[]{1,2,3,4};
if (pos > arr.length) {
throw new ArithmeticException("数组越界异常");
}
System.out.println("after");
运行结果如下:
可以看到最后一句打印after并没有执行
throw相关注意事项:
1.throw必须写到方法内部
2.throw只能抛出Exception的对象或Exception子类的对象
3.异常一经抛出,其后的代码将不再执行
2.2 异常的声明:throws
throws在方法的参数列表之后,用法如下:
public static void func() throws ArithmeticException {
System.out.println(10/0);
}
其作用是告诉该方法的调用者此方法可能有这个异常,调用者必须处理该异常或继续将该异常告诉它的调用者,如果没有进行上述操作,则:1. 若抛出的编译时异常,则编译报错,2. 若抛出的是运行时异常,则该异常会交给JVM处理,程序会直接终止
public static void func1() throws Exception{
}
public static void main(String[] args) throws Exception{
func1();
}
上述代码中func1方法声明一个Exception,Exception类派生出IOException和RuntimeException两个类,main方法会将这里当成一个编译时异常处理,那么要想不编译报错,应该在main方法也声明Exception
throws相关注意事项:
1. throws关键字必须在方法的参数列表之后
2. 只能声明Exception或Exception的子类
3. 如果要声明多个异常,则让异常之间用逗号隔开即可,若多个异常具有父子关系,则只声明父类异常即可
2.3 异常的捕获:try-catch
try-catch通常用来处理异常用的,使用方法如下图:
下面举个实例:
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println(arr[100]);
System.out.println("after"); //由于上面那行代码抛出了异常,所以这行代码不会执行
} catch (ArrayIndexOutOfBoundsException e) { //异常被这个catch捕获
System.out.println("这是个数组下标越界异常");
e.printStackTrace(); //打印红字
} catch (NullPointerException e) {
System.out.println("这是个空指针异常");
e.printStackTrace();
}
System.out.println("after try catch");
}
打印结果:
2.4 finally
finally一般放在try-catch结构的最后,
finally中的代码一定会被执行,所以通常在其中进行一些资源清理的扫尾工作
public static int getData() {
Scanner scanner = new Scanner(System.in);
try {
int data = scanner.nextInt();
return data; //data被返回后,finally中的代码仍然会执行
} catch (InputMismatchException e) {
e.printStackTrace();
System.out.println("输入异常错误");
} finally {
System.out.println("执行finally中的代码");
scanner.close();
}
System.out.println("try-catch-finally之后的代码"); //这里的代码不会被执行
return 0;
}
三、自定义异常
前面说过异常是一个类,自定义一个异常类必须继承Exception或RuntimeException,继承自Exception的异常默认为编译时异常,继承自RuntimeException的异常默认为运行时异常
//自定义用户名错误异常
public class UserNameException extends RuntimeException{
public UserNameException() {
super();
}
public UserNameException(String s) {
super(s);
}
}
//自定义密码错误异常
public class PassWordException extends RuntimeException {
public PassWordException() {
super();
}
public PassWordException(String s) {
super(s);
}
}
class Test {
private String admin = "admin";
private String passWord = "123456";
public void login(String admin, String passWord) {
if (!this.admin.equals(admin)) {
throw new UserNameException("用户名错误!");
} else if (!this.passWord.equals(passWord)) {
throw new PassWordException("密码错误!");
}
}
public static void main(String[] args) {
//int a = getData();
Test test = new Test();
test.login("admin1", "123456");
}
}
下面是空指针异常的源码,我们上面自定义异常的内容是仿照此来写的