検索

キーワード


【Java】例外構文 3種類の活用方法解説

  • 公開日:2021-01-26 08:21:20
  • 最終更新日:2021-01-26 08:19:32
【Java】例外構文 3種類の活用方法解説

こんにちは、駆け出しエンジニアの伊藤です。

東京ITカレッジのJava研修で学んだ内容を復習も兼ねて記事にしたいと思います。

今回は、Javaの例外基本構文、try-with-resource構文、java.lang.AutoCloseable インターフェイス、java.io.Closeable インターフェイスなどについて解説していきます。

Javaやプログラムについて勉強し始めた方の参考になれば幸いです!

(eclipseを使用して計算を行っています)

また、この記事に関して、例外の概要や try-catch、マルチキャッチ等について知っている方を対象としています。

関連記事にて、例外の概要や try-catch について詳しく解説していますので、理解を深めたい方はご一読ください。


関連記事:【Java】例外クラスの概要や使用実例を解説‐Exception、RuntimeException、Errorなど

     【Java】例外クラスの制御方法 3種類の基本と使い方を解説

     【Java】例外制御、例外の活用方法入門

     【Java】独自例外を作るには?作成方法を解説




try-with-resources 構文

try-catch-finally 構文を使用することで、プログラムで繋げたネットワークの切断、接続したデータベースの開放やファイルのクローズなど、例外発生の有無に関わらず必ず実行しなければならないプログラムを必ず処理できるようになります。


try-catch-finally構文を使った、テキストファイルを1行ずつ読み込み表示をするサンプルプログラムです。

(このサンプルプログラムは、事前に【Java】例外クラスの制御方法 3種類の基本と使い方を解説 にあるサンプルプログラムIOExceptionサンプルを実行した後に実行することで、実行結果が出力されます)

 1 // ToStringFinallySample
 2 package exception.gaiyou;
 3
 4 import java.io.BufferedReader;
 5 import java.io.File;
 6 import java.io.FileNotFoundException;
 7 import java.io.FileReader;
 8 import java.io.IOException;
 9
10 public class ToStringFinallySample {
11     public static void main(String[] args) {
12         System.out.println("-----実行結果-----");
13         BufferedReader br = null;
14         File file = new File("c:\\Sample\\Hello.txt");
15         // closeメソッドを自動的に呼び出し
16         try {
17             FileReader fr = new FileReader(file);
18             br = new BufferedReader(fr);
19             //
20             String date;
21             //ファイルの中身を出力
22             while((date = br.readLine()) != null) {
23                 System.out.println(date);
24             }
25         } catch(FileNotFoundException e) {
26             System.out.println("指定したファイルが存在しない、もしくは開くことができないため例外が発生しました。");
27             e.printStackTrace();
28         } catch(IOException e) {
29             System.out.println("入出力処理の失敗、または割り込みの発生により例外が発生しました。");
30             e.printStackTrace();
31         } finally {
32             if (br != null) {
33                 try {
34                     br.close();
35                 } catch (IOException e) {
36                     e.printStackTrace();
37                 }
38             }
39         }
40     }
41 }


-----実行結果-----
Hello 
Bonjour 
こんにちは


finallyブロックで、ファイルの close() への対応が確実に実行されるようになりました。

しかしtry-catch-finally 構文は、プログラム自体が冗長であったり、記述忘れなどが起こることがあります。


その為、JDK7から新たな構文が用意されました。


それが、「try-with-resources」です。


<try-with-resources 構文>

try (例外発生の有無に関わらず必ず実行しなければならない変数宣言) {
	例外が発生する可能性があるプログラム
} catch (例外クラス 変数名) {
	例外が発生した場合に実行されるプログラム
}


実際にサンプルプログラムで見てみましょう。

 1 //try-with-resources構文サンプル
 2 package exception.gaiyou;
 3
 4 import java.io.BufferedReader;
 5 import java.io.File;
 6 import java.io.FileNotFoundException;
 7 import java.io.FileReader;
 8 import java.io.IOException;
 9
10 public class ToStringResourcesSample {
11     public static void main(String[] args) {
12         System.out.println("-----実行結果-----");
13         File file = new File("c:\\Sample\\Hello.txt");
14         // closeメソッドを自動的に呼び出し
15         try (FileReader fr = new FileReader(file); BufferedReader br = new BufferedReader(fr)){
16             //
17             String date;
18             //ファイルの中身を1行ずつ出力
19             while((date = br.readLine()) != null) {
20                 System.out.println(date);
21             }
22         } catch(FileNotFoundException e) {
23             System.out.println("指定したファイルが存在しない、もしくは開くことができないため例外が発生しました。");
24             e.printStackTrace();
25         } catch(IOException e) {
26             System.out.println("入出力処理の失敗、または割り込みの発生により例外が発生しました。");
27             e.printStackTrace();
28         }
29     }
30 }


-----実行結果-----
Hello 
Bonjour 
こんにちは


finallyブロックを記述しなくても、try の後に例外発生の有無に関わらず必ず実行しなければならない変数の宣言(リソース)を記述することで、tryブロックを抜ける際、コンパイラによって自動的に closeメソッドが呼び出され処理されます。


ToStringFinallySampleと、ToStringResourcesSampleは両プログラムともテキストファイルを1行ずつ読み込み表示をするプログラムです。

2つのサンプルプログラムの違いは、finallyブロックを記述するかしないかです。

try-with-resources構文サンプルは、簡潔に書かれていることがよく分かると思います。


また、リソースが複数ある場合はセミコロンで区切り記述をします。

そして、複数あるリソースの close() される順番は、定義の逆からとなります。

try-with-resources構文サンプルで言うと、BufferedReader の br が1番に close() され、FileReader の fr が2番目に close() されます。

もし、close() の処理中に例外が発生した場合は例外の伝播が発生します。


try-with-resources構文を使用する際には以下のことに気を付ける必要があります。

・try の後に宣言する変数は、Java.lang.AutoCloseable インタフェースか、
  java.io.Closeable インターフェイスを実装しているクラスのみ


プログラムで繋げたネットワークの切断、接続したデータベースの開放やファイルのクローズなどに用いるAPIの多くは、AutoCloseable を実装しているので、try-with-resource 構文を使って簡潔に記述することができます。


関連記事:【Java】例外クラスの制御方法 3種類の基本と使い方を解説




java.lang.AutoCloseable インターフェイス

AutoCloseable インターフェイスとは、JDK7から導入されたもので、try-with-resources構文で宣言した時に、リソースが必要なくなるまで(try-with-resources構文の終了まで)リソースを保持できるオブジェクトです。


AutoCloseable で用意されているメソッドは、closeメソッドです。

try-with-resources で書かれたプログラムでは、tryブロックを抜けた後、自動的にこの closeメソッドが呼び出され実行されるのです。

try-with-resources構文でAutoCloseableインターフェースを実装するクラスを使用することで、自動的にリソースを開放することが出来ます。


AutoCloseable を実装しているクラスの一例

・AbstractSelector
・BufferedReader
・BufferedWriter
・CharArryaReader
・FileLock
・FileWriter
・InputStream
・XMLDecoder


これは、一部です。より詳しく知りたい方は、JavaDoc の インタフェースAutoCloseable をご覧ください。

AutoCloseable を実装しているクラスは、ファイルやソケット・ハンドル等の操作に関わるクラスがほとんどです。


また、AutoCloseable インタフェースの closeメソッドは Exception 系の例外をスローします。



関連記事:インタフェースAutoCloseable(JavaDoc)




java.io.Closeable インターフェイス

Closeable インターフェイスは、JDK5から導入されていて、閉じることができるデータ(ファイルやソケット・ハンドル等)のオブジェクトが保持しているリソースを解放するために呼び出されるオブジェクトです。


Closeable インタフェースは AutoCloseable インタフェースを拡張したもので、Closeable は、AutoCloseable を継承しています。

用意されているメソッドは closeメソッドで、ファイルの入出力等のストリームを閉じて、それに関連するすべてのシステムやリソースの開放ができます。


Closeable を実装しているクラスの一例

・AbstractSelector
・BufferedReader
・BufferedWriter
・CharArryaReader
・FileWriter
・InputStream


これは、一部です。より詳しく知りたい方は、JavaDoc の インタフェースCloseable(JavaDoc)をご覧ください。

AutoCloseable を継承しているので、Closeable を実装しているクラスは AutoCloseable と同じクラスが多くなっています。


また、Closeable インタフェースの closeメソッドは IOException系の例外をスローします。


関連記事:インタフェースCloseable(JavaDoc)




実装例(簡単な実例)

実際に、AutoCloseableインタフェース と Closeableインタフェース を実装したサンプルプログラムを見てみましょう。


 1 //InterfaceSample
 2 package exception.gaiyou;
 3
 4 import java.io.Closeable;
 5 import java.io.IOException;
 6
 7 public class InterfaceSample {
 8     public static void main(String[] args) {
 9         //AutoCloseable と Closeable をインスタンス化
10         try (AutoCloseable ac = new AutoCloseableTest();
11                         Closeable close = new CloseableTest()) {
12             System.out.println("-----実行結果-----");
13             System.out.println("tryブロックを実行");
14             System.out.println("tryブロックの処理を終了 ");
15         } catch (IOException e) {
16             System.out.println("catchブロックで例外キャッチ");
17             e.getStackTrace();
18         } catch (Exception e) {
19             System.out.println("catchブロックで例外キャッチ");
20             e.getStackTrace();
21         }
22     }
23 }
24 // AutoCloseable 実装したクラス
25 class AutoCloseableTest implements AutoCloseable {
26     @Override
27     public void close() throws Exception {
28         System.out.println("2:AutoCloseableTest の close処理");
29     }
30 }
31 // Closeable を実装したクラス
32 class CloseableTest implements Closeable {
33     @Override
34     public void close() throws IOException {
35         System.out.println("1:CloseableTest の close処理");
36     }
37 }


-----実行結果-----
tryブロックを実行
tryブロックの処理を終了 
1:CloseableTest の close処理
2:AutoCloseableTest の close処理


InterfaceSample では、AutoCloseableインタフェース と Closeableインタフェースを実装した各クラスのcloseメソッドが、try-with-resource構文の中で自動的に呼び出されていることが確認できます。

tryブロックの処理を終了した後、closeメソッドの処理が行われていることが実行結果から分かります。

また、try-with-resources のところでも解説した「複数あるリソースの close() される順番は、定義の逆からとなります」という意味が分かったと思います。実行結果を見ると、リソースの定義した逆から表示されています。


関連記事:アノテーションについて



実装例(例外発生時の説明)

それでは、tryブロック内で例外が発生すると、処理はどのような順に進むでしょうか?

次のInterfaceSample2プログラムでは、tryブロック内でわざと例外が発生するように記述してあります。

それ以外は、InterfaceSampleプログラムと全く同じことをしています。


 1 //InterfaceSample2
 2 package exception.gaiyou;
 3
 4 import java.io.Closeable;
 5 import java.io.IOException;
 6
 7 public class InterfaceSample2 {
 8     public static void main(String[] args) {
 9         //AutoCloseable と Closeable をインスタンス化
10         try (AutoCloseable ac = new AutoCloseableTest2();
11                         Closeable close = new CloseableTest2()) {
12             System.out.println("-----実行結果-----");
13             System.out.println("tryブロックを実行");
14             System.out.println("tryブロックの処理を終了 ");
15             //わざと例外を発生させる
16             throw new Exception();
17         } catch (IOException e) {
18             System.out.println("catchブロックでIOException例外キャッチ");
19             e.printStackTrace();
20         } catch (Exception e) {
21             System.out.println("catchブロックでException例外キャッチ");
22             e.printStackTrace();
23         }
24     }
25 }
26 // AutoCloseable 実装したクラス
27 class AutoCloseableTest2 implements AutoCloseable {
28     @Override
29     public void close() throws Exception {
30         System.out.println("2:AutoCloseableTest の close処理");
31     }
32 }
33 // Closeable を実装したクラス
34 class CloseableTest2 implements Closeable {
35     @Override
36     public void close() throws IOException {
37         System.out.println("1:CloseableTest の close処理");
38     }
39 }


-----実行結果-----
tryブロックを実行
tryブロックの処理を終了 
1:CloseableTest の close処理
2:AutoCloseableTest の close処理
catchブロックでException例外キャッチ
java.lang.Exception
	at exception.gaiyou.InterfaceSample2.main(InterfaceSample2.java:16)


実行結果から、tryブロック内で例外が発生すると、例外を知らせる catchブロックの処理の前にcloseメソッドの処理が行われています。


try-catch-finally構文で例外発生した際の処理順が変わってくるところがポイントです。

・try-catch-finally 構文で例外の発生がある場合 : 
            tryブロックの処理 → catchブロックの処理 → finallyブロックの処理

・try-with-resources 構文で例外の発生がある場合 : 
            tryブロックの処理 → closeメソッドの処理 → catchブロックの処理


InterfaceSample2では、tryブロックの処理が終了すると、CloseableTest2 の closeメソッド → AutoCloseableTest2 の closeメソッドの処理が行われ、その後に catchブロックに処理が移行する流れになっています。


関連記事:【Java】例外クラスの制御方法 3種類の基本と使い方を解説

     アノテーションについて




抑制された例外

抑制された例外とは、try-with-resources 構文を使って、自動的に呼び出された closeメソッドで例外が発生した場合、スローされるはずの closeメソッドの例外が抑制(無視)されることがあります。


<抑制された例外の動きの例>

・try ブロックで例外が発生し、さらに自動的に呼び出される closeメソッドで例外が発生した場合 → closeメソッドの例外が抑制される

・try ブロックで例外が発生せずに正常に closeメソッドへ移行後、自動的に複数の closeメソッドが呼び出され、それぞれの closeメソッドで例外が発生した場合 → 最初の closeメソッド例外は通知され、それ以降の close() メソッドの例外は抑制される


また抑制された例外の動きで、複数の closeメソッドがあった場合、

try ( a() ; b() ; c()) {
	……

上記の a b c では、c から close処理がされるので、c で例外が発生した場合はその後の a b は無視されるということです。


また、抑制された例外(close() で発生した例外)を知りたい場合は、Throwable.getSuppressedメソッドを利用して、スローされた例外から取得することができます。


プログラムを実行し、一番初めに発生した例外のみがスローされ、getSuppressedメソッドで他にどんな例外が発生していたかを知ることができる、ということです。


実際にサンプルプログラムを見てみましょう。

 1 // SuppressedSample - 抑制された例外取得サンプル
 2 package exception.gaiyou;
 3
 4 import java.io.Closeable;
 5
 6 public class SuppressedSample {
 7     public static void main(String[] args) {
 8         -----実行結果-----
 9         //AutoCloseable と Closeable をインスタンス化
10         try (AutoCloseable acs = new AutoClosableSample(); Closeable cs = new ClosableSample()) {
11             System.out.println("try 内処理");
12             throw new Exception("try 内からスローされた例外");
13         } catch (Exception e) {
14             System.out.println("例外をキャッチ:" + e.getMessage());
15             // Throwable.getSuppressedメソッドを利用して、スローされた例外から 抑制された例外を取得
16             Throwable[] tt = e.getSuppressed();
17             System.out.println("抑制された例外の数:" + tt.length);
18             for (Throwable t : tt) {
19                 System.out.println("抑制された例外:" + t.getMessage());
20             }
21         }
22     }
23 }


 1 // SuppressedSample でインスタンス化するクラス定義
 2 package exception.gaiyou;
 3
 4 import java.io.Closeable;
 5 import java.io.IOException;
 6
 7 // AutoCloseable 実装したクラス
 8 class AutoClosableSample implements AutoCloseable {
 9     @Override
10     public void close() throws Exception {
11         throw new Exception("AutoClosableSample#close() でスローされた例外");
12     }
13 }
14
15 // Closeable 実装したクラス
16 class ClosableSample implements Closeable {
17     @Override
18     public void close() throws IOException {
19         throw new IOException("ClosableSample#close() でスローされた例外");
20     }
21 }


-----実行結果-----
try 内処理
例外をキャッチ:try 内からスローされた例外
抑制された例外の数:2
抑制された例外:ClosableSample#close() でスローされた例外
抑制された例外:AutoClosableSample#close() でスローされた例外


関連記事:【Java】例外クラスの制御方法 3種類の基本と使い方を解説

     アノテーションについて



まとめ

try-with-resources構文について、使い方や例外が発生した際のプログラムの処理順等、分かったでしょうか?

自動的にリソースを開放できて、プログラムを簡潔に書くことができるので、覚えると使いやすいでしょう。

関連記事:【Java】例外クラスの概要や使用実例を解説‐Exception、RuntimeException、Errorなど

     【Java】例外クラスの制御方法 3種類の基本と使い方を解説

     【Java】例外制御、例外の活用方法入門

     【Java】独自例外を作るには?作成方法を解説



【著者】

伊藤

Javaを研修で3か月学んだ、駆け出しのエンジニアです。
現在は、ベンダー資格を取得するため、勉強を日課にできるよう努力中です。

よく読まれている記事
【Java】JSPでタグライブラリを使う(JSTL)

【Java】JSPでタグライブラリを使う(JSTL)

こんにちは。エンジニアの新田です!ここでは、システムエンジニアとして働いている私が、システム開発手法や開発言語について紹介していこうと思います。今回は、JSPの標準タグライブラリ「JSTL」について紹介します。Javaについて勉強している方、Webアプリケーションを構築したいと思っている方の参考になれば幸いです!関連記事リンク: 【Java】JSPの基本的な構文/【Java】JSPのアクションタグ

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!そもそもStringとは何?『 String 』... Java言語において文字列のデータ型を指します。基本デ

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!Stringクラスformatメソッドの文字列整形【java.utilパッケージ】Formatterクラスfo

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者にも✅、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!文字列を扱う3つのクラス【java.langパッケージ】java.langパッケージの文字列を扱うクラスにはS

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!プリミティブ型と参照型プログラム開発では型を持った変数を使ってデータのやり取りをしますが、データ型によって仕様