[Java] 예외 처리 & 입출력(IO)

배창민·2025년 9월 16일
post-thumbnail

Java 예외 처리 & 입출력(IO)


1) 예외 처리 개요

예외(Exception) vs 오류(Error)

  • 오류(Error): 시스템 수준의 치명적 문제 → 코드로 복구 불가 (예: JVM 오류, 하드웨어 문제)
  • 예외(Exception): 예상 가능한 비정상 상황 → 코드로 처리 가능

왜 처리하나?

  • 사용자 경험 보호(비정상 종료 방지), 안정성↑ / 신뢰성↑
  • 원인·위치 파악 쉬워 디버깅 효율↑

2) 예외 클래스 구조 & 주요 타입

기본 구조

  • Throwable

    • Error (복구X)

    • Exception

      • Checked: 반드시 처리 필요(미처리시 컴파일 에러)
      • Unchecked = RuntimeException 계열: 런타임에 발생, 선택적 처리

흔한 RuntimeException

// ArithmeticException: 0으로 나눔
int x = 3 / 0;

// ArrayIndexOutOfBoundsException: 인덱스 초과
int[] arr = new int[0];
System.out.println(arr[1]);

// NullPointerException: null 접근
int[] a = null;
System.out.println(a[0]);

// ClassCastException: 잘못된 캐스팅
Object o = "hello";
Integer i = (Integer) o;

// NegativeArraySizeException: 음수 길이 배열
int[] bad = new int[-1];

3) 예외 처리 방법

A. throws로 상위에 위임

void readFile() throws IOException { /* ... */ }

B. try-catch(-finally)로 직접 처리

try {
    // 예외 가능 코드
} catch (FileNotFoundException e) { // 구체적인 것 먼저
    // ...
} catch (IOException e) {            // 상위 타입은 나중에
    // ...
} finally {
    // 예외와 무관하게 반드시 실행(자원 반납 등)
}

C. 오버라이딩 시 예외 규칙

  • 자식 메소드는 부모보다 더 상위 타입 예외를 던질 수 없다
  • 같거나 더 구체적인(하위) 예외만 가능
class Super { void m() throws IOException {} }
class Sub extends Super {
    @Override void m() throws FileNotFoundException {} // OK (하위)
    // @Override void m() throws Exception {}          // X (상위)
}

D. try-with-resources (자바 7+)

  • AutoCloseable 자원은 자동 close() + 내부적으로 flush()
try (BufferedReader in = new BufferedReader(new FileReader("test.dat"))) {
    String s;
    while((s = in.readLine()) != null) System.out.println(s);
} catch (IOException e) { e.printStackTrace(); }

4) 입출력(IO) 개요

스트림(Stream)

  • 단방향 통로(입력/출력 분리)
  • 바이트 단위 기본, 문자 단위 전용 스트림 제공(다국어 안전)
  • 한글 등은 2~3바이트 → 문자 스트림(Reader/Writer) 사용 권장

왜 IO를 쓰나?

  • 사용자 입력/출력, 파일 영속 저장, 다른 장치와 데이터 교환

5) 파일(File) 클래스 핵심

File f = new File("C:/data/file.txt");
f.createNewFile();  // 파일 생성
f.delete();         // 삭제
f.isFile();         // 파일 여부
f.isDirectory();    // 디렉토리 여부
f.length();         // 크기(byte)
f.getName(); f.getPath(); f.getAbsolutePath(); f.getParent();
분류메소드설명
생성/삭제createNewFile() / mkdir() / mkdirs() / delete()파일·디렉토리 생성/삭제
속성canRead()/canWrite()/canExecute()접근 가능 여부
정보isFile()/isDirectory()/isHidden()타입/숨김
메타length()/lastModified()크기/수정시각

6) 기반 스트림(바이트/문자)

A. 바이트: InputStream / OutputStream

// FileInputStream: 바이트 읽기
try (FileInputStream fin = new FileInputStream("in.bin")) {
    int b;
    while((b = fin.read()) != -1) System.out.println(b);
    // 파일 전체를 배열로
    byte[] buf = fin.readAllBytes(); // (자바 9+) 또는 length()로 크기만큼 배열 생성 후 read()
}

// FileOutputStream: 바이트 쓰기
try (FileOutputStream fout = new FileOutputStream("out.bin")) {
    fout.write(97);                      // 'a'
    fout.write(new byte[]{98,99,100});   // b c d
    // 이어쓰기: new FileOutputStream("out.bin", true)
}

다국어 텍스트는 바이트 스트림으로 읽으면 깨짐. → 문자 스트림 사용!

B. 문자: Reader / Writer

// FileReader: 문자 읽기
try (FileReader fr = new FileReader("in.txt")) {
    char[] cbuf = new char[1024];
    int n;
    while((n = fr.read(cbuf)) != -1) System.out.print(new String(cbuf, 0, n));
}

// FileWriter: 문자 쓰기
try (FileWriter fw = new FileWriter("out.txt")) {
    fw.write('A');
    fw.write(new char[]{'a','p','p','l','e'});
    fw.write("우리나라 대한민국");
}

7) 보조(필터) 스트림

기반 스트림에 덧씌워 기능 향상. 단독 사용 불가.

7-1. 버퍼링(성능↑): BufferedReader / BufferedWriter

// 쓰기
try (BufferedWriter bw = new BufferedWriter(new FileWriter("buf.txt"))) {
    bw.write("안녕하세요"); bw.newLine();
    bw.write("반갑습니다");
    bw.flush(); // 남은 데이터 강제 전송(종료 전 close가 flush 수행)
}

// 읽기: 라인 단위
try (BufferedReader br = new BufferedReader(new FileReader("buf.txt"))) {
    String line;
    while((line = br.readLine()) != null) System.out.println(line);
}

7-2. 바이트↔문자 브리지: InputStreamReader / OutputStreamWriter

// 콘솔 입력(문자)
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
    System.out.print("문자열 입력: ");
    String s = br.readLine();
    System.out.println("value: " + s);
}

// 콘솔 출력(문자)
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out))) {
    bw.write("java oracle jdbc"); bw.flush();
}

7-3. 기본형 I/O: DataInputStream / DataOutputStream

// 기록 (형 보존)
try (DataOutputStream out = new DataOutputStream(new FileOutputStream("score.bin"))) {
    out.writeUTF("홍길동"); out.writeInt(95); out.writeChar('A');
}

// 읽기 (기록한 **순서대로** 읽어야 함)
try (DataInputStream in = new DataInputStream(new FileInputStream("score.bin"))) {
    while (true) System.out.println(in.readUTF()+", "+in.readInt()+", "+in.readChar());
} catch (EOFException e) {
    System.out.println("파일 읽기 완료!");
}

7-4. 객체 I/O(직렬화): ObjectInputStream / ObjectOutputStream

// DTO는 반드시 implements Serializable
try (ObjectOutputStream oos =
         new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("mem.obj")))) {
    oos.writeObject(new MemberDTO(/*...*/)); oos.flush();
}

try (ObjectInputStream ois =
         new ObjectInputStream(new BufferedInputStream(new FileInputStream("mem.obj")))) {
    while(true) System.out.println((MemberDTO) ois.readObject());
} catch (EOFException e) { System.out.println("끝"); }

직렬화 미지정 시 NotSerializableException 발생


8) 핵심 정리

  • 캐치 순서: 구체 → 일반(상위)
  • 자원관리: try-with-resources 권장(자동 close/flush)
  • 문자열 파일: Reader/Writer(다국어 안전), 성능은 Buffered 조합
  • 바이트 파일(이미지/영상): InputStream/OutputStream
  • 데이터 형 유지: Data 스트림, 객체는 Object 스트림(+직렬화)
  • 예외 규약: equals/hashCode처럼, throws 오버라이딩은 부모보다 좁게

최소한의 안전한 읽기/쓰기 템플릿

// 읽기(문자)
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    for (String line; (line = br.readLine()) != null; ) { /* use line */ }
}

// 쓰기(문자)
try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) {
    bw.write(content); // bw.newLine();
}

// 읽기(바이트)
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(path))) {
    byte[] buf = in.readAllBytes(); // (자바 9+)
}

// 쓰기(바이트)
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(path))) {
    out.write(bytes); out.flush();
}
profile
개발자 희망자

0개의 댓글