- 컴파일 에러 : 빨간줄 뜨는것! (개발자의 오타나 로직실수로) 실행하지못하는것
- 런타임 에러 : 프로그램이 정상적으로 실행되지만 실행도중에 중간에 에러나는거 EX) 널포인터오류, 배열크기벗어난오류
- 시스템 에러 : 램의 공간이 부족하거나, 전원이 차단되는 에러 (개발자가 처리할 수 없음)
이러한 에러를 해결하기 위해서는 소스 수정으로 해결 가능한 에러를 예외 라고 한다
부모 계층의 클래스들이 자식 계층의 클래스들의 오류를 다 처리해준다
ex) catch문에 RuntimeException 넣으면 ArithmeticExcption, NullPointerException 등 자식 exception들을 다 처리할 수 있음
Exception
throw
키워드 사용 -> 예외도 객체이므로 new
로 생성 후 예외 발생 throws
키워드 사용public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
public class Client {
public void call() throws MyCheckedException { // 해결못해서 밖으로 던지기
throw new MyCheckedException("ex"); // 예외 발생시키기 (ex = 메시지입력)
}
}
public class Service {
Client client = new Client();
// 예외를 잡아서 처리하는 코드
public void callCatch(){
try{
client.call(); // call 메소드에서 예외발생시 catch에서 예외처리
}catch(MyCheckedException e){
// 예외처리로직
}
}
// 체크 예외를 밖으로 던지는 코드
public void catchThrow() throws MyCheckedException { // 예외를 잡지 않고 밖으로 던지기
client.call();
}
}
//ex) 밑에 예외들은 무조건 try ~ catch로 예외처리 해야함!
// try ~ catch로 하지 않을시 프로그램 시작 자체가 안됨!
throw new FileNotFoundException(); //오류
throw new EOFException(); // 오류
throw new SQLException(); // 오류
예외처리 2가지 규칙
try catch
문throws 예외이름
Exception
을 지정하면, 자식 예외들 모두 처리 가능Checked Exception
장단점
// 발생하는 예외에 대해서 처리하지 않아도 되는 예외 (Unchecked Exception)
ArithmeticException : 수학적으로 계산이 불가능할 때 발생하는 Exception
int su = 10;
int su2 = 0;
System.out.println(su/su2);
// ArrayIndexOutOfBounsException
배열의 인덱스범위를 초과해서 접근할 때 발생하는 예외
+int[] intArr = new int[5];
System.out.println(intArr[5]);
// ClassCastException
클래스형변환을 잘못했을 때
Object o = new String("안녕");
Integer num = (Integer)o; // 문자열을 정수형으로 바꿀 수 없음
// NullPointException
참조형변수에 null값이 있을 때 접근연산자를 사용하면 발생하는 예외
String name = null;
name.length(); // name에는 널값이 있는데 .연산자로 접근하고 있어서 발생
// NumberFormatException
문자열을 숫자형으로 변환할 때 변환 불가능한 문자가 있는 경우
String name = null;
su = Integer.parseInt(name); //문자열을 정수로 바꿀 수 없음
// InputMissMatchException
입력값의 타입이 일치하지 않을 때 발생
Scanner sc = new Scanner(System.in);
int su3 = sc.nextInt();
UnChecked Exception
장단점
✅ 체크 예외 vs 언체크 예외
체크예외 : 예외를 잡아서 처리하지 않으면 항상 명시적으로 throws
키워드로 예외를 던져야함
-> Exception
을 상속받은 예외는 체크 예외가 됨
언체크예외 : 예외를 잡아서 처리하지 않아도 throws
키워드를 생략 가능
-> RuntimeException
을 상속받은 예외는 언체크 예외가 됨
try catch
문을 이용해서 예외를 처리할 경우
정상적으로 연결이 되든 안되든 필수적으로 실행해야하는 기능이 있다 (ex : 자원 반환)
그럴 경우 catch 뒤 finally
키워드를 사용한다
try{
// 정상흐름
}catch{
// 예외흐름
}finally{
// 반드시 호출해야하는 마무리 흐름
}
try
문이 시작되면, catch
안에서 잡을 수 없는 예외가 발생해도 finally
코드는 어떤 경우라도 호출됨
try finally
만 사용할 수도 있음
try
에서 외부 자원을 사용하고, try
가 끝나면 외부 자원을 반납하는 패턴이 반복됨
-> 자바7에서부터 try-with-resources
라는 편의 기능을 제공함
이 기능을 사용하려면 먼저 AutoCloseable
인터페이스를 구현해야함
이 인터페이스를 구현하면 try
가 끝나는 시점에 close()
가 자동으로 호출됨
-> try
에서만 자원을 사용하기 때문에, catch
문 시작전에 try
가 끝나는 순간 close
() 호출됨
try-with-resources
장점
close()
호출이 필요 없으므로 코드가 간결해짐try-> catch-> finally
로 catch
이후에 자원을 반납했지만try
블럭이 끝나면 즉시 close()
를 호출한다 (더 빠른 자원해제가 가능)try{
// 정상흐름
}catch(ConnectException e){
// 연결오류
}catch(NetworkClientException e){
// 네트워크 오류
}catch(Exception e){
// 알수없는 오류 -> 위 2개 예외로 처리못한 나머지 모든 예외들은 여기서 처리됨
}finally{
// 무조건실행되는 로직
}
실무에서는 수많은 예외처리들이 있다
하지만 실제로 개발자가 catch
문을 사용해서 처리할 수 있는 것이 많지 않다
그래서 모든 예외들을 잡는 것은 좋지 않다 -> catch
로 처리해도 똑같은 문제가 발생하기 때문에
ex) 데이터베이스 서버 문제
그러면 모든 예외들을 throws
를 사용해서 던지면 되지 않을까?
하지만 요즘 라이브러리도 많이 사용하면서, 수 많은 예외들이 존재한다
모든 예외들을 해결하지 않고 밖으로 던지면 모든 클래스에서 지저분한 코드만 추가해지는 것 뿐이다
-> 해결하지 못한 예외를 폭탄처럼 돌리기 때문에
그러면 최상위 계층인 Exception
을 던져서 처리하면 되지 않을까?
코드는 깔끔해지지만 치명적인 문제가 발생한다
-> 최상위 타입인 Exception
을 던지게되면 다른 체크 예외를 체크할 수 있는 기능이 무효화됨
-> 중요한 체크 예외를 다 놓치게 됨
-> 중간에 중요한 체크 예외가 발생해도 컴파일러는 문법에 맞다 판단해서 컴파일 오류를 발생 X
즉 꼭 필요한 경우가 아니라면, Exception
자체를 밖으로 던지는 것은 좋지 않다
요즘은 본인이 해결할 수 있는 예외만 잡아서 처리하고
해결할 수 없는 예외는 던지는것이 더 나은 선택일 수도 있다
그리고 처리할 수 없는 예외들은 공통
으로 처리할 수 있는 곳을 만들어서 한곳에서 해결한다
ex) 처리할 수 없는 모든 예외들 -> 현재 시스템에 문제가 있습니다 오류 메시지 출력
// 공통 예외처리
private static void exceptionHandler(Exception e) {
//공통 처리
System.out.println("사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.");
System.out.println("==개발자용 디버깅 메시지==");
e.printStackTrace(System.out); // 스택 트레이스 출력
//e.printStackTrace(); // System.err에 스택 트레이스 출력
//필요하면 예외 별로 별도의 추가 처리 가능
if (e instanceof SendExceptionV4 sendEx) {
System.out.println("[전송 오류] 전송 데이터: " + sendEx.getSendData());
}
}
try {
networkService.sendMessage(input);
} catch (Exception e) { // 모든 예외를 잡아서 처리
exceptionHandler(e);
}
최근 만들어진 라이브러리는 대부분 UnChecked Runtime 에러를
사용함
// CharCheckException 클래스
package com.bs.practice.charCheck.exception;
public class CharCheckException extends Exception{ // 최상위 예외클래스인 Exception 상속
public CharCheckException() {
// TODO Auto-generated constructor stub
}
public CharCheckException(String msg) {
super(msg); // 부모생성자로 메시지 호출
}
}
// CharacterMenu 클래스
package com.bs.practice.charCheck.run;
import java.util.Scanner;
import com.bs.practice.charCheck.controller.CharacterController;
import com.bs.practice.charCheck.exception.CharCheckException;
public class CharacterMenu{
public void menu() {
Scanner sc = new Scanner(System.in);
System.out.print("문자열 입력 : ");
String str = sc.nextLine();
int cnt = 0;
try {
cnt = new CharacterController().countAlpha(str); // 문자전달
} catch (CharCheckException e) { // 예외발생시 경고문 출력
e.printStackTrace();
}
if(cnt == 0) {
return;
}else {
System.out.println("'"+str+"'"+"에 포함된 영문자 개수 : "+cnt);
}
}
}
// CharacterController 클래스
package com.bs.practice.charCheck.controller;
import com.bs.practice.charCheck.exception.CharCheckException;
public class CharacterController {
public CharacterController() {}
public int countAlpha(String s) throws CharCheckException { // 예외던지기
int cnt =0;
try{
for(int i=0; i<s.length(); i++) {
if((s.charAt(i) >= 'A' && s.charAt(i) <= 'Z')
|| (s.charAt(i) >= 'a' && s.charAt(i) <= 'z')) {
cnt++;
}
if(s.charAt(i) == ' ') { // 문자열에 공백 있을시 강제로 예외발생
throw new CharCheckException("체크할 문자열 안에 공백이 포함되어 있습니다.");
}
}
}catch(CharCheckException e){ // 예외발생시
e.printStackTrace(); // 해당 문구 출력
return 0;
}
// finally {
// return cnt;
// }
return cnt;
}
}
✅ try~catch~finally 문을 이용해서 직접예외를 처리하기
int[] intArr = { 1, 2, 3, 4, 5 };
try {
int a = intArr[5]; // 예외발생하는 부분까지만 실행하고 catch로 넘어감!
System.out.println(a);
} catch (ArrayIndexOutOfBoundsException e) { // 클래스라서 객체 e 만들어줌
System.out.println("인덱스 부족해!"); // catch문에서 해결 방법 구현!
int[] temp = new int[intArr.length + 5];
System.arraycopy(intArr, 0, temp, 0, intArr.length);
intArr = temp;
int a = intArr[5];
System.out.println(a);
}
System.out.println("실행되니?");
String name = null;
try {
if (name.length() < 2) { // 예외가 발생한 지점에서 바로 catch로 넘어감
System.out.println("이름은 두글자 이상 작성해주세요");
} else {
System.out.println(name + " 참 멋진 이름이네요");
}
} catch (NullPointerException e) {
System.out.println("name이 null 이면 처리 할 수 없습니다.");
}
System.out.println("예외처리 후 실행되는 로직");
// try~catch문을 작성했을 때 catch문을 여러개 작성할 수 있다.
// 동시에 두개의 오류가 발생할 순 없다
String[] names = {"유병승",null,"최하리"};
try {
for(int i=0; i<=names.length; i++) {
if(names[i].length()>2) {
System.out.println(name);
}
}
}catch(NullPointerException e) { // 예외처리 1
System.out.println("널포인트 Exception");
}catch(ArrayIndexOutOfBoundsException e) { // 예외처리 2
System.out.println("인덱스가 부족해");
}
// 위 코드와 같은 결과갖을 갖으며 |연사자를 통해서 둘중하나 걸렸을 때 처리할 수 있음
String[] names = { "유병승", null, "최하리" };
try {
for (int i = 0; i <= names.length; i++) {
if (names[i].length() > 2) {
System.out.println(name);
}
}
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
e.printStackTrace(); // 오류구문이 빨간문장으로 뜨지만 프로그램은 종료되지 않음
// printStackTrace는 어떤위치에서 어떤오류가 발생했는지 확인할 때 사용
System.out.println("예외발생!!!");
}
// 부모 Exception은 자식 Exception이 발생했을 때 모두 처리 할 수 있다.
// 즉 예외처리 최상위 클래스인 Throwable은 모든 예외를 처리 할 수 있음
// 반대로 자식 예외로 부모 예외를 처리할 수 없음
// 공통적으로 처리하는 부분이 동일하면 Exception으로 모든 예외를 처리하고
// 오류부분을 일일이 찾아내고싶을 때는 해당 예외를 넣는다.
// 주로 각각의 예외부분을 먼저 넣은 후에 마지막부분에 최상위 예외클래스를 넣는다.
Object o = new String("test");
try {
int a =(int)o;
a = intArr[11];
}catch(Throwable e){
System.out.println(e.getMessage());
System.out.println("예외처리");
}
Object o = new String("test");
try {
int a =(int)o;
a = intArr[11];
}catch(Throwable e){
System.out.println(e.getMessage());
System.out.println("예외처리");
}
// 예외처리 구문에서 반드시 실행해야할 구문이 있는 경우
// finally{예외가 발생하던, 안하던 무조건 실행되는 구문}
// try,catch문에 break가 있어도 finally는 실행되고 넘어감
try {
String n = null;
n.length(); // 널포인터 예외오류 발생
System.out.println("try문");
}catch(NullPointerException e) {
System.out.println("catch문");
}finally { // 예외가 발생하든 안하든 실행함
System.out.println("반드시 실행해");
}
// 개발자가 원하는 순간에 Exception을 발생시킬 수 있음
private void exceptionTest(Object o) {
if(o instanceof String) {
System.out.println(o);
}else {
// 직접예외를 발생시키기
throw new IllegalArgumentException(); // 예외처리명은 마음대로 정해도됨
}
}