입출력 I/O

2025. 2. 26. 15:34Java/Java 문법

1. 자바 입출력 I/O

1-1. 입출력 I/O

  • Input and Output
    • 입력(Input): 키보드, 파일, 네트워크 등에서 데이터를 읽어오는 것
    • 출력(Output): 화면, 파일, 네트워크 등으로 데이터를 보내는 것
  • 자바는 java.io 패키지를 이용해 입출력을 처리한다. 
    컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 연동을 위한 자바 라이브러리
  • 입출력 사용 이유
    • 입출력을 사용함으로써 사용자로 부터 입력을 받거나 화면이나 스피커로 출력해 줄 수 있다.
    • 또한 파일 형태로 프로그램의 종료 여부와 상관없이 영구적으로 데이터를 저장할 수도 있다.
  • 자바의 입출력은 스트림(Stream) 개념을 사용한다. 

1-2. 스트림 (Stream)

  • 입출력 장치에서 데이터를 읽고 쓰기 위한 단방향 통로로 자바에서 제공하는 클래스
  • 한 방향만 처리가 가능하여 입력 스트림과 출력 스트림을 따로 구성해야 한다.
    • 입력 스트림(InputStream, Reader): 데이터를 읽어오는 통로
    • 출력 스트림(OutputStream, Writer): 데이터를 내보내는 통로
  • 스트림의 구분
    • 기반 스트림: 데이터를 직접 읽고 쓰는 스트림
    • 보조 스트림: 기반 스트림을 보조해서 성능을 높이거나 추가 기능 제공. 보조 스트림은 단독으로 동작할 수 없고 반드시 기능에 맞는 기반 스트림과 함께 사용해야 한다. 
구분 스트림 종류 설명 클래스
기반 스트림
(Base Stream)
바이트 스트림 1바이트 단위 입출력 InputStream, OutputStream
문자 스트림 2바이트(문자) 단위 입출력 Reader, Writer
보조 스트림
(Filter Stream)

버퍼 스트림 입출력 속도 향상 BufferedInputStream (바이트기반)
BufferedReader (문자기반) 
데이터 스트림 기본 타입(정수, 실수 등) 입출력 DataInputStream, DataOutputStream (바이트기반)
객체 스트림  객체 직렬화  ObjectInputStream, ObjectOutputStream (바이트기반)
변환 스트림  바이트를 문자로 변환  InputStreamReader, OutputStreamWriter

 

  • 기반 스트림의 하위 클래스

 

2. 바이트 스트림

2-1. 바이트 스트림 :  InputStream, OutputStream

  • 바이트 단위로 데이터를 처리하는 스트림
  • 이미지, 동영상, 파일 같은 바이너리 데이터를 다룰 때 사용
  • 입력 클래스: InputStream
    • FileInputStream: 파일에서 바이트 단위로 데이터를 읽음
    • ByteArrayInputStream: 메모리 배열에서 바이트 단위로 데이터를 읽음
  • 출력 클래스: OutputStream
    • FileOutputStream: 파일에 바이트 단위로 데이터를 씀
    • ByteArrayOutputStream: 메모리 배열에 바이트 단위로 데이터를 씀
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt")) {
            
            int data;
            while ((data = fis.read()) != -1) { // 한 바이트씩 읽기
                fos.write(data); // 한 바이트씩 쓰기
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

2-2. 바이트 스트림과 함께 쓰이는 보조 스트림 

1) 바이트 기반 버퍼 스트림: BufferedInputStream / BufferedOutputStream 

  • 버퍼(Buffer)는 데이터를 한 번에 모아서 처리하는 메모리 공간
  • 버퍼를 사용하면 입출력 성능이 향상됨.
  • 원래 한 바이씩 읽고 써서 비효율적 → 버퍼에 데이터를 모아서 한 번에 처리. 속도 향상
import java.io.*;

public class BufferedByteStreamExample {
    public static void main(String[] args) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
            
            int data;
            while ((data = bis.read()) != -1) { // 버퍼를 사용하여 여러 바이트 읽기
                bos.write(data); // 버퍼를 사용하여 여러 바이트 쓰기
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

2) 데이터 스트림 : DataOutputStream / DataInputStream  

  • 기본 데이터 타입을 파일에 저장하고 읽을 때 사용 (예: int, double, char, boolean 등)
  • DataOutputStream: 데이터를 쓸 때 사용, 기본 타입을 바이트로 변환해서 저장
  • DataInputStream: 데이터를 읽을 때 사용, 바이트를 다시 기본 타입으로 변환해서 읽어옴
import java.io.*;

public class DataStreamExample {
    public static void main(String[] args) {
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
             DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"))) {

            // 데이터를 파일에 쓰기
            dos.writeInt(123);    // 정수 쓰기
            dos.writeDouble(3.14); // 실수 쓰기
            dos.writeBoolean(true); // 불리언 쓰기

            // 데이터를 파일에서 읽기
            System.out.println("정수: " + dis.readInt()); 	//정수: 123
            System.out.println("실수: " + dis.readDouble()); 	//실수: 3.14
            System.out.println("불리언: " + dis.readBoolean());	//불리언: true

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

3) 객체 스트림 (Object I/O) : ObjectOutputStream / ObjectInputStream

  • 객체 직렬화를 위한 스트림
  • 객체를 파일이나 네트워크로 전송하려면 객체를 직렬화해서 바이트 형태로 저장해야 한다. 
  • 직렬화는 객체를 바이트 스트림으로 변환하는 과정이고, 역직렬화는 바이트 스트림을 다시 객체로 변환하는 과정
  • ObjectOutputStream: 객체를 직렬화해서 쓸 때 사용 (직렬화)
  • ObjectInputStream: 직렬화된 객체를 읽을 때 사용 (역직렬화) 
import java.io.*;

class Person implements Serializable {  // Serializable 인터페이스를 구현해야 직렬화 가능
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class ObjectStreamExample {
    public static void main(String[] args) {
        Person person = new Person("John", 30);

        // 객체 직렬화
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 객체 역직렬화
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println(deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


3
. 문자 스트림 

3-1. 문자 스트림: Reader, Writer

  • 문자 데이터를 처리하는 스트림
  • 텍스트 파일을 다룰 때 사용
  • 입력 클래스: Reader
    • FileReader: 파일에서 문자 단위로 읽음
  • 출력 클래스: Writer
    • FileWriter: 파일에 문자 단위로 저장
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamExample {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("input.txt");
             FileWriter fw = new FileWriter("output.txt")) {
            
            int data;
            while ((data = fr.read()) != -1) { // 한 문자씩 읽기
                fw.write(data); // 한 문자씩 쓰기
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3-2. 문자 스트림과 함께 쓰이는 보조 스트림 

1) 문자 기반 버퍼 스트림 : BufferedReader / BufferedWriter

  • 버퍼를 사용하면 입출력 성능이 향상됨.
  • 원래 한 글자씩 읽고 써서 비효율적 → 버퍼 사용하면 한 줄씩 처리. 성능 향상, 편리함
import java.io.*;

public class BufferedCharStreamExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
            
            String line;
            while ((line = br.readLine()) != null) { // 한 줄씩 읽기
                bw.write(line);
                bw.newLine(); // 줄바꿈 추가
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

2) 바이트 -> 문자 변환 스트림 : InputStreamReader, OutputStreamWriter

  • 바이트 기반의 InputStream과 OutputStream을 포장해 문자 기반의 Reader 와 Writer로 변환하는 클래스
    • InputStream -[ InputStreamReader ]-> Reader
    • OutputStream -[ OutputStreamWriter ]-> Writer
  • System.in, System.out과 같은 바이트 스트림을 문자 기반 스트림으로 변환한 후,
    BufferedReader, BufferedWriter을 사용하면 입출력 성능이 향상된다. 
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
try {
	System.out.print("문자열 입력 : ");
	String value = br.readLine();
	
	System.out.println("value : " + value);
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if(br != null) {
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

/* 출력을 위한 것도 마찬가지 방식으로 사용할 수 있다. */
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

try {
	bw.write("java oracle jdbc");

} catch (IOException e) {
	e.printStackTrace();
} finally {
	if(br != null) {
		try {
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

4. 표준 입출력 (Standard I/O)

4-1. System.in (입력: 키보드)

import java.util.Scanner;

public class ConsoleInputExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("이름을 입력하세요: ");
        String name = scanner.nextLine();
        System.out.println("입력된 이름: " + name);
    }
}

 

4-2. System.out (출력: 화면)

public class ConsoleOutputExample {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");  // 줄바꿈 포함 출력
        System.out.print("안녕, ");  
        System.out.print("자바!");  // 줄바꿈 없이 출력
    }
}