생활코딩 자바 - 예외
낙법 / 덜 실패하는 법
예외란
오류 처리 위한 방법
//
계산결과는 java.lang.ArithmeticException: / by zero
- try 삽입
뒷수습
//
다양한 예외 & 다중캐치
- 배열에 벗어난 값으로 인한 오류
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();
}
}
}
예외 만들기
- 계산기 예제
//
계산결과는 오류 발생/ 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