アットウィキロゴ
Javaプログラミング入門
掲示板 掲示板 ページ検索 ページ検索 メニュー メニュー

Javaプログラミング入門

21. 例外処理

最終更新:

javatutorial

- view
管理者のみ編集可

これまでの不便な点と「例外処理」のメリット

前回の「クラスライブラリ~後編~」で、キーボードから入力を受け取る「Scanner」クラスを学びました。
その際、「数字を入力する場面で、間違えて『あいうえお』などの文字を入力すると、コンピュータが混乱してプログラムがエラーで強制終了してしまう」という注意点がありました。

これまでの知識だと、エラーが起きないようにユーザーに気をつけてもらうことしかできませんでした。
しかし、実際のスマートフォンアプリやゲームで、ユーザーが一度操作を間違えただけでアプリの画面が突然消えて(クラッシュして)しまったら、とても不便で使いづらいですよね。

「もし想定外のエラーが起きても、プログラムを強制終了させずに、安全に別の処理(エラーメッセージを出すなど)に切り替える」という仕組みが必要です。
これを実現するのが、今回学ぶ例外処理です。

例外処理は、通学のカバンに入れておく「折りたたみ傘(予備の対策)」のようなものです。

いつも通り晴れていれば(エラーが起きなければ)、そのまま自転車で学校へ向かいます(通常のプログラム)。
しかし、途中で「突然のゲリラ豪雨」という想定外のトラブル(エラー)が起きたとき、何の対策もしていないと、ずぶ濡れになって途中で家に引き返すことになってしまいます(プログラムの強制終了)。
でも、あらかじめカバンに折りたたみ傘を入れておき、「もし雨が降ったら、自転車を降りて傘をさして歩く」という手順(バックアッププラン)を用意しておけば、トラブルを乗り越えて無事に学校までたどり着くことができますよね。
例外処理は、そんな「もしもの時のための代わりの行動」をあらかじめコンピュータに教えておく頼もしい機能なのです。

例外処理の基本構文「try-catch」

例外処理を行うには、trycatch という2つのブロック(波カッコ {} で囲まれた部分)をセットで使います。
try {
    // エラーが起きるかもしれない「通常のプログラム」を書く
} catch (例外のクラス名 変数名) {
    // もしtryの中でエラーが起きたら、強制終了せずにここへワープしてくる
    // (バックアッププランを書く)
} 
専門用語として、プログラムの実行中に発生するエラーのことを例外(Exception)と呼び、そのエラーが発生することを「例外がスローされる(投げられる)」、それをcatchで受け止めることを「例外をキャッチする」と表現します。

例外処理を使ったプログラム例

10. 配列」で、用意した箱の数よりも外側の番号を使おうとするとエラーになることを学びました。これをわざと発生させて、try-catchで安全に受け止めるプログラムを書いてみましょう。

サンプル:ExceptionTest.java
  1. public class ExceptionTest {
  2. public static void main(String[] args) {
  3. System.out.println("プログラムを開始します。");
  4.  
  5. int[] scores = new int[3]; // 0, 1, 2の3つの箱を用意
  6.  
  7. try {
  8. // わざと存在しない「5番目」の箱にデータを入れてエラー(例外)を起こす
  9. System.out.println("配列にデータを入れます...");
  10. scores[5] = 100;
  11.  
  12. // ↑上の行でエラーが起きると、以下の処理は無視されてcatchへワープする!
  13. System.out.println("データの入力が完了しました。");
  14.  
  15. } catch (Exception e) {
  16. // 例外をキャッチしたので、強制終了せずにここの処理が行われる
  17. System.out.println("エラー発生!配列の枠を超えています!");
  18. }
  19.  
  20. // 強制終了されていないので、最後までプログラムが動き続ける
  21. System.out.println("プログラムを無事に終了します。");
  22. }
  23. }
実行結果
プログラムを開始します。
配列にデータを入れます...
エラー発生!配列の枠を超えています!
プログラムを無事に終了します。 
もしtry-catchを書いていなかったら、途中で赤い英語のエラーメッセージが大量に出て、最後の「プログラムを無事に終了します。」は絶対に表示されません。
例外処理のおかげで、プログラムが最後まで生き残ることができました。

多態性を活かした例外のキャッチ

先ほどのcatchのカッコの中にException eと書きました。
実は、Javaで発生するすべてのエラー(例外)は、クラスとして作られています。
よく発生する例外クラスの種類
クラス名 原因の例
ArithmeticException 「10 / 0」のように、ゼロで割り算をしようとした
ArrayIndexOutOfBoundsException 配列の枠より外側の番号を使おうとした
InputMismatchException Scannerで数字の代わりに文字を入力した
「どのエラーが起きるか分からないから、全部の例外クラスに合わせてcatchをたくさん書かないといけないの?」と思うかもしれません。
しかし、「19. 多態性」で素晴らしい仕組みを学びましたね。「親の箱には、どの子クラスの実体でも入れることができる(アップキャスト)」というルールです。

実は、上記の表にある様々な例外クラスは、すべて一番の基本となる Exception クラスを継承した子クラスなのです。
そのため、catch (Exception e)と「親の型」で待ち構えておけば、多態性の力によって、ゼロ割り算だろうが配列のエラーだろうが、どんな例外でもこの1つの箱でまとめてキャッチできるというわけです。

最後に必ず通る道「finally」

try-catchに続けて、finally というブロックを付け足すことができます。
try {
    // 通常処理
} catch (Exception e) {
    // エラー時の処理
} finally {
    // エラーが起きても、起きなくても「絶対に最後に実行される」処理
} 
finallyは「後片付け」のために使われます。
たとえば、前回の「Fileクラス」の発展でファイルを読み込む準備をしたとします。もし途中でエラーが起きて処理が中断されても、開きっぱなしのファイルを「最後に必ず閉じる」という命令をfinallyに書いておけば、安全にパソコンの部品(メモリ)を片付けることができます。
💡注意!
開発現場では「とりあえずExceptionで全部キャッチすればエラーで落ちないから安心!」と、エラーを無視するような横着なプログラムを書くのはマナー違反とされています。
エラーが起きた本当の原因が隠れて分からなくなってしまうからです。
基本は「想定される個別の例外(InputMismatchExceptionなど)」をキャッチして適切な対応(もう一度入力させるなど)を行い、Exceptionは「どうしても予測できなかった最後のお守り」として使うのが良い設計です。

コラム

例外(Exception)とエラー(Error)の深い違い

Javaの裏側(JVMの仕様)では、問題が発生したときに投げられるクラスは大きく2つのグループに分けられています。
1つは私たちが今回扱った java.lang.Exception(とその子孫たち)です。これは「プログラムの工夫次第で回復できる可能性のある問題」を表します。

もう1つは、java.lang.Errorという恐ろしいクラスグループです。
たとえば、パソコンのメモリ(記憶領域)が完全に足りなくなってしまったOutOfMemoryErrorや、JVMそのものが壊れてしまった場合などに投げられます。
これらは「プログラムの書き方でどうにかできる限界を超えている、致命的な異常事態」です。
そのため、Javaの言語仕様として「Errorクラスの仲間は、try-catchで一般には捕まえない・握りつぶさないのが原則(キャッチしても回復不可能なので、素直に強制終了させるべき)」というルールが存在します。
私たちがプログラミングで安全ネット(例外処理)を張ってよいのは、あくまで「Exception」の範囲内だけなのです。

最近更新されたスレッド
人気記事ランキング
ウィキ募集バナー