자바IO
자바 IO
- 입출력을 위한 인터페이스와 클래스들
- IO에서 I는 Input, O는 Output
- 프로그램 상으로 들어오는 모든 데이터를 Input Data, 프로그램에서 파일, 네트워크, 콘솔 등으로 나가는 모든 데이터를 Output Data라고 함
- 자바 IO는 데코레이터 패턴(Decorator Pattern)으로 만들어짐
- 데코레이터 패턴: 장식하는 것처럼, 하나의 클래스를 생성자에서 감싸서 새로운 기능을 계속 추가할 수 있도록 클래스를 만드는 방식
- Java에서는 파일이나 콘솔의 입출력을 직접 다루지 않고, 모든 IO가 스트림(Stream)을 통해 이루어짐
스트림(Stream)
- 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름
- 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 함
- 한 방향으로만 통신할 수 있으므로, 입력과 출력을 동시에 처리할 수는 없음
- 선입선출 구조(First In First Out): 먼저 들어온 게 먼저 나감
- 시간에 따라 순차적으로 받음 (타임 슬라이싱)
- 사용 목적에 따라 입력 스트림과 출력 스트림으로 구분됨
- Java에서는 java.io 패키지를 통해 InputStream과 OutputStream 클래스를 별도로 제공
- InputStream: 입력장치로부터 Java 프로그램으로 데이터를 전달
- OutputStream: 출력장치로부터 데이터를 출력
- Java에서는 java.io 패키지를 통해 InputStream과 OutputStream 클래스를 별도로 제공
- Java에서의 스트림 생성이란 스트림 클래스 타입의 인스턴스를 생성한다는 의미
장식대상 클래스와 장식하는 클래스
- 자바 IO가 데코레이터 패턴(Decorator Pattern)으로 만들어졌기 때문에 장식하는 클래스, 장식대상 클래스라는 표현이 쓰임
- 장식하는 클래스와 장식대상 클래스는 4가지 추상클래스(InputStream, OutputStream, Reader, Writer)를 매개변수로 받아들이는 생성자의 존재 여부로 나뉨
장식하는 클래스
- 개념
- 4가지 추상 클래스를 매개변수로 받아들이는 생성자가 있는 경우
- 어디에 쓸지는 결정할 수 없기 때문에 이를 결정하는 다른 장식대상 클래스를 함께 써야 함
- 다양한 입출력 방법을 제공하는 자바 IO 클래스
- 입출력 데이터를 처리할 때 장식대상 클래스보다 더 편한 방식을 제공
- 종류
- 다양한 데이터타입을 입력받고 출력
- DataOutputStream: 다양한 타입으로 데이터를 저장할 수 있는 클래스
- DataInputStream: 다양한 타입의 데이터를 읽어낼 수 있는 클래스
- BufferedReader: 한 줄씩 입력받기 위한 클래스
- 한 줄 입력받는 readLine( )메소드를 가짐
- PrintWriter: 편리하게 출력할 수 있게 하는 클래스
- 다양하게 한 줄 출력하는 println( )메소드를 가짐
- 다양한 데이터타입을 입력받고 출력
장식대상 클래스
- 개념
- 4가지 추상 클래스를 매개변수로 받아들이는 생성자가 없는 경우
- 어디로부터, 어디에라는 대상을 지정할 수 있는 IO클래스
- 대상이 되는 부분으로부터 무조건 값을 입력받거나 출력하는 클래스
- 1byte나 1char 단위로 입출력 > 입출력 메소드가 단순하게 제공된다는 뜻
- 종류
- 파일로부터 입력받고 쓰기 위한 클래스
- FileInputStream: 파일로부터 읽어오기 위한 클래스
- FileOutputStream: 파일에 쓸 수 있게 해주는 클래스
- FileReader: 파일에서 읽어오는 클래스
- FileWriter: 파일에 쓰는 클래스
- 배열로부터 입력받고 쓰기 위한 클래스
- ByteArrayInputStream: 바이트 배열로부터 읽어오기 위한 클래스
- ByteArrayOutputStream: 바이트 배열에 데이터를 쓸 수 있게 해주는 클래스
- CharReader: CharArrayReader를 상속받음
- CharWriter: CharArrayWriter를 상속받음
- 파일로부터 입력받고 쓰기 위한 클래스
장식하는 클래스와 장식대상 클래스의 상호작용
- 파일에 데이터 쓰기: DataOutputStream과 FileOutputStream:
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
- 파일로부터 데이터를 읽어오기: DataInputStream과 FileInputStream
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
- 키보드로부터 한 줄 입력받아서 콘솔에 출력하기: BufferedReader와 InputStreamReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- 파일에서 한 줄씩 입력받아서 파일에 출력하기: BufferedReader와 FileReader, PrintWriter와 FileWriter
BufferedReader br = new BufferedReader(new FileReader("src/javaIO/exam/CharIOExam02.java"));
PrintWriter pw = new PrintWriter(new FileWriter("test.txt"));
입출력 스트림
- InputStream 클래스에는 read() 메소드가, OutputStream 클래스에는 write() 메소드가 각각 추상 메소드로 포함되어 있음
- 사용자는 이 두 메소드를 상황에 맞게 적절히 구현해야만 입출력 스트림을 생성하여 사용할 수 있음
클래스 | 메소드 | 설명 |
InputStream | abstract int read() | 해당 입력 스트림으로부터 다음 바이트를 읽어들임 |
int read(byte[] b) | 해당 입력 스트림으로부터 특정 바이트를 읽어들인 후, 배열 b에 저장 | |
int read(byte[] b, int off, int len) | 해당 입력 스트림으로부터 len 바이트를 읽어들인 후, 배열 b[off]부터 저장 | |
OutputStream | abstract void write(int b) | 해당 출력 스트림에 특정 바이트를 저장 |
void write(byte[] b) | 배열 b의 특정 바이트를 배열 b의 길이만큼 해당 출력 스트림에 저장 | |
void write(byte[] b, int off, int len) | 배열 b[off]부터 len 바이트를 해당 출력 스트림에 저장 |
- read( ) 메소드의 리턴 타입만 int형인 이유
- read( ) 메소드는 해당 입력 스트림에서 더 이상 읽어들일 바이트가 없으면, -1을 반환해야 함
- But, 반환 타입을 byte 타입으로 하면 0부터 255까지의 바이트 정보는 표현할 수 있지만 -1은 표현할 수 없음
- 따라서 InputStream의 read( ) 메소드는 리턴타입을 int형으로 선언
byte 기반 스트림과 문자 기반 스트림
- 자바 IO는 크게 byte 단위의 입출력 클래스과 문자(char) 단위의 입출력 클래스로 나뉨
- byte 단위 입출력클래스: InputStream과 OutputStream이라는 추상클래스를 상속받아 만들어짐
- 문자(char) 단위 입출력클래스: Reader와 Writer라는 추상클래스를 상속받아 만들어짐
byte 기반 스트림
- Java에서 스트림은 기본적으로 바이트 단위로 데이터를 전송
입력 스트림 | 출력 스트림 | 입출력 대상 |
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리 |
PipedInputStream | PipedOutputStream | 프로세스 |
AudioInputStream | AudioOutputStream | 오디오 장치 |
byte 기반 보조 스트림
- Java에서 제공하는 보조 스트림은 실제로 데이터를 주고받을 수는 없지만, 다른 스트림의 기능을 향상시키거나 새로운 기능을 추가해 주는 스트림
입력 스트림 | 출력 스트림 | 설명 |
FilterInputStream | FilterOutputStream | 필터를 이용한 입출력 |
BufferedInputStream | BufferedOutputStream | 버퍼를 이용한 입출력 |
DataInputStream | DataOutputStream | 입출력 스트림으로부터 자바의 기본 타입으로 데이터를 읽어올 수 있게 함 |
ObjectInputStream | ObjectOutputStream | 데이터를 객체 단위로 읽거나, 읽어 들인 객체를 역직렬화시킴 |
SequenceInputStream | X | 두 개의 입력 스트림을 논리적으로 연결 |
PushbackInputStream | X | 다른 입력 스트림에 버퍼를 이용해 push back이나 unread와 같은 기능을 추가 |
X | PrintStream | 다른 출력 스트림에 버퍼를 이용해 다양한 데이터를 출력하기 위한 기능을 추가 |
문자 기반 스트림
- 자바에서 스트림은 기본적으로 byte 단위로 데이터를 전송하지만, 가장 작은 타입인 char 형이 2바이트이므로, 1바이트씩 전송되는 byte 기반 스트림으로는 원활한 처리가 힘든 경우가 있음
- 따라서 byte 기반 스트림뿐만 아니라 문자 기반의 스트림도 별도로 제공
- 이러한 문자 기반 스트림은 기존의 byte 기반 스트림에서 InputStream을 Reader로, OutputStream을 Writer로 변경하면 사용할 수 있음
입력 스트림 | 출력 스트림 | 입출력 대상 |
FileReader | FileWriter | 파일 |
CharArrayReader | CharArrayWriter | 메모리 |
PipedReader | PipedWriter | 프로세스 |
StringReader | StringWriter | 문자열 |
문자 기반 보조 스트림
입력 스트림 | 출력 스트림 | 설명 |
FilterReader | FilterWriter | 필터를 이용한 입출력 |
BufferedReader | BufferedWriter | 버퍼를 이용한 입출력 |
PushbackReader | X | 다른 입력 스트림에 버퍼를 이용하여 push back이나 unread와 같은 기능을 추가 |
X | PrintWriter | 다른 출력 스트림에 버퍼를 이용하여 다양한 데이터를 출력하기 위한 기능을 추가 |
Byte 단위 입출력
Byte단위 입출력 클래스는 클래스의 이름이 InputStream이나 OutputStream으로 끝남
파일로부터 1byte씩 읽어들여, 파일에 1byte씩 저장하는 프로그램
- FileInputStream: 파일로부터 1byte씩 읽어들임
- FileOutputStream: 파일에 1byte씩 저장
완성 코드
- 파일로부터 1byte씩 읽어들여 파일에 1byte씩 저장하는 코드
public class ByteIOExam2 {
public static void main(String[] args){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("src/javaIO/exam/ByteExam1.java");
fos = new FileOutputStream("byte.txt");
//읽어들인 값을 담기 위해 readData라는 변수 선언
int readData = -1;
//읽어들일 코드가 여러 줄이기 때문에 반복문 while 이용
//읽어들이는 데이터를 readData에 담아줄 거니까 readData = fis.read()
//파일의 데이터를 다 읽을 때까지 수행시켜야 하니까 -1이 아닐 때만 반복(-1이면 반복문 종료)
while((readData = fis.read()) != -1){
fos.write(readData);
}
} catch (Exception e) {
*// TODO Auto-generated catch block*e.printStackTrace();
}finally{
try {
fos.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*e.printStackTrace();
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*
e.printStackTrace();
}
}
}
}
파일로부터 1byte씩 읽어들여 파일에 1byte씩 저장하는 코드를 작성하는 과정
객체 선언 및 생성
- FileInputStream: 파일로부터 읽어오기 위한 java.io의 클래스
- FileOutputStream: 파일에 쓸 수 있게 해주는 java.io의 클래스
//객체를 선언함과 동시에 초기화
FileInputStream fis = null;
FileOutputStream fos = null;
//객체 생성과 Exception 처리
try {
//FileInputStream의 괄호 안에 읽어들일 파일의 경로 기재
fis = new FileInputStream("src/javaStudy2/part5/ByteExam1.java");
//FileOutputStream의 괄호 안에 작성할 파일의 경로 기재
fos = new FileOutputStream("byte.txt");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 객체 생성할 때 괄호 안에 파일의 경로 작성 (경로를 적지 않으면 프로젝트 밑에 저장됨)
- Exception 처리
- FileNotFoundException(파일이 없는 경우 발생하는 Exception)을 처리하기 위해 try-catch문 사용
- FileNotFoundException 외의 예외가 발생할 수 있기 때문에 catch문 안의 예외는 Exception으로 기재
- 객체를 try문 밖에서도 사용할 수 있게 try문 밖에 선언 및 초기화
read( )메소드로 값을 읽어들이고 write( )메소드로 값을 적기
int readData = -1;
while((readData = fis.read()) != -1){
fos.write(readData);
}
- 파일로부터 읽어들인 값을 담기 위해 readData라는 변수 선언 및 초기화
- 읽어들인 값을 readData에 넣음
- 읽어들일 값이 여러 줄일 수 있기 때문에 반복문 while 사용
- FileInputStream로 읽어들인 값을 readData에 담아줄 거니까 readData = fis.read( )
- 파일의 데이터를 다 읽을 때까지 수행시켜야 하니까 (readData = fis.read( ))의 값이 -1이 될 때까지 반복 (-1이 되면 반복문 종료)
- 읽어들인 데이터인 readData를 FileOutputStream의 write( )메소드를 이용해 byte.txt에 적음
close( )메소드로 java.io의 모든 인스턴스화된 객체 닫기
}finally{
try {
fos.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*e.printStackTrace();
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*
e.printStackTrace();
}
}
- java.io의 모든 객체들은 인스턴스화하고 나면 닫아줘야 함(close( )메소드)
- 위에서 Exception이 발생해도 꼭 닫아줘야 하니까 finally문 안에 써줌
- FileOutputStream 객체를 close( )메소드로 닫아줌
- close( )메소드도 throws해서 IOException을 발생시키니까 try-catch문으로 Exception 처리해줘야 함
- FileInputStream 객체도 close( )메소드로 닫아줌
- close( )메소드도 throws해서 IOException을 발생시키니까 try-catch문으로 Exception 처리해줘야 함
- FileOutputStream 객체를 close( )메소드로 닫아줌
실행결과
- System.out.println( )을 사용하지 않았기 때문에 콘솔에 출력되는 건 없음
- Package Explorer에서 프로젝트를 선택하고 새로고침하면 byte.txt 파일이 만들어진 걸 확인할 수 있음
- 파일 안의 내용은 FileInputStream 객체에 적은 파일(ByteExam1)의 내용과 똑같음
FileInputStream의 read( )메소드
- 1byte씩 읽을 수 있음
- 정수 4byte 중 마지막 byte에 읽어들인 1byte를 저장
- 리턴타입은 char가 아니라, int
- read( )메소드가 char를 리턴한다면 끝을 나타내는 값(-1)을 표현할 수 없기 때문에, char가 아닌 int를 리턴
- char 자료형은 2byte로 (0 ~ 2^16 - 1) 의 범위를 가짐
- But, read( )메소드는 더 이상 읽을 문자가 없으면 -1을 리턴하기 때문에 실제로 필요한 범위는 (-1 ~ 2^16 - 1)
- 이 범위를 만족하는 가장 작은 자료형이 int형이므로 리턴값의 크기가 int형(4byte)이 된 것
- read( )메소드가 char를 리턴한다면 끝을 나타내는 값(-1)을 표현할 수 없기 때문에, char가 아닌 int를 리턴
- 음수의 경우 맨 좌측 비트가 1이 됨. 읽어들일 것이 있다면 항상 양수를 리턴한다고 볼 수 있음
- 더 이상 읽어들일 것이 없을 때 -1을 리턴
실습
- 문제 설명
- data.txt의 내용을 그대로 읽어서 copy.txt로 복사하려고 합니다. 12번째 줄 부터 fis와 fos를 이용해서 코드를 완성해 보세요.
- 해설
- data.txt 파일에서 FileInputStream객체의 read( )메소드로 읽어들인 값을 readData에 저장하고, readData를 FileOutputStream객체의 write( )메소드로 copy.txt 파일에 작성하는 코드를 만들면 됨
- ByteReadWriteExam 클래스
import java.io.*;
public class ByteReadWriteExam{
public static void main(String[] args){
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("data.txt");
fos = new FileOutputStream("copy.txt");
//data.txt의 파일을 읽어서 그대로 copy.txt에 복사하려고 합니다.
//이 아래에서 fis와 fos를 이용해서 파일 복사를 완성해 보세요.
int readData = -1;
while((readData = fis.read()) != -1){
fos.write(readData);
}
}
catch(Exception e){
System.out.println(e);
}
finally{
//fis와 fos를 사용하고 나면 close해야 합니다.
try{
fis.close();
fos.close();
}
catch(Exception e){
System.out.println(e);
}
}
}
}
Byte 단위 입출력 심화(File)
Byte단위 입출력 클래스는 클래스의 이름이 InputStream이나 OutputStream으로 끝남
용량이 큰(512byte) Byte 단위 입출력 프로그램
- 파일로부터 512byte씩 읽어들여 파일에 512byte씩 저장하는 프로그램을 작성
- 1byte씩 읽어들이는 프로그램보다 속도가 빠름
- 파일로부터 1byte씩 읽어들여 파일에 1byte씩 저장하는 코드를 재활용할 예정
- 위의 코드에서 수정할 부분
- 512byte를 읽어들이기 위해 byte배열 buffer을 사용
- 얼마나 빠른지 실행시간을 확인하기 위해 System.currentTimeMillis( )메소드 사용
완성 코드
- 파일로부터 512byte씩 읽어들여, 파일에 512byte씩 저장하는 코드
public class ByteIOExam1 {
public static void main(String[] args){
*//메소드가 시작된 시간을 구하기 위함*
long startTime = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("src/javaIO/exam/ByteExam1.java");
fos = new FileOutputStream("byte.txt");
int readCount = -1;
byte[] buffer = new byte[512];
while((readCount = fis.read(buffer))!= -1){
fos.write(buffer,0,readCount);
}
} catch (Exception e) {
*// TODO Auto-generated catch block*e.printStackTrace();
}finally{
try {
fos.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
*// TODO Auto-generated catch block*e.printStackTrace();
}
}
*//메소드가 끝났을때 시간을 구하기 위함*
long endTime = System.currentTimeMillis();
*//메소드를 수행하는데 걸린 시간을 구할 수 있음*
System.out.println(endTime-startTime);
}
}
파일로부터 512byte씩 읽어들여, 파일에 512byte씩 저장하는 코드를 작성하는 과정
객체 선언 및 생성
- FileInputStream: 파일로부터 읽어오기 위한 java.io의 클래스
- FileOutputStream: 파일에 쓸 수 있게 해주는 java.io의 클래스
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("src/javaStudy2/part5/ByteExam1.java");
fos = new FileOutputStream("byte.txt");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 객체 생성할 때 괄호 안에 파일의 경로 작성 (경로를 적지 않으면 프로젝트 밑에 저장됨)
- Exception 처리
- FileNotFoundException(파일이 없는 경우 발생하는 Exception)을 처리하기 위해 try-catch문 사용
- FileNotFoundException 외의 예외가 발생할 수 있기 때문에 catch문 안의 예외는 Exception으로 기재
- 객체를 try문 밖에서도 사용할 수 있게 try문 밖에 선언 및 초기화
최대 512byte를 읽어들이기 위해 byte배열 buffer을 사용
int readCount = -1;
byte[] buffer = new byte[512];
while((readCount = fis.read(buffer))!= -1){
fos.write(buffer,0,readCount);
}
- byte[] buffer = new byte[512];
- 최대 512byte만큼 읽어들이기 위해 byte배열을 사용
- byte배열의 크기는 읽어들일 수만큼인 최대 512byte로 설정
- (readCount = fis.read(buffer))!= -1
- FileInputStream 객체의 read( )메소드에 byte배열 buffer 삽입
- 512byte를 읽어들이지 못하는 경우도 있음
- 만약 파일의 크기가 1000byte라면 처음엔 512byte를 읽지만, 두 번째엔 488byte를 읽고, 마지막엔 읽어들일 값이 없기 때문에 read( )메소드는 -1을 반환할 것
- 값을 저장할 변수로 readData라는 명칭 대신, 분명한 의미 전달을 위해 readCount로 수정
- fos.write(buffer,0,readCount);
- ‘byte배열 buffer를 0번째 값부터 readCount만큼 써주세요’라는 요청
- 512byte만큼 읽어들였기 때문에 buffer를,
- buffer의 0번째 인덱스부터 시작하니까 0을,
- 읽어들이는 수를 readCount가 갖고 있기 때문에 readCount를 작성
- ‘byte배열 buffer를 0번째 값부터 readCount만큼 써주세요’라는 요청
- while문 실행 과정 (파일의 크기가 1000byte라면)
- 처음에 fis.read(buffer)가 512byte를 읽어 buffer[ ]에 입력하고 readCount에 512을 리턴 > fos.write(buffer,0,readCount)가 buffer[0]부터 buffer[511]까지 읽어서 출력
- fis.read(buffer)가 나머지 488byte를 읽어 buffer[ ]에 입력하고 readCount에 488을 리턴 > fos.write(buffer,0,readCount)가 buffer[0]부터 buffer[487]까지 읽어서 출력
- 그 다음에 fis.read(buffer)는 -1을 리턴하고, 반복문 종료
Sytem클래스의 currentTimeMillis( )메소드로 실행시간 확인
long startTime = System.currentTimeMillis();
.
.
.
long endTime = System.currentTimeMillis();
*//메소드를 수행하는데 걸린 시간을 구할 수 있음*
System.out.println(endTime-startTime);
}
- System.currentTimeMillis( );
- 현재 시간을 long타입으로 반환
- currentTimeMillis( )메소드로 startTime과 endTime을 만듦
- endTime - startTime: 프로그램 실행시간
실행 결과
1byte 프로그램과 512byte 프로그램의 실행시간 비교
- 1byte 프로그램에도 System.currentTimeMillis( )메소드를 활용한 코드를 삽입
- 1byte 프로그램과 512byte 프로그램의 실행시간을 각각 출력
- 512byte 프로그램이 1byte 프로그램보다 빠름
1byte 프로그램과 512byte 프로그램의 실행시간이 속도 차이가 나는 이유
- 파일을 1byte씩만 읽어오라고 해도 보통 운영체제는 512byte씩 읽어오니까 처음부터 배열의 크기를 512byte의 배수로 설정하는 게 성능상 좋음
- 1byte를 두 번 읽어오라고 하면 512byte를 한 번 읽어와서 1byte만 쓰고 511byte는 버리고, 또 512byte를 한 번 읽어와서 1byte만 쓰고 511byte는 버림
read( )메소드의 오버로딩
- read( )메소드와 read(byte[ ])메소드의 행동이 완전 다름
- read(byte[ ])메소드는 read( )메소드를 오버라이딩한 것
- read( )메소드와 read(byte[ ])메소드 비교
- read( )메소드는 Byte 단위 입출력에서 사용한 메소드
- read(byte[ ])메소드는 Byte 단위 입출력 심화에서 사용한 메소드 - read(buffer)
- 공통점
- 읽을 데이터가 없을 경우, -1을 리턴
- 차이점
- read( )메소드: 읽어온 바이트를 int로 리턴
- read(byte[ ])메소드: 읽어온 byte는 byte[ ]에 저장하고 읽어온 값의 크기를 int로 리턴
다양한 타입의 출력: try-with-resources 활용
try-with-resources
- close( )메소드를 사용하지 않아도, Exception이 발생하지 않았다면 사용한 자원을 자동으로 종료시켜주는 기능
- java io객체는 인스턴스를 만들고, 모두 사용하면 close()메소드를 호출해야 하지만, try-with-resources를 사용하면 close( )메소드 쓸 필요 X
try(
*//io객체 선언*
){
*//io객체 사용*
}catch(Exception ex){
ex.printStackTrace();
}
파일에 데이터를 쓰는 프로그램
- data.txt 파일에 데이터를 쓸 것
완성 코드
- 파일에 데이터를 쓰는 코드
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class ByteExam3 {
public static void main(String[] args) {
try(
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
){
out.writeInt(100);
out.writeBoolean(true);
out.writeDouble(50.5);
}catch (Exception e) {
e.printStackTrace();
}
}
}
파일에 데이터를 쓰는 코드를 작성하는 과정
try문의 소괄호( ) 안에 DataOutputStream 객체 선언
- DataOutputStream는 어디에 데이터를 출력할지 결정할 수 없기 때문에 결정해줄 수 있는 다른 OutputStream을 넣어야 함
- DataOutputStream 객체 out을 생성할 때 파일에 출력하게 하는 FileOutputStream를 넣어줌
- 아무런 경로를 입력하지 않으면 프로젝트 바로 밑에 저장됨
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
try문의 중괄호 { } 안에서 DataOutputStream 객체 사용
- DataOutputStream의 메소드: writeInt( )메소드, writeBoolean( )메소드, writeDouble( )메소드
try(
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
){
out.writeInt(100);
out.writeBoolean(true);
out.writeDouble(50.5);
}
catch블럭에서 Exception 처리
try(
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
){
out.writeInt(100);
out.writeBoolean(true);
out.writeDouble(50.5);
}catch (Exception e) {
e.printStackTrace();
}
실행 결과
- 프로젝트를 새로고침하면 data.txt 파일이 생성된 걸 확인할 수 있음
- 기본으로는 문자형이 쓰여지지만, 현재는 데이터 타입으로 사용했기 때문에 읽어낼 때 DataInputStream 클래스를 이용해야 함
DataOutputStream 클래스: 다양한 타입으로 데이터를 저장할 수 있는 클래스
- DataOutputStream의 생성자는 추상클래스인 OutputStream을 매개변수로 받아들임
- OutputStream의 자손이라면 무엇이든 받아들인다는 의미
- DataOutputStream이 장식의 역할을 한다는 의미
- 다양한 메소드를 제공한다는 뜻
- But, 어디에 쓸지는 결정할 수 없기 때문에 결정하는 다른 장식대상 클래스(예: FileOutputStream)를 함께 써야 함
- DataOutputStream의 메소드
- DataOutputStream의 write( )메소드를 다양한 형식으로 오버로딩함
- writeInt( )메소드: int 값으로 저장. int는 4byte를 차지
- writeBoolean( )메소드: boolean 값으로 저장. boolean은 1byte를 차지
- writeDouble( )메소드: double 값으로 저장. double은 8byte를 차지
- 이외에도 데이터타입에 따라 메소드가 더 존재함
- DataOutputStream의 write( )메소드를 다양한 형식으로 오버로딩함
실습
- 문제 설명
- 다음 파일의 out을 이용해서 data.txt에 int값 100, double값 3.14를 순서대로 저장하세요.
- 해설
- out.writeInt( )메소드와 out.writeDouble( )메소드 사용
- DataOutputStreamExam 클래스
import java.io.*;
public class DataOutputStreamExam{
public static void main(String[] args){
try(
//try의 뒤에나오는 괄호()사이에서 만든 stream은 별도로 close하지 않아도 됩니다.
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
){
//이 아래에 out을 이용해서 data.txt에 int값 100, double값 3.14를 저장하세요.
out.writeInt(100);
out.writeDouble(3.14);
}
catch(Exception e){
e.printStackTrace();
}
}
}
다양한 타입의 입력: try-with-resources 활용
다양한 타입의 출력에서 만든 data.txt 파일 크기의 구성요소
- 13byte = 4byte(int) + 1byte(boolean) + 8byte(double)
파일에 데이터를 읽어오는 프로그램
- data.txt 라는 파일로부터 데이터를 읽어올 것
완성 코드
- 파일에 데이터를 쓰는 코드
import java.io.DataInputStream;
import java.io.FileInputStream;
public class ByteExam4 {
public static void main(String[] args) {
try(
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
){
int i = in.readInt();
boolean b = in.readBoolean();
double d = in.readDouble();
System.out.println(i);
System.out.println(b);
System.out.println(d);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
파일로부터 데이터를 읽어오는 코드를 작성하는 과정
try문의 소괄호( ) 안에 DataInputStream객체 선언
- DataInputStream는 어디에 데이터를 출력할지 결정할 수 없기 때문에 결정해줄 수 있는 다른 InputStream을 넣어야 함
- DataInputStream 객체 in을 생성할 때 파일에 출력하게 하는 FileInputStream를 넣어줌
- 아무런 경로를 입력하지 않으면 프로젝트 바로 밑에 저장됨
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
try문의 중괄호 { } 안에서 DataInputStream 객체 사용
- DataInputStream의 메소드: readInt( )메소드, readBoolean( )메소드, readDouble( )메소드
- 메소드를 사용해 읽어온 값을 변수에 담음
try(
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
){
int i = in.readInt();
boolean b = in.readBoolean();
double d = in.readDouble();
System.out.println(i);
System.out.println(b);
System.out.println(d);
}
catch블럭에서 Exception 처리
try(
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
){
int i = in.readInt();
boolean b = in.readBoolean();
double d = in.readDouble();
System.out.println(i);
System.out.println(b);
System.out.println(d);
}catch(Exception ex){
ex.printStackTrace();
}
실행 결과
- data.txt 파일에 저장할 때 넣어둔 값들(100, true, 50.5)이 콘솔에 출력됨
- 파일에 들어갔을 때 파일의 내용을 눈으로 볼 순 없지만, DataOutputStream을 이용해 데이터 타입 자체로 저장한 후에 DataInputStream을 이용해 해당 데이터타입으로 불러내서 사용할 수 있음
try-with-resources
- close( )메소드를 사용하지 않아도, Exception이 발생하지 않았다면 사용한 자원을 자동으로 종료시켜주는 기능
- java io객체는 인스턴스를 만들고, 모두 사용하면 close()메소드를 호출해야 하지만, try-with-resources를 사용하면 close( )메소드 쓸 필요 X
- try문의 소괄호 ( )에서는 io 객체를 선언하고, 중괄호 { }에서는 io 객체를 사용
DataInputStream 클래스: 다양한 타입의 데이터를 읽어낼 수 있는 클래스
- data.txt로부터 값을 읽어들여 화면에 출력하는 클래스
- DataInputStream의 생성자는 추상클래스인 InputStream을 매개변수로 받아들임
- InputStream의 자손이라면 무엇이든 받아들인다는 의미
- DataInputStream이 장식의 역할을 한다는 의미
- 다양한 메소드를 제공한다는 뜻
- But, 어디에 쓸지는 결정할 수 없기 때문에 결정하는 다른 장식대상 클래스(예: FileInputStream)를 함께 써야 함
- DataInputStream의 메소드
- read( )메소드를 다양한 형식으로 오버로딩함
- 아래의 메소드들이 호출되면 내부적으로 FileInputStream의 read( )메소드를 통해 파일로부터 읽어들여짐
- readInt( )메소드: int 값을 읽어들이는 메소드
- readBoolean( )메소드: boolean 값을 읽어들이는 메소드
- readDouble( )메소드: double 값을 읽어들이는 메소드
- 이외에도 데이터타입에 따라 메소드가 더 존재함
- 파일에 저장된 순서대로 읽어들여야 함
- 위의 예제에서 int, boolean, double 순서대로 저장했기 때문에 읽어들일 때도 같은 순서로 읽어들여야 함
실습
- 문제 설명
- data.txt에는 int형의 숫자가 3개 연속으로 들어있습니다. DataInputStream을 이용해 값을 읽어들인 다음 sum에 저장하세요.
- 해설
- try 구문의 소괄호 ( ) 안에 DataInputStream 객체를 선언
- try 구문의 중괄호 { } 안에 int 값 3개를 저장, sum에 그 3개의 합 담기
- DataInputStreamExam 클래스
import java.io.*;
public class DataInputStreamExam{
public static int read3(){
int sum = 0;
//data.txt로부터 int값 3개를 읽어들여서 sum에 더하세요.
try(
DataInputStream in = new DataInputStream(new FileInputStream("data.txt"));
){
int a = in.readInt();
int b = in.readInt();
int c = in.readInt();
sum = a + b + c;
}catch(Exception e){
e.printStackTrace();
}
//아래는 테스트를 위한 코드입니다. 수정하지 마세요.
return sum;
}
}
Char 단위 입출력(Console)
char 단위 입출력 클래스는 클래스 이름이 Reader나 Writer로 끝남
키보드로부터 한 줄 입력받아서 콘솔에 출력하는 프로그램
- System.in를 이용해 키보드로부터 데이터를 받아
- BufferedReader를 이용해 한 줄 입력받기
완성 코드
- char 단위 입출력 클래스를 이용해 키보드로부터 한 줄 입력받아서 콘솔에 출력하는 코드
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class CharIOExam01 {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
*//키보드로 입력받은 문자열을 저장하기 위해 line변수를 선언*
String line = null;
try {
line = br.readLine()
} catch (IOException e) {
e.printStackTrace();
}
*//콘솔에 출력*
System.out.println(line);
}
}
키보드로부터 입력받아서 콘솔에 출력하는 코드를 작성하는 과정
InputStreamReader를 이용해 BufferedReader 객체 선언
- 키보드로부터 한 줄씩 입력받기 위해서는 BufferedReader와 System.in이 필요
- BufferedReader는 어디에서 데이터를 읽어올지 결정할 수 없기 때문에 결정해줄 수 있는 다른 Reader을 넣어야 한다는 뜻
- System.in: 키보드로부터 입력받을 수 있음
- BufferedReader: 한 줄씩 입력받을 수 있음
- But, BufferedReader의 생성자엔 Reader 객체만 들어갈 수 있으며, System.in은 InputSteram 타입의 객체
- 생성자에서 InputStream을 받아들일 수 있는 InputStreamReader가 필요
- InputStreamReader은 System.in을 받아들일 수 있음
- 생성자에서 InputStream을 받아들일 수 있는 InputStreamReader가 필요
- 결론: BufferedReader 객체를 생성할 때, BufferedReader의 생성자에 InputSteam 타입의 System.in을 Reader 타입으로 바꿔주는 InputStreamReader를 넣어주면 된다!
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
키보드로부터 한 줄씩 입력받을 수 있도록 String 변수를 설정하고 Exception 처리
- BufferedReader의 readLine( )메소드는 Exception 처리를 해줘야 함
- 키보드로부터 읽어온 값을 변수 line에 담아냄
- try문 바깥에서도 사용할 수 있게 try문 밖에서 선언
String line = null;
try {
line = br.readLine()
} catch (IOException e) {
e.printStackTrace();
}
최종 출력 코드 작성
- 변수 line을 출력하는 코드
- 변수 line: BufferedReader의 readLine( )메소드가 수행될 때 한 줄 입력받아서 대입된 값
System.out.println(line);
실행 결과
- 콘솔에 문장을 입력하고 Enter를 누르면 그대로 출력됨
키보드로부터 입력받아서 콘솔에 출력하는 코드의 다양한 활용법
- 반복문을 이용해 여러 줄을 입력받게 할 수도 있고,
- 데코레이터 패턴을 이용해 키보드 대신 File로부터 입력받게 할 수도 있고,
- 입력받은 부분을 콘솔 대신 File이나 ArrayList 같은 자료구조로 저장할 수도 있음
System.in: 키보드를 의미 (InputStream)
- java.lang 패키지 - System 클래스 - in 필드
- System.in의 타입이 InputStream
- BufferedReader의 생성자에 바로 들어갈 수 없음
- nputStreamReader 클래스를 이용해야 함
- System.in을 사용하면 키보드로부터 입력받을 수 있음
BufferedReader: 한 줄씩 입력받기 위한 클래스
- java.io 패키지의 BufferedReader 클래스
- 한 줄 입력받는 readLine( )메소드를 가짐
- BufferedReader 클래스의 생성자
- Reader 객체만 받아들일 수 있음
- InputStream 타입의 객체를 입력받는 생성자가 없음
- InputStream 타입의 객체인 System.in을 Reader 타입의 객체로 바꾸기 위해 java.io 패키지의 InputStreamReader을 이용해야 함
InputStreamReader: Reader 타입 클래스
- java.io 패키지의 InputStreamReader 클래스
- java.io 패키지의 Reader를 상속받기 때문에 Reader 타입임
- 생성자에서 InputStream 타입을 받아들임
- System.in을 받아들일 수 있음
데코레이터 패턴(Decorator Pattern)
- 객체에 추가적인 요건(기능)을 동적으로 첨가하는 방식
- 근원지가 될 수 있는 부분과 기능을 가지고 있는 부분을 끼워서 사용
- 근원지: 키보드로부터 값을 입력받는 System.in
- 기능: 한 줄씩 입력받는 BufferedReader
- 서브클래스를 만드는 것을 통해 기능을 유연하게 확장할 수 있는 방법을 제공
- 서브클래스 예시: InputStreamReader(System.in)
- BufferedReader의 생성자에 InputSteam 타입의 System.in을 Reader 타입으로 바꿔주는 InputStreamReader를 넣어주는 것도 데코레이터 패턴의 일종
Char 단위 입출력(File)
char단위 입출력 클래스는 클래스 이름이 Reader나 Writer로 끝남
파일에서 한 줄씩 입력받아서 파일에 출력하는 프로그램
- 파일에서 읽기 위해 FileReader 클래스 이용
- 한 줄 읽어들이기 위해서 BufferedReader 클래스 이용
- 파일에 쓰게 하기 위해 FileWriter 클래스 이용
- 편리하게 출력하기 위해 PrintWriter 클래스 이용
완성 코드
- 파일에서 한 줄씩 입력받아서 파일에 출력하는 코드
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class CharIOExam02 {
public static void main(String[] args) {
BufferedReader br = null;
PrintWriter pw = null;
try{
br = new BufferedReader(new FileReader("src/javaIO/exam/CharIOExam02.java"));
pw = new PrintWriter(new FileWriter("test.txt"));
String line = null;
while((line = br.readLine())!= null){
pw.println(line);
}
}catch(Exception e){
e.printStackTrace();
}finally {
pw.close();
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
파일에서 한 줄씩 입력받아서 파일에 출력하는 코드를 작성하는 과정
FileReader를 이용해 BufferedReader 객체 생성
+ FileWriter를 이용해 PrintWriter 객체 생성
+ FileReader를 Exception 처리
- FileReader에서 FileNotFoundException이 발생할 수 있기 때문에 Exception 처리를 해줘야 함
- 다른 메소드를 사용할 때 FileNotFoundException 외의 Exception이 발생할 수 있기 때문에 catch문 소괄호( )안의 Exception 타입을 Exception을 처리
- try문 안에서 객체를 선언하면 try문 밖에서 못 쓰니까 try문 밖에서 선언 및 초기화
- 요즘은 PrintWriter가 발전해서 PrintWriter 생성자 자체가 파일을 받아들임
- FileWriter를 쓰지 않아도 된다는 뜻
- PrintWriter pw = new PrintWriter(”test.txt”); 로도 작성 가능
BufferedReader br = null;
PrintWriter pw = null;
try{
br = new BufferedReader(new FileReader("src/javaStudy2/part5/CharIOExam2.java"));
pw = new PrintWriter(new FileWriter("test.txt"));
}catch(Exception e){
e.printStackTrace();
}
파일로부터 한 줄씩 입력받을 수 있도록 String 변수를 설정
+ 입력받은 값을 파일에 저장하는 반복문 작성
- 한 줄 읽었을 때 그 값을 저장할 수 있는 변수 line 선언
- 여러 줄을 받기 위해 while문 사용
- 파일로부터 한 줄 입력받은 값을 line에 대입하고, 그 값이 null이 될 때까지 반복문 실행
- BufferedReader의 readLine( )메소드는 더 이상 읽어들일 내용이 없을 때 null을 리턴
- 파일로부터 한 줄 입력받은 값을 line에 대입하고, 그 값이 null이 될 때까지 반복문 실행
- pw.println(line);
- 읽어들인 값을 파일에 작성
String line = null;
while((line = br.readLine())!= null){
pw.println(line);
}
close( )메소드로 java.io의 모든 인스턴스화된 객체 닫기
- PrintWriter 클래스의 close( )메소드는 Exception 처리를 안 해도 되고, BufferedReader의 close( )메소드는 Exception 처리를 꼭 해야 함
- br.close( )만 Exception 처리를 하는 이유
- PrintWriter 클래스가 정의하는 close( )메소드
- public void close( ) 으로 정의되어 있음
- Exception을 throws하고 있지 않으므로 UnCheckedException
- 메소드를 사용하는 쪽에서 Exception을 처리할 필요 X
- BufferedReader 클래스의 close( ) 메소드
- public void close( ) throws IOException 으로 정의되어 있음
- 메소드 안에서 발생할 수 있는 IOException을 throws하고 있으므로 CheckedException
- 그 메소드를 사용하는 쪽에서는 반드시 처리할 수밖에 없음 > 해당 예제에선 try-catch 블럭을 이용해서 Exception을 처리한 것
- Exception을 처리하는 두 가지 방법
- try-catch 블럭을 이용해서 직접 처리 또는 throws 시키기
- throws: 예외가 발생했을 때 예외를 호출한 쪽에서 처리하도록 던져줌(떠넘김) > exception을 처리하지 않았다는 의미이기도 함
- CheckedException은 반드시 처리해야만 하니까 throws했을 땐 반드시 해당 메소드를 사용하는 쪽에서 처리해야지만 에러가 발생하지 않음
- PrintWriter 클래스가 정의하는 close( )메소드
}finally {
pw.close();
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
실행 결과
- System.out.println( )을 사용하지 않았기 때문에 콘솔에 출력되는 건 없음
- Package Explorer에서 프로젝트를 선택하고 새로고침하면 test.txt 파일이 만들어진 걸 확인할 수 있음
- test.txt와 CharIOExam2.java 파일의 내용이 완전히 일치함
BufferedReader: 한 줄씩 입력받기 위한 클래스
- java.io 패키지의 BufferedReader 클래스
- BufferedReader는 장식하는 클래스라 어디에 쓸지는 결정할 수 없기 때문에 결정하는 다른 장식대상 클래스인 FileReader를 함께 써야 함
- 한 줄 입력받는 readLine( )메소드를 가짐
- readLine( )메소드는 더 이상 읽어들일 내용이 없을 때 null을 리턴
FileReader: 파일에서 읽어오는 클래스
- java.io 패키지의 FileReader 클래스
- java.io 패키지의 Reader를 상속받기 때문에 Reader 타입임
PrintWriter: 편리하게 출력할 수 있게 하는 클래스
- java.io 패키지의 PrintWriter 클래스
- PrintWriter는 장식하는 클래스라 어디에 쓸지는 결정할 수 없기 때문에 결정하는 다른 장식대상 클래스인 FileWriter를 함께 써야 함
- But, 요즘은 PrintWriter가 발전해서 PrintWriter 생성자 자체가 파일을 받아들임
- FileWriter를 쓰지 않아도 됨
- 예: PrintWriter pw = new PrintWriter(”test.txt”);
- println( )메소드를 가짐
- System.out.println( )에서 System.out의 타입이 PrintWriter
FileWriter: 파일에 쓰는 클래스
- java.io 패키지의 FileWriter 클래스
- java.io 패키지의 Writer를 상속받기 때문에 Writer 타입임
실습
- 문제 설명
- PrintWriter를 통해 data.txt파일에 "안녕하세요. PrintWriter입니다."라고 적어보세요.
- 해설
- FileNotFoundException 등의 Exception을 처리하기 위해 try-catch-finally문 사용
- FileWriter 클래스를 사용해 PrintWriter 객체 선언 및 생성
- try 밖에서도 객체를 사용할 수 있게 try문 밖에서 객체를 선언 및 초기화
- PrintWriter의 println( )메소드를 이용해 문제의 값 담기
- catch문으로 Exception 처리
- finally문으로 객체 닫기
- CharIOExam 클래스
import java.io.*;
public class CharIOExam{
public static void main(String[]args){
PrintWriter pw = null;
try{
pw = new PrintWriter(new FileWriter("data.txt"));
pw.println("안녕하세요. PrintWriter입니다.");
} catch(Exception e){
e.printStackTrace();
} finally {
pw.close();
}
}
}
'☕ Java 웹 프로그래밍 > Java' 카테고리의 다른 글
[프로그래머스] Java(자바) 중급 | Part 7. 스레드 (0) | 2023.05.18 |
---|---|
[프로그래머스] Java(자바) 중급 | Part 6. 어노테이션 (0) | 2023.05.16 |
[프로그래머스] Java(자바) 중급 | Part 4. 날짜와 시간 (2) | 2023.05.13 |
[프로그래머스] Java(자바) 중급 | Part 3. java.util 패키지 (0) | 2023.05.13 |
[프로그래머스] Java(자바) 중급 | Part 2. java.lang 패키지 (0) | 2023.05.11 |