第8章の NullPointerException のように、実行の途中で起きるエラーを 例外 といいます。例外が起きると、プログラムはそこで止まります。この章では、代表的な例外と、例外が起きても処理を続けられるようにする try〜catch、例外が起きることをメソッドに書いておく throws を学びます。
この章で学ぶこと
- 例外とスタックトレースの読み方
- 代表的な例外(
NullPointerException・ArrayIndexOutOfBoundsException・ArithmeticException・NumberFormatException) -
try〜catchで例外を受け止める - 例外はメソッドの呼び出し元へ伝わる
-
throwで例外を起こす、throwsで宣言する
12.1 この章で作るもの
この章では、ch12 パッケージに Main クラスを作り、わざと例外を起こしてスタックトレースを読んだ後、例外を受け止める書き方に直していきます。章の最後には、実行するとコンソールに次のように表示されるようになります。
数に変換できません:abc
年齢:26
年齢は 15 以上 99 以下で入力してください:100
処理を終わります
| ファイル | 作業 | 内容 |
|---|---|---|
ch12/Main.java |
新規 | 例外を起こし、受け止める練習用のクラス |
12.2 例外とスタックトレース
例外 は、コンパイルは通ったのに、実行したときに処理を続けられなくなった状態です。例外が起きると、スタックトレース が Eclipse のコンソールに赤い文字で表示されて、プログラムが止まります。まず、わざと例外を起こしてみましょう。
-
srcを右クリックし、新規 → パッケージ でch12パッケージを作ります。 -
ch12パッケージを右クリックし、新規 → クラス で、名前Main、mainに チェックを入れて クラスを作り、緑の行を入力します。ch12/Main.javapackage ch12; public class Main { public static void main(String[] args) { int[] ages = {26, 27, 24}; System.out.println(ages[3]); } } -
保存して実行します。コンソールに赤い文字で次のように表示されます。
コンソールException in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 at ch12.Main.main(Main.java:7)「要素が3つの配列に、添字 3 でアクセスした」という例外が、
Main.javaの 7 行目で起きたことを表しています。Main.java:7の部分はリンクになっていて、クリックするとエディターのその行に飛びます。 -
2つの行を、緑の行に書き換えます。
ch12/Main.javapackage ch12; public class Main { public static void main(String[] args) { String input = "abc"; int age = Integer.parseInt(input); System.out.println("年齢:" + age); } } -
保存して実行します。
コンソールException in thread "main" java.lang.NumberFormatException: For input string: "abc" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:668) at java.base/java.lang.Integer.parseInt(Integer.java:786) at ch12.Main.main(Main.java:7)
今度は at の行が4つあります。スタックトレースは、例外が起きた場所から、呼び出し元へさかのぼる順 に並びます。上の3行は Java の内部(Integer.parseInt の中)で、いちばん下の ch12.Main.main(Main.java:7) が、自分の書いたコードの中で例外につながった場所です。自分のコードを探すときは、自分のパッケージ名(ch12)で始まる行 を見ます。
代表的な例外
この教材で出会う例外は次の4つです。どれも、原因となる書き方が決まっています。
| 例外 | 起きるとき | 例 |
|---|---|---|
NullPointerException |
null の変数のメソッドやフィールドを使った |
String s = null; s.length();(第8章) |
ArrayIndexOutOfBoundsException |
配列に無い添字を使った |
ages[3](要素が3つのとき) |
ArithmeticException |
整数を 0 で割った |
10 / zero(zero が 0 のとき)。メッセージは / by zero
|
NumberFormatException |
数に変えられない文字列を Integer.parseInt に渡した |
Integer.parseInt("abc") |
例外はすべてクラスで、java.lang.NullPointerException のように java.lang パッケージにあるものは import なしで使えます。この教材で出会う例外のクラス名は、どれも最後が Exception で終わります。
【便利な使い方】スタックトレースの読み方の手順
- 1行目の
Exceptionで終わるクラス名で、何が起きたか を知る - 1行目の
:の後ろのメッセージで、詳しい原因 を知る(Index 3 out of bounds for length 3、because "e4" is nullなど) -
atの行のうち、自分のパッケージ名で始まる いちばん上の行 を見て、どこで起きたか を知る。リンクをクリックして、その行を確かめる
スタックトレースの読み方は、付録A「実行時のエラーの読み方」 にもまとめています。
12.3 try〜catch
例外が起きたときにプログラムを止めず、代わりの処理を行うには、try〜catch を使います。
try {
例外が起きるかもしれない処理
} catch (例外のクラス 変数名) {
例外が起きたときの処理
}
-
Main.javaを、緑の行のように書き換えます。ch12/Main.javapackage ch12; public class Main { public static void main(String[] args) { String input = "abc"; try { int age = Integer.parseInt(input); System.out.println("年齢:" + age); } catch (NumberFormatException e) { System.out.println("数に変換できません:" + input); } System.out.println("処理を終わります"); } } -
保存して実行します。
コンソール数に変換できません:abc 処理を終わります -
inputを"26"に変えて実行すると、年齢:26と処理を終わりますが表示されます。確かめたら"abc"に戻します。
| 部分 | 意味 |
|---|---|
try { … } |
例外が起きるかもしれない処理を囲む。途中で例外が起きると、残りの処理は飛ばされる(年齢: の行は表示されない) |
catch (NumberFormatException e) { … } |
try の中で NumberFormatException が起きたときに実行される。e には、起きた例外のオブジェクトが入る |
e.getMessage() |
例外のメッセージ(スタックトレースの1行目の : の後ろ)を返す。表示や記録に使う |
catch に書いたクラスと違う例外が起きたときは、受け止められずに、これまでと同じくプログラムが止まります。受け止める例外は、起きる可能性があるものを、catch に具体的に書きます。
【知識メモ】Spring入門では try〜catch を書かない
Spring入門のコードには、try〜catch は1つも出てきません。入力の変換に失敗したときの処理は Spring が行い、エラーの画面を表示してくれるためです。ただし、Spring が表示するエラーの画面やコンソールには、この章で学んだスタックトレースが出ます。読み方を知っていることが、原因を探す力になります。
12.4 例外の伝わり方
例外は、起きたメソッドの中で受け止められなければ、そのメソッドを呼び出した側に伝わります。呼び出した側でも受け止められなければ、さらにその呼び出し元に伝わり、main メソッドまで伝わっても受け止められなければ、プログラムが止まります。
12.2 の NumberFormatException は、Java の内部の Integer.parseInt の中で起きて、そこでは受け止められずに main まで伝わってきたものでした。スタックトレースの at の行が下に向かって呼び出し元をたどっているのは、この伝わり方を表しています。
したがって、try〜catch は、例外が起きるメソッドの中に書いても、それを呼ぶ側に書いてもかまいません。12.3 では、parseInt を呼ぶ側の main に書きました。
12.5 throw と throws
例外は、Java が起こすだけでなく、自分で起こすこともできます。「年齢が範囲外なら、処理を続けられない」という状況を、例外で表してみましょう。
-
Main.javaに、緑の行を書き足します。ch12/Main.javapackage ch12; public class Main { public static void main(String[] args) { String input = "abc"; try { int age = Integer.parseInt(input); System.out.println("年齢:" + age); } catch (NumberFormatException e) { System.out.println("数に変換できません:" + input); } try { checkAge(26); checkAge(100); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); } System.out.println("処理を終わります"); } /** 登録や変更の前に、年齢が 15 以上 99 以下かを確かめる * @param age 調べる年齢 * @throws IllegalArgumentException 範囲外のとき */ static void checkAge(int age) throws IllegalArgumentException { if (age < 15 || age > 99) { throw new IllegalArgumentException("年齢は 15 以上 99 以下で入力してください:" + age); } System.out.println("年齢:" + age); } } -
保存して実行します。
コンソール数に変換できません:abc 年齢:26 年齢は 15 以上 99 以下で入力してください:100 処理を終わります
| コード | 意味 |
|---|---|
throw new IllegalArgumentException("…"); |
例外のオブジェクトを作って、例外を起こす。この文が実行されると、メソッドの残りの処理は行われず、呼び出し元に例外が伝わる。IllegalArgumentException は「引数の値が正しくない」ことを表す、Java に用意された例外 |
static void checkAge(int age) throws IllegalArgumentException |
「このメソッドは IllegalArgumentException を起こすことがある」という 宣言。メソッドの ( ) の後ろに書く |
@throws IllegalArgumentException 範囲外のとき |
Javadoc コメントで、どんなときに例外を起こすかを書く |
checkAge(100) で例外が起きると、checkAge の中の 年齢: の表示は行われず、例外が main に伝わって、catch で受け止められました。
【知識メモ】チェック例外と、throws が必須になるとき
この章で扱った例外(NumberFormatException、IllegalArgumentException など)は、throws を書かなくても使えます。throws は、呼ぶ側に「例外が起きることがある」と知らせるために書いています。
Java には、これとは別に チェック例外 と呼ばれる種類の例外(ファイルの読み書きで起きる IOException など)があります。チェック例外を起こす可能性のあるメソッドを呼ぶときは、try〜catch で受け止めるか、自分のメソッドに throws を書くかの どちらかが必須 で、書かないと「処理されない例外の型 IOException」というコンパイルエラーになります。この教材と Spring入門では、チェック例外を扱うコードは書きません。
12.6 うまくいかないときは
| 症状 | 考えられる原因と対処 |
|---|---|
try〜catch を書いたのに、プログラムが止まる |
catch に書いた例外のクラスと、実際に起きた例外が違います。スタックトレースの1行目のクラス名を、catch に書いてください。詳しくは 付録A「実行時のエラーの読み方」 を見てください。 |
try の行や、閉じる } の行に「構文エラーがあります。… を挿入して…」が出る |
try ブロックの後ろに catch がありません。try { … } catch (…) { … } の形で書いてください。 |
「age を変数に解決できません」が try の外で出る |
try ブロックの中で宣言した変数は、ブロックの外では使えません(第7章 7.6 のスコープ)。外で使う変数は、try の前で宣言してください。 |
| 「到達不能コード」 |
throw の後ろに文を書いています。throw の後ろの文は実行されないので、消すか、if の中に入れてください。 |
| 「処理されない例外の型 …」 | チェック例外を起こすメソッドを呼んでいます。この教材では使わないので、補完で別のクラスやメソッドを選んでいないかを確かめてください。 |
ほかのエラーは、付録A「よくあるコンパイルエラー」・付録A「よくある実行時のエラー」 も参考にしてください。
12.7 まとめと確認問題
まとめ
- 例外 は実行の途中で起きるエラー。受け止めなければ、スタックトレース を表示してプログラムが止まる
- スタックトレースは、1行目で「何が」、
atの行で「どこで」起きたかを表す。自分のパッケージ名で始まる行を見る。リンクをクリックするとその行に飛べる - 代表的な例外:
NullPointerException(nullを使った)、ArrayIndexOutOfBoundsException(配列の範囲外)、ArithmeticException(0 で割った)、NumberFormatException(数に変えられない文字列) -
try { … } catch (例外のクラス e) { … }で例外を受け止める。e.getMessage()でメッセージを取り出せる - 例外は呼び出し元へ伝わる。
mainまで伝わって受け止められなければ止まる -
throw new 例外のクラス("メッセージ");で例外を起こす。メソッドのthrows 例外のクラスは、例外を起こすことがあるという宣言
この章で使った書き方は、付録B「文法早見表」の「メソッドとクラス」 にまとめています。
確認問題
問1 次のスタックトレースから、何が・どこで起きたかを答えてください。Exception in thread "main" java.lang.ArithmeticException: / by zero at ch12.Main.main(Main.java:9)
解答を見る
整数を 0 で割ったことによる ArithmeticException が、Main.java の 9 行目(Main クラスの main メソッド)で起きました。
問2 Integer.parseInt(s) で起きる可能性がある例外を受け止めるように、int n = Integer.parseInt(s); を try〜catch で囲んでください。受け止めたときは「数ではありません」と表示します。
解答を見る
try { int n = Integer.parseInt(s); } catch (NumberFormatException e) { System.out.println("数ではありません"); }
問3 try ブロックの途中で例外が起きたとき、try ブロックの残りの処理はどうなりますか。
解答を見る
実行されずに飛ばされ、catch ブロックに移ります。catch ブロックが終わると、try〜catch の後ろの処理に進みます。
問4 throw と throws の違いを答えてください。
解答を見る
throw は例外を実際に起こす文で、throw new 例外のクラス(…); と書きます。throws はメソッドの宣言に付けて、「このメソッドは例外を起こすことがある」と知らせるものです。
問5 メソッドの中で起きた例外が、そのメソッドの中で受け止められなかったときは、どうなりますか。
解答を見る
そのメソッドを呼び出した側に伝わります。どこでも受け止められずに main メソッドまで伝わると、スタックトレースを表示してプログラムが止まります。

