戻り値のための変数とメソッド内で使用されているvalは違う
...
なぜでしょうか。
このプログラムの例外シナリオ
- array[3] ここで添字が範囲外
- ArrayIndexOutOfBoundsException (RuntimeException)が発生する
- catch に処理が移る
- val = 10;
- return val; <— 1)
- val += 10; <— 2) finally で加算される
- return <— 3) 戻りを実行する
eclipse のデバッガで変数を追うと、val の値が 10 から 20 に変わったあとで return され、main に戻り、result の値を見ると 10 。return されたのは 20 だと思ったのに。上の 1), 2), 3) デバッガで追った動作に何か隠された処理があるのだろうか。
バイトコード
わからないときはバイトコードを調べましょう。
bash
1javap -p -v -c chapter8.Main
2
sample()のローカル変数は次のように対応しています。
- 0 val
- 1 arrayの参照
- 2 RuntimeExceptionの参照
- 3 戻り値用の無名のローカル変数 (例外処理の時だけ追加され使われます)
ローカル変数テープルに 3 は存在しませんが、メソッドのローカル変数が 4 個なのでコードを読み確認します。locals=4, args_size=0
例外処理のコードを読みます。
byte
134: astore_1
235: bipush 10
337: istore_0
438: iload_0
539: istore_3
640: iinc 0, 10
743: iload_3
844: ireturn
(34 スタックのトップにある参照をローカル変数 1 (array)に格納)
35 オペランドスタックに10をプッシュ
36 ローカル変数 0 (val) に(10)を格納 <— val = 10
37 オペランドスタックに、ローカル変数 0 (val) の値、10 をロード
38 ローカル変数 3 (戻り値) に (10) を格納 <— 戻り値をいったん退避
40 ローカル変数 0 (val) に 10 加算。20 <- finally の加算 val += 10
43 オペランドスタックに、ローカル変数 3 (戻り値) 10 をロード
44 オペランドスタックのトップを戻り値としてsampleからリターン
戻り値をいったんローカル変数 3 に退避しておき、finally の val += 10 を実行しますが、val は戻り値として使わず、ローカル変数 3 を戻り値として使います。これが「戻り値のための変数とメソッド内で使用されているvalは違う」の意味。
例外処理のなかで return が記述されているので、直前の val の値を確定した戻り値として退避したのです。
デバッガで見ることができる変数は、val, RuntomeException のみ。退避動作もデバッガでは省略されています。