<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在學習Java的過程中,想必大家都一定學習過異常這個篇章,異常的基本特性和使用這裡就不再多講了。想必大家都能夠理解看懂,並正確使用。
但是,光學會基本例外處理和使用不夠的,在工作中常會有自定義業務異常的場景,根據不同的業務異常做對應例外處理,出現異常並不可怕,有時候是需要使用異常來驅動業務的處理,例如: 在使用唯一約束的資料庫的時候,如果插入一條重複的資料,那麼可以通過捕獲唯一約束異常DuplicateKeyException,如果出現CommunicationsException是不是又要去處理呢?如果兩種情況都使用同樣業務邏輯來處理,是不是同樣捕獲呢?這個時候,其實如果在DAO層統一捕獲Exception,然後向上丟擲自定義異常,在呼叫成層根據對應的業務異常再進行處理,而且自定義異常能做很多輕量化處理(請看下文解釋),是不是方便很多呢?所以這裡自定義業務異常:既是對業務不同異常場景下的區分,又是通過異常來驅動業務流程的處理,以自定義異常好處很多。
Java中預設的異常資訊有哪些呢?Java程式中捕獲異常之後會將異常進行輸出,不知道細心的同學有沒有注意到一點,輸出的異常是什麼東西呢?下面來看一個常見的ArithmeticException異常:
java.lang.ArithmeticException: / by zero at greenhouse.ExceptionTest.testException(ExceptionTest.java:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
再看看一個Java程式設計師耳熟能詳的NullPointerException空指標異常:
java.lang.NullPointerException at greenhouse.ExceptionTest.testException(ExceptionTest.java:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
大家有沒有發現一個特點,就是異常的輸出中能夠精確的輸出異常出現的地點,精確到每一行程式碼,還有後面一大堆的執行過程類呼叫,也都列印出來了,這些資訊從哪兒來呢?
這些資訊是從棧中獲取的,在列印異常紀錄檔的時候,會從JVM 棧中去獲取這些呼叫資訊。能夠精確的定位異常出現的異常當然是好,但是我們有時候考慮到程式的效能,以及一些需求時,我們有時候並不需要完全的列印這些資訊,並且去方法呼叫棧中獲取相應的資訊,是有效能消耗的,對於一些效能要求高的程式,我們完全可以在例外處理方面為程式效能做一個效能提升。
所以如何避免輸出這些堆疊資訊呢? 那麼自定義異常就可以解決這個問題:
首先,自定義異常需要繼承RuntimeException,然後,再通過是重寫fillInStackTrace,toString 方法,例如下面我定義一個AppException異常:
package com.green.monitor.common.exception; import java.text.MessageFormat; /** * 自定義異常類 */ public class AppException extends RuntimeException { private boolean isSuccess = false; private String key; private String info; public AppException(String key) { super(key); this.key = key; this.info = key; } public AppException(String key, String message) { super(MessageFormat.format("{0}[{1}]", key, message)); this.key = key; this.info = message; } public AppException(String message, String key, String info) { super(message); this.key = key; this.info = info; } public boolean isSuccess() { return isSuccess; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } @Override public Throwable fillInStackTrace() { return this; } @Override public String toString() { return MessageFormat.format("{0}[{1}]",this.key,this.info); } }
那麼為什麼要重寫fillInStackTrace,和 toString 方法呢? 我們首先來看原始碼是怎麼一回事。
public class RuntimeException extends Exception { static final long serialVersionUID = -7034897190745766939L; /** Constructs a new runtime exception with <code>null</code> 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</code> 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); } }
RuntimeException是繼承Exception,但是它裡面只是呼叫了父類別的方法,本身是沒有做什麼其餘的操作。那麼繼續看Exception裡面是怎麼回事。
public class Exception extends Throwable { static final long serialVersionUID = -3387516993124229948L; /** * Constructs a new exception with <code>null</code> 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</code> 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); } }
從原始碼中可以看到,Exception裡面也是直接呼叫了父類別的方法,和RuntimeException一樣,自己其實並沒有做什麼。那麼直接來看Throwable裡面是怎麼一回事:
public class Throwable implements Serializable { public Throwable(String message) { fillInStackTrace(); detailMessage = message; } /** * Fills in the execution stack trace. This method records within this * <code>Throwable</code> object information about the current state of * the stack frames for the current thread. * * @return a reference to this <code>Throwable</code> instance. * @see java.lang.Throwable#printStackTrace() */ public synchronized native Throwable fillInStackTrace(); /** * Provides programmatic access to the stack trace information printed by * {@link #printStackTrace()}. Returns an array of stack trace elements, * each representing one stack frame. The zeroth element of the array * (assuming the array's length is non-zero) represents the top of the * stack, which is the last method invocation in the sequence. Typically, * this is the point at which this throwable was created and thrown. * The last element of the array (assuming the array's length is non-zero) * represents the bottom of the stack, which is the first method invocation * in the sequence. * * <p>Some virtual machines may, under some circumstances, omit one * or more stack frames from the stack trace. In the extreme case, * a virtual machine that has no stack trace information concerning * this throwable is permitted to return a zero-length array from this * method. Generally speaking, the array returned by this method will * contain one element for every frame that would be printed by * <tt>printStackTrace</tt>. * * @return an array of stack trace elements representing the stack trace * pertaining to this throwable. * @since 1.4 */ public StackTraceElement[] getStackTrace() { return (StackTraceElement[]) getOurStackTrace().clone(); } private synchronized StackTraceElement[] getOurStackTrace() { // Initialize stack trace if this is the first call to this method if (stackTrace == null) { int depth = getStackTraceDepth(); stackTrace = new StackTraceElement[depth]; for (int i=0; i < depth; i++) stackTrace[i] = getStackTraceElement(i); } return stackTrace; } /** * Returns the number of elements in the stack trace (or 0 if the stack * trace is unavailable). * * package-protection for use by SharedSecrets. */ native int getStackTraceDepth(); /** * Returns the specified element of the stack trace. * * package-protection for use by SharedSecrets. * * @param index index of the element to return. * @throws IndexOutOfBoundsException if <tt>index < 0 || * index >= getStackTraceDepth() </tt> */ native StackTraceElement getStackTraceElement(int index); /** * 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 <tt>getLocalizedMessage</tt> returns <tt>null</tt>, 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; }
從原始碼中可以看到,到Throwable就幾乎到頭了,在fillInStackTrace() 方法是一個native方法,這方法也就是會呼叫底層的C語言,返回一個Throwable物件,toString 方法,返回的是throwable的簡短描述資訊,並且在getStackTrace 方法和 getOurStackTrace 中呼叫的都是native方法getStackTraceElement,而這個方法是返回指定的棧元素資訊,所以這個過程肯定是消耗效能的,那麼我們自定義異常中的重寫toString方法和fillInStackTrace方法就可以不從棧中去獲取異常資訊,直接輸出,這樣對系統和程式來說,相對就沒有那麼"重",是一個優化效能的非常好的辦法。
按照上面我們舉例的自定義AppException異常,如果出現異常了,這個AppException異常輸出是什麼樣的資訊呢?請看下面吧:
@Test public void testException(){ try { String str =null; System.out.println(str.charAt(0)); }catch (Exception e){ throw new AppException("000001","空指標異常"); } }
執行上面單元測試,在異常異常的時候,系統將會列印我們自定義的異常資訊:
000001[空指標異常]
Process finished with exit code -1
所以特別簡潔,優化了系統程式效能,讓程式不這麼“重”,所以對於效能要求特別要求的系統,趕緊自定義業務異常試一試吧!
到此這篇關於一起聊聊Java中的自定義異常的文章就介紹到這了,更多相關Java自定義異常內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45