在Java中,将程序执行过程中发生的不正常行为称为异常
一、异常的体系结构
异常种类繁多,为了对不同的异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构
从上图可以看到:
1、Throwable:是异常体系的顶层类,其派生出两个重要的子类:Error和Exception
2、Error:指的是Java虚拟机无法解决的严重问题,典型代表:StackOverflowError和OutOfMemoryError
//java.lang.StackOverflowError
public static void method5(){
method5();
}
//java.lang.OutOfMemoryError
public static void method6(){
int[] arr = new int[1024*1024*1024];
}
3、Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行
二、异常的分类
异常可能在编译时出现,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为:
1、编译时异常
在编译期间发生的异常,称为编译时异常,也称为受检查异常
public class Person implements Cloneable{
String name;
int age;
String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
@Override
public Person clone() throws CloneNotSupportedException {
return new Person(name,age,gender);
}
}
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person("jim",18,"男");
//Person p2 = p1.clone(); //编译时异常
}
}
2、运行时异常
在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常
RunTimeException以及其子类对应的异常,都称为运行时异常,以下为经典例子:
1)算数异常
算数异常
public static void method1(){
System.out.println(10/0);
}
2)数组越界异常
数组越界异常
public static void method2(){
int[] nums = {1,2,3};
System.out.println(nums[nums.length]);
}
3)空指针异常
空指针异常
public static void method3(){
int[] nums = null;
System.out.println(nums[0]);
}
4)不匹配
public static void method4(){
Scanner sc = new Scanner(System.in);//输入“hello”
int data = sc.nextInt();//java.util.InputMismatchException
System.out.println(data);
sc.close();
}
三、异常的处理
1、防御式编程:在程序出现问题的时候及时通知程序猿,主要的方式有:
1)LBYL:Look Before You Leap:在操作之前就做充足的检查,即事前防御型(if else)
2)EAFP:遇到问题在处理,即事后认错型(try catch)(推荐)
public class Main1 {
static int[] a = new int[10];
public static int method(int index){
//下标越界
if(index < 0 || index >= a.length){
throw new ArrayIndexOutOfBoundsException("下标越界");//异常抛出
}
return a[index];
}
public static void main(String[] args) {
//运行时异常一般不处理,就是需要暴露出问题然后给出提示
try {
System.out.println(method(100));
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
注:在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws
2、异常的抛出
在编写程序时,如果程序中出现错误,此时需要将错误的信息告知给调用者
在Java中,可以借助throws关键字,抛出一个指定的异常对象,将错误信息告知给调用者
throw new XXXException("异常产生的原因")
public class Main {
static int[] a = new int[10];
public static int method(int index){
//下标越界
if(index < 0 || index >= a.length){
throw new ArrayIndexOutOfBoundsException("下标越界");//异常抛出
}
return a[index];
}
public static void main(String[] args) {
method(100);
}
}
注意:
1、throw必须写在方法体内部
2、抛出的对象必须是Exception或者Exception的子类对象
3、如果抛出的是RunTimeException或者其子类对象,可以不用处理,交给JVM
4、如果抛出的是编译时异常,则用户必须处理,否则无法通过编译
5、异常一旦抛出,其后的代码就不会执行
四、异常的捕获
异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws以及try-catch捕获处理
1、异常声明throws
处在方法声明参数列表之后,当方法抛出编译时异常时,用户不想处理该异常,此时就可以借助throws关键字给方法的调用者来处理,即当前方法不处理异常,提醒方法的调用者处理异常
@Override
public Person clone() throws CloneNotSupportedException {
return new Person(name,age,gender);
}
注意:
1、throws必须跟在方法的参数列表之后
2、声明的异常必须是Exception或者其子类
3、方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系 ,直接声明父类即可
4、调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
2、try-catch捕获并处理
如果真正处理,就需要try-catch
try{
//将可能出现异常的代码放在这里
} catch(要捕获的异常类型 e){
//对异常进行处理
} finally{
//此处代码一定会被执行到
}
//后序代码
//当异常被捕获到时,异常就被处理了,这里的后续代码一定会执行
/如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行
//try中的代码可能会抛出异常,也可能不会
注意:
1、try块内抛出异常位置之后的代码将不会被执行
2、如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理
3、try可能会抛出多个不同的异常对象,则必须用多个catch来捕获,即多种异常,多次捕获
4、可以通过一个catch捕获所有的异常,即多个异常一次捕获(不推荐)