때로는 응용 프로그램을 실행할 때 다음과 같은 오류가 발생합니다.
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)
사람들은 이것을 “스택 추적”이라고합니다. 스택 추적이란 무엇입니까? 내 프로그램에서 발생하는 오류에 대해 무엇을 알 수 있습니까?
이 질문에 대해-초보자 프로그래머가 “오류를 얻는”곳에서 질문이 나오고 스택 추적이 무엇인지 또는 어떻게 사용할 수 있는지 이해하지 않고 스택 추적과 임의의 코드 블록을 붙여 넣습니다. 이 질문은 스택 추적의 가치를 이해하는 데 도움이 필요한 초보자 프로그래머를위한 참고 자료입니다.
답변
간단히 말해서 스택 추적 은 예외가 발생했을 때 응용 프로그램이 수행했던 메서드 호출 목록입니다.
간단한 예
질문에 주어진 예를 통해 응용 프로그램에서 예외가 발생한 위치를 정확하게 결정할 수 있습니다. 스택 추적을 살펴 보자.
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
line을 열어 볼 수 있습니다 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
이것과 다른 점은 “Caused by”입니다. 때로는 예외에 여러 “원인”섹션이 있습니다. 이를 위해 일반적으로 스택 추적에서 가장 낮은 “원인”섹션 중 하나 인 “근본 원인”을 찾습니다. 우리의 경우 :
Caused by: java.lang.NullPointerException <-- root cause
at com.example.myproject.Book.getId(Book.java:22) <-- important line
다시 말하지만,이 예외와 함께 우리는 라인을보고 싶어 22
의 Book.java
가 발생할 수 있습니다 무엇을 참조하십시오 NullPointerException
여기.
라이브러리 코드로 더 어려운 예제
일반적으로 스택 추적은 위의 두 예제보다 훨씬 더 복잡합니다. 다음은 예제입니다 (긴 예제이지만 여러 수준의 연결 예외를 보여줍니다).
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
패키지에있는 것입니다. 위의 두 번째 예에서 먼저 근본 원인을 찾아 보려고합니다.
Caused by: java.sql.SQLException
그러나 그 아래의 모든 메소드 호출은 라이브러리 코드입니다. 위의 “Caused by”로 넘어 가서 코드에서 시작된 첫 번째 메소드 호출을 찾으십시오.
at com.example.myproject.MyEntityService.save(MyEntityService.java:59)
이전 예제에서와 같이이 오류가 발생한 곳이기 때문에 MyEntityService.java
온라인 에서 살펴 봐야 59
합니다 (SQLException은 오류를 나타 내기 때문에 오류가 발생한 것이 약간 분명하지만 디버깅 절차는 우리가 따르는 것입니다).
답변
이 답변을 게시하여 최상위 답변 (활동별로 정렬 할 때)이 잘못 된 것이 아닙니다.
Stacktrace 란 무엇입니까?
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.
Stacktrace / Exception을 어떻게 처리해야합니까?
처음에 예외의 원인을 찾으십시오. 예외 이름을 Google로 검색하여 예외의 원인을 찾으십시오. 대부분 잘못된 코드로 인해 발생합니다. 위의 예제에서 모든 예외는 잘못된 코드로 인해 발생합니다. 따라서 NullPointerException 예제의 경우 a
해당 시간이 null이 아닌지 확인할 수 있습니다. 예를 들어 a
다음과 같은 검사를 초기화 하거나 포함시킬 수 있습니다.
if (a!=null) {
a.toString();
}
이런 식으로 문제가되는 행은 if가 실행되지 않습니다 a==null
. 다른 예제도 마찬가지입니다.
때로는 예외가 발생하지 않았는지 확인할 수 없습니다. 예를 들어, 프로그램에서 네트워크 연결을 사용하는 경우 컴퓨터가 인터넷 연결을 잃는 것을 막을 수 없습니다 (예 : 사용자가 컴퓨터의 네트워크 연결을 끊는 것을 막을 수 없습니다). 이 경우 네트워크 라이브러리에서 예외가 발생합니다. 이제 예외를 잡아서 처리 해야합니다. 즉, 네트워크 연결의 예에서 연결을 다시 열거 나 사용자에게 알리거나 이와 유사한 것을 알려야합니다. 또한 catch를 사용할 때마다 항상 catch하려는 예외 만 포착하고 다음 과 같은 넓은 catch 문을 사용하지 마십시오.catch (Exception e)
그것은 모든 예외를 잡을 것입니다. 그렇지 않으면 실수로 잘못된 예외를 포착하여 잘못된 방식으로 대응할 수 있기 때문에 이것은 매우 중요합니다.
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;
}
}
어떤이 코드가 시도된다 캐치하는 것입니다 ArithmeticException
0으로 가능한 부문에 의한 그러나 그것은 또한 가능한 잡는다 NullPointerException
경우 발생되는 a
또는 b
됩니다 null
. 이것은 당신이 얻을 수 NullPointerException
있지만 그것을 ArithmeticException으로 취급하고 아마도 잘못된 일을 할 것임을 의미합니다. 가장 좋은 경우에는 여전히 NullPointerException이 있다는 것을 그리워합니다. 그런 것들로 인해 디버깅이 훨씬 어려워 지므로 그렇게하지 마십시오.
TLDR
- 예외의 원인을 파악하고 수정하여 예외가 전혀 발생하지 않도록하십시오.
-
1. 불가능한 경우 특정 예외를 잡아서 처리하십시오.
- try / catch를 추가 한 다음 예외를 무시하지 마십시오. 하지마!
- 를 사용하지 말고
catch (Exception e)
항상 특정 예외를 잡으십시오. 그것은 당신에게 많은 두통을 저장합니다.
답변
Rob이 언급 한 내용을 추가합니다. 응용 프로그램에서 중단 점을 설정하면 스택을 단계별로 처리 할 수 있습니다. 이를 통해 개발자는 디버거를 사용하여 메서드가 예상치 못한 작업을 수행하는 정확한 지점을 확인할 수 있습니다.
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가 발생하여 프로그램이 중단 될 수 있습니다.
답변
Throwable 제품군은 스택 추적 정보 를 조작 할 수있는 또 하나의 스택 추적 기능을 제공 합니다.
표준 행동 :
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)
답변
이름을 이해하려면 : 스택 추적은 가장 예외적 인 예외 (예 : 서비스 계층 예외)에서 가장 깊은 예외 (예 : 데이터베이스 예외)에 이르기까지 예외 목록 (또는 “원인”목록)이라고 할 수 있습니다. 우리가 그것을 ‘스택’이라고 부르는 이유와 마찬가지로 스택이 FILO (First in Last out)이기 때문에 가장 처음부터 가장 깊은 예외가 발생한 다음 일련의 예외가 발생하여 표면 예외가 마지막이었습니다. 하나는 제 시간에 일어 났지만 우리는 처음에 그것을 봅니다.
키 1 : 여기서 이해해야 할 까다 롭고 중요한 것은 가장 깊은 원인이 “근본 원인”이 아닐 수 있습니다. “불량한 코드”를 작성하면 그 아래 계층보다 더 깊은 예외가 발생할 수 있기 때문입니다. 예를 들어, 잘못된 SQL 쿼리로 인해 syndax 오류 대신 bottem에서 SQLServerException 연결이 재설정 될 수 있으며 이는 스택의 중간에있을 수 있습니다.
키 2 : 또 다른 까다 롭지 만 중요한 것은 각 “원인”블록 내부에 있으며, 첫 번째 줄은 가장 깊은 레이어이며이 블록의 첫 번째 장소입니다. 예를 들어
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이 근본 원인이었습니다. 여기에 시간순으로 트레이스 스택을 정렬하는 다이어그램을 첨부하십시오.
답변
다른 예제에 추가하기 위해 부호 와 함께 나타나는 내부 (중첩) 클래스 가 $
있습니다. 예를 들면 다음과 같습니다.
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)
답변
다른 게시물은 스택 추적이 무엇인지 설명하지만 여전히 작업하기가 어려울 수 있습니다.
스택 추적을 받고 예외의 원인을 추적하려는 경우 Eclipse 에서 Java 스택 추적 콘솔 을 사용하는 것이 좋습니다 . 다른 IDE를 사용하는 경우 비슷한 기능이있을 수 있지만이 답변은 Eclipse에 관한 것입니다.
먼저 Eclipse 프로젝트에서 모든 Java 소스에 액세스 할 수 있는지 확인하십시오.
그런 다음 Java Perspective에서 콘솔 탭 (일반적으로 맨 아래)을 클릭하십시오 . 콘솔보기가 표시되지 않으면 메뉴 옵션 창->보기 표시 로 이동하여 콘솔을 선택하십시오 .
그런 다음 콘솔 창에서 다음 버튼을 클릭하십시오 (오른쪽)
드롭 다운 목록에서 Java 스택 추적 콘솔 을 선택 하십시오.
스택 추적을 콘솔에 붙여 넣습니다. 그런 다음 소스 코드 및 사용 가능한 다른 소스 코드에 대한 링크 목록을 제공합니다.
이것은 당신이 볼 수있는 것입니다 (Eclipse 문서의 이미지).
가장 최근에 호출 된 메소드 호출은 스택 의 맨 위 입니다 (메시지 텍스트 제외). 스택을 내려 가면 시간이지나갑니다. 두 번째 줄은 첫 번째 줄 등을 호출하는 방법입니다.
오픈 소스 소프트웨어를 사용하는 경우 검사하려는 경우 소스를 다운로드하여 프로젝트에 첨부해야합니다. 프로젝트에서 소스 jar을 다운로드하고 Referenced Libraries 폴더를 열어 공개 소스 모듈 (클래스 파일이있는 모듈)의 jar을 찾은 다음 마우스 오른쪽 단추를 클릭하고 특성을 선택 하고 소스 jar을 첨부하십시오.