例外処理(try-catch)って実行時に発生するエラーに対して、記述した処理が実行されるようにするために使われますよね。
例外処理の例として真っ先に思い付くのが0(ゼロ)での割り算や、ファイル操作です。
ファイルからデータを読み込む処理を記述したいけれども、読み込みたいファイルがそもそもない場合を想定しておかなければなりません。
でも、例外処理にはthrowを使った処理とthrowsを使った処理があって、似た用語なので使い分けに迷ったり、例外が重なる場合はどうすればいいのか分からなくて困ることってありませんか?
この記事では、例外処理について
- throwの使い方
- throwsの使い方
- throwsを使う場合の入れ子とは
- 例外の種類に応じた処理の分け方
などの基本から応用的な使い方についても解説していきます。今回は例外処理について、使い方をわかりやすく解説します!
なお、Javaの記事については、こちらにまとめています。
throwとは
throwは例外を意図的に起こし、例外処理を実行する場合に使われます。
意図的に例外を起こすとは、ある条件と一致する場合に例外を発生させ、エラーメッセージを返すということです。
throwでexceptionが発生する場合
それでは、サンプルコードでthrowの使い方をみていきましょう。除算(割り算)において、0(ゼロ)で割る場合の例外処理の例です。
このサンプルコードでは、throwを使ってIllegalArgumentExceptionの例外を発生させています。
public class Main { public static void main(String[] args) { // ゼロでの除算 div(0); } static void div(int i){ if (i != 0) { System.out.println("除算の結果は " + (3 / i) + " です"); } else { throw new IllegalArgumentException("除算の分母がゼロで不正です"); } } }
実行結果:
Exception in thread "main" java.lang.IllegalArgumentException: 除算の分母がゼロで不正です at Main.div(Main.java:13) at Main.main(Main.java:6)
throwsとは
次にthrowsについて解説します。throwsは例外が発生した場合、呼び出し元に例外処理を投げることができます。
例外処理を記述する際には2つの方法があります。
throwsで呼び出し元に例外処理を投げるか、try-catch構文を用いるかのどちらかです。try-catchを用いる場合はメソッドを使ったその場で例外処理を実装することができます。
throwsを用いる場合は呼び出し元に処理を投げるので、呼び出し元の処理によってはプログラム全体の処理が停止することもあります。try-catchを用いる場合は例外処理を実施後、それ以降の処理も実行されます。
throwsの使い方
それではthrowsの使い方について、サンプルコードをみていきましょう。
throwsを使う場合
まずは、throwsでメソッドの呼び出し元に例外を投げる場合です。サンプルコードで確認しましょう。
なお、読み込む対象となるtest.txtファイルはあえて用意をせず、FileNotFoundExceptionの例外を発生させています。
import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "test.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 + "を読み込みました"); } }
実行結果:
test.txtを読み込みませんでした 処理が終了しました
このサンプルコードでは、throwsSampleメソッドでFileReaderクラスを使用しているのでエラー処理を実施する必要があります。
例外処理はthrowsを使って呼び出し元のクラスに投げています。
この場合、throwsで呼び出し元のクラスに例外処理を投げた後の処理は中止され、例外処理をmainメソッドでcatchしてその後処理をしています。
try-catchを使う場合
ちなみに、throwsを使わずにtry-catch構文を用いる場合は次のサンプルコードのようになります。
import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "test.txt"; try { FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); } catch (FileNotFoundException e) { System.out.println(fileName + "を読み込みませんでした"); } System.out.println("処理が終了しました"); } }
実行結果:
test.txtを読み込みませんでした 処理が終了しました
この場合もthrowsを使う場合と同じようにcatchで例外処理を受けた後の処理も実行され、「処理が終了しました」と表示されています。
入れ子でthrowsを使う場合
例外処理が必要な処理を含むメソッドを使用する場合、そのメソッドを使用する場所でも例外処理を記述する必要があります。
この場合、throwsを入れ子で使用することになります。
メソッドを使用する場所での例外処理はメソッドの呼び出し元にまず投げられ、メソッド内の例外処理がさらに呼び出し元に投げられるということになります。
こちらもサンプルコードで確認していきましょう。
import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "test.txt"; try { throwsSample1(fileName); } catch (FileNotFoundException e) { System.out.println(fileName + "を読み込みませんでした"); } System.out.println("処理が終了しました"); } public static void throwsSample1(String fileName) throws FileNotFoundException { throwsSample2(fileName); } public static void throwsSample2(String fileName) throws FileNotFoundException { FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); } }
実行結果:
test.txtを読み込みませんでした 処理が終了しました
throwsは呼び出し元のメソッドに例外処理を投げます。
このサンプルコードでは、まずthrowsSample2メソッドで発生したFileNotFoundExceptionの例外処理をthrowsSample1に投げています。
そこからさらにmainメソッドに例外を投げてその例外をcatchしているということになります。
例外の種類に応じて処理を分ける場合
例外の種類に応じて処理を分ける場合、処理を記述する順番に注意をする必要があります。hrowsを使う場合では呼び出し元のクラスに例外処理を投げ処理が終了となり、その後の処理が行われないからです。
例外の種類が複数ある場合は、throwsの後にそれぞれの呼び出し元を「,」(カンマ)で区切って記述します。
それではサンプルコードで確認していきましょう。
import java.io.FileNotFoundException; import java.io.FileReader; public class Main { public static void main(String[] args) { String fileName = "test.txt"; try { throwsSample(fileName); } catch (FileNotFoundException | ArithmeticException e) { if (e instanceof FileNotFoundException) System.out.println(fileName + "は読み込めませんでした"); if (e instanceof ArithmeticException) System.out.println("除算の分母がゼロで不正です"); } System.out.println("処理が終了しました"); } public static void throwsSample(String fileName) throws FileNotFoundException, ArithmeticException { // FileNotFoundExceptionクラスの例外発生 FileReader r = new FileReader(fileName); System.out.println(fileName + "を読み込みました"); // ArithmeticExceptionクラスの例外発生 System.out.println("除算の結果は " + (3 / 0) + " です"); } }
test.txtファイルが存在しない場合の実行結果:
test.txtは読み込めませんでした 処理が終了しました
test.txtファイルが存在する場合の実行結果:
test.txtを読み込みました 除算の分母がゼロで不正です 処理が終了しました
このサンプルコードでは、2つの例外が発生する可能性があります。
先に記述した例外が発生した場合はその例外処理が行われ、後の処理は実行されていません。先に記述した処理に例外が発生しなければ、後の処理も実行することができます。
このように記述する順番によって、処理が分けられる場合とそうでない場合があるので注意しましょう!
このサンプルコードでは、catch以降を「catch (FileNotFoundException | ArithmeticException e)」のように記述しました。Java7からこのような簡潔な記述ができるようになりました。ただし、親子関係にあるクラスをこのように並べて記述することはできません。
FileNotFoundExceptionクラスはIOExceptionクラスのサブクラスですので、この2つのクラスをこのサンプルコードのように並べて記述することはできませんので注意してください。
try catch文の使い方総まとめ
この記事では紹介しきれなかったtry catch文のいろいろな使い方を次の記事にまとめているので、ぜひ確認してください!
try-catch-finally文の使い方
try-catch文にはfinallyを使って例外の発生にかかわらず必ず行う処理を記述することができます。
try-catch-finally文の使い方については次の記事にまとめているので、ぜひ確認してください!
まとめ
ここでは、例外処理のthrowとthrowsの使い分けについて説明しました。
冒頭にも書きましたが、エラー処理で実行が止まってはトラブルの原因になる可能性がありますので、例外処理はしっかりとマスターするようにしましょう!
とは言え、習い始めの頃はなかなか慣れないかもしれませんので、使いこなせることができるようにこの記事を何度も参考にして下さいね!