입출력 I/O
2025. 2. 26. 15:34ㆍJava/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("자바!"); // 줄바꿈 없이 출력
}
}