예외를 처리하는 법
1. 시스템의 최종 사용자에게 메시지 남기기(파일이 존재하지 않아서 프로그램이 실패)
2. 프로그램 에러에 의해 발생했다면 그 오류를 제거하기 위한 정보를 충분히 제공하기
e.printStackTrace();
오류가 발생한 경로를 알려준다. (어떻게 발생했는지)
e.getMessage()
오류메시지를 띄운다.
public class ExceptionHandlingRunner {
//예외 발생시 다음 코드에서 sysout이 아무것도 실행되지 않음
//main -> method1 -> method2 인데, method2가 예외를 발생시키고 있기 때문.
// call chein에 의해, method 2 -> 예외처리를 method1로 보냄 -> method1은 main으로 보냄-> main이 처리가 불가능하므로 전체 코드가 실행되지 않음.
//->예외가 생겨나고 제대로 처리되지 않으면 이 오류를 서로 떠넘기는 상황이 발생.
public static void main(String[] args) {
method1();
System.out.println("Main Ended");
}
private static void method1() {
method2();
System.out.println("method1 Ended");
}
private static void method2() {
String str = null;
str.length();
System.out.println("method2 Ended");
} // 메소드 2에서 발생한 예외적인 상황으로 method1과 main에 존재하는 그 어떤 코드도 실행할 수 없게 됨.
}
}
예외가 발생하면 stack trace 생성 => 어떻게 오류가 생겨났는지의 과정에 대해 알 수 있음.
public class ExceptionHandlingRunner2 {
public static void main(String[] args) {
method1();
System.out.println("Main Ended");
}
private static void method1() {
method2();
System.out.println("method1 Ended");
}
private static void method2() { //method1로 예외가 넘어가지 않도록 try catch 블럭 실행
try {
String str = null;
str.length();
System.out.println("method2 Ended");
} catch (Exception ex) { // Exception은 java.lang에 포함되어있는 클래스 중 하나
ex.printStackTrace(); //예외발생시 전체 stack trace를 결과로 출력
/*
만약 catch에서 ex.printStackTrace(); 를 해주지 않고 빈 코드로 진행한다면(catch에 아무 처리도 하지 않는다면)
method1 Ended 와 Main Ended가 출력된다.
Main에서 method1 불러내고, method1은 method2를 불러내고, method2에서 예외가 발생하지만 어떤 처리도 해주지 않음
==> 올바르지 않은 해결방식.
ex.printStackTrace() 를 통해 오류를 알려야 한다.
ex.printStackTrace()를 사용해 오류를 알리면, 오류 메세지와 method1 Ended Main Ended가 나오면서
예외처리가 된다. 이후에는 method2에서 예외를 넘기지 않기 때문에 수습이 된다.
전체 출력이 안되는 상황 없이 메소드 1,메인은 출력이 된다.(2에서 catch로 수습을 하기 때문에)
결국, 프로그램은 실행되지만, 에러 log도 같이 띄워서 사용자가 에러가 났는지 알수 있다.
method1와 main은 예외가 발생했다는 것을 모른 채 (method2에게 떠넘김) 잘 실행된다.
!! 에러 log를 띄우는 이유: 사용자가 예외적인 상황이 발생했었다는 사실을 알 수 있도록 하기 위함 !!
*/
}
}
}
nullpointerException은 RuntimeException을 상속받고,
RuntimeException이 exception을 상속받는다.
exception - 최상위 부모
ㅣ
RuntimeException - 부모
ㅣ
nullpointerException -자식
private static void method2() { //method1로 예외가 넘어가지 않도록 try catch 블럭 실행
try {
String str = null;
str.length();
System.out.println("method2 Ended");
} catch(NullPointerException ex) { //가장 구체적인 exception이 실행된다.
System.out.println("NullPointerException");
ex.printStackTrace();
} catch (Exception ex) { // Exception은 java.lang에 포함되어있는 클래스 중 하나
ex.printStackTrace();
}
}
// ArrayIndexOutOfBoundsException 발생시키기
private static void method2() { //method1로 예외가 넘어가지 않도록 try catch 블럭 실행
try {
// String str = null;
// str.length();
int[] i = {1,2};
int number = i[3]; // ArrayIndexOutOfBoundsException 예외 -> sysout "Matched Exception"이 실행된다(상속으로)
System.out.println("method2 Ended");
} catch(NullPointerException ex) { //가장 구체적인 exception이 실행된다.
System.out.println("Matched NullPointerException");
ex.printStackTrace();
} catch (Exception ex) { // Exception은 java.lang에 포함되어있는 클래스 중 하나
System.out.println("Matched Exception");
ex.printStackTrace();
}
}
}
// 해결
private static void method2() { //method1로 예외가 넘어가지 않도록 try catch 블럭 실행
try {
// String str = null;
// str.length();
int[] i = {1,2};
int number = i[3]; // ArrayIndexOutOfBoundsException 예외 -> sysout "Matched Exception"이 실행된다(상속으로)
System.out.println("method2 Ended");
} catch(NullPointerException ex) { //가장 구체적인 exception이 실행된다.
System.out.println("Matched NullPointerException");
ex.printStackTrace();
} catch (ArrayIndexOutOfBoundsException ex) { // Exception은 java.lang에 포함되어있는 클래스 중 하나
System.out.println("Matched ArrayIndexOutOfBoundsException");
ex.printStackTrace();
} catch (Exception ex) { // Exception은 java.lang에 포함되어있는 클래스 중 하나
System.out.println("Matched Exception");
ex.printStackTrace();
}
}
}
예외와 일치하는 catch문이 있거나, 상위 클래스의 예외와 일치해야 처리가 가능하다.
exception 처리 시 상위 클래스의 예외가 마지막에 나와야 한다.(서열대로)
public class FinallyRunner {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] numbers = {12,3,4,5};
int number = numbers[5];
//ArrayIndexOutOfBoundsException 발생 - 즉시 코드가 끊김
System.out.println("Before Scanner Close");
scanner.close();
// 코드가 끊겨서 scanner가 닫히지 않음.
}
}
public class FinallyRunner {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int[] numbers = {12, 3, 4, 5};
int number = numbers[5];
//ArrayIndexOutOfBoundsException 발생 - 즉시 코드가 끊김
// 코드가 끊겨서 scanner가 닫히지 않음.
} catch(Exception e) {
e.printStackTrace();
} finally { // finally에 포함된 코드는 항상 실행된다.
System.out.println("Before Scanner Close");
scanner.close();
/*
=> finally를 사용해 생성된 모든 객체가 닫힌 상태가 되었는지 확인할 수 있음
*/
}
System.out.println("실행 완료");
}
}
finally는 예외가 발생하지 않거나, return이 존재하거나, 예외가 발생하거나 이 모든 상황에 실행이 된다.
System.exit(1);를 하는 극단적인 상황에서는 실행되지 않는다(하지 말 것)
try~ catch, try + finally 의 조합으로 컴파일된다. 하지만, try만 쓰는 경우 컴파일되지 않는다.
String[] str_arr = {"10", "스물", "30"};
for(int i=3; i>=0; i--) {
try {
int val = Integer.parseInt(str_arr[i])/i;
// str_arr[3]/3 str_arr[2]/2 str_arr[1]/1 str_arr[0]/0
System.out.println(val);
} catch (ArrayIndexOutOfBoundsException e) { // Exception은 항상 작은게 먼저 온다.
System.out.println("에러메시지 : 배열의 인덱스 번호 " + e.getMessage() +
"이 존재하지 않습니다.");
} catch (ArithmeticException e) {
System.out.println("에러메시지 : 분모에는 0이 올 수 없습니다.");
} catch (Exception e) {
System.out.println("에러메시지 : " + e.getMessage());
// 모든 Exception을 다 받아주는 Exception들의 부모 클래스
[ 출력 결과]
에러메시지 : 배열의 인덱스 번호 3이 존재하지 않습니다.
15
에러메시지 : For input string: "스물"
에러메시지 : 분모에는 0이 올 수 없습니다.
예외처리시 부모클래스의 예외가 맨 아래에 나와야 한다.
예외 처리는 위에서 처리되면서 위의 것이 처리가 안되면
아래로 내려가라는 말이다.
부모 클래스의 예외가 먼저 나오고 자식 클래스의 예외가 아래에 나오면 부모클래스 익셉션이 처리를 못한것을 자식클래스 익셉션이 처리를 하라는 것은 모순이기 때문이다.
//== 사용자(개발자)가 정의하는 예외(익셉션)절 클래스 만들기 == //
/*
1. Exception 클래스를 상속받아야함
2. 생성자내에서 예외메시지(오류메시지)를 등록해주면 끝남
*/
public class Jango_lack_Exception extends Exception { // 사용자정의 exception의 조상은 java.lang.Exception => 클래스 생성 시 Superclass 에서 수정하기
private static final long serialVersionUID = 1L;
// 기본생성자
public Jango_lack_Exception() {
super(">> 잔고량이 주문량보다 적으므로 주문이 불가합니다. <<");
}
// 파라미터가 있는 생성자
public Jango_lack_Exception(String err_msg) {
super(err_msg);
}
}
public interface Product {
// 주문받기1
void order(int jumun_su) throws Jango_lack_Exception; // 사용자정의 exception => 잔고부족 exception
// 주문받기2
void jumun(int jumun_su) throws Jango_lack_Exception;
}
public class Product_imple implements Product { // extends Object 생략
//public class Product_imple extends Object implements Product { 와 같은 의미
// 모든 클래스의 최상위 부모 클래스는 Object 이며, 클래스 생성시 extends 를 하지 않으면 자동적으로 extends Object 가 생략된 것
// field
private String prod_name; // 제품명("새우깡" "감자깡" "양파링")
private int jango; // 잔고 ( 100 50 150 )
// method
public String getProd_name() {
return prod_name;
}
public void setProd_name(String prod_name) {
this.prod_name = prod_name;
}
public int getJango() {
return jango;
}
public void setJango(int jango) {
this.jango = jango;
}
// 제품의 정보 보여주기
@Override
public String toString() { // Object 클래스에는 기본적으로 toString() 메소드가 존재하기 때문에 자식클래스에 재정의가 가능함
return "1.제품명 : " + prod_name + "\n" + "2.잔고량 : " + jango + "개\n";
}
// 주문받기1
@Override
public void order(int jumun_su) throws Jango_lack_Exception {
// order(int jumun_su) 메소드는 파라미터로 들어오는 jumun_su 의 값에 따라서
// 사용자(개발자)가 만든 Jango_lack_Exception 익셉션을 유발 할 수 있다는 의미
if(jango < jumun_su) { // 주문량보다 잔고량이 적을 경우 사용자정의 exception 발생
throw new Jango_lack_Exception(); // 기본생성자
}
else {
System.out.println(prod_name + " 제품을 " + jumun_su + "개를 주문하셨습니다.");
jango -= jumun_su;
System.out.println(toString());
}
}
// 주문받기2
@Override
public void jumun(int jumun_su) throws Jango_lack_Exception {
// jumun(int jumun_su) 메소드는 파라미터로 들어오는 jumun_su 의 값에 따라서
// 사용자(개발자)가 만든 Jango_lack_Exception 익셉션을 유발 할 수 있다는 의미
if(jango < jumun_su) { // 주문량보다 잔고량이 적을 경우 사용자정의 exception 발생
throw new Jango_lack_Exception(">> " + prod_name + "은 잔고가 " + jango + "개인데 주문량이 " + jumun_su + "개라서 잔고부족으로 주문이 불가합니다. <<"); // 파라미터가 있는 생성자
}
else {
System.out.println(prod_name + " 제품을 " + jumun_su + "개를 주문하셨습니다.");
jango -= jumun_su;
System.out.println(toString());
}
}
}
public class Main {
public static void main(String[] args) {
Product_imple p1 = new Product_imple();
p1.setProd_name("새우깡");
p1.setJango(100);
// === Product_imple 클래스에서 .toString() 메소드를 오버라이딩(재정의) 하기 전 ===
// System.out.println(p1); // p1만 쓰게되면 .toString() 이 생략되어진 것
// my.day17.b.user_define_exception.Product_imple@6f75e721
// System.out.println(p1.toString());
// my.day17.b.user_define_exception.Product_imple@6f75e721
// === Product_imple 클래스에서 .toString() 메소드를 오버라이딩(재정의) 한 후 ===
// System.out.println(p1); // == p1.toString()
/*
1.제품명 : 새우깡
2.잔고량 : 100개
*/
// System.out.println(p1.toString());
/*
1.제품명 : 새우깡
2.잔고량 : 100개
*/
Product_imple p2 = new Product_imple();
p2.setProd_name("감자깡");
p2.setJango(50);
Product_imple p3 = new Product_imple();
p3.setProd_name("양파링");
p3.setJango(150);
Product[] prod_arr = new Product[3];
prod_arr[0] = p1;
prod_arr[1] = p2;
prod_arr[2] = p3;
for(Product prod : prod_arr) { // 3번 반복
System.out.println(prod); // prod.toString() 생략
} // end of for ----------
/*
1.제품명 : 새우깡
2.잔고량 : 100개
1.제품명 : 감자깡
2.잔고량 : 50개
1.제품명 : 양파링
2.잔고량 : 150개
*/
System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~\n");
try {
prod_arr[0].order(30);
} catch (Jango_lack_Exception e) {
e.printStackTrace();
}
System.out.println("\n========================\n");
try {
prod_arr[1].order(80);
} catch (Jango_lack_Exception e) {
// e.printStackTrace();
System.out.println("예외메시지 : " + e.getMessage());
}
// 예외메시지 : >> 잔고량이 주문량보다 적으므로 주문이 불가합니다. <<
System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~\n");
try {
prod_arr[2].jumun(200);
} catch (Jango_lack_Exception e) {
// e.printStackTrace();
System.out.println("예외메시지 : " + e.getMessage());
}
// 예외메시지 : >> 양파링은 잔고가 150개인데 주문량이 200개라서 잔고부족으로 주문이 불가합니다. <<
} // end of public static void main(String[] args) ----------
}