摘要:異常處理程序拋出的異常必須在異常處理程序中得到處理。終止與恢復異常處理有兩種模型,支持終止模型,一旦異常被拋出,表明錯誤無法挽回,無法退回來繼續執行之前出錯的代碼。對于異常來說,最重要的部分就是類名。
一、概念
使用異常能降低處理錯誤代碼的復雜程度,并且將錯誤在一個地方進行處理,于是將“描述在正常行為過程中做過什么事”的代碼和“出了問題怎么辦”的代碼相分離
二、基本異常異常情形指的是當前環境下沒有足夠的信息來讓我們解決這個問題,比如當除數為0發生的時候,我們不知道除數為零代表著什么,(比如在算淘寶購物花銷所占百分比的時候,你發現你這個月根本沒花錢,總數是零),并不知道該如何處理這個異常。因此就要拋出異常
拋出異常后,會在堆上new出一個新的異常對象,然后當前執行路徑終止,并彈出這個異常對象的引用,然后異常處理機制接管程序抓住這個異常,進行異常處理。
拋出異常的時候就像這樣。
throw new NullPointerException()1.異常參數
異常也是對象,也有他自己的構造器,當在堆上new出一個異常對象的時候,他也可以執行不同的對象構造器。標準異常類都有兩個構造器:一個是默認構造器;另一個是接受字符串參數,比如:
throw new NullPointerException("t=null");
從效果上看,將這個異常給throw了,就像是從方法中“返回”一樣,另外還能用拋出異常的方式從當前作用域退出。
能夠拋出任意類型的Throwable對象,他是異常類型的根類。
首先要理解監控區域的概念,他是一段可能產生異常的代碼,后面跟著處理這些可能出現的異常的代碼。
1.try塊如果在方法內部拋出了異常,那么這個方法將在拋出異常的時候結束,如果不希望方法直接結束,可以在方法內設置一個塊來“嘗試”各種可能產生異常的方法。
try{ //code }2.異常處理程序
拋出的異常必須在異常處理程序中得到處理。異常處理程序跟隨在try塊后
try{ }catch(Type1 id1){ //handle exceptions of type1 }catch(Type2 id2){ }
當在try塊中出現異常后,異常被拋出,異常處理程序將負責搜尋與這個異常參數類型匹配的第一個異常處理程序,然后進行異常處理,一旦catch結束,則異常處理程序的查找過程結束。
3.終止與恢復異常處理有兩種模型,Java支持終止模型,一旦異常被拋出,表明錯誤無法挽回,無法退回來繼續執行之前出錯的代碼。
另一種叫做恢復模型,指的是異常處理程序的工作是修正錯誤然后重新嘗試調用出問題的方法,并認為第二次能成功。
要自己定義異常類,必須從已有的異常類繼承,最好選擇意思相近的異常類繼承。
package tij.exception; public class Test { void f() throws SimpleException { System.out.println("Throw SimpleException from f()"); throw new SimpleException(); } public static void main(String[] args) { Test t = new Test(); try { t.f(); } catch (SimpleException e) { System.out.println("Caught it"); } } } class SimpleException extends Exception {}
對于異常來說,最重要的部分就是類名。
這個例子的結果被打印到了控制臺上,也可以通過寫入System.err將錯誤發送給標準錯誤流。通常這比把錯誤輸出到System.out要好,因為System.out也許會被重定向。
package tij.exception; public class Test { static void f() throws MyException { System.out.println("Throw MyException from f()"); throw new MyException(); } static void g() throws MyException { System.out.println("Throw MyException from g()"); throw new MyException(); } public static void main(String[] args) { try { f(); } catch (MyException e) { e.printStackTrace(System.out); } try { g(); } catch (MyException e) { e.printStackTrace(); } } } class MyException extends Exception { public MyException() {} public MyException(String msg) { super(msg); } }
在異常處理程序中,調用了在Throwable類(Exception也是從他繼承的)的printStackTrace方法,它將打印“從方法調用處知道異常拋出處的方法調用序列”,在上例中,如果信息被發送到了System.out,則將信息顯示在輸出中,如果使用默認版本e.printStackTrace則將輸出到標準錯誤流。
看,上下兩個顏色不一樣
1.異常與記錄日志可以使用java.util.logging將輸出記錄到日志中。
package tij.exception; import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.Logger; public class Test { public static void main(String[] args) { try { throw new LoggingException(); } catch (LoggingException e) { System.err.println("Caught " + e); } try { throw new LoggingException(); } catch (LoggingException e) { System.err.println("Caught " + e); } } } class LoggingException extends Exception { private static Logger logger = Logger.getLogger("LoggingExcetpion"); public LoggingException() { StringWriter trace = new StringWriter(); printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } }
LoggingException首先創建了一個Logger對象,這個對象會將其輸出發送到System.err。如果為了產生日志記錄信息,現在我們想把棧軌跡記錄下來。而printStackTrace不會產生字符串,因此采用了帶有PrintWriter參數的printStackTrace方法,這個方法會將棧軌跡的字符串信息傳入到PrintWriter中,然后將棧軌跡信息穿進trace,然后運用severe方法向Logger寫入信息。(其實書上沒說這個是我猜的)
StringWriter我以前也沒有見過,于是查了查api,用了一下發現挺好玩
一個字符流,可以用其回收在字符串緩沖區中的輸出來構造字符串。
然后試了試這玩意有啥用
public class Test { public static void main(String[] args) { StringWriter str=new StringWriter(); PrintWriter pw=new PrintWriter(str); pw.print("abc"); System.out.println(str.toString()); } }
看起來這個StringWriter就是用來收集各種緩沖區里的字符串的。上面的代碼也就好解釋了。
好回到原來的問題,更常見的情形是,我們需要捕捉與記錄其他人編寫的異常,因此可以在異常處理程序中生成日志信息
package tij.exception; import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.Logger; public class Test { private static Logger logger = Logger.getLogger("LoggingException"); static void logException(Exception e) { StringWriter trace = new StringWriter(); e.printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } public static void main(String[] args) { try { throw new NullPointerException(); } catch (NullPointerException e) { logException(e); } } }
還可以進一步自定義異常,比如加入額外的構造器和成員
package tij.exception; public class Test { static void f() throws MyException{ System.out.println("Throwing MyException from f()"); throw new MyException(); } static void g() throws MyException{ System.out.println("Throwing MyException from g()"); throw new MyException("Originated in g()",47); } public static void main(String[] args) { try{ f(); }catch(MyException e){ e.printStackTrace(); } try{ g(); }catch(MyException e){ e.printStackTrace(); } } } class MyException extends Exception{ private int x; public MyException(){} public MyException(String msg){ super(msg); } public MyException(String msg,int x){ super(msg); this.x=x; } public int val(){ return x; } public String getMessage(){ return "Detail Message: "+x+" "+super.getMessage(); } }五、異常說明
大段文字,沒啥可說
一個簡單的
catch(Exception e)
可以捕獲所有類型的異常,因為Exception是所有與編程相關的異常的父類,但最好把他放在處理列表的末尾。
Exception作為父類自然不會有太多具體信息,但他可以調用從Throwable繼承下來的方法比如
String getMessaget()
獲取異常詳細信息
String getLocalizedMessage
用本地語言表示的異常信息
void printStackTrace()
打印調用棧軌跡,調用棧顯示了“發你帶到異常拋出地點”的方法調用序列,這個方法輸出到標準錯誤流。
void printStackTrace(PrintWriter)
可以選擇輸出的流
void printStackTrace(PrintStream)
可以選擇輸出的流
void fillinStackTrace(PrintStream)
用于在Throwable對象的內部記錄棧幀的當前狀態
package tij.exception; import java.util.Arrays; public class Test { static void f() throws Exception{ System.out.println("Throwing Exception from f()"); throw new Exception(); } public static void main(String[] args) { try{ f(); }catch(Exception e){ System.out.println(Arrays.asList(e.getStackTrace())); e.printStackTrace(); } } }
看起來棧中的一幀指的是一次方法調用啊
2.重新拋出異常當前異常處理程序里也可以重新拋出異常
catch(MyException e){ throw e; }
如果要想把當前的異常對象重新拋出,那再調用printStackTrace方法的時候將是原來異常拋出點的調用棧信息,沒有重新拋出點的信息,要想更新這個信息,可以調用fillInStackTrace方法,這將返回一個Throwable對象,它是通過把當前調用棧信息填入原來那個異常對象而建立的。
package tij.exception; public class Test { static void f() throws Exception { System.out.println("originating the exception from f()"); throw new Exception(); } static void g() throws Exception { try { f(); } catch (Exception e) { System.out.println("Inside g().e.printStackTrace"); e.printStackTrace(System.out); throw e; } } static void h() throws Exception{ try{ f(); }catch(Exception e){ System.out.println("Inside h().e.printStackTrace"); e.printStackTrace(System.out); throw (Exception)e.fillInStackTrace(); } } public static void main(String[] args) { try { g(); } catch (Exception e) { System.out.println("main:printStackTrace()"); e.printStackTrace(System.out); } try { h(); } catch (Exception e) { System.out.println("main:printStackTrace()"); e.printStackTrace(System.out); } } }
對比輸出結果,發現在主程序中的針對h的catch塊中打印棧軌跡的時候,發現她只有兩行,因為他捕捉到的異常其實是(Exception)e.fillInStackTrace(),這其實是一個新返回的異常,它只記錄了自己這個位置的棧信息,因為他是一個新的異常。
恩要注意重新拋出的異常和原來的異常到底是啥關系,很可能就沒啥關系的
在捕獲一個異常后拋出另一個異常,并希望吧原是一場的信息保留下來,這被稱為異常鏈。所有Throwable的子類的構造器可以接受一個cause對象作為參數,cause表示原始異常對象。
package tij.exception; public class Test { public static void main(String[] args) { DynamicFields df = new DynamicFields(3); System.out.println(df); try { df.setField("d", "a value of d"); df.setField("killer47", 47); df.setField("fatkiller48", 48); System.out.println(df); df.setField("d", "a new value of d"); df.setField("thinkiller", 11); System.out.println("df:" + df); System.out.println("df.getField("d")" + df.getField("d")); Object field = df.setField("d", null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (DynamicFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class DynamicFieldException extends Exception {} class DynamicFields { private Object[][] fields; public DynamicFields(int initialSize) { this.fields = new Object[initialSize][2]; for (int i = 0; i < initialSize; i++) { fields[i] = new Object[]{null, null}; } } public String toString() { StringBuilder result = new StringBuilder(); for (Object[] object : fields) { result.append(object[0] + ": " + object[1] + " "); } return result.toString(); } private int hasField(String id) { for (int i = 0; i < fields.length; i++) { if (id.equals(fields[i][0])) return i; } return -1; } private int getFieldNumber(String id) throws NoSuchFieldException { int fieldNum = hasField(id); if (fieldNum == -1) { throw new NoSuchFieldException(); } return fieldNum; } private int makeField(String id) { for (int i = 0; i < fields.length; i++) { if (fields[i][0] == null) { fields[i][0] = id; return i; } } // 如果空間滿了,那就在造一個空間 Object[][] temp = new Object[fields.length + 1][2]; for (int i = 0; i < fields.length; i++) { temp[i] = fields[i]; } temp[fields.length] = new Object[]{null, null}; fields = temp; return makeField(id); } public Object getField(String id) throws NoSuchFieldException { return fields[getFieldNumber(id)][1]; } public Object setField(String id, Object value) throws DynamicFieldException { if (value == null) { DynamicFieldException dfe = new DynamicFieldException(); dfe.initCause(new NullPointerException()); throw dfe; } int fieldNumber = hasField(id); if (fieldNumber == -1) { fieldNumber = makeField(id); } Object result = null; try { result = getField(id); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } fields[fieldNumber][1] = value; return result; } }
其實他就是完成了一個類似于map的數據結構,在
Object field = df.setField("d", null);
這段代碼中,嘗試插入一個value為null的對兒,他拋出了一個DynamicFieldException異常,這個異常是由于NullPointerException引起的,在結果中可以看到,雖然拋出的是DynamicFieldException,但NullPointerException也被記錄了下來
七、Java標準異常Throwable對象可以分為兩類:Error用來表示編譯時和系統錯誤;Exception是可以被拋出的基本類型。
1.特例:RuntimeException運行時異常發生的時候會自動被虛擬機拋出不一定要在異常說明中將它們列出來。
但如果不人工捕獲這種異常,他會穿越所有執行路徑直達main方法。
package tij.exception; public class Test { static void f() { throw new RuntimeException("From f()"); } static void g() { f(); } public static void main(String[] args) { g(); } }
對于這種異常我們程序猿心里要有點B數,不處理的話出錯了全崩了
八、使用finally進行清理對于一些代碼,無論try塊中是否有異常拋出,他們都應該執行。這通常適用于內存回收之外的情況。可以運用finally語句。
package tij.exception; public class Test { static int count = 0; public static void main(String[] args) { while (true) { try { if (count++ == 0) throw new ThreeException(); System.out.println("No Exception"); } catch (ThreeException e) { System.out.println("ThreeException"); } finally { System.out.println("in finally clause"); if (count == 2) break; } } } } class ThreeException extends Exception {}1.finally用來做什么
(額= =Java內存回收機制和構析函數不是一個東西么?)
package tij.exception; public class Test { private static Switch sw = new Switch(); static void f() throws OnOffException1, OnOffException2 {} public static void main(String[] args) { try { sw.on(); f(); } catch (OnOffException1 e) { System.out.println("OnOffException1"); } catch (OnOffException2 e) { System.out.println("OnOffException2"); } finally { sw.off(); } } } class OnOffException1 extends Exception {} class OnOffException2 extends Exception {} class Switch { private boolean state = false; boolean read() { return this.state; } void on() { this.state = true; System.out.println(this); } void off() { this.state = false; System.out.println(this); } public String toString() { return state ? "on" : "off"; } }
可以保證sw最后都是關閉的。
2.在return中使用finallyfinally總會執行,所以一個方法中,可以從多個點返回
package tij.exception; public class Test { static void f(int i) { try { System.out.println("Point 1"); if (i == 1) return; System.out.println("Point 2"); if (i == 2) return; System.out.println("Point 3"); if (i == 3) return; } finally { System.out.println("Performing cleanup"); } } public static void main(String[] args) { for (int i = 1; i <= 4; i++) { f(i); } } }3.異常缺失
異常有時候會被輕易地忽略。
package tij.exception; public class Test { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main(String[] args) { try { Test t = new Test(); try { t.f(); } finally { t.dispose(); } } catch (Exception e) { System.out.println(e); } } } class VeryImportantException extends Exception { public String toString() { return "A very important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trival exception"; } }
還有一種更加容易丟失的異常
package tij.exception; public class Test { @SuppressWarnings("finally") public static void main(String[] args) { try { throw new RuntimeException(); } finally { return; } } }九、異常的限制
package tij.exception; public class Test { public static void main(String[] args) { try { StormyInning si = new StormyInning(); si.atBat(); } catch (PopFoul e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (RainedOut e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (BaseballException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { Inning i = new StormyInning(); i.atBat(); } catch (RainedOut e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Strike e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Foul e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BaseballException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class BaseballException extends Exception {} class Foul extends BaseballException {} class Strike extends BaseballException {} abstract class Inning { public Inning() throws BaseballException {} public void event() throws BaseballException {} public abstract void atBat() throws Strike, Foul; public void walk() {} } class StormException extends Exception {} class RainedOut extends StormException {} class PopFoul extends Foul {} interface Storm { public void event() throws RainedOut; public void rainHard() throws RainedOut; } class StormyInning extends Inning implements Storm { // 對于構造方法來說,你可以添加新的拋出異常,但是你必須也得拋出父類構造方法所聲明的異常 public StormyInning() throws RainedOut, BaseballException {} public StormyInning(String s) throws Foul, BaseballException {} // 普通方法拋出的異常必須必須遵循父類,父類拋啥你拋啥,拋多了也不行,父類不拋你也不許拋,阿父真的很嚴格 // public void walk() throws PopFoul{} // 可以看到接口和父類中有一個相同的方法event,他們拋出了不同的異常,前面說了繼承方法不能多拋異常,所以即使是接口,也不能向父類中已經存在的方法添加新的拋出異常 // public void event() throws RainedOut{} // 但rainHard只在接口中出現了,同樣也不能多拋其他異常 public void rainHard() throws RainedOut {} // 但慶幸的是即使父類或者接口的方法拋異常了,子類重寫的方法可以不拋異常,就是說可以偷懶恩 public void event() {} // 并且子類拋出的異常可以遵循繼承原則,下面這個函數中相當于把Strike異常忽略了,然后拋出了Foul異常的子類PopFoul public void atBat() throws PopFoul {} }
看書,書上這段寫的超棒!
十、構造器class InputFile { private BufferedReader in; InputFile(String fname) throws Exception { try { in = new BufferedReader(new FileReader(fname)); } catch (FileNotFoundException e) { System.out.println("Could not open " + fname); // 這個文件并沒有被成功的打開 throw e; } catch (Exception e) { try { in.close(); } catch (IOException e2) { System.out.println("in沒有被成功關閉"); } throw e; } finally { // 不要關閉這個文件 } } String getLine() { String s; try { s = in.readLine(); } catch (IOException e) { throw new RuntimeException("readLine() failed"); } return s; } void dispose() { try { in.close(); System.out.println("dispose() successful"); } catch (IOException e2) { throw new RuntimeException("in.close() failed"); } } }
從中可以看出,如果in這個對象創建失敗,他會拋出一個創建異常,并且它不需要關閉,因為他根本沒有被成功創建出來;如果in這個對象如果創建成功了,但如果除了其他的岔子,這個in應該被關閉掉,這個對象的構造函數具備了這個功能。
public class Test { public static void main(String[] args) { try { InputFile in = new InputFile("src ijexceptionTest.java"); String s; try { while ((s = in.getLine()) != null) { } } catch (Exception e) { System.out.println("caught Exception in main"); e.printStackTrace(System.out); } finally { in.dispose(); } } catch (Exception e) { System.out.println("InputFile construction failed"); } } }
由于InputFile in這個對象滿足兩個特征:1.構造的時候可能產生異常。2.用完之后需要被清理。因此上面的try-catch嵌套用法是最安全的。因為它保證了:1.如果創建失敗,直接拋出異常,這個對象不需要也不應該執行關閉方法(因此不能傻了吧唧的都把close丟finally塊中)。2.如果創建成功,那么應該保證這個對象在用完之后關閉掉。
package tij.exception; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class Test { public static void main(String[] args) { NeedsCleanup nc1=new NeedsCleanup(); try{ }finally{ nc1.dispose(); } NeedsCleanup nc2=new NeedsCleanup(); NeedsCleanup nc3=new NeedsCleanup(); try{ }finally{ nc3.dispose(); nc2.dispose(); } try { NeedsCleanup2 nc4=new NeedsCleanup2(); try { NeedsCleanup2 nc5=new NeedsCleanup2(); try{ }finally{ nc5.dispose(); } } catch (ConstructionException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ nc4.dispose(); } } catch (ConstructionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class NeedsCleanup{ //我的構造不會出錯 private static long counter=1; private final long id=counter++; public void dispose(){ System.out.println("NeedsCleanup "+id+" disposed"); } } class ConstructionException extends Exception{} class NeedsCleanup2 extends NeedsCleanup{ public NeedsCleanup2() throws ConstructionException{ } }
nc123都不會出錯,而nc45都可能出錯的,上面的方法雖然麻煩,但是可行且可靠。
十一、異常匹配從上到下,遵循繼承
十二、其他可選方式書上這段寫的主要是思想方面的事,回來補,今天累= =馬克
end
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67580.html
摘要:內部類中也可以取得這個外部類對象引用。創建成員內部類對象的時候需要外部類對象。另外在方法中的內部類不能加等權限修飾符,只能加和修飾符。可以在接口內部定義內部類,而且他們即使沒有修飾,也會自動變成的。 Thinking in Java撈干貨,寫筆記 一、成員內部類 1.最基本使用 public class Demo { class Contents{ privat...
摘要:迭代器解決了這個問題。刪除后于是我們可以寫一個方法,接受一個類型,然后讓他調用方法,這就不需要考慮這個是個還是了,也就是說,可以將遍歷容器的操作與序列底層的結構分離,迭代器統一了對容器類的訪問方式。十二和兩種遍歷的方法,與迭代器方法。 一、泛型和類型安全的容器 package tij.hoding; import java.util.ArrayList; public class ...
摘要:但如果導出類還有抽象方法,那這個類還應該加上聲明為抽象類。并且接口具有繼承的一系列特點,如向上轉型等等。接口中的方法是自動是的。 Thinking in Java 好書全是干貨 一、抽象類和抽象方法 抽象方法:這種方法只有聲明而沒有方法體,下面是抽象方法生命所采用的語法 abstract void f(); 包含抽象方法的類叫做抽象類,如果一個類包含一個或多個抽象方法,該類必須被限定為...
摘要:基類導出類導出類繼承了基類的特點,基類和導出類具有相同的基礎接口,形成兩者差異的做法在導出類中添加新方法在導出類型中添加新的接口元素,擴展了接口。覆蓋在導出類用創建方法的新定義,覆蓋基類中的方法定義純粹替代,只覆蓋。 一、抽象過程 建模基于計算機的結構 解空間的解 匯編語言:對底層機器的輕微抽象 命令式語言:匯編語言的抽象 建模基于待解決問題 問題空間的元素 面向對象 二、每個...
摘要:四上的操作看五格式化輸出運用和語言很相似和是等價的喲類格式化說明符轉換六正則表達式網上教程學七掃描輸入新增了類。 一、不可變String String類型的對象是不可變的,所有的改變實際上都是創建了一個新的String對象,另外當String作為傳入參數的時候,其實實際上傳入的是這個引用的一個拷貝,這個方法結束了之后這個傳入的引用也就消失了,原來的那個String不會受到方法內的影響而...
閱讀 430·2024-11-06 13:38
閱讀 809·2024-09-10 13:19
閱讀 937·2024-08-22 19:45
閱讀 1386·2021-11-19 09:40
閱讀 2626·2021-11-18 13:14
閱讀 4291·2021-10-09 10:02
閱讀 2318·2021-08-21 14:12
閱讀 1286·2019-08-30 15:54