자바에서 데이터를 스트림을 통해 입출력된다. 이때, 스트림이란 데이터가 이동하는것을 지칭한다. 자바에서는 당연히 클래스를 통해 스트림을 사용할 수 있고, java.io패키지에 저장되어있다.
표준 입출력 이외에 스트림은 크게 바이트기반 스트림과 문자 기반 스트림으로 구분된다.
public final class System {
...
public **static** final InputStream in = null;
...
}
호스트의 기능 및 시스템과 관련된 기능들을 사용할 수 있게 하는 클래스.
특히 여기에서는 JVM이라는 가상머신에서 호스트 컴퓨터의 입출력장치와 스트림을 만들어주는 System.in
, System.out
, System.err
와 같은 필드를 주로 사용할것이다.
참고 : Oracle Docs - System, Morph's House
public static final InputStream in = null;
System.in은 표준 입력 스트림이다. 자바에서 콘솔을 통해 입력을 받을때는 System.in 정적 필드를 사용한다. 다른 입출력 스트림과 마찬가지로 java.in에서 import하며, InputStream타입이다.
System.in을 InputStream 변수에 참조시켜 사용하거나, 보조 스트림에 대입하여 사용.
+) 예외처리문(try-catch문 안에서 사용해야한다)
→ 방향이 없는 쌩 스트림에 System.in을 대입하여 표준입력으로 방향을 만들어 준다고 생각.
package systeminexer;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
public class SystemInTest {
public static void main(String[] args) {
InputStream iS = System.in;
Reader reader = new InputStreamReader(iS);
BufferedReader br = new BufferedReader(reader);
try {
String lineStr = br.readLine();
System.out.println(lineStr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(뒤에서 다룰 내용들이긴하지만 간략하게.)
InputStreamReader객체에 System.in필드를 매개변수로 넣어서, 표준입력과 연결시키고 Reader객체에 연결한다. Reader를 이용하면 1byte대신 2byte의 문자(한글)을 받을 수 있게된다. 이 reader를 다시 BufferedReader객체에 넣어주면 버퍼를 사용할 수 있게 된다.
package stream.inputstream;
import java.io.IOException;
public class SystemInTest {
public static void main(String[] args) throws IOException {
System.out.println("Input Test!");
int input;
try {
while ((input = System.in.read()) != -1) {
// System.out.flush();
if (input != 10)
System.out.println((char)input);
else // 입력받은 문자가 개행이면, 입력받는 것을 종료
break;
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("The End!");
}
}
Input Test!
123
1
2
3
The End!
// 생성자
public Scanner(InputStream source) {
this(new InputStreamReader(source),WHITESPACE_PATTERN);
}
java.util에 있는 클래스. 생성자의 매개변수로 InputStream를 받는다. InputStream형의 source를 InputStreamReader로 다시 인스턴스화하여 넘겨주기 때문에, 2byte의 한글도 읽을 수 있다. 그리고 입력종료 시점(구획 문자)을 공백문자로 지정한다(WHITESPACE_PATTERN).
+) 위의 코드에서 this는 아래처럼 private로 숨겨져있는 생성자이다.
private Scanner(Readable source, Pattern pattern) {
...
}
Scanner은 next...메소드 시리즈를 통해 입력을 받는다. 이때 내부에서 정규식을 깐깐하게 검사하고 입력으로 넣어주게 되므로 속도가 상당히 느리다고 한다...
참고 및 자세한 정보 : st-lab 자바 입력 뜯어보기
next자료형() - nextBoolean(), nextInt(), nextString()...
들어오는 입력(토큰)을 해당 자료형으로 해석하여 반환한다.
next(), next(Pattern pattern)
입력받은 String을 공백문자(스페이스 포함) 직전까지 그대로 반환하는 메소드. 이때 공백문자는 버퍼에 그대로 남아있게된다. pattern을 매개변수로 넣으면 패턴과 일치하는 경우 문자열을 반환한다.
nextLine()
개행을 만날때까지 입력받은 String을 반환하는 메소드. 개행까지 버퍼에서 읽지만, 출력은 하지 않는듯하다.
hasNext자료형()
입력된 내용이 해당 자료형으로 해석될 수 있으면 true를 반환, 아닌 경우 false를 반환.
참고 - 버퍼에 의한 next()와 nextLine()사용시 문제점
package scanner.exer;
import java.io.InputStreamReader;
import java.util.Scanner;
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String a = scanner.next();
String b = scanner.nextLine();
String c = scanner.next();
System.out.println(">> a : " + a);
System.out.println(">> b : " + b);
System.out.println(">> c : " + c);
}
}
// 입력
aaa
bbbb
>> a : aaa
>> b :
>> c : bbbb
next()는 버퍼를 읽고, 개행직전까지 내용을 가져온다. 따라서 버퍼에는 개행이 그대로 남아있어서 nextLine()에서 버퍼를 읽었을 때 바로 개행을 만나게 되어 입력을 의도하지않은대로 받게 된다.
System.in
을 사용하지 않고 console 창 내용을 읽을 수 있는 클래스. console에서 직접 입력받을 때 사용. 이 클래스를 사용했을 때는 평소처럼 run
하지 않고 콘솔창에서 class 파일을 직접 실행시킨다. 한글도 깨지지 않고 읽을 수 있다.
메서드 | 설명 |
---|---|
String readLine() | 문자열을 읽습니다. |
char[] readPasword() | 사용자에게 문자열을 보여주지 않고 읽습니다. |
Reader reader() | Reader 클래스를 반환합니다. |
PrintWriter writer() | PrintWriter 클래스를 반환합니다. |
/Users/jiwonwoo/IdeaProjects/inout/out/production/inout/stream/inputstream/ConsoleTest
package stream.inputstream;
import java.io.Console;
public class ConsoleTest {
public static void main(String[] args) {
Console console = System.console();
System.out.print("이름 : ");
String name = console.readLine();
System.out.print("직업 : ");
String job = console.readLine();
System.out.print("비밀번호 : ");
char[] pass = console.readPassword();
String strPass = new String(pass);
System.out.println(name);
System.out.println(job);
System.out.println(pass);
}
}
# 클래스 파일이 속해있는 패키지의 상위폴더로 이동
jiwonwoo@MacBookAir inout % pwd
/Users/jiwonwoo/IdeaProjects/inout/out/production/inout
# 패키지 이름까지 포함하여 클래스 파일 실행
jiwonwoo@MacBookAir inout % java stream.inputstream.ConsoleTest
# 콘솔에서 입력받는 부분
이름 : 안녕
직업 : 학생
비밀번호 :
# 입력 받은 것 그대로 출력
안녕
학생
12345
바이트 단위 입력을 위한 최상위 입출력 스트림 클래스.
메서드 | 설명 |
---|---|
read() | 입력 스트림으로부터 1바이트를 읽고 읽은 바이트를 리턴합니다. |
read(byte[ ] b) | 입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열b에 저장하고 실제로 읽은 바이트 수를 리턴합니다. |
read(byte[] b, int off, int len) | 입력 스트림으로부터 len개의 바이트만큼 읽고 매개값으로 주어진 바이트 배열 b[off]부터 len개까지 저장합니다. 그리고 실제로 읽은 바이트 수인 len개를 리턴합니다. 만약 len개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴합니다. |
close() | 사용한 시스템 자원을 반납하고 입력스트림을 닫습니다. |
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class InputStreamTest1{
public static void main(String []args)
{
int i = 0;
InputStream inputStream = null;
// OutputStream outputStream = System.out;
try{
inputStream = System.in;
while ((i = inputStream.read()) != -1)
{
System.out.println((char)i);
}
}
catch(IOException e)
{
System.out.println(e);
}
}
}
바이트 단위 출력을 위한 최상위 입출력 스트림 클래스.
메서드 | 설명 |
---|---|
write(int b) | 출력 스트림으로부터 1바이트를 보냅니다.(b의 끝 1바이트) |
write(byte[ ] b) | 출력 스트림으로부터 주어진 바이트 배열 b의 모든 바이트를 보냅니다. |
write(byte[ ] b, int off, int len) | 출력 스트림으로 주어진 바이트 배열 b[off]부터 len개까지의 바이트를 보냅니다. |
flush() | 버퍼에 잔류하는 모든 바이트를 출력합니다. |
close() | 사용한 시스템 자원을 반납하고 출력 스트림을 닫습니다. |
입력 스트림으로 부터 b[]
크기의 자료를 b[]
의 off 변수 위치부터 저장하며 len만큼 읽습니다. 읽은 자료의 바이트 수를 반환합니다.
import java.io.*;
public class OutputStreamTest{
public static void main(String []args)
{
byte[]bs = new byte[26];
byte data = 65;
for (int i =0; i < bs.length; i++)
{
bs[i] = data++;
}
try (OutputStream fos = System.out)
{
fos.write(bs);
}
catch(IOException e)
{
System.out.println(e);
}
System.out.println("end");
}
}
ABCDEFGHIJKLMNOPQRSTUVWXYZ
스트림 클래스 | 설명 |
---|---|
FileInputStream | 파일에서 바이트 단위로 자료를 읽습니다. |
ByteArrayInputStream | Byte 배열 메모리에서 바이트 단위로 자료를 읽습니다. |
FilterInputStream | 기반 스트림에서 자료를 읽을때 추가기능을 제공하는 보조 스트림의 상위 클래스 입니다. |
메서드 | 설명 |
---|---|
int read() | 입력 스트림으로 부터 한 바이트의 자료를 읽습니다. 읽은 자료의 바이트 수만큼 반환 합니다. |
int read(byte b[]) | 입력 스트림으로 부터 b[]크기의 자료를 b[]에 읽습니다. 읽은 자료의 바이트 수를 반환 합니다. |
int read(byte b[], int off, int Len) | 읽은 자료의 바이트 수를 반환합니다.,입력 스트림으로 부터 b[]크기의 자료를 b[]의 off 변수 위치부터 저장하며 len만큼 읽습니다. |
void close() | 입력 스트림과 연결된 대상 리소스를 닫습니다. |
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
public class FileInputStreamTest
{
public static void main(String[]args)
{
FileReader fis = null;
int i = 0;
try{
fis = new FileReader("input.txt");
while ((i = fis.read()) != -1)
{
System.out.println((char)i);
}
}
catch(IOException e)
{
System.out.println(e);
}
finally
{
try{
fis.close();
}
catch(IOException e)
{
System.out.println(e);
}
catch(NullPointerException e)
{
System.out.println(e);
}
}
// System.out.println("end");
}
}
abcasdfa asd
asdf a
adfadf ad
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamTest2{
public static void main(String[]args)
{
try(FileInputStream fis = new FileInputStream("input2.txt"))
{
byte []bs = new byte[10];
int i = 0;
while ((i = fis.read(bs)) != -1)
{
// for (byte b : bs)
// {
// System.out.println((char)b);
// }
for (int j = 0; j < i; j++)
{
System.out.println((char)bs[j]);
}
}
}catch (IOException e)
{
System.out.println(e);
}
}
}
abcdefghijklmnopadfi
스트림 클래스 | 설명 |
---|---|
FileOutputStream | 바이트 단위로 파일에 자료를 씁니다. |
ByteArrayOutputStream | Byte 배열에 바이트 단위로 자료를 씁니다. |
FilterOutputStream | 기반 스트림에서 자료를 쓸때 추가 기능을 제공하는 보조 스트림의 상위클래스 |
메서드 | 설명 |
---|---|
void write(int b) | 한 바이트를 출력 |
void write(byte[] b) | b[] 배열에 있는 자료 출력 |
void write(byte[] b, int off, int Len) | b[] 배열에 있는 자료의 off 위치부터 |
void flush() | 출력을 위해 잠시 자료가 머무르는 출력 버퍼를 강제로 비워 자료를 출력 |
void close() | 출력 스트림과 연결 대상 리소스를 닫습니다. 출력 버퍼가 비워짐 |
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutStreamTest{
public static void main(String[]args)
{
byte [] bs = new byte[26];
byte data = 65;
for (int i = 0; i < bs.length; i++)
{
bs[i] = data++;
}
try (FileOutputStream fos = new FileOutputStream("output2.txt"))
{
fos.write(bs);
}
catch(IOException e)
{
System.out.println(e);
}
System.out.println("end");
}
}
ABCDEFGHIJKLMNOPQRSTUVWXYZ
import java.io.*;
import org.w3c.dom.ls.LSException;
public class InputStreamTest
{
public static void main(String args[]) throws IOException {
System.out.println("아무 글이나 입력하시고 Enter를 쳐주세요");
System.out.println(" 'S'를 입력하면 프로그램이 종료됩니다.");
int ch;
InputStream in = System.in;
OutputStream out = System.out;
while((ch=in.read()) != -1) {
if(ch == 'S') {
byte[] arr= new byte[4];
arr[0] = 83;
arr[1] = 84;
arr[2] = 79;
arr[3] = 80;
out.write(arr);
out.flush();
out.close();
in.close();
System.exit(-1);
}
System.out.println("Char: "+(char)ch+", Available: "+in.available());
}
}
}
문자(char) 단위로 읽는 스트림 중 최상위 스트림. 바이트 스트림이 바이트 단위로 읽는 것과 달리 문자는 char형의 크기, 2byte 씩 읽으므로, 한글도 깨지지 않고 잘 읽을 수 있게 된다.
스트림 클래스 | 설명 |
---|---|
FileReader | 파일에서 문자 단위로 읽는 스트림 클래스 |
InputStreamReader | 바이트 단위로 읽은 자료를 문자로 변환해주는 보조 스트림 클래스 |
BufferedReader | 문자로 읽을 때 배열을 제공하여 한꺼번에 읽을 수 잇는 기능을 제공해주는 보조 스트림 클래스 |
메서드 | 설명 |
---|---|
int read() | 파일로부터 한 문자를 읽고 읽은 값을 반환한다 |
int read(char[] buf) | 파일로부터 최대 buf 길이만큼 문자를 읽어서 buf에 저장,읽은 길이를 반환 |
int read(char[] buf, int off, int len) | 파일로부터 최대 buf 길이만큼 문자를 읽어서 buf에 저장,단 buf[off] 부터 len개의 문자를 저장,읽은 길이를 반환 |
void close() | 스트림과 연결된 파일 리소스를 닫는다 |
메서드 | 설명 |
---|---|
FileReader(String name) | 파일 이름(경로 포함)을 매개변수로 받아 입력 스트림 생성 |
FileReader(File f) | File 클래스 정보를 매개변수로 받아 입력 스트림 생성 |
FileReader
생성자의 매개변수가 경로를 포함한 파일 이름이라면 현재 작업 디렉토리 이후부터 명시하면 된다.
// 현재 작업 디렉토리 구하는 방법
String path = System.getProperty("user.dir");
System.out.println("Working Directory = " + path);
Working Directory = /Users/jiwonwoo/IdeaProjects/inout
input.txt
hello
hi
this is input
현재 input.txt
의 절대 경로는 아래와 같다.
/Users/jiwonwoo/IdeaProjects/inout/out/production/inout/stream/inputstream/input.txt
package stream.inputstream;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
try(FileReader fr = new FileReader("out/production/inout/stream/inputstream/input.txt")) {
int i;
while((i = fr.read()) != -1) {
System.out.print((char)i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
hello
hi
this is input
package stream.inputstream;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
try(FileReader fr = new FileReader("out/production/inout/stream/inputstream/input.txt")) {
int input;
char[] buf = new char[5];
while((input = fr.read(buf)) != -1) {
// System.out.print((char)input);
for (int i = 0; i < input; i++)
System.out.print(buf[i]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
hello
hi
this is input
buf의 길이와 read한 문자의 개수는 다를 수 있다.
package stream.inputstream;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
try(FileReader fr = new FileReader("out/production/inout/stream/inputstream/input.txt")) {
int input;
char[] buf = new char[5];
int off = 2;
int len = 3;
buf[0] = '0';
buf[1] = '1';
buf[2] = '2';
buf[3] = '3';
buf[4] = '4';
while((input = fr.read(buf, off, len)) != -1) {
System.out.println("input : " + input);
System.out.print("buf : ");
for (int i = 0; i < off + input; i++)
System.out.print(buf[i]);
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
input : 3
buf : 01hel
input : 3
buf : 01lo
input : 3
buf : 01hi
input : 3
buf : 01thi
input : 3
buf : 01s i
input : 3
buf : 01s i
input : 3
buf : 01npu
input : 1
buf : 01t
문자 단위로 출력하는 스트림 중 최상위 스트림.
스트림 클래스 | 설명 |
---|---|
FileWriter | 파일에서 문자 단위로 출력하는 스트림 클래스 |
OutputStreamWriter | 파일에 바이트 단위로 출력한 자료를 문자로 변환해주는 보조 스트림 클래스 |
BufferedWriter | 문자로 쓸 때 배열을 제공하여 한꺼번에 쓸 수 있는 기능을 제공해주는 보조 스트림 |
메서드 | 설명 |
---|---|
int write(int c) | 한 문자를 파일에 출력 |
int write(char[] buf) | 문자 배열 buf의 내용을 파일에 출력 |
int write(char[] buf, int off, int len) | 문자 배열 buf의 off 인덱스 부터 len 개의 문자를 파일에 출력 |
int write(String str) | 문자열 str을 파일에 출력 |
int write(String buf, int off, int len) | 문자열 str의 off 번째 문자부터 len개의 문자를 파일에 출력 |
void flush() | 파일에 출력하기 전에 자료가 있는 공간(출력 버퍼)를 비워 출력 |
void close() | 파일과 연결된 스트림을 닫고 출력 버퍼를 비움 |
메서드 | 설명 |
---|---|
FileWriter(String name) | 파일 이름(경로 포함)을 매개변수로 받아 출력 스트림 생성 |
FileWriter(String name, boolean append) | 파일 이름(경로 포함)을 매개변수로 받아 출력 스트림 생성. append 값이 true면 파일 스트림을 닫고 다시 생성할 때 파일의 끝에서 이어서 출력하고, false면 같은 상황에서 파일을 덮어씀 |
FileWriter(File f) | File 클래스 정보를 매개변수로 받아 출력 스트림 생성 |
FileWriter(File f, boolean append) | File 클래스 정보를 매개변수로 받아 출력 스트림 생성. append 값이 true면 파일 스트림을 닫고 다시 생성할 때 파일의 끝에서 이어서 출력하고, append 값이 false면 같은 상황에서 파일을 덮어씀 |
package stream.inputstream;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
try(FileWriter fw = new FileWriter("writer.txt", false)) {
char[] buffer = new char[26];
for(int i = 0; i < buffer.length; i++)
buffer[i] = (char)(65 + i);
fw.write(97);
fw.write("\n");
fw.write(buffer);
fw.write("\n");
fw.write(buffer, 5, 5);
fw.write("\n");
fw.write("한글한글");
fw.write("안녕하세요", 0, 2);
} catch (IOException e) {
System.out.println("Error!");
} finally {
System.out.println("The End!");
}
}
}
writer.txt
a
ABCDEFGHIJKLMNOPQRSTUVWXYZ
FGHIJ
한글한글안녕
package stream.inputstream;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
try(FileWriter fw = new FileWriter("writer.txt", true)) {
char[] buffer = new char[26];
for(int i = 0; i < buffer.length; i++)
buffer[i] = (char)(97 + i);
fw.write(65);
fw.write("\n");
fw.write(buffer);
fw.write("\n");
fw.write(buffer, 0, 10);
fw.write("\n");
fw.write("한글한글");
fw.write("안녕하세요", 2, 3);
} catch (IOException e) {
System.out.println("Error!");
} finally {
System.out.println("The End!");
}
}
}
writer.txt
a
ABCDEFGHIJKLMNOPQRSTUVWXYZ
FGHIJ
한글한글안녕A
abcdefghijklmnopqrstuvwxyz
abcdefghij
한글한글하세요
이미지 출처 : https://kkk-kkk.tistory.com/
직접 읽거나 쓰는 기능은 없지만 입출력 단위를 바이트에서 문자로 바꾸는 등 입출력 스트림에 보조 기능을 추가하는 스트림이다. 다른 스트림을 감싸고 있다고 하여 Wrapper 스트림이라고도 하고, 디자인 패턴에서는 데코레이터(decorator)라고 부른다.
일반적으로 프로그램은 주 입력스트림에서 직접데이터를 읽지 않고, 주 입력스트림으로 데이터를 받고, 보조스트림의 기능을 이용해(데이터 가공?) 데이터를 받는다. 출력도 이와 같다.
보조 스트림의 주된 목적은 데이터 변환, 출력형식 지정, 성능 향상 등이다.
FilterInputStream과 FilterOutputStream은 보조 스트림의 상위 클래스이다. 보조 스트림은 위에서 언급했든 자체 입출력 기능이 없기 때문에 항상 다른 입출력 스트림과 함께 행동해야한다. 두 클래스의 유일한 생성자는 다음과 같으며, 하위 보조 스트림 클래스는 이 생성자를 통해 생성된다.
생성자 | 설명 |
---|---|
protected FilterInputStream(InputStream in) | 생성자의 매개변수로 InputStream을 받음 |
public FilterOutputStream(OutputStream out) | 생성자의 매개변로 OutputStream을 받음 |
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
}
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
}
FilterInputStream의 생성자는 접근제어자가 protected이기 때문에 인스턴스를 생성해서 사용할 수 없고 상속을 통해서 오버라이딩되어야 한다. 그리고 생성자 내에 있는 volatile 키워드는 변수가 CPU cache가 아닌 Main memory에 저장되는 것을 보장하는 키워드이다.
Filter 클래스를 살펴보면 InputStream 혹은 OutputStream 클래스를 상속 받아 구현되었고, 내부에 InputStream 혹은 OutputStream 클래스 변수가 선언되어 있는 것을 볼 수 있다. 그리고 생성자의 매개변수로 받은 스트림을 내부 변수에 할당하고 사용하는 것을 알 수 있다.
이처럼 보조 스트림은 외부에서 다른 스트림을 받아야한다. 이때 매개변수로 받는 스트림은 입출력 스트림을 감싼 또다른 보조 스트림일 수도 있다. 핵심은 자체 입출력이 가능한 스트림의 포함 여부이다.
바이트 단위를 사용하는 스트림은 한글 입출력이 불가능하다. 가능하다면 문자단위의 입출력 스트림을 이용하는 게 좋지만 표준 입출력 스트림이나 소켓 스트림의 경우, 바이트 단위로만 입출력이 가능하다. 그러므로 생성된 바이트 스트림을 문자로 변환해주는 보조 스트림이 필요한데, 이때 사용하는 것이 InputStreamReader와 OutputStreamWriter 클래스이다.
생성자 | 설명 |
---|---|
InputStreamReader(InputStream in) | InputStream을 매개변수로 받아 Reader를 생성 |
InputStreamReader(InputStream in, Charset cs) | InputStream과 Charset을 받아 Reader를 생성 |
InputStreamReader(InputStream in, CharsetDecoder dec) | InputStream과 CharsetDecoder를 매개변수로 받아 Reader를 생성. |
InputStreamReader(InputStream in, String charsetName) | InputStream과 Charset 의 이름을 String로 받아 Reader를 생성. |
InputStreamReader와 OutputStreamWriter의 생성자는 일반적으로 InputStream과 Charset을 매개변수로 받는다. Charset 이란 문자를 표현하는 인코딩 방식을 의미하며, 만약 Charset를 명시하지 않으면 시스템이 기본으로 사용하는 Charset이 적용된다.
OutputStreamWriter 클래스의 생성자는 첫번째 매개변수가 OutputStream이라는 것만 다를 뿐 형태는 거의 동일하다.
package stream.decorator;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderTest {
public static void main(String[] args) {
try (InputStreamReader isr = new InputStreamReader(System.in)) {
int ret;
System.out.println("=== InputStreamReader Test ===");
System.out.print("입력 : ");
while ((ret = isr.read()) != -1) {
if (ret == 10)
break;
else
System.out.print((char)ret);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("\n==============================");
}
}
=== InputStreamReader Test ===
입력 : hello, 한글도 잘 입력됩니다.
hello, 한글도 잘 입력됩니다.
==============================
package stream.decorator;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class OutputStreamWriteTest {
public static void main(String[] args) {
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"))) {
char[] charArr = new char[3];
charArr[0] = '안';
charArr[1] = '녕';
charArr[2] = '!';
osw.write(charArr);
osw.write(10);
osw.write("한글도 잘 출력되나요?");
} catch (IOException e) {
e.printStackTrace();
}
}
}
osw.txt
안녕!
한글도 잘 출력되나요?
package stream.decorator;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class OutputStreamWriteTest {
public static void main(String[] args) {
try (InputStreamReader isr1 = new InputStreamReader(new FileInputStream("osw.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("osw.txt"));
OutputStreamWriter osw = new OutputStreamWriter(System.out)) {
int ret;
System.out.println("=== System.out.write(int b)로 출력 ===");
while ((ret = isr1.read()) != -1) {
System.out.write(ret);
}
System.out.println("\n\n=== OutputStreamWriter 보조스트림 사용 ===");
while ((ret = isr2.read()) != -1) {
osw.write((char)ret);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
=== System.out.write(int b)로 출력 ===
HU!
\� � �%��?
=== OutputStreamWriter 보조스트림 사용 ===
안녕!
한글도 잘 출력되나요?
지금까지 본 입출력 스트림은 바이트나 문자 단위였다. 그러나 입출력 단위가 바이트나 문자단위일 경우, 그만큼 입출력을 여러번 반복해야하기 때문에 프로그램 수행 속도가 느려질 수 밖에 없다. 속도를 개선하기 위해 스트림에 들어온 입출력을 버퍼에 저장하여 한번에 출력하는 기능을 제공하는 보조 스트림 클래스가 있는데, 바로 Buffered 스트림이다. Buffered 스트림은 내부적으로 8192바이트의 배열을 가지고 있다.
생성자 | 설명 |
---|---|
BufferedInputStream | 바이트 단위로 읽는 스트림에 버퍼링 기능 제공 |
BufferedOutputStream | 바이트 단위로 출력하는 스트림에 버퍼링 기능 제공 |
BufferedReader | 문자 단위로 읽는 스트림에 버퍼링 기능을 제공 |
BufferedWriter | 문자 단위로 출력하는 스트림에 버퍼링 기능을 제공 |
생성자 | 설명 |
---|---|
BufferedInputStream(InputStream in) | InputStream를 매개변수로 받아 BufferedInputStream 생성. 버퍼의 디폴트 사이즈는 8192byte 이다. |
BufferedInputStream(InputStream in, int size) | InputStream과 버퍼 크기를 매개변수로 받아 BufferedInputStream 생성 |
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte[] buf;
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in); // super(in) == FilterInputStream(InputStream in)
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
package stream.decorator;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyTest {
public static void main(String[] args) {
long ms = 0;
try (FileInputStream fis = new FileInputStream("frontend_jwoo.zip");
FileOutputStream fos = new FileOutputStream("copy.zip")) {
ms = System.currentTimeMillis();
int ret;
while ((ret = fis.read()) != -1) {
fos.write(ret);
}
ms = System.currentTimeMillis() - ms;
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(ms);
}
}
354691
package stream.decorator;
import java.io.*;
public class FileCopyTest {
public static void main(String[] args) {
long ms = 0;
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("frontend_jwoo.zip"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy2.zip"))) {
ms = System.currentTimeMillis();
int ret;
while ((ret = bis.read()) != -1) {
bos.write(ret);
}
ms = System.currentTimeMillis() - ms;
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(ms);
}
}
2383
소켓이란 통신에 사용하는 네트워크 연결 리소스로, 자바에서는 Socket 클래스로 이용 가능하다. Socket 클래스는 getInputStream() 메서드와 getOutputStream() 메서드를 제공하는데, 각각 InputStream과 OutputStream을 반환해준다. 이때 이 스트림은 바이트 단위이므로, 버퍼링을 제공해주는 Buffered 보조 스트림을 추가해주는 것이 좋을 것이다.
Socket socket = new Socket();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
앞에서 다룬 스트림은 텍스트 형식의 자료를 다루었지만, 지금 다룰 DataInputStream과 DataOutputStream은 메모리에 저장된 0과 1을 그대로 읽거나 쓰기 때문에 자료형의 크기가 그대로 보존되며, 각 자료형 별 메서드가 있어 자료형에 따라 읽거나 쓸 수 있다.
생성자 | 설명 |
---|---|
DataInputStream(InputStream in) | InputStream를 매개변수로 받아 DataInputStream 생성 |
DataOutputStream(OutputStream out) | OutputStream를 매개변수로 받아 DataOutputStream 생성 |
읽은 그대로 써야하므로 DataInputStream 클래스의 read 메서드와 DataOutputStream의 write 메서드는 서로 대응된다.
생성자 | 설명 |
---|---|
btye readByte() | 1 바이트를 읽고, 읽은 값을 반환 |
boolean readBoolean() | 읽은 자료가 0이 아니면 true, 0이면 false 반환 |
char readChar() | 한 문자를 읽어 반환 |
short readShort() | 2 바이트를 읽어 정수 값을 반환 |
int readInt() | 4 바이트를 읽어 정수 값을 반환 |
long readLong() | 8 바이트를 읽어 정수 값을 반환 |
float readFloat() | 4 바이트를 읽어 실수 값을 반환 |
double readDouble() | 8 바이트를 읽어 실수 값을 반환 |
String readUTF() | 수정된 UTF-8 인코딩 기반으로 문자열을 읽어 반환 |
생성자 | 설명 |
---|---|
void writeByte(int v) | 1 바이트의 자료를 출력 |
void writeByte(boolean v) | 1 바이트의 값을 출력 |
void writeChar(int v) | 2 바이트를 출력 |
void writeShort(int v) | 2 바이트를 출력 |
void writeInt(int v) | 4 바이트를 출력 |
void writeLong(ling v) | 8 바이트를 출력 |
void writeFloat(float v) | 4 바이트를 출력 |
void writeDouble(double v) | 8 바이트를 출력 |
void writeUTF(String str) | 수정된 UTF-8 인코딩 기반으로 문자열을 출력 |
자료형을 그대로 읽고 쓰는 스트림이기 때문에 같은 정수라도 자료형에 따라 다르게 처리한다. 즉, writeByte(200), 1 바이트로 쓰인 200과, writeInt(200), 4바이트로 쓰인 200은 다르다.
package stream.decorator;
import java.io.*;
public class DataStreamTest {
public static void main(String[] args) {
// 데이터 쓰기
try(FileOutputStream fos = new FileOutputStream("data.txt");
DataOutputStream dos = new DataOutputStream(fos)) {
dos.writeByte(127);
dos.writeByte(-128);
dos.writeByte(128);
dos.writeByte(-129);
dos.writeChar('나');
dos.writeInt(128);
dos.writeInt(-129);
dos.writeFloat(3.14f);
dos.writeUTF("english, 한글");
} catch (IOException e) {
e.printStackTrace();
}
// 데이터 읽기
try(FileInputStream fis = new FileInputStream("data.txt");
DataInputStream dis = new DataInputStream(fis)) {
System.out.println(dis.readByte());
System.out.println(dis.readByte());
System.out.println(dis.readByte());
System.out.println(dis.readByte());
System.out.println(dis.readChar());
System.out.println(dis.readInt());
System.out.println(dis.readInt());
System.out.println(dis.readFloat());
System.out.println(dis.readUTF());
} catch (IOException e) {
e.printStackTrace();
}
}
}
127
-128
-128
127
나
128
-129
3.14
english, 한글
직렬화(直列化, serialization)
직렬화(直列化) 또는 시리얼라이제이션(serialization)은 컴퓨터 과학의 데이터 스토리지 문맥에서 데이터 구조나 오브젝트 상태를 동일하거나 다른 컴퓨터 환경에 저장(이를테면 파일이나 메모리 버퍼에서, 또는 네트워크 연결 링크 간 전송)하고 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다. - 위키백과
객체를 저장하거나 메모리, 데이터베이스 혹은 파일로 옮기려면 어떻게 해야할까? 이럴 때 필요한 것이 직렬화다. 직렬화란 객체를 바이트 스트림으로 바꾸는 것, 즉 객체에 저장된 데이터를 스트림에 쓰기(write) 위해 연속적인 serial 데이터로 변환하는 것이다.
직렬화의 주된 목적은 객체를 상태 그대로 저장하고 필요할 때 다시 생성하여 사용하는 것이다.
...
역직렬화Deserialization는 직렬화의 반대말로, 네트워크나 영구저장소에서 바이트 스트림을 가져와서 객체가 저장되었던 바로 그 상태로 변환하는 것이다(convert it back to the Object with the same state).
직렬화하면서 생긴 바이트 스트림은 플랫폼에 독립적이다(platform independent). 직렬화된 객체는 다른 플랫폼에서 역직렬화가 가능하다는 뜻이다. - lunay0ung의 글
직렬화란 자바 시스템 내부의 객체, 데이터 등을 외부 자바 시스템에서 사용할 수 있는 byte형태로 데이터(파일 등)를 변환하는 기술. 시스템적으로 이야기하면 JVM의 힙, 스택등에 상주한 객체 데이터를 파일형태로 변환하는 기술이다.
역직렬화는 직렬화와 반대로 byte로 저장된 데이터를 스트림을 통해 읽고 다시 객체로 변환하는 기술을 통칭하는 용어이다. 시스템적으로 이야기하면 byte형태의 데이터를 객체로 변환하여 JVM으로 상주시키는 것이다.
참고 : 우아한형제들 기술블로그
직렬화 할수 있는 클래스를 만들기 위해서는 java.io.Serializable 인터페이스를 구현해야한다.
public class Member **implements Serializable** {
...
}
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
**import java.io.Serializable;**
class Person **implements Serializable**{
String name;
**transient** String title; //transient 저장하고 싶지 않을때, null 값으로 저장이 된다.
public Person(){};
public Person(String name, String title)
{
this.name = name;
this.title = title;
}
public String toString()
{
return name + "," + title;
}
}
public class SerializationTest {
public static void main(String[]args) throws ClassNotFoundException
{
Person person1 = new Person("진성대", "매니저");
try(FileOutputStream fos = new FileOutputStream("serial.dat"); ObjectOutputStream oos = new ObjectOutputStream(fos))
{
**oos.writeObject(person1); // 직렬화,** 인텔리제이 기준 프로젝트 루트폴더에 저장됨.
}
catch (IOException e)
{
System.out.println(e);
}
try(FileInputStream fis = new FileInputStream("serial.dat"); ObjectInputStream ois = new ObjectInputStream(fis))
{
**Object obj = ois.readObject(); // 역직렬화.**
if (obj instanceof Person)
{
**Person p = (Person)obj;**
System.out.println(p);
}
}
catch(IOException e)
{
System.out.println(e);
}
}
}
진성대,null
Serializationable 인터페이스는 자료를 읽고 쓰는데 필요한 부분을 따로 구현할 필요가 없지만, Externalizable 인터페이스는 프로그래머가 직접 구현해야한다. 읽는 데 사용하는 readExternal(ObjectInput in)
메서드와 쓰는데 사용하는 writeExternal(ObjectOutput out)
메서드를 구현해야하며, 복원할 때 디폴트 생성자가 호출되기 때문에 디폴트 생성자를 추가해주어야한다. 만약 write 할때 클래스 내의 멤버변수를 모두 적지 않았다면, 복원할 때 디폴트 생성자 내에서 초기화된 변수 값으로 설정된다.
Book
클래스 package bookmanagementprogram;
import java.io.*;
public class Book implements Externalizable {
private static final long serialVersionUID = -5525783142235723859L;
protected String sName;
transient protected String sAuthor;
protected int iPage;
protected String sGenre;
public Book() {
sName = "제목";
sAuthor = "작가";
iPage = 0;
// sGenre = "장르";
}
public Book(String name, String author, int page, String genre) {
sName = name;
sAuthor = author;
iPage = page;
sGenre = genre;
}
public String getName() {
return sName;
}
public String getAuthor() {
return sAuthor;
}
public int getPage() {
return iPage;
}
public String getGenre() {
return sGenre;
}
@Override
public String toString() {
return "도서 이름 : " + this.sName + "\n도서 저자 : " + this.sAuthor + "\n도서 페이지 수: " + this.iPage + "\n도서 장르 : " + this.sGenre;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(sName);
out.writeUTF("작자 미상");
out.writeInt(iPage);
// out.writeUTF(sGenre);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
sName = in.readUTF();
sAuthor = in.readUTF();
iPage = in.readInt();
// sGenre = in.readUTF();
}
}
package stream.serialization;
import bookmanagementprogram.Book;
import java.io.*;
public class ExternalizableTest {
public static void main(String[] args) throws ClassNotFoundException {
Book book = new Book("이노아카", "jwoo", 42, "42Seoul");
try(FileOutputStream fos = new FileOutputStream("serial.out");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(book);
} catch (IOException e) {
e.printStackTrace();
}
try(FileInputStream fis = new FileInputStream("serial.out");
ObjectInputStream ois = new ObjectInputStream(fis)) {
Book b = (Book)ois.readObject();
System.out.println(b);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package stream.serialization;
import bookmanagementprogram.Book;
import java.io.*;
public class ExternalizableTest {
public static void main(String[] args) throws ClassNotFoundException {
Book book = new Book("이노아카", "jwoo", 42, "42Seoul");
try(FileOutputStream fos = new FileOutputStream("serial.out");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(book);
} catch (IOException e) {
e.printStackTrace();
}
try(FileInputStream fis = new FileInputStream("serial.out");
ObjectInputStream ois = new ObjectInputStream(fis)) {
Book b = (Book)ois.readObject();
System.out.println(b);
} catch (IOException e) {
e.printStackTrace();
}
}
}
도서 이름 : 이노아카
도서 저자 : 작자 미상
도서 페이지 수: 42
도서 장르 : null
SerialVersionUID를 사용한 버전관리
객체를 역직렬화할 때, 직렬화 당시의 클래스 상태와 다르면 오류가 발생한다. 즉, 멤버변수가 변경되거나 하면 역직렬화하는데 어려움이 있다. 하지만 serialVersionUID를 활용하면 클래스의 버전을 좀 더 쉽게 관리할 수 있다. 클래스에 수정사항이 발생했을때 버전 정보를 변경하면, 나중에 버전 정보만 가지고도 클래스가 수정이 되었는지 안되었는지 알 수 있다.
[intellij 플러그인] SerialVersionUID 생성 가이드
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileTest{
public static void main(String []args) throws IOException
{
RandomAccessFile raf = new RandomAccessFile("random.txt", "rw");
raf.writeInt(100); //4
System.out.println(raf.getFilePointer());
raf.writeDouble(3.14); // 8
System.out.println(raf.getFilePointer());
raf.writeChar('a'); //2
System.out.println(raf.getFilePointer());
raf.seek(0); // seek -> filepointer 이동
System.out.println("=====================");
int i = raf.readInt();
double d = raf.readDouble();
char c = raf.readChar();
System.out.println(i);
System.out.println(d);
System.out.println(c);
}
}
4
12
14
=====================
100
3.14
a