web-dev-qa-db-ja.com

また、それを使用してアプリケーションエラーをデバッグすることができますか?

私は自分のアプリケーションを実行するときに時々それは私のようなエラーを私に与える:

Exception in thread "main" Java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.Java:16)
        at com.example.myproject.Author.getBookTitles(Author.Java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.Java:14)

人々はこれを「スタックトレース」と呼んでいます。 スタックトレースとは何ですか? 自分のプログラムで発生しているエラーについて教えてください。


この質問について - 私は初心者のプログラマーが「エラーを起こしている」ところで質問をすることがよくあります、そして彼らは単にスタックトレースが何であるか彼らができることを理解せずこれを使って。この質問は、スタックトレースの価値を理解するのに手助けが必要な初心者プログラマのための参考資料です。

581
Rob Hruska

簡単に言うと、 スタックトレース は、例外がスローされたときの中間にあったメソッド呼び出しのリストです。

簡単な例

問題の例では、アプリケーション内のどこで例外がスローされたのかを正確に判断できます。スタックトレースを見てみましょう。

Exception in thread "main" Java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.Java:16)
        at com.example.myproject.Author.getBookTitles(Author.Java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.Java:14)

これは非常に単純なスタックトレースです。 「at ...」のリストの先頭から始めると、エラーが発生した場所を特定できます。私たちが探しているのは、アプリケーションの一部である 一番上の メソッド呼び出しです。この場合、それは:

at com.example.myproject.Book.getTitle(Book.Java:16)

これをデバッグするために、Book.Javaを開いて16行を見てください。

15   public String getTitle() {
16      System.out.println(title.toString());
17      return title;
18   }

これは、上記のコードで何か(おそらくtitle)がnullであることを示しています。

一連の例外を含む例

アプリケーションは例外をキャッチし、それを別の例外の原因として再スローすることがあります。これは通常次のようになります。

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // this method it throws a NullPointerException on line 22
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

これにより、スタックトレースが表示されることがあります。

Exception in thread "main" Java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.Java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.Java:14)
Caused by: Java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.Java:22)
        at com.example.myproject.Author.getBookIds(Author.Java:36)
        ... 1 more

これと違うのは「原因」です。場合によっては、例外に複数の「原因」セクションがあります。これらの場合、通常は「根本原因」を見つける必要があります。これは、スタックトレースの最も低い「原因」セクションの1つになります。私たちの場合、それは:

Caused by: Java.lang.NullPointerException <-- root cause
        at com.example.myproject.Book.getId(Book.Java:22) <-- important line

繰り返しますが、この例外を除いて、ここでNullPointerExceptionが発生する原因を確認するには、22Book.Java行を見てください。

ライブラリコードを使ったもっとやっかいな例

通常、スタックトレースは上記の2つの例よりもはるかに複雑です。例を示します(長い例ですが、いくつかのレベルの連鎖例外を示しています)。

javax.servlet.ServletException: Something bad happened
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.Java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.Java:1157)
    at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.Java:28)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.Java:1157)
    at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.Java:33)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.Java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.Java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.Java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.Java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.Java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.Java:418)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.Java:152)
    at org.mortbay.jetty.Server.handle(Server.Java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.Java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.Java:943)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.Java:756)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.Java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.Java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.Java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.Java:582)
Caused by: com.example.myproject.MyProjectServletException
    at com.example.myproject.MyServlet.doPost(MyServlet.Java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.Java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.Java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.Java:1166)
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.Java:30)
    ... 27 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.Java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.Java:66)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.Java:64)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.Java:2329)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.Java:2822)
    at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.Java:71)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.Java:268)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.Java:321)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.Java:204)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.Java:130)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.Java:210)
    at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.Java:56)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.Java:195)
    at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.Java:50)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.Java:93)
    at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.Java:705)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.Java:693)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.Java:689)
    at Sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
    at Java.lang.reflect.Method.invoke(Method.Java:597)
    at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.Java:344)
    at $Proxy19.save(Unknown Source)
    at com.example.myproject.MyEntityService.save(MyEntityService.Java:59) <-- relevant call (see notes below)
    at com.example.myproject.MyServlet.doPost(MyServlet.Java:164)
    ... 32 more
Caused by: Java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.Java:105)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.Java:57)
    ... 54 more

この例では、もっとたくさんあります。私たちが最も心配しているのは、 私たちのコード から来ているメソッドを探すことです。これはcom.example.myprojectパッケージに含まれるものです。上の2番目の例から、まず根本的な原因を探してみましょう。

Caused by: Java.sql.SQLException

ただし、その下のすべてのメソッド呼び出しはライブラリコードです。それでは、その上の "Caused by"に移動して、コードから始まる最初のメソッド呼び出しを探します。

at com.example.myproject.MyEntityService.save(MyEntityService.Java:59)

前の例のように、MyEntityService.Javaの行で59を見てください。それがこのエラーが発生した場所です(SQLExceptionがエラーを示しているので、これは少し明らかに間違っていましたが、デバッグ手順は後にあります)。

532
Rob Hruska

私はこの答えを投稿しているので、一番上の答え(アクティビティでソートされたとき)は、単なる間違ったものではありません。

Stacktraceとは何ですか?

スタックトレースは非常に便利なデバッグツールです。キャッチされていない例外がスローされた時点(またはスタックトレースが手動で生成された時点)の呼び出しスタック(つまり、その時点までに呼び出された関数のスタック)が表示されます。エラーが発生した場所だけでなく、コードのその場所でプログラムがどのように終了したかも表示されるため、これは非常に便利です。これは次の質問につながります。

例外とは何ですか?

例外は、エラーが発生したことをランタイム環境がユーザーに知らせるために使用するものです。一般的な例は、NullPointerException、IndexOutOfBoundsException、またはArithmeticExceptionです。あなたが不可能である何かをやろうとするとき、これらのそれぞれは引き起こされます。たとえば、Nullオブジェクトを間接参照しようとすると、NullPointerExceptionがスローされます。

Object a = null;
a.toString();                 //this line throws a NullPointerException

Object[] b = new Object[5];
System.out.println(b[10]);    //this line throws an IndexOutOfBoundsException,
                              //because b is only 5 elements long
int ia = 5;
int ib = 0;
ia = ia/ib;                   //this line throws an  ArithmeticException with the 
                              //message "/ by 0", because you are trying to
                              //divide by 0, which is not possible.

Stacktraces/Exceptionsをどのように扱うべきですか?

最初に、例外の原因を突き止めてください。例外の名前をGoogleで調べて、その例外の原因を調べます。ほとんどの場合、それは誤ったコードによって引き起こされます。上記の例では、すべての例外が誤ったコードによって引き起こされています。したがって、NullPointerExceptionの例では、その時点でaがnullにならないようにすることができます。たとえば、aを初期化したり、次のようなチェックを含めることができます。

if (a!=null) {
    a.toString();
}

このように、a==nullの場合、問題の行は実行されません。他の例についても同様です。

時にはあなたはあなたが例外を受けないことを確認できない。たとえば、プログラムでネットワーク接続を使用している場合、コンピュータがインターネット接続を失うのを止めることはできません(たとえば、ユーザーがコンピュータのネットワーク接続を切断するのを止めることはできません)。この場合、ネットワークライブラリはおそらく例外をスローします。これで、例外をキャッチして handle itにする必要があります。つまり、ネットワーク接続の例では、接続を再度開くか、ユーザーまたはそのようなものに通知するようにしてください。また、catchを使用するときは常に、常に捕捉したい例外のみを捕捉してください。 すべての例外を捕捉するcatch (Exception e)のような広範なcatchステートメントは使用しないでください。そうでなければ、誤って間違った例外をキャッチして間違った方法で反応する可能性があるので、これは非常に重要です。

try {
    Socket x = new Socket("1.1.1.1", 6789);
    x.getInputStream().read()
} catch (IOException e) {
    System.err.println("Connection could not be established, please try again later!")
}

なぜcatch (Exception e)を使わないのですか?

小さな例を使用して、すべての例外を捕捉できない理由を説明しましょう。

int mult(Integer a,Integer b) {
    try {
        int result = a/b
        return result;
    } catch (Exception e) {
        System.err.println("Error: Division by zero!");
        return 0;
    }
}

このコードが実行しようとしているのは、0による除算が原因で発生する可能性のあるArithmeticExceptionをキャッチすることです。ただし、NullPointerExceptionまたはabの場合にスローされる可能性のあるnullもキャッチします。これはNullPointerExceptionを受け取るかもしれないが、それをArithmeticExceptionとして扱い、おそらく間違ったことをすることを意味します。最良のケースでは、NullPointerExceptionがあったことを見逃しています。そのようなものはデバッグをより困難にするので、そうしないでください。

_ tldr _

  1. 例外の原因を突き止めて修正し、例外がまったくスローされないようにします。
  2. 不可能な場合は、特定の例外をキャッチして処理します。

    • ただtry/catchを追加してから、例外を無視してください。しないでください。
    • 決してcatch (Exception e)を使用せず、常に特定の例外をキャッチします。それはあなたに多くの頭痛を救うでしょう。
72
Dakkaron

ロブが述べたことに追加します。アプリケーションにブレークポイントを設定すると、スタックの段階的な処理が可能になります。これにより、開発者はデバッガを使用して、メソッドが予期しないことを実行している正確な時点を確認できます。

RobはNullPointerException(NPE)を使用して一般的なものを説明しているため、次の方法でこの問題を解決できます。

次のようなパラメータを取るメソッドがある場合:void (String firstName)

このコードでは、firstNameに値が含まれていることを評価します。これは次のようにします。if(firstName == null || firstName.equals("")) return;

上記により、firstNameを安全でないパラメーターとして使用できなくなります。したがって、処理する前にnullチェックを行うことで、コードが適切に実行されることを確認できます。メソッドでオブジェクトを利用する例を拡張するために、ここを見ることができます:

if(dog == null || dog.firstName == null) return;

上記はnullをチェックするための適切な順序です。ベースオブジェクト(この場合はdog)から始めて、処理の前にすべてが有効であることを確認するために可能性のツリーを歩き始めます。順序を逆にすると、NPEがスローされ、プログラムがクラッシュする可能性があります。

21
Woot4Moo

Throwableファミリが提供するもう1つのスタックトレース機能があります - 操作する可能性 /スタックトレース情報。

標準の動作:

package test.stack.trace;

public class SomeClass {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        methodC();
    }

    public void methodC() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

スタックトレース:

Exception in thread "main" Java.lang.RuntimeException
    at test.stack.trace.SomeClass.methodC(SomeClass.Java:18)
    at test.stack.trace.SomeClass.methodB(SomeClass.Java:13)
    at test.stack.trace.SomeClass.methodA(SomeClass.Java:9)
    at test.stack.trace.SomeClass.main(SomeClass.Java:27)

操作されたスタックトレース:

package test.stack.trace;

public class SomeClass {

    ...

    public void methodC() {
        RuntimeException e = new RuntimeException();
        e.setStackTrace(new StackTraceElement[]{
                new StackTraceElement("OtherClass", "methodX", "String.Java", 99),
                new StackTraceElement("OtherClass", "methodY", "String.Java", 55)
        });
        throw e;
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

スタックトレース:

Exception in thread "main" Java.lang.RuntimeException
    at OtherClass.methodX(String.Java:99)
    at OtherClass.methodY(String.Java:55)
15
przemek hertel

名前を理解するには :スタックトレースは、最も表面的な例外(例:Service Layer Exception)から最も深いもの(例:Service Layer Exception)までの例外のリスト(または「原因による」のリスト)です。データベース例外)。スタックが先入れ先出し(FILO)であるという理由とまったく同じように、スタックは最初から最後まで発生し、その後一連の例外が発生し、表面的な例外が最後に発生しました。 1つは間に合ったが、私たちはそもそもそれを見ます。

Key 1 :ここで理解しておく必要がある重要なことは、最も深い原因が「根本的な原因」ではない可能性があるということです。その層よりも深いです。たとえば、不正なSQLクエリを実行すると、シンタックスエラーの代わりにSQLServerException接続がボットムでリセットされることがあります。これは、スタックの途中で発生する可能性があります。

- > 真ん中の根本原因を突き止めるのはあなたの仕事です。 enter image description here

Key 2 :もう1つの注意が必要だが重要なことは各 "Cause by"ブロックの内側にあり、最初の行が最も深い層であり、このブロックの最初の場所にある。例えば、

Exception in thread "main" Java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.Java:16)
           at com.example.myproject.Author.getBookTitles(Author.Java:25)
               at com.example.myproject.Bootstrap.main(Bootstrap.Java:14)

Book.Java:16がBootstrap.Java:14によって呼び出されたAuther.Java:25によって呼び出された、Book.Java:16が根本的な原因でした。ここにダイアグラムを添付してトレーススタックを時系列でソートします。 enter image description here

14
Kevin Li

他の例に加えて、 内側の(入れ子になった)クラス があり、それらは$記号で現れます。例えば:

public class Test {

    private static void privateMethod() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Runnable() {
            @Override public void run() {
                privateMethod();
            }
        };
        runnable.run();
    }
}

このスタックトレースになります:

Exception in thread "main" Java.lang.RuntimeException
        at Test.privateMethod(Test.Java:4)
        at Test.access$000(Test.Java:1)
        at Test$1.run(Test.Java:10)
        at Test.main(Test.Java:13)
8
Eugene S

他の記事ではスタックトレースとは何かについて説明していますが、それでもまだ作業が難しい場合があります。

スタックトレースを取得して例外の原因をトレースしたい場合は、 Javaスタックトレースコンソール in Eclipse を使用することを理解するのに良い出発点です。別のIDEを使用している場合は、同様の機能があるかもしれませんが、この回答はEclipseに関するものです。

まず、すべてのJavaソースがEclipseプロジェクト内でアクセス可能であることを確認してください。

次に、 Java パースペクティブで、 Console タブをクリックします(通常は一番下にあります)。コンソールビューが表示されていない場合は、メニューオプション ウィンドウ - >ビューの表示 に進み、 コンソール を選択します。

それからコンソールウィンドウで、(右側の)次のボタンをクリックしてください。

Consoles button

次に、ドロップダウンリストから[ Java Stack Trace Console ]を選択します。

スタックトレースをコンソールに貼り付けます。それはあなたのソースコードと利用可能な他のソースコードへのリンクのリストを提供します。

これはあなたが見るかもしれないものです(Eclipseドキュメンテーションからの画像):

Diagram from Eclipse documentation

最後に行われたメソッド呼び出しは、スタックの top になります。これは最上行です(メッセージテキストを除く)。スタックを降りることは時間的にさかのぼります。 2行目は1行目などを呼び出すメソッドです。

オープンソースソフトウェアを使用している場合、調べたい場合は、ソースをダウンロードしてプロジェクトに添付する必要があります。プロジェクト内のソースjarファイルをダウンロードし、 Referenced Libraries フォルダを開いてオープンソースモジュール用のjarファイル(クラスファイルを含むもの)を見つけ、右クリックして Properties を選択します。元のjarファイルを添付してください。

5
rghome