“朋友,十小屋!”
“我很高興學習Java,船長!”
“放心,阿米戈。今天我們有一個超級有趣的話題。我們將討論Java程序如何與外部資源交互,我們將研究一個非常有趣的Java語句。最好不要摀住耳朵。”
“我洗耳恭聽。”
“當 Java 程序運行時,有時它會與 Java 機器外部的實體進行交互。例如,與磁盤上的文件進行交互。這些實體通常稱為外部資源。”
“那什麼才算是內部資源?”
“內部資源是在 Java 機器內部創建的對象。通常,交互遵循以下方案:
“操作系統嚴格跟踪可用資源,並控制不同程序對它們的共享訪問。例如,如果一個程序更改文件,則另一個程序不能更改(或刪除)該文件。這個原則不是僅限於文件,但它們提供了最容易理解的示例。
“操作系統具有允許程序獲取和/或釋放資源的功能(API)。如果資源繁忙,則只有獲取它的程序才能使用它。如果資源空閒,則任何程序都可以獲取它。
“想像一下,一個辦公室共用咖啡杯。如果有人拿了一個杯子,其他人就不能再拿了。但是一旦杯子用完、洗乾淨並放回原處,那麼任何人都可以再拿一次。”
“知道了,就像地鐵或者其他公共交通工具的座位一樣,如果有空位,誰都可以坐,如果有人坐,就由坐的人控制。”
“是的。現在我們來談談獲取外部資源的問題。每次你的 Java 程序開始使用磁盤上的文件時,Java 機器都會向操作系統請求獨占訪問它。如果資源是空閒的,那麼 Java 機器就會獲取它。
“但是當你處理完這個文件之後,這個資源(文件)必須被釋放,即你需要通知操作系統你不再需要它。如果你不這樣做,那麼這個資源將繼續被釋放由你的程序持有。”
“聽起來很公平。”
“為了保持這種狀態,操作系統維護了一個每個正在運行的程序佔用的資源列表。如果你的程序超過了分配的資源限制,那麼操作系統將不再給你新的資源。
“這就像可以吃掉所有內存的程序......”
“類似的東西。好消息是,如果您的程序終止,所有資源都會自動釋放(操作系統本身會這樣做)。”
“如果這是好消息,是否意味著有壞消息?”
“正是這樣。壞消息是,如果您正在編寫服務器應用程序……”
“但是我會寫這樣的應用程序嗎?”
“很多服務器應用程序都是用 Java 編寫的,因此您編寫它們很可能是為了工作。正如我所說,如果您正在編寫服務器應用程序,那麼您的服務器需要不間斷地運行數天、數週、數月, ETC。”
“換句話說,程序不會終止,這意味著內存不會自動釋放。”
“完全正確。如果你每天打開 100 個文件並且不關閉它們,那麼幾週後你的應用程序將達到其資源限制並崩潰。”
“這還差幾個月的穩定工作呢!有什麼辦法?”
“使用外部資源的類有一種釋放它們的特殊方法:close()
.
“這是一個程序示例,它向文件寫入內容,然後在完成後關閉文件,即釋放操作系統的資源。它大致如下所示:
代碼 | 筆記 |
---|---|
|
文件的路徑。 獲取文件對象:獲取資源。 寫入文件 關閉文件 - 釋放資源 |
“啊……所以,在處理文件(或其他外部資源)後,我必須調用鏈接close()
到外部資源的對像上的方法。”
“是的,這一切看起來很簡單。但是程序運行時可能會出現異常,外部資源不會被釋放。”
“那很糟糕。怎麼辦?”
“為了確保close()
始終調用該方法,我們需要將我們的代碼包裝在一個--try
塊中並將該方法添加到該塊中。它看起來像這樣:catch
finally
close()
finally
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
“嗯……這裡有什麼不對嗎?”
“對。這段代碼不會編譯,因為output
變量是在try{}
塊內聲明的,因此在finally
塊中是不可見的。
讓我們修復它:
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
“現在一切都好嗎?”
“沒關係,但如果我們在創建對象時發生錯誤,它就不會起作用FileOutputStream
,而且很容易發生這種情況。
讓我們修復它:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
“現在一切正常了嗎?”
“還有一些批評。首先,如果在創建對象時發生錯誤FileOutputStream
,則output
變量將為空。必須在塊中考慮這種可能性finally
。
”其次,該close()
方法總是在finally
塊中調用,這意味著它在塊中沒有必要try
。最終代碼將如下所示:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (output!=null)
output.close();
}
“即使我們不考慮catch
塊,可以省略,那麼我們的3行代碼就變成了10行。但我們基本上只是打開文件並寫入1。”
“呼……總算把事情搞定了,比較好理解,但也有些乏味,不是嗎?”
“就是這樣。這就是為什麼 Java 的創建者通過添加一些語法糖來幫助我們。現在讓我們繼續討論程序的亮點,或者更確切地說,本課:
try
-有資源
“從第 7 版開始,Java 有一個新的try
-with-resources 聲明。
“它的創建正是為了解決強制調用方法的問題close()
。”
“聽起來很有希望!”
“一般情況看起來很簡單:
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
“所以這是try
聲明的另一種變體?”
“是的。你需要在關鍵字後面加上括號try
,然後在括號內創建帶有外部資源的對象。對於括號中的每個對象,編譯器都會添加一個finally
段和對方法的調用close()
。
“下面是兩個等價的例子:
長碼 | 使用 try-with-resources 編寫代碼 |
---|---|
|
|
“太棒了!使用 -with-resources 的代碼try
更短且更易於閱讀。我們擁有的代碼越少,出現拼寫錯誤或其他錯誤的可能性就越小。”
“我很高興你喜歡它。順便說一下,我們可以在 -with-resources 聲明中添加catch
和finally
阻止try
。或者如果不需要它們,你可以不添加它們。
多個變量同時出現
“你可能經常會遇到需要同時打開多個文件的情況。假設你正在復制一個文件,那麼你需要兩個對象:你從中復制數據的文件和你要復制數據的文件.
“在這種情況下,try
-with-resources 語句允許您在其中創建一個但多個對象。創建對象的代碼必須用分號分隔。這是此語句的一般外觀:
try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
Code that works with the name and name2 variables
}
複製文件示例:
短代碼 | 長碼 |
---|---|
|
|
“好吧,我們能在這裡說什麼?-try
有資源是一件很棒的事情!”
“我們能說的是我們應該使用它。”
GO TO FULL VERSION