Java 异常机制

抄自:详解Java中的异常


异常机制的概述

人非圣贤,孰能无过。程序也一样,特别是对于大程序而言,不出bug是不可能的,我们该关注的是出了错的程序该如何运行。

程序错误分为三种:

  1. 编译错误

    编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置

  2. 运行时错误

    运行时错误是程序在执行期间,运行环境发现了不能执行的操作

  3. 逻辑错误

    逻辑错误是因为程序没有按照预期的逻辑顺序执行

异常也就是指程序运行时发生的错误

而异常机制是指当程序出现错误后,程序如何处理这个异常、控制这个流程的行为。

具体来说,异常机制提供了程序退出的安全通道,当发生错误时,程序执行流程发生改变,将控制权转移给异常处理器


异常的结构

在 Java 中,所有的异常都有一个共同的祖先 Throwable。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
image

Throwable

是Java中所有异常的共同祖先,异常机制的基类,它指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。

它有两个重要的子类:

  • Exception(异常)
  • Error(错误)

Error

是程序无法处理的错误,表示运行应用程序中较严重问题,大多数错误与代码编写者的操作无关,而是表示代码运行时JVM出现的问题。

比如:之前说过的当JVM没有足够的内存资源进行分配时,将会出现OutOfMemoryError。

这些错误发生时,JVM一般会终止线程,这些错误表示故障发生在虚拟机本身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。

Exception

是程序本身可以处理的异常,其中可以分为两大类:

  • 运行时异常
  • 非运行时异常(编译异常)
运行时异常

RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

非运行时异常(编译异常)

RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。


异常处理的机制

Java中异常处理机制有:抛出异常、捕获异常

抛出异常

如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws或throw来抛出异常。

throws抛出

throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。throws语句的语法格式为:

public void test thorows Exception1,ExceptionN{
    ……
}

方法名后的throws Exception1,Exception2,…,ExceptionN 为声明要抛出的异常列表。当方法抛出异常列表的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。

使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。

throw抛出

throw总是出现在方法体中,用来抛出一个Throwable类或者其子类的实例对象异常。
例如:

throw new IOException;

程序会在throw语句后立即终止,它后面的语句执行不到(除了之后会说的finally),然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。

捕获异常

在Java中,异常通过try-catch语句捕获。其一般语法形式为:

try {  
	// 可能会发生异常的程序代码  
} catch (Type1 id1){  
	// 捕获并处置try抛出的异常类型Type1  
} catch (Type2 id2){  
	 //捕获并处置try抛出的异常类型Type2  
}

关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。

Java方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之外,由Java运行时系统试图寻找匹配的catch子句以捕获异常。若有匹配的catch子句,则运行其异常处理代码,try-catch语句结束。

匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。

因此一般有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。

try-catch语句还可以包括第三部分,就是finally子句。它表示无论是否出现异常,都应当执行的内容。try-catch-finally语句的一般语法形式为:

try {  
	// 可能会发生异常的程序代码  
} catch (Type1 id1){  
	// 捕获并处置try抛出的异常类型Type1  
} catch (Type2 id2){  
	 //捕获并处置try抛出的异常类型Type2  
}finally {  
	// 无论是否发生异常,都将执行的语句块  
} 

无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。

在以下4种特殊情况下,finally块不会被执行:

  1. 在finally语句块中发生了异常。
  2. 在前面的代码中用了System.exit()退出程序。
  3. 程序所在的线程死亡。
  4. 关闭CPU。

try、catch、finally语句块的执行顺序:

  1. 当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
  2. 当try捕获到异常,catch语句块里没有处理此异常的情况:此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
  3. 当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句。

Java常见异常

RuntimeException子类

异常名称 异常描述
ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出
ArithmeticException 算术条件异常。譬如:整数除零等
SecurityException 安全性异常
IllegalArgumentException 非法参数异常
ArrayStoreException 数组中包含不兼容的值抛出的异常
NegativeArraySizeException 数组长度为负异常
ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常
NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等

IOException子类

异常名称 异常描述
IOException 操作输入流和输出流时可能出现的异常
EOFException 文件已结束异常
FileNotFoundException 文件未找到异常

其他

异常名称 异常描述
ClassCastException 类型转换异常类
ArrayStoreException 数组中包含不兼容的值抛出的异常
SQLException 操作数据库异常类
NoSuchFieldException 字段未找到异常
NoSuchMethodException 方法未找到抛出的异常
NumberFormatException 字符串转换为数字抛出的异常
StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
IllegalAccessException 不允许访问某类异常
InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

自定义异常

虽然Java系统已经预定义好了很多异常类,但是有时会遇到预定义的异常类不能描述出现的错误。在这种情况下,程序员可以通过扩展Exception类及其子类来定义自己的异常类。

自定义异常,格式如下:

class 异常类名 extends Exception 
{ 
    public 异常类名(String msg) 
    { 
        super(msg); 
    } 
}

在创建一个类时,继承Throwable的任何子类孙类,并用构造方法super()到父类



原文地址:https://www.cnblogs.com/zohnn/p/11222829.html