2022.08.17 [WED]
- Day 14-
수업 주요 목차
- Exception Handling (예외처리)
- try
- catch
- finally
- throws
- throw
🤖Review
Exception Handling (예외처리) : 프로그램 수행 중 exception 발생시 예외대처흐름/대처방안(a.k.a.handler logic)을 실행하고 프로그램을 정상 실행하는 것
👾 JVM이 프로그램 실핼 중 Exception을 발생시키고 별도의 Handling이 없으면 기본적으로 예외발생 즉시 종료가 된다.
ex.) 배가 아파서(Exception : 예외) 약을 먹고(Handling : 처) 공부를 계속한다.(정상실행)
- try : Exception 발생 예상 영역 지정
- catch : Exception Handling (예외처리) Exception 발생시 대처 방안 실행 후 정상 수행이 목적 / 하나의 try, 다양한 catch 가능
- finally : Exception 발생/처리 여부와 관계없이 항상 처리 ( ex. 무조건 카드를 돌려준다.)
- throws : Exception을 호출한 측으로 던질 수 있다는 의미 (method 선언시 사용)
- throw : Exception을 필요시 고의로 발생시킨다는 의미
Checked Exception 계열 | UnChecked Exception 계열 |
Runtime계열 Exception을 제외한 모든 Exception | RuntimeException 클래스의 자식 Exception class들 |
컴파일 시점에서 Exception처리를 체크할 수 있는 Exception 계열 | 대표적으로 NullPointerException, IndexOutOfBoundsException, NumberFormatException 등 |
Exception 처리는 try~catch 또는 throws로 선택지가 있다. | method에서 throws를 명시하지 않아도 Runtime계열의 Exception은 자동으로 throws 된다.(호출한 측으로 전파된다.) |
🔎 Eclipse 실습 내용
1. Exception 1
java application 실행시 jvm은 Exception 상황을 만나면 해당 Exception(아래 경우는 NullPointerException)을 발생시킨 후 별도의 처리(Handler)가 없다면 즉지 메세지 출력 후 비정상 종료된다.
package step1;
public class TestException1 {
public static void main(String[] args) {
System.out.println("프로그램 실행시작");
String name="아이유";
name=null; //고의로 null을 할당해 Exception 발생되도록 해본다.
System.out.println(name.length()+"자로 이름이 구성됨"); //실행내용(business logic)
System.out.println("프로그램 정상수행");
}
}
↓
프로그램 실행시작
Exception in thread "main" java.lang.NullPointerException
at step1.TestException1.main(TestException1.java:12)
2. Exception 2
Exception Handling을 통해 Exception 발생시 대처방안을 실행하고 프로그램이 정상수행되도록 한다.
package step1;
public class TestException2 {
public static void main(String[] args) {
System.out.println("프로그램 실행시작");
String name="아이유";
name=null; //고의로 null을 할당해 Exception 발생되도록 해본다.
try{ // try 구문은 Exception발생 예상영역 지정
System.out.println(name.length()+"자로 이름이 구성됨"); //실행내용(business logic)
} catch(NullPointerException ne) { // catch 구문은 예외를 처리하는 영역(Exception Handling)
System.out.println(ne.getMessage()+": 예외처리 => 이름 정보가 존재하지 않아 길이를 구할 수 없습니다.");
ne.printStackTrace(); // Exception 발생경로, 종류를 모두 출력
}
System.out.println("프로그램 정상수행");
}
}
↓
프로그램 실행시작
null: 예외처리 => 이름 정보가 존재하지 않아 길이를 구할 수 없습니다.
java.lang.NullPointerException
at step1.TestException2.main(TestException2.java:19)
프로그램 정상수행
예제 1과 2의 차이점은 Exception Handling을 통해 프로그램이 정상수행이 여부이고 이것이 포인트
3. Exception 3 : Exception 1처럼 Exception을 고의로 발생시켜 비정상 종료되는 예제
package step2;
import java.util.ArrayList;
public class TestException3 {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("짜장면");
list.add("탕수육");
System.out.println(list.get(2)); // JVM이 실행될 때만 알 수 있는 에러
System.out.println("정상수행");
}
}
↓
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.rangeCheck(ArrayList.java:659)
at java.util.ArrayList.get(ArrayList.java:435)
at step2.TestException3.main(TestException3.java:12)
4. Exception 4 : Exception3을 보완하여 Exception Handling 한다.(예외 처리)
package step2;
import java.util.ArrayList;
public class TestException4 {
public static void main(String[] args) {
//TestException3에서는 IndexOutOfBoundsException을 고의로 발생시켜 비정상 종료되는 예제
// TestException4에서 Exception Handling 한다.(예외 처리)
ArrayList<String> list=new ArrayList<>();
list.add("짜장면");
list.add("탕수육");
String name=null;
try {
System.out.println(name.length()); //발생되는 즉시 건너 뛰어 catch로 감
System.out.println(list.get(2));
System.out.println("A");
} catch(IndexOutOfBoundsException ie){ // 해당되는 exception이 아니라서 건너 뛰어 아래 catch로 감
// 구체적인 타입으로 처리하면 해당 exception에 대한 상세한 대처가 가능 (자식->부모 순서로 작성해야 함)
System.out.println("B");
System.out.println(ie.getMessage()+" 리스트 요소 범위 초과하여 요소를 반환할 수 없습니다 : ex)팝업");
} catch(Exception e){ //부모타입 Exception으로도 다양한 자식 Exception을 처리할 수 있음
System.out.println("C");
e.printStackTrace();
}
System.out.println("정상수행");
System.out.println("D");
}
}
이 코드 블럭에서 발생할 수 있는 오류가 여러가지 통증을 최대한 구체적으로(상세하게) 처리하는 것이 효율적인 것처럼 최후의 보루로 남겨두고 자식객체로 처리하는 것을 추천
↓
C
java.lang.NullPointerException
at step2.TestException4.main(TestException4.java:14)
정상수행
D
5. Finally : finally를 테스트하기 위한 예제
고의로 예외 처리에 실패하고 예외 처리에 실패해도 finally구문은 언제나 실행한다.
(ex. 현금 인출기는 어떤 상황이 벌어져도 입력받은 고객의 카드를 반드시 마지막에 되돌려주도록 처리한다.)
package step3;
public class TestFinally {
public static void main(String[] args) {
String age="스물";
try {
int iage=Integer.parseInt(age); //문자열을 정수형으로 변환
System.out.println(iage+1); // 정수 변환이 정상적으로 된 후의 후속작업이므로 try블록 안에 위치
} catch(NullPointerException ne) {
System.out.println(ne.getMessage()+"이므로 출력불가"); // NumberFormatException 발생하는데 다른 예외를 고의로 처리해본다. -> 처리안됨
} finally { // 현 위치에 finally 블록을 쓰는 것과 안 쓰는 것의 차이 -> Exception Handling에 실패하더라도 finally구문은 실행
System.out.println("finally 영역 : 예외 발생 및 처리여부와 관계없이 항상 실행");
}
System.out.println("정상수행");
}
}
↓
finally 영역 : 예외 발생 및 처리여부와 관계없이 항상 실행
Exception in thread "main" java.lang.NumberFormatException: For input string: "스물"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at step3.TestFinally.main(TestFinally.java:13)
6. try catch finally review 1
* 실행결과를 예상해본다.
package step4;
import java.util.ArrayList;
public class TestReviewException {
public static void main(String[] args) {
System.out.println("A");
try {
ArrayList<Integer> list=new ArrayList<>();
System.out.println(list.get(0)); // IndexOutOfBoundException 발생
System.out.println("B");
} catch(NullPointerException ne) {
System.out.println("C");
} finally {
System.out.println("D");
}
System.out.println("E");
}
}
↓
A
D
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:659)
at java.util.ArrayList.get(ArrayList.java:435)
at step4.TestReviewException.main(TestReviewException.java:14)
7. try catch finally review 2
* 실행결과를 예상해본다.
package step4;
import java.util.ArrayList;
public class TestReviewException2 {
public static void main(String[] args) {
System.out.println("A");
try {
ArrayList<Integer> list=new ArrayList<>();
System.out.println(list.get(0)); // IndexOutOfBoundException 발생
System.out.println("B");
} catch(RuntimeException re) { // IndexOutOfBoundException의 부모
System.out.println("C");
} finally {
System.out.println("D");
}
System.out.println("E");
}
}
↓
A
C
D
E
8. Throws 1
package step5;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class TestThrows1 {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("/Users/hasense/kosta250/se-workspace/a.rtf");
// FileReader fr = new FileReader("/Users/hasense/kosta250/se-workspace/a2.rtf");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("정상수행");
}
}
// 파일이 실제로 존재하면 정상적으로 입력작업처리, 파일이 존재하지 않으면 FileNotFoundException이 발생되어 throws되고 catch이 실행된다.
// java se api에서 제공하는 FileReader 클래스의 생성자에서 throws하는 케이스
↓
정상수행
//파일이 있을 경우 실행결과
java.io.FileNotFoundException: /Users/hasense/kosta250/se-workspace/a2.rtf (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at step5.TestThrows1.main(TestThrows1.java:10)
정상수행
//파일이 없을 경우 실행결과
9. Throws 2
package step5;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class TestThrows2 {
public static void main(String[] args) throws FileNotFoundException {
//main에서 throws를 한다는 것은 전달하겠다는 의미(예외처리를 하지 않겠다는 의미, 단순히 CheckedException에 의한 compile error를 피하는 용도)
FileReader fr = new FileReader("/Users/hasense/kosta250/se-workspace/a2.rtf");
System.out.println(fr+"file read");
System.out.println("정상수행");
}
}
↓
Exception in thread "main" java.io.FileNotFoundException: /Users/hasense/kosta250/se-workspace/a2.rtf (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at step5.TestThrows2.main(TestThrows2.java:9)
10. Throws 3
package step6;
import java.io.FileReader;
import java.io.IOException;
public class MemoService {
public String readMemo(String fileName) throws IOException {
//파일을 입력받기 위한 클래스
FileReader fr=null;
try {
fr=new FileReader(fileName); //파일이 없는 경우 만들어지지 않았기 때문에 fr=null
System.out.println("A");
} finally {
if(fr!=null) //null이 아닐 때
fr.close();
System.out.println("D");
}
return fileName+"에 기록된 정보"; // 정상 흐름일 때 동작
}
}
-메인
package step6;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TestThrows3 {
public static void main(String[] args) {
MemoService service=new MemoService();
// A B C 출력결과를 예상해본다.
String fileName="/Users/hasense/kosta250/se-workspace/a.rtf"; // 존재하는 파일, 정상처리
fileName="/Users/hasense/kosta250/se-workspace/a2.rtf"; // 존재하지 않는 파일, 예외
String result;
try {
result = service.readMemo(fileName);
// 위 read method가 정상 흐름일 때 아래 후속 작업을 진행한다.
System.out.println("읽은 내용: "+result);
System.out.println("B");
} catch (FileNotFoundException e) {
System.out.println("C");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("정상수행");
}
}
↓
A
D
읽은 내용: /Users/hasense/kosta250/se-workspace/a.rtf에 기록된 정보
B
정상수행
// 파일이 있는 경우
D
C
java.io.FileNotFoundException: /Users/hasense/kosta250/se-workspace/a2.rtf (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at step6.MemoService.readMemo(MemoService.java:11)
at step6.TestThrows3.main(TestThrows3.java:14)
정상수행
//파일이 없는 경우
11. Throws 4
//throws 사례 : DB 설치되어 있지 않고 URL이 없으므로 아래 코드는 findEmployee() method의 getConnection 부분에서 SQLException이 발생된다.
// 이 때 코드의 실행 흐름을 예상해보는 예제
//현재 코드는 Exception 관련 keyword를 확인하는 예제이므로 IO또는 DB관련 코드는 이후 자세히 설명
package step7;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
// DAO : Data Access Object
public class EmployeeDAO {
public String findEmployee(String url,String id) throws SQLException {
String result=null;
Connection con=null;
//database에 연결하기 위한 객체
try {
con=DriverManager.getConnection(url);
System.out.println("C");
} finally {
if(con!=null) //con이 null이 아니고 존재하면 반드시 close 처리한다.
con.close();
System.out.println("D");
}
System.out.println("E");
return result;
}
}
-메인
package step7;
import java.sql.SQLException;
public class TestThrows4 {
public static void main(String[] args) {
EmployeeDAO dao=new EmployeeDAO();
try {
String result=dao.findEmployee("java","db url");
System.out.println("db연동 결과: "+result);
System.out.println("A");
} catch(SQLException e) {
System.out.println("B");
e.printStackTrace();
}
}
}
↓
D
B
java.sql.SQLException: No suitable driver found for java
at java.sql.DriverManager.getConnection(DriverManager.java:689)
at java.sql.DriverManager.getConnection(DriverManager.java:270)
at step7.EmployeeDAO.findEmployee(EmployeeDAO.java:14)
at step7.TestThrows4.main(TestThrows4.java:12)
12. Throw 1
package step8;
import java.io.IOException;
//Throws : Exception 발생시 호출한 쪽으로 전파 또는 위임
// Throw : Exception 발생시킬 때 사용
class Service{
public void test1(boolean flag) { // Runtime계열 exception(Unchecked Exception)은 별도로 throws가 명시 필요 없음
if(flag)
throw new NullPointerException();
System.out.println("throw test");
}
public void test2(boolean flag) throws IOException { // checked exception이라서 별도의 throws or try catch가 필요
if(flag) // throw는 예외를 발생시킬 때 사용하는 키워드
throw new IOException();
System.out.println("throw test");
}
}
public class TestThrow1 {
public static void main(String[] args) {
Service service=new Service();
// try {
// service.test1(true);
// } catch (NullPointerException ne) {
// System.out.println("test1 예외발생에 대해 처리");
// }
//service.test1(true); // unchecked - 별도의 throws가 명시 필요 없음
try {
service.test2(true);
} catch (IOException e) {
System.out.println("test2 예외발생에 대해 처리");
}
System.out.println("정상수행");
}
}
↓
test1 예외발생에 대해 처리
Exception in thread "main" java.lang.NullPointerException
at step8.Service.test1(TestThrow1.java:10)
at step8.TestThrow1.main(TestThrow1.java:27)
//////////////////////////////////////////////////////////
test2 예외발생에 대해 처리
정상수행
13. Throw 2
// 직접 예외 클래스를 정의 : java.lang.Exception을 상속받으면 된다
// serialVersionUID부분은 이후 객체직렬화에서 공부 예정 (지금은 몰라도 됨)
package step8;
public class AgeException extends Exception {
private static final long serialVersionUID = 220838069605410987L;
public AgeException() {
super("나이 정보를 다신 확인하세요."); // 상속받은 부모 Exception 클래스의 생성자 호출해 message 할당, 이후 ㅊatch 구문에서 getMessage()로 확인 가능
}
public AgeException(String message) {
super(message);
}
}
package step8;
public class MovieService {
public void enterAdultMovie(int age) throws AgeException {
if(age<=0) // 잘못된 나이 정보면 예외 발생
throw new AgeException(age+"세는 잘못된 나이 정보입니다");
if(age<=19) // 미성년자이면 성인영화이므로 예외 발생
throw new AgeException(age+"세 미성년이므로 관람 불가합니다.");
System.out.println("나이 확인되었습니다.");
}
}
-메인
package step8;
public class TestThrow2 {
public static void main(String[] args) {
MovieService service=new MovieService();
int age=-1; // 잘못된 나이정보
age=13; //13세 미성년 예외
age=20; // 20이상 정상수행
try {
service.enterAdultMovie(age);
} catch(AgeException ae) {
System.out.println(ae.getMessage());
}
System.out.println("정상수행");
}
}
↓
-1세는 잘못된 나이 정보입니다
정상수행
/////////////////////////////
13세 미성년이므로 관람 불가합니다.
정상수행
/////////////////////////////
나이 확인되었습니다.
정상수행
14.
// 생일은 1 이상 31이하일 때만 할당
// 그 외의 정보는 DayException throw, throws 발생시키고 전달하겠다는 의미
package step9;
public class DayException extends Exception {
private static final long serialVersionUID = 4105410795203597452L; // 이후 공부예정
public DayException(String message) {
super(message);
}
}
package step9;
public class MyDate {
private int birthDay;
public MyDate(int birthDay) throws DayException{
this.birthDay=birthDay;
if(1>birthDay||birthDay>31)
throw new DayException(birthDay+" 잘못된 day 정보입니다.");
}
public int getBirthDay() {
return birthDay;
}
}
-메인
package step9;
public class TestThrow3 {
public static void main(String[] args) {
int birthDay=5; // 정상흐름
birthDay=0; //예외흐름
birthDay=32; //예외흐름
// MyDate date; //try 문 안에서 생
try {
MyDate date = new MyDate(birthDay);
System.out.println(date.getBirthDay()); // sysout을 안해줬어서 정상흐름이 출력 안되었었음!
} catch (DayException e) {
System.out.println(e.getMessage());
}
//32 잘못된 day 정보입니다.
//or 0잘못된 day 정보입니다.
}
}
↓
5
//////////////////////////////////////
0 잘못된 day 정보입니다.
//////////////////////////////////////
32 잘못된 day 정보입니다.
MyDate 클래스는 깔끔하게 성공!
main에서는 sysout을 안써줘서 5(정상흐름)가 출력이 안됐었다. 그리고 변수선언을 try문 밖에다가 쓰지 않고 안에다가 써야하는지 고민하던 중 시간이 다 되었다. 강사님께 굳이 밖에다가 정의하는 이유가 혹시 이 후에 또 사용할 참조변수라서 쓰는거냐고 물어보니 자동으로 이클립스가 만든게 밖으로 빼는거고 그게 맞다고 하셨다. 츄라이문 안에 넣어도 된다고 하셨다.
// 오늘의 단축키
//오늘의 질문
Q. AgeException 클래스에서 오버로딩시 문자열로 매개변수 선언했는데 age가 들어갔는데 왜 오류가 안뜨나요?
A. +로 문자열과 연결되면 문자열로 인식함
//오늘의 숙제
복습....!