程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java中的異常處置用法及其架構和應用建議

Java中的異常處置用法及其架構和應用建議

編輯:關於JAVA

Java中的異常處置用法及其架構和應用建議。本站提示廣大學習愛好者:(Java中的異常處置用法及其架構和應用建議)文章只能為提供參考,不一定能成為您想要的結果。以下是Java中的異常處置用法及其架構和應用建議正文


Java異常是Java供給的一種辨認及呼應毛病的分歧性機制。
Java異常機制可使法式中異常處置代碼和正常營業代碼分別,包管法式代碼加倍優雅,並進步法式硬朗性。在有用應用異常的情形下,異常能清楚的答復what, where, why這3個成績:異常類型答復了“甚麼”被拋出,異常客棧跟蹤答復了“在哪“拋出,異常信息答復了“為何“會拋出。
Java異常機制用到的幾個症結字:try、catch、finally、throw、throws。
症結字 解釋 try 用於監聽。將要被監聽的代碼(能夠拋出異常的代碼)放在try語句塊以內,當try語句塊內產生異常時,異常就被拋出。 catch 用於捕捉異常。catch用來捕捉try語句塊中產生的異常。 finally finally語句塊老是會被履行。它重要用於收受接管在try塊裡翻開的物力資本(如數據庫銜接、收集銜接和磁盤文件)。只要finally塊,履行完成以後,才會回來履行try或許catch塊中的return或許throw語句,假如finally中應用了return或許throw等終止辦法的語句,則就不會跳回履行,直接停滯。 throw 用於拋出異常。 throws 用在辦法簽名中,用於聲明該辦法能夠拋出的異常。
上面經由過程幾個示例對這幾個症結字停止簡略懂得。
示例一: 懂得try和catch根本用法

public class Demo1 {

 public static void main(String[] args) {
  try {
   int i = 10/0;
    System.out.println("i="+i); 
  } catch (ArithmeticException e) {
    System.out.println("Caught Exception"); 
   System.out.println("e.getMessage(): " + e.getMessage()); 
   System.out.println("e.toString(): " + e.toString()); 
   System.out.println("e.printStackTrace():");
   e.printStackTrace(); 
  }
 }
}

運轉成果:

Caught Exception
e.getMessage(): / by zero
e.toString(): java.lang.ArithmeticException: / by zero
e.printStackTrace():
java.lang.ArithmeticException: / by zero
 at Demo1.main(Demo1.java:6)

成果解釋:在try語句塊中有除數為0的操作,該操作會拋出java.lang.ArithmeticException異常。經由過程catch,對該異常停止捕捉。
不雅察成果我們發明,並沒有履行System.out.println("i="+i)。這解釋try語句塊產生異常以後,try語句塊中的殘剩內容就不會再被履行了。
示例二: 懂得finally的根本用法
在"示例一"的基本上,我們添加finally語句。

public class Demo2 {

 public static void main(String[] args) {
  try {
   int i = 10/0;
    System.out.println("i="+i); 
  } catch (ArithmeticException e) {
    System.out.println("Caught Exception"); 
   System.out.println("e.getMessage(): " + e.getMessage()); 
   System.out.println("e.toString(): " + e.toString()); 
   System.out.println("e.printStackTrace():");
   e.printStackTrace(); 
  } finally {
   System.out.println("run finally");
  }
 }
}

運轉成果:

Caught Exception
e.getMessage(): / by zero
e.toString(): java.lang.ArithmeticException: / by zero
e.printStackTrace():
java.lang.ArithmeticException: / by zero
 at Demo2.main(Demo2.java:6)
run finally

成果解釋:終究履行了finally語句塊。
示例三: 懂得throws和throw的根本用法
throws是用於聲明拋出的異常,而throw是用於拋出異常。

class MyException extends Exception {
 public MyException() {}
 public MyException(String msg) {
  super(msg);
 }
}

public class Demo3 {

 public static void main(String[] args) {
  try {
   test();
  } catch (MyException e) {
   System.out.println("Catch My Exception");
   e.printStackTrace();
  }
 }
 public static void test() throws MyException{
  try {
   int i = 10/0;
    System.out.println("i="+i); 
  } catch (ArithmeticException e) {
   throw new MyException("This is MyException"); 
  }
 }
}

運轉成果:

Catch My Exception
MyException: This is MyException
 at Demo3.test(Demo3.java:24)
 at Demo3.main(Demo3.java:13)

成果解釋:MyException是繼續於Exception的子類。test()的try語句塊中發生ArithmeticException異常(除數為0),並在catch中捕捉該異常;接著拋出MyException異常。main()辦法對test()中拋出的MyException停止捕捉處置。


Java異常框架
Java異常架構圖:

1. Throwable
Throwable是 Java 說話中一切毛病或異常的超類。
Throwable包括兩個子類: Error 和 Exception。它們平日用於指導產生了異常情形。
Throwable包括了其線程創立時線程履行客棧的快照,它供給了printStackTrace()等接口用於獲得客棧跟蹤數據等信息。

2. Exception
Exception及其子類是 Throwable 的一種情勢,它指出了公道的運用法式想要捕捉的前提。

3. RuntimeException
RuntimeException是那些能夠在 Java 虛擬機正常運轉時代拋出的異常的超類。
編譯器不會檢討RuntimeException異常。例如,除數為零時,拋出ArithmeticException異常。RuntimeException是ArithmeticException的超類。現代碼產生除數為零的情形時,倘使既"沒有經由過程throws聲明拋出ArithmeticException異常",也"沒有經由過程try...catch...處置該異常",也能經由過程編譯。這就是我們所說的"編譯器不會檢討RuntimeException異常"!
假如代碼會發生RuntimeException異常,則須要經由過程修正代碼停止防止。例如,若會產生除數為零的情形,則須要經由過程代碼防止該情形的產生!

4. Error
和Exception一樣,Error也是Throwable的子類。它用於指導公道的運用法式不該該試圖捕捉的嚴重成績,年夜多半如許的毛病都是異常前提。
和RuntimeException一樣,編譯器也不會檢討Error。
Java將可拋出(Throwable)的構造分為三品種型:被檢討的異常(Checked Exception),運轉時異常(RuntimeException)和毛病(Error)。

(1) 運轉時異常
界說: RuntimeException及其子類都被稱為運轉時異常。
特色: Java編譯器不會檢討它。也就是說,當法式中能夠湧現這類異常時,倘使既"沒有經由過程throws聲明拋出它",也"沒有效try-catch語句捕捉它",照樣會編譯經由過程。例如,除數為零時發生的ArithmeticException異常,數組越界時發生的IndexOutOfBoundsException異常,fail-fail機制發生的ConcurrentModificationException異常等,都屬於運轉時異常。
     固然Java編譯器不會檢討運轉時異常,然則我們也能夠經由過程throws停止聲明拋出,也能夠經由過程try-catch對它停止捕捉處置。
     假如發生運轉時異常,則須要經由過程修正代碼來停止防止。例如,若會產生除數為零的情形,則須要經由過程代碼防止該情形的產生!

(2) 被檢討的異常
界說: Exception類自己,和Exception的子類中除"運轉時異常"以外的其它子類都屬於被檢討異常。
特色: Java編譯器會檢討它。此類異常,要末經由過程throws停止聲明拋出,要末經由過程try-catch停止捕捉處置,不然不克不及經由過程編譯。例如,CloneNotSupportedException就屬於被檢討異常。當經由過程clone()接口去克隆一個對象,而該對象對應的類沒有完成Cloneable接口,就會拋出CloneNotSupportedException異常。
     被檢討異常平日都是可以恢復的。

(3) 毛病
界說: Error類及其子類。
特色: 和運轉時異常一樣,編譯器也不會對毛病停止檢討。
     當資本缺乏、束縛掉敗、或是其它法式沒法持續運轉的前提產生時,就發生毛病。法式自己沒法修復這些毛病的。例如,VirtualMachineError就屬於毛病。
     依照Java通例,我們是不該該是完成任何新的Error子類的!
關於下面的3種構造,我們在拋出異常或毛病時,究竟該哪種?《Effective Java》中給出的建議是:關於可以恢復的前提應用被檢討異常,關於法式毛病應用運轉時異常。

關於異常處置的幾條建議

第1條: 只針對不正常的情形才應用異常
建議:異常只應當被用於不正常的前提,它們永久不該該被用於正常的掌握流。
經由過程比擬上面的兩份代碼停止解釋。
代碼1

try {
 int i=0;
 while (true) {
  arr[i]=0;
  i++;
 }
} catch (IndexOutOfBoundsException e) {
}
代碼2
for (int i=0; i<arr.length; i++) {
 arr[i]=0;
}

兩份代碼的感化都是遍歷arr數組,並設置數組中每個元素的值為0。代碼1的是經由過程異常來終止,看起來異常難明,代碼2是經由過程數組界限來終止。我們應當防止應用代碼1這類方法,重要緣由有三點:
異常機制的設計初志是用於不正常的情形,所以很少會會JVM完成試圖對它們的機能停止優化。所以,創立、拋出和捕捉異常的開支是很昂貴的。
把代碼放在try-catch中前往阻攔了JVM完成原來能夠要履行的某些特定的優化。
對數組停止遍歷的尺度形式其實不會招致冗余的檢討,有些古代的JVM完成會將它們優化失落。
現實上,基於異常的形式比尺度形式要慢很多。測試代碼以下:

public class Advice1 {

 private static int[] arr = new int[]{1,2,3,4,5};
 private static int SIZE = 10000;

 public static void main(String[] args) {

  long s1 = System.currentTimeMillis();
  for (int i=0; i<SIZE; i++)
   endByRange(arr);
  long e1 = System.currentTimeMillis();
  System.out.println("endByRange time:"+(e1-s1)+"ms" );

  long s2 = System.currentTimeMillis();
  for (int i=0; i<SIZE; i++)
   endByException(arr);
  long e2 = System.currentTimeMillis();
  System.out.println("endByException time:"+(e2-s2)+"ms" );
 }

 // 遍歷arr數組: 經由過程異常的方法
 private static void endByException(int[] arr) {
  try {
   int i=0;
   while (true) {
    arr[i]=0;
    i++;
    //System.out.println("endByRange: arr["+i+"]="+arr[i]);
   }
  } catch (IndexOutOfBoundsException e) {
  }
 }

 // 遍歷arr數組: 經由過程界限的方法
 private static void endByRange(int[] arr) {
  for (int i=0; i<arr.length; i++) {
   arr[i]=0;
   //System.out.println("endByException: arr["+i+"]="+arr[i]);
  }
 }
}

運轉成果:

endByRange time:8ms
endByException time:16ms

成果解釋:經由過程異常遍歷的速度比通俗方法遍歷數組慢許多!

第2條: 關於可恢復的前提應用被檢討的異常,關於法式毛病應用運轉時異常

異常
解釋
運轉時異常 RuntimeException類及其子類都被稱為運轉時異常。 被檢討的異常 Exception類自己,和Exception的子類中除"運轉時異常"以外的其它子類都屬於被檢討異常

它們的差別是:Java編譯器會對"被檢討的異常"停止檢討,而對"運轉時異常"不會檢討。
也就是說,關於被檢討的異常,要末經由過程throws停止聲明拋出,要末經由過程try-catch停止捕捉處置,不然不克不及經由過程編譯。而關於運轉時異常,倘使既"沒有經由過程throws聲明拋出它",也"沒有效try-catch語句捕捉它",照樣會編譯經由過程。固然,雖然說Java編譯器不會檢討運轉時異常,然則,我們異樣可以經由過程throws對該異常停止解釋,或經由過程try-catch停止捕捉。
rithmeticException(例如,除數為0),IndexOutOfBoundsException(例如,數組越界)等都屬於運轉時異常。關於這類異常,我們應當經由過程修正代碼停止防止它的發生。而關於被檢討的異常,則可以經由過程處置讓法式恢復運轉。例如,假定由於一個用戶沒有存儲足足數量的前,所以他在妄圖在一個免費德律風長進行呼喚就會掉敗;因而就將一個被檢討異常拋出。

第3條: 防止不用要的應用被檢討的異常
"被檢討的異常"是Java說話的一個很好的特征。與前往代碼分歧,"被檢討的異常"會強制法式員處置破例的前提,年夜年夜進步了法式的靠得住性。
然則,過火應用被檢討異常會使API用起來異常不便利。假如一個辦法拋出一個或多個被檢討的異常,那末挪用該辦法的代碼則必需在一個或多個catch語句塊中處置這些異常,或許必需經由過程throws聲明拋出這些異常。 不管是經由過程catch處置,照樣經由過程throws聲明拋出,都給法式員添加了弗成疏忽的累贅。
實用於"被檢討的異常"必需同時知足兩個前提:第一,即便准確應用API其實不能阻攔異常前提的產生。第二,一旦發生了異常,應用API的法式員可以采用有效的舉措對法式停止處置。

第4條: 盡可能應用尺度的異常
代碼重用是值得倡導的,這是一條通用規矩,異常也不破例。重用現有的異常有幾個利益:
第一,它使得你的API加倍易於進修和應用,由於它與法式員本來曾經熟習的習氣用法是分歧的。
第二,關於用到這些API的法式而言,它們的可讀性更好,由於它們不會充滿著法式員不熟習的異常。
第三,異常類越少,意味著內存占用越小,而且轉載這些類的時光開支也越小。
Java尺度異常中有幾個是常常被應用的異常。以下表格:

異常
應用場所
IllegalArgumentException 參數的值不適合 IllegalStateException 參數的狀況不適合 NullPointerException 在null被制止的情形下參數值為null IndexOutOfBoundsException 下標越界 ConcurrentModificationException 在制止並發修正的情形下,對象檢測到並發修正 UnsupportedOperationException 對象不支撐客戶要求的辦法

固然它們是Java平台庫迄今為止最常被重用的異常,然則,在允許的前提下,其它的異常也能夠被重用。例如,假如你要完成諸如單數或許矩陣之類的算術對象,那末重用ArithmeticException和NumberFormatException將長短常適合的。假如一個異常知足你的須要,則不要遲疑,應用便可以,不外你必定要確保拋出異常的前提與該異常的文檔中描寫的前提分歧。這類重用必需樹立在語義的基本上,而不是名字的基本上!
最初,必定要清晰,選擇重用哪種異常並沒有必需遵守的規矩。例如,斟酌紙牌對象的情況,假定有一個用於發牌操作的辦法,它的參數(handSize)是發一手牌的紙牌張數。假定挪用者在這個參數中傳遞的值年夜於整副牌的殘剩張數。那末這類情況既可以被說明為IllegalArgumentException(handSize的值太年夜),也能夠被說明為IllegalStateException(絕對客戶的要求而言,紙牌對象的紙牌太少)。

第5條: 拋出的異常要合適於響應的籠統
假如一個辦法拋出的異常與它履行的義務沒有顯著的聯系關系關系,這類情況會讓人手足無措。當一個辦法傳遞一個由低層籠統拋出的異常時,常常會產生這類情形。這類情形產生時,不只讓人迷惑,並且也"淨化"了高層API。
為了不這個成績,高層完成應當捕捉低層的異常,同時拋出一個可以依照高層籠統停止引見的異常。這類做法被稱為"異常轉譯(exception translation)"。
例如,在Java的聚集框架AbstractSequentialList的get()辦法以下(基於JDK1.7.0_40):

public E get(int index) {
 try {
  return listIterator(index).next();
 } catch (NoSuchElementException exc) {
  throw new IndexOutOfBoundsException("Index: "+index);
 }
}

listIterator(index)會前往ListIterator對象,挪用該對象的next()辦法能夠會拋出NoSuchElementException異常。而在get()辦法中,拋出NoSuchElementException異常會讓人覺得迷惑。所以,get()對NoSuchElementException停止了捕捉,並拋出了IndexOutOfBoundsException異常。即,相當於將NoSuchElementException轉譯成了IndexOutOfBoundsException異常。

第6條: 每一個辦法拋出的異常都要有文檔
要零丁的聲明被檢討的異常,而且應用Javadoc的@throws標志,精確地記載下每一個異常被拋出的前提。
假如一個類中的很多辦法處於異樣的緣由而拋出統一個異常,那末在該類的文檔正文中對這個異常做文檔,而不是為每一個辦法零丁做文檔,這是可以接收的。

第7條: 在細節新聞中包括掉敗 -- 捕捉新聞
簡而言之,當我們自界說異常或許拋出異常時,應當包括掉敗相干的信息。
當一個法式因為一個未被捕捉的異常而掉敗的時刻,體系會主動打印出該異常的棧軌跡。在棧軌跡中包括該異常的字符串表現。典范情形下它包括該異常類的類名,和緊隨厥後的細節新聞。

第8條: 盡力使掉敗堅持原子性
當一個對象拋出一個異常以後,我們總希冀這個對象依然堅持在一種界說優越的可用狀況當中。關於被檢討的異常而言,這尤其主要,由於挪用者平日希冀從被檢討的異常中恢復過去。
普通而言,一個掉敗的辦法挪用應當堅持使對象堅持在"它在被挪用之前的狀況"。具有這類屬性的辦法被稱為具有"掉敗原子性(failure atomic)"。可以懂得為,掉敗了還堅持著原子性。對象堅持"掉敗原子性"的方法有幾種:
(1) 設計一個非可變對象。
(2) 關於在可變對象上履行操作的辦法,取得"掉敗原子性"的最多見辦法是,在履行操作之前檢討參數的有用性。以下(Stack.java中的pop辦法):

public Object pop() {
 if (size==0)
  throw new EmptyStackException();
 Object result = elements[--size];
 elements[size] = null;
 return result;
}

(3) 與上一種辦法相似,可以對盤算處置進程調劑次序,使得任何能夠會掉敗的盤算部門都產生在對象狀況被修正之前。
(4) 編寫一段恢復代碼,由它來說明操作進程中產生的掉敗,和使對象回滾到操作開端之前的狀況上。
(5) 在對象的一份暫時拷貝上履行操作,當操作完成以後再把暫時拷貝中的成果復制給本來的對象。
固然"堅持對象的掉敗原子性"是希冀目的,但它其實不老是可以做獲得。例如,假如多個線程妄圖在沒有恰當的同步機制的情形下,並發的拜訪一個對象,那末該對象就有能夠被留在紛歧致的狀況中。
即便在可以完成"掉敗原子性"的場所,它也不是總被希冀的。關於某些操作,它會明顯的增長開支或許龐雜性。
總的規矩是:作為辦法標准的一部門,任何一個異常都不該該轉變對象挪用該辦法之前的狀況,假如這條規矩被違背,則API文檔中應當清晰的指明對象將會處於甚麼樣的狀況。

第9條: 不要疏忽異常
當一個API的設計者聲明一個辦法會拋出某個異常的時刻,他們正在試圖解釋某些工作。所以,請不要疏忽它!疏忽異常的代碼以下:

try {
 ...
} catch (SomeException e) {
}

空的catch塊會使異常達不到應有的目標,異常的目標是強制你處置不正常的前提。疏忽一個異常,就好像疏忽一個失火旌旗燈號一樣 -- 若把失火旌旗燈號器封閉了,那末認真正的火警產生時,就沒有人看到失火旌旗燈號了。所以,至多catch塊應當包括一條解釋,用來說明為何疏忽這個異常是適合的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved