検索

キーワード


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

  • 公開日:2021-01-26 08:21:20
  • 最終更新日:2021-01-26 08:19:20
【Java】例外制御、例外の活用方法入門

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

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

今回は、Javaの例外制御について、throws、throw、printStackTrace()、getMessage()、getCause()などを使った制御方法について解説していきます。

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

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

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

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


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

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

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

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



例外制御、例外処理について(検査例外)

Javaでは、プログラム実行中に例外が発生する可能性がある場合、例外発生時の処理をプログラムに記述しないと、コンパイルエラーが発生するようになっています。このような例外を検査例外と呼びます。(検査例外については、関連記事に解説があります)。

その為、例外発生の可能性がある場合に、例外処理を記述するための構文が用意されています。


例外処理は、以下のいずれかの方法で記述する必要があります。

try - catch でキャッチする

throws句で例外を宣言する(呼び出し側で try-catch を実装する場合)




例外の伝播

例外の発生が考えられる場合は、例外処理を記述しますが、呼び出し先のメソッドで例外が発生し、そこで適切に例外処理が行われず、呼び出し側に例外が返される場合があります。

このように呼び出し側のメソッドにまで影響が及ぶことを、「例外の伝播」と言います。


検査例外(Exceptionクラス系)は、mainメソッドもしくは、呼び出し先の各メソッドで try-catch 構文を使って適切に例外処理する必要があります。

しかし、呼び出し先の各メソッドで例外処理を行わず、呼び出し側に例外処理を一任したい場合、メソッド宣言の後に throws を記述することで、呼び出し側へと例外を投げる(スローする)ことができます。(この場合、検査例外によるコンパイルエラーは発生しない)


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



throws

throws を宣言することで、例外をそのメソッド内では処理せず、呼び出し側のメソッドに例外を投げる(スローする)ことができます。


<throws 構文>

アクセス修飾子 戻り値 メソッド名() throws 例外クラス {
    メソッド内容
}


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

 1 // throws構文サンプル
 2 package exception.gaiyou;
 3
 4 import java.io.File;
 5 import java.io.FileWriter;
 6 import java.io.IOException;
 7
 8 public class ThrowsSample {
 9
10     public static void main(String[] args) {
11         try {
12             //呼び出し先メソッド名
13             sample();
14         //呼び出し先メソッドで例外が発生すると、ここに移行
15         } catch(IOException e) {
16             System.out.println("ファイルもしくは入出力処理の失敗、または割り込みの発生により例外が発生しました。");
17         }
18     }
19
20     // throws宣言
21     private static void sample() throws IOException {
22         System.out.println("-----実行結果-----");
23         FileWriter fw = null;
24         File file = new File("c:\\Sample\\Hello.txt");
25         fw = new FileWriter(file);
26         System.out.println("ファイルの作成に成功しました。");
27         //ファイルの書き込み
28         fw.write("Hello World \n");
29         fw.write("こんにちは!");
30         System.out.println("ファイルの書き込みに成功しました。");
31         //ファイルを閉じる
32         if (fw != null) {
33             fw.close();
34             System.out.println("正常にファイルを閉じました。");
35         }
36     }
37 }


-----実行結果-----
ファイルの作成に成功しました。
ファイルの書き込みに成功しました。
正常にファイルを閉じました。


throws構文サンプルでは 、sampleメソッドで「ファイルを作成→書き込み→ファイルを閉じる」ということを行っています。

sampleメソッドの宣言後に、throws を記述したことで検査例外のコンパイルエラーが起こらず、呼び出し側の mainメソッドに例外処理が任されています。


もしも、呼び出し先メソッドで throws を記述せず、mainブロックで try-catch をしなかった場合、以下のようになります。

 1 // throws エラーサンプル
 2 package exception.gaiyou;
 3
 4 import java.io.File;
 5 import java.io.FileWriter;
 6
 7 public class ThrowsSample2 {
 8
 9     public static void main(String[] args) {
10         //メソッド呼び出し
11         sample();
12     }
13
14     // throws宣言
15     private static void sample() {
16         System.out.println("-----実行結果-----");
17         FileWriter fw = null;
18         File file = new File("c:\\Sample\\Hello.txt");
19         fw = new FileWriter(file);
20         System.out.println("ファイルの作成に成功しました。");
21         //ファイルの書き込み
22         fw.write("Hello World \n");
23         fw.write("こんにちは!");
24         System.out.println("ファイルの書き込みに成功しました。");
25         //ファイルを閉じる
26         if (fw != null) {
27             fw.close();
28             System.out.println("正常にファイルを閉じました。");
29         }
30     }
31 }


Exception in thread "main" java.lang.Error: Unresolved compilation problems: 

	処理されない例外の型 IOException
	処理されない例外の型 IOException
	処理されない例外の型 IOException
	処理されない例外の型 IOException

	at exception.gaiyou.ThrowsSample2.sample(ThrowsSample2.java:19)
	at exception.gaiyou.ThrowsSample2.main(ThrowsSample2.java:11)

検査例外を記述していないため、コンパイルエラーが発生してしまいます。


次のサンプルプログラムは、throws 宣言はしていますが、呼び出し側で try-catch していないものです。

 1 // try-catch エラーサンプル
 2 package exception.gaiyou;
 3
 4 import java.io.File;
 5 import java.io.FileWriter;
 6 import java.io.IOException;
 7
 8 public class ThrowsSample3 {
 9
10     public static void main(String[] args) {
11         //呼び出し先メソッド名
12         sample();
13     }
14
15     // throws宣言
16     private static void sample() throws IOException {
17         System.out.println("-----実行結果-----");
18         FileWriter fw = null;
19         File file = new File("c:\\Sample\\Hello.txt");
20         fw = new FileWriter(file);
21         System.out.println("ファイルの作成に成功しました。");
22         //ファイルの書き込み
23         fw.write("Hello World \n");
24         fw.write("こんにちは!");
25         System.out.println("ファイルの書き込みに成功しました。")>;
26         //ファイルを閉じる
27         if (fw != null) {
28             fw.close();
29             System.out.println("正常にファイルを閉じました。");
30         }
31     }
32 }


Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	処理されない例外の型 IOException

	at exception.gaiyou.ThrowsSample3.main(ThrowsSample3.java:12)

mainメソッドで、コンパイルエラーが発生しています。

呼び出し先で throws で例外を投げる場合、呼び出し側では try-catch で例外をキャッチしなくてはいけません。

また、throws 宣言後の例外クラスはカンマで区切って複数指定することも可能です。


関連記事:【Java】例外クラスの概要や使用実例を解説 ‐検査例外・非検査例外など

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

     java.io.Fileクラスの使い方(ファイル一覧の取得、フィルタ)



throw

throw を使うことで、任意の箇所で任意の例外を発生させることができます。


throw 構文

throw new 例外クラス;


throw とは投げる、という意味です。

任意の例外クラスで例外を発生させることができます。

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

 1 // throw サンプルプログラム
 2 package exception.gaiyou;
 3
 4 import java.util.Scanner;
 5
 6 public class ThrowSample {
 7
 8     public static void main(String[] args) {
 9         //外部入力
10         System.out.println("あなたの年齢を入力してください");
11         Scanner scanner = new Scanner(System.in);
12         int yourAge = scanner.nextInt();
13         scanner.close();
14         //メソッド呼び出し
15         try {
16             age(yourAge);
17         } catch(IllegalArgumentException e) {
18             System.out.println("0 以上 120 以下で入力をしてください。");
19             throw e;
20         }
21         System.out.println("あなたの年齢は、" + yourAge + " 歳です。");
22     }
23
24     private static void age(int age) throws IllegalArgumentException {
25         System.out.println("-----実行結果-----");
26         if (age < 0 | age > 120) {
27             // mainメソッドから渡った引数の数値が、0 以上120以下かチェック
28             throw new IllegalArgumentException();
29         }
30     }
31 }


あなたの年齢を入力してください
20
-----実行結果-----
あなたの年齢は、20 歳です。

throw サンプルプログラムでは、年齢確認を行っています。

IllegalArgumentExceptionは、不正な引数、または不適切な引数をメソッドに渡したことを示すためにスローされる例外クラスです。

ただし、非検査例外のため try-catchをせずともコンパイルエラーは発生しません。

しかし、基本的に例外処理は try-catchで囲むか、例外を呼び出し側に投げるかをする必要があります。


throw サンプルプログラムでは一般的に考え、年齢は0歳以上120歳以下です。それ以外は、例外となるように設定してあります。

もし入力した値が範囲外の場合は、IllegalArgumentException インスタンスが投げられることで「入力値が範囲外のため処理を継続できない」という例外が発生し、throws で呼び出し側に例外がスローされ例外処理されています。


もし、0 ~ 120 以外の数値が入力された場合は、どのような結果が出力されるでしょうか?

あなたの年齢を入力してください
131
-----実行結果-----
0 以上 120 以下で入力をしてください。
Exception in thread "main" java.lang.IllegalArgumentException
	at exception.gaiyou.ThrowSample.age(ThrowSample.java:28)
	at exception.gaiyou.ThrowSample.main(ThrowSample.java:16)


28行目で設定した throw によって、引数で渡ってきた数値が範囲外だったため、例外が発生しています。


プログラムの動きは、以下のようになります。

(1)12行目 外部入力

(2)16行目 ageメソッド呼び出し

(3)26行目 mainメソッドから渡った引数の数値が、0 以上120以下かチェック

(4)21行目 0 以上120以下であれば、年齢表示

         17行目 0 ~ 120 以外の数値であれば、例外が呼び出し側にスローされ例外処理が行われる



関連記事:【Java】例外クラスの概要や使用実例を解説‐検査例外・非検査例外など

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




どの例外が発生するかについて

上記で出てきた FileWriterは、IOExceptionが発生する可能性があるため、例外処理の記述が必須なことは分かりましたね。

FileWriter以外にも、いくつかのメソッドやコンストラクタで例外が発生する可能性があります。

どのメソッドやコンストラクタを使用すると、どんな例外が発生する可能性があるのかは、JavaDocに書かれています。


コンストラクタの詳細
FileWriter
public FileWriter(String fileName)
           throws IOException
ファイル名を指定して FileWriter オブジェクトを構築します。
パラメータ:
fileName - システムに依存するファイル名の文字列。
例外:
IOException - 指定されたファイルが存在するが通常ファイルではなくディレクトリである場合、存在せず作成もできない場合、またはなんらかの理由で開くことができない場合

 (JavaDocより引用)


上記は、FileWriterコンストラクタの詳細です。

もし、使用したいメソッドやコンストラクタの宣言後に「throws 例外クラス」が書かれていると、例外処理の記述が必要となります。

例外クラスは、発生する可能性がある例外クラスのことです。

例外については、JavaDocを参考にしてみてください。



スタックトレース

例外が発生した場合、どんな例外なのかが出力されます。

その中でも、例外が発生するまでの過程を表示したものを「スタックトレース」と言います。


Javaは実行時、メソッド呼び出しなどの命令をメモリのスタック領域に作成します。

スタックトレースは、その作成された命令などをリスト化して、例外発生時に表示ができるのです。




printStackTrace()

printStackTraceは、スタックトレースを標準エラーに出力するメソッドです。

上記のスタックトレースで解説した通り、例外が発生するまでの過程をすべて表示できます。


また、printStackTraceメソッドは、Java言語のすべてのエラーと例外のスーパー・クラスである、Throwableクラスのメソッドになります。

例外クラス


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

 1 // printStackTraceメソッドサンプル
 2 package exception.gaiyou;
 3
 4 import java.io.File;
 5 import java.io.FileWriter;
 6 import java.io.IOException;
 7
 8 public class PrintStackTraceSample {
 9
10     public static void main(String[] args) {
11         try {
12             //呼び出し先メソッド名
13             sample();
14             //呼び出し先メソッドで例外が発生すると、ここに移行
15         } catch(IOException e) {
16             //スタックトレース出力
17             e.printStackTrace();
18         }
19     }
20
21     // throws宣言
22     private static void sample() throws IOException {
23         System.out.println("-----実行結果-----");
24         FileWriter fw = null;
25         // 存在しないパスをわざと設定し、例外発生
26         File file = new File("q:\\Sample\\Hello.txt");
27         fw = new FileWriter(file);
28         System.out.println("ファイルの作成に成功しました。");
29         //ファイルの書き込み
30         fw.write("Hello World \n");
31         fw.write("こんにちは!");
32         System.out.println("ファイルの書き込みに成功しました。");
33         //ファイルを閉じる
34         if (fw != null) {
35             fw.close();
36             System.out.println("正常にファイルを閉じました。");
37         }
38     }
39 }


-----実行結果-----
java.io.FileNotFoundException: q:\Sample\Hello.txt (指定されたパスが見つかりません。)
    at java.base/java.io.FileOutputStream.open0(Native Method)
    at java.base/java.io.FileOutputStream.open(FileOutputStream.java:298)
    at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:237)
    at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:187)
    at java.base/java.io.FileWriter.<init>(FileWriter.java:96)
    at exception.gaiyou.PrintStackTraceSample.sample(PrintStackTraceSample.java:27)
    at exception.gaiyou.PrintStackTraceSample.main(PrintStackTraceSample.java:13)

上記の実行結果の内容が、スタックトレースです。

printStackTraceメソッドサンプルでは、ファイルを作成して書き込みをする、というものです。

しかし、指定したドライブが存在しないために例外が発生しています。


<スタックトレース例>

発生した例外クラス:エラーメッセージ
	at クラス名.メソッド名(ソース名:行番号)
	at クラス名.メソッド名(ソース名:行番号)
	…
	at クラス名.メソッド名(ソース名:行番号)

最初の1行目は、発生した例外のクラス名とエラーメッセージが表示されます。(エラーメッセージがない場合もあります)

2行目からは、スタックに作られたプログラムの命令が書かれています。スタックトレースは、新しい命令が来ると、前の命令の上にリストとして表示されるので、プログラム順には下からリスト化されています。

メソッド名に<init>が記載されている場合は、コンストラクタを示しています。

ソース名に(Native Method)が記載されている場合は、Javaのネイティブメソッド(Java以外の言語で開始されるJavaメソッド)を示しています。


では、printStackTraceメソッドサンプルの例外発生の原因解析してみます。

1行目では、発生した例外クラスが「java.io.FileNotFoundException」、エラーメッセージが「q:\Sample\Hello.txt (指定されたパスが見つかりません。)」です。

FileNotFoundExceptionクラスは、指定されたパス名で示されるファイルが開けなかったことを通知します。


今回は、1行目で原因の特定ができます。

「q ドライブ」というものは存在していないため、ありえないパスを指定しているので例外が発生しているのです。

もしも、原因の特定出来ない場合は、2行目以降の内容を見ていきます。


スタックトレースを見ると、Javaプログラムの実行では、多くのクラスやメソッドが用意されており、裏で動いているのがよく分かりますね。

スタックトレースを理解できるようになることで、デバッグの効率が飛躍的に上がります。



関連記事:【Java】例外クラスの概要や使用実例を解説 ‐検査例外・非検査例外など

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

     java.io.Fileクラスの使い方(ファイル一覧の取得、フィルタ)



getMessage()、getCause()

printStackTraceメソッドでは、スタックトレースを全文表示することが出来ました。

printStackTraceメソッドサンプルでは、スタックトレースは短い物でしたが、もっと長くなることもあります。

そのため、もっと簡潔に表示してほしい場合は getMessage()、getCause() を使用します。

getMessageメソッド・getCauseメソッドは、Java言語のすべてのエラーと例外のスーパー・クラスである、Throwableクラスのメソッドになります。


getMessageメソッドは、エラーメッセージを表示します。

getCauseメソッドは、実際に発生した例外を取得できます。


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

 1 // getMessage、getCauseメソッドサンプル
 2 package exception.gaiyou;
 3
 4 import java.io.File;
 5 import java.io.FileWriter;
 6 import java.io.IOException;
 7
 8 public class GetSample {
 9
10     public static void main(String[] args) {
11         try {
12             //呼び出し先メソッド名
13             sample();
14         //呼び出し先メソッドで例外が発生すると、ここに移行
15         } catch(IOException e) {
16             //例外状況のエラーメッセージを出力
17             System.out.println("getMessage:例外状況のエラーメッセージを出力");
18             System.out.println(e.getMessage());
19             System.out.println();
20             //例外の原因クラスを出力
21             System.out.println("getCause:例外の原因を出力");
22             if (e.getCause() == null) {
23             	   System.out.println("原因例外なし");
24             } else {
25                 e.getCause().printStackTrace();
26             }
27         }
28     }
29
30     // throws宣言
31     private static void sample() throws IOException {
32         System.out.println("-----実行結果-----");
33         FileWriter fw = null;
34         // 存在しないパスをわざと設定し、例外発生
35         File file = new File("q:\\Sample\\Hello.txt");
36         fw = new FileWriter(file);
37         System.out.println("ファイルの作成に成功しました。");
38         //ファイルの書き込み
39         fw.write("Hello World \n");
40         fw.write("こんにちは!");
41         System.out.println("ファイルの書き込みに成功しました。");
42         //ファイルを閉じる
43         if (fw != null) {
44             fw.close();
45             System.out.println("正常にファイルを閉じました。");
46         }
47     }
48 }


-----実行結果-----
getMessage:例外状況のエラーメッセージを出力
q:\Sample\Hello.txt (指定されたパスが見つかりません。)

getCause:例外の原因を出力
原因例外なし

getMessageメソッドでは、スタックトレースのエラーメッセージが出力されます。

getCauseメソッドでは、例外原因の例外クラスを取得しますが、今回のサンプルプログラムは e.getCause() で null が返されるため、実行結果は原因例外なしと出力されます。



関連記事:【Java】例外クラスの概要や使用実例を解説

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

     java.io.Fileクラスの使い方(ファイル一覧の取得、フィルタ)



まとめ

例外制御の throws、throw の使い方は分かりましたか?

例外制御には、他にも try-catch、マルチキャッチなどもありますので、関連記事から一読していただくと、例外への理解がより深まるでしょう。

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

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

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

     【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】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

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