java.io package에서 제공하는 다양한 API중 데이터 입출력이 가능한 기능이 있다.
이것을 통해 콘솔 창에 데이터를 입력하고, 입력받은 값이 다시 출력되게 할 수 있다.
java의 데이터 흐름(stream)은 단방향이라 읽기 혹은 쓰기만 가능하다.
즉, 하나의 클래스가 읽고 쓰기 모두는 불가능하다!
1byte 크기의 데이터를 읽는 API들은 ~InputStream
1byte 크기의 데이터를 출력하는 API들은 ~OutputStream
2byte 크기의 데이터를 읽는 API들은 ~Reader
2byte 크기의 데이터를 출력하는 API들은 ~Writer 가 뒤에 붙어서 이름으로 구분이 가능하다.
ex ) 1. 데이터를 file로부터 read 하는 기능
1byte : FileInputStream / 2byte : FileReader
2. 데이터를 file로부터 write 하는 기능
1byte : FileOutputStream / 2byte : FileWriter
3. 1byte와 2byte 모두 호환되는 기능
read : InputStreamReader / writer : OutputStreamWriter
1byte와 2byte 모두 호환 가능한 API인 InputStreamReader에 대하여 알아보자.
public void test1() {
InputStreamReader read = new InputStreamReader(System.in);
}
생성은 이와 같이 가능하지만 바로 읽는 기능을 사용할 수는 없다.
입력 값이 없는 예외가 생길 수 있기 때문에 exception 처리를 위해 try~catch 문장을 사용하여 읽어들인 값을 출력하는 출력문을 함께 작성했다.
@Test //lombok을 이용하여 @Test로 실행했다.
public void test1() {
InputStreamReader a = new InputStreamReader(System.in);
try {
System.out.println(a.read());
} catch (IOException e) {
e.printStackTrace();
}
}
콘솔 창에 문자를 입력하면 입력한 값이 그대로 출력되게 하는 것이 목표이다.
ex ) a 입력 -> 출력 : a
new InputStreamReader(System.in)에 들어가는 System.in에 대한 설명 정리 글
하지만 위의 코드를 실행하면 a를 입력시 97이 출력되는 것을 볼 수 있는데, 97은 'a'에 해당하는 아스키코드 값이다. [아스키코드 표]
따라서 출력 값이 문자로 나오게끔 출력문에 char로 형변환을 해주어야 한다.
< 수정한 코드 >
@Test
public void test1() {
InputStreamReader a = new InputStreamReader(System.in);
try {
System.out.println((char)a.read());
} catch (IOException e) {
e.printStackTrace();
}
}
입력 : a
출력 : a
이제 입력한 값과 출력한 값이 동일한 것을 확인할 수 있다.
이번에는 Buffer 기능을 지원하는 BufferedReader를 이용해서 한 글자가 아닌 한 문장 단위로 읽는 readLine( )을 이용해보자.
그런데 버퍼 기능이 대체 무엇을 말하는 걸까?
먼저 버퍼에 대해 알아보자.
Buffer : 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역
즉, 버퍼는 임시로 데이터들을 보관하고 있는 영역을 말한다.
readLine을 BufferedReader 없이 작성하게 되면 빨간 밑줄이 생기면서 코드가 실행되지 않는 것을 확인할 수 있다.
자바의 read는 코드를 한 글자씩 읽어서 아스키코드 값으로 저장한다.
따라서 라인 단위로 문자를 읽는다는 것은, 임시 공간에 글자를 한글자씩 읽어서 저장해뒀다가 문장 단위로 한번에 가져오는 것이다.
이제 한 줄 단위로 문자를 읽는 기능을 구현해보자.
@Test
public void test2() {
BufferedReader a = new BufferedReader(new InputStreamReader(System.in));
try {
String data = a.readLine(); //readLine도 발생 가능한 예외때문에 try~catch 사용
System.out.println(data);
} catch (IOException e) {
e.printStackTrace();
}
}
입력 : abcdefg
출력 : abcdefg
이번에는 한 문장만 읽는 것이 아니라 문장을 계속 입력하면 똑같이 계속 출력되게 하는 기능을 구현해보자.
while(조건문)을 이용해 해당 로직을 작성해보았다.
@Test
public void test2() {
BufferedReader a = new BufferedReader(new InputStreamReader(System.in));
try {
String data = a.readLine(); //0
while (data != null) { //2
System.out.println(data); //3
data = a.readLine(); //1
}
} catch (IOException e) {
e.printStackTrace();
}
}
반복문 실행 구조를 보다 쉽게 파악하기 위해 주석을 함께 작성했다.
0에서 문장을 읽고, 2에서 null이 아니면 3으로 넘어가서 문장을 출력한다.
1로 넘어가서 문장을 읽은 뒤 다시 2로 가는데, 여기서 null이 아니면 3으로 넘어가서 다시 문장을 출력하는 것이다.
이렇게 계속 반복하며 문장을 출력하는 구조이다.
. . . . . . .
만약 data에 들어온 값이 null이라면 해당 반복문은 종료될 것이다.
while 반복문이 종료되고 예외는 catch가 잡아서 예외처리가 된다. 하지만 처음 선언한 "BufferedReader a"는 계속 대기 상태로 남아있게 된다.
이 경우 a가 계속 대기 상태로 남아있어 불필요한 자원 소모가 발생한다.
이런 불필요한 자원 소모를 줄이기 위해 java에서는 close( ) 메서드를 제공하고 있다.
.Close( )
Close는 자원을 반환하는 기능을 가지고 있다.
대기 중인 자원이 생길 수 있는 경우에 Close( ) 메서드를 사용하면 '이 자원 이제 그만 쓸게요!' 하고 반납하게 되는 것이다.
try~catch문의 옵션인 finally를 이용하여 Close( )로 자원 반환 로직을 함께 작성해보았다.
finally {
try {
a.close();
} catch (IOException e) {
e.printStackTrace();
}
a = null;
}
전부 합치면 전체 코드는 이렇게 된다.
public void test2() {
BufferedReader a = new BufferedReader(new InputStreamReader(System.in));
try {
String data = a.readLine();
while (data != null) {
System.out.println(data);
data = a.readLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
a.close();
} catch (IOException e) {
e.printStackTrace();
}
a = null;
}
}