📁 프로그램 오류
프로그램이 실행중 오작동하거나 비정상적으로 종료되게 만드는 원인
발생시점에 따라 컴파일에러, 런타임에러, 논리적에러가 있다.
컴파일에러
프로그램을 컴파일할 떄 발생하는 오류
런타임에러
프로그램 실행도중 발생하는 오류
논리적에러
프로그램이 작성 의도와 다르게 동작하는 오류
런타임에러
컴파일러는 컴파일시 오타나 잘못된 구문, 타입 체크 등을 통해 에러 여부를 알려준다.
그러나 표면적인 소스코드만을 체크할 뿐 실행 중 발생할 수 있는 잠재적인 오류까지 검사할 수는 없다.
따라서 문제없이 컴파일이 되더라도 런타임에 오류가 발생할 수 있다.
에러
프로그램 코드에 의해 수습될 수 없는 심각한 오류
예외
프로그램 코드에 의해 수습될 수 있는 다소 미약한 오류
📁 예외클래스의 계층구조
자바에서는 실행 중 발생할 수 있는 런타임에러를 클래스로 정의하였다.
Exception 클래스
예외클래스의 최고 조상.
런타임예외를 제외하면 사용자의 실수와 같은 외적인 요인에 의해 발생하는 Checked 예외이다.
🤔 Checked 예외
컴파일러가 예외 처리 여부를 확인하는 예외.
예외를 처리하지 않으면 컴파일 오류가 발생하기 때문에 예외처리가 필수적이다.
RuntimeException 클래스
프로그래머의 실수로 발생하는 예외
🤔 Unchecked 예외
컴파일러가 예외 처리 여부를 확인하지 않는다.
예외처리는 선택적으로 컴파일에 지장이 없다.
📁 예외처리
프로그램 실행 시 발생할 수 있는 예외에 대비해 코드를 작성하는 것
프로그램의 비정상적인 종료를 막고 정상적인 실행상태를 유지하기 위함이다.
예외를 처리하지 않을 경우 프로그램이 비정상적으로 종료되며 JVM의 예외처리기에 의해 예외의 원인이 출력된다.
📁 try-catch문
특정한 예외가 발생했을 때 이를 처리하기 위해 수행할 코드를 작성하는 구문.
try {
// 예외가 발생할 가능성이 있는 문장
} catch(Exception e) {
// 예외가 발생했을 때 이를 처리하기 위해 수행할 문장
} finally {
// 예외 발생 여부와 상관없이 항상 수행되어야 하는 문장
}
구문 흐름
- try{}에서 예외가 발생하면 해당 예외클래스의 인스턴스가 생성된다.
- 생성된 예외와 일치하는 catch{}이 있는지 확인하고 블럭 내의 코드를 수행한다.
- finally{}의 내용이 수행된다.
📂 try 블럭
예외가 발생할 가능성이 있는 코드를 작성한다.
try 블럭에서 예외가 발생하면 그 이후의 문장들은 수행되지 않기 때문에 범위 선정이 중요하다.
📂 catch 블럭
발생 가능성이 있는 예외를 처리하기 위한 코드를 작성한다.
- 하나의 try블럭 밑에 다수의 catch 블럭을 선언할 수 있다.
- 상속관계에 있는 catch 블럭들의 경우 반드시 조상타입 블럭이 자손타입 블럭보다 밑에 있어야 한다.
- 발생한 예외와 최초로 일치하는 catch 블럭을 찾으면 그 이후의 catch 블럭은 탐색하지 않는다.
- 일치하는 catch 블럭이 없으면 예외가 처리되지 못해 프로그램이 비정상적으로 종료된다.
멀티 catch 블럭
하나의 블럭에서 여러 예외를 처리할 수 있으며 여러개의 예외에 같은 내용을 수행해야 할 때 사용한다.
상속관계에 있는 블럭은 합칠 수 없다.
catch(ExceptionA | ExceptionB e) {}
예외 정보 출력
예외 클래스에 있는 메서드를 통해 예외와 관련된 정보를 출력하거나 문자열로 얻을 수 있다.
public void printStackTrace() // 예외발생 당시 호출스택에 있었던 메서드 정보와 예외 메세지를 화면에 출력
public String getMessage() // 생성된 예외 인스턴스에 저장된 메세지 문자열을 반환
📂 finally 블럭
예외의 발생과 상관없이 처리되어야 하는 문장을 작성한다.
- 예외가 처리되지 못하더라도 수행된다.
- try 블럭이나 catch 블럭에서 return을 만나더라도 수행된다.
📁 예외 발생시키기
throw를 이용해 프로그래머가 임의로 예외를 발생시킬 수 있다.
Exception e = new Exception("예외메세지");
throw e;
📁 메서드에 예외 선언하기
메서드에 처리되지 않은 checked 예외가 있는 경우 예외를 선언해 예외가 발생할 수 있음을 명시해야 한다.
public void method() throws Exception {}
메서드에 예외를 선언한다는 것은 발생가능성이 있는 예외에 대해 해당 메서드에서 예외처리를 하지 않고 메서드를 호출한 상위 메서드에 예외처리를 위임하는 것을 의미한다.
public static void main(String[] args) {
try {
method();
} catch(Exception e) {}
}
public void method() throws Exception {
throw new Exception();
}
📁 예외 되던지기
예외를 처리한 후 다시 인위적으로 예외를 발생시키는 것.
한 메서드에서 발생할 수 있는 예외가 여럿인 경우 호출한 메서드와 호출된 메서드가 나눠서 처리할 수 있다.
public static void main(String[] args) {
try {
method();
} catch(ExceptionB e) {
System.out.println("main에서 예외 B를 처리하였습니다.");
}
}
public static void method() throws ExceptionB {
try {
throw new ExceptionA();
} catch(ExceptionA e) {
System.out.println("method에서 예외 A를 처리하였습니다.");
}
throw new ExceptionB();
}
method에서 예외 A를 처리하였습니다.
main에서 예외 B를 처리하였습니다.
하나의 예외를 호출한 메서드와 호출된 메서드 양쪽에서 처리할 수도 있다.
public static void main(String[] args) {
try {
method();
} catch(Exception e) {
System.out.println("main에서 예외가 처리되었습니다.");
}
}
static void method() throws Exception {
try {
throw new Exception();
} catch(Exception e) {
System.out.println("method에서 예외가 처리되었습니다.");
throw e;
}
}
method에서 예외가 처리되었습니다.
main에서 예외가 처리되었습니다.
📁 연결된 예외
한 예외가 다른 예외를 발생시킬 수 있다.
예외 A가 예외 B를 발생시키면 A는 B의 원인예외라고 한다.
initCause()
예외 사이에 관계를 맺어주면 여러가지 예외를 하나의 예외로 묶어서 다룰 수 있다.
public Throwable initCause(Throwable cause) // 한 예외를 다른 예외의 원인예외로 등록
public Throwable getCause() // 원인이 되는 예외 인스턴스를 반환
SpaceException과 MemoryException을 InstallException의 원인예외로 등록하여 메서드에 선언하였다.
이제 메인에서 install()을 사용할 때에는 InstallException에 대한 예외처리만 해주면 된다.
에러메세지를 호출하면 InstallException이 발생하기 이전에 실제로 어떤 예외가 발생했는지 출력된다.
public static void main(String[] args) {
try {
install();
} catch(InstallException ie) {
e.printStackTrace();
}
}
public static void install() throws InstallException {
try {
startInstall();
} catch(SpaceException se) {
InstallException ie = new InstallException("설치 중 예외발생");
ie.initCause(se);
throw ie;
} catch(MemoryException me) {
InstallException ie = new InstallException("설치 중 예외발생");
ie.initCause(me);
throw ie;
}
}
2) checked예외→unchecked예외
RuntimeException의 생성자를 통해 checked 예외를 unchecked 예외로 변경할 수 있다.
의미없는 예외처리 부담을 줄여 간결한 코드 작성이 가능해진다.
public RuntimeException(Throwable cause) // 원인예외를 등록하는 생성자
InstallException은 원래 Exception의 자손으로 반드시 예외처리를 해주어야 했지만 RuntimeException으로 감쌌기 때문에 예외처리를 해주지 않아도 된다.
public static void method() {
throw new RuntimeException(new InstallException("설치 중 예외발생"));
}
📁 사용자정의 예외 만들기
이미 만들어져있는 예외클래스 외에 사용자가 직접 예외클래스를 정의할 수도 있다.
- 예외처리를 강제할지 여부에 따라 Exception과 RuntimeException 중 상속할 조상을 선택한다.
class MyException1 extends Exception {} // checked 예외로 선언
class MyException2 extends RuntimeException {} // unchecked 예외로 선언
- 예외메세지를 직접 입력할 수 있도록 문자열을 매개변수로 받는 생성자를 선언할 수 있다.
public MyException(String msg) {
super(msg);
}