8.3 自定义异常及经验小结
在开发实践中很少用到自定义异常。因为在Java中已经定义好了非常多的异常类而且那些开源的框架中也有自己的异常类,所以不是写一些开源框架或者一些大型系统的情况下很少使用自定义异常。
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需集成Exception类即可
在程序中使用自定义异常类,大体可分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出**给方法调用者**的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常
自定义异常类的示例
-
自定义异常类需要继承Exception类才可以成为自定义的异常类
-
在Exception类中我们可以看到有很多的子类的,这些异常几乎满足了大部分的需求
-
定义完异常类需要自定义一些方法(我们可以从Java自带的异常类来学习如何写自定义异常类比如ArrayIndexOutOfBoundsException数组下标越界的异常类)
package java.lang;/*** Thrown to indicate that an array has been accessed with an* illegal index. The index is either negative or greater than or* equal to the size of the array.** @author unascribed* @since JDK1.0*/ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {private static final long serialVersionUID = -5116101128118950844L;/*** Constructs an <code>ArrayIndexOutOfBoundsException</code> with no* detail message.*/public ArrayIndexOutOfBoundsException() {super();}/*** Constructs a new <code>ArrayIndexOutOfBoundsException</code>* class with an argument indicating the illegal index.** @param index the illegal index.*/public ArrayIndexOutOfBoundsException(int index) {super("Array index out of range: " + index);}/*** Constructs an <code>ArrayIndexOutOfBoundsException</code> class* with the specified detail message.** @param s the detail message.*/public ArrayIndexOutOfBoundsException(String s) {super(s);} }
从代码中我们可以看出ArrayIndexOutOfBoundsException继承了IndexOutOfBoundsException异常类,我们就去看这个父异常类IndexOutOfBoundsException
public class IndexOutOfBoundsException extends RuntimeException {private static final long serialVersionUID = 234122996006267687L;/*** Constructs an <code>IndexOutOfBoundsException</code> with no* detail message.*/public IndexOutOfBoundsException() {super();}/*** Constructs an <code>IndexOutOfBoundsException</code> with the* specified detail message.** @param s the detail message.*/public IndexOutOfBoundsException(String s) {super(s);} }
代码中我们可以看出IndexOutOfBoundsException继承了一个运行时异常类RuntimeException
package java.lang;/*** {@code RuntimeException} is the superclass of those* exceptions that can be thrown during the normal operation of the* Java Virtual Machine.** <p>{@code RuntimeException} and its subclasses are <em>unchecked* exceptions</em>. Unchecked exceptions do <em>not</em> need to be* declared in a method or constructor's {@code throws} clause if they* can be thrown by the execution of the method or constructor and* propagate outside the method or constructor boundary.** @author Frank Yellin* @jls 11.2 Compile-Time Checking of Exceptions* @since JDK1.0*/ public class RuntimeException extends Exception {static final long serialVersionUID = -7034897190745766939L;/** Constructs a new runtime exception with {@code null} as its* detail message. The cause is not initialized, and may subsequently be* initialized by a call to {@link #initCause}.*/public RuntimeException() {super();}/** Constructs a new runtime exception with the specified detail message.* The cause is not initialized, and may subsequently be initialized by a* call to {@link #initCause}.** @param message the detail message. The detail message is saved for* later retrieval by the {@link #getMessage()} method.*/public RuntimeException(String message) {super(message);}/*** Constructs a new runtime exception 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 runtime exception's detail message.** @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 <tt>null</tt> value is* permitted, and indicates that the cause is nonexistent or* unknown.)* @since 1.4*/public RuntimeException(String message, Throwable cause) {super(message, cause);}/** Constructs a new runtime exception with the specified cause and a* detail message of <tt>(cause==null ? null : cause.toString())</tt>* (which typically contains the class and detail message of* <tt>cause</tt>). This constructor is useful for runtime exceptions* that are little more than wrappers for other throwables.** @param cause the cause (which is saved for later retrieval by the* {@link #getCause()} method). (A <tt>null</tt> value is* permitted, and indicates that the cause is nonexistent or* unknown.)* @since 1.4*/public RuntimeException(Throwable cause) {super(cause);}/*** Constructs a new runtime exception with the specified detail* message, cause, suppression enabled or disabled, and writable* stack trace enabled or disabled.** @param message the detail message.* @param cause the cause. (A {@code null} value is permitted,* and indicates that the cause is nonexistent or unknown.)* @param enableSuppression whether or not suppression is enabled* or disabled* @param writableStackTrace whether or not the stack trace should* be writable** @since 1.7*/protected RuntimeException(String message, Throwable cause,boolean enableSuppression,boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);} }
这段代码我们看出RuntimeException继承了Exception异常
也就是说ArrayIndexOutOfBoundsException是好几个子类下面的,继承了四层关系,下面我们看一下这个异常是怎么写的,我们回到ArrayIndexOutOfBoundsException异常类的方法区
package java.lang;/*** Thrown to indicate that an array has been accessed with an* illegal index. The index is either negative or greater than or* equal to the size of the array.** @author unascribed* @since JDK1.0*/ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {private static final long serialVersionUID = -5116101128118950844L;/*** Constructs an <code>ArrayIndexOutOfBoundsException</code> with no* detail message.*/public ArrayIndexOutOfBoundsException() {super();}/*** Constructs a new <code>ArrayIndexOutOfBoundsException</code>* class with an argument indicating the illegal index.** @param index the illegal index.*/public ArrayIndexOutOfBoundsException(int index) {super("Array index out of range: " + index);}/*** Constructs an <code>ArrayIndexOutOfBoundsException</code> class* with the specified detail message.** @param s the detail message.*/public ArrayIndexOutOfBoundsException(String s) {super(s);} }
我们可以看到异常类里面主要写了类的构造方法,构造方法是空参的是继承父类的,和实参的是继承父类的方法用来接收下标index。我们去父类看一下这个方法,也就是IndexOutOfBoundsException,在这个异常类中我们看到有参的构造方法又调用了父类的方法
public IndexOutOfBoundsException(String s) {super(s);}
我们接着去IndexOutOfBoundsException的父类RuntimeException看它的构造方法
public RuntimeException(String message) {super(message);}
发现RuntimeException调用了他的父类Exception的构造方法,接着我们去Exception看
package java.lang;/*** The class {@code Exception} and its subclasses are a form of* {@code Throwable} that indicates conditions that a reasonable* application might want to catch.** <p>The class {@code Exception} and any subclasses that are not also* subclasses of {@link RuntimeException} are <em>checked* exceptions</em>. Checked exceptions need to be declared in a* method or constructor's {@code throws} clause if they can be thrown* by the execution of the method or constructor and propagate outside* the method or constructor boundary.** @author Frank Yellin* @see java.lang.Error* @jls 11.2 Compile-Time Checking of Exceptions* @since JDK1.0*/ public class Exception extends Throwable {static final long serialVersionUID = -3387516993124229948L;/*** Constructs a new exception with {@code null} as its detail message.* The cause is not initialized, and may subsequently be initialized by a* call to {@link #initCause}.*/public Exception() {super();}/*** Constructs a new exception with the specified detail message. The* cause is not initialized, and may subsequently be initialized by* a call to {@link #initCause}.** @param message the detail message. The detail message is saved for* later retrieval by the {@link #getMessage()} method.*/public Exception(String message) {super(message);}/*** Constructs a new exception 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 exception's detail message.** @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 <tt>null</tt> value is* permitted, and indicates that the cause is nonexistent or* unknown.)* @since 1.4*/public Exception(String message, Throwable cause) {super(message, cause);}/*** Constructs a new exception with the specified cause and a detail* message of <tt>(cause==null ? null : cause.toString())</tt> (which* typically contains the class and detail message of <tt>cause</tt>).* This constructor is useful for exceptions that are little more than* wrappers for other throwables (for example, {@link* java.security.PrivilegedActionException}).** @param cause the cause (which is saved for later retrieval by the* {@link #getCause()} method). (A <tt>null</tt> value is* permitted, and indicates that the cause is nonexistent or* unknown.)* @since 1.4*/public Exception(Throwable cause) {super(cause);}/*** Constructs a new exception with the specified detail message,* cause, suppression enabled or disabled, and writable stack* trace enabled or disabled.** @param message the detail message.* @param cause the cause. (A {@code null} value is permitted,* and indicates that the cause is nonexistent or unknown.)* @param enableSuppression whether or not suppression is enabled* or disabled* @param writableStackTrace whether or not the stack trace should* be writable* @since 1.7*/protected Exception(String message, Throwable cause,boolean enableSuppression,boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);} }
从代码中我们可以看出Exception又调用了他的父类Throwable的构造方法,我们接着去Throwable中查看代码
public Throwable(String message) {fillInStackTrace();detailMessage = message;}
到了Throwable中发现构造方法没有再继承,而是写了方法体,也就是去打印这个message。所以这里我们就可以看出我们在自定义异常的时候需要自己写一个描述信息。
于是我们就写了自己的自定义异常
package com.baidu.www.exception.demo02;/*** 1.普通类必须继承Exception才能成为自定义的异常类*/ public class MyException extends Exception {//传递数字,如果数字大于10,就抛出异常private int detail;public MyException(int a) {this.detail = a;}//toString:异常的打印信息@Overridepublic String toString() {return "MyException{" +"detail=" + detail +'}';} }
首先我们自己先写一个描述信息,在上面的示例中我们是给异常类传递一个数字,当数字大于10的时候就抛出异常,抛出异常的时候需要给人们提出一些提示信息,首先第一个一个私有变量detail来接收这个数字,根据上面异常类的编写方法写一个构造器就可以了,所以我们写了一个消息的构造器,当然我们这里传递的是一个整型的数字。
如果想要别人去打印这个信息就需要用到一个方法toString()方法。在IDEA中使用alt+insert快捷键插入toString方法
接下来写一个测试来测试一下我们的自定义异常
package com.baidu.www.exception.demo02;public class Test {//写一个可能会存在异常的方法static void test(int a) throws MyException {System.out.println("传递的参数为:"+a);if(a>10){throw new MyException(a);//抛出异常,也可以在方法内部捕获,如果在方法内部捕获,在方法外部就不用捕获了}System.out.println("ok");}public static void main(String[] args) {try {test(11);} catch (MyException e) {//e.printStackTrace();System.out.println("MyException=>"+e);}} } /*** 传递的参数为:11* MyException=>MyException{detail=11}** Process finished with exit code 0*/
首先我们要写一个可能存在异常的方法。在我们的测试方法test()中抛出异常,这里我们定义的方法中如果传递参数大于10就抛出异常。如果在方法中抛出异常就会告知我们有一个未知的异常需要处理一下,结局方法是可以在外面添加try-catch语句来捕获并处理异常,第二种方式在方法上将异常抛出去,抛到更高级别,让调用这个方法的时候再捕获这个异常并处理。
在测试方法中我们可以看到,当程序调用测试方法时抛出了异常被捕获到,执行catch代码块中的语句,其中catch捕获到了MyException的异常,捕获到之后就执行catch代码块,e就是MyException中定义的toString方法。
实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理(因为用了try-catch就不至于程序突然的卡死)
- 在多重catch块后面,可以加上一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常,在IDEA中如果有异常会出现红色的波浪线,使用alt+enter快捷键可以添加try-catch,或将异常抛出方法。
- 尽量去处理异常,切记只是简单的调用==printStackTrace()==去打印输出(这是一个默认信息,使用快捷键生成异常处理块会自动生成的处理方式),而是使用一些判断语句进行异常的处理,如此一来就能减少损失。
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定(因为各种业务涉及的异常不同,比如银行金融、大小公司遇到的需求各不相同)
- 尽量添加finally语句块去释放占用的资源(尤其在IO流)