异常是什么

在理想状态下,用户输入的数据的格式永远都是正确的,选择打开的文件也一定存在,并且永远不会出现Bug。——《Java核心技术》

为了避免用户在程序运行的过程中出现错误等,Java使用了一种称为异常处理机制的错误捕获机制来进行处理异常的情况。

以下均为针对异常处理机制进行的描述与补充。

概述

Java中异常的层次结构(主要)如下图。

由上图可知,Throwable 是Java语言中所有错误和异常的类。只有作为此类(或其一个子类)的实例的对象由Java虚拟机抛出,或者可以由Java throw语句抛出。 类似地,只有这个类或其子类可以是catch子句中的参数类型。

补充:
unchecked:未检查异常。
checked:已检查异常。

什么时候会抛出异常?

如果遇到了无法处理的情况,那么java的方法可以抛出一个异常。那么什么情况下可能抛出异常呢?

1、用户输入错误
2、设备错误
3、物理限制
4、程序出现错误。 ----- 例如,a[-1]=0会抛出ArrayIndexOutOfBoundsException的uncheck异常。
5、调用一个抛出已检查的方法。----- 例如,。public FileInputStream(String name) throws FileNotFoundException
6、程序运行中发现的错误
7、Java虚拟机和运行时库出现的内部错误

抛出&捕获异常?

此章节主要是关键字throws、throw、和try-catch(finally)的作用及区别。

【补充】JVM对异常的处理方法 :打印异常的跟踪栈信息,并终止程序运行。

抛出异常

(1)throws通常在方法首部的声明后抛出异常。

class MyAnimation{  
    public Image loadImage(String s) throws IOException{
        ...
    }
}
  • throws抛出的是可能发生的异常。(并不一定会发生这些异常)
  • 当该方法被调用的时候必须捕获,或者也可以再次抛出异常,最终由Java虚拟机处理。
  • 用来声明一个方法可能产生的所有异常(用,分隔), 不做任何处理而是将异常往上传,谁调用我我就抛给谁。

    throw关键字通常用在方法体中,并且抛出一个异常对象。

    String readData(Scanner in)throws EOFException{  
        while(...){    
            if(!in.hasNext())//遇到EOFException异常    
            if(n<len){      
                throw new EOFException();    
            }...  
        }
    }
*  throw则是抛出了异常,执行throw则一定抛出了某种异常。
* throw语句由方法体内的语句处理。
* 只能抛出一个异常对象。
* 有两种方式捕获。要么自己捕获异常 try-catch 代码块,要么是抛出一个异常(throws 异常)。
## 小总结
------由上述throws、throw比较可知。
(1)throw抛出一个异常对象时需要由函数的上层调用处理这个异常,此时,可以通过try-catch(finally)代码块,也可以通过throws进行抛出。(一定要处理)
(2)throws抛出一个可能的异常时可以不对此异常进行处理。

编译时异常需要手动进行处理;而运行时异常jvm会打印异常的跟踪栈信息,并终止程序运行。
所以一般运行时异常不需要手动捕获,但编译时异常需要手动处理。

# 捕获异常(try-catch-finally)
* 编写代码需要用到异常捕获的时候。
* java中即使不会发生异常的代码也是可以用try-catch进行捕获。
【注】在定义一个方法的时候,可以使用throws关键字声明,使用throws声明的方法表示此方法不处理异常,而是交给方法的调用处进行处理。
【注】使用了throw,抛出了异常,必然要进行捕获和处理,就是说,必须要进行try-catch处理或者throws抛出。

使用1:

try
{
//需要被检测的异常代码
}catch(Exception e){

//异常处理,即处理异常的代码(打印异常信息并处理)

}finally{
  //一定会被执行的代码(通常可以进行资源的清除工作)
}

使用2:

try
{
//需要被检测的异常代码
}catch(Exception e){

//异常处理,即处理异常的代码(打印异常信息并处理)

}

使用3:

try
{
//需要被检测的异常代码
}finally{
  //一定会被执行的代码(通常可以进行资源的清除工作)
}

try
{
//需要被检测的异常代码
}catch(Exception e1){

//异常处理,即处理异常的代码(打印异常信息并处理)

}catch(IOException e2){

//异常处理,即处理异常的代码(打印异常信息并处理)

}//可以通过catch处理多个异常。

---------举个栗子-------

public class TestException {

//测试0
boolean testEx() throws Exception{
    boolean res = true;
    try{
        res = testEx1();//执行
    }catch (Exception e){
        System.out.println("测试0,捕获异常");
        res = false;
        throw e;
    }finally{
        System.out.println("测试0, finally 最终返回的值 =" + res);
        return res;
    }
}
//测试1
boolean testEx1() throws Exception{
    boolean res= true;
    try{
        res = testEx2();
        if (!res){
            return false;
        }
        return res;
    }catch (Exception e){
        System.out.println("测试1, catch捕获");
        res = false;
        throw e;
    }finally{
        System.out.println("测试1, finally最终返回值 =" + res);
        return res;
    }
}
//测试2
boolean testEx2() throws Exception{
    boolean res = true;
    try{
        int b = 2;
        int c;
        for (int i = 2; i >= 0; i--){
            c = b / i;
            System.out.println("c="+c+"\ti=" + i);
        }
        return true;
    }catch (Exception e){
        System.out.println("测试2, catch捕获");
        res = false;
        throw e;
    }finally{
        System.out.println("测试2, finally最终值 =" + res);
        return res;
    }
}

}//借鉴Angel_Kitty的例子

上述结果
![](https://upload-images.jianshu.io/upload_images/23140115-bac5d8684ba514e0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



# 如何抛出异常?(举个栗子叭)
此节主要描述对抛出异常以及捕获异常的整个过程的实例。包括自定义异常类等。

## 自定义接口抛出异常

public interface ComparePerson {

//    比较两个Person,是否相等
void compareInfo(Person person1,Person person2) throws MyException;

}

## 自定义异常类

//自定义的异常类(需要继承Exception才是异常类)
class MyException extends Exception{

public MyException(String s){//输出异常信息
    System.out.println(s);
}

}

#3 定义一个类去实现此接口

public class Person implements ComparePerson {

//    姓名
private String name;
//    年龄
private int age;

public Person(String name, int age) {
    this.name = name;
    this.age = age;
}

// 两个Person不相等,则抛出自定义异常,打印2个人的信息,以及不相等的信息

@Override
public void compareInfo(Person person1, Person person2) {
    try{
        System.out.println(person1.compare(person2));
    }catch (MyException e){

// e.printStackTrace();

    }
}

// 比较两个对象是否相同,不相同时抛出异常并输出异常信息

public String compare(Object obj) throws MyException {//compareInfo方法在接口中抛出了异常,此时调用了compare方法也需要抛出想应的异常。

// 判断2个对象的地址是否相同

    if (this == obj) {
        return "2个人属性完全相同";
    }

// 判断obj对象是否是Person的派生类(子类)

    if (obj instanceof Person) {
        System.out.println("********");
        Person obj1 = (Person) obj;//强制转换一定成功
        if (obj1.getName().equals(this.name) && obj1.getAge() == this.age) {
            throw new MyException("2个人属性相同!");//需要调用此方法的上层调用者throws 此异常或者使用try-catch语句捕获异常。
        } else if (obj1.getAge() == this.age) {
        //打印对象的详细信息
            System.out.println(this.toString());
            System.out.println(obj1.toString());
            throw new MyException("2个人姓名不同~");
        } else if (obj1.getName().equals(this.name)) {
        //打印对象的详细信息
            System.out.println(this.toString());
            System.out.println(obj1.toString());
            throw new MyException("2个人年龄不同~");
        }
    }
    return "2个人不同a";

}

//打印各参数
public String toString() {
    return "name is:" + this.name + "\tage is:" + this.age;
}
//set get方法
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

}

## 测试主类加载测试

class TestMain {

public static void main(String[] args) throws MyException {

// 2个Person对象

    Person person1 = new Person("掰掰", 18);
    Person person2 = new Person("憨憨", 18);
    person1.compareInfo(person1, person2);

}

}

上述结果
![](https://upload-images.jianshu.io/upload_images/23140115-60d492d03de05419.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

class TestMain {

public static void main(String[] args) throws MyException {

// 2个Person对象

    Person person1 = new Person("掰掰", 18);
    Person person2 = new Person("掰掰", 18);
    person1.compareInfo(person1, person2);

}

}


![](https://upload-images.jianshu.io/upload_images/23140115-a7cc76ccaf6af416.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

class TestMain {

public static void main(String[] args) throws MyException {

// 2个Person对象

    Person person1 = new Person("掰掰", 18);
    Person person2 = new Person("掰掰", 20);
    person1.compareInfo(person1, person2);
}

}

![](https://upload-images.jianshu.io/upload_images/23140115-cfb95fa64748a4e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


【注】上述Person类中,注释了 e.printStackTrace()方法,如果取消注释,会打印相应抛出的异常信息。如下图:
![](https://upload-images.jianshu.io/upload_images/23140115-838a5f5d2e8ad65f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


上述抛出的异常就是自定义的异常信息。

可以拿着上述例子直接运行一下哦,还可以再此例子进行更深化的学习!
# 最后
在文章的最后作者为大家整理了很多资料!包括java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书等等!

前程有光
936 声望618 粉丝