深入了解Java异常

1,390 阅读7分钟

一、什么是Java异常?

1. Java异常

异常是指程序运行过程中由于外部问题导致的程序异常事件,产生的异常会中断程序的运行。在Java中,异常本身就是一个对象,产生异常就是产生一个异常对象。异常是一个事件,它发生在程序运行期间,中断了正在执行的程序的正常指令流。

2.Java中异常产生的三个原因

  • Java 内部错误发生异常,Java 虚拟机产生的异常。
  • 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,即运行时异常,在编译期是感知不到的,一般需要在某些类中集中处理这些异常。
  • 通过 throw 语句手动生成的异常,这种异常称为受检异常,一般用来告知该方法的调用者一些必要的信息。

3.Java异常体系

在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 ExceptionError

(1)Exception:表示程序可以处理的异常,可以捕获且可能恢复。

  • RuntimeException(运行时异常):Error以及RuntimeException以及子类都是非受检异常,这些异常一般是由程序逻辑错误引起的。java编译器在编译时,不会提示和发现这样的异常,出现这类异常时,也会编译通过,对于这些异常,我们应该去修正代码,而不是通过异常去处理。
  • CheckedException(受检异常):一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会要求你把这段可能出现异常的程序使用try{}catch{}去捕获或者使用throws在方法后面声明抛出它,否则编译不通过。

(2)Error :表示程序应用程序本身无法克服和恢复的一种严重问题,程序只能瘫痪,例如内存溢出和线程死锁等问题。

7.png

二、Java异常处理机制

在 Java 应用程序中,异常处理机制为:抛出异常捕捉异常。 而异常处理机制主要依赖于try、catch、finally、throw、throws五个关键字

  1. try:它里面放置可能引发异常的代码。
  2. catch:后面对应异常类型和一个代码块,用于表明该catch块用于处理这种类型的代码块,可以有多个catch块。
  3. finally:主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件),异常机制总是保证finally块总是被执行。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者   throw等终止方法的语句,则就不会跳回执行,直接停止。
  4. throw:用于抛出一个实际的异常,可以单独作为语句使用,抛出一个具体的异常对象。
  5. throws:用在方法签名中,用于声明该方法可能抛出的异常。

(一)、捕获异常

一般通过try-catch语句或者try-catch-finally语句来实现。

public class TryCatchDemo {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try {
            String str = "";
            System.out.println(str.length());
            System.out.println(str.charAt(9));
            System.out.println("一切正常");
        } catch (NullPointerException e) {
            System.out.println("发生了空指针异常");
        } catch (StringIndexOutOfBoundsException e) {
            System.out.println("字符串索引超出边界异常");
        }
        System.out.println("程序结束");
    }
}


public class TryCatchFinallyDemo {
    public static void main(String[] args) {
        System.out.println("程序开始");
        String str = "";
        try {
            System.out.println(str.length());
        } catch (Exception e) {
            System.out.println("出错了");
        } finally {
            System.out.println("finally中的代码块执行了");
        }
        System.out.println("程序结束");
    
    }
}

(二)、抛出异常

抛出异常有三种形式:throwsthrow系统自动抛出异常

public static void main(String[] args) {
        String str = "hello offer";
         int index = 10;
         if (index >= str.length())
            //1:使用 throw 在方法内抛出异常
         throw new StringIndexOutOfBoundsException();
      }else {
          str.substring(0,index);
   }
}
//2:使用 throws 在方法上抛出异常
int div(int a,int b) throws Exception{
        return a/b;
}

三、自定义异常

自定义异常,通常就是定义了一个继承自Exception类的子类,那么这个类就是一个自定义异常类。 使用编写自定义异常类,可分为以下几个步骤:

(1)创建自定义异常类。
(2)在方法中通过throw关键字抛出异常对象
(3)如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
(4)在出现异常方法的调用者中捕获并处理异常。

四、相关面试点

1.throws 和 throw 的区别

  • 位置不同。throws是用在方法函数头,后面跟的是异常类,而throw是用在方法体中的,后面跟的是异常对象

  • 功能不同。throws是用来声明异常的,让调用者只知道该方法中可能出现的问题,使它的调用者知道使用该方法时需要捕获这个异常,而自己不具体处理。throw是用来抛出具体的问题对象,执行到 throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。

  • 性质不同。throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw 则是抛出了异常,执行 throw 则一定抛出了某种异常对象。

2.finally是无条件执行的吗?

不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句,finally块总会被执行

  • 注意事项: 如果在try块或catch块中使用 System.exit(1); 来退出虚拟机,则finally块将失去执行的机会。但是我们在实际的开发中,重来都不会这样做,所以尽管存在这种导致finally块无法执行的可能,也只是一种可能而已。

3.在finally中return会发生什么?

在通常情况下,不要在finally块中使用return、throw等导致方法终止的语句,一旦在finally块中使用了return、throw语句,将会导致try块、catch块中的return、throw语句失效

  • 注意事项: 当Java程序执行try块、catch块时遇到了return或throw语句,这两个语句都会导致该方法立即结束,但是系统执行这两个语句并不会结束该方法,而是去寻找该异常处理流程中是否包含finally块,如果没有finally块,程序立即执行return或throw语句,方法终止;如果有finally块,系统立即开始执行finally块。只有当finally块执行完成后,系统才会再次跳回来执行try块、catch块里的return或throw语句;如果finally块里也使用了return或throw等导致方法终止的语句,finally块已经终止了方法,系统将不会跳回去执行try块、### final、finally、finalize 有什么区别? catch块里的任何代码。

4.final、finally、finalize 有什么区别?

  • final:是一个修饰符,final修饰的类不能被继承,被final修饰的方法不能被重写,被final修饰的变量初始化之后不能被修改(不一定)。

  • finally:一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码放在方法finally代码块中,前面try{}、catch{}代码块中没有return语句的前提下,该代码块都会执行,一般用来释放资源。

  • finalize:是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

5. 常见的 RuntimeException 有哪些?

  • ClassCastException(类转换异常)
  • IndexOutOfBoundsException(数组越界)
  • NullPointerException(空指针)
  • ArrayStoreException(数据存储异常,操作数组时类型不一致)
  • 还有IO操作的BufferOverflowException异常

6. Error 和 Exception 区别是什么?

  • Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复

  • Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行