Dấu vết ngăn xếp là gì và làm cách nào tôi có thể sử dụng nó để gỡ lỗi các ứng dụng của mình?


643

Đôi khi khi tôi chạy ứng dụng, nó báo lỗi như sau:

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)

Mọi người đã gọi nó là "dấu vết ngăn xếp". Dấu vết ngăn xếp là gì? Nó có thể cho tôi biết gì về lỗi xảy ra trong chương trình của tôi?


Về câu hỏi này - khá thường xuyên tôi thấy một câu hỏi xuất hiện trong đó một lập trình viên mới đang "gặp lỗi" và họ chỉ cần dán dấu vết ngăn xếp của họ và một số khối mã ngẫu nhiên mà không hiểu dấu vết ngăn xếp là gì hoặc làm thế nào họ có thể sử dụng nó. Câu hỏi này nhằm mục đích tham khảo cho các lập trình viên mới làm quen, những người có thể cần trợ giúp để hiểu giá trị của dấu vết ngăn xếp.


25
Ngoài ra, nếu một dòng stacktrace không chứa tên tệp và số dòng, lớp cho dòng đó không được biên dịch với thông tin gỡ lỗi.
Thorbjørn Ravn Andersen

Câu trả lời:


589

Nói một cách đơn giản, theo dõi ngăn xếp là một danh sách các lệnh gọi phương thức mà ứng dụng nằm ở giữa khi một Ngoại lệ được đưa ra.

Ví dụ đơn giản

Với ví dụ được đưa ra trong câu hỏi, chúng ta có thể xác định chính xác nơi ngoại lệ được ném trong ứng dụng. Chúng ta hãy xem dấu vết ngăn xếp:

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)

Đây là một dấu vết ngăn xếp rất đơn giản. Nếu chúng ta bắt đầu ở đầu danh sách "tại ...", chúng ta có thể biết lỗi xảy ra ở đâu. Những gì chúng ta đang tìm kiếm là trên cùng phương pháp gọi đó là một phần của ứng dụng của chúng tôi. Trong trường hợp này, đó là:

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

Để gỡ lỗi này, chúng ta có thể mở ra Book.javavà xem xét dòng 16, đó là:

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

Điều này sẽ chỉ ra rằng một cái gì đó (có thể title) nằm nulltrong đoạn mã trên.

Ví dụ với một chuỗi các trường hợp ngoại lệ

Đôi khi các ứng dụng sẽ bắt một Ngoại lệ và ném lại nó là nguyên nhân của Ngoại lệ khác. Điều này thường trông giống như:

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   }

Điều này có thể cung cấp cho bạn một dấu vết ngăn xếp trông giống như:

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

Điều khác biệt ở cái này là "Nguyên nhân bởi". Đôi khi các trường hợp ngoại lệ sẽ có nhiều phần "Nguyên nhân bởi". Đối với những điều này, bạn thường muốn tìm "nguyên nhân gốc", đây sẽ là một trong những phần "Nguyên nhân" thấp nhất trong theo dõi ngăn xếp. Trong trường hợp của chúng tôi, đó là:

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

Một lần nữa, với ngoại lệ này chúng tôi muốn nhìn vào dòng 22của Book.javađể xem những gì có thể gây ra NullPointerExceptionở đây.

Ví dụ đáng ngại hơn với mã thư viện

Thông thường dấu vết ngăn xếp phức tạp hơn nhiều so với hai ví dụ trên. Đây là một ví dụ (đây là một ví dụ dài, nhưng thể hiện một số mức độ ngoại lệ bị xiềng xích):

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

Trong ví dụ này, có nhiều hơn nữa. Điều chúng tôi chủ yếu quan tâm là tìm kiếm các phương thức từ mã của chúng tôi , đó sẽ là bất cứ thứ gì trong com.example.myprojectgói. Từ ví dụ thứ hai (ở trên), trước tiên chúng tôi muốn xem xét nguyên nhân gốc, đó là:

Caused by: java.sql.SQLException

Tuy nhiên, tất cả các cuộc gọi phương thức theo đó là mã thư viện. Vì vậy, chúng tôi sẽ chuyển đến "Nguyên nhân bởi" phía trên nó và tìm kiếm cuộc gọi phương thức đầu tiên có nguồn gốc từ mã của chúng tôi, đó là:

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

Giống như trong các ví dụ trước, chúng ta nên xem xét MyEntityService.javatrực tuyến 59, bởi vì đó là lỗi bắt nguồn (điều này hơi rõ ràng là đã xảy ra lỗi, vì SQLException nêu lỗi, nhưng quy trình gỡ lỗi là những gì chúng ta theo sau).


3
@RobHruska - Giải thích rất rõ. +1. Bạn có biết bất kỳ trình phân tích cú pháp nào lấy dấu vết ngoại lệ làm chuỗi và cung cấp các phương thức hữu ích để phân tích stacktrace không? - như getLastCausedBy () hoặc getCausedByForMyAppCode ("com.example.myproject")
Andy Dufresne

1
@AndyDufresne - Tôi cũng không bắt gặp, nhưng một lần nữa tôi cũng không thực sự nhìn.
Rob Hruska

1
Đề xuất cải tiến: giải thích dòng đầu tiên của dấu vết ngăn xếp bắt đầu bằng Exception in thread "main"ví dụ đầu tiên của bạn. Tôi nghĩ sẽ rất hữu ích khi giải thích rằng dòng này thường đi kèm với một thông điệp, chẳng hạn như giá trị của một biến, có thể giúp chẩn đoán vấn đề. Tôi đã cố gắng tự chỉnh sửa, nhưng tôi đang cố gắng để phù hợp với những ý tưởng này vào cấu trúc câu trả lời của bạn.
Code-Apprentice

5
Ngoài ra java 1.7 đã thêm "Suppressed:" - liệt kê các dấu vết ngăn xếp ngoại lệ bị chặn trước khi hiển thị "Nguyên nhân bởi:" cho ngoại lệ này. Nó tự động được sử dụng bởi cấu trúc try-with-resource: docs.oracle.com/javase/specs/jls/se8/html/ trộm và chứa ngoại lệ nếu có bất kỳ trường hợp nào bị ném trong quá trình đóng tài nguyên.
dhblah

Có một openjdk.java.net/jeps/8220715 của JEP nhằm cải thiện hơn nữa tính dễ hiểu của các NPE đặc biệt bằng cách cung cấp các chi tiết như "Không thể viết trường 'nullInstanceField' vì 'this.nullInstanceField' là null."
Mahatma_Firth_Error

80

Tôi đang đăng câu trả lời này để câu trả lời trên cùng (khi được sắp xếp theo hoạt động) không phải là câu trả lời sai.

Một Stacktrace là gì?

Một stacktrace là một công cụ gỡ lỗi rất hữu ích. Nó hiển thị ngăn xếp cuộc gọi (có nghĩa là ngăn xếp các hàm được gọi cho đến thời điểm đó) tại thời điểm một ngoại lệ chưa được phát hiện (hoặc thời điểm ngăn xếp được tạo thủ công). Điều này rất hữu ích vì nó không chỉ cho bạn biết lỗi xảy ra ở đâu, mà còn là cách chương trình kết thúc ở nơi đó của mã. Điều này dẫn đến câu hỏi tiếp theo:

Ngoại lệ là gì?

Ngoại lệ là những gì môi trường thời gian chạy sử dụng để cho bạn biết rằng đã xảy ra lỗi. Các ví dụ phổ biến là NullPulumException, IndexOutOfBoundException hoặc ArithaturesException. Mỗi điều này được gây ra khi bạn cố gắng làm điều gì đó không thể. Ví dụ: một NullPulumException sẽ bị ném khi bạn cố gắng hủy đăng ký một đối tượng Null:

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.

Làm thế nào tôi nên đối phó với Stacktraces / Ngoại lệ?

Đầu tiên, tìm hiểu những gì gây ra Ngoại lệ. Hãy thử google tên của ngoại lệ để tìm hiểu, nguyên nhân của ngoại lệ đó là gì. Hầu hết thời gian nó sẽ được gây ra bởi mã không chính xác. Trong các ví dụ đã cho ở trên, tất cả các ngoại lệ được gây ra bởi mã không chính xác. Vì vậy, đối với ví dụ NullPulumException, bạn có thể chắc chắn rằng anó không bao giờ rỗng tại thời điểm đó. Bạn có thể, ví dụ, khởi tạo ahoặc bao gồm một kiểm tra như thế này:

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

Bằng cách này, dòng vi phạm không được thực thi nếu a==null. Các ví dụ khác cũng vậy.

Đôi khi bạn không thể chắc chắn rằng mình không có ngoại lệ. Ví dụ: nếu bạn đang sử dụng kết nối mạng trong chương trình của mình, bạn không thể ngăn máy tính mất kết nối internet (ví dụ: bạn không thể ngăn người dùng ngắt kết nối mạng của máy tính). Trong trường hợp này, thư viện mạng có thể sẽ ném một ngoại lệ. Bây giờ bạn nên bắt ngoại lệ và xử lý nó. Điều này có nghĩa, trong ví dụ với kết nối mạng, bạn nên cố gắng mở lại kết nối hoặc thông báo cho người dùng hoặc đại loại như thế. Ngoài ra, bất cứ khi nào bạn sử dụng lệnh bắt, luôn luôn chỉ bắt ngoại lệ bạn muốn bắt, không sử dụng câu lệnh bắt rộng nhưcatch (Exception e)Điều đó sẽ bắt tất cả các ngoại lệ. Điều này rất quan trọng, vì nếu không, bạn có thể vô tình bắt nhầm ngoại lệ và phản ứng sai cách.

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!")
}

Tại sao tôi không nên sử dụng catch (Exception e)?

Hãy sử dụng một ví dụ nhỏ để chỉ ra lý do tại sao bạn không nên nắm bắt tất cả các ngoại lệ:

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;
    }
}

Những gì mã này đang cố gắng làm là bắt nguyên ArithmeticExceptionnhân của một phép chia có thể bằng 0. Nhưng nó cũng bắt được một khả năng có thể NullPointerExceptionđược ném nếu ahoặc bnull. Điều này có nghĩa là, bạn có thể nhận được một NullPointerExceptionnhưng bạn sẽ coi nó như một Số học ngoại lệ và có thể làm sai. Trong trường hợp tốt nhất bạn vẫn nhớ rằng đã có NullPulumException. Những thứ như vậy làm cho việc gỡ lỗi khó hơn nhiều, vì vậy đừng làm điều đó.

TLD

  1. Tìm hiểu nguyên nhân của ngoại lệ là gì và khắc phục nó, để nó không ném ngoại lệ nào cả.
  2. Nếu 1. không thể, bắt ngoại lệ cụ thể và xử lý nó.

    • Không bao giờ chỉ cần thêm một thử / bắt và sau đó chỉ cần bỏ qua ngoại lệ! Đừng làm vậy!
    • Không bao giờ sử dụng catch (Exception e), luôn luôn bắt ngoại lệ cụ thể. Điều đó sẽ giúp bạn tiết kiệm rất nhiều đau đầu.

1
lời giải thích hay cho lý do tại sao chúng ta nên tránh mặt nạ lỗi
Sudip Bhandari

2
Tôi đang đăng câu trả lời này để câu trả lời trên cùng (khi được sắp xếp theo hoạt động) không phải là câu trả lời sai. Tôi không biết bạn đang nói về vấn đề nào vì điều này có lẽ đã thay đổi ngay bây giờ. Nhưng câu trả lời được chấp nhận chắc chắn là nhiều
can thiệp

1
Cái tôi muốn nói đã bị xóa ngay bây giờ, theo như tôi biết. Về cơ bản, nó nói "chỉ cần thử {} Catch (Exception e) {} và bỏ qua tất cả các lỗi". Câu trả lời được chấp nhận cũ hơn rất nhiều so với câu trả lời của tôi, vì vậy tôi đã nhắm đến việc đưa ra một chút quan điểm khác về vấn đề này. Tôi không nghĩ rằng nó giúp bất cứ ai chỉ cần sao chép câu trả lời của người khác hoặc che đậy những gì người khác đã che đậy tốt.
Dakkaron

Thật sai lầm khi nói "Đừng bắt ngoại lệ", đó chỉ là một trường hợp sử dụng. Ví dụ của bạn là tuyệt vời, nhưng làm thế nào về nơi bạn đang ở đầu vòng lặp chủ đề của bạn (bên trong chạy)? Bạn LUÔN LUÔN bắt ngoại lệ (hoặc có thể Ném được) ở đó và ghi nhật ký để nó không biến mất một cách vô hình (Các ngoại lệ được ném từ chạy thường không được ghi lại chính xác trừ khi bạn thiết lập luồng / logger để làm như vậy).
Bill K

1
Tôi không bao gồm trường hợp đặc biệt này vì nó chỉ quan trọng với đa luồng. Trong một luồng đơn, một ngoại lệ bị rò rỉ sẽ giết chết chương trình và được ghi lại rõ ràng. Nếu ai đó không biết cách xử lý ngoại lệ đúng cách, họ thường không biết cách sử dụng đa luồng.
Dakkaron

21

Để thêm vào những gì Rob đã đề cập. Đặt điểm dừng trong ứng dụng của bạn cho phép xử lý từng bước của ngăn xếp. Điều này cho phép nhà phát triển sử dụng trình gỡ lỗi để xem tại thời điểm chính xác mà phương thức đang làm một cái gì đó không dự đoán được.

Vì Rob đã sử dụng NullPointerException(NPE) để minh họa một cái gì đó phổ biến, chúng tôi có thể giúp loại bỏ vấn đề này theo cách sau:

nếu chúng ta có một phương thức lấy các tham số như: void (String firstName)

Trong mã của chúng tôi, chúng tôi muốn đánh giá firstNamecó chứa một giá trị, chúng tôi sẽ làm như vậy:if(firstName == null || firstName.equals("")) return;

Ở trên ngăn chúng ta sử dụng firstNamenhư một tham số không an toàn. Do đó, bằng cách thực hiện kiểm tra null trước khi xử lý, chúng tôi có thể giúp đảm bảo rằng mã của chúng tôi sẽ chạy đúng. Để mở rộng trên một ví dụ sử dụng một đối tượng với các phương thức, chúng ta có thể xem tại đây:

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

Trên đây là thứ tự thích hợp để kiểm tra null, chúng ta bắt đầu với đối tượng cơ sở, con chó trong trường hợp này và sau đó bắt đầu đi xuống cây khả năng để đảm bảo mọi thứ đều hợp lệ trước khi xử lý. Nếu đơn hàng bị đảo ngược, NPE có khả năng bị ném và chương trình của chúng tôi sẽ bị sập.


Đã đồng ý. Cách tiếp cận này có thể được sử dụng để tìm ra tham chiếu nào trong một tuyên bố là nullkhi một NullPointerExceptionđang được kiểm tra, ví dụ.
Rob Hruska

16
Khi làm việc với String, nếu bạn muốn sử dụng phương thức equals, tôi nghĩ rằng nên sử dụng hằng số ở phía bên trái của phép so sánh, như sau: Thay vì: if (firstName == null || firstName.equals ("" )) trở về; Tôi luôn sử dụng: if ((""). Bằng (FirstName)) Điều này ngăn ngoại lệ Nullpulum
Torres

15

Có thêm một tính năng stacktrace được cung cấp bởi gia đình throwable - khả năng thao túng thông tin theo dõi ngăn xếp.

Hành vi tiêu chuẩn:

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();
    }
}

Dấu vết ngăn xếp:

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)

Thao tác theo dõi ngăn xếp:

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();
    }
}

Dấu vết ngăn xếp:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)

2
Tôi không biết tôi cảm thấy thế nào về điều này ... với bản chất của luồng, tôi sẽ khuyên các nhà phát triển mới không xác định dấu vết ngăn xếp của riêng họ.
PeonProgrammer

15

Để hiểu tên : Theo dõi ngăn xếp là một danh sách Ngoại lệ (hoặc bạn có thể nói danh sách "Nguyên nhân do"), từ Ngoại lệ bề mặt nhất (ví dụ: Ngoại lệ lớp dịch vụ) đến trường hợp sâu nhất (ví dụ: Ngoại lệ cơ sở dữ liệu). Giống như lý do chúng ta gọi nó là 'stack' là vì stack là First in Last out (FILO), ngoại lệ sâu sắc nhất đã xảy ra ngay từ đầu, sau đó một chuỗi ngoại lệ được tạo ra một loạt hậu quả, Ngoại lệ là lần cuối một điều đã xảy ra trong thời gian, nhưng chúng ta thấy nó ở nơi đầu tiên.

Khóa 1 : Một điều khó khăn và quan trọng ở đây cần phải hiểu là: nguyên nhân sâu xa nhất có thể không phải là "nguyên nhân gốc rễ", bởi vì nếu bạn viết một số "mã xấu", nó có thể gây ra một số ngoại lệ bên dưới lớp sâu hơn lớp của nó. Ví dụ: truy vấn sql xấu có thể khiến thiết lập lại kết nối SQLServerException trong nút thay vì lỗi syndax, có thể ở giữa ngăn xếp.

-> Xác định vị trí nguyên nhân gốc ở giữa là công việc của bạn. nhập mô tả hình ảnh ở đây

Phím 2 : Một điều khó khăn nhưng quan trọng khác nằm trong mỗi khối "Nguyên nhân bởi", dòng đầu tiên là lớp sâu nhất và xảy ra ở vị trí đầu tiên cho khối này. Ví dụ,

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 được gọi bởi Auther.java:25, được gọi bởi Bootstrap.java:14, Book.java:16 là nguyên nhân gốc. Ở đây đính kèm một sơ đồ sắp xếp ngăn xếp theo dõi theo thứ tự thời gian. nhập mô tả hình ảnh ở đây


8

Chỉ cần thêm vào các ví dụ khác, có các lớp bên trong (lồng nhau) xuất hiện cùng với $dấu hiệu. Ví dụ:

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();
    }
}

Sẽ dẫn đến theo dõi ngăn xếp này:

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)

5

Các bài viết khác mô tả một dấu vết ngăn xếp là gì, nhưng nó vẫn có thể khó làm việc với.

Nếu bạn nhận được một dấu vết ngăn xếp và muốn theo dõi nguyên nhân của ngoại lệ, một điểm khởi đầu tốt để hiểu nó là sử dụng Bảng điều khiển theo dõi ngăn xếp Java trong Eclipse . Nếu bạn sử dụng một IDE khác có thể có một tính năng tương tự, nhưng câu trả lời này là về Eclipse.

Đầu tiên, đảm bảo rằng bạn có tất cả các nguồn Java có thể truy cập được trong một dự án Eclipse.

Sau đó, trong phối cảnh Java , bấm vào tab Console (thường ở dưới cùng). Nếu giao diện Bảng điều khiển không hiển thị, hãy chuyển đến tùy chọn menu Cửa sổ -> Hiển thị Chế độ xem và chọn Bảng điều khiển .

Sau đó trong cửa sổ giao diện điều khiển, nhấp vào nút sau (bên phải)

Nút điều khiển

và sau đó chọn Java Stack Trace Console từ danh sách thả xuống.

Dán dấu vết ngăn xếp của bạn vào bàn điều khiển. Sau đó, nó sẽ cung cấp một danh sách các liên kết vào mã nguồn của bạn và bất kỳ mã nguồn nào khác có sẵn.

Đây là những gì bạn có thể thấy (hình ảnh từ tài liệu Eclipse):

Sơ đồ từ tài liệu Eclipse

Cuộc gọi phương thức gần đây nhất được thực hiện sẽ là đỉnh của ngăn xếp, là dòng trên cùng (không bao gồm văn bản thông báo). Đi xuống ngăn xếp đi ngược thời gian. Dòng thứ hai là phương thức gọi dòng đầu tiên, v.v.

Nếu bạn đang sử dụng phần mềm nguồn mở, bạn có thể cần tải xuống và đính kèm vào dự án của mình các nguồn nếu bạn muốn kiểm tra. Tải xuống các tệp nguồn, trong dự án của bạn, mở thư mục Thư viện tham chiếu để tìm bình của bạn cho mô-đun nguồn mở (tệp có tệp lớp) sau đó nhấp chuột phải, chọn Thuộc tính và đính kèm tệp nguồn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.