JAVA 중급강의 정리 자바IO 입력과 출력
본 포스팅은 프로그래머스 자바 중급 강의에 기반하여 작성되었습니다.
자바 강의의 모든 내용이 적힌것이 아닌 복습시 놓쳤던 부분을 하나하나 정리한 내용입니다.
순서 자바IO, Byte단위 입출력, 다양한 타입의 출력, 다양한 타입의 입력,
char 단위 입출력(Console), char 단위 입출력(File)
자바IO (입력과 출력)
자바IO 에서 I는 Input을 의미하고 O는 Output을 의미한다.
프로그램 상에서 들어오는 모든 데이터를 Input Data라고 한다. 밖으로 나가는 모든 데이터를 Output Data
입출력을 위한 인터페이스와 클래스들
- 자바 IO는 크게 byte단위 입출력과 문자(char) 단위 입출력클래스로 나뉩니다.
- byte단위 입출력클래스는 모두 InputStream과 OutputStream이라는 추상클래스를 상속받아 만들어집니다.
- 문자(char)단위 입출력클래스는 모두 Reader와 Writer라는 추상클래스를 상속받아 만들어집니다.
- 4가지 추상클래스(InputStream,OutputStreamReader,Reader,Writer)를 받아들이는 생성자가 있다면, 다양한 입출력방법을 제공하는 클래스입니다.
- 4가지 클래스를 받아들이는 생성자가 없다면, 어디로부터 입력받을 것인지, 어디에 쓸것인지를 나타내는 클래스
- 어디로부터 입력받을 것인지, 어디에 쓸것인지를 나타내는 클래스는 1byte나 1char 단위로 입력하거나 byte배열 혹은 char배열 단위로 입출력을 하게된다. 입력하고 출력하는 메소드가 단순하게 제공되기 때문에 특별한 방법으로 입출력을 하려면 4가지 추상메소드를 생성자에서 받아들이는 IO클래스들을 이용하여야 한다.
- 파일로 부터 입력받고 쓰기 위한 클래스 : FileInputStream, FileOutputStream, FileReader, FileWriter
- 배열로 부터 입력받고 쓰기 위한 클래스 : ByteArrayInputStream, ByteArrayOutputStream, CharReader, CharWriter
- 해당 클래스들은 어디로부터, 어디에라는 대상을 지정할 수 있는 IO클래스입니다. 이런 클래스를 장식대상 클래스라고 합니다.
- DataInputStream, DataOutputStream같은 클래스를 보면 다양한 데이터 형을 입력받고 출력합니다.
- PrintWriter는 다양하게 한줄 출력하는 pintln()메소드를 가지고있습니다.
- BufferedReader는 한줄 입력받는 readLine()메소드를 가집니다.
- 이런 클래스들은 다양한 방식으로 입력하고, 출력하는 기능을 제공합니다. 이런 클래스를 장식하는 클래스라고 합니다.
자바IO는 데코레이터 패턴으로 만들어져 있다.
데코레이터 패턴(Decorator Pattern)
하나의 클래스를 장식하는 것처럼 생성자에서 감싸서 새로운 기능을 계속 추가 할 수 있도록
클래스를 만드는 방식
디자인 패턴에대한 부분은 나중에 별도로 공부해보자
Byte 단위 입출력
Byte단위 입출력 클래스는 클래스의 이름이 InputStream이나 OutputStream으로 끝납니다.
- 파일로 부터 1byte씩 읽어들여 파일에 1byte씩 저장하는 프로그램을 작성
- 파일로 부터 읽어오기 위한 객체 - FileInputStream
- 파일에 쓸수 있게 해주는 객체 - FileOutputStream .
- read()메소드
- byte를 리턴한다면 끝을 나타내는 값을 표현할 수가 없기 때문에, byte가 아닌 int를 리턴한다.
- 음수의 경우 맨 좌측 비트가 1이 된다. 읽어들일 것이 있다면 항상 양수를 리턴한다고볼 수 있다. .
- FileInputStream과 FileOutputStream을 이용하여, 1바이트씩 읽어들여 1바이트씩 저장
- read()메소드가 리턴하는 타입은 정수인데, 정수 4바이트중 마지막 바이트에 읽어들인 1바이트를 저장한다.
- read()메소드는 더이상 읽어들일 것이 없을때 -1을 리턴한다.
public class ByteIOExam1 {
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"); // 파일에 쓸 수 있는 객체
int readData = -1;
while((readData = fis.read())!= -1){ // 읽어들일 데이터가 여러줄 있을수 있기때문에 while
fos.write(readData); // 파일이 끝나면 -1이 됨
}
} catch (Exception e) { // 파일이 없을 경우를 대비해 예외처리
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fos.close(); // IO에 모든 객체들은 인스턴스화 후 반드시 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);
}
}
// 결과값 4
위에서 작성한 클래스를 개선, 1byte씩 읽어서 1byte씩 사용했던것을 512byte씩으로
Byte 단위 입출력 심화
Byte단위 입출력 클래스는 클래스의 이름이 InputStream이나 OutputStream으로 끝납니다.
- 파일로 부터 512byte씩 읽어들여 파일에 512byte씩 저장하는 프로그램을 작성
- byte[] buffer = new byte[512];
- 512byte만큼 읽어 들이기 위해 byte배열을 사용
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]; // 512byte만큼 읽어들여야하니 배열생성
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);
}
}
- System.currentTimeMillis();
- 현재 시간을 롱타입으로 반환한다.
// 결과값 1
왜 두 예제의 속도가 차이가 날까?
우리가 사용하는 운영체제는 1byte씩만 읽어오라고 해도 512byte씩 읽어온다.
그래서 1byte를 2번 읽어오라고 하면 512byte를 읽어와서 1byte만 쓴다음에 511byete는 버리고
다시 512byte를 읽어와서 1byte만 쓴다음에 511byete는 버린다.
어차피 OS에서 512byte씩 읽어오기 때문에 파일을 읽어오기 때문에 파일을 읽어올때는 512byte의 배수로
배열을 받아주는것이 성능상 좋다.
다양한 타입의 출력
- try-with-resources 블럭 선언
- java io객체는 인스턴스를 만들고, 모두 사용하면 close()메소드를 호출해야 한다.
- close()메소드를 사용자가 호출하지 않더라도, Exception이 발생하지 않았다면 자동으로 close()가 되게 할 수 있는 방법이 있다
try(
//io객체 선언
){
//io객체 사용
}catch(Exception ex){
ex.printStackTrace();
}
- 다양한 타입으로 저장 할 수 있는 DataOutputStream
- wirteInt() - 정수값으로 저장
- wirteBoolean() - boolean값으로 저장
- writeDouble() - double 값으로 저장
아무런 경로를 지정하지 않으면 해당 프로젝트에 파일이 생성된다.
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class ByteExam3 {
public static void main(String[] args) {
try(
//try의 뒤에나오는 괄호()사이에서 만든 stream은 별도로 close하지 않아도 됩니다.
DataOutputStream out = new DataOutputStream(new FileOutputStream("data.txt"));
){
out.writeInt(100);
out.writeBoolean(true);
out.writeDouble(50.5);
}catch (Exception e) {
e.printStackTrace();
}
}
}
DataOutputStream은 혼자 생성할 수 없고 다른 OutputStream을 넣어야한다.
다양한 타입의 입력
- data.txt로부터 값을 읽어들여 화면에 출력하는 클래스
- 다양한 타입의 데이터를 읽어낼 수 있는 DataInputStream
- readInt() -정수를 읽어들이는 메소드
- readBoolean() - boolean 값을 읽어들이는 메소드
- readDouble() - douboe 값을 읽어들이는 메소드
import java.io.DataInputStream;
import java.io.FileInputStream;
public class ByteIOExam4 {
public static void main(String[] args) {
try(
DataInputStream out = new DataInputStream(new FileInputStream("data.txt"));
){
int i = out.readInt();
boolean b = out.readBoolean();
double d = out.readDouble();
System.out.println(i);
System.out.println(b);
System.out.println(d);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
- 실행결과
100
true
50.5
- 파일에 저장된 순서대로 읽어 들여야한다.
- int, boolean, double순서대로 저장하였기 때문에 읽어들일 때도 같은 순서로 읽어 들여야 한다.
데이터 타입 자체로 저장이 가능하고 해당 데이터 타입을 이용하여 불러낼 수 있다.
이렇듯 IO 클래스들은 다양하게 조합하여 사용이 가능하다.
Char 단위 입출력(Console)
char단위 입출력 클래스는 클래스 이름이 Reader나 Writer로 끝이 납니다.
- char단위 입출력 클래스를 이용해서 키보드로 부터 한줄 입력 받아서 콘솔에 출력
- System.in - 키보드를 의미 (InputStream )
- BufferedReader - 한줄씩 입력 받기위한 클래스
- BufferedReader 클래스의 생성자는 InputStream을 입력받는 생성자가 없다. Reader타입만 받음
- System.in은 InputStream 타입이므로 BufferedReader의 생성자에 바로 들어갈 수 없으므로 System.in을 Reader 타입으로 바꿔줄 수 있는 InputStreamReader 클래스를 이용해야함.
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);
}
}
실행결과
입력한값!
입력한값!
데코레이터 패턴을 이용하여 키보드가 아니라 파일로부터 입력을 받게 할 수도 있다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
데코레이터 패턴(Decorator Pattern)
객체에 추가적인 요건(기능)을 동적으로 첨가하는 방식
서브클래스를 만드는 것을 통해 기능을 유연하게 확장할 수 있는 방법 제공
Char 단위 입출력(File)
char단위 입출력 클래스는 클래스 이름이 Reader나 Writer로 끝이 납니다.
- 파일에서 한 줄씩 입력 받아서 파일에 출력
- 파일에서 읽기위해서 FileReader 클래스 이용
- 한 줄 읽어 들이기 위해서 BufferedReader 클래스 이용
- BufferedReader 클래스가 가지고 있는 readLine() 메소드가 한줄씩 읽게 해준다.
- readLine()메소드는 읽어낼 때 더 이상 읽어 들일 내용이 없을 때 null을 리턴한다.
- 파일에 쓰게하기 위해서 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();
}
}
}
}
꼭 기억해야할 부분
항상 IO는 열어주었으면 반드시 close(); 해야한다.