Java では、プログラムを実行する最中に発生するエラーを 例外(exception)として扱うことができます。例外には例えば、0で割り算をした、メモリが枯渇した、存在しないファイルを開こうとしたなどがあります。
FileReader() は、指定したファイルが存在しない場合に FileNotFoundException 例外を発生させます。これは、FileReader() のマニュアルに下記のように記述してあることから分かります。
public FileReader(String fileName) throws FileNotFoundException
try は、例外を発生させる可能性のある処理を呼び出す場合に用います。try { ... } の間で例外が発生した場合、catch を用いてこの例外を捕捉します。下記の例では、FileNotFoundException 例外が発生した場合に、その内容を標準出力に書き出します。このような例外発生時の処理を 例外ハンドラ と呼びます。
try { FileReader in = new FileReader("data.txt"); } catch (FileNotFoundException e) { System.out.println(e); }
catch は例外発生時に投げられる 例外オブジェクト に応じて複数記述することができます。いずれの catch にも該当しない例外やファイルクローズなどの後処理を行うために、finally を用います。finally の部分は、たとえ try 部分で return を行っても実行されます。
try { : } catch (TestAException e) { : } catch (TestBException e) { : } finally { : }
例外が発生した場合、try { ... } catch の間の残りの処理はスキップされます。下記の例では、例外が発生した場合 (1) (2) (4) (5) (6) の処理が実行され、(3) の処理はスキップされます。例外が発生しない場合は (1) (2) (3) (5) (6) の処理が実行されます。
System.out.println("(1)"); try { System.out.println("(2)"); FileReader in = new FileReader("data.txt"); ← 例外発生!! System.out.println("(3)"); ← スキップされる } catch (FileNotFoundException e) { System.out.println("(4)"); } finally { System.out.println("(5)"); } System.out.println("(6)");
例外オブジェクトは次のようなクラス階層を持ちます。
Object Throwable Error OutOfMemory : アウトオブメモリ AssertionError : アサーションエラー : Exception RuntimeException ArithmeticException : 数学関連 IllegalArgumentException : 不正な引数 NullPointerException : NULLポインタ不正 ClassCastException : クラスキャスト不正 IndexOutOfBoundsException : インデックス不正 ArrayIndexOutOfBoundsException : 配列インデックス不正 : IOException : I/O関連 FileNotFoundException : ファイルが見つからない : XxxxException :
それぞれのクラス(およびそのサブクラス)は次のように分類できます。
クラス | 説明 |
---|---|
Error | JavaVM で検出される致命的エラー。例外ハンドラを記述しても処理を継続できないことが多い。OutOfMemory(メモリ枯渇)など。 |
RuntimeException | Javaの処理上発生するエラー。例外ハンドラは省略可能。ArithmeticException(ゼロ除算)、IllegalArgumentException(引数不正)、NullPointerException(NULLポインタ参照)、ArrayIndexOutOfBoundsException(不正インデックスによる配列参照)など。 |
Exception | 通常の例外。例外ハンドラを記述しないとコンパイルエラーとなる。 |
独自の例外を作成するには Exception クラスを継承します。
class MyException extends Exception { MyException(String message) { super(message); } }
throw を用いて例外を投げることができます。例外を投げるメソッドを定義する場合は、throws を用いて、そのメソッドが例外を投げる可能性があることを明示しておきます。throws 宣言されたメソッドは、try catch で例外を補足しないと、コンパイル時にエラーとなります。
class MyException extends Exception { MyException(String message) { super(message); } } class Main { public static void main(String[] args) { try { methodA(); } catch (MyException e) { System.out.println(e); } } static void methodA() throws MyException { throw new MyException("MyException ERROR!!!"); } }
例外が発生した際にスタックトレースを表示することができます。
try { .... } catch (Exception e) { e.printStackTrace(); }
Java 6 まではキャッチしたい例外の数だけ catch 文を記述する必要がありました。
try { ... } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }
Java 7 からはひとつの catch 文で複数の例外をキャッチできるようになりました。
try { ... } catch (IOException | SQLException e) { e.printStackTrace(); }
Java 6 までは、FileReader などのリソースを確実にクローズするためには、try-catch-finally の finally 節で close() を呼び出していました。
import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; public class Main { public static void main(String[] args) { try { BufferedReader br = null; try { br = new BufferedReader(new FileReader(new File("Test.txt"))); String line; while ((line = br.readLine()) != null) { System.out.println(line); } } finally { if (br != null) { br.close(); } } } catch (IOException e) { e.printStackTrace(); } } }
Java 7 からは try-with-resource と呼ばれる方式で、finally 節を記述しなくても自動的に close() が呼ばれるようになりました。対象は java.lang.AutoCloseable または java.io.Closeable インタフェースを実装するクラスである必要があります。
import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; public class Main { public static void main(String[] args) { try { try (BufferedReader br = new BufferedReader(new FileReader(new File("Test.txt")))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } } catch (IOException e) { e.printStackTrace(); } } }
assert は、「ある時点でこの変数の値はこうなっているはずだ」ということを記述しておき、期待通りになっていない場合に AssertionError 例外を発生させる仕組みです。開発時の動作保証などで利用されます。
public class Main { public static void main(String[] args) { try { int n = 123; assert n == 0 : "n is not zero."; System.out.println(n); } catch (AssertionError e) { System.out.println(e); } } }
assert を有効にするには java 実行時に -ae オプションを指定する必要があります。
java -ae Main
上記を実行すると、assert の箇所で n は 0 であるはずなのに 0 以外の値が設定されているため AssertionError 例外が発生し、下記の様に表示されます。
java.lang.AssertionError: n is not zero.