0) 예외란?
1) 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생한 프로그램 오류를 말하며 예외 처리(Exception Handling)을 통해 프로그램을 종료하지 않고 실행할 수 있음.
2) 자바에서는 예외를 클래스로 관리하며 예외가 발생하면 예외 클래스 객체를 생성함.
3) 모든 예외 클래스는 java.lang.Exception을 상속받음.
1. 예외 처리
1) 컴파일 오류 (compile error) : 프로그램 코드 작성 중, 컴파일 할 때 발생하는 에러.
2) 논리적 오류 (logical error) : 작성 의도와 다르게 동작하는 것.
3) 실행 오류 (runtime error) : 실행할 때 발생하는 에러. 실행중인 프로그램이 의도하지 않는 동작(bug)을 하거나 프로그램이 중지됨. 실행 오류는 비정상 종료가 되는 경우 시스템의 심각한 장애를 초래할 수 있는데, 자바 예외 처리를 통하여 프로그램의 비정상 종료를 막고 log를 남길 수 있음.
Java의 런타임 에러 에러 (error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류 예외 (exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류 → 에러(error)는 어쩔 수 없지만 예외 (exception)는 처리할 수 있음. |
3) 예외처리 정의와 목적
- 정의 : 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것.
- 목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것.
2. 예외 클래스의 계층 구조
1) Exception 클래스들 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
2) RuntimeException 클래스들 : 프로그래머의 실수로 발생하는 예외
3) checked 예외와 unchecked 예외
- checked 예외 : 컴파일러가 예외 처리 여부를 체크(예외 처리 필수) ex.Exception과 자손들
- unchecked 예외 : 컴파일러가 예외 처리 여부를 체크 안함(예외 처리 선택) ex.RuntimeException과 자손들
4) 예외 종류
- NullPointerException : 객체 참조가 없는 상태일 때 발생. null 값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생. (객체가 없는 상태에서 객체를 사용하려 했으므로)
- ArrayIndexOutOfBoundsException : 배열에서 인덱스 범위를 초과하여 사용할 때 발생.
- NumberFormatException : 문자열로 되어 있는 데이터를 숫자로 변경하는 경우 발생. 매개값인 문자열이 숫자로 변환될 수 있는 경우도 있지만, 숫자로 변환될 수 없는 문자가 포함되어 있을 경우 발생하는 오류.
- ClassCastException : 허용되지 않는 형변환을 억지로 시도할 경우 발생.
=> 타입 변환 전에 instanceof 연산자로 확인하는 것이 좋음. instanceof 연산자 결과가 true면 좌항에서 우항으로 변환이 가능하다는 뜻.
- ArithmeticException : 산술연산에서 오류 발생. 어떤 숫자이든 0으로 나누면 무조건 오류 발생.
- NegativeArraySizeException : 배열의 크기가 음수값인 경우 발생.
- OutOfMemoryException : 사용 가능한 메모리가 없는 경우 발생.
- NoClassDefFoundException : 원하는 클래스를 찾지 못한 경우 발생.
- ArrayStoreException : 배열 유형이 허락하지 않는 객체를 배열에 저장하려는 경우 발생.
- IllegalArgumentException : Method에 type이 일치하지 않는 매개변수를 전달하는 경우 발생.
- IllegalMonitorStateException : Thread가 Thread에 속하지 않는 객체를 모니터하려고 기다리는 경우 발생.
- IllegalStateException : 적절하지 않는 때에 Method를 호출하는 경우 발생.
3. 예외 처리하기 1 - try-catch문
1) try-catch문
- try 문안의 수행할 문장들에서 예외가 발생하지 않는다면 catch문 다음의 문장들은 수행이 되지 않음. 하지만 try 문안의 문장을 수행하는 도중에 예외가 발생하면 예외에 해당되는 catch문이 수행됨.
- if문과 달리, try 블럭이나 catch블럭 내에 포함된 문장이 하나뿐이어도 괄호{ }를 생략할 수 없음.
try {
... //예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch(Exception1 e1) {
... //Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 넣는다.
} catch(Exception2 e2) {
... //Exception2가 발생했을 경우, 이를 처리하기 위한 문장을 넣는다.
} catch(ExceptionN eN) {
... //ExceptionN이 발생했을 경우, 이를 처리하기 위한 문장을 넣는다.
...
}
2) try-catch 문에서의 흐름
① try블럭 내에서 예외가 발생한 경우
- 발생한 예외와 일치하는 catch블럭이 있는지 확인.
- 일치하는 catch 블럭을 찾게되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행함. 만일 일치하는 catch블럭을 찾지 못하면 예외는 처리되지 못하고 프로그램이 비정상 종료됨.
② try블럭 내에서 예외가 발생하지 않은 경우
- catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속함.
int c;
try {
c = 4 / 2; //조건들을 하나씩 거치다가 예외를 발견하면 catch 문장으로 가게 됨.
//예외가 없으면 catch 문장을 가지 않고 try문을 빠져나감.
c = 4 / 0; //0으로 숫자 나누기 금지되어 있어서 예외 발생.
} catch(ArithmeticException e) {
c = -1; //예외가 발생하여 이 문장이 수행된다.
}
예시
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(4/0); //0으로 나누기 금지되어 있으므로 오류 발생
System.out.println(5); //위에서 오류 발생해서 이 문장은 실행되지 않음.
} catch (ArithmeticException e) { //ArithmeticException과 일치함.
if(e instanceof ArithmeticException)
System.out.println("true");
System.out.println("ArithmeticException");
} catch (Exception e) { //예외처리의 조상. 다른 예외에서 못잡아내는 경우 Exception이 잡아냄.
System.out.println("Exception");
} //try-catch의 끝
System.out.println(6); //위에서 예외 잡아냈기 때문에 try-catch문을 빠져나가고 다음 문장이 수행됨.
} //main 메서드의 끝
}
4. try-catch-finally 블럭
1) 예외 발생 여부와 관계없이 수행되어야 하는 코드를 넣음.
2) 다중 catch문
- try 블록에서 동시에 예외가 발생하지 않고 하나의 예외가 발생하면 실행을 멈추고 catch 블록으로 이동하기 때문에 catch 블록이 여러개여도 하나의 블록만 실행됨.
- 주의할 점 : 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 함. 상위 예외 클래스의 catch 블록이 위에 있다면 하위 예외 클래스는 실행되지 않음. 왜냐하면 하위 예외는 상위 예외를 상속받았기 때문에 상위 예외 타입도 되기 때문.
3) 멀티 catch문
- 자바 7부터 멀티 catch가 나옴.
- catch 괄호 안에 동일하게 처리하고 싶은 예외를 | 로 연결.
5. printStackTrace()와 getMessage()
예외가 발생하면 예외 객체가 생성이 되는데, 거기엔 예외에 관한 정보가 들어있고 그 정보를 이 메서드들을 통해서 가져올 수 있음.
1) printStackTrace() : 예외 발생 당시의 호출 스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력.
2) getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있음.
try {
...
System.out.println(0/0); //예외 발생
...
} catch(ArithmeticException e) { //참조변수 e의 유효범위는 try-catch문이 끝날 때 까지.
e.printStackTrace(); //객체에 담긴 예외정보를 알 수 있음.
System.out.println("예외메시지 : " + e.getMessage());
} catch (Exception e) {
...
}
6. 멀티 catch 블럭
- 내용이 같은 catch블럭을 하나로 합친 것 (java 7부터)
try {
....
} catch(ExceptionA e) {
e.printStackTrace();
} catch (ExceptionB e2) {
e2.printStackTrace();
}
||
try {
....
} catch(ExceptionA || ExceptionB e) {
e.printStackTrace();
}
- 부모, 자식 관계의 예외인 경우 부모 예외만 써줘야 함.
- ExceptionA와 ExceptionB의 공통 멤버만 사용 가능하므로 한쪽만 사용 가능한 메서드는 사용 불가능. (꼭 해야 할 경우 형변환 해서 사용)
7. 예외 처리하기 3 - 메서드에 예외 선언하기(예외 떠넘기기)
1) 예외가 발생한 곳메서드에서 처리하지 않고, 메서드를 호출한 곳으로 예외를 던쳐서 메서드를 호출한 부분에서 예외를 처리하는 방법.
- main() 에서 throws 를 사용하면 JVM 가상머신에서 처리됨.
2) 메서드가 호출시 발생 가능한 예외를 호출하는 쪽에 알리는 것. 예외를 선언하는 것만으로는 예외가 처리되는 것은 아니고, main 쪽에서 처리할 지, 메서드 쪽에서 처리할 지 결정하고 try-catch 구문을 넣어줘야 함.
- throws 키워드를 메서드 선언부 끝에 작성하면 메서드에서 처리하지 않은 예외를 메서드를 호출한 곳으로 떠넘길 수 있음.
- throws 뒤에 떠넘길 예외들을 쉼표로 구분해서 나열해주면 됨.
- throws가 붙은 메서드는 반드시 try 블록 내에서 호출돼야 하고 catch 블록에서 떠넘겨받은 예외를 처리해야 함.
예시
public static void main(String[] args) throws Exception {
method1(); //같은 클래스내의 static 멤버이므로 객체 생성 없이 호출 가능.
} //main 메서드의 끝
static void method1() throws Exception {
method2();
} //method1의 끝
static void method2() throws Exception {
throw new Exception();
} //method2의 끝
- 예외 처리를 main 쪽으로 떠넘기기
public static void main(String[] args) {
try {
File f = createFile(""); //예외 발생
System.out.println(f.getName()+"파일이 성공적으로 생성되었습니다.");
} catch (Exception e) {
System.out.println(e.getMessage()+" 다시 입력해 주시기 바랍니다.");
}
} //main 메서드의 끝
static File createFile(String fileName) throws Exception {
if(fileName==null || fileName.equals(""))
throw new Exception("파일이름이 유효하지 않습니다."); //main쪽에서 처리하도록 떠넘김
File f = new File(fileName); //File 클래스의 객체를 만듦.
//File 객체의 createNewFile메서드를 이용해서 실제 파일을 생성.
f.createNewFile();
return f; //생성된 객체의 참조를 반환.
} //createFile메서드의 끝
} //클래스의 끝
//출력 화면
파일이름이 유효하지 않습니다. 다시 입력해 주시기 바랍니다.
- 예외 처리를 메서드 내에서 직접 하기
public static void main(String[] args) {
File f = createFile(""); //예외 발생
System.out.println(f.getName()+"파일이 성공적으로 생성되었습니다.");
}
static File createFile(String fileName) {
try {
if(fileName==null || fileName.equals(""))
throw new Exception("파일이름이 유효하지 않습니다.");
} catch (Exception e) {
fileName = "제목없음.txt";
}
File f = new File(fileName); //File 클래스의 객체를 만듦.
//File 객체의 createNewFile메서드를 이용해서 실제 파일을 생성.
try {
f.createNewFile(); //이 메서드도 예외 발생시킬 수 있기 때문에 try-catch문으로 감싸기
} catch (IOException e) {
e.printStackTrace();
}
return f; //생성된 객체의 참조를 반환.
} //createFile메서드의 끝
} //클래스의 끝
//출력 화면
제목없음.txt파일이 성공적으로 생성되었습니다.
8. 연결된 예외 (chained exception)
1) 한 예외가 다른 예외를 발생시킬 수 있음.
2) 예외 A가 예외 B를 발생시키면, A는 B의 원인 예외(cause exception)
- Throwable initCause(Throwable cause) : 지정한 예외를 원인 예외로 등록
- Throwable getCause() : 원인 예외를 반환
3) 사용하는 이유
- 예외가 너무 많아지면 try-catch 블럭도 많아짐. 그 예외들을 하나로 묶어서 다룰 때 사용
- checked 예외를 unchecked 예외로 변경할 때 사용
참고 : [한빛미디어] 이것이 자바다 (신용권의 Java 프로그래밍 정복) Chapter 10.예외 클래스
참고 : [도우출판] JAVA의 정석(3ND EDITION)-자바의 정석 최신 Java 8.0 포함 Chapter 8. 예외처리
'JAVA' 카테고리의 다른 글
Java - 다형성, instanceof (0) | 2022.10.29 |
---|---|
Java - 제네릭스 (Generics) (0) | 2022.10.29 |
오버로딩(Overloading), 오버라이딩(Overriding) (0) | 2022.09.09 |
인터페이스 (Interface) (0) | 2022.09.02 |
Map 컬렉션 (HashMap, TreeMap) (0) | 2022.08.28 |
댓글