目录
什么是异常
异常是指程序在执行过程中发生的不正当行为,是程序发生错误的一种。(常见的异常有:算术异常,数组越界异常,空指针异常等)
java中不同类型的异常都有一个对应的类进行描述,内部维护了一个异常体系结构:
● Throwable: 是异常体系的顶层类,其派生出两个重要的子类 , Error 和 Exception● Error: 指的是 Java虚拟机JVM 的内部错误、资源耗尽等无法解决的严重问题 (如:StackOverflflowError 和OutOfMemoryError)● Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。
异常的分类
异常根据发生时期的不同可以分为编译时异常和运行时异常
1.编译时异常
在程序编译期间发生的异常,称为编译时异常,也称为受检查异常 (Checked Exception)public class Person { private String name; private String gender; int age; @Override public Person clone() { return (Person)super.clone();//报错:未处理异常: java.lang.CloneNotSupportedException } }
2.运行时异常
在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常 (Unchecked Exception)import java.util.Date; import java.util.Scanner; public class Test { //ArithmeticException public void test1(){ int a = 1; int b = 0; System.out.println(a / b); } //InputMismatchException public void test2(){ Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); System.out.println(n); } //NumberFormatException public void test3(){ String str = "123"; int num = Integer.parseInt(str); } //ClassCastException public void test4(){ Object obj = new Date(); String str = (String)obj; } //IndexOutOfBoundsException public void test5(){ //ArrayIndexOutOfBoundsException int[] arr = new int[3]; System.out.println(arr[3]); //StringIndexOutOfBoundsException String str = "abc"; System.out.println(str.charAt(3)); } //NullPointerException public void test6(){ int[] arr = null; System.out.println(arr[3]); } }
*注:编译时出现的语法性错误,不能称之为异常
异常的处理
若没有处理异常,异常会交给JVM处理,程序异常终止;若处理了异常,异常则由程序员处理,可以继续执行后续代码。
我们可以通过防御式编程(LBYL或者EAFP)来处理代码中可能出现的异常
1.LBYL: Look Before You Leap.
在操作之前就做充分的检查(通过if-else来处理)
public class Test { public int div(int a, int b) { if(b == 0) { //处理b为0时发生的发生算术异常 return 0; } return a/b; } }
2.EAFP: It's Easier to Ask Forgiveness than Permission.
先操作, 遇到问题再处理(通过try-catch来处理)
public class Test { public static void main(String[] args) { try { System.out.println(10 / 0); //可能出现异常的代码 } catch (ArithmeticException e) { e.printStackTrace(); //定为异常的位置 System.out.println("这里出现了算术异常"); //需要捕获的异常 } } }
当然,通过try-catch也可以捕获多个异常:
public class Test { public static void main(String[] args) { try { System.out.println(10 / 0); } catch (ArithmeticException | NullPointerException | ArrayIndexOutOfBoundsException e) { e.printStackTrace(); System.out.println("这里出现了异常"); } } }
或:
public class Test { public static void main(String[] args) { try { System.out.println(10 / 0); } catch (ArithmeticException e) { System.out.println("这里出现了算术异常"); } catch (NullPointerException e) { System.out.println("这里出现了空指针异常"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("这里出现了数组越界异常"); } } }
try-catch还可以搭配 finally 关键字,finally 中的代码一定会执行的,一般在 finally 中进行一些资源清理的扫尾工作:
public class Test { public int test() { Scanner sc = null; try { sc = new Scanner(System.in); int data = sc.nextInt(); return data; } catch (InputMismatchException e) { e.printStackTrace(); } finally { sc.close(); //一定会执行到 } return 0; } }
*注:
1.try块内抛出异常位置之后的代码将不会被执行
2.catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象
3.如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM 收到后中断程序
4.如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误
异常的抛出
在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。
public class Test { public static void test(int a) { if (a == 10) { throw new ArithmeticException();//抛出一个算术异常 } } }
那要如何处理主动抛出的异常呢?
如果抛出的是编译时异常,则需要在抛出异常的同时进行处理:
public class Test { public static void test2(int a) { try { if (a == 10) { throw new CloneNotSupportedException();//抛出一个编译时异常 } } catch (ArithmeticException e) { System.out.println("这里发生了异常"); } } }
若抛出的是运行时异常,还可以这样子处理:
public class Test { public static void test1(int a) { if (a == 10) { throw new ArithmeticException(); } } public static void main(String[] args) { try { test1(10); } catch (ArithmeticException e) { System.out.println("这里发生了异常"); } } }
*注:
1. throw必须写在方法体内部2. 抛出的对象必须是Exception 或者 Exception 的子类对象3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译5. 异常一旦抛出,其后的代码就不会执行
异常的声明
当我们不想处理该异常时,可以借助throws将异常抛给方法的调用者来处理,即声明当前方法不处理异常,提醒方法的调用者处理异常:
public class Test { public static void test1(int a) throws CloneNotSupportedException { if (a == 10) { throw new CloneNotSupportedException(); } } }
若抛出的编译时异常有对应的异常声明,则也可以将异常处理交给调用者处理:
public class Test { public static void test1(int a) throws CloneNotSupportedException { if (a == 10) { throw new CloneNotSupportedException(); } } public static void main(String[] args) { try { test1(10); } catch (CloneNotSupportedException e) { System.out.println("这里发生了异常"); } } }
若方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开:
public class Test { public static void test1(int a) throws CloneNotSupportedException,ArithmeticException,NullPointerException { if (a == 10) { throw new CloneNotSupportedException(); } if (a == 20) { throw new ArithmeticException(); } if (a == 30) { throw new NullPointerException(); } } }
当调用声明抛出异常的方法时,我们必须对该异常进行处理:
public class Test { public static void test1(int a) throws CloneNotSupportedException { if (a == 10) { throw new CloneNotSupportedException(); } } public static void main(String[] args) { try { test1(10); } catch (CloneNotSupportedException e) { System.out.println("这里发生了异常"); } } }
或者继续使用throws抛出:
public class Test { public static void test1(int a) throws CloneNotSupportedException { if (a == 10) { throw new CloneNotSupportedException(); } } public static void main(String[] args) throws CloneNotSupportedException { test1(10); } }
*注:
1. throws必须跟在方法的参数列表之后2. 声明的异常必须是 Exception 或者 Exception 的子类,如果抛出多个异常类型具有父子关系,直接声明父类即可。
异常的处理流程
1.程序先执行 try 中的代码2.如果 try 中的代码出现异常 , 就会结束 try 中的代码 , 看和 catch 中的异常类型是否匹配 .3.如果找到匹配的异常类型 , 就会执行 catch 中的代码4.如果没有找到匹配的异常类型 , 就会将异常向上传递到上层调用者5.无论是否找到匹配的异常类型 , finally 中的代码都会被执行到 ( 在该方法结束之前执行 )6.如果上层调用者也没有处理的了异常 , 就继续向上传递7.一直到 main 方法也没有合适的代码处理异常 , 就会交给 JVM 来进行处理 , 此时程序就会异常终止
自定义异常类
在java中不仅内置了丰富的异常类,还可以根据实际情况自定义符合实际需求的异常:
class UserNameException extends RuntimeException { public UserNameException (String str) { super(str); } } public class Test { String userName = "a"; public void test1(String userName) { if (this.userName.equals(userName)) { throw new UserNameException("用户名错误"); } } }
* 注:
1.自定义异常通常会继承自 Exception 或者 RuntimeException2.继承自 Exception 的异常默认是编译时异常3.继承自 RuntimeException 的异常默认是运行时异常