본문 바로가기
Java/Java Basic

[Java Basic] 48 - Java I/O Stream1 - Stream 클래스 및 보조Stream

by Rosmary 2022. 10. 5.
728x90
반응형

 

 

 

 

 

이번 포스팅에서는 필자의 Java Basic 마지막 주제인 입출력(Input Output) 스트림, 그 중에서도 입출력 스트림에 사용되는 주 스트림 클래스와 보조스트림 클래스에 대해 알아보려 한다. 앞선 포스팅에서 설명한 Stream과 용어(Terminology)는 동일하지만, 의미하는 바는 다른데, 바로 직전의 포스팅에서 작성한 내용은 Array와 Collections 타입을 조금 더 효율적으로 다루기 위한 방법을 다룬 반면, 이번 포스팅에서는 입력과 출력을 담당하는 하나의 흐름(Stream)을 제어하는 방법에 대한 내용이다. 

 

그럼, 이 포스팅에서 말하는 Stream이란 무엇을 의미하는가에 대해 먼저 알아보아야 한다. 

 

 

 

1. Stream 개요

 

메모장을 열고 키보드로 한 글자를 타이핑 해보자. 키보드 -> 컴퓨터로 입력이 진행된 뒤에, 컴퓨터 -> 모니터로 입력한 글자의 출력이 일어나게 된다. 이 과정에 대해 아주 약간만 상세히 들여다보자.

 

키보드의 입력값을 컴퓨터에서 받으려면, 키보드와 컴퓨터의 연결선(USB 포트 등)을 따라 타이핑을 진행한 키의 전기 신호가 컴퓨터로 이동할 수 있어야 한다. 키보드와 컴퓨터 사이 통신을 위한 연결 선을 Stream(Arduino나 RasberryPi 등 마이크로컨트롤러 분야에서는 버스-Bus-라는 용어를 사용한다)이라고 하는데, 입력을 위해 키보드->컴퓨터로 단방향 통신만 진행하기 때문에 입력 스트림(InputStream)이라고 한다. 

 

반면에 입력값을 인지한 컴퓨터에서 화면에 글자를 출력하기 위해서는, 컴퓨터에서 모니터로 출력할 값에 대한 정보를 보낼 수 있는 선이 필요하다. 이 역시 출력 신호만 컴퓨터 -> 모니터로 전송되기 때문에 이를 출력 스트림(OutputStream)이라고 한다. 

 

필자가 쉬운 예시를 위해 컴퓨터와 모니터를 예시로 들었는데, 파일을 열고 읽는 과정 역시 이와 다르지 않다. 다만 파일의 경우 컴퓨터가 읽어들여야하는 정보가 하드 디스크 내에 저장되어 있기 때문에 하드디스크와 컴퓨터 사이 입력 스트림을 통해 파일 내용을 읽어들인다. 혹은 연산 중 중간 연산값을 메모리에 저장하는 경우, 중간 저장된 값을 읽어들이기 위해 메모리와 컴퓨터 사이 입력 스트림을 사용하기도 하며, 중간 연산값을 메모리에 저장하기 위해 메모리, 컴퓨터 사이 출력 스트림을 사용한다. 

 

즉, 스트림은 특정 정보가 이동하는 통로라 보면 되는데, 단방향 통신만 진행하며 FIFO(First In First Out) 특성을 가진다. 대략적인 그림으로 나타내면 아래와 같다.

 

 

CPU는 JVM과도 동일하다고 볼 수 있다. 그림에 표시하지 않았지만, CPU 내에 존재하는 프로세스 사이의 입출력 스트림도 존재한다.

 

 

 

 

2. 입출력 스트림 관련 클래스 분류

 

이제 위에서 설명한 입출력 스트림을 제어하는 클래스에 대해 알아보자. 필자가 대다수 Java 포스팅에서 소개했던 클래스와 인터페이스는 대부분 java.util 패키지 내에 존재했다. 하지만 입출력 스트림을 제어하는 인터페이스와 클래스는 모두 java.io 패키지에 존재한다. 

 

 

 

하나의 패키지 자체가 입출력 Stream을 다루기 위해 별도로 만들어진만큼, 인터페이스와 클래스의 양도 상당히 많다. 대부분 입력 스트림을 제어하는 클래스는 InputStream 또는 Reader라는 이름을, 출력 스트림을 제어하는 클래스는 OutputStream이나 Writer라는 이름을 포함하고 있다. 

 

각각의 클래스들은 위의 InputStream, Reader, OutputStream, Writer로 일차 분류를 할 수 있는데, 각 분류별로 상속받는 상위 추상클래스가 존재한다. 각각의 추상클래스 이름은 InputStream, Reader, OutputStream, Writer이며, 이들을 상속받는 클래스는 이름 접미사로 이 인터페이스 이름이 포함되어 있는 것이다.

 

InputStream 추상 클래스에 대한 Documentation 내용이다.

 

 

추상 클래스를 구현하는 각 분류의 클래스들 또한 특성에 따라 주요스트림과 보조스트림으로 분류된다. 보조 스트림의 경우 단독으로 사용될 수 없고, 주 스트림의 객체를 매개변수로 받아 인스턴스를 생성하게 된다.

 

[ InputStream 클래스 ]

< 주 Stream 클래스 >     : FileInputStream, PipedInputStream, ByteArrayInputStream, AudioInputStream

< 보조 Stream 클래스 > : FilterInputStream, BufferedInputStream, DataInputStream, SequenceInputStream,                     

                                          LineNumberInputStream, ObjectInputStream, PushbackInputStream

 

[ OutputStream 클래스 ]

< 주 Stream 클래스 >     : FileOutputStream, PipedOutputStream, ByteArrayOutputStream, AudioOutputStream

< 보조 Stream 클래스 > : FilterOutputStream, BufferedOutputStream, DataOutputStream,  ObjectOutputStream,

                                          PrintOutputStream

 

[ Reader 클래스 ]
< 주 Stream 클래스 >     : FileReader, PipedReader, StringReader
< 보조 Stream 클래스 > : BufferedReader, InputStreamReader, CharArrayWriter

[ Writer 클래스 ]
< 주 Stream 클래스 >     : FileWriter, PipedWriter, StringWriter
< 보조 Stream 클래스 > :  BufferedWriter, OutputStreamWriter, CharArrayWriter

 

 

 

상위 추상클래스에 따라 하위 클래스 분류를 진행해보면 InputStream과 Reader, OutputStream과 Writer가 유사한 이름을 중복으로 가지고 있는 것이 확인된다. 두 분류 사이에 기능상의 큰 차이는 없지만, 입출력을 다루는 단위를 1 Byte로 할 것인지, 아니면 2 Byte로 할 것인지에 따라 InputStream, OutputStream과 Reader, Writer로 나뉜다. 1 Byte 기반으로 Stream을 다루는 클래스들을 바이트기반 Stream 클래스라고하며, 2 Byte 기반으로 Stream을 다루는 클래스는 문자기반 Stream 클래스라고 한다. Java에서 문자(char)를 2 Byte로 다룬다는 것을 기억하고 있다면, 이 분류의 기준을 어느 정도 이해할 수 있을 것이다. 

 

Stream 클래스, Reader, Writer 클래스는 상위 추상 매서드를 상속받기 때문에 사용하는 방법은 서로 매우 유사하다. 다만 입출력 대상에 따라 사용하는 클래스가 다르기 때문에, 기본적인 Stream 제어 클래스 사용법에 대해 설명한 뒤에, 하위 클래스에서 추가로 다루어야 할 내용에 대해 조금 더 추가 설명을 진행하려한다.

 

[ 바이트기반 주요 스트림 ]

* FileIput / Output Stream               : 파일 내용의 읽기 및 쓰기 진행 시 사용

* ByteArrayInput / OutputStream   : 메모리 내 내용의 읽기 및 쓰기 진행 시 사용

* PipedInput / OutputStream          : CPU 프로세스 사이 내용의 읽기 및 쓰기 진행 시 사용

* AudioInput / OutputStream          : 오디오 장치에 대한 읽기 및 쓰기 진행 시 사용

 

[ 문자기반 주요 스트림 ]

* FileReader / Writer                        : 파일 내용의 읽기 및 쓰기 진행 시 사용

* StringArrayReader / Wrtier          : 메모리 내 내용의 읽기 및 쓰기 진행 시 사용

* PipedReader / Writer                    : CPU 프로세스 사이 내용의 읽기 및 쓰기 진행 시 사용

 

 

 

3. 바이트기반 주요 스트림

 

바이트기반 스트림 클래스는 스트림 내에 존재하는 정보를 Byte 단위로 제어한다. 즉, 이진법으로 -128~127 사이 값을 처리할 수 있다.

 

우선 Byte 배열을 하나 만들어 입력 스트림과 출력 스트림을 다루는 예시를 살펴보자.

 

 

 

 

Byte 배열을 하나 만들어 범위 내 숫자 몇 개를 저장하고, 이를 InputStream에서 제어하기 위해 ByteArrayInputStream 클래스의 인스턴스를 생성했다. ByteArrayInputStream은 Byte[] 배열을 매개변수로 받는데, 인스턴스가 생성되는 순간 매개변수로 받은 배열을 메모리 InputStream에서 제어할 수 있게 된다. 

 

배열의 첫 요소를 읽어보자. 이와 관련된 매서드는 read()다. 

 

 

read() 매서드로 첫 요소가 반환된 것이 확인된다. 계속해서 read() 매서드를 사용해보자.

 

 

 

InputStream에 들어온 배열의 각 요소는 read() 매서드로 인해 순서대로 읽히게 된다. 만약 InputStream에 아무 값이 남아있지 않은 상태인데 read() 매서드가 호출되는 경우, -1 값이 반환된다. 이를 System.out 이라는 표준 출력 스트림을 사용하여 화면 출력을 진행했기 때문에 Eclipse 콘솔 창에 각 요소의 값이 출력되는 것이다.

 

그럼, InputStream에 출력할 내용이 남아있는지 미리 확인할 수 있는 방법은 없을까? 이는 available()이라는 매서드를 사용하면 되는데, available()은 읽어들일 수 있는 대상의 수를 반환한다. 현재의 예시에서는 Byte 기반으로 Stream 내용을 읽어들이기 때문에 Stream에 남아있는 요소 수 만큼 available 값으로 반환된다.

 

 

 

available과 read()를 적절히 사용하면 루프문을 사용하여 Stream의 요소를 모두 출력하는 코드를 간략히 작성하는 것이 가능하다.

 

 

 

모든 요소를 출력하는 것 외에도, 특정 지점을 마킹한 뒤 추후에 다시 해당 지점에서 입력 Stream의 요소를 다시 출력하도록 만드는 매서드도 있다.

 

 

 

mark()와 reset() 매서드는 메모리 InputStream 내의 특정 데이터를 다시 돌아갈 수 있는 지점으로 표시하고 되돌아갈 수 있도록 하는 기능을 한다. 무언가 반복적인 작업이 필요하다면 이 매서드를 사용할 수 있을 듯 한데, 그 외에는 어디에 사용해야할지 필자가 감이 잘 오지 않는다.

 

 

 

만약 inputStream의 요소들을 별도의 배열에 저장하고 싶다면, read()의 오버로딩 매서드를 사용하면 된다.

 

 

 

 

만약 저장할 배열의 크기를 줄인다면, read() 매서드는 매개변수 배열 크기만큼만 InputStream의 데이터를 저장한다. 

 

 

 

read() 매서드를 다시 사용하게 되는 경우, 다음 3개의 요소가 result 배열에 저장된다. 

 

 

 

 

만약 저장할 배열의 특정 위치에, 특정 개수의 요소만 저장하고자 한다면 read()의 오버로딩 매서드를 사용해야한다.

 

read (byte [] 저장배열변수, int 배열저장시작위치index, int 저장요소개수) 

 

 

 

이 read() 매서드 사용 시 유의해야 할 점은, 저장할 배열의 index를 초과하는 요소 수가 들어오는 경우 IndexOutOfBound Exception이 발생한다는 것이다. 

 

InputStream의 초기 요소 몇 개를 건너뛰는 skip()이라는 매서드도 존재한다.

 

 

 

 

다음으로 OutputStream도 살펴보자. 필자는 위의 예시를 그대로 사용할 예정이기 때문에 메모리의 출력 스트림을 제어하기 위한 클래스인 ByteArrayOutputStream 클래스를 인스턴스화 할 것이다. 그리고 필자는 InputStream에서 읽어들인 내용을 OutputStream으로 새 배열에 저장하려한다.

 

 

 

코드는 간단하다. 먼저 출력스트림을 제어하기 위해 ByteArrayOutputSream의 인스턴스를 생성한다. 출력스트림은 외부로부터 받아들여야 하는 값이 별도로 존재하지 않기 때문에 기본 생성자를 사용하여 인스턴스를 생성하면 된다. 입력스트림으로부터 읽어들인 요소를 outputStream.write() 매서드를 사용하여 하나씩 출력 스트림에 저장해준다. 모든 요소에 대해 출력 스트림 저장이 완료되면, toByteArray() 매서드를 사용하여 byte 배열인 result에 저장한다.

 

write() 역시 오버로딩 매서드가 read()와 동일하게 존재한다. 하지만 read()의 매개변수인 byte 배열이 입력스트림 요소의 저장과 관련된 내용이라면, write()의 매개변수 byte 배열은 값을 출력할 요소가 저장된 배열과 관련이 있다.

 

 

 

 

현재는 필자가 test 배열의 수와 tmp 배열 수가 나머지 0값으로 떨어질 수 있게 크기를 조절해서 아무런 문제가 없지만, 만약 tmp 배열의 크기를 2로 변경하면 result에 저장되는 값의 결과가 의도한 것과 다르게 변한다.

 

 

 

tmp에 저장된 모든 요소를 출력스트림에 저장하다보니 발생한 일인데, 출력스트림에 저장할 tmp 요소의 개수를 write() 매서드에서 제한해주면 이 문제는 해결된다.

 

 

 

File과 관련한 입출력 스트림을 다루는 FileInputStream과 FileOuputStream 역시 사용 방법에 있어서 큰 차이는 없다. 다만 입력스트림 생성자가 ByteArray에 비해 다양하므로, Documentation으로 어떻게 사용하는지에 대해 먼저 확인해야한다.

 

test.txt 내용은 Alexandr Rybak의 'If you were gone'의 가사다.

 

 

파일의 문자를 Byte 기반 Stream 클래스로 작업을 진행하니, read() 결과가 ASCII 코드로 출력되는 것이 확인된다. Byte의 범위를 양수로 한정할 경우, 0~255 범위의 수가 표시될 수 있는데, 이 수를 2 Byte 형태로 변환한다고 해도 데이터 손실이 일어나지 않기 때문에 0~255 범위 내에서 표시될 수 있는 문자로 작성된 파일이라면 FileInput/OutputStream 클래스를 사용하더라도 큰 문제가 발생하지 않는다. 

 

File의 입력 스트림 내용을 일괄로 출력스트림에 저장하는 방법이 하나 있는데, FileInputStream에서 제공하는 transferTo() 매서드다. 이 매서드는 매개변수로 출력스트림 객체를 받아들이며, 파일에 쓸 값을 입력스트림으로부터 읽어들어와 매개변수로 지정된 출력 스트림에 저장하는 역할을 한다.

 

 

 

FileInputStream과 FileOutputStream은 매개변수로 지정한 파일이 존재하지 않는 경우, FileNotFoundException의 발생을 방지하기 위해 try-catch 문을 사용하여 예외처리를 반드시 진행해주어야 한다.

 

 

 

 

4. 바이트기반 보조스트림

 

주요 스트림 외에 보조스트림이 존재한다고 위에서 언급했는데, 보조스트림은 주 스트림의 기능과 효율을 강화하기 위한 스트림이다. 따라서 주요 스트림과 분리하여 사용이 불가능하다. 

 

보조스트림도 바이트 기반과 문자 기반 스트림으로 분류되는데, 바이트기반 스트림으로 분류되는 클래스는 아래와 같다.

 

* FilterInputStream / FilterOutputStream               :  바이트기반 보조스트림의 조상 클래스. 생성자가 protected로

                                                                                      정의되어 직접 인스턴스 생성과 사용은 불가.

* BufferedInputStream / Buffered OutputStream  :  여러 바이트에 대한 입출력을 진행할 수 있는 보조스트림 클래스

* DataInputStream / DataOutputStream               : 데이터를 기본형 타입으로 처리하는 보조스트림 클래스

* SequenceInputStream                                         : 두 스트림을 하나로 연결하는 보조스트림 클래스

* LineNumberInputStream                                     : 입력스트림으로부터 읽은 데이터를 라인 번호로 카운트하는 기능을

                                                                                  가지는 보조스트림 클래스

* ObjectInputStream / ObjectOutputStream        : 데이터를 객체 타입으로 처리하는 보조스트림 클래스  

 

FilterInputStream과 FilterOutputStream을 제외한 나머지 클래스는 조상 클래스 역할을 하는 FilterInputStream과 FilterOutputStream을 상속받으며, 상속자와 필드, read() 매서드에 대해 별도 정의가 되어 있다.

 

모든 클래스의 사용 방법은 비슷하니, Buffered와 Data 보조스트림 클래스만 추가 설명을 진행하려한다.

 

 

 

(1) BufferedInput / OutputStream

 

먼저 BufferedInput/OutputStream부터 살펴보자.

 

BufferedInput/OutputStream 클래스는 바이트기반의 입력 스트림에서 데이터를 읽을 때 1 Byte 단위를 사용하면서 나타나는 비효율성을 개선하기 위한 클래스다. 파일을 예로 들면, ByteArrayInputStream은 단순히 파일에서 1 Byte를 추출하여 입력스트림에 저장하는 반면, BufferedInputStream이 적용된 상태에서는 특정 크기의 별도 버퍼를 만들고, 파일에서 지정한 Byte를 버퍼로 추출한 뒤, read() 매서드가 호출되면 입력 스트림에 저장하게 된다. 출력스트림의 경우도 마찬가지로 내부 버퍼를 사용하는데, write() 매서드는 반드시 버퍼가 가득 차 있는 상태에서만 버퍼의 데이터를 출력시킨다는 차이가 있다.

 

ByteArrayStream을 단독으로 사용하는 것보다 BufferedStream을 사용하면, 내부의 버퍼를 사용하기 때문에 파일과 스트림 사이 전환 횟수가 감소하여 속도도 개선된다.

 

 

 

사용 방법은 주요 스트림 클래스와 별반 다르지 않다. 다만, 생성자로 인스턴스 선언 시, 데이터를 읽어들일 혹은 쓰기위한 크기를 Byte 단위로 매개변수 지정을 해주어야 한다. 매개변수를 지정하지 않는 경우 기본값으로 8192 Byte(8 kB)가 적용된다. 

 

BufferedOutputStream은 지정된 버퍼에 데이터가 가득 차야만 출력이 진행되는데, 위의 예시를 보면 마지막 index인 9가 전체 크기 4 Byte인 버퍼를 채우지 못했기 때문에 result 배열로 출력되지 않은 것이 확인된다. 이렇게 출력 버퍼에 데이터가 가득차지 않은 경우라도 출력을 진행해야하는 경우, BufferedOutputStream 클래스에서 제공하는 close() 또는 flush() 매서드를 사용하면 된다. 이 매서드들은 지정된 출력버퍼에 남아있는 글자를 출력으로 전환하도록 유도한다. BufferedOutputStream의 특성 상, 반드시 마지막에는 close(), flush() 매서드를 사용하여 버퍼 내 저장된 정보를 삭제해주는 것을 습관화 할 필요가 있다.

 

 

 

 

(2) DataInputStream / DataOutputStream 

 

DataInput / OutputStream은 읽어들여오거나 쓰기를 진행할 데이터의 타입을 변환하는 기능을 가지는 매서드가 포함되어 있다. 직접적으로 데이터를 형변환하는 것이 아니라, 입력스트림 혹은 출력스트림 내에 저장된 정보를 각 타입의 크기만큼 추출하여 변환하는 방식을 사용한다. 아래의 예를 보자.

 

 

 

byte 배열 내 요소들은 byte 단위의 크기로 지정되어 있으므로, DataInputStream에서 read() 또는 readByte() 매서드를 사용하게 되면 각 요소(1 Byte) 단위로 출력이 이루어진다. 만약 필자가 readShort() 매서드를 호출하게되면, 입력 스트림의 요소를 2 Byte 단위로 호출하게되므로, 첫 readByte() 매서드 값은 이진 수 00000000 00000000 값인 0이 출력된다. 같은 방식으로 두 번 째 호출 시 1이, 세 번째 호출 시 256, 마지막 호출 시 257이 출력된다.

 

 

 

출력 역시 마찬가지다. write() 매서드는 기본 Byte 단위로 출력을 진행하며, write의 파생 매서드를 통해 출력할 데이터를 추출할 단위를 지정할 수 있다. 

 

 

 

DataInput / OutputStream의 경우 기본 타입의 변환이 가능하기 때문에 Byte 단위로만 데이터를 저장할 수 있는 ByteArrayInput / OutputStream과 함께 사용되는 일이 드물다.

 

 

 

얼핏 보면, 기본 Byte로만 잘 동작하는 것처럼 보이기 때문에 효용성이 매우 떨어지는 것처럼 보일 수 있는데, 무언가 은밀한 데이터를 보낼 때 DataInputStream과 DataOutputStream을 사용하면 꽤 유용할 듯 하다. 아래와 같이 인적사항을 입력하는 코드를 하나 만들고, 이 데이터를 Byte로 변환해서 저장한다고 해보자. 그리고 Byte로 변환된 자료를 출력해보자.

 

 

 

만약 Byte로 변환된 데이터를 제대로 읽어야 하는 상황이 발생한다면 어떻게 해야할까? read() 혹은 read() 계열 매서드만 단독으로 사용해서는 이 자료의 원본 내용을 절대 알기 어렵다.

 

 

 

DataInputStream으로 입력된 타입대로 출력이 진행되어야 변환된 자료를 정확히 확인할 수 있다. 위의 예시도 2 Byte인 문자열 요소야 출력에 문제가 없지만 4 Byte 단위인 int와 8 Byte 단위인 Double은 자신들의 데이터가 조각조각 난 뒤 표시되기 때문에 제대로 된 정보가 표시되지 않는 것이다.

 

 

 

 

 

 

5. 문자 기반 주요 스트림과 보조 스트림

 

문자 기반 스트림 계열의 클래스 역시 바이트 스트림 계열 클래스와 사용법은 별반 다르지 않다. 차이가 있다면, 클래스 이름이 InputStream -> Reader, OutputStream -> Writer로 지정되었다는 것과, 스트림 데이터를 2 Byte 단위로 읽거나 쓴다는 차이만 있다.

 

문자 기반 스트림은 '문자(char)' 단위로 데이터를 다루기 때문에 인코딩 정보를 포함하게 되는데, Java의 문자 기반 스트림은 Java에서 사용하는 UTF-16과 다른 인코딩 종류의 변환을 자동으로 처리해준다. 따라서 바이트 기반 스트림에서는 간간히 처리가 어려운 한글도 문자 기반 스트림에서 문제없이 진행할 수 있게 된다.

 

 

 

Byte 기반 스트림에서는 1 Byte 단위로 데이터를 읽기 때문에 한글 유니코드값을 읽기가 쉽지 않다. 하지만 문자 기반 스트림은 2 Byte 단위로 데이터를 읽으므로 유니코드값도 무리없이 입력 스트림으로 저장할 수 있다.

 

 

 

 

문자 기반 보조 스트림에는, 입출력 버퍼를 이용하는 BufferedReader와 BufferedWriter 클래스 및 바이트 기반 스트림을 문자 기반 스트림으로 연결하는 InputStreamReader와 OutputStreamWriter 클래스를 예로 들 수 있다. BufferedReader와 Writer는 BufferedInputStream / OutputStream과 사용 방법이 동일하기 때문에 큰 설명이 필요없을 듯 하며, InputStreamReader / OutputStreamWriter 또한 문자 인코딩과 관련된 내용인데, 마땅한 예시도 없을 뿐더러 요즘 왠만한 외국 글자는 다 표시되기 때문에 크게 사용 빈도가 높은 클래스가 아니라 설명을 생략하고 넘어가려한다.

 

 

 

 

6. 표준스트림 - System.in, System.out, System.err

 

지금까지 입력값을 받기 위해 Scanner 생성자 인스턴스로 System.in을 항상 코드에 기입했었고, 화면에 글자를 출력하기 위해 System.out을 항상 입력했었다. 대략적으로 눈치채신 분들이 있겠지만, Java와 콘솔(CMD 창) 사이 입력 / 출력스트림이 바로 System.in과 System.out이다. System 클래스 내부에 in, out이 각각 InputStream과 PrintStream(OutputStream의 바이트 기반 클래스 일종이다)객체로 지정되어 있다. 따라서 System.in으로 키보드로 타이핑 한 값을 JVM(CPU)에 입력할 수 있게 되며, System.out 출력스트림을 통해 JVM 메모리에 저장된 값을 콘솔로 출력할 수 있게 된다.

 

System.in, System.out의 사용 예시. System.in.read() 매서드는 입력받은 값을 Byte로 변환하여 저장한다. InputStream 객체라는 것을 기억하자.

 

 

추가로 에러 출력을 위한 출력 스트림인 System.err도 존재한다. 이 스트림 역시 System.out과 마찬가지로 PrintStream 객체지만, 단지 에러에 대한 내용을 출력한다는 것을 제외하면 System.out과 큰 차이가 없다.  

 

 

 

System 클래스에는 입출력 스트림을 변경할 수 있는 매서드인 setIn(), setOut(), setErr() 매서드가 정의되어 있다. 이들 매서드는 매개인자로 PrintStream(setOut(), setErr()) 또는 inputStream 객체를 받아들이며, 매개인자로 지정된 스트림으로 표준 입력과 출력을 진행하게 된다.

 

참고로 바이트 기반으로 파일을 읽고 썼기 때문에, 영문을 제외한 나머지 글자는 오버플로우로 인해 정상적인 값이 출력되지 않는다.

 

 

 

 

 


 

사실 입출력 스트림은 기능에 따른 클래스 종류가 너무 많아 하나의 포스팅으로 작성하려면 스크롤 압박이 너무 커진다. 필자의 Java 포스팅을 처음부터 참고해서 여기까지 오신 분들이라면 이제 Documentation 정보를 이해하는데 큰 문제가 없을테니 필자가 언급하지 않은 클래스의 사용법에 대해서는 Documentation을 찾아보기를 권한다.

 

다음은 Java의 마지막 포스팅이다. 객체의 입출력 스트림을 제어하기 위한 직렬화와, 파일을 다루는 Stream 클래스에 대해 간략하게 설명하려한다.

 

 

 

 

Fin.

반응형

댓글