throwとは
例外的状況が発生したどうかはJVMが監視しており、JVMが例外的状況を検知すると処理をcatchブロックに移します。
この例外を監視しているJVMに対して、意図的に「Exceptionという例外的状況になりました」と報告することも可能です。
例外を報告するのに使われるのが throw キーワードです。
監視しているJVMに対して例外的状況を報告することを「例外を投げる」もしくは「例外を送出する」と言います。
例外が投げられると、JVMは例外を検知し、即時でcatchブロックの実行や伝播に処理を移行します。
では、例外インスタンスを自分で投げるサンプルを確認してみます。
■記述例 public class Main { public static void main(String args[]){ Member m = new Member(); m.setAge(-26); } } class Member { int age; void setAge(int age) { if (age < 0) { throw new IllegalArgumentException("年齢は正の数を指定してください。指定値=" + age); } this.age = age; } }
Memberクラス の setAgeメソッド では、引数を age に代入しています。
年齢に負の値が入ることはないため、 age が負の値になるのを防ぐために代入前に引数を検査する形にしました。
もし、引数に負の値が代入されている場合、IllegalArgumentException インスタンスを投げることで「引数の値が異常値であるため処理が継続できない」という例外的状況が発生していることを、JVMに報告します。
setAgeメソッド で生じた例外は、呼び出し元の mainメソッドに伝播しますが、mainメソッドでは例外をキャッチしていないため、JVMがプログラムを強制的に終了します。
実行結果は次の通りです。
■実行結果 mbp:desktop potepan$ java Main Exception in thread "main" java.lang.IllegalArgumentException: 年齢は正の数を指定してください。指定値=-26 at Member.setAge(Main.java:12) at Main.main(Main.java:4)
throwの処理の流れ
先述した例外の説明の内容から、throwの処理の流れをまとめると下記の通りです。
例外発生 → 例外オブジェクト生成 → 例外のスロー → 例外のキャッチ
参考までに、Throwableクラスのリファレンス文も掲載します。
Throwableクラスは、Java言語のすべてのエラーと例外のスーパー・クラスです。このクラス(またはそのサブクラスの内の1つ)のインスタンスであるオブジェクトだけがJava仮想マシンによってスローされるか、Javaのthrow構文によってスローされます。同じように、このクラスまたはそのサブクラスの内の1つだけがcatch節の引数の型に指定できます。
(中略)
2つのサブクラスErrorとExceptionのインスタンスは従来、例外的な状況が発生したことを示すために使用されます。これらのインスタンスは、通常、関連する情報(スタック・トレース・データなど)を格納するために、例外的な状況に応じて新しく作成されます。
スロー可能オブジェクトには、作成時のそのスレッドの実行スタックのスナップショットが含まれます。これには、エラーについての詳細情報を示すメッセージ文字列を含めることもできます。※参考:Throwable (Java Platform SE 8)
throwとthrowsの違いは?
Javaの例外処理には、先述した「throw」と「throws」という処理があります。
両者ともその名前の通り、処理を「投げる」という意味合いを持っています。
しかし、名前は似ている「throw」と「throws」ですが、処理内容には大きな違いがあるのです。
- throwは、例外を意図的に発生させて、例外処理を実行させることができます。
- throwsは、メソッド内で例外が発生した場合、自身のメソッド内でcatchするのではなく呼び出し元に例外を投げます。
ここでは、throwsの特徴やそれぞれの違いを解説します。
throwsとは
throwsは、プログラムに例外が生じた際に、呼び出し元に例外処理を投げられる方法です。
例外処理を記述する場合、呼び出し元にthrowsで例外処理を投げる、または、try-catch構文を使うかのどちらかを使用します。
try-catch構文を使用する場合は、メソッドを使う場所で例外処理を実装可能です。
throwsを使用する場合は、呼び出し元に処理を投げるため、呼び出し元の処理内容によってはプログラム全体の処理を停止させることもあります。
一方、try-catch構文を使う場合は、例外処理を実施後にそれ以降の処理も実行されます。
try-catch構文については、以下の記事で詳しく解説していますので、参考までに。
【関連記事】
▶︎【Java】try-catch文の基本を解説!例外・エラー処理をマスターしよう【サンプルあり】
ここでは、throwsを使った例と、try-catch構文を使った例を確認してみます。
■throwsを使った記述例 import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "sample.txt"; try { throwsSample(fileName); } catch (FileNotFoundException e) { System.out.println(fileName + "を読み込みませんでした"); } System.out.println("処理が終了しました"); } public static void throwsSample(String fileName) throws FileNotFoundException { FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); } } ■実行結果 mbp:desktop potepan$ java Main sample.txtを読み込みませんでした 処理が終了しました
■try-catch構文を使った記述例 import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "sample.txt"; try { FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); } catch (FileNotFoundException e) { System.out.println(fileName + "を読み込みませんでした"); } System.out.println("処理が終了しました"); } } ■実行結果 mbp:desktop potepan$ java Main sample.txtを読み込みませんでした 処理が終了しました
throwsを複数使うには?
throwsは入れ子にして複数使うこともできます。
記述例は、次の通りです。
■記述例 import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "sample.txt"; try { throwsSampleFirst(fileName); } catch (FileNotFoundException e) { System.out.println(fileName + "を読み込みませんでした"); } System.out.println("処理が終了しました"); } public static void throwsSampleFirst(String fileName) throws FileNotFoundException { throwsSampleSecond(fileName); } public static void throwsSampleSecond(String fileName) throws FileNotFoundException { FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); } } ■実行結果 mbp:desktop potepan$ java Main sample.txtを読み込みませんでした 処理が終了しました