java里的异常处理
Exception 是在程序执行过程中发生的一些不希望发生的事情,这些事情如果不被好好处理,就会导致奇怪的结果或者是程序终结。Exception Handler是那些当异常发生时处理这些异常的代码。
在 Java 中一个异常的产生,主要有如下三种原因:
- Java 内部错误发生异常,Java 虚拟机产生的异常。(ERROR)
- 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,一般需要在某些类中集中处理这些异常。(Exception)
- 通过 throw 语句手动生成的异常,这种异常称为检査的异常,一般用来告知该方法的调用者一些必要的信息。
Exception和Error的区别
首先Exception和Error都是继承于Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。Exception和Error体现了JAVA这门语言对于异常处理的两种方式。
Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。
Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。
checked exception和unchecked exception
Java中存在两种异常,一种是checked exception,一种是unchecked exception。
两者区别
区别1:
checked exception 必须被显式的抛出或者捕获,比如FileNotFoundException,就是一个必须处理的异常。
unchecked exception 不用被显式的抛出或者捕获,比如NullPointerException,没见过代码里面需要捕获或者抛出这个异常的吧。
区别2:
checked exception继承Exception类
unchecked exception 继承RuntimeException类
举个栗子:
创建两个类CheckedExceptionA和UnCheckedExceptionB:
class CheckedExceptionA extends Exception {
}
class UnCheckedExceptionB extends RuntimeException {
}
创建一个Handle类使用他们时,CheckedExceptionA必须处理,UnCheckedExceptionB则不需要
public class Handle {
public void handleChecked(){
try {
throw new CheckedExceptionA();
} catch (CheckedExceptionA checkedExceptionA) {
checkedExceptionA.printStackTrace();
}
}
public void handleUnChecked(){
throw new UnCheckedExceptionB();
}
}
生产环境如何选择
很明显checkedException编译器会显示的提醒,不会忘记处理异常,UnCheckedException会使得代码精简,没有大量捕获代码,效率更高。
对于unchecked类型,如果忘记处理异常可能会导致项目流程中断。
对于两者的选择,仍然不应该太死板,需要根据实际需要进行选择,如果出现了该异常后我们需要捕捉到进行一些处理,这样我们就选用checked类型,但如果出现某种异常需要中断程序,那么可以采用unChecked类型。
补充
unchecked exception不只有RuntimeException及其子类,还有Error 及其子类。
RuntimeException(unchecked)及其详解
AnnotationTypeMismatchException | 抛出以指示程序已尝试访问注释编辑(或序列化)后类型已更改的注释元素。 API used to read annotations reflectively可以抛出这个异常。 |
ArrayStoreException | 抛出异常算术条件时抛出。 例如,“除以零”的整数会抛出此类的一个实例。
|
ArithmeticException | 抛出以表示尝试将错误类型的对象存储到对象数组中。 例如,以下代码生成一个ArrayStoreException :Object x[] = new String[3]; x[0] = new Integer(0); |
BufferOverflowException | 操作达到目标缓冲区限制时抛出的未检查异常。 |
BufferUnderflowException | 当相对 get操作达到源缓冲区的限制时抛出未检查的异常 |
CannotRedoException | 抛出一个UndoableEdit被告知redo() 并且不能。 |
CannotUndoException | 当UndoableEdit被告知到undo() 并且不能被抛出。 |
ClassCastException | 抛出表示代码尝试将对象转换为不属于实例的子类。 例如,以下代码生成一个ClassCastException : Object x = new Integer(0); System.out.println((String)x); |
CMMException | 如果本机CMM返回错误,则抛出此异常。 |
CompletionException | 在完成结果或任务的过程中遇到错误或其他异常时抛出异常。 |
ConcurrentModificationException | 当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。 |
DataBindingException | 表示JAXB操作失败的异常。此异常不同于JAXBException 的,这是一个未经检查的异常,而JAXBException是经过检查的异常。 |
DateTimeException | 用于在计算日期时间时指示问题的异常。该异常用于指示创建,查询和操作日期时间对象的问题。 |
DOMException | DOM操作只会在“异常”情况下引发异常,即当操作无法执行时(出于逻辑原因,因为数据丢失,或者由于实现变得不稳定))。 通常,DOM方法在普通处理情况下返回特定的错误值,例如使用NodeList 时的NodeList 错误。在其他情况下,实施应引起其他例外。 例如,当null 不被预期时,如果传递null 参数,则实现应该引发实现相关的异常。某些语言和对象系统不支持异常的概念。 对于这样的系统,可以使用本机错误报告机制来指示错误状况。 对于某些绑定,例如,方法可能返回类似于相应方法描述中列出的错误代码。 |
EmptyStackException | 由 Stack 类中的方法抛出,表示堆栈为空。 |
EnumConstantNotPresentException | 当应用程序尝试通过名称访问枚举常量时抛出,枚举类型不包含指定名称的常量。 API used to read annotations reflectively可以抛出这个异常。 |
EventException | 事件操作可能会引发EventException 像在其方法描述中指定。 |
FileSystemAlreadyExistsException | 尝试创建已存在的文件系统时抛出运行时异常。 |
FileSystemNotFoundException | 无法找到文件系统时抛出的运行时异常。 |
IllegalArgumentException | 抛出表示一种方法被传递了非法或不正确的参数。 |
IllegalMonitorStateException | 抛出以表示线程已尝试在对象的监视器上等待或通知其他线程等待对象的监视器,而不拥有指定的监视器。 |
IllegalPathStateException | 所述IllegalPathStateException 表示,如果这是在相对于所述特定操作的非法状态被执行,如附加的路径段的路径上执行操作时引发一个例外GeneralPath不具有初始moveto |
IllegalStateException | 表示在非法或不适当的时间调用了一种方法。 换句话说,Java环境或Java应用程序对于请求的操作并不处于适当的状态。 |
IllformedLocaleException | 通过Locale 和Locale.Builder 中的方法抛出,以表明参数不是一个格式正确的BCP 47标签。 |
ImagingOpException | 所述ImagingOpException 如果所述一个被抛出BufferedImageOp 种或RasterOp 过滤方法不能处理图像。 |
IncompleteAnnotationException | 抛出以指示程序已尝试访问在编辑(或序列化)注释后添加到注释类型定义的注释类型的元素。 如果新元素具有默认值,则不会抛出此异常。 API used to read annotations reflectively可以抛出这个异常 |
IndexOutOfBoundsException | 抛出以表示某种索引(例如数组,字符串或向量)的索引超出范围。应用程序可以将此类子类化以指示类似的异常。 |
JMRuntimeException | JMX实现发出的运行时异常。 |
LSException | 如果处理停止,解析器或写入操作可能会抛出LSException 。 该处理可由于停下来一DOMError 用的严重性DOMError.SEVERITY_FATAL_ERROR 或非恢复DOMError.SEVERITY_ERROR ,或DOMErrorHandler.handleError() 返回false 。 |
MalformedParameterizedTypeException | 当需要实例化的反射方法遇到语义畸变的参数化类型时抛出。 例如,如果参数化类型的类型参数的数量是错误的。 |
MalformedParametersException | 当the java.lang.reflect package 尝试从类文件中读取方法参数并确定一个或多个参数格式错误时抛出。 以下是可以抛出此异常的条件列表: 参数(parameter_count)的数量对于该方法是错误的 常数池索引超出范围。 常量池索引不涉及UTF-8条目 参数名称为“”,或包含非法字符 标志字段包含非法标志(除了FINAL,SYNTHETIC或MANDATED之外的其他标志) 见Executable.getParameters() 以获取更多信息。 |
MirroredTypesException | 当应用程序尝试访问类 对象时抛出的对象,每个对象对应一个TypeMirror 。 |
MissingResourceException | 表示资源丢失。 |
NegativeArraySizeException | 抛出一个应用程序尝试创建一个负数大小的数组。 |
NoSuchElementException | 被各种访问器方法抛出,表示被请求的元素不存在。 |
NoSuchMechanismException | 当请求特定的XML机制但在环境中不可用时抛出此异常。 一个 |
NullPointerException | 当应用程序尝试在需要对象的情况下使用null 时抛出。 这些包括: 调用一个 访问或修改 取 访问或修改的时隙 投掷 应用程序应该抛出此类的实例以指示 |
ProfileDataException | 在访问或处理ICC_Profile对象时发生错误时抛出此异常。 |
ProviderException | 提供者异常的运行时异常(例如错误配置错误或不可恢复的内部错误),它们可能由提供程序子类化以抛出专门的提供者特定的运行时错误。 |
ProviderNotFoundException | 无法找到所需类型的提供者时抛出的运行时异常。 |
RasterFormatException | 如果RasterFormatException 中有无效的布局信息,则会抛出RasterFormatException 。 |
RejectedExecutionException | 异常通过抛出Executor 当任务不能执行所接受。 |
SecurityException | 由安全管理员抛出,表示安全违规。 |
SystemException | 所有CORBA标准异常的根类。 这些异常可能会由于任何CORBA操作调用而抛出,也可能由许多标准的CORBA API方法返回。 标准异常包含次要代码,允许更详细的规范和完成状态。 该类被子类化以生成标准ORB异常集合中的每一个。SystemException 延伸java.lang.RuntimeException ; 因此,没有一个SystemException 例外需要在从IDL接口中的操作映射的Java方法的签名中声明。 |
TypeConstraintException | 此异常表示检测到违反动态检查的类型约束。该异常可以由生成的派生Java内容类的setter方法抛出。 但是,由于故障快速验证是JAXB提供程序支持的可选功能,当违反类型约束时,并非所有setter方法都将抛出此异常。如果在调用失败快速设置器时抛出此异常,则该属性的值将保持不变,就像从未调用setter一样。 |
TypeNotPresentException | 当应用程序尝试使用表示类型名称的字符串访问类型时抛出,但没有找到具有指定名称的类型的定义。 此异常不同于ClassNotFoundException 在ClassNotFoundException是检查的异常,而此异常是未经检查的。请注意,当访问未定义的类型变量以及加载类型(例如类,接口或注释类型)时,可能会使用此异常。 特别是,这个例外可以由API used to read annotations reflectively抛出。 |
UncheckedIOException | 封装一个IOException 与未经检查的异常。 |
UndeclaredThrowableException | 通过方法调用代理实例时抛出的调用处理程序的invoke 方法抛出检查异常(一Throwable 是不能分配给RuntimeException 或Error )是不能分配给任何中声明的异常类型throws 方法的条款,在代理实例上调用并分派到调用处理程序。UndeclaredThrowableException实例 包含调用处理程序抛出的未声明的已检查异常,可以使用 从版本1.4开始,这种异常已被改进以符合通用异常链接机制。 可以在构建时提供并通过 |
UnknownEntityException | 例外的超类,表示遇到一个未知类型的实体。 如果语言演变和引入新的构造,就会发生这种情况。 访问者可能会抛出此异常的子类,以指示访问者是为先前版本的语言创建的。这些异 常的常见超类允许单个catch块具有统一处理它们的代码。 |
UnmodifiableSetException | 抛出以表示所请求的操作无法执行,因为该集合是不可修改的。 |
UnsupportedOperationException | 抛出以表示不支持请求的操作。 |
WebServiceException | WebServiceException 类是所有JAX-WS API运行时异常的基本异常类。 |
WrongMethodTypeException | 抛出以表示代码尝试通过错误的方法类型调用方法句柄。 与正常Java方法调用的字节码表示一样,方法句柄调用强烈类型与与调用站点相关联的特定类型描述符。当组合两个方法句柄时,也可能会抛出此异常,并且系统检测到它们的类型无法正确匹配。 这相当于在方法句柄构建时间对类型不匹配的早期评估,而不是调用不匹配的方法句柄时。 |
Exception(checked)及其详解(部分)
AclNotFoundException | 这是在引用不存在的ACL(访问控制列表)时抛出的异常 |
ActivationException | 激活界面使用的常规异常。 |
AlreadyBoundException | 一个 AlreadyBoundException 如果试图将注册表中的对象绑定到已具有相关绑定的名字被抛出。 |
ApplicationException | 此类用于报告ORB和存根之间的应用程序级异常。 |
AWTException | 表示发生抽象窗口工具包异常。 |
BackingStoreException | 抛出以表示由于后备存储中的故障而无法完成首选项操作,或无法联系后备存储 |
BadAttributeValueExpException | 当无效的MBean属性传递给查询构造方法时抛出。 这个异常在评估查询期间由JMX内部使用。 用户代码通常不会看到它 |
BadBinaryOpValueExpException | 当无效表达式传递给构造查询的方法时抛出。 这个异常在评估查询期间由JMX内部使用。 用户代码通常不会看到它。 |
BadLocationException | 此异常是报告文档模型中的不良位置(即尝试引用不存在的位置)。 |
BadStringOperationException | 当无效的字符串操作传递给构造查询的方法时抛出。 |
BrokenBarrierException | 当线程尝试等待处于断开状态的屏障或线程等待时进入断开状态时抛出异常。 |
CertificateException | 此异常表示各种证书问题之一。 |
CloneNotSupportedException | 抛出,表明该clone 类方法Object 被称为克隆对象,但该对象的类无法实现Cloneable 接口。 覆盖 |
DataFormatException | 发出数据格式错误的信号。 |
DatatypeConfigurationException | 表示严重的配置错误。 |
DestroyFailedException | 信号destroy 操作失败。当destroy 方法失败时,通过执行Destroyable接口的Destroyable 抛出此异常。 |
ExecutionException | 尝试检索通过抛出异常中止的任务的结果时抛出的异常。 可以使用Throwable.getCause() 方法检查此异常 。 |
ExpandVetoException | 用于停止和展开/崩溃的异常发生。 见How to Write a Tree-Will-Expand Listener在Java教程进一步的信息和示例。 |
FontFormatException | 在 Font 类中通过 Font 方法抛出,以指示指定的字体是坏的。 |
GeneralSecurityException | GeneralSecurityException 类是一种通用的安全性异常类,它为所有与安全相关的异常类提供类型安全性。 |
等等
常见的checked Exception
ClassNotFoundException | 当应用程序尝试通过其字符串名称加载到类中时抛出 |
NamingException | 正在调用的命名操作已被中断时抛出此异常。 例如,应用程序可能会中断执行搜索的线程。 如果搜索支持被中断,它将抛出InterruptedNamingException。 一个操作是否可中断,何时取决于其实现(由服务提供商提供)。 不同的实现方式有不同的方式来保护资源和对象不被意外中断所损坏。 |
SQLException | 提供有关数据库访问错误或其他错误的信息的异常。 |
IOException | 表示发生某种类型的I / O异常。 此类是由失败或中断的I / O操作产生的一般异常类。 |
error和unchecked exception的不同
虽然都发生在runtime,但是error不被建议去处理(handle)。 大部分情况下即使是加了catch也无法修复。而RuntimeException可以去handle。
Throw和Throws的区别
throw:作用在方法内,表示抛出具体异常,由方法体内的语句处理。一旦进入被执行,程序立即会转入异常处理阶段,后面的语句就不再执行,而且所在的方法不再返回有意义的值。
throws:作用在方法的声明上,表示如果抛出异常,则由该方法的调用者来进行异常处理。是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。用于声明异常,例如,如果一个方法里面不想有任何的异常处理,则在没有任何代码进行异常处理的时候,必须对这个方法进行声明有可能产生的所有异常(其实就是,不想自己处理,那就交给别人吧,告诉别人我会出现什么异常,报自己的错,让别人处理去吧)。
如何处理异常
有一些方法在声明的时候就声明扔出一个exception。如果这个exception是checked exception,则调用这个方法的时候就必须handle它。
checked exception就像一个炸弹,如果说方法A的某处扔出一个炸弹,或者从别处接到一个炸弹(调用了另一个扔出exception的方法), 有两种解决方案:
自己把它拆了( try-catch-finally)。 这样的话调用方法A的方法不用担心这个炸弹(异常)了
继续丢出去, 谁调用方法A谁来处理(在A的method declaration的时候加上throws.)
如果采用第一种方案,当方法B调用方法A的时候,方法A已经把炸弹拆了,方法B不用担心任何事情。
如果采用第二种方法,方法B调用方法A的时候知道同时要接到一个炸弹,于是它有两种解决方案,拆了,或者继续throws。
try-catch-finally:
一个常见的数组越界exception。是个unchecked的exception,因为编译时没有要求handle,所以是个常见的新手 runtime异常。(不要求被handle,但是如果想handle也是可以的。)
import java.io.*;
public class ExceptionTest1 {
public static void main(String args[]) {
try {
int a[] = new int[2];
System.out.println(a[3]); //数组越界访问
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e);//虽然没有做有意义的事情,但是阻止了程序死在半截。
}
System.out.println("run successfully");
}
}
一个try可以跟着好几个catch, 为了分开处理不同的错误:
try {
//doing something
} catch (Exception1 e) {
// handle the first type exception
System.out.println("Exception1 happened")
} catch (Exception2 f){
// handle the first type exception
System.out.println("Exception2 happened")
}
一个exception发生之后,如果是Exception1类型的,就会被第一个handle,如果不是Exception1类型的就会接着往下找catch,如果是Exception2类型的,就会被第二个catch块handle。
如果Exception1和Exception2是父子关系,则儿子要先被检测,因为如果爸爸先被检测,就永远也到不了儿子那个catch块了。
finally block 是无论如何也会发生的一个block。 catch里的代码如果不发生异常就不会被执行,但是finally里面的代码无论如何都会执行。(除非是在try或者catch里面用System.exit(1)
结束jvm。)通常用来关闭文件。
try {
//doing something
} catch (Exception e) {
//handle the exception
}finally {
//一定会被执行的代码。
}
}
}
另外, try-finally
也是合法的。
throws exception
用throws
关键词
//m1抛出了一个异常
void m1 throws FileNotFoundException(){
//大概是想读一些文件,又不想考虑如果文件找不到咋整。(甩锅)
}
//m2调用了m1,相当于从m1手里接到了这个异常,自己不处理,继续往外甩。
void m2 throws FileNotFoundException(){
m1();//想用m1方法,m1声明如果想用我就要考虑文件找不到的情况。
//但是m2依然不想考虑文件找不到咋整。
}
下面代码由于接到了exception没有正确handle而产生编译错误:
import java.io.File;
import java.io.FileReader;
//会有编译错误
public class ReadFileTest {
public static void main(String args[]) {
getFile();
}
public static void getFile(){
File file = new File("file.txt");
FileReader fr = new FileReader(file);//error: unreported exception FileNotFoundException; must be caught or declared to be thrown
}
}
因为FileReader的constructor throws了一个异常
public FileReader(String fileName) throws FileNotFoundException
所以当getFile方法调用FileReader的时候必须handle这个异常。
下面是一个例子用上面两种方法处理异常:
mport java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
public class ReadFileTest {
public static void main(String args[]) {
getFile1();
System.out.println("=============");
try{
// 因为getFile2又把exception甩出来了,所以main方法得处理它。
getFile2();
}catch(FileNotFoundException e){
System.out.println("Exception handled in main");
}
}
//getFile1选择了自己处理,不麻烦调用它的方法
public static void getFile1() {
try{
File file = new File("file.txt");
FileReader fr = new FileReader(file);
}catch(FileNotFoundException e){
System.out.println("Exception handled in getfile1");
}
}
//getFile2选择了不处理,继续throws
public static void getFile2() throws FileNotFoundException{
File file = new File("file.txt");
FileReader fr = new FileReader(file);
}
}
运行结果:
Exception handled in getfile1
=============
Exception handled in main
一般来讲,所有的exception到了main方法这里都应该已经被解决了。如果,main
方法也不负责任的往外扔。。。
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
public class ReadFileTest {
public static void main(String args[]) throws FileNotFoundException{
getFile1();
System.out.println("=============");
getFile2();
}
public static void getFile1() {
try{
File file = new File("file.txt");
FileReader fr = new FileReader(file);
}catch(FileNotFoundException e){
System.out.println("Exception handled in getfile1");
}
}
public static void getFile2() throws FileNotFoundException{
File file = new File("file.txt");
FileReader fr = new FileReader(file);
}
}
这样做是可以的。。。在没有文件的情况下(触发exception)运行结果:
Exception handled in getfile1
=============
Exception in thread "main" java.io.FileNotFoundException: file.txt (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileReader.<init>(FileReader.java:72)
at ReadFileTest.getFile2(ReadFileTest.java:21)
at ReadFileTest.main(ReadFileTest.java:8)
如何创建一个custom的exception类
import java.io.*;
class MyException extends Exception
{
public MyException(String s)
{
super("a MyException happens: " + s);
//还可以做一些其他的事
}
}
如何甩出(throw)一个custom的exception
import java.io.*;
public class Main
{
public static void main(String args[])
{
try {
throwTest();
} catch (MyException e) {
System.out.println("Caught something");
System.out.println(e.getMessage());
}
//throwTest2是一个正常的方法,扔的exception被自己handle了。
throwTest2();
//另用一个block测试test3,
//如果写在同一个block,throwTest()抛出异常后,throwTest3()不会被执行
try {
throwTest3();
} catch (MyException e) {
System.out.println("Caught something");
System.out.println(e.getMessage());
}
}
// 自己不handle,扔出来之后继续在方法声明里告诉调用自己的方法需要handle
public static void throwTest() throws MyException {
throw new MyException("something");
}
//自己handle了,变成一个正常的方法
public static void throwTest2() {
try {
throw new MyException("somethingElse");
} catch (MyException e) {
System.out.println("Caught somethingElse");
System.out.println(e.getMessage());
}
}
//自己handle了,在catch里继续往外扔。
public static void throwTest3() throws MyException{
try {
throw new MyException("somethingElse2");
} catch (MyException e) {
//一个非常不负责任的catch,但是是合法的。
throw e;
}
}
}
运行结果:
Caught something
a MyException happens: something
Caught somethingElse
a MyException happens: somethingElse
Caught something
a MyException happens: somethingElse2
关于Error和Exception面试问题典型回答
请对比Exception和Error,另外,运行时异常与一般异常有什么区别?
Exception和Error都是继承了Throwable类,在java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),他是异常处理机制的基本组成类型。
Exception和Error体现了java平台设计者对不同异常情况的分类,Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常状态,不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
Exception又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。前面我们介绍的不可查的Error,是Throwable不是Exception。
不检查异常就是所谓的运行时异常,类似NullPointerException,ArrayIndexOutOfBoundsExceptin之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。
关于事务异常处理的补充:
Spring的AOP即声明式事务管理默认是针对unchecked exception回滚