java基础 --- Java异常处理

Posted Overboom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础 --- Java异常处理相关的知识,希望对你有一定的参考价值。

1. 什么是异常

异常本质上是程序的错误,包括程序逻辑错误和系统错误,比喻空的引用、数组下标越界、内存溢出错误等。编译错误在编译期间有编译器帮助修正,运行错误便不是编译器力所能及的了,并且运行期间的错误是难以预料的。如何应对运行期间的错误并进行补救?Java提供异常机制来进行处理,通过异常机制来处理程序运行期间出现的错误。

异常发生的原因很多,通常包含以下几大类:

  • 用户输入非法数据
  • 要打开的文件不存在
  • 网络通信时连接中断,或者JVM内存溢出

这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。

要搞清楚Java异常处理是如何工作的,需要掌握三种类型的异常:

  • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
  • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
  • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

2. java异常的分类

在Java中异常被当做对象来处理,根类是java.lang.Throwable类,包括2个子类,Error类和Exception类。

其中,Error指的是JVM错误,这是程序并未执行,无法处理;Exception指程序运行中产生的异常,用户可以使用处理格式处理。

2.1 java内置异常类

Java 语言定义了一些异常类在 java.lang 标准包中。

标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。

Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。

异常描述
ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException当不支持请求的操作时,抛出该异常。

下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。

异常描述
ClassNotFoundException应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException拒绝访问一个类的时候,抛出该异常。
InstantiationException当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException请求的变量不存在
NoSuchMethodException请求的方法不存在

2.2 异常方法

下面的列表是 Throwable 类的主要方法:

序号方法及说明
1public String getMessage()
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
2public Throwable getCause()
返回一个Throwable 对象代表异常原因。
3public String toString()
使用getMessage()的结果返回类的串级名字。
4public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。
5public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
6public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

3. java中的异常处理

3.1 java异常执行流程

Error与Exception的区别:

Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

在catch捕获异常时,为什么不考虑使用Throwable类型,而只是使用Exception来进行接收?

Throwable表示的范围要比Exception大。实际上程序使用Throwable来进行处理,没有任何语法问题,但是却会存在逻辑问题。因为此时出现的(或者说用户能够处理的)只有Exception类型,而如果使用Throwable接收,还会表示可以处理Error的错误,而用户是处理不了Error错误的,所以在开发中用户可以处理的异常都要求以Exception类为主。

异常是一起处理好还是分开处理好?

根据实际的开发要求是否严格来决定。在实际的项目开发项目工作中,所有的异常是统一使用Exception处理还是分开处理,完全根据开发者的项目开发标准来决定。如果项目开发环境严谨,基本上要求针对每一种异常分别进行处理,并且要详细记录下异常产生的时间以及产生的位置,这样可以方便程序维护人员进行代码的维护。再次注意:处理多个异常时,捕获范围小的异常要放在捕获范围大的异常之前处理。

throw和throws的区别?

throw和throws都是在异常处理中使用的关键字,区别如下:

  • throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化或者抛出已存在的);
  • throws:在方法的声明上使用,表示此方法在调用时必须处理异常。

检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)区别?

  • 所有的检查性异常都继承自java.lang.Exception;所有的非检查性异常都继承自java.lang.RuntimeEx ception。
  • 检查性异常和非检查性异常最主要的区别在于其处理异常的方式:检查性异常必须使用try catch或者throws等关键字进行处理,否则编译器会报错;非检查性异常一般是程序代码写的不够严谨而导致的问题,可以通过修改代码来规避。
  • 常见的运行时异常:空指针异常(NullPointerException)、除零异常(ArithmeticException)、数组越界异常(ArrayIndexOutOfBoundsException)等;
  • 常见的检查性异常:输入输出异常(IOException)、文件不存在异常(FileNotFoundException)、SQL语句异常(SQLException)等。

3.2 异常处理方案

try...catch、try...catch...finally、try...finally
    try{
        可能会发生的异常
    }catch(异常类型 异常名(变量)){
        针对异常进行处理的代码
    }catch(异常类型 异常名(变量)){
        针对异常进行处理的代码
    }...
    [finally{
        释放资源代码;
    }]

注意:

  • catch 不能独立于 try 存在。
  • catch里面不能没有内容
  • 在 try/catch 后面添加 finally 块并非强制性要求的。
  • try 代码后不能既没 catch 块也没 finally 块。
  • try里面越少越好。
  • try, catch, finally 块之间不能添加任何代码。
  • finally里面的代码最终一定会执行(除了JVM退出)
  • 如果程序可能存在多个异常,需要多个catch进行捕获。
  • 异常如果是同级关系,catch谁前谁后没有关系
  • 如果异常之间存在上下级关系,上级需要放在后面

3.3 try/catch/finally/throws/ throw关键字的理解

1. try catch finally

try关键字用来包围可能出现异常的逻辑代码,它无法单独使用,必须配合catch或者finally使用。java编译器允许使用的组合形式有下面三种

try...catch...
try...finally...
try...catch...finally...

Note:

1> catch块可以有多个,try块只能有一个,finally块是可选的(但是最多也只能有一个finally块)。

2> 三个块的执行顺序为try->catch->finally

3> 如果没有发生异常,则catch块不会执行。但是finally块无论在什么情况下都是会执行的(这点要非常注意,因此部分情况下,都会将释放资源的操作放在finally块中进行)。

4> 在有多个catch块的时候,是按照catch块的先后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配。

5> 在使用try..catch..finally块的时候,注意千万不要在finally块中使用return,因为finally中的return会覆盖已有的返回值。看一个示例

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
 
 
public class Main {
    public static void main(String[] args) {
        String str = new Main().openFile();
        System.out.println(str);
         
    }
     
    public String openFile() {
        try {
            FileInputStream inputStream = new FileInputStream("d:/a.txt");
            int ch = inputStream.read();
            System.out.println("aaa");
            return "step1";
        } catch (FileNotFoundException e) {
            System.out.println("file not found");
            return "step2";
        }catch (IOException e) {
            System.out.println("io exception");
            return "step3";
        }finally{
            System.out.println("finally block");
            //return "finally";
        }
    }
}

输出结果为:

可以看出,在try块中发生FileNotFoundException之后,就跳到第一个catch块,打印"file not found"信息,并将"step2"赋值给返回值,然后执行finally块,最后将返回值返回。

从这个例子说明,无论try块或者catch块中是否包含return语句,都会执行finally块。

如果将这个程序稍微修改一下,将finally块中的return语句注释去掉,运行结果是:

最后打印出的是"finally",返回值被重新覆盖了。

因此如果方法有返回值,切忌不要再finally中使用return,这样会使得程序结构变得混乱。

 2. throws和throw关键字

1)throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,允许throws后面跟着多个异常类型;

2)一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出现在方法体中,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw出去。throw关键字的一个非常重要的作用就是 异常类型的转换(会在后面阐述道)。

  throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由方法去处理异常,真正的处理异常由此方法的上层调用处理。

4. 自定义异常

在 Java 中你可以自定义异常。如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。

按照国际惯例,自定义的异常应该总是包含如下的构造函数:

  • 一个无参构造函数
  • 一个带有String参数的构造函数,并传递给父类的构造函数。
  • 一个带有String参数和Throwable参数,并都传递给父类构造函数
  • 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。

下面是IOException类的完整源代码,可以借鉴。
 

package java.io;
 
public class IOException extends Exception {
    static final long serialVersionUID = 7818375828146090155L;
 
    public IOException() {
	super();
    }
 
    public IOException(String message) {
	super(message);
    }
 
    public IOException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public IOException(Throwable cause) {
        super(cause);
    }
}

参考链接:

https://www.cnblogs.com/dolphin0520/p/3769804.html

https://blog.csdn.net/sugar_no1/article/details/88593255

https://www.runoob.com/java/java-exceptions.html

以上是关于java基础 --- Java异常处理的主要内容,如果未能解决你的问题,请参考以下文章

java 反射代码片段

Java 基础 ------- 异常

Java基础-异常

3.3 java基础总结①异常②自定义异常

[ Java学习基础 ] Java异常处理

java基础-异常