본문 바로가기

(09.03) java 기초 - 예외처리 개념, try-catch, throws, 사용자 지정 예외, Java IO

@starweb2025. 9. 3. 19:50

[ 4주차 - 0903 ] 

    금일 커리큘럼
        ├ 09:00 ~ 12:00 자바 프로그래밍 기초  (예외처리 개념, try-catch, throws)
        └ 13:00 ~ 18:00 자바 프로그래밍 기초  (throw new, 사용자 지정 예외, Java IO 개념)

1. 예외처리 개념 (*전일복습)

프로그램 실행 중에 발생할 수 있는 예상치 못한 상황(예외)에 대비하여
프로그램의 정상적인 흐름을 유지하고 예외 상황을 안전하게 처리하는 프로그래밍 기법

 

1.1 예외처리 문법

  • try-catch
    • 해당 로직 안에서 직접 예외를 처리하는 방식
    • 예외 발생 가능성이 있는 코드를 try 블록에 작성하고, 발생한 예외를 catch 블록에서 잡아서 처리
    • catch 같은 경우 여러번 가능 (=케이스별 정의)
  • throws
    • 호출한 쪽으로 예외를 떠넘기는 방식 (=책임전가)
    • 메서드 선언부에 throws 예외타입 명시

예외 처리할 때 호출하는 쪽에서 반드시 try-catch로 잡거나, 또다시 throws로 위임해야 함

 

1.2 예외의 종류

int[] arr = {1, 2, 3};
System.out.println(arr[5]); 
// ArrayIndexOutOfBoundsException | 배열 인덱스

String str = null;
System.out.println(str.length()); 
// NullPointerException | 없음

String num = "abc";
int value = Integer.parseInt(num); 
// NumberFormatException | 숫자 타입 변환

int a = 10;
int b = 0;
System.out.println(a / b); 
// ArithmeticException | 산수

import java.io.*;
FileReader fr = new FileReader("없는파일.txt");
// FileNotFoundException | 파일없음 (checked exception)

 

1.3 예외 계층 구조

Throwable
  ├── Error                     # 시스템 에러 (개발자가 처리 안 함)
  └── Exception
       ├── Checked Exception    # 컴파일러가 처리 강제 (외부자원)
       │    (IOException, SQLException 등)
       │
       └── Unchecked Exception  # 컴파일러 강제 X (내부코드)
            └── RuntimeException
                 ├── NullPointerException
                 ├── ArithmeticException
                 ├── ArrayIndexOutOfBoundsException
                 ├── IllegalArgumentException
                 └── ...
  1. Checked Exception (체크드 예외)
    • 컴파일 시점에 반드시 처리(try-catch or throws)해야 하는 예외
    • Exception을 상속하지만 RuntimeException을 상속하지 않은 것들
    • 파일/네트워크/DB 같이 외부 자원과 연결된 경우 자주 발생 (= 외부)
    • 예: IOException, SQLException, FileNotFoundException
// 반드시 try-catch or throws 필요
import java.io.*;
public class ExamChecked {
    public static void main(String[] args) {
        try {
            FileReader fr = new FileReader("test.txt"); // FileNotFoundException
        } catch (FileNotFoundException e) {
            System.out.println("파일이 존재하지 않습니다.");
        }
    }
}
  1. Unchecked Exception (= RuntimeException 계열)
    • 컴파일러가 강제하지 않음 → 원하면 처리, 아니면 그냥 프로그램 터짐
    • RuntimeException을 상속한 예외
    • 주로 개발자 실수/논리 오류 때문에 발생 (= 내부)
    • 예: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException, NumberFormatException
public class ExamUnchecked {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
    }
}
구분 Checked Exception Unchecked Exception
발생 시점 주로 외부 요인 (파일, DB, 네트워크) 주로 코드 로직/개발자 실수
처리 강제 여부 컴파일러가 강제 (try-catch or throws 필요) 강제 없음
상속 계층 Exception (단, RuntimeException 제외) RuntimeException 상속
예시 IOException, SQLException, ClassNotFoundException NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException

2. try-catch

해당 로직 안에서 직접 예외를 처리하는 방식

 

try-catch-finally 심플 예시

import java.util.Scanner;

public class Exam {
    final static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        System.out.print("입력: ");
        String test = sc.nextLine();
        try {
            int i = Integer.parseInt(test);
            System.out.println("try블럭: " + 10/i);
        } catch (ArithmeticException e) {
            System.out.println("산수오류 캐치");
            System.out.println(e);
        } catch (NumberFormatException e) {
            System.out.println("넘버포맷오류 캐치");
            System.out.println(e);
        } finally {
            System.out.println("파이널 무조건 실행됨");
        }
    }
}
# 실행결과 - 1
입력: 0
산수오류 캐치
java.lang.ArithmeticException: / by zero
파이널 무조건 실행됨

# 실행결과 - 2
입력: 반갑습니다
넘버포맷오류 캐치
java.lang.NumberFormatException: For input string: "반갑습니다"
파이널 무조건 실행됨

# 실행결과 - 3
입력: 5
try블럭: 2
파이널 무조건 실행됨

 

if, try-catch 활용방식

import java.util.Scanner;

public class ExamOrder {
    final static Scanner sc = new Scanner(System.in);

    public static void order01() {
        int price = 0;
        long maxMoney = 100_000;
        System.out.printf("보유현금액: %,d원%n", maxMoney);
        while (true) {
            System.out.print("금액입력: ");
            try {
                price = Integer.parseInt(sc.nextLine());
                if (price <= 0) {
                    System.out.println("0원이하 안됨");
                    continue; // 다시 입력
                }
                if (price > maxMoney) {
                    System.out.println("보유현금보다 높음");
                    continue; // 다시 입력
                }
                // 정상 입력이면 break
                System.out.println("통과! / 가격: " + price + "원");
                break;
            } catch (NumberFormatException e) { // 넘버포맷
                System.out.println("[NumberFormat!] 숫자로 입력해야 합니다.");
            }
        }
    }
    public static void main(String[] args) {
        order01();
    }
}
# 실행 결과 - catch
보유현금액: 100,000원
금액입력: #@(D
[NumberFormat!] 숫자로 입력해야 합니다.

# 실행 결과 - if
금액입력: 0
0원이하 안됨

# 실행 결과 - if2
금액입력: 10000000
보유현금보다 높음

# 실행 결과 - 통과
금액입력: 23
통과! / 가격: 23원

3. throws ( 예외 떠넘기기 )

호출한 쪽으로 예외를 떠넘기는 방식

  • throws는 메서드 내부에서 직접 예외를 처리하지 않고, 호출한 쪽으로 예외 처리를 넘기는 키워드
  • 즉 책임 전가 = "이런 예외가 날 수 있으니, 호출자가 책임지고 처리해주세요"
  • 선언부에 throws 예외타입을 명시
리턴타입 메서드명(매개변수) throws 예외타입1, 예외타입2 {
    // 코드...
}

 

unchecked 익셉션 방식

runtime 계열

public class ExamThrows {
    public static int divide(int a, int b) throws ArithmeticException {
        return a / b;
    }

    public static void main(String[] args) {
        // 호출자가 예외 처리
        try {
            System.out.println(divide(10, 2)); // 정상 출력: 5
            System.out.println(divide(10, 0)); // 예외 발생
            System.out.println("이거 안나옴");
        } catch (ArithmeticException e) {
            System.out.println("예외 메시지: " + e.getMessage());
        }

        // 캐치 이후에도 실행됨
        System.out.println("이후 실행됨");
    }
}
# 실행 결과
5
예외 메시지:  / by zero
이후 실햄됨

 

checked 익셉션 방식

import java.io.FileReader;
import java.io.IOException;

public class ExamCheckedThrows {
    // FileReader는 파일이 없으면 IOException 발생 (Checked Exception)
    public static void readFile() throws IOException {
        FileReader fr2 = new FileReader("README.md");
        System.out.println("README.md 파일 열기 완료");
        FileReader fr1 = new FileReader("null.txt");
        System.out.println("null.txt 파일 열기 완료");
    }

    public static void main(String[] args) {
        // 호출자가 예외 처리
        try {
            readFile(); 
        } catch (IOException e) {
            System.out.println("없는파일: " + e.getMessage());
        }
    }
}
# 실행 결과
README.md 파일 열기 완료
없는파일: null.txt (지정된 파일을 찾을 수 없습니다)

 

throw new 직접 예외 사용

  • 자바에서 예외 객체를 직접 만들어서 강제로 발생시키는 구문
public class ExamException00 {
    public static void setAge(int age) {
        if (age < 0) {
            // 직접 익셉션 던짐
            throw new IllegalArgumentException("음수야: " + age);
        }
        System.out.println("나이: " + age);
    }

    public static void main(String[] args) {
        setAge(-5); // 잘못된 인자로 예외 발생
    }
}
# 실행 결과
Exception in thread "main" java.lang.IllegalArgumentException: 음수야: -5
    at week_04._0903.ExamException00.setAge(ExamException00.java:7)
    at week_04._0903.ExamException00.main(ExamException00.java:13)

 

throw와 throws의 차이

  • throw: 예외를 강제로 발생시킴 (throw new Exception())
  • throws: 메소드가 예외를 던질 수 있음을 선언 (method() throws Exception)

4. 사용자 지정 예외

  1. Checked Exception 방식
class MyException extends Exception { /* ... */ }
  • Exception을 상속
  • 여전히 반드시 throws 선언 필요, 호출자는 try-catch 강제됨
  1. Unchecked Exception 방식
class MyException extends RuntimeException { /* ... */ }
  • RuntimeException 상속 → Unchecked
  • throws 선언 강제 없음, try-catch도 선택 사항

 

Checked 지정 예외 예시

// 사용자 지정 예외 (Checked)
class MyException extends Exception {
    public MyException(String msg) {
        super(msg);
    }
    public MyException(Exception e) {
        super(e);
    }
}

public class ExamChecked {
    public static void checkNumber(int n) throws MyException {
        if (n < 1 || n > 100) {
            throw new MyException("범위를 벗어난 값: " + n);
        }
        System.out.println("정상 입력: " + n);
    }

    public static void main(String[] args) {
        try {
            checkNumber(-1); // 예외 발생
        } catch (MyException e) {
            System.out.println("[Checked Exception 예외 잡힘] " + e.getMessage());
        }
    }
}
[Checked Exception 예외 잡힘] 범위를 벗어난 값: -1

 

Unchecked 지정 예외 예시

// 사용자 지정 예외 (Unchecked)
class MyException extends RuntimeException {
    public MyException(String msg) {
        super(msg);
    }
    public MyException(Exception e) {
        super(e);
    }
}

public class ExamUnchecked {
    public static void checkNumber(int n) {
        if (n < 1 || n > 100) {
            throw new MyException("범위를 벗어난 값: " + n);
        }
        System.out.println("정상 입력: " + n);
    }

    public static void main(String[] args) {
        checkNumber(150); // try-catch 안 해도 컴파일 OK
        System.out.println("이 코드는 실행되지 않음");
    }
}
Exception in thread "main" week_04._0903.MyException: 범위를 벗어난 값: 150
    at week_04._0903.ExamUnchecked.checkNumber(ExamUnchecked.java:15)
    at week_04._0903.ExamUnchecked.main(ExamUnchecked.java:21)

5. 예외 주요 메서드 정리

메서드 설명 출력 예시
getMessage() 예외 생성 시 전달한 메시지 반환 For input string: "abc"
toString() 예외 클래스명 + 메시지 반환 ...: For input string: "abc"
printStackTrace() 예외 발생 경로(스택 트레이스)를 콘솔에 출력 at Exam.main(Exam.java:5)
getClass() 예외 객체의 클래스 타입 반환 class java.lang.NumberFormatException
getStackTrace() 스택 추적 정보를 배열(StackTraceElement[])로 반환 [StackTraceElement, ...]
getCause() 원인이 된 다른 예외를 반환 (체인 예외) null 또는 java.io.FileNotFoundException
initCause(Throwable cause) 원인 예외를 수동으로 연결 e.initCause(new IOException())
public class ExamExceptionInfo {
    public static void main(String[] args) {
        try {
            int num = Integer.parseInt("abc"); // NumberFormatException 발생
        } catch (NumberFormatException e) {
            System.out.println("getMessage(): " + e.getMessage());
            System.out.println("toString(): " + e.toString());
            System.out.println("getClass(): " + e.getClass());
            System.out.println("getStackTrace()[0]: " + e.getStackTrace()[0]);
            System.out.println("printStackTrace(): ");
            e.printStackTrace();
        }
    }
}
# 실행 결과
getMessage(): For input string: "abc"
toString(): java.lang.NumberFormatException: For input string: "abc"
getClass(): class java.lang.NumberFormatException
getStackTrace()[0]: ExamExceptionInfo.main(ExamExceptionInfo.java:5)
printStackTrace(): 
java.lang.NumberFormatException: For input string: "abc"
    at ExamExceptionInfo.main(ExamExceptionInfo.java:5)

6. Java IO (입출력) 개념

  1. 입출력
  • Input (입력) : 키보드, 파일, 네트워크 등 외부에서 데이터를 읽어오는 것
  • Output (출력) : 모니터, 파일, 네트워크 등 외부로 데이터를 내보내는 것
  • 자바에서는 java.io 패키지를 통해 입출력 스트림(Stream) 개념으로 처리
  1. 스트림
  • 데이터가 흐르는 통로 (단방향, 파이프처럼 생각하면 됨)
  • 입력 스트림(InputStream/Reader) → 데이터를 프로그램 안으로
  • 출력 스트림(OutputStream/Writer) → 데이터를 프로그램 밖으로
[데이터 소스] ──→ [입력 스트림] ──→ [프로그램] ──→ [출력 스트림] ──→ [데이터 대상]
  1. IO 분류
  • 데이터 방향 : input, output
  • 데이터 단위
    • 바이트 단위 : InputStream, OutputStream (이미지, 영상, 바이너리 처리)
    • 문자 단위 : Reader, Writer (텍스트 처리)
  • 처리 방식
    • 기반 스트림 : 실제 데이터와 연결되는 통로 (예: FileInputStream, FileReader)
      • 주인공임
    • 보조 스트림 : 기반 스트림을 감싸 기능 추가 (예: BufferedReader, BufferedWriter)
      • 커넥터 역할 (보조, 장식)
구분 바이트 스트림 문자 스트림
입력 FileInputStream FileReader
출력 FileOutputStream FileWriter
보조 BufferedInputStream BufferedReader

 

바이트 단위 입출력 (InputStream/OutputStream)

모든 데이터(이미지, 영상 등) 가능

  • 이진 데이터 처리에 적합 (이미지, 오디오, 비디오 등)
  • 1바이트씩 처리
  • 모든 데이터를 바이트로 표현 가능
  1. InputStream 계열
  • FileInputStream : 파일에서 바이트 단위로 읽기
  • ByteArrayInputStream : 바이트 배열에서 읽기
  • BufferedInputStream : 버퍼를 이용한 효율적인 읽기
  • DataInputStream : 기본 데이터 타입 읽기
  1. OutputStream 계열
  • FileOutputStream : 파일에 바이트 단위로 쓰기
  • ByteArrayOutputStream : 바이트 배열에 쓰기
  • BufferedOutputStream : 버퍼를 이용한 효율적인 쓰기
  • DataOutputStream : 기본 데이터 타입 쓰기
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteExam {
    public static void main(String[] args) {
        String srcFile = "src/etc/IOTEST.md";
        String srcCopy = "src/etc/IOTEST_COPY.md";

        // try-with-resources를 사용한 자동 리소스 관리
        try (FileInputStream in = new FileInputStream(srcFile);
             FileOutputStream out = new FileOutputStream(srcCopy)) {

            int byteData;
            // 파일 끝(-1)까지 한 바이트씩 읽기
            while ((byteData = in.read()) != -1) {
                out.write(byteData);
            }
            System.out.println("파일 복사 완료!");

        } catch (IOException e) {
            System.err.println("파일 처리 중 오류: " + e.getMessage());
        }


        // 복사 완료 후 확인
        System.out.println("[파일 읽기 - 바이트]");
        System.out.println("─".repeat(10));
        try (FileInputStream fis = new FileInputStream(srcCopy)) {
            int byteData;
            // 파일에서 한 글자씩 읽어서 int 값으로 반환
            // 더 이상 읽을 문자가 없으면 -1 반환됨
            while ((byteData = fis.read()) != -1) {
                System.out.print((char) byteData);
            }
        } catch (IOException e) {
            System.out.println("오류: " + e.getMessage());
        }
        System.out.println();
        System.out.println("─".repeat(10));

    }
}
파일 복사 완료!
[파일 읽기 - 바이트]
──────────
# IO TEST
──────────

 

문자 단위 입출력 (Reader/Writer)

텍스트 전용 (인코딩 지원, 다국어 처리 강점)

  • 텍스트 파일 처리에 적합
  • 문자 인코딩 자동 처리
  • 유니코드 지원으로 다국어 처리 가능
  1. Reader 계열
  • FileReader : 파일에서 문자 단위로 읽기
  • StringReader : 문자열에서 읽기
  • BufferedReader : 버퍼를 이용한 효율적인 읽기 (readLine() 제공)
  • InputStreamReader : 바이트 스트림을 문자 스트림으로 변환
  1. Writer 계열
  • FileWriter : 파일에 문자 단위로 쓰기
  • StringWriter : 문자열로 쓰기
  • BufferedWriter : 버퍼를 이용한 효율적인 쓰기
  • OutputStreamWriter : 문자 스트림을 바이트 스트림으로 변환
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileExam {
    public static void main(String[] args) {
        String srcFile = "src/etc/IOTEST.md";

        System.out.println("[파일 읽기 - 원본]");
        System.out.println("─".repeat(10));

        // 파일 읽기
        try (FileReader fr = new FileReader(srcFile)) {
            int ch;
            // 파일에서 한 글자씩 읽어서 int 값으로 반환
            // 더 이상 읽을 문자가 없으면 -1 반환됨
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
        } catch (IOException e) {
            System.out.println("오류: " + e.getMessage());
        }
        System.out.println();
        System.out.println("─".repeat(10));

        // 파일 쓰기 (apeend)
        try (FileWriter fw = new FileWriter(srcFile, true)) {
            fw.write("\n## java로 추가. 반갑습니다");
        } catch (IOException e) {
            System.out.println("오류: " + e.getMessage());
        }

        // 다시 읽기
        System.out.println("\n[파일 쓰고 읽기 - 수정 후]");
        System.out.println("─".repeat(10));
        try (FileReader fr = new FileReader(srcFile)) {
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
        } catch (IOException e) {
            System.out.println("오류: " + e.getMessage());
        }
        System.out.println();
        System.out.println("─".repeat(10));
    }
}
[파일 읽기 - 원본]
──────────
# IO TEST
──────────

[파일 쓰고 읽기 - 수정 후]
──────────
# IO TEST
## java로 추가. 반갑습니다
──────────

 

starweb
@starweb :: starweb 님의 블로그

starweb 님의 블로그 입니다.

공감하셨다면 구독도 환영합니다!

목차