프로그램을 실행할때 사진과 같은 에러가 나오는 경우가 종종 있을것이다.
예전엔 모르고 넘어가도 상관없지만 개발을 하는 입장이 되었으니 이것이 왜 에러가 나타나는지 알아야한다!
오늘은 이러한 에러 및 예외에 관하여 설명하겠다.
에러와 예외
먼저 에러와 예외의 정의를 확인하고 가자.
예외 (Exception) - 코드로 수습할수 있는 미약한 오류 (실행은 됩니다!)
오류(Error) - 코드로 수습 불가능한 심각한 오류로 수습이 안되니 일단 종료시키는것이다. (실행도 안됩니다!, 가장 자주 나타는 오류는 OM으로 사용되는 메모리를 초과할때 나타나는 오류인데 게임 2개를 동시에 켜보면 알것이다..)
여기서 OM이란 out of memory의 약자로 만일 GC(Garbage Collection)가 제대로 실행되지 않는다면 메모리 누수가 점점 쌓여서 OM이 발생될것이다.
하단의 사진은 에러와 예외의 관한 계층도이다.
여기서 에러의 종류를 나타내면 다음과 같다.(더있을수도 있다..)
분류 | 설명 |
컴파일 에러 (Compile Error) | 컴파일 작업시 발생하는 에러(문법적인 오류) |
런타임 에러 (Run-time Error) | 실행할때 발생하는 에러(무한루프, Nullpoint 등 설계미숙) |
논리적 에러 (Logic Error) | 사용자가 의도한 작업을 실행하지 못한에러 |
링킹 에러 (Linking Error) | 컴파일 후 이를 연결할때 발생 (헤더파일등이 없는경우 발생) |
파스 에러 (Parse Error) | 인터프리터 언어에서 나타나는 문법오류 |
컴파일 : 내가 작성한 "hello world"(고급언어)를 add esp,12(어셈블리어) -> 010100010101(기계어)로 바꾸는것
인터프리터 : 고급언어로 작성된 코드를 한줄씩 읽어 내려가며 실행하는 프로그램(파이썬, MatLAB)
상단에서 오류는 수습이 불가능하지만 예외는 수습이 가능하다고 설명을 해놨는데 이것을 예외처리 한다는 표현을 사용한다. (프로그램 실행시 발생할수 있는 예외의 발생에 대비한 코드를 작성하는것 -> 프로그램의 비정상적인 종료를 막기 위함)
에러의 종류
분류 | 설명 |
컴파일 에러 (Compile Error) | 컴파일 작업시 발생하는 에러(문법적인 오류) |
런타임 에러 (Run-time Error) | 실행할때 발생하는 에러(무한루프, Nullpoint 등 설계미숙) |
논리적 에러 (Logic Error) | 사용자가 의도한 작업을 실행하지 못한에러 |
링킹 에러 (Linking Error) | 컴파일 후 이를 연결할때 발생 (헤더파일등이 없는경우 발생) |
파스 에러 (Parse Error) | 인터프리터 언어에서 나타나는 문법오류 |
예외의 종류
Exception 클래스 - 사용자 실수에 따른 문제발생 (메모리가 안맞음, 사용자의 파일이 없음 등)
IOException - I/O (Input,Output)오류가 발생하는경우에 throw되는 예외
Runtime Exception 클래스 - 프로그래머 실수에 따른 문제발생 (코드 안맞음)
Runtime Exception 종류
예외코드 | 설명 |
NullPointerException | 객체의 참조가 없을경우 (Null을 갖는경우) |
ArrayIndexOutOfBoundException | 배열을 참조하는 인덱스가 잘못된경우(초과하는경우) |
NumberFormatException | 문자열 <-> 숫자간 변경할수 없는경우 출력 |
ClassCastException | 클래스 형변환(Casting)이 어려울경우 -> 다형성 참고 |
ArithmeticException | 예욎거인 산술조건이 발생하는경우 (5/0.0/6 등) |
NoClassDefFoundException | 원하는 클래스가 없음 |
NegativeArraySizeException | 배열의 크기가 음수인경우 |
OutOfMemoryException | 사용 가능한 메모리가 없는경우(OM) |
interruptedException | |
ILLEGAlThreadStateException(대소문자 확인 잘할것..) | 스레드가 실행중인데 또 Start를 호출한경우(이중실행시) |
상단 외에도 기타 문제가 발생하면 콘솔창에 예외코드가 나타나게 되는데 이러한문제를 방지하는걸 예외처리 한다고 표현한다.
예외를 처리하는 방법은 크게 2가지 방법이 있다.
1.try-catch-finally 문법(JDK 7 미만), try-with-resources(JDK 7 이상)문법을 활용해 에러를 처리하는 방법
2.throws 2가지이다.
try-catch-finally
//try-catch예시
//try { 에러 발생우려 코드 }
//catch ( 예외클래스 예외객체생성 ) { 해당 에러시 실행할 내용 }
try {
//예외 가능성이 있는 문장을 넣는다
} catch (Exception1 e1) { //case1
//Exception1이 발생할 우려가 있는경우
System.out.println("잔액이 부족합니다");
} catch (Exception e2) { //case2
System.out.println("최소주문금액이 부족합니다");
} catch (Exception e3) { //case3
System.out.println("배달불가지역 입니다");
} finally {
System.out.println("이전화면으로 돌아갑니다"); //예외처리가 발생해도 실행
}
이것을하단의 예시를 참고하면서 설명을진행하겠다.
사용자 정의 예외클래스(A)
//사용자 정의 예외 클래스 (A)
public class BalanceInsufficientException extends Exception {
public BalanceInsufficientException {}
public BalanceInsufficientException (String messge) { //오버로딩
super(messge); //부모클래스 호출
}
}
에러 발생메서드 (B)
//사용자 정의 예외 발생시키기(B)
public class Account{
private long balance;
public Account() {
}
public lon getBalance() { //값얻기.
return balance;
}
public voud deposit (int money) {
balance += money;
}
public void withdraw(int money) throws BalanceInsufficientException {
if (balance < money) {
throw new BalanceInsufficientException("잔고부족:"+(money-balance)+"원 모자람");
}
balance -= money;
}
}
실제코드(C)
//사용자 정의 예외 테스트 코드(C)
public class AccountExample {
public static void main(String[] args) {
Account account = new Account(); //생성자
//예금하기
account.deposit(10000); //입금하는금액
System.out.println("예금액 : "+account.getBalnce());
//출금하기
try {
account.withdraw(30000); //가격비교용 메서드
} catch(BalanceInsufficientException e) { //에러throws
String messge = e.getMessage();
System.out.println(messge);
System.out.println();
e.printStackTrace();
}
finally {
if(e != null) is.close();
}
}
}
//deposit = 입금액
//getBalance = 현재 잔고에 있는금액 (정상입금 확인용)
//withdraw = 입금액과 상품가격 비교 메서드
(A)는 사용자가 예외클래스를 정의한 내용
(B)는 예외를 발생시켰을때 실행할 내용을 작성
(C)는 실제 사용되는 코드 이다.
(C)를 기준으로 설명하자면
deposit 메서드를 사용하여 10000을 넣었고((B)의 Balance에 삽입), 정상적으로 입금된것인지 확인하기 위하여 출력문을 작성하였다.
이후 withdraw메서드를 활용하여 Balance에서 값을 출력하는 방식인데 Balance는 10000밖에 없으니 당연히 e에러가 출력될것이다.
이것을 (C)의 catch에서 잡아서 (B)로 예외(e)를 (B)로던지고 (B)는 에러에 해당하는 메세지를 출력하게 되는것이다.
하지만 try-catch 방식은 사용이 종료한후 close메서드를 호출해야 자원을 반납할수 있었는데 이러한 방식을 해결하기 위해 나타난것이 try-with-resources 방식이다.
try-with-resources
try-with-resources는 close를 자동적으로 진행되는 기능으로 하단의 코드를 보며 설명하겠다.
//try-catch-finally예시
public class TCF{
public stati void main(String[] args) {
FileWriter f = null;
try{
f = new FileWriter ("data.txt");
f.write("Hello");
} catch (IOException e){
e.printStackTrace();
} finally{
if(f != null){
try {
f.close();
} catch(IOException e){
e.printStackTrace();
}
}
}
}
}
상단의 코드를 줄인것이 하단의 코드이다.
//try-with-resource예시
public class TWR{
public stati void main(String[] args) {
//try with resource statemanets
try (FileWriter f = new FileWriter ("data.txt")) {
//close를 시켜야하는 코드를 파라미터로 넣는다.
f.write("Hello");
} catch(IOException e){
e.printStackTrace();
System.out.println("에러내용 :"e.getMessage());
//e.getMessage 메서드는 내장메서드로 어떠한 오류인지 간략하게 출력해주는 메서드
}
}
}
코드의 줄이 눈에띄게 줄어든것을 확인할수 있다.
try안에 close()가 필요한 클래스를 인스턴스화 시켜 자동으로 f.close를 내부적으로 실행하는것이다.
(Filewriter에 내장된 API중 AutoCloseable란 인터페이스가 try문 안에 선언된 객체를 자동으로 close해주기 때문에 가능)
추가로 printStackTrace()는 예외 발생 당시의 호출스택(Call Stack)에 잇던 메서드의 정보와 예외결과를 화면에 출력하는것으로 디버깅할때 유용하게 사용할수 있다.
하지만 성능은 떨어져(리플렉션을 활용해 에러를 추적하기에 오버헤드 발생, 스택정보 취합으로 서버에 부하 우려) 실제 런칭할때는 없애고 테스트 할때만 사용하는게 좋다.
Throws,Throw
예외를 임의적으로 발생시켜 해당 문제가 발생했을때 해당 에러가 나타나도 정상적으로 작동되도록 하는 기능이다.
throws - 메서드,새생성자가 수행할때 발생하는 예외를 선언할때 사용하며 상위의 메서드로 예외를 보낸다(던진다)
throw값을 받는곳에서 예외를 처리하도록 예외처리의 주체를 바꾸는것
public class ThrowExample {
//요기!!
public void divideNum(int i, int a) throws OneShouldNotInHereException {
if (a == 1) {
throw new OneShouldNotInHereException();
} else {
System.out.println(i / a);
}
}
public static void main(String[] args) {
ThrowExample throwExample = new ThrowExample();
System.out.println("*첫 번째 계산");
// 예외 발생 가능성이 있는 코드
int i = 100;
int a = 1;
try {
throwExample.divideNum(i, a);
}
catch (OneShouldNotInHereException e) {
System.out.println("에러: 1로 나눌 수 없습니다.");
}
}
}
throw - 강제로 예외를 발생시키는경우 사용, 발생시킨 예외를try-catch로 잡는다.
나눗셈을 만들었는데 1로 나누면 안되도록 조건을걸어 코딩
public class ThrowExample {
public void divideNum(int i, int a) {
if (a == 1) {
try {
throw new OneShouldNotInHereException();
} catch (OneShouldNotInHereException e) {
System.out.println("에러: 1로 나눌 수 없습니다.");
}
} else {
System.out.println(i / a);
}
}
public static void main(String[] args) {
ThrowExample throwExample = new ThrowExample();
System.out.println("*첫 번째 계산");
// 예외 발생 가능성이 있는 코드
int i = 100;
int a = 1;
throwExample.divideNum(i, a);
}
}
예외코드
public class OneShouldNotInHererException extends Exception{
}
'JAVA' 카테고리의 다른 글
프로세스와 스레드 (0) | 2023.03.07 |
---|---|
동일성(identity)과 동등성(equality) (0) | 2023.02.26 |
중첩클래스 (0) | 2023.02.21 |
생성자(Constructor)와 this (0) | 2023.02.06 |
오버로딩 (0) | 2023.02.02 |