<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## [構造器](https://lingcoder.gitee.io/onjava8/#/book/15-Exceptions?id=%e6%9e%84%e9%80%a0%e5%99%a8) 有一點很重要,即你要時刻詢問自己“如果異常發生了,所有東西能被正確的清理嗎?"盡管大多數情況下是非常安全的,但涉及構造器時,問題就出現了。構造器會把對象設置成安全的初始狀態,但還會有別的動作,比如打開一個文件,這樣的動作只有在對象使用完畢并且用戶調用了特殊的清理方法之后才能得以清理。如果在構造器內拋出了異常,這些清理行為也許就不能正常工作了。這意味著在編寫構造器時要格外細心。 你也許會認為使用 finally 就可以解決問題。但問題并非如此簡單,因為 finally 會每次都執行清理代碼。如果構造器在其執行過程中半途而廢,也許該對象的某些部分還沒有被成功創建,而這些部分在 finaly 子句中卻是要被清理的。 在下面的例子中,建立了一個 InputFile 類,它能打開一個文件并且每次讀取其中的一行。這里使用了 Java 標準輸入/輸出庫中的 FileReader 和 BufferedReader 類(將在[附錄:I/O 流](https://lingcoder.gitee.io/onjava8/#/./Appendix-IO-Streams)中討論),這些類的基本用法很簡單,你應該很容易明白: ~~~ // exceptions/InputFile.java // Paying attention to exceptions in constructors import java.io.*; public class InputFile { private BufferedReader in; public InputFile(String fname) throws Exception { try { in = new BufferedReader(new FileReader(fname)); // Other code that might throw exceptions } catch(FileNotFoundException e) { System.out.println("Could not open " + fname); // Wasn't open, so don't close it throw e; } catch(Exception e) { // All other exceptions must close it try { in.close(); } catch(IOException e2) { System.out.println("in.close() unsuccessful"); } throw e; // Rethrow } finally { // Don't close it here!!! } } public String getLine() { String s; try { s = in.readLine(); } catch(IOException e) { throw new RuntimeException("readLine() failed"); } return s; } public void dispose() { try { in.close(); System.out.println("dispose() successful"); } catch(IOException e2) { throw new RuntimeException("in.close() failed"); } } } ~~~ InputFile 的構造器接受字符串作為參數,該字符串表示所要打開的文件名。在 try 塊中,會使用此文件名建立 FileReader 對象。FileReader 對象本身用處并不大,但可以用它來建立 BufferedReader 對象。注意,使用 InputFile 的好處之一是把兩步操作合而為一。 如果 FileReader 的構造器失敗了,將拋出 FileNotFoundException 異常。對于這個異常,并不需要關閉文件,因為這個文件還沒有被打開。而任何其他捕獲異常的 catch 子句必須關閉文件,因為在它們捕獲到異常之時,文件已經打開了(當然,如果還有其他方法能拋出 FileNotFoundException,這個方法就顯得有些投機取巧了。這時,通常必須把這些方法分別放到各自的 try 塊里),close() 方法也可能會拋出異常,所以盡管它已經在另一個 catch 子句塊里了,還是要再用一層 try-catch,這對 Java 編譯器而言只不過是多了一對花括號。在本地做完處理之后,異常被重新拋出,對于構造器而言這么做是很合適的,因為你總不希望去誤導調用方,讓他認為“這個對象已經創建完畢,可以使用了”。 在本例中,由于 finally 會在每次完成構造器之后都執行一遍,因此它實在不該是調用 close() 關閉文件的地方。我們希望文件在 InputFlle 對象的整個生命周期內都處于打開狀態。 getLine() 方法會返回表示文件下一行內容的字符串。它調用了能拋出異常的 readLine(),但是這個異常已經在方法內得到處理,因此 getLine() 不會拋出任何異常。在設計異常時有一個問題:應該把異常全部放在這一層處理;還是先處理一部分,然后再向上層拋出相同的(或新的)異常;又或者是不做任何處理直接向上層拋出。如果用法恰當的話,直接向上層拋出的確能簡化編程。在這里,getLine() 方法將異常轉換為 RuntimeException,表示一個編程錯誤。 用戶在不再需要 InputFile 對象時,就必須調用 dispose() 方法,這將釋放 BufferedReader 和/或 FileReader 對象所占用的系統資源(比如文件句柄),在使用完 InputFile 對象之前是不會調用它的。可能你會考慮把上述功能放到 finalize() 里面,但我在[封裝](https://lingcoder.gitee.io/onjava8/#/./Housekeeping)講過,你不知道 finalize() 會不會被調用(即使能確定它將被調用,也不知道在什么時候調用),這也是 Java 的缺陷:除了內存的清理之外,所有的清理都不會自動發生。所以必須告訴客戶端程序員,這是他們的責任。 對于在構造階段可能會拋出異常,并且要求清理的類,最安全的使用方式是使用嵌套的 try 子句: ~~~ // exceptions/Cleanup.java // Guaranteeing proper cleanup of a resource public class Cleanup { public static void main(String[] args) { try { InputFile in = new InputFile("Cleanup.java"); try { String s; int i = 1; while((s = in.getLine()) != null) ; // Perform line-by-line processing here... } 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"); } } } ~~~ 輸出為: ~~~ dispose() successful ~~~ 請仔細觀察這里的邏輯:對 InputFile 對象的構造在其自己的 try 語句塊中有效,如果構造失敗,將進入外部的 catch 子句,而 dispose() 方法不會被調用。但是,如果構造成功,我們肯定想確保對象能夠被清理,因此在構造之后立即創建了一個新的 try 語句塊。執行清理的 finally 與內部的 try 語句塊相關聯。在這種方式中,finally 子句在構造失敗時是不會執行的,而在構造成功時將總是執行。 這種通用的清理慣用法在構造器不拋出任何異常時也應該運用,其基本規則是:在創建需要清理的對象之后,立即進入一個 try-finally 語句塊: ~~~ // exceptions/CleanupIdiom.java // Disposable objects must be followed by a try-finally class NeedsCleanup { // Construction can't fail 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 { // Construction can fail: NeedsCleanup2() throws ConstructionException {} } public class CleanupIdiom { public static void main(String[] args) { // [1]: NeedsCleanup nc1 = new NeedsCleanup(); try { // ... } finally { nc1.dispose(); } // [2]: // If construction cannot fail, // you can group objects: NeedsCleanup nc2 = new NeedsCleanup(); NeedsCleanup nc3 = new NeedsCleanup(); try { // ... } finally { nc3.dispose(); // Reverse order of construction nc2.dispose(); } // [3]: // If construction can fail you must guard each one: try { NeedsCleanup2 nc4 = new NeedsCleanup2(); try { NeedsCleanup2 nc5 = new NeedsCleanup2(); try { // ... } finally { nc5.dispose(); } } catch(ConstructionException e) { // nc5 const. System.out.println(e); } finally { nc4.dispose(); } } catch(ConstructionException e) { // nc4 const. System.out.println(e); } } } ~~~ 輸出為: ~~~ NeedsCleanup 1 disposed NeedsCleanup 3 disposed NeedsCleanup 2 disposed NeedsCleanup 5 disposed NeedsCleanup 4 disposed ~~~ * \[1\] 相當簡單,遵循了在可去除對象之后緊跟 try-finally 的原則。如果對象構造不會失敗,就不需要任何 catch。 * \[2\] 為了構造和清理,可以看到將具有不能失敗的構造器的對象分組在一起。 * \[3\] 展示了如何處理那些具有可以失敗的構造器,且需要清理的對象。為了正確處理這種情況,事情變得很棘手,因為對于每一個構造,都必須包含在其自己的 try-finally 語句塊中,并且每一個對象構造必須都跟隨一個 try-finally 語句塊以確保清理。 本例中異常處理的混亂情形,有力的論證了應該創建不會拋出異常的構造器,盡管這并不總會實現。 注意,如果 dispose() 可以拋出異常,那么你可能需要額外的 try 語句塊。基本上,你應該仔細考慮所有的可能性,并確保正確處理每一種情況。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看