Step13_Exception
- java에서 어떤 상황에서 예외(오류)가 나타나는지 관찰한다.
- 자바의정석 3판 p414
<MainClass01>
package test.main;
import java.util.Scanner;
public class MainClass01 {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
System.out.println("숫자입력:");
//숫자 형식의 문자열을 입력받는다. "10" "20" "10.1" 등등
String inputNum=scan.nextLine();
try {
//입력한 숫자를(문자열) 실제 숫자로 바꾼다.
double num=Double.parseDouble(inputNum);
//입력한 숫자에 100을 더한다.
double result=num+100;
System.out.println("입력한 숫자+100: "+result);
}catch(NumberFormatException nfe) {
/*
* 실행스택에서 일어난 일을 콘솔창에 출력하기
* (자세한 예외정보를 예외객체가 콘솔창에 출력하게 하기)
*/
nfe.printStackTrace();
}
System.out.println("무언가 중요한 마무리 작업을 하고 main 메소드가 종료됩니다.");
}
}
- try~catch 절의 사용
- 예외처리(Exception Handling)란, 프로그램 실행 시 발생할 수 있는 예외의 발생에 대비한 코드를 작성하는 것.
- 예외처리의 목적은 예외의 발생으로 인한 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이다.
- 예외가 발생하는 경우: 숫자가 아닌 문자를 집어넣었을 때, 문자를 넣지 않았을 때 등등 (NumberFormatException)
- 저 오류 문장이 출력되면 마지막 syso '마무리작업' 부분의 메소드가 수행되지 않고 끝난다.
double num=Double.parseDouble(inputNum);
- 이 부분에서 오류가 발생한 것이다. 숫자로 변환할 수 없기 때문에!
- 오류가 나더라도 마무리 작업은 진행될 수 있도록 하고싶다면?
→ 이런 일이 발생할 가능성을 대비해 다른 코딩을 해두는것.
→ try { } catch( ) { } 문을 사용한다.
- try { 예외가 발생할 가능성이 있는 문장 } catch ( 타입 지역변수 ) { 예외상황에서 처리를 위한 문장 };
- NumberFormatException 이라는 클래스가 생성되어있다.
- NumberFormatException을 타입으로 넣고, 지역변수 nfe 로 설정
- 위와 같이 작성해두면, NumberFormatException 타입의 exception이 발견되면
객체의 참조값이 자동으로 nfe로 전달되고 catch{}가 실행된다.
- java 에서는 경미한 오류는 전부 예외로 처리한다.
- 초록: 예외가 발생하지 않을 경우의 경로 (try → 마무리작업)
- 빨강: 예외가 발생할 경우의 경로 (try → catch → 마무리작업)
- 오류와 관계없이 맨 아래의 syso 는 실행한다!!
(오류가 나지만 무시하고 끝까지 실행하는 것)
- 예외객체에도 메소드가 있다!
printStackTrace(); : 오류 메시지가 콘솔창에 출력되는 메소드
- 메소드의 인자가 전달되듯이, nfe 변수로 예외객체의 참조값이 전달되어 예외객체의 메소드를 사용할 수 있다.
** NumbeFormatException 설명 : 링크
- java의 대부분의 클래스는 Serializable이라는 인터페이스를 구현했다.
- 모든 예외타입의 부모타입은 Exception이다.
- 원한다면 Exception을 상속받아 커스텀 Exception도 만들 수 있다.
- 실행(runtime)중에 발생하는 예외, 컴파일(compile)중에 발생하는 예외가 있다.
- Compile Exception 의 경우 이클립스가 바로 오류 표시를 해준다.
- Runtime Exception 은 다양한 Exception들의 부모타입에 포함되어 있다.
<MainClass02>
package test.main;
import java.util.Scanner;
public class MainClass02 {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
System.out.println("나눌 수 입력:");
String inputNum1=scan.nextLine();
System.out.println("나누어지는 수 입력:");
String inputNum2=scan.nextLine();
try {
int num1=Integer.parseInt(inputNum1);
int num2=Integer.parseInt(inputNum2);
int result=num2/num1; //몫
int result2=num2%num1; //나머지
System.out.println(num2+" 를 "+num1+" 으로 나눈 몫 :"+result);
System.out.println(num2+" 를 "+num1+" 으로 나눈 나머지 :"+result2);
}catch(NumberFormatException nfe) {
System.out.println("숫자 형식으로 입력해 주세요.");
}catch(ArithmeticException ae) {
System.out.println("어떤 수를 0으로 나눌 수는 없어요.");
}catch(Exception e) {
System.out.println("예외가 발생했습니다.");
}finally {//위의 예외 발생과 상관없이 반드시 실행이 보장되는 블럭
System.out.println("무언가 중요한 마무리 작업을 해요!");
}
System.out.println("main 메소드가 정상 종료 됩니다.");
}
}
try {예외 발생 가능성이 있는 문장}
→ catch {예외처리를 위한 문장}
→ finally {예외와 관계없이 반드시 실행되어야 할 문장}
NumberFormatException : 숫자가 들어가야 하는 자리에 숫자가 아닌 값을 입력하면 발생하는 exception
ArithmeticException : 어떤 숫자를 0으로 나누면 발생하는 exception
- catch 2개 사용하면 오류가 발생했을 때 더 다양하게 대비할 수 있다.
catch(Exception e) {
System.out.println("예외가 발생했습니다.");
}
- 예상할 수 없는 exception이 발생할 경우를 대비해서,
모든 예외(오류)상황의 부모타입인 exception으로 받으면 된다.
초록: 정상적인 경로 (try → finally)
빨강: 오류 발생시의 경로 (try → catch → finally)
- 어느 쪽이든 마무리작업 부분은 잘 실행된다.
- finally 예약어 : 예외가 발생하건 하지 않건 마무리작업을 실행하도록 한다.
→ try~catch 구문의 결과에 관계없이 무조건 실행한다.
<MainClass03>
package test.main;
public class MainClass03 {
public static void main(String[] args) {
System.out.println("main메소드가 시작됩니다.");
try {
/*
* 실행의 흐름을 스레드라고 하는데 스레드를 임의로 5초동안 잡아두기
* 컴파일시에 발생하는 Exception이 발생하기 때문에 반드시 try~catch 블럭으로
* 예외처리를 해야 한다.
*/
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main메소드가 종료됩니다.");
}
}
- 실행의 흐름을 '스레드' 라고 한다.
sleep : 1/1000초 단위로 실행을 잠깐 멈출수있다.
ex) 5초라면 5000 입력
- 근데 오류가 발생한다....
Unhandled exception type InterruptedException : 컴파일 시에 발생하는 예외
- try-catch 쪽을 클릭하면 try구문으로 묶어주는 구문이 자동완성된다.
** InterruptedException 설명 : 링크
- Runtime Exception이 아니다.(부모타입에 Runtime Exception이 없다)
- 그래서 실행중에 넣을 수 없기 때문에 수정이 필요하다.
<MainClass04>
package test.main;
import java.io.File;
import java.io.IOException;
public class MainClass04 {
public static void main(String[] args) {
/*
* 현재 존재하거나 혹은 존재하지 않는 파일이나 폴더를 제어할 수 있는 File 객체를 생성해서
* 참조값을 f라는 지역변수에 담기
*/
File f=new File("c:/acorn202210/myFolder/memo.txt");
//실제 memo.txt파일이 존재하지 않으면 파일을 만들고
//존재하면 memo.txt파일을 삭제하도록 프로그래밍해 보세요.
try {
if(f.exists()) {
f.delete();
}else {
f.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 파일 시스템에 관련된 작업을 하는 객체
- 파일객체의 기능을 이용하면 없던 파일을 만들거나 있던 파일을 제거하는 것도 가능하다.
- 생성자가 4개. 이중 첫번째 사용! (문자열로 access할 파일의 경로를 적어준다.)
new File("c:/경로/memo.txt");
- 실제 존재하지 않아도 된다. 존재하지 않으면 새로 만들고 존재하면 access한다.
- 만들기: make, create / 삭제하기: remove, delete
createNewFile : 새 파일 만들기
- 만드는데 성공하면 true, 실패하면 false를 내보낸다. 궁금하면 리턴값을 받아도 된다.
canRead : 읽을 수 있는지 여부 (read 권한이있는지)
- createNewFile 메소드를 사용하면 Exception이 발생한다.
- try~catch 문으로 작성하는 것 클릭!(자동완성됨)
- run하면 실제로 파일이 만들어진다.
.exists : 파일이 존재하는지 여부를 확인하는 메소드
- if문 안에 exists 를 넣는다!
- if 안에 있으면 삭제하는 구문을 넣고, else안에 없으면 생성하는 구문을 넣는다.
- run 하면 삭제와 파일 생성이 반복된다!
새 패키지(mypac)에 새 클래스 만들기
<WowException>
package test.mypac;
public class WowException extends RuntimeException{
//생성자
public WowException(String msg) {
super(msg);
}
}
- RuntimeException을 상속하는(부모 클래스로 갖는) WowException이라는 새 예외를 생성
- 생성자의 인자로 전달받은 문자열(msg)을 부모 생성자의 인자로 전달!
<MainClass05>
package test.main;
import java.util.Random;
import test.mypac.WowException;
public class MainClass05 {
public static void main(String[] args) throws WowException {
Random ran=new Random();
//0~4 사이의 랜덤한 정수 얻어내기
int ranNum=ran.nextInt(5);
//우연히 가장 큰 수가 나오면 WowException을 발생시키기
if(ranNum==4) {
//throw 예약어와 함께 예외 객체를 생성하면 예외가 발생한다.
throw new WowException("놀랍네 이거");
}
System.out.println("main메소드가 종료됩니다.");
}
}
- WowException이라고 하는 새 예외를 만들어본다!
- 새로운 예외를 만들려면 RuntimeException / Exception 둘 중 하나의 클래스는 상속해야 한다.
- Throw 예약어를 사용해서 프로그래머가 고의로 Exception을 발생시킬 수 있다.
- 생성자의 인자로 전달한 값이 가장 큰 값(4)이 나오면 이렇게 exception이 발생한다.
- 아래를 try~catch 문으로 묶으면 예외 처리가 가능하다.
- RuntimeException이 아니라 그냥 Exception 을 상속하면 throw 문에 추가 설명이 필요하다.
- Throw 예약어와 함께 예외 객체를 생성하면 예외가 발생한다.
- 예외를 의도적으로 발생시켜야 할 일이 종종 있다.
<MyUtil>
package test.mypac;
public class MyUtil {
public static void draw() {
System.out.println("5초 동안 그림을 그려요.");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("그림 완성!");
}
public static void send() throws InterruptedException {
System.out.println("5초 동안 전송을 해요.");
Thread.sleep(5000);
System.out.println("전송 완료!");
}
}
<MainClass06>
package test.main;
import test.mypac.MyUtil;
public class MainClass06 {
public static void main(String[] args) {
try {
MyUtil.send();
} catch (InterruptedException e) {
e.printStackTrace();
}
MyUtil.draw();
}
}
Thread.sleep(); 의 오류 해결방식이 둘이 다른데,
1 : try~catch 로 묶어서 직접 메소드안에서 처리하기
2 : 메소드를 사용하는 입장에서 처리(컨트롤)하는 것이 낫다고 판단하면 throw하기
- 2가 간편해 보이지만, 사용하는 쪽에서는 불편하다.
(해당 예외를 호출하는 쪽으로 책임을 돌린 것!)
- 메인메소드에서 static 자원은 new할 필요없이 클래스명에 . 점 찍어서 사용한다.
- draw는 문제없이 사용할 수 있다.
- send는 수정이 필요하다. 사용하는 시점에서 처리해야한다. 결국 try~catch를 해줘야 한다.
- 처리해야 할 예외가 있을 때, throw하는 것만이 능사는 아니다. 어디에선가는 예외처리를 해줘야한다!
- try-catch / finally절 / throw 잘 알아두기!
'국비교육(22-23)' 카테고리의 다른 글
20일차(1)/java(26) : Frame(2) (0) | 2022.11.02 |
---|---|
19일차(4)/java(25) : Frame(1) (0) | 2022.11.01 |
19일차(2)/java(23) : UtilClass (HashSet, Iterator) / While문 (0) | 2022.11.01 |
19일차(1)/java 퀴즈 : HashMap 활용 예제 (0) | 2022.11.01 |
[참고] 이클립스 2개로 git 저장소 clone 연습 (0) | 2022.11.01 |