Java Exception

Java Exception

异常在所有开发语言中,都是架构设计中不可或缺的一部分,非常重要,那么在Java开发语言中又是如何设计处理的呢?

1. 异常设计概览

在Java中 所有异常的父类是 Throwable,一切的异常子类都是基于它扩展的。

这里我们简单通过源码分析下:

/**
 * 异常是所有error和exception的父类,也是基础。只有throwable以及子类的实例才能被虚拟机或者throw 关键字 抛出
 * The {@code Throwable} class is the superclass of all errors and
 * exceptions in the Java language. Only objects that are instances of this
 * class (or one of its subclasses) are thrown by the Java Virtual Machine or
 * can be thrown by the Java {@code throw} statement. Similarly, only
 * this class or one of its subclasses can be the argument type in a
 * {@code catch} clause.
 * checkException 检查异常 不是RuntimeException子类或者Error子类,
 * 换句话说 异常可以分为检查性异常 和 非检查性异常
 * For the purposes of compile-time checking of exceptions, {@code
 * Throwable} and any subclass of {@code Throwable} that is not also a
 * subclass of either {@link RuntimeException} or {@link Error} are
 * regarded as checked exceptions.
 *
 * <p>Instances of two subclasses, {@link java.lang.Error} and
 * {@link java.lang.Exception}, are conventionally used to indicate
 * that exceptional situations have occurred. Typically, these instances
 * are freshly created in the context of the exceptional situation so
 * as to include relevant information (such as stack trace data).
 * 一个throwable 包含了异常发生创建时的快照 同时也包含了关于这个错误(异常)更多的信息
 * 异常可以被其他异常传播 最后它还可以包含原因cause(另一个异常导致这个异常的原因) 这样不停的传播 就形成了一个异常链 
 * <p>A throwable contains a snapshot of the execution stack of its
 * thread at the time it was created. It can also contain a message
 * string that gives more information about the error. Over time, a
 * throwable can {@linkplain Throwable#addSuppressed suppress} other
 * throwables from being propagated.  Finally, the throwable can also
 * contain a <i>cause</i>: another throwable that caused this
 * throwable to be constructed.  The recording of this causal information
 * is referred to as the <i>chained exception</i> facility, as the
 * cause can, itself, have a cause, and so on, leading to a "chain" of
 * exceptions, each caused by another.
 * 异常发生的一个原因是它被构建在一个低层抽象之中,由于低层错误导致了上层操作失败,
 * 让低层将错误抛出去是一个差劲的设计,因为它和上层不相关,这样做将上层和实现细节关联起来了,假设低层异常是一个检查性异常,抛出一个包装异常
 * 允许上层和调用方交流失败的详细信息 而不是导致任何缺点,它在没有改变api的情况下保证了上层实现的灵活性
 * <p>One reason that a throwable may have a cause is that the class that
 * throws it is built atop a lower layered abstraction, and an operation on
 * the upper layer fails due to a failure in the lower layer.  It would be bad
 * design to let the throwable thrown by the lower layer propagate outward, as
 * it is generally unrelated to the abstraction provided by the upper layer.
 * Further, doing so would tie the API of the upper layer to the details of
 * its implementation, assuming the lower layer's exception was a checked
 * exception.  Throwing a "wrapped exception" (i.e., an exception containing a
 * cause) allows the upper layer to communicate the details of the failure to
 * its caller without incurring either of these shortcomings.  It preserves
 * the flexibility to change the implementation of the upper layer without
 * changing its API (in particular, the set of exceptions thrown by its
 * methods).
 *  另一个cause是方法抛出异常必须符合通用接口,而通用接口不允许直接抛出 
 * <p>A second reason that a throwable may have a cause is that the method
 * that throws it must conform to a general-purpose interface that does not
 * permit the method to throw the cause directly.  For example, suppose
 * a persistent collection conforms to the {@link java.util.Collection
 * Collection} interface, and that its persistence is implemented atop
 * {@code java.io}.  Suppose the internals of the {@code add} method
 * can throw an {@link java.io.IOException IOException}.  The implementation
 * can communicate the details of the {@code IOException} to its caller
 * while conforming to the {@code Collection} interface by wrapping the
 * {@code IOException} in an appropriate unchecked exception.  (The
 * specification for the persistent collection should indicate that it is
 * capable of throwing such exceptions.)
 * cause可以通过两种方式和throwable关联起来,
 * 1. 将cause作为构造器的一个参数 
 * 2. 使用initCause方法 
 *  那些希望cause与其关联起来的 New throwable 应该提供一个cause参数的构造方法并委托或者间接给一个Throwable 构造方法
 * <p>A cause can be associated with a throwable in two ways: via a
 * constructor that takes the cause as an argument, or via the
 * {@link #initCause(Throwable)} method.  New throwable classes that
 * wish to allow causes to be associated with them should provide constructors
 * that take a cause and delegate (perhaps indirectly) to one of the
 * {@code Throwable} constructors that takes a cause.
 *
 * Because the {@code initCause} method is public, it allows a cause to be
 * associated with any throwable, even a "legacy throwable" whose
 * implementation predates the addition of the exception chaining mechanism to
 * {@code Throwable}.
 *
 * <p>By convention, class {@code Throwable} and its subclasses have two
 * constructors, one that takes no arguments and one that takes a
 * {@code String} argument that can be used to produce a detail message.
 * Further, those subclasses that might likely have a cause associated with
 * them should have two more constructors, one that takes a
 * {@code Throwable} (the cause), and one that takes a
 * {@code String} (the detail message) and a {@code Throwable} (the
 * cause).
 *
 * @author  unascribed
 * @author  Josh Bloch (Added exception chaining and programmatic access to
 *          stack trace in 1.4.)
 * @jls 11.2 Compile-Time Checking of Exceptions
 * @since JDK1.0
 */
public class Throwable implements Serializable {
    
}
1.1 设计总结
  1. Throwable 是所有异常类的父类,只有它或子类的实例才能被 Java虚拟机抛出或者 throw 关键字抛出
  2. 异常可以分为检查性异常(check exception) 和 非检查性异常
  3. 异常原因(cause)具有传播性,从低层发生异常处通过包装异常开始向外传播
1.2 Cause两种实现

Cause可以通过两种方式于throwable 关联起来,一个是提供一个带有cause的构造方法 或者使用 initCause()方法 这是Throwable 方法

如果你创建的Throwable希望和cause关联 要么你提供一个包含cause的构造函数 要么借助其他的拥有cause构造函数的throwable

  try {
         lowLevelOp();
     } catch (LowLevelException le) {
      // 第一种 throw new LowLevelException(ie)
      // 第二种 le.initCause(ie)
      // 第三种 借助 其他throwable实现类
         throw new HighLevelException(le);  
     }
1.3 Throwable 构造方法

Java public级别的构造方法只有4个,主要围绕message、cause两个参数和一个默认的无参构造方法

    /** 空 不需要错误信息
     * Constructs a new throwable with {@code null} as its detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     *
     * <p>The {@link #fillInStackTrace()} method is called to initialize
     * the stack trace data in the newly created throwable.
     */
    public Throwable() {
        fillInStackTrace();
    }
   /** 包含特定错误信息的
     * Constructs a new throwable with the specified detail message.  The
     * cause is not initialized, and may subsequently be initialized by
     * a call to {@link #initCause}.
     *
     * <p>The {@link #fillInStackTrace()} method is called to initialize
     * the stack trace data in the newly created throwable.
     *
     * @param   message   the detail message. The detail message is saved for
     *          later retrieval by the {@link #getMessage()} method.
     */
    public Throwable(String message) {
        fillInStackTrace();
        detailMessage = message;
    }
    /**使用cause 和 message构造一个新的throwable
     * Constructs a new throwable with the specified detail message and
     * cause.  <p>Note that the detail message associated with
     * {@code cause} is <i>not</i> automatically incorporated in
     * this throwable's detail message.
     *
     * <p>The {@link #fillInStackTrace()} method is called to initialize
     * the stack trace data in the newly created throwable.
     *
     * @param  message the detail message (which is saved for later retrieval
     *         by the {@link #getMessage()} method).
     * @param  cause the cause (which is saved for later retrieval by the
     *         {@link #getCause()} method).  (A {@code null} value is
     *         permitted, and indicates that the cause is nonexistent or
     *         unknown.)
     * @since  1.4
     */
    public Throwable(String message, Throwable cause) {
        fillInStackTrace();
        detailMessage = message;
        this.cause = cause;
    }

   /** 使用一个cause参数 此时detail就是cause.toString()
     * Constructs a new throwable with the specified cause and a detail
     * message of {@code (cause==null ? null : cause.toString())} (which
     * typically contains the class and detail message of {@code cause}).
     * This constructor is useful for throwables that are little more than
     * wrappers for other throwables (for example, {@link
     * java.security.PrivilegedActionException}).
     *
     * <p>The {@link #fillInStackTrace()} method is called to initialize
     * the stack trace data in the newly created throwable.
     *
     * @param  cause the cause (which is saved for later retrieval by the
     *         {@link #getCause()} method).  (A {@code null} value is
     *         permitted, and indicates that the cause is nonexistent or
     *         unknown.)
     * @since  1.4
     */
    public Throwable(Throwable cause) {
        fillInStackTrace();
        detailMessage = (cause==null ? null : cause.toString());
        this.cause = cause;
    }
1.4 Throwable其他方法
 /**   初始化cause 如果你构造方法 没有初始化cause 可以借助此方法来初始化cause 建立关联
     * Initializes the <i>cause</i> of this throwable to the specified value.
     * (The cause is the throwable that caused this throwable to get thrown.)
     *
     * <p>This method can be called at most once.  It is generally called from
     * within the constructor, or immediately after creating the
     * throwable.  If this throwable was created
     * with {@link #Throwable(Throwable)} or
     * {@link #Throwable(String,Throwable)}, this method cannot be called
     * even once.
     *
     * <p>An example of using this method on a legacy throwable type
     * without other support for setting the cause is:
     *
     * <pre>
     * try {
     *     lowLevelOp();
     * } catch (LowLevelException le) {
     *     throw (HighLevelException)
     *           new HighLevelException().initCause(le); // Legacy constructor
     * }
     * </pre>
     *
     * @param  cause the cause (which is saved for later retrieval by the
     *         {@link #getCause()} method).  (A {@code null} value is
     *         permitted, and indicates that the cause is nonexistent or
     *         unknown.)
     * @return  a reference to this {@code Throwable} instance.
     * @throws IllegalArgumentException if {@code cause} is this
     *         throwable.  (A throwable cannot be its own cause.)
     * @throws IllegalStateException if this throwable was
     *         created with {@link #Throwable(Throwable)} or
     *         {@link #Throwable(String,Throwable)}, or this method has already
     *         been called on this throwable.
     * @since  1.4
     */
    public synchronized Throwable initCause(Throwable cause) {
        if (this.cause != this)
            throw new IllegalStateException("Can't overwrite cause with " +
                                            Objects.toString(cause, "a null"), this);
        if (cause == this)
            throw new IllegalArgumentException("Self-causation not permitted", this);
        this.cause = cause;
        return this;
    }

    /** 
     * Returns a short description of this throwable.
     * The result is the concatenation of:
     * <ul>
     * <li> the {@linkplain Class#getName() name} of the class of this object
     * <li> ": " (a colon and a space)
     * <li> the result of invoking this object's {@link #getLocalizedMessage}
     *      method
     * </ul>
     * If {@code getLocalizedMessage} returns {@code null}, then just
     * the class name is returned.
     *
     * @return a string representation of this throwable.
     */
    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }
// 省略 printStackTrace

2. 异常分类

虽然Throwable是所有类的父类,但是根据第一章节 我们也知道 Throwable分为 检查性异常和非检查性异常。

那么什么是检查性异常,什么又是非检查性异常呢。

检查性异常(Checked Exception),必须处理的异常,编译器会强制开发者对其进行处理,换句话说,开发者如果想要使用它,必须处理该异常情况 例如 构建一个文件流 FileInputStream,就会抛出FileNotFoundException 隶属于 IOExeception。像特定情况 例如 网络连接失败、IO异常 都属于检查性异常。

非检查性异常(Unchecked Exception) 一般发生在运行时发生的异常,比如RuntimeException, 一般是由开发者代码逻辑造成的。当然也不全是 例如 Error的子类 也是属于非检查性异常。但不是有代码造成的。

根据Throwable设计分析,Throwable又分为Exception和Error两大类。

Exception 表示合理且能够catch处理的异常

Error 表示 严重 且无法 try catch的异常 例如 OutOfMemoryError

3. 常见的异常

上面章节只是让你对Throwable 整体设计、分类有一个了解。但其实真正打交道最多的还是下面这些异常。

这些异常 需要了解并熟悉 且在开发中经常发生。

3.1 NullPointerException

NullPointerException 空指针异常。例如调用一个实例方法 但是这个实例为空 就会抛出空指针异常 这是最常见 最容易发生的

3.2 IllegalArgumentException

IllegalArgumentException 违法参数异常 也是非常容易发生的 用于校验参数时使用 表示参数违法或不合适

3.3 IllegalStateException

IllegalStateException 状态异常 也是最常见的异常 状态不正确。

3.4 IndexOutOfBoundsException

IndexOutOfBoundsException 索引越界异常。这是在和集合或数组打交道的是时候 非常容易发生的异常

3.5 ClassCastException

ClassCastException 类型转化异常,也是非常容易发生,经常发生在强转类型,

// 此时就会发生ClassCastException
Object x = new Integer(1);
System.out.println((String)x);
3.6 反射相关异常

ReflectiveOperationException 反射操作异常 由于Java反射特性,可以直接操作Class类型来创建一个对象实例或者获取其参数类型 由此产生的异常 例如

ClassNotFoundException、NoSuchFieldException、NoSuchMethodException、InstantiationException(创建实例异常)、IllegalAccessException 违法访问异常

4. 运行时异常

RuntimeException 运行时异常,开发过程中打交道异常最多的父类,是Exception下面的一个分支 称之为运行时异常。像上面 空指针、索引越界、参数异常等都属于运行时异常。

5. 处理异常

Java 提供了 try catch finally模式来处理异常。针对检查性异常,直接使用catch捕捉

// 示例1 
File file = new File("");
try {
    FileInputStream stream = new FileInputStream(file);
} catch (IOException e){
    e.printStackTrace();
}
5.1 处理异常的两种方式
  1. 直接在catch里面处理 例如示例1
  2. 在catch里面捕捉到异常后 使用 throw 关键字抛出异常。

6. 简单总结

​ 本文基本介绍了Java 异常的整体结构,包括分类,并列举了常见的异常,其实对于开发者来说最长打交道的还是RuntimeException. 一般实现自定义异常也都是继承它。本篇文章的定位也是属于扫盲文,让大家了解Java Exception, 后期如果在写Exception 基本就针对某个点去写。例如 try catch finally 返回值问题。Error常见错误等。

6.1 自定义异常
/**
 * 通用自定义异常
 **/
public class ServiceException extends RuntimeException {
    
    public ServiceException(){
        super();
    }

    public ServiceException(String message){
        super(message);
    }

    public ServiceException(String message, Throwable throwable){
        super(message, throwable);
    }

    public ServiceException(Throwable throwable){
        super(throwable);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值