});予期しないエラーに対処するには?プログラムのエラーには文法エラーのようにコンパイル時に発見できるものと、0で割り算してしまうような、コンパイル時には発見できないものがあります。ハードディスクが故障してデータをファイルに書き込めないなど、実行時にはプログラマが想定できないようなエラーも発生します。このような想定外の場合に備えて、エラー発生時の処理を決めておくのが「例外」という機能です。簡単な例を示しますので、どのような動作になるかを確認してみましょう。);}}この実行結果は以下のとおりです。スローするにはキャッチまたは、スロー宣言をしなければなりません。個C:\>一見正しくコンパイルできそうなプログラムでしたがエラーになってしまいました。このプログラムがコンパイルできなかったのか考えてみましょう。本連載で初めて登場するクラス「BufferedReader」はバッファからデータを取り出すために使うクラスです。InputStreamReader(System.in)と指定することにより、読み込み元のバッファを「InputStreamReader」からと指定しています。InputStreamReaderはバイト単位のデータを文字として読み込むためのクラスです。この場合、バイト単位のデータの読み込み元を「System.in」に指定しています。これまでのサンプルプログラムで何度となく登場していた「System.out」が画面出力を表すのに対して、System.inはキーボードからの入力を表します。)」全体では「キーボードから入力したバイト単位のデータを文字として読み込んでバッファにためるためのオブジェクトを生成し、そのオブジェクトにBufferedReader型の変数rでアクセスできるようにする」という意味になります。r.readLine();」の部分です。BufferedReaderクラスのメソッドであるreadLine()を実行するときに、「IOException」という例外が発生する可能性があるのに対処されていませんよ、というエラーです。readLine()のように、メソッドによっては発生し得る特定の例外についてプログラマが対処方法を決めておかなければならないのです。このとき使うのが「try〜catch」のブロックです。);}}}今回は無事にコンパイルできたはずです。r.readLine();」の部分でキーボードから文字を入力し、リターンキーを押したところで文字列が変数sに格納されます。変数sに格納された文字列はInteger型の静的メソッドであるparseInt()によって数値に変換され、int型の変数nに格納される、という流れです。parseInt()はHTMLDocument型クラスでHTMLを解釈するときに作ったparse()と同じように、文字列を解釈するメソッドです。プログラムを実行してみましょう。DivideCalc210÷Nの計算をしますNの値を入力してください:5答えは2です。}」のブロックに入れました。}」ブロックで、tryブロック内で発生したIOExceptionの例外を捕捉し、エラーメッセージを表示するようにしました。try〜catchの構文では、tryブロックに本来の処理、catchブロックに例外発生時の処理を書くことで、予期しないエラーに対処できるようになります。このプログラムは発生し得る例外に対処し切れていません。試しに「abc」という文字列を入力してみましょう。Integer.parseInt(s);」の部分で例外が発生してしまいました。実は、tryブロック内の処理で発生した例外を正しく捕捉するには、適切なcatchブロックを個々に記述しなければならないのです。仕様でIntegerクラスのparseInt()メソッドで発生する例外を調べると、文字列が構文解析可能な整数型を含まない場合に「NumberFormatException」という例外が発生することが分かります。そこで、プログラムを以下のように修正します。);}}}このプログラムを実行し、Nの値に「abc」を入力してみましょう。DivideCalc310÷Nの計算をしますNの値を入力してください:abc数値に変換できる文字を入力してくださいC:\>今回はエラーが表示されずにプログラム内で例外が捕捉されて、ユーザーには対処方法のメッセージが表示されました。)」と記述することで、例外ごとに対処方法を切り分けられるのです。例外を捕捉できさえすればよい、という場合もあるでしょう。発生し得るすべての例外をいちいち調べていたらきりがありません。そこで、例外の種類を特定しない以下のようなcatchブロックの書き方もできます。);}}}例外発生の有無に関係なく実行するための−finallyブロック−catchブロックは例外を捕捉し、例外発生時の処理を記述するために使いますが、例外が発生してもしなくても処理しなければならないことはどうしたらいいのでしょうか?例えば、テキストファイルの内容を画面に表示するプログラムを考えてみます。WindowsなどのOSが管理しているファイルを読み込むには、まず特定のファイルを扱うことをOSに対して宣言(オープン)し、その後、使い終わったことをOSに通知(クローズ)しなければなりません。ディスクの読み込み中にエラーが発生し例外を捕捉しても、そのままプログラムを終了させてしまうのではなく、必ずファイルを閉じなければならないのです。このように、特定のリソースの利用開始から終了するまでに起こる例外を捕捉しつつ、最後に確実にリソースを解放することは、ユーザーに安定したサービスを提供するプログラムを書くために欠かせないことです。そこでfinallyという文を使います。{System.out.println("ファイルを閉じられません");return;}}}tryブロックの中にtryブロックがある、一見複雑な構造になりました。そこで重要な部分を抜き出して説明します。{fis.close();}1行目では、テキストファイル読み込み用のクラスFileInputStreamのオブジェクトを生成しています。FileInputStreamクラスのコンストラクタの引数になっている「args[0]」は、コマンドラインでこのプログラムを実行したときの1番目の引数を格納する配列です。TEST.TXT」と入力したときのargs[0]には「TEST.TXT」が格納されます。例として挙げたテキストファイルを読み込んで表示するプログラムの主要部分は、3つの部分に分かれています。tryブロックでは、データを1文字ずつ読み込んで、ファイルの終わりに達するまで画面に表示します。catchブロックではIOExceptionの例外を捕捉し、ディスクI/Oエラーによる読み込みの中断に備えています。最後のfinallyブロックでは、現在開いているファイルを閉じています。この例で重要なのは、最後のfinallyブロックはたとえ例外が発生しても必ず実行されることです。ディスクI/Oエラーが発生してcatchブロックに制御が移ったとします。このときプログラムが画面に「ディスクI/Oエラー」と表示することまでは通常の文と同じです。次にあるreturn文が実行されてメソッドを終了するとき、すぐにはメソッドを抜け出さずにfinallyブロックにある「fis.close()」が実行されるのです。プログラムの制御構造を理解する」でreturnを「メソッドを呼び出した親メソッドに制御を戻す」文と説明しました。try〜catch〜finallyの場合は実行順が通常とは少し異なり、finallyブロックにある文を実行してから親メソッドに制御が戻されます。例外処理はreturnやbreakといった処理を中断させる制御文のふるまいを若干変える効果があるのです。この例ではファイルをオープンして内容を表示するのに関連する処理が、本来の処理、エラー時の処理、終了処理の3つに区分けされました。例外を使うと、forやwhileなどの制御文とは別の意味で、プログラムの構造を役割に応じて記述できるのです。そこで次回は、プログラムを分かりやすく記述するという例外のもう1つの側面について説明します。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。