End

异常 Exception Throwable Error 基础

本文地址


目录

异常 Exception Throwable Error 基础

异常简介

异常就是java通过面向对象的思想将不正常情况封装成了对象,用异常类对其进行描述。

异常机制

  • 如果函数正常流程返回的是result,那么异常返回的是Exception对象。正常流程用return关键字返回,异常流程用throw关键字返回。
  • 函数抛出异常后,使用try-catch机制处理异常。运行时搜索调用栈找到处理异常的函数调用。

异常机制的优点

  • 把异常处理流程和正常流程代码分开编写
  • 使用错误码的场景对外提供API时,使用者可能不会注意到可能产生哪些错误;而使用Checked Exception通过编译时机制提醒使用者注意可能发生的错误
  • 报告发生异常时的调用栈,方便定位问题
  • 易于根据不同类型的问题进行分组,比如IOException是所有的IO类异常的父类

try catch finally 语句

  • try语句中若检测到异常(Throwable)会将相关的信息封装成异常对象,然后传递给catch,catch捕获到后对异常进行处理。
  • try中是一个独立的代码块,在其中定义的变量只在该代码块中有效,如果要在try以外继续使用,需要在try外进行声明。
  • try中如果可能会捕获到多个异常,那么应该(建议)有对应个数的catch进行针对性处理。
  • 一个try对应多个catch语句时,后面的catch子句中的参数类型只能是前面catch字句中参数类型的同级或父类,而不能是子类,也不能是同一个类型。
  • 一个try对应多个catch语句时,若前面的catch子句中的参数类型和实际捕获到的异常类型一致,则只执行此catch语句,而不会再匹配或执行下面的catch字句
  • finally语句里通常用来关闭资源。比如:数据库资源,IO资源等。

子类在覆盖父类方法时

  • 子类的方法只能抛出父类的方法抛出的异常或者该异常的子类,而不能抛出该异常的父类或其他类型的异常
  • 如果父类的方法抛出多个异常,那么子类的该方法只能抛出父类该方法抛出的异常的子集
  • 如果父类的方法没有抛出异常,那么子类覆盖父类的该方法时也不能抛,如果有异常只能使用 try catch 语句

throw 语句

  • throw 语句用于抛出异常对象,一条 throw 语句只能抛出一个异常对象,throw 语句抛出的异常也可以被 try 语句捕获
  • throw 语句之后不能再有其他语句,因为 throw 语句后面的代码是执行不到的
  • RuntimeException 以及其子类可以在函数中通过 throw 抛出而不用在函数上声明。

异常体系

  • Throwable
    • Error:程序外部发生错误导致程序发生的异常,比如 OutOfMemoryErrorrStackOverflowError
    • Exception:表示程序需要捕捉、需要处理的异常,是由于程序设计的不完善而出现的问题
      • RuntimeException:运行时异常,即 Unchecked Exceptione,是那些可能在JVM正常运行期间抛出的异常,通常是逻辑错误或者API调用异常,可以即不必捕获也不抛出,可以通过 UncaughtExceptionHandler 处理
      • Checked Exceptione:编译时异常,错误码的变种形式,通知程序发生了可以预知的异常;必须通过try-catch被显式地捕获或者通过throws子句进行传递,不可以通过 UncaughtExceptionHandler 处理

Throwable 的序列化

public class Throwable implements Serializable

关于序列化

  • 对象序列化就是把一个对象的状态转化成一个字节流
  • 序列化后的对象可以存储在硬盘上,可以通过网络传输,通过RMI远程调用等方式传输
  • 通过ObjectOutputStreamObjectInputStream对对象进行序列化及反序列化
  • 反序列化后可以重建该对象

为什么Throwable会支持序列化?

  • 参考
  • 异常出现后存储成日志。
  • 服务端出现错误可以通知到客户端。

官方文档

Throwable

继承自:Object
实现的接口:Serializable
直接已知子类:Error, Exception

Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

两个子类的实例,Error 和 Exception,通常用于指示发生了异常情况。通常,这些实例是在异常情况的上下文中新近创建的,因此包含了相关的信息(比如堆栈跟踪数据)。

Throwable 包含了其线程创建时线程执行堆栈的快照,它还包含了给出有关错误更多信息的消息字符串。最后,它还可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。此 cause 设施在 1.4 版本中首次出现。它也称为 异常链 设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。

导致 throwable cause 的一个理由是,抛出它的类构建在低层抽象之中,而高层操作由于低层操作的失败而失败。
导致 throwable cause 的另一个理由是,抛出它的方法必须符合通用接口,而通用接口不允许方法直接抛出 cause。

在版本 1.4 中还引入了 getStackTrace() 方法,它允许通过各种形式的 printStackTrace() 方法编程访问堆栈跟踪信息,这些信息以前只能以文本形式使用。此信息已经添加到该类的序列化表示形式,因此 getStackTrace 和 printStackTrace 将可在反序列化时获得的 throwable 上正确操作。

构造方法

Throwable(String message, Throwable cause) //构造一个带指定详细消息和 cause 的新 throwable
Throwable(String message)  //Cause未初始化,可在以后通过调用 initCause(Throwable) 来初始化
Throwable(Throwable cause)  //构造一个将 null 作为其详细消息的新 throwable
Throwable()  //构造一个将 null 作为其详细消息的新 throwable
  • 注意,与 cause 相关的详细消息不是自动合并到这个 throwable 的详细消息中的。调用 fillInStackTrace() 方法来初始化新创建的 throwable 中的堆栈跟踪数据。
  • 参数:message - 详细消息。保存此消息,以便以后通过 getMessage() 方法获取它
  • 参数:cause - 原因。保存此 cause,以便以后通过 getCause() 方法获取它。允许 null 值,指出 cause 是不存在的或是未知的。

普通 get/set 方法

  • String getMessage() 返回此 throwable 的详细消息字符串。
  • String getLocalizedMessage() 创建此 throwable 的本地化描述。
    • 子类可以重写此方法,以便生成特定于语言环境的消息。
    • 对于不重写此方法的子类,默认实现返回与 getMessage() 相同的结果。
  • String toString() 返回此 throwable 的简短描述。
    • 如果 getLocalizedMessage 返回 null,则只返回类名称。
    • 格式【此对象的类的 name: (冒号和一个空格) 调用此对象 getLocalizedMessage() 方法的结果】,如【java.lang.Throwable: 异常信息】
  • Throwable getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。该 Cause 是导致抛出此 throwable 的throwable。
  • Throwable initCause(Throwable cause) 将此 throwable 的 cause 初始化为指定值。
    • 允许 null 值,指出 cause 是不存在的或是未知的。
    • 此方法至多可以调用一次。
    • 此方法通常从构造方法中调用,或者在创建 throwable 后立即调用。
    • 如果此 throwable 通过 Throwable(Throwable) 或 Throwable(String,Throwable) 创建,此方法一次也不能调用。

操作堆栈跟踪信息

  • Throwable fillInStackTrace() 在异常堆栈跟踪中填充。此方法在 Throwable 对象信息中记录有关当前线程堆栈帧的当前状态。
  • StackTraceElement[] getStackTrace() 提供编程访问由 printStackTrace() 输出的堆栈跟踪信息。
  • void setStackTrace(StackTraceElement[] stackTrace) 设置将由 getStackTrace() 返回,并由 printStackTrace() 和相关方法输出的堆栈跟踪元素。

打印堆栈跟踪信息

  • void printStackTrace() 将此 throwable 及其追踪输出至标准错误流System.err。
  • void printStackTrace(PrintStream s) 将此 throwable 及其追踪输出到指定的输出流。
  • void printStackTrace(PrintWriter s) 将此 throwable 及其追踪输出到指定的PrintWriter。

注意,不必 重写任何 PrintStackTrace 方法,应通过调用 getCause 方法来确定 throwable 的 cause。
通过 printStackTrace 输出的堆栈跟踪信息的格式:第一行包含此对象的 toString() 方法的结果,剩余行表示以前由方法 fillInStackTrace() 记录的数据。

Error

Error 是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获严重问题。大多数这样的错误都是异常条件。虽然 ThreadDeath 错误是一个“正规”的条件,但它也是 Error 的子类,因为大多数应用程序都不应该试图捕获它。

在执行该方法期间,无需在其 throws 子句中声明可能抛出但是未能捕获的 Error 的任何子类,因为这些错误【可能是再也不会发生】的异常条件

常见的直接子类:

  • OutOfMemoryError:因为内存溢出或没有可用的内存提供给垃圾回收器时,Java 虚拟机无法分配一个对象,这时抛出该异常。
  • StackOverflowError:当应用程序递归太深而发生堆栈溢出时,抛出该错误。
  • NoClassDefFoundError:当 Java 虚拟机或 ClassLoader 实例试图在类的定义中加载,但无法找到该类的定义时,抛出此异常。
  • UnsatisfiedLinkError:当 Java 虚拟机无法找到声明为 native 的方法的适当本地语言定义时,抛出该错误。
  • IOError:当发生严重的 I/O 错误时,抛出此错误。
  • LinkageError:LinkageError 的子类指示一个类在一定程度上依赖于另一个类;但是,在编译前一个类之后,后一个类发生了不相容的改变。
  • ClassCircularityError:当初始化类时检测到类的循环调用的时候,抛出该错误。
  • ClassFormatError:当 Java 虚拟机试图读取类文件并确定该文件存在格式错误或无法解释为类文件时,抛出该错误。
  • ExceptionInInitializerError:静态初始化程序中发生意外异常的信号。抛出此错误表明在计算静态初始值或静态变量的初始值期间发生异常。
  • IncompatibleClassChangeError:在某些类定义中出现不兼容的类更改时抛出该异常。某些目前执行的方法所依赖的类定义已发生了变化。
  • VerifyError:当“校验器”检测到一个类文件虽然格式正确,但包含着一些内部不一致性或安全性问题时,抛出该错误。
  • ThreadDeath:调用 Thread 类中带有零参数的 stop 方法时(此方法已过时),受害线程将抛出一个 ThreadDeath 实例。仅当应用程序在被异步终止后必须清除时才应该捕获这个类的实例。
  • VirtualMachineError:当 Java 虚拟机崩溃或用尽了它继续操作所需的资源时,抛出该错误。
  • InternalError:该异常指示 Java 虚拟机中出现一些意外的内部错误。
  • UnknownError:当 Java 虚拟机中出现一个未知但严重的异常时,抛出该错误。

Exception

Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。

直接已知子类:

AclNotFoundException, ActivationException, AlreadyBoundException, ApplicationException, AWTException, BackingStoreException, BadAttributeValueExpException, BadBinaryOpValueExpException, BadLocationException, BadStringOperationException, BrokenBarrierException, CertificateException, ClassNotFoundException, CloneNotSupportedException, DataFormatException, DatatypeConfigurationException, DestroyFailedException, ExecutionException, ExpandVetoException, FontFormatException, GeneralSecurityException, GSSException, IllegalAccessException, IllegalClassFormatException, InstantiationException, InterruptedException, IntrospectionException, InvalidApplicationException, InvalidMidiDataException, InvalidPreferencesFormatException, InvalidTargetObjectTypeException, InvocationTargetException, IOException, JAXBException, JMException, KeySelectorException, LastOwnerException, LineUnavailableException, MarshalException, MidiUnavailableException, MimeTypeParseException, MimeTypeParseException, NamingException, NoninvertibleTransformException, NoSuchFieldException, NoSuchMethodException, NotBoundException, NotOwnerException, ParseException, ParserConfigurationException, PrinterException, PrintException, PrivilegedActionException, PropertyVetoException, RefreshFailedException, RemarshalException, RuntimeException, SAXException, ScriptException, ServerNotActiveException, SOAPException, SQLException, TimeoutException, TooManyListenersException, TransformerException, TransformException, UnmodifiableClassException, UnsupportedAudioFileException, UnsupportedCallbackException, UnsupportedFlavorException, UnsupportedLookAndFeelException, URIReferenceException, URISyntaxException, UserException, XAException, XMLParseException, XMLSignatureException, XMLStreamException, XPathException

RuntimeException

RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类
可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在【throws】子句中进行声明

直接已知子类:

AnnotationTypeMismatchException
ArithmeticException
ArrayStoreException
BufferOverflowException
BufferUnderflowException
CannotRedoException
CannotUndoException
ClassCastException
CMMException
ConcurrentModificationException
DOMException
EmptyStackException
EnumConstantNotPresentException
EventException
IllegalArgumentException
IllegalMonitorStateException
IllegalPathStateException
IllegalStateException
ImagingOpException
IncompleteAnnotationException
IndexOutOfBoundsException
JMRuntimeException
LSException
MalformedParameterizedTypeException
MirroredTypeException
MirroredTypesException
MissingResourceException
NegativeArraySizeException
NoSuchElementException
NoSuchMechanismException
NullPointerException
ProfileDataException
ProviderException
RasterFormatException
RejectedExecutionException
SecurityException
SystemException
TypeConstraintException
TypeNotPresentException
UndeclaredThrowableException
UnknownAnnotationValueException
UnknownElementException
UnknownTypeException
UnmodifiableSetException
UnsupportedOperationException
WebServiceException

Checked 异常和 Unchecked 异常

面试题:两者的区别

面试题:NullPointerExceptionIOException有什么区别?

答:

  • 这两个是 Java 中的两种异常,一个是 Unchecked Exceptione,一个是 Checked Exceptione
  • Checked异常继承java.lang.Exception类,Checked异常必须通过try-catch被显式地捕获或者通过throws子句进行传递。
  • Unchecked异常即运行时异常,继承自java.lang.RuntimeException类,是那些可能在 Java 虚拟机正常运行期间抛出的异常,unchecked 异常可以既不必捕获也不抛出

运行时异常

运行时异常我们一般不处理,当出现这类异常的时候程序会由 JVM 接管。比如,我们从来没有去处理过 NullPointerException,而且这个异常还是最常见的异常之一。

出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇到处理代码,如果没有 catch 块进行处理,到了最上层,如果是多线程就有 Thread.run() 抛出,如果不是多线程那么就由 main.run() 抛出。抛出之后,如果是线程,那么该线程也就终止了,如果是主程序,那么该程序也就终止了。

其实运行时异常的也是继承自 Exception,也可以用 catch 块对其处理,只是我们一般不处理罢了,也就是说,如果不对运行时异常进行 catch 处理,那么结果不是线程退出就是主程序终止。如果不想终止,那么我们就必须捕获所有可能出现的运行时异常。

两种异常的使用场景

  • Checked和unchecked异常从功能的角度来讲是等价的,可以用checked异常实现的功能必然也可以用unchecked异常实现,反之亦然。
  • 选择checked异常还是unchecked异常是个人习惯或者组织规定问题。并不存在谁比谁强大的问题。
  • Unchecked异常避免了不必要的try-catch块,不会使代码显得杂乱;Unchecked异常不会因为异常声明聚集使方法声明显得杂乱。

checked 异常使用案例

public class Test {
    public static void main(String[] args) {

        try {
            new Test().testException();
        } catch (MyException e) {
            e.printStackTrace();
            System.out.println("调用抛出checked异常的方法时,同样必须通过try-catch显式地捕获或者通过throws子句进行传递");
        }
    }

    void testException() throws MyException {
        throw new MyException("抛出checked异常时,必须通过try-catch显式地捕获或者通过throws子句进行传递");
    }

    class MyException extends Exception {
        MyException(String s) {
            super(s);
        }
    }
}

unchecked 异常使用案例

和上面相比,只需把自定义的异常由继承自Exception改为继承自RuntimeException即可。

由于RuntimeException继承自Exception,所以修改后上面其他代码都不需要改变就可以正常使用。但RuntimeException可以即不必捕获也不抛出:

public class Test {
    public static void main(String[] args) {
        new Test().testException();
    }

    void testException() {
        throw new MyException("unchecked异常可以即不必捕获也不抛出");
    }

    class MyException extends RuntimeException {
        MyException(String s) {
            super(s);
        }
    }
}

checked 异常错误使用案例

public class Test {

    public static void main(String[] args) {
        try {
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void start() {
        System.out.println("这个方法并没有声明会抛出checked 异常,例如IOException");
    }
}

上面的代码编译是通不过的:

因为IOException是checked异常,而start方法并没有抛出IOException,编译器将在处理IOException时报错。

但是如果你将IOException改为Exception,编译器报错将消失,因为Exception可以用来捕捉所有运行时异常(包括unchecked异常),这样就不需要声明抛出语句。

将上例中的 IOException 改为 unchecked 异常也是可以的,例如改为 NullPointerException

其他小知识点

error和exception有什么区别

  • error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。
  • exception 表示程序需要捕捉、需要处理的异常,是由于程序设计的不完善而出现的问题,程序必须处理的问题。

final、finally、finalize的区别

  • final用于声明变量、方法和类的,分别表示变量值不可变,方法不可覆盖,类不可以继承
  • finally是异常处理中的一个关键字,表示finally{}里面的代码一定要执行
  • finalize是Object类的一个方法,在垃圾回收的时候会调用被回收对象的此方法。

常见的Exception和Error有哪些

  • 常见的 Checked 异常:ClassNotFoundExceptionCloneNotSupportedException,DataFormatException,IllegalAccessException,InterruptedExceptionIOExceptionNoSuchFieldExceptionNoSuchMethodException,ParseException,TimeoutException,XMLParseException
  • 常见的 Unchecked 异常:BufferOverflowException,ClassCastExceptionIllegalArgumentException,IllegalStateException,IndexOutOfBoundsException,NoSuchElementException,NullPointerException,SecurityException,SystemException,UnsupportedOperationException
  • 常见的 Error:OutOfMemoryErrorStackOverflowError、NoClassDefFoundError、UnsatisfiedLinkError、IOErrorThreadDeath、ClassFormatError、InternalError、UnknownError

使用 try-catch 会影响性能吗?

  • 无异常发生时,使用try-catch语句影响不大
  • 有异常发生相比无异常发生,处理时长更长
  • printStackTrace()很耗时

2018-06-09

posted @ 2018-06-09 14:27  白乾涛  阅读(2161)  评论(2编辑  收藏  举报