1-3. 예외처리 활용
1) 예외처리 위임하기
- method1에서 발생이 예상되는 예외가 checkedException 일 때, 반드시
- try catch를 사용해서 직접 예외처리를 하거나
- throws를 사용해서 예외처리를 위임 - method2에서 발생이 예상되는 예외가 uncheckedExciption 일 때,
- 예외처리를 하지 않아도 된다.
- try catch를 사용해서 직접 예외처리를 할 수 있다. 근데 안해도 됨
- 예외처리를 위임시킬 때는 throws를 사용하지 않아도 이 메소드에서 발생한 예외가 이 메소드를 호출한 측에게 예외가 전달되고, 그 호출한 메소드로 예외처리가 위임된다 (위임할 때는 throws를 안적어도 됨)
--> throws에 던질 메소드들을 안적어도 되니까 얘를 많이 씀
- checkedException과 uncheckedExciption 연습
import java.io.FileWriter;
import java.io.IOException;
public class ExceptionApp1 {
public static void main(String[] args) throws IOException{
// main() 메소드에서 method1와 method2를 호출했을 때 발생이 예상되는 예외를 다시 예외처리 위임할 수 있다.
// main() 메소드에서 예외처리를 위임(throws)하면, 예외가 발생했을 때 자바가상머신으로 전달되고, 프로그램이 종료된다.
// 따라서, main() 메소드에서 예외처리를 위임하는 것은 실제로 아무런 예외처리를 하지 않는 것과 같다.
// 예외처리를 안해도 -> 예외가 자바가상머신으로 전달되는데, main에서 자바가상머신으로 throws -> 예외가 자바가상머신으로 전달
ExceptionApp1.method1();
ExceptionApp1.method2("1234", "5678");
}
// checkedException 발생이 예외되는 수행문을 실행하는 메소드
// 컴파일러가 예외처리 여부를 검사하기 때문에 try ~ catch , throws 중 하나를 이용해서 예외처리 해야 함
// method1() 메소드에서 발생이 예상되는 예외를 직접 처리하지 않고,
// 이 메소드를 호출한 측에게 예외를 전달하고
// 이 메소드를 호출한 측에서 예외처리를 위임하기 위해서는 throws 키워드 사용
// 이 메소드를 호출한 측에서는 반드시 예외처리(try ~ catch로 직접 처리 or throws로 다시 위임)을 해야 함
public static void method1() throws IOException {
FileWriter writer = new FileWriter("sample.txt");
writer.write("연습");
writer.flush();
writer.close();
}
// uncheckedException 발생이 예외되는 수행문을 실행하는 메소드
// 컴파일러가 예외처리 여부를 검사하지 않기 때문에 try ~ catch , throws를 작성할 필요 없음
// method2() 메소드에서 예외가 발생하면,
// 이 메소드를 호출한 측에게 예외가 전달되고 ( 작성하지 않아도 전달됨 )
// 이 메소드를 호출한 측에서 예외처리를 해아 한다. (에외처리 위임)
// 이 메소드를 호출한 측에서도 예외처리를 반드시 할 필요는 없음
public static void method2(String str1, String str2) {
int number1 = Integer.parseInt(str1);
int number2 = Integer.parseInt(str2);
System.out.println("합계: " + (number1 + number2));
}
}
2) 예외클래스의 주요 API
Throwable 클래스
- Error, Exception의 부모 클래스
- 오류와 관련된 모든 메소드들이 구현되어 있다.
- 모든 Error, Exception 클래스는 Throwable에 구현된 메소드를 상속받고, 사용할 수 있다.
- 특별한 경우가 아니면 사용자정의 예외클래스를 정의할 때 생성자만 정의해도 된다.
- 멤버변수
- private String message
오류 메세지를 저장하는 변수 - private Throwable cause
오류의 원인이 되었던 예외객체를 저장하는 변수
- private String message
- 주요 생성자
- public Throwable() { ... }
기본생성자 - public Throwable(String message) { ... }
오류와 관련된 메세지를 전달받는 생성자 - public Throwable(String message, Throwable cause) { ... }
오류와 관련된 메세지 및 오류의 원인이 되었던 이전 예외객체를 전달받는 생성자 - public Throwable(Throwable cause) { ... }
오류의 원인이 되었던 이전 예외객체를 전달받는 생성자
- public Throwable() { ... }
- 주요 메소드
- String getMessage()
오류와 관련된 상세한 메세지를 반환한다. - void printStackTrace()
디버깅에 필요한 오류정보를 출력한다.
오류 발생과 관련되어서 실행되었던 코드를 화면에 출력한다.
오류검출을 위한 디버깅 작업에서 참조한다.
- String getMessage()
Exception 예외 클래스에는 생성자만 있고 메소드가 없어
-> Throwable에 있는 메소드를 그대로 사용 (실질적인 멤버 메소드는 Throwable클래스만 갖고있음)
(Throwable 클래스가 부모클래스이기 때문에, 이 안에 있는 메소드는 모든 예외 클래스가 사용할 수 있음)
-> 오류 내용들을 담아야 되는데 변수도 없어
-> super를 통해 부모의 생성자를 호출해서 부모의 변수 message, cause에 오류 내용을 담음
-> 대부분의 예외클래스들은 생성자만 갖고있고, 이 생성자 안에는 부모클래스인 Throwable클래스의 생성자 메소드를 호출하는 super( ) ; 메소드가 있음
-> 사용자 정의 예외클래스를 만들 때도 생성자만 만들면 됨
-예외객체의 주요 메소드 사용하기
public class ExceptionApp2 {
public static void main(String[] args) {
// 예외 객체의 주요 메소드 사용하기
try {
int number = Integer.parseInt("1234A");
System.out.println(number);
} catch(Exception ex) {
// 오류 메세지 조회
String errorMessage = ex.getMessage(); // getMessage : 모든 예외객체가 갖고있는 메소드
System.out.println("오류 메세지: " + errorMessage);
// 디버깅에 필요한 정보 출력하기 (오류를 찾아가는 과정)
System.out.println("## 디버깅에 필요한 정보 출력하기");
ex.printStackTrace(); // printStackTrace : 오류과정을 알려주는 메소드
}
}
}
오류 메세지: For input string: "1234A"
## 디버깅에 필요한 정보 출력하기
java.lang.NumberFormatException: For input string: "1234A"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:668)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at day26.ExceptionApp2.main(ExceptionApp2.java:9)
system. out -> 정상적인 출력 (흰색)
system. err -> 오류메세지 출력 (빨간색)
3) 예외 강제 발생
- 업무로직상의 오류가 발생한 경우(비밀번호 불일치, 잔액부족, 로그인 실패 등), 예외객체를 생성하고 예외를 강제로 발생시켜서 이 메소드를 호출한 측에게 전달(던지기, 오류가 발생했음을 알리는 것)하는 것이다.
===> 이게 개발자가 해야하는 일
(비밀번호 불일치, 잔액부족 같은 오류클래스는 따로 없단말이야 -> 만들어줌) - 오류 상황에 대한 더 많은 정보 제공 (객체니까)
-예외객체를 보고 어떤 종류의 오류인지 알 수 있고
-관련된 오류 메시지도 출력할 수 있다 - throw 키워드를 사용한다.
- throws와 throw
- throws : 예외처리 위임
public void method() throws 예외1, 예외2 {
예외1과 예외2가 발생되는 수행문들; // 여기서 직접 처리하지 않고, 예외처리를 위임하기 위해서 throws 사용
} - throw : 예외 강제 발생
public void method( ) {
if ( score < 0 ) { // 점수는 절대로 음수가 될 수 없다. 잘못된 경우가 true로 판정되면
throw new 예외클래스("오류메세지: 올바른 점수가 아닙니다."); // 예외 강제발생
}
}
* 예외객체를 생성(예외객체의 생성자(메시지출력, cause)) -> 생성한 예외객체를 던짐
* 강제로 발생시키는 예외는 자바에서 제공하는 예외클래스 혹은 사용자정의 예외클래스로 예외객체를 생성할 수 있다.
- throws : 예외처리 위임
public class ExceptionApp3 {
public static void main(String[] args) {
// int score = -100; // 오류 (원하는 값이 아님)
//
// if(score < 0) {
// throw new Exception("점수가 올바르지 않습니다."); // 예외 강제 발생
// }
// System.out.println("입력한 점수 : " + score);
// }
// 예외 일괄처리
try {
ExceptionApp3.method2(100, 100, 100);
ExceptionApp3.method2(100, 100, -2);
} catch(RuntimeException ex) {
System.err.println("### 오류상황 발생");
String errorMessage = ex.getMessage();
System.err.println("### 오류 메세지: " + errorMessage);
System.err.println("### 디버깅 정보 출력");
ex.printStackTrace();
}
}
// checkedException이 발생되는 메소드
// 이 메소드를 호출한 측에게 오류 발생 원인을 정확하게 전달할 수 있다.
public static void method1(int korScore, int engScore, int mathScore) throws Exception {
if(korScore < 0) {
throw new Exception("국어점수가 올바르지 않습니다."); // 예외 강제 발생
}
if(engScore < 0) {
throw new Exception("영어점수가 올바르지 않습니다."); // 예외 강제 발생
}
if(mathScore < 0) {
throw new Exception("수학점수가 올바르지 않습니다."); // 예외 강제 발생
}
int total = korScore + engScore + mathScore;
System.out.println("합계 : " + total);
}
// uncheckedException이 발생되는 메소드
// 예외를 강제 발생 시켜서 -> 이 메소드를 호출한 측에 오류발생 원인을 정확하게 전달할 수 있다.
public static void method2(int korScore, int engScore, int mathScore) {
if(korScore < 0) {
throw new RuntimeException("국어점수가 올바르지 않습니다."); // 예외 강제 발생
}
if(engScore < 0) {
throw new RuntimeException("영어점수가 올바르지 않습니다."); // 예외 강제 발생
}
if(mathScore < 0) {
throw new RuntimeException("수학점수가 올바르지 않습니다."); // 예외 강제 발생
}
int total = korScore + engScore + mathScore;
System.out.println("합계 : " + total);
}
// 예외를 배우기 전 - 그냥 점수가 잘못됐는지 여부만 알 수 있음 (업무로직의 성공/실패 여부만 제공), 원인은 알 수 x
// 예외를 사용하면 -> 어디서 어떤 오류가 발생했는지를 알 수 있음
public static boolean method3(int korScore, int engScore, int mathScore) {
if (korScore < 0) {
return false;
}
if (engScore < 0) {
return false;
}
if (mathScore < 0) {
return false;
}
int total = korScore + engScore + mathScore;
System.out.println(total);
return true;
}
}
합계 : 300
### 오류상황 발생
### 오류 메세지: 수학점수가 올바르지 않습니다.
### 디버깅 정보 출력
java.lang.RuntimeException: 수학점수가 올바르지 않습니다.
at day26.ExceptionApp3.method2(ExceptionApp3.java:58)
at day26.ExceptionApp3.main(ExceptionApp3.java:18)
4) 사용자정의 예외클래스
- 사용자정의 예외클래스를 정의하는 목적
- 개발하는 애플리케이션에 대한 오류 정보를 표현하는 예외 클래스를 정의하기 위해서
- 개발하는 애플리케이션에서 발생하는 다양한 예외를 사용자정의 예외로 변환해서 예외처리를 단순화한다.
(안그러면 발생하는 Exception마다 try catch가 엄청 늘어남 -> 하나의 예외처리로 단순화)
*상속관계
CustomCheckedException -> Exception -> Throwable
Throwable 클래스가 있고
Exception 예외클래스는 Throwable을 상속받음
CustomCheckedException 예외클래스는 그 부모객체인 Exception을 상속받음
Exception에서는 super를 통해서 부모객체인 throwable의 생성자를 호출,
실제로 에러와 관련된 정보는 모두 Throwable에 있음
CustomCheckedException에서도 supuer를 통해서 상속받은 부모객체인 exception의 생성자 호출
private이라서 접근을 못해 , set도 없어 -> 생성자를 통해 접근
super를 통해서 상위클래스의 생성자로 접근 -> throwable의 생성자로 최종 접근 가능
throwable에 있는 메소드 두개도 모두 실행(출력)한 다음(=오류메시지 출력) 프로그램 종료
throw에 있는 변수 message : 오류 메시지, cause: 원인이 되는 오류 객체
내가 원하는 형태의 오류메시지를 넣을 수 있고,
다양한 에러를 잡고, 대신 내가 만든 오류객체 하나만 발생시킴
일괄처리에서 처리해야할 예외의 개수가 확 줄어듦
오류메시지 분석
"yyyy-MM-dd" 형식의 텍스트가 아니네? 왜? 도대체 뭘 넣었길래?
원인이 되는 객체 - "202223" 아 이렇게 넣었었구나
- CustomCheckedException
/*
* 사용자정의 checked 예외 클래스
* Exception 클래스를 상속받는다.
*/
public class CustomCheckedException extends Exception {
private static final long serialVersionUID = 1L; //노란줄 없애기위함 시리얼넘버가 필요해서 만들었다 (나중에배움)
public CustomCheckedException(String message) {
super(message);
}
public CustomCheckedException(Throwable cause) {
super(cause);
}
public CustomCheckedException(String message, Throwable cause) {
super(message, cause);
}
}
-CustomUncheckedException
/*
* 사용자정의 unchecked 예외 클래스
* RuntimeException 클래스를 상속받는다
*/
public class CustomeUncheckedException extends RuntimeException {
private static final long serialVersionUID = 1L;
public CustomeUncheckedException(String message) {
super(message);
}
public CustomeUncheckedException(Throwable cause) {
super(cause);
}
public CustomeUncheckedException(String message, Throwable cause) {
super(message, cause);
}
}
* 중간에 Runtime이 껴있으면 -> Unchecked,
안껴있으면 -> Checked
-이렇게 하면 던져야될 예외가 너무 많아 (CustomeUncheckedException, NumberFormatException, ParseException)
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomExceptionApp1 {
public static void main(String[] args) {
}
// uncheckedException 발생 가능성이 있는 total메소드
public static int total(int kor, int eng, int math) {
if(kor < 0) {
throw new CustomeUncheckedException("국어점수가 올바르지 않습니다.");
}
if(eng < 0) {
throw new CustomeUncheckedException("영어점수가 올바르지 않습니다.");
}
if(math < 0) {
throw new CustomeUncheckedException("수학점수가 올바르지 않습니다.");
}
int total = kor + eng + math;
return total;
}
// NumberFormatException 발생 가능성이 있는 메소드
public static int textToInt(String text) {
return Integer.parseInt(text); //text를 정수로 변환 (근데 숫자말고 다른게 들어있으면 예외발생)
}
// ParseException 발생 가능성이 있는 메소드
public static Date textToDate(String text) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(text);
return date;
}
}
-내가 정의한 예외만 던지면 됨 (CustomUncheckedException)
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomExceptionApp1 { // 내가 정의한 한 종류의 예외만 발생하도록 하기
public static void main(String[] args) {
int result1 = CustomExceptionApp1.total(100, 100, 100);
System.out.println("합계: " + result1);
int result2 = CustomExceptionApp1.total(-100, 100, 100);
System.out.println("합계: " + result2);
int result3 = CustomExceptionApp1.textToInt("1234");
System.out.println("정수값: " + result3);
int result4 = CustomExceptionApp1.textToInt("1234가가"); // numberformat예외가 발생할건데, 우리가 만든 예외객체에서 이걸 잡고
System.out.println("정수값: " + result4);
Date result5 = CustomExceptionApp1.textToDate("2022-12-31");
System.out.println("날짜: " + result5);
Date result6 = CustomExceptionApp1.textToDate("20221231");
System.out.println("날짜: " + result6);
}
public static int total(int kor, int eng, int math) { // unchecked 예외를 발생시키는 total메소드
if(kor < 0) {
throw new CustomeUncheckedException("국어점수가 올바르지 않습니다.");
}
if(eng < 0) {
throw new CustomeUncheckedException("영어점수가 올바르지 않습니다.");
}
if(math < 0) {
throw new CustomeUncheckedException("수학점수가 올바르지 않습니다.");
}
int total = kor + eng + math;
return total;
}
// NumberFormatException 발생 가능성이 있는 메소드 -> CustomeUncheckedException 발생 가능성
// 한종류의 예외만 발생시키도록
// 내가 만든 예외에 대한 정보 + 예외의 원인이 됐던 예외에 대한 정보
public static int textToInt(String text) {
try {
return Integer.parseInt(text); //text를 정수로 변환 (근데 숫자말고 다른게 들어있으면 예외발생)
} catch(NumberFormatException cause) { // number예외는 여기서 잡아버리고 unchecked를 대신 던져버려. 원인이 되는 오리지널 Exception
throw new CustomeUncheckedException("숫자가 아닌 문자가 포함되어 있습니다.", cause);
}
}
// ParseException 발생 가능성이 있는 메소드 -> CustomeUncheckedException 발생 가능성
public static Date textToDate(String text) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(text);
return date;
} catch (ParseException cause) { // parse 예외는 여기서 잡아버리고, unchecked를 발생시킴
throw new CustomeUncheckedException("yyyy-MM-dd 형식의 텍스트가 아닙니다.", cause);
}
}
}
*unchecked 예외를 발생시키는 이유
: checked를 발생시키면, 예외처리를 반드시 해야해서 -> throws를 다 붙여줘야함
그럼 던질게 너무 많아져
unchecked는 안적어도 던져짐
합계: 300
Exception in thread "main" day26.CustomeUncheckedException: 국어점수가 올바르지 않습니다.
at day26.CustomExceptionApp1.total(CustomExceptionApp1.java:30)
at day26.CustomExceptionApp1.main(CustomExceptionApp1.java:13)
정수값: 1234
Exception in thread "main" day26.CustomeUncheckedException: 숫자가 아닌 문자가 포함되어 있습니다.
at day26.CustomExceptionApp1.textToInt(CustomExceptionApp1.java:50)
at day26.CustomExceptionApp1.main(CustomExceptionApp1.java:18)
Caused by: java.lang.NumberFormatException: For input string: "1234가가"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:668)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at day26.CustomExceptionApp1.textToInt(CustomExceptionApp1.java:48)
... 1 more
날짜: Sat Dec 31 00:00:00 KST 2022
Exception in thread "main" day26.CustomeUncheckedException: yyyy-MM-dd 형식의 텍스트가 아닙니다.
at day26.CustomExceptionApp1.textToDate(CustomExceptionApp1.java:62)
at day26.CustomExceptionApp1.main(CustomExceptionApp1.java:23)
Caused by: java.text.ParseException: Unparseable date: "20221231"
at java.base/java.text.DateFormat.parse(DateFormat.java:399)
at day26.CustomExceptionApp1.textToDate(CustomExceptionApp1.java:58)
... 1 more
'수업내용 > Java' 카테고리의 다른 글
[2022.11.08.화] JSP 내장객체, JSP 요청객체 (0) | 2022.11.08 |
---|---|
[2022.10.12.수] 예외처리 실습 (0) | 2022.10.12 |
[2022.10.07.금] 예외처리 (0) | 2022.10.07 |
[2022.10.06.목] List 실습, Map<K, V> 인터페이스 (0) | 2022.10.06 |
[2022.10.05.수] 콜렉션(List<E>) (0) | 2022.10.05 |