JAVA I/O 입출력 시스템
Java에서의 입출력(I/O)은 데이터의 입력과 출력을 다루는 프로그래밍 요소입니다. 이는 파일 시스템, 네트워크 통신, 키보드 또는 마우스와 같은 다양한 외부 소스와의 입력과 출력을 포함하며, 다양한 매체와의 상호작용을 통해 데이터를 송수신할 수 있도록 합니다. Java에서 I/O는 주로 java.io 패키지를 통해 제공되며, 이를 사용하여 파일 읽기, 쓰기, 네트워크 통신 등을 수행할 수 있습니다.
이 글에서는 Java에서 입출력을 수행하기 위한 다양한 클래스 및 인터페이스, 그리고 각 스트림을 효율적으로 사용하는 방법을 설명합니다. 바이트 스트림과 문자 스트림, 파일 처리, 버퍼링 및 직렬화 등 다양한 주제를 다루며, 이를 코드 예제와 함께 이해하기 쉽게 정리하였습니다.
1. Java I/O의 개요
Java에서 I/O(입출력)는 프로그램 외부의 데이터 소스와 데이터를 송수신하는 기능을 의미합니다. 여기서 입력(Input)은 프로그램이 외부로부터 데이터를 읽어 들이는 행위를, 출력(Output)은 프로그램이 외부로 데이터를 내보내는 행위를 뜻합니다.
입출력은 파일, 네트워크 소켓, 사용자 인터페이스 장치(키보드, 마우스) 등 다양한 매체를 대상으로 할 수 있습니다. 이러한 매체와 상호작용하는 Java의 I/O 기능은 java.io 패키지를 통해 제공되며, 스트림(Stream)을 사용하여 데이터를 연속적으로 송수신할 수 있습니다.
Java에서는 I/O 스트림을 통해 파일, 콘솔, 네트워크와 같은 다양한 매체를 다룰 수 있으며, 이러한 I/O 작업은 데이터 유형(바이트/문자)에 따라 구분됩니다.
2. 입출력 스트림 (I/O Streams)
2.1 바이트 기반 스트림
바이트 스트림은 데이터를 바이트(byte) 단위로 처리합니다. 주로 바이너리 데이터(예: 이미지 파일, 실행 파일)를 읽고 쓸 때 사용되며, InputStream 및 OutputStream 클래스를 통해 바이트 스트림을 다룰 수 있습니다.
주요 클래스
- InputStream: 데이터를 바이트 단위로 입력받기 위한 최상위 추상 클래스입니다.
- OutputStream: 데이터를 바이트 단위로 출력하기 위한 최상위 추상 클래스입니다.
// 파일에서 바이트 데이터를 읽고 쓰기
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream("d:/example/read.txt");
FileOutputStream outputStream = new FileOutputStream("d:/example/write.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
위 코드에서는 FileInputStream과 FileOutputStream을 사용하여 파일을 바이트 단위로 읽고 씁니다. try-with-resources 구문을 사용하여 자원을 자동으로 해제하도록 하였으며, 예외 처리를 통해 파일 읽기/쓰기 중 발생할 수 있는 오류를 처리하였습니다.
2.2 문자 기반 스트림
문자 스트림은 데이터를 문자 단위로 처리합니다. 주로 텍스트 파일(예: .txt, .csv)을 읽고 쓸 때 사용되며, Reader 및 Writer 클래스를 통해 문자 스트림을 다룰 수 있습니다.
주요 클래스
- Reader: 문자를 읽기 위한 최상위 추상 클래스입니다.
- Writer: 문자를 쓰기 위한 최상위 추상 클래스입니다.
// 파일에서 문자 데이터를 읽고 쓰기
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamExample {
public static void main(String[] args) {
try (FileReader reader = new FileReader("d:/example/read.txt");
FileWriter writer = new FileWriter("d:/example/write.txt")) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
위 코드에서는 FileReader와 FileWriter를 사용하여 파일을 문자 단위로 읽고 씁니다. 문자 스트림을 사용할 때는 바이트 스트림보다 코드가 간결해지며, 텍스트 데이터를 더 쉽게 처리할 수 있습니다.
3. 파일 처리
Java는 파일 처리와 관련된 다양한 클래스를 제공합니다. File 클래스는 파일 및 디렉터리의 정보를 얻고, 파일의 생성, 삭제, 경로 확인 등의 작업을 수행할 수 있습니다.
import java.io.File;
public class FileExample {
public static void main(String[] args) {
File directory = new File("d:/example");
File file = new File("d:/example/read.txt");
// 디렉토리가 존재하지 않으면 생성
if (!directory.exists()) {
directory.mkdirs();
System.out.println("디렉토리가 생성되었습니다.");
}
// 파일이 존재하는지 확인
if (file.exists()) {
System.out.println("파일이 있습니다.");
} else {
System.out.println("파일이 없습니다.");
}
}
}
File 클래스는 파일 및 디렉터리의 메타 정보를 처리할 수 있으며, 파일 존재 여부, 크기, 경로, 디렉터리 생성 등의 기능을 제공합니다.
4. 버퍼를 활용한 입출력
입출력 성능을 향상시키기 위해 버퍼를 사용하는 것이 좋습니다. BufferedReader, BufferedWriter, BufferedInputStream, 및 BufferedOutputStream 클래스는 버퍼링을 제공하여 입출력 속도를 크게 향상시킬 수 있습니다.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedCharStreamExample {
public static void main(String[] args) {
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/example/read.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/example/write.txt"))) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = bufferedReader.read(buffer)) != -1) {
bufferedWriter.write(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedReader와 BufferedWriter는 파일 읽기/쓰기 성능을 크게 향상시키며, 대량의 데이터를 처리할 때 유리합니다.
5. 직렬화 (Serialization)
직렬화는 객체의 상태를 바이트 스트림으로 변환하는 프로세스입니다. 이는 객체를 파일에 저장하거나 네트워크를 통해 전송할 때 사용됩니다. 직렬화된 객체는 역직렬화를 통해 다시 객체로 변환할 수 있습니다.
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.IOException;
class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
public class SerializationExample {
public static void main(String[] args) {
Student student = new Student("John Doe", 20);
try (FileOutputStream fileOut = new FileOutputStream("student.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(student);
System.out.println("직렬화가 완료되었습니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
위 코드는 Student 객체를 student.ser 파일로 직렬화합니다. 직렬화는 객체를 파일이나 네트워크로 전송할 때 유용하며, Java에서는 Serializable 인터페이스를 구현하여 직렬화가 가능하도록 해야 합니다.
6. Java I/O의 주요 고려사항
- 자원 해제: I/O 작업 후에는 반드시 자원을 해제해야 합니다. 이를 위해 try-with-resources 구문을 사용하여 자원을 자동으로 해제할 수 있습니다.
- 예외 처리: 파일 경로가 잘못되었거나, 읽기/쓰기 권한이 없는 경우 IOException 또는 FileNotFoundException이 발생할 수 있습니다. 항상 적절한 예외 처리를 수행해야 합니다.
- 입출력 성능: 대량의 데이터를 처리할 때는 버퍼링을 사용하여 성능을 최적화할 수 있습니다.
- 직렬화 고려사항: 직렬화 시 serialVersionUID를 명시적으로 선언하는 것이 좋으며, 객체가 직렬화 가능한지 확인해야 합니다.
7. 결론
Java의 I/O 시스템은 다양한 매체와의 입출력 작업을 지원하며, 프로그램에서 데이터를 효율적으로 처리할 수 있도록 합니다. 바이트 스트림과 문자 스트림의 차이를 이해하고, 각각의 상황에 맞는 클래스를 선택하여 사용하는 것이 중요합니다. 또한, 버퍼링 및 직렬화를 통해 성능을 향상시킬 수 있으며, 이러한 기능을 잘 활용하여 최적의 I/O 성능을 얻는 것이 중요합니다.
Java I/O는 다양한 데이터 처리 작업에서 필수적인 요소이며, 올바른 사용법과 패턴을 익혀 효율적이고 안정적인 프로그램을 개발할 수 있습니다.
'JAVA > JAVA 기초' 카테고리의 다른 글
Java String 클래스 (0) | 2024.10.15 |
---|---|
Java 이너 클래스 (Inner Class) (0) | 2024.10.14 |
Java 람다 표현식(Lambda Expression)과 스트림(Stream) (0) | 2024.10.12 |
Java Thread 활용 (0) | 2024.10.11 |
Java 예외(Exception) 처리 (0) | 2024.10.10 |