Javaのエラー解決の王道~スタックトレースの読み方~

プログラミング中に必ずと言っていいほど発生するのがエラーです。

画面いっぱいに表示されるエラーメッセージに圧倒されそっと画面を閉じてしまうなんて人も多いかもしれませんが、実はこのエラーメッセージを読めるか否かでプログラミングの理解、スピードは大きく変わります。

そもそもエラーメッセージとはなぜそのようなエラーが発生したかを知らせてくれる情報なのですからそこに問題解決の糸口があるのです。今回はJavaのエラーメッセージであるスタックトレースの読み方を理解しましょう。

スタックトレースとは?

Javaプログラミング中に下のようなエラーメッセージを見たことがあると思いますが、これがスタックトレースです。

Exception in thread “main” java.lang.NumberFormatException: For input string: “b”
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at com.ittoybox.error.Calc.add(Calc.java:7)
at com.ittoybox.error.Main.main(Main.java:8)

上記スタックトレースを発生させるプログラムは以下の通りです。


package com.ittoybox.error;

public class Main {

  public static void main(String[] args) {
    Calc calc = new Calc();
    System.out.println(calc.add("1", "2")); //正常終了
    System.out.println(calc.add("1", "b")); //異常終了
  }
}


package com.ittoybox.error;

public class Calc {

  public int add(String a , String b) {
    int aa = Integer.valueOf(a);
    int bb = Integer.valueOf(b);
    return aa + bb;
  }

}

スタックトレースの読み方を理解しよう

もう一度スタックトレースを見てみましょう。

スタックトレース3

Javaのスタックトレースには以下2つの情報が含まれています。

  1. エラー情報
  2. スタックトレース情報

これらの情報をどのように読み解けばよいか解説します。

エラー情報を理解しよう!!

まず、確認すべきポイントとしては1.エラー情報です。

どのようなエラーが発生したかを確認しましょう。

上記例ですと発生しているエラーはNumberFormatExceptionが発生しています。NumberFormatExceptionとはJavaの例外の一種で数値に変換できない場合に発生します。

「For input string: b」から文字列の「b」が入力されていることが読み取れます。

よって今回は数字に変換しようとしたけど変換できない文字が入力されたためエラーとなります。

Javaの例外はJavadocを見ることである程度発生原因がつかめます。

代表的な例外は以下の通りです。

  • NullPointerException:利用するオブジェクトがNull
  • IndexOutOfBoundsException:配列等数があるオブジェクトに対してインデックスが要素数より大きい(または小さい)
  • などなど

エラーは発生した場所を特定しよう!!

エラー内容がわかったら次にエラーの場所を特定しましょう。

エラーの場所は2.スタックトレース情報から知ることができます。

そもそもスタックトレースとは何かというと「スタック(積み上げた)トレース(追跡情報)」です。

もう少しプログラミングっぽく言い直すと「プログラムが実行された情報を積み上げた情報」すなわち「プログラムが実行された情報を時系列に確認できる情報」です。

もう一度スタックトレース情報だけを見てみます。

スタックトレース情報

プログラムを実行した情報を積み上げるのですから下から上に見ていきます。

スタックトレース情報を解説すると以下のようになります。

  1. com.ittoybox.error.Mainクラスのmainメソッドが実行された(Mainクラスの8行目)
  2. com.ittoybox.error.Calcクラスのaddメソッドが実行された(Calcクラスの7行目)
  3. java.lang.IntegerクラスのvalueOfメソッドが実行された(Integerクラスの766行目)
  4. java.lang.IntegerクラスのparseIntメソッドが実行された(Integerクラスの580行目)
  5. →そしてNumberFormatExceptionが発生した。

あとはここからどこでエラーが発生しているかを特定するだけになります。きちんとやるのであればデバッグ機能を使い実行しているプログラム、変数を逐一見て確認することになりますが、手っ取り早く確認する方法をお教えします。

それはずばり、

スタックトレース情報を上から順番に見ていき、自分(またはチームメンバー)が作ったクラス、メソッド周辺を疑え!!

です。

というのも、Javaで標準に組み込まれている機能やネットで公開されているライブラリはしっかりテストしてリリースされておりバグが混入されている可能性は低いです。

ということは十分テストをしていない箇所(自分やチームメンバーが作ったばかりの機能)を疑うほうが効率が良いということです。

もちろんすべてのケースであてはまるわけではありませんが、可能性として高いです。

ということで今回の例ですと、以下のプログラム周辺が怪しいということになります。

at com.ittoybox.error.Calc.add(Calc.java:7)

デバッグして確認してみると、

エラー特定

(Mainクラスの8行目から実行されたCalc.addメソッドの)7行目でInteger.valueOfに「b」という文字列が入っておりこれを数値変換しようとしていることが分かります。

エラーの修正

エラーの原因と場所が特定できたら後はプログラムの修正です。

対応方法はいくつか考えられます。

  • Mainクラス側で文字列だったらCalc.addメソッドに渡さない
  • Calcメソッド側で数値に変換できる場合のみ変換、それ以外は(例えば)0として扱う
  • などなど

今回は「Calcメソッド側で数値に変換できる場合のみ変換、それ以外は0として扱う」方針でプログラムを修正します。

Calc.addを以下のように修正しました。


package com.ittoybox.error;

public class Calc {

  public int add(String a , String b) {
    int aa = 0;
    int bb = 0;

    try {
      aa = Integer.valueOf(a);
    } catch (NumberFormatException e) {
      //数値に変換できない場合は何もしない
    }

    try {
      bb = Integer.valueOf(b);
    } catch (NumberFormatException e) {
      //数値に変換できない場合は何もしない
    }

    return aa + bb;
  }

}

数値変換してエラーとなったらNumberFormatExceptionが発生するのでその場合は何もしない対応をすることでプログラムを実現しました。

実行するとコンソールに以下のように表示されエラーが発生しないことが分かります。

3
1

スポンサーリンク
スポンサードリンク
スポンサードリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする