Programming/자바

생활코딩 자바 - 예외

지고르 2022. 4. 12. 13:31

낙법 / 덜 실패하는 법

 

예외란

오류 처리 위한 방법

 

  •  
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);  
System.out.print(" 입니다.");
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();  => 분모 0으로 인한 오류
}
}

//

 계산결과는 java.lang.ArithmeticException: / by zero

 

  • try 삽입
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
try {    => 오류가 예측되는 구간
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){    => 오류 발생시 실행 / 오류 정보를 담고 있는 객체(클래스타입이 Exception인 e)를 매개변수로 전달
System.out.println("오류가 발생했습니다 : "+e.getMessage());    => Exception 클래스의 메소드로 getMessage 있음 => e는 Exception의 인스턴스이므로 getMessage 실행시킴

 

}
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();
 
Calculator c2 = new Calculator();
c2.setOprands(10, 5);
c2.divide();
}
}

 

 

뒷수습
 
  •  
class Calculator{
int left, right;

 

public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());
System.out.println("\n\ne.toString()\n"+e.toString());
System.out.println("\n\ne.printStackTrace()");
e.printStackTrace();
}
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();
}
}

 

  •  
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());     //예외에 대한 간단한 힌트
System.out.println("\n\ne.toString()\n"+e.toString());    //e 인스턴스에 대한 자세한 정보
System.out.println("\n\ne.printStackTrace()");
e.printStackTrace();     //화면에 뜨는 에러사항 출력
}}}
 
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();
}
}

 

//

계산결과는
 
e.getMessage()
/ by zero
 
 
e.toString()
java.lang.ArithmeticException: / by zero
 

 

 
e.printStackTrace()
java.lang.ArithmeticException: / by zero
at org.opentutorials.javatutorials.exception.Calculator.divide(CalculatorDemo.java:11)
at org.opentutorials.javatutorials.exception.CalculatorDemo.main(CalculatorDemo.java:25)

 

 

  •  
class Calculator{
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());
}
System.out.println("Divide End")    //catch 구문 끝나면 바깥 로직도 실행됨
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();
}}
 
//
e.getMessage()
/ by zero
Divide End

 

 

다양한 예외 & 다중캐치

 

  • 배열에 벗어난 값으로 인한 오류

class A {
private int[] arr = new int[3];    //arr 인스턴스 변수는 세 개의 정수를 넣을 수 있는 정수 배열임
A() {
arr[0]=0;
arr[1]=10;
arr[2]=20;
}
public void z(int first, int second) {
System.out.println(arr[first]/arr[second]);
}
}

public class ExceptionDemo1 {
public static void main(String[] args) {
A a =new A();
a.z(10, 1);   //정해진 배열 내 값이 아니므로 에러
}
}

 

//

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3
at org.opentutorials.javatutorials.exception.A.z(ExceptionDemo1.java:11)
at org.opentutorials.javatutorials.exception.ExceptionDemo1.main(ExceptionDemo1.java:18)

 

  • 분모 0으로 인한 오류

class A {
private int[] arr = new int[3];
A() {
arr[0]=0;
arr[1]=10;
arr[2]=20;
}
public void z(int first, int second) {
System.out.println(arr[first]/arr[second]);
}
}

public class ExceptionDemo1 {
public static void main(String[] args) {
A a =new A();
a.z(1, 0);
}
}

 

//

java.lang.ArithmeticException: / by zero

 

 

  • 두 가지 오류에 대비한 예외 설정

class A {
private int[] arr = new int[3];
A() {
arr[0]=0;
arr[1]=10;
arr[2]=20;
}
public void z(int first, int second) {
try {
System.out.println(arr[first]/arr[second]);
catch(ArithmeticException e) { 
System.out.println("A");
catch(ArrayIndexOutOfBoundsException e){
System.out.println("B");

} catch(Exception e) {}    //기타 예외사항
}}

public class ExceptionDemo1 {
public static void main(String[] args) {
A a =new A();
a.z(10, 1);
}
}

 

 

finally : 예외와 상관없이 실행되는 로직

 

  •  

class A {
private int[] arr = new int[3];
A() {
arr[0]=0;
arr[1]=10;
arr[2]=20;
}

 

public void z(int first, int second) {
try {
System.out.println(arr[first]/arr[second]);
} catch(ArithmeticException e) { 
System.out.println("A");
} catch(ArrayIndexOutOfBoundsException e){
System.out.println("B");
} catch(Exception e) {
System.out.println("exception");
finally {
System.out.println("finally");
}

 

public class ExceptionDemo1 {
public static void main(String[] args) {
A a =new A();
a.z(10, 1);
a.z(1, 0);
a.z(2, 1);
}
}

 

//

B
finally
A
finally
2
finally

 

  • finally의 용도

DB와 애플리케이션들의 접속을 끊어줌

예외건 예외가 아니건 실행해야 하는 로직 실행

 

 

예외의 강제

 

  •  

import java.io.*;

public class CheckedExceptionDemo {

public static void main(String[] args) {
BufferedReader bReader = new BufferedReader (new FileReader("out.txt"));    //F2-help(ㅠ왜 f2가 안 먹지)-클래스 설명에서 throws-FileNotFoundException 확인 가능함 => 해당 에러에 대한 예외 처리 필수
String input = bReader.readLine();
System.out.println(input);
}

}

 

//

Unhandled exception type FileNotFoundException
Unhandled exception type IOException

 

  •  

import java.io.*;

public class CheckedExceptionDemo {

public static void main(String[] args) {
BufferedReader bReader = null;
String input = null;


try {
bReader = new BufferedReader (new FileReader("out.txt"));
catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
input = bReader.readLine();
catch (IOException e) {
e.printStackTrace();
}
System.out.println(input);
}
}

 

 

예외 사슬

 

  • new FileReader("out.txt") 

FileReader은 내부적으로 예외를 발생(throw)시켜 개발자들이 예외 처리(try, catch)하도록 만들어짐

 

 

  •  

class B{
void run() {}
}

class C {
void run() {
B b = new B();
b.run();
}

public class ThrowExceptionDemo {
public static void main(String[] args) {
C c = new C();
c.run();
}

}}

 

 

B -> C -> Demo -> 일반 사용자

C는 B의 사용자, Demo는 C의 사용자, 최종 사용자는 일반 사용자

 

catch 하지 않고 throw하는 것

=> B에서 예외 발생시 C로 넘김, C도 Demo로 넘김, Demo는 일반 사용자에게 넘김(=애플리케이션 예외 처리 않고 자동 종료 시킴)

 

 

책임의 전가 throws

 

  • 클래스 C에 넘기기

import java.io.*;

class B{
    void run() throws FileNotFoundException,IOException{    //run 메소드에서 F오류 생길 수 있음을 암시하며 넘김
        BufferedReader bReader = null;
        String input = null;    //2. 예외에 대한 책임 C로 넘겼으므로 try catch 삭제
        bReader = new BufferedReader(new FileReader("out.txt"));  
        input = bReader.readLine();
        System.out.println(input); 
    }
}
class C{
    void run(){
        B b = new B();
        try {
b.run();    //1. run만 했을 시 예상되는 오류 대응하지 못하므로 try catch 구문 추가 (class B throws 전)
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}  
    }
}
public class ThrowExceptionDemo {
    public static void main(String[] args) {
         C c = new C();
         c.run();
    }   
}

 

 

  • 메인 메소드에 넘기기

import java.io.*;

class B{
    void run() throws FileNotFoundException,IOException{    
        BufferedReader bReader = null;
        String input = null;
        bReader = new BufferedReader(new FileReader("out.txt"));  
        input = bReader.readLine();
        System.out.println(input); 
    }
}
class C{
    void run() throws FileNotFoundException, IOException{
        B b = new B();
b.run(); 
    }
}
public class ThrowExceptionDemo {
    public static void main(String[] args) {
         C c = new C();
         try {
c.run();
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {    //F오류의 상위에 IO오류가 있으므로 F오류는 삭제 가능
e.printStackTrace();
}
    }   
}

 

  • 오류 시 다른 메시지 뜨도록 하기

class B{
    void run() throws FileNotFoundException,IOException{    
        BufferedReader bReader = null;
        String input = null;
        bReader = new BufferedReader(new FileReader("out.txt")); 
        input = bReader.readLine();
        System.out.println(input); 
    }
}
class C{
    void run() throws FileNotFoundException, IOException{
        B b = new B();
b.run(); 
    }
}
public class ThrowExceptionDemo {
    public static void main(String[] args) {
         C c = new C();
         try {
c.run();
} catch (FileNotFoundException e) {
System.out.println("파일 필요");
} catch (IOException e) {    
e.printStackTrace();
}
    }   
}

 

 

예외 만들기

 

  • 계산기 예제
class Calculator{
int left, right;
public void setOprands(int left, int right){    => 1. IllegalArgumentException (잘못된 인자)
this.left = left;
this.right = right;
}
public void divide(){    => 2. ArithmeticException (분모 0)
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());
System.out.println("\n\ne.toString()\n"+e.toString());
System.out.println("\n\ne.printStackTrace()");
e.printStackTrace();
}
}
}
public class CalculatorDemo {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 0);
c1.divide();
}
}

 

//

계산결과는 오류 발생/ by zero

e.toString()
java.lang.ArithmeticException: / by zero

e.printStackTrace()
java.lang.ArithmeticException: / by zero
at org.opentutorials.javatutorials.exception.Calculator.divide(CalculatorDemo.java:12)
at org.opentutorials.javatutorials.exception.CalculatorDemo.main(CalculatorDemo.java:27)

 

  • I 오류 예외 만들기

class Calculator2{
    int left, right;
    public void setOprands(int left, int right){
     if (right == 0) {
     throw new IllegalArgumentException("두번째 인자로 0 허용하지 않음");
     }
        this.left = left;
        this.right = right;
    }

 

~

 

//

Exception in thread "main" java.lang.IllegalArgumentException: 두번째 인자로 0 허용하지 않음
at org.opentutorials.javatutorials.exception.Calculator2.setOprands(CalculatorDemo2.java:7)
at org.opentutorials.javatutorials.exception.CalculatorDemo2.main(CalculatorDemo2.java:29)

 

 

=> 전역으로 두번째 인자로 0 들어오지 못함

 

  • A 오류 만들기

class Calculator3{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
     if (right==0) {
     throw new ArithmeticException("허용하지 않음");
     }    //1. throw문 실행시 divide 로직 중단

     try {

 

=> divde 관련된 작업 시에만 0 비허용

 

~    public static void main(String[] args) {
        Calculator3 c1 = new Calculator3();
        c1.setOprands(10, 0);
        try {
         c1.divide();
        } catch(ArithmeticException e){   // 2. new ArithmeticException을 catch문의 ArithmeticException으로 넣어 실행 (메소드처럼)
         System.out.println(e.getMessage());
        }
        }
    }

 

//

허용하지 않음

 

  •  
예외 사용해야 할 상황
IllegalArgumentException 매개변수가 의도하지 않은 상황을 유발시킬 때
IllegalStateException 메소드를 호출하기 위한 상태가 아닐 때
NullPointerException 매개 변수 값이 null 일 때
IndexOutOfBoundsException 인덱스 매개 변수 값이 범위를 벗어날 때
ArithmeticException 산술적인 연산에 오류가 있을 때

 

 

예외의 여러 가지 상황들

 

  •  

import java.io.IOException;

class E{
void throwA() {
throw new ArithmeticException();
}
void throwI() {
throw new IOException();    => checked E
}
}

 

  • 오류 수정하는 두가지 방법

class E{
void throwA() {
throw new ArithmeticException();
}
void throwI1() {
try{
throw new IOException();
catch(IOException e) {
e.printStackTrace();
}}
void throwI2() throws IOException {    //사용자에게 넘김
throw new IOException();
}
}

 

 

checked & unchecked

 

  • 예외의 선조 : throwable

ArithmeticException API 문서 - java.lang. object ~throwable ~Exception ~RuntimeException ~ArithmeticException

 

throwable 메소드 - getMessage, printStackTrace, toString, 

 

 

  • 예외의 종류

         

throwable <= Error (throwable 직접 상속 / JVM 에러일시 발생 / 우리가 관여할 수 없음)

              <= Exception <= IOException

                       "        <= RuntimeException <= ArithmeticException

 

ArithmeticE는 IOE와 달리 부모클래스로 RuntimeE 가짐

=> unchecked => 예외 처리 필수 아님

IOE => checked => 반드시 try catch나 throws 사용하여 예외 처리

 

 

사용자 정의 예외 (<->표준 예외)

 

예외 만들기 전 checked / unchecked 결정

=> API가 예외 던졌을 때 사용자가 예외상황 복구할 수 있다면 checked (예외 처리 강제)

=> 사용자가 API 사용법 어겨 발생하는 문제거나 예외 발생 시점에서 프로그램 종료가 덜 위험할 때 unchecked 

 

  • unchecked

class DivideException extends RuntimeException{
DivideException(){
super();
}
DivideException(String message){
super(message);
}
}

class Calculator3{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
     if (right==0) {
     throw new DivideException("허용하지 않음");
     }}}
    
public class CalculatorDemo3 {
    public static void main(String[] args) {
        Calculator3 c1 = new Calculator3();
        c1.setOprands(10, 0);
        try {
         c1.divide();
        } catch(ArithmeticException e){
         System.out.println(e.getMessage());
        }}}

 

//

Exception in thread "main" org.opentutorials.javatutorials.exception.DivideException: 허용하지 않음

 

 

  • checked

class DivideException extends Exception{
DivideException(){
super();
}
DivideException(String message){
super(message);
}
}

class Calculator3{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
     if (right==0) {
     throw new Exception("허용하지 않음");
     }}}
    
public class CalculatorDemo3 {
    public static void main(String[] args) {
        Calculator3 c1 = new Calculator3();
        c1.setOprands(10, 0);
        try {
         c1.divide();
        } catch(ArithmeticException e){
         System.out.println(e.getMessage());
        }}}

 

 

  •  

class DivideException extends Exception{
public int left;
public int right;
DivideException(){
super();
}
DivideException(String message){
super(message);}
DivideException(String message, int left, int right){
super(message);
this.left = left;
this.right = right;
}}

class Calculator3{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide() throws DivideException {
     if (right==0) {
     throw new DivideException("허용하지 않음", this.left, this.right);
     }
     System.out.println(this.left/this.right);}}
    
public class CalculatorDemo3 {
    public static void main(String[] args) {
        Calculator3 c1 = new Calculator3();
        c1.setOprands(10, 0);
        try {
         c1.divide();
        } catch(DivideException e){
         System.out.println(e.getMessage());
         System.out.println(e.left);
         System.out.println(e.right);
        }}}

 

//

허용하지 않음
10
0