1.스트림
1.1 스트림의 개요
입출력을 위한 공통된 방법
다양한 장소에 존재하는 데이터들을 핸들하기 위해서는 입출력 데이터를 처리하는 공통된 방법이 있어야 한다.
입출력을 위한 공통된 방법이 없다면?
각각의 하드웨어 장치를 잘 알고 있다는 가정하에, 각각의 하드 웨어 장치에 직접 접근해야 한다.
스트림(Stream)의 정의
자료의 입출력을 도와주는 중간 매개체
입출력 장치
파일 디스크(파일)
키보드, 모니터, 마우스
메모리
네트워크
1.2 스트림이란?
입출력 장치의 일반적인 특징
일반적인 입출력 장치는 대부분 데이터를 읽고 기록한다는 특징이 있다.
스트림의 역할
스트림은 데이터를 읽고 쓰기 위한 공통된 방법을 제공
장치(Device)와 스트림(Stream)
장치마다 연결할 수 있는 각각의 스트림이 존재
1.2 스트림이란?
File 입력 스트림의 연결과 작업
FileInputStream fis = new FileInputStream(파일);
//fis.read()를이용해서데이터읽기
File 출력 스트림의 연결과 작업
FileOutputStream fos = new FileOutputStream(파일);
//fos.write()를이용해서데이터쓰기
스트림의 내부 동작원리는 몰라도 된다. 사용할 줄만 알면 된다.
사용자는 스트림이 어떻게 장치와 연결되고 작업이 되는지 몰라도 된다.
단지 스트림을 어떻게 생성하며 어떻게 데이터를 읽고 쓰 는지만 알면 된다.
1.3 스트림의 원리
스트림이란?
스트림이란 빨대다.
스트림으로부터 데이터 읽기
목표지점에 적절한 입력용 스트림을 생성한다.
스트림으로부터 데이터를 읽는다.
스트림으로부터 필요한 만큼 계속해서 데이터를 읽는다.
스트림을 닫는다.
빨대로부터 음료수 마시기
적절한 빨대를 음료수에 꽂는다.
빨대로부터 음료수를 흡입한다.
빨대로부터 마시고 싶은 만큼 계속해서 음료수를 흡입한다.
빨대를 제거한다.
1.3 스트림의 원리
스트림에 데이터 기록하기
목표지점에 적절한 출력용 스트림을 생성한다.
스트림에 데이터를 기록한다.
스트림에 필요한 만큼 계속해서 데이터를 기록한다.
스트림을 닫는다.
빨대로 음료수 내뱉기
적절한 빨대를 컵에 꽂는다.
빨대로 입안에 있는 음료수를 내뱉는다.
빨대로 내뱉고 싶은 만큼 음료수를 내뱉는다.
빨대를 제거한다.
입력 스트림
출력 스트림
1.4 스트림의 종류
기본적인 분류
입력 스트림(예: FileInputStream, ObjectInputStream)
출력 스트림(예: FileOutputStream, ObjectOutputStream)
문자 스트림의 패턴
Reader는 입력용 문자 스트림이다.
Writer는 출력용 문자 스트림이다.
Reader나 Writer가 붙는다면 문자 스트림이다.
바이트 스트림의 패턴
InputStream는 입력용 바이트 스트림이다.
OutputStream는 출력용 바이트 스트림이다.
'InputStream'나 'OutputStream'이라는 단어가 붙어 있다 면 바이트 스트림이다.
PrintStream은 출력용만 존재하기 때문에 약간 특이하다.
1.4 스트림의 종류
스트림의 종류 I
문자 스트림: Reader, Writer(문자 단위로 처리)
바이트 스트림 : InputStream, OutputStream(바이트 단 위로 처리)
스트림의 종류 II
입력 스트림 : Reader나 InputStream
출력 스트림 : Writer나 OutputStream
스트림의 규칙
InputStream과 Reader는 읽어 들이는 메서드를 포함하 고 있어야 한다.
OutputStream과 Writer는 기록하는 메서드를 포함하고 있어야 한다.
1.4 스트림의 종류
read method
바이트 단위의 read() 메서드
int read()
int read(byte cbuf[])
int read(byte cbuf[], int offset, int length)
문자 단위의 read() 메서드
int read()
int read(char cbuf[])
int read(char cbuf[], int offset, int length)
1.4 스트림의 종류
write method
바이트 단위의 write() 메서드
int write(int c)
int write(byte cbuf[])
int write(byte cbuf[], int offset, int length)
문자 단위의 write() 메서드
int write(int c)
int write(char cbuf[])
int write(char cbuf[], int offset, int length)
1.4 스트림의 종류 – Stream류
InputStream
ByteArrayInputStream
FileInputStream
DataInputStream
BufferedInputStream
PushBackInputStream
LinedNumberInputStream
PipeInputStream
FilterInputStream
ObjectInputStream
SequenceInputStream
StringBufferInputStream
OutputStream
ByteArrayOutputStream
FileOutputStream
DataOutputStream
BufferedOutputStream
PrintStream
PipeOutputStream
FilterOutputStream
ObjectOutputStream
1.4 스트림의 종류 – Reader/Writer류
Reader
FilterReader
BufferedReader
FileReader
PushReader
LinedNumberReader
CharArrayReader
InputStreamReader
PipeReader
StringReader
Writer
PipeWriter
CharArrayWriter
FilterWriter
OutputStreamWriter
FilterWriter
StringWriter
BufferedWriter
1.5 문자 스트림과 바이트 스트림
바이트 저장소
입력 스트림의 방향
출력 스트림의 방향
나
나를 기준으로 데이터가 들어온다 면 무조건 입력 스트림
나를 기준으로 데이터가 밖으로 나간다면 무조건 출력 스트림
바이트
바이트 그 자체를 핸들하는 스트림
바이트를 문자로 인코딩하여 핸들한 다. 인코딩은 기본적으로 스트림이 자동으로 판단한다.
바이트스트림
문자
문자 인코딩
스트림이 빨대 랬지!
스트림의 종류와 방향성
문자스트림
2 표준 스트림과 File 클래스
2.1 표준 입출력
System 클래스의 표준 입출력 스트림 멤버
package java.lang;
public class System{
public static PrintStream out;
public static InputStream in;
public static PrintStream err;
//중간생략
}
키보드(표준입력)
모니터(표준출력)
모니터(표준에러출력)
2.1 표준 입출력
System.out
표준 출력(모니터) 스트림
System.out.println("에러메시지");
System.in
표준 입력(키보드) 스트림
int d = System.in.read(); //한 바이트 읽어내기
System.err
표준 에러 출력(모니터) 스트림
System.err.println("데이터");
2.2 System.in
예제
import java.io.*;
public class SystemInMain3{
public static void main(String[] args) throws IOException{
System.out.print("문자를입력한후엔터를누르세요? ");
int i;
while( (i = System.in.read()) != '\n'){
System.out.println((char)i +":" + i);
}
}
}
2.2 System.in
실행결과
문자를 입력한 후 엔터를 누르세요? Hello
H:72
e:101
l:108
l:108
o:111
:13
설명
Console을 통해 입력받은 문자열을 char형으로 나누어 이의 정수 값 을 표현한 예제이다.
2.3 System.out과 System.err의 차이
Standard Out과 Standard Error의 차이
out과 err는 별개의 스트림이며, 독립적으로 리다이렉트된다.
out은 버퍼링 지원, err는 버퍼링 지원하지 않음
예제
public class SystemOutErrMain{
public static void main(String[] args){
System.out.println("out : Hello ");
System.out.println("out : World");
System.err.println("err : It's eror stream");
System.out.println("out : World!");
}
}
2.3 System.out과 System.err의 차이
실행결과
c:\javasrc\chap09>javac SystemOutErrMain.java
c:\javasrc\chap09>java SystemOutErrMain
out : Hello
out : World
err : It's eror stream
out : World!
리다이렉트시 실행 결과
c:\javasrc\chap09>java SystemOutErrMain > a.txt
err : It's eror stream
out의 결과는 a.txt에
err의 결과는 콘솔창에 출력된다.
2.4 File 클래스
예제 - File클래스의 용법
import java.io.*;
import java.net.*;
class FileTest{
public static void main(String[] args)throws Exception{
File f = new File("FileMain.java");
PrintStream out = System.out;
out.println("isFile(): " + f.isFile()); //파일인지아닌지
out.println("isDirectory(): " + f.isDirectory()); //디렉터리인지아닌지
out.println("isHidden(): " + f.isHidden()); //숨김파일인지
out.println("lastModified(): " + f.lastModified()); //마지막에수정된날짜
out.println("canRead(): " + f.canRead()); //읽기속성을가진파일인지
out.println("canWrite(): " + f.canWrite()); //쓰기속성을가진파일인지
out.println("getPath(): " + f.getPath()); //상대경로
out.println("getAbsolutePath(): "+ f.getAbsolutePath()); //절대경로
out.println("getName(): " + f.getName()); //디렉터리또는파일의이름
out.println("toURL(): " + f.toURL()); //URL형식의경로
out.println("exists(): " + f.exists()); //파일이존재하는지
out.println("length(): " + f.length()); //파일의길이
}
}
2.4 File 클래스
결과
isFile(): false
isDirectory(): false
isHidden(): false
lastModified(): 0
canRead(): false
canWrite(): false
getPath(): FileMain.java
getAbsolutePath(): …ch09\FileMain.java
getName(): FileMain.java
toURL(): file:/…ch09/FileMain.java
exists(): false
length(): 0
2.5 파일 목록 출력하기
예제 - File클래스의 용법
import java.io.*;
import java.net.*;
class FileTest2{
public static void main(String[] args)throws Exception{
File dir1 = new File(".");
File dir2 = new File("..");
System.out.println("AbsolutePath");//AbsolutePath
System.out.println ("Current dir-> " + dir1.getAbsolutePath());
System.out.println ("Parent dir-> " + dir2.getAbsolutePath());
System.out.println("CanonicalPath");//CanonicalPath
System.out.println ("Current dir-> " + dir1.getCanonicalPath());
System.out.println ("Parent dir-> " + dir2.getCanonicalPath());
File dir3 = new File("c:\\");//절대경로지정
System.out.println ("c:\\-> " + dir3.getAbsolutePath());
File f = new File("c:\\");
File[] fs = f.listFiles();
for(int i=0; i<fs.length; i++){
System.out.println(fs[i].getName());
}
}
}
결과
상황에 따라 다양하다.
2.5 디렉터리 생성, 삭제, 파일이름 변경
디렉터리의 생성
File f = new File("디렉터리명");
if(!f.exist()){
f.mkdir();
}
디렉터리를 삭제하는 예
File f = new File("NewFolder"); //디렉터리의경우비어있어야한다.
if(f.exists()){ //디렉터리가존재하는지확인
f.delete(); //디렉터리삭제
}
2.5 디렉터리 생성, 삭제, 파일이름 변경
파일을 삭제하는 예
File f = new File("파일명");
if(f.exists()){
f.delete();
}
파일의 이름을 변경하는 예
File f = new File("원본파일명");
File t = new File("변경할파일명");
if(f.exists()){
f.renameTo(t);
}
3 File 스트림
3.1 파일 입출력의 종류
파일 입출력을 위한 스트림
바이트 단위
FileInputStream
FileOutputStream
문자 단위
FileReader
FileWriter
3.1 파일 입출력의 종류
바이트와 문자 스트림
바이트 단위의 파일을 핸들할 때에는 FileInputStream과 FileOutputStream을 사용한다.
문자 단위의 파일을 핸들하고자 할 때에는 FileReader와 FileWriter를 사용한다.
입력과 출력 스트림
FileInputStream을 생성하는 이유는 파일을 읽기 위해서 이다.
FileOutputStream을 생성하는 이유는 파일을 기록하기 위해서이다.
FileWriter와 FileReader에서도 이것은 마찬가지이다.
3.2 바이트 단위의 출력 파일 스트림 I
예제
import java.io.*;
class FileOutputStreamTest{
public static void main(String[] args) throws Exception{
FileOutputStream fos = new FileOutputStream("a.dat");
fos.write(72);
fos.write(101);
fos.write(108);
fos.write(108);
fos.write(111);
fos.close();
System.out.println("a.dat 파일기록완료");
}
}
3.2 바이트 단위의 출력 파일 스트림 I
해설
바이트 단위의 File 출력 스트림의 생성
FileOutputStream fos = new FileOutputStream("a.dat");
//fos로쓰기작업
fos.close(); //스트림 닫기
바이트 단위의 File 입력 스트림의 생성
FileInputStream fis = new FileInputStream("b.dat");
//fis로읽기작업
fis.close(); //스트림 닫기
3.3 바이트 단위의 출력 파일 스트림 II
FileOutputStream으로 데이터 기록하기 II
예제
import java.io.*;
class FileOutputStreamTest2{
public static void main(String[] args) throws Exception{
FileOutputStreamfos= new FileOutputStream("a1.dat");
byte[] b = new byte[]{72, 101, 108, 108, 111};
fos.write(b);
fos.close();
System.out.println("a1.dat 파일기록완료");
}
}
설명
a1.dat 파일에 byte array인 b의 값을 기록하였다.
3.3 바이트 단위의 출력 파일 스트림 II
존재하는 파일의 끝에 덧붙여쓰기
예제
import java.io.*;
class FileOutputStreamTest3{
public static void main(String[] args) throws Exception{
File f = new File("a1.dat");
if(f.exists()){
FileOutputStreamfos= new FileOutputStream("a1.dat", true);
byte[] b = new byte[]{72, 101, 108, 108, 111};
fos.write(b);
fos.close();
System.out.println("a1.dat 파일의끝부분에데이터추가하기완료");
}
}
}
설명
a1.dat 파일에 byte array인 b의 값을 기록하였다.
FileOutputStream(“파일명”, 붙여쓰려면true)을 이용하여 파일의 끝부분에서 덧붙였다.
3.4 바이트 단위의 입력 파일 스트림 I
FileInputStream을 이용해서 파일 읽기
예제
import java.io.*;
class FileInputStreamTest{
public static void main(String[] args) throws Exception{
FileInputStreamfis= new FileInputStream("a.dat");
inti;
while( (i=fis.read()) != -1 ){
System.out.print((char)i);
}
fis.close();
}
}
설명
a.dat 파일의 값을 읽어서 이를 char형으로 캐스팅하여 출력
3.4 바이트 단위의 입력 파일 스트림 I
FileInputStream의 read(byte[] b) - FileInputStream을 이 용한 여러 바이트 읽기
예제
import java.io.*;
class FileInputStreamTest2{
public static void main(String[] args) throws Exception{
FileInputStreamfis= new FileInputStream("a1.dat");
intcount;
byte[] b = new byte[10];
while( (count=fis.read(b)) != -1 ){
for(inti=0; i<count; i++){
System.out.print((char)b[i]);
}
}
fis.close();
}
}
설명
a.dat 파일의 값을 읽는 것은 동일하지만 이를 byte array로 읽어서 이를 char형 으로 캐스팅하여 출력한다.
이렇게 여러 개의 바이트를 한꺼번에 읽을 수 있다.
3.5 바이트 단위의 입력 파일 스트림 II
바이트 단위의 파일입력 스트림
예제
import java.io.*;
class FileInputStreamTest3{
public static void main(String[] args) throws Exception{
File f = new File("FileInputStreamTest3.java");
intfileSize= (int)f.length();
System.out.println("파일의사이즈:" + fileSize);
byte[] b = new byte[fileSize];
FileInputStreamfis= new FileInputStream("FileInputStreamTest3.java");
intpos = 0;
intsize = 10;
inttemp;
while( (size=fis.read(b, pos, size)) > 0 ){
pos += size;
temp = b.length-pos;//배열의길이즉, 파일사이즈에서10씩제한다!
if(temp< 10){
size = temp;//temp의크기가읽기최소단위인10보다작아지면pos에사이 즈를지정하여파일사이즈보다더큰값을읽지않도록한다.
}
}
fis.close();
System.out.println("읽은바이트수:" + pos);
FileOutputStreamfos= new FileOutputStream("test.txt");
fos.write(b);
fos.close();
}
}
3.5 바이트 단위의 입력 파일 스트림 II
설명
파일 사이즈 알아내기
File f = new File("FileInputStreamTest3.java”);
intfileSize= (int)f.length();
System.out.println("파일의사이즈:" + fileSize);
파일 사이즈에 해당하는 배열 만들기
byte[] b = new byte[fileSize];
스트림을 이용해서 배열에 데이터 채우기
FileInputStreamfis= new FileInputStream("FileInputStreamTest3.java”);
intpos = 0;
intsize = 10;
inttemp;
while((size=fis.read(b, pos, size))> 0 ){
pos += size;
temp = b.length-pos;
if(temp< 10){
size = temp;
}
}
fis.close();
System.out.println("읽은바이트수:" + pos);
배열을 통째로 파일에 기록하기
FileOutputStreamfos= new FileOutputStream("test.txt");
fos.write(b);
fos.close();
3.6 문자 단위의 출력 파일 스트림
FileWriter로문자기록하기
예제
import java.io.*;
class FileWriterTest{
public static void main(String[] args) throws Exception{
FileWriterfos= new FileWriter("writer.dat");
fos.write(72);
fos.write(101);
fos.write(108);
fos.write(108);
fos.write(111);
fos.close();
}
}
설명
writer.dat파일에 write메서드내의 매개변수대로 기록이 될 것이다.
3.6 문자 단위의 출력 파일 스트림
FileWriter에문자열과문자배열기록하기
예제
import java.io.*;
class FileWriterTest2{
public static void main(String[] args) throws Exception{
char[] content = new char[]{72, 101, 108, 108, 111};
String str= new String("HelloWorld!");
FileWriterfos= new FileWriter("writer2.dat");
fos.write(content); //문자배열의기록
fos.write(str); //문자열의기록
fos.close();
}
}
설명
writer2.dat파일에 content배열의 값과
str문자열의 값이 기록될 것이다.
3.6 문자 단위의 출력 파일 스트림
파일의 마지막 부분에 덧붙여넣기
예제
import java.io.*;
class FileWriterTest3{
public static void main(String[] args) throws Exception{
String str= new String("NewHello World!");
FileWriterfos= new FileWriter("writer2.dat", true);
fos.write(str);
fos.close();
}
}
설명
writer2.dat파일에 str문자열의 값이 추가로 기록될 것 이다.
이는 FileOutputStream의 용법과 동일하다.
3.7 문자 단위의 입력 파일 스트림
FileReader를생성하는방법
FileReader fr = new FileReader("파일명");
//fr을이용해서문자읽기
fr.close(); //스트림 닫기
FileReader의 read() 계열의 메서드
int read()
한 문자 단위로 읽어 들인다.
int read(char[] cbuf, int offset, int length)
시작점 offset에서 length만큼씩 읽어 이를 char array인 cbuf에 저장한다.
3.7 문자 단위의 입력 파일 스트림
예제 - FileReader를 이용해서 파일 읽기
import java.io.*;
class FileReaderTest{
public static void main(String[] args) throws Exception{
FileReaderfr= new FileReader("readr.dat");
inti;
while( (i=fr.read()) != -1 ){
System.out.print((char)i);
}
fr.close();
}
}
3.8 파일복사 I
예제 - File 스트림을 이용한 파일 복사(바이트 단위)
import java.io.*;
class FileCopyTest{
public static void main(String[] args) throws Exception{
inti, len=0;
FileInputStreamfis= new FileInputStream("writer.dat");
FileOutputStreamfos= new FileOutputStream("writer2.dat");
long psecond= System.currentTimeMillis();
while((i=fis.read()) != -1) {
fos.write(i);
len++;
}
fis.close();
fos.close();
psecond= System.currentTimeMillis() -psecond;
}
}
3.8 파일복사 II
예제 – File, Buffered 스트림을 이용한 파일 복사 (바이트 단위)
import java.io.*;
class BufferedFileCopy{
public static void main(Stringargs[]) throws Exception{
inti, len=0;
FileInputStreamfis= new FileInputStream("writer1.dat");
FileOutputStreamfos= new FileOutputStream("writer2.dat");
BufferedInputStreambis= new BufferedInputStream(fis);
BufferedOutputStreambos= new BufferedOutputStream(fos);
long psecond= System.currentTimeMillis();
while((i=bis.read()) != -1) {
bos.write(i);
len++;
}
bis.close();
bos.close();
psecond= System.currentTimeMillis() -psecond;
}
}
3.9 RandomAccessFile
임의의 접근(Random Access)
스트림을 사용할 때 순차적으로 read()를 호출하게 되며, reset()을 이용하면 처음부터 다시 읽을 수 있다. 하지만 사용자가 원하는 위치로 이동하거나 되돌릴 수는 없다.
그렇기 때문에 일반적인 스트림에서는 데이터를 랜덤하게 접근할 수 없다.
3.9 RandomAccessFile
예제 - 임의 접근방식으로 파일 접근 하여 RandomAccessFile을 테스트
public class RandomAccessFileMain{
public static void main(String[] args) throws IOException{
String s = "I love normal java";
String q = "jabook";
RandomAccessFilerf= new RandomAccessFile("raccess.txt", "rw");
rf.writeBytes(s);
rf.close();
RandomAccessFilerf2 = new RandomAccessFile("raccess.txt", "rw");
rf2.seek(7);
rf2.writeBytes(q);
rf2.close();
RandomAccessFilerf3 = new RandomAccessFile("raccess.txt", "r");
rf3.seek(2);
System.out.println(rf3.writeLine());
rf3.close();
}
}
4 2차 스트림
4.1 2차 스트림이란
목표지점에 직접 연결되는 스트림
File 스트림 : File에 직접 연결되는 스트림
Memory 스트림 : Memory에 직접 연결되는 스트림
Network 스트림: Network에직접연결되는스트림
대표적인 1차 스트림들
InputStream, OutputStream
FileInputStream, FileOutputStream
FileReader, FileWriter
ByteArrayInputStream, ByteArrayOutputStream
CharArrayReader, CharArrayWriter
StringReader, StringWriter
1차 스트림의 공통된 특징
데이터가 존재하는 목표지점에 스트림이 직접 연결된다는 것.
4.1 2차 스트림이란(계속)
FileInputStream을 BufferedInputStream으로 변환
FileInputStream fis = new FileInputStream("파일명");
BufferedInputStream bis = new BufferedInputStream(fis);
BuffererInputStream(2차 스트림)을 사용하면 속도가 빨라진다.
2차 스트림의 가공 목적
사용자가 원하는 용도의 스트림으로 가공하는 것
대표적인 2차 스트림들
InputStreamReader, OutputStreamWriter
DataInputStream, DataOutputStream
BufferedInputStream, BufferedOutputStream
BufferedReader, BufferedWriter
ObjectInputStream, ObjectOutputStream
4.1 2차 스트림이란(계속)
FileReader를 BufferedReader로 변환
1차 스트림을 2차 스트림으로 가공
FileReader fr = new FileReader("파일명");
BufferedReader br = new BufferedReader(fr);
String str = br.readLine();
결론
2차 스트림으로 가공하는 목적
한 바이트 또는 한 문자 단위로 데이터를 처리하는 것보다 다양한 방법으로 스트림을 처리하고자 하는 것
4.2 스트림 변환법
스트림 변환 규칙I
입력 스트림은 입력 스트림 계열로 변환
출력 스트림은 출력 스트림 계열로 변환
스트림 변환 규칙II
바이트 스트림은 바이트 스트림 계열로 변환
문자 스트림은 문자 스트림 계열로 변환
4.2 스트림 변환법
문자 스트림끼리의 변환
FileReader BufferedReader
FileWriter BufferedWriter
바이트 스트림끼리의 변환
FileInputStream BufferedInputStream
FileOutputStream BufferedOutputStream
4.2 스트림 변환법(계속)
다른 계열끼리의 변환(특수한 경우)
바이트 스트림 문자 스트림
바이트 스트림에서 문자 스트림으로 변환
InputStream -> InputStreamReader
OutputStream -> OutputStreamWriter
문자 스트림을 바이트 스트림으로 변환하는 방법은 존재하지 않는다.
4.3 문자 스트림으로 변환
InputStreamReader
바이트 입력 스트림을 문자 입력 스트림으로 변환할 때사 용되는 스트림
OutputStreamWriter
바이트 출력 스트림을 문자 출력 스트림으로 변환할 때사 용하는 스트림
바이트 스트림에서 문자 스트림으로 변환할 때 반드 시 위두 클래스를 사용해야 한다.
InputStreamReader와 OutputStreamWriter의 원 형
public class InputStreamReader extends Reader
public class OutputStreamWriter extends Writer
4.3 문자 스트림으로 변환
표준 입력 스트림의 문자 스트림 변환
InputStreamReader isr = new InputStreamReader(System.in);
//System.in은 InputStream이다.
FileInputStream을 문자 입력 스트림으로 변환
FileInputStream fis = new FileInputStream("파일명");
InputStreamReader isr = new InputStreamReader(fis);
FileOutputStream을 문자 출력 스트림으로 변환
FileOutputStream fos = new FileOutputStream("파일명");
OutputStreamWriter osw = new OutputStreamWriter(fos);
4.3 문자 스트림으로 변환
예제 - a.dat파일을 복사하는 예제
import java.io.*;
public class ByteToCharMain{
//ByteToCharMain 프로그램의실행
//java ByteToCharMain 원본파일이름목표파일이름
//예) java ByteToCharMain ByteToCharMain.java tmp.txt
public static void main (String[] args) throws IOException {
FileInputStream fis = new FileInputStream (“a.dat”);
FileOutputStream fos = new FileOutputStream (“copied_a.dat”);
InputStreamReader isr = new InputStreamReader (fis);
OutputStreamWriter osw = new OutputStreamWriter (fos);
int i;
while((i = isr.read()) != -1){
osw.write (i);
}
osw.close();
isr.close();
System.out.println("작업완료");
} //end of main
} //end of ByteToCharMain class
4.4 Buffered 스트림
Buffered 스트림
버퍼를 사용하지 않고 읽기와 쓰는 동작을 할 때마다 한 바이트씩 읽고 쓰게 되면 성능이 떨어 진다.
Buffered 스트림은 내부의 버퍼에 데이터를 담아 처리하 기 때문에 처리 속도가 빠르다.
내부적으로 지원하기 때문에 2차 스트림이라 한다.
직접 생성은 안 된다.
버퍼를 지원해 주는 스트림
BufferedReader, BufferedWriter(Buffered 문자스트림)
BufferedInputStream, BufferedOutputStream(Buffered 바이트스트림)
4.4 Buffered 스트림
FileInputStream을 BufferedInputStream으로 변환
FileInputStream fis = new FileInputStream("파일명");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream을 BufferedOutputStream으로 변환
FileOutputStream fos = new FileOutputStream("파일명");
BufferedOutputStream bos = newBufferedOutputStream( fos);
FileReader을 BufferedReader로 변환
FileReader fr = new FileReader("파일명");
BufferedReader br = new BufferedReader(fr);
FileWriter을 BufferedWriter로 변환
FileWriter fw = new FileWriter("파일명");
BufferedWriter bw = new BufferedWriter(fw);
4.4 Buffered 스트림
예제 – FileInputStream을 이용하여 파일 복사
import java.io.*;
public class FileCopy {
public static void main(String[] args) throws IOException{
int i, len=0;
FileInputStream fis = new FileInputStream("a.dat");
FileOutputStream fos = new FileOutputStream(
“b.dat”);
long psecond = System.currentTimeMillis();
while((i=fis.read()) != -1){
fos.write(i);
len++;
}
fis.close();
fos.close();
psecond = System.currentTimeMillis() -psecond;
System.out.println(len + " bytes " + psecond +" miliseconds");
} //end of main
} //end of FileCopy class
4.4 Buffered 스트림
예제 – BufferedInputStream를 이용해서 파일복사
import java.io.*;
class BufferedFileCopy{
public static void main(String args[]) throws Exception{
int i, len=0;
FileInputStream fis = new FileInputStream("writer1.dat");
FileOutputStream fos = new FileOutputStream("writer2.dat");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
long psecond = System.currentTimeMillis();
while((i=bis.read()) != -1) {
bos.write(i);
len++;
}
bis.close();
bos.close();
psecond = System.currentTimeMillis() -psecond;
}
}
4.4 Buffered 스트림
예제 – BufferedReader를 이용해서 파일 복사
import java.io.*;
public class BufferedCharFileCopy {
public static void main(String[] args) throws IOException{
int i, len=0;
FileReader fr = new FileReader("a.dat");
FileWriter fw = new FileWriter("c.dat");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
long psecond = System.currentTimeMillis();
while((i=br.read()) != -1){
bw.write(i);
len++;
}
br.close();
bw.close();
psecond = System.currentTimeMillis() -psecond;
System.out.println(len + " bytes " + psecond +" miliseconds");
} //end of main
} //end of BufferedCharFileCopy class
4.4 Buffered 스트림
설명
File 문자 스트림을 Buffered 문자 스트림으로 변환
FileReader fr = new FileReader(“a.dat”);
FileWriter fw = new FileWriter(“b.dat”);
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
변환과정
바이트 스트림 -> 문자 스트림 -> Buffered 스트림
4.4 Buffered 스트림
예제 – System.in!!!
import java.io.*;
public class BufferedwriteLineMain {
public static void main(String[] args) throws IOException{
int i, len=0;
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String temp;
if((temp=br.readLine())!= null ){
System.out.println(temp);
}
}
}
4.4 Buffered 스트림(계속)
설명
InputStream인 System.in으로 문자를 핸들하는 예제
다음과 같이 실행하면 다음과 같이 출력된다.
입력 : Hello World 안녕하세요 줄 단위 읽기 테스트
출력 : Hello World 안녕하세요 줄 단위 읽기 테스트
즉, console상으로 입력된 값이
InputStream을 InputStreamReader로 변환
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
InputStreamReader를 BufferedReader로 변환
BufferedReader br = new BufferedReader(isr);
BufferedReader를 이용해서 줄 단위로 데이터 읽어내기
if((temp=br.readLine())!= null ){
System.out.println(temp);
}
BufferedReader를 이용해서 텍스트 파일에 존재하 는 모든 데이터를 줄 단위로 읽을 려면
다음과 같이 while문을 사용하면 된다.
String temp;
while((temp=br.readLine())!= null ){
//작업
}
BufferedReader를 사용하는 이유
버퍼링을 지원해 주기 때문
BufferedReader가 readLine()을 제공해주기 때문에 문자 열을 라인별로 다루기가 손쉽다.
4.4 Buffered 스트림(계속)
4.4 Buffered 스트림
예제 – System.in!!!
import java.io.*;
public class BufferedReadLineMain2 {
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader(
“a.dat”);
BufferedReader br = new BufferedReader(fr);
String temp;
while((temp=br.readLine())!= null ){
System.out.println(temp);
}
br.close();
}
}
실행결과
HelloHello
5 직렬화(Serialization)
5.1 직렬화란?
객체를 컴퓨터에 저장했다가 다음에 다시 꺼내어 쓸수 없는지
또는 네트워크를 통해 컴퓨터간에 객체를 주고 받 을 수는 없을까라고 고민해 본적이 있는가?
바로 직렬화를 통해 이러한 일들이 가능하다!
5.1 직렬화란?
직렬화
객체를 데이터 스트림으로 만드는 것
객체에 저장된 데이터를 스트림에 쓰기위해 연속적인 데이 터로 변환하는 것
반대로 스트림으로 부터 데이터를 읽어서 객체를 만드는 것을 역직렬화라고 한다.
객체를 연속적인 데이터로 변환한다는 것은 객체의 모든 인스턴스 변수의 값을 저장한다는 것이다.
어떤 객체를 저장하고자 한다면 현재 객체의 모든 인스턴 스 변수의 값을 저장하기만 하면 된다.
클래스에 정의된 인스턴스 변수가 단순히 기본형일 경우에 는 인스턴스 변수의 값을 저장하는 일이 간단하다.
그러나 참조형인 경우는 간단하지 않다.
5.1 직렬화란?
직렬화(계속)
예를 들어 인스턴스 변수의 타입이 배열인 경우
배열에 저장된 값들도 모두 저장되어야 할 것이다.
그러나 우리는 어떻게 객체를 직렬화해야 하는지 고민하지 않아도 된다.
다만 객체를 직렬화/역직렬화 할 수 있는
ObjectInputStream과 ObjectOutputStream을 사용하는 방 법만 알면 된다.
즉, 직렬화 할 때에는 ObjectInputStream
역직렬화 할 때에는 ObjectOutputStream을 사용한다.
ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)
5.2 ObjectInputStream, ObjectOutputStream
객체를 직렬화할때
다음과 같이 하면 된다.
FileOutputStream fos = new
FileOutputStream(“objectfile.ser”);
ObjectOutputStream = new ObjectOutputStream(fos);
out.writeObject(new UserInfo());
이 코드는 objectfile.ser이라는 파일에 UserInfo클래스의 객체를 직렬화하여 저장한다.
즉, ObjectOutputStream의 writeObject(Object obj)를 이용해서 객체를 출력하면 객체가 파일에 직렬화되어 저장 된다.
객체를 역직렬화할때
간단하다
직렬화때와는 반대로 입력스트림을 사용하고 writeObject(Object obj) 대신
writeObject()를 사용하여 저장된 데이터를 읽기만 하면 객체 로역직렬화된다.
다만 writeObject의 리턴타입이 Object이기 때문에 객체원래 의 타입으로 형변환해주어야 한다.
FileInputStream fis = new
FileInputStream(“objectfile.ser”);
ObjectInputStream in = new ObjectInputStream(fis);
UserInfo info = (UserInfo)in.writeObject();
이 두 스트림 클래스는 write/writeObject메서드 외에도 다양한 메서드를 제공한다.(API참조!)
5.2 ObjectInputStream, ObjectOutputStream
ObjectInputStream 클래스의 메서드들
void defaultReadObject()
int read()
int read(byte[] buf, int off, int len)
boolean readBoolean()
byte readByte()
char readChar()
double readDouble()
float readFloat()
int readInt()
long readLong()
short readShort()
Object readObject()
String readUTF()
5.2 ObjectInputStream, ObjectOutputStream
ObjectOutputStream 클래스의 메서드들
void defaultWriteObject()
int write(byte[] buf)
int write(int val)
int write(byte[] buf, int off, int len)
boolean writeBoolean(boolean val)
byte writeByte(int val)
byte writeBytes(String str)
char writeChar(int val)
char writeChars(String str)
double writeDouble(double val)
float writeFloat(float val)
int writeInt(int val)
long writeLong(long val)
short writeShort(short val)
Object writeObject(Object val)
String writeUTF(String str)
5.2 ObjectInputStream, ObjectOutputStream
두페이지에 걸쳐 나열된 메서드들은 직렬화와 역직렬 화를 직접 구현할 때 주로 사용되고,
반면에 defaultReadObjct(), defaultWriteObjct()는 자동직렬화를 수행한다.
5.2 ObjectInputStream, ObjectOutputStream
간단하다.
java.io.Serializable 인터페이스를 구현하도록 하면 된다.
예를 들어 다음과 같은 클래스가 있다고 가정하자
class UserInfo{
String name;
String password;
int age;
}
이 클래스를 직렬화가 가능하도록 하고자 한다면,
class UserInfo implements java.io.Serailizable{
String name;
String password;
int age;
}
이 인터페이스는 아무런 내용도 없는 빈 인터페이스이지만 직 렬화를 고려하여 작성한 클래스인지를 판단하는 기준이 된다.
5.3 직렬화 가능한 클래스 만들기
아래와 같이 Serializable을 구현한 클래스를 상속받는 다면 따로 Serializable을 구현하지 않아도 된다.
class SuperUserInfo implements java.io.Serailizable{
String name;
String password;
}
class UserInfo extends SuperUserInfo{
int age;
}
그러나 아래와 같은 경우에는 name과 password는 직렬화 대상에서 제외된다.
class SuperUserInfo {
String name;
String password;
}
class UserInfo extends SuperUserInfo implements java.io.Serailizable{
int age;
}
5.3 직렬화 가능한 클래스 만들기
앞의 경우에는 name과 password가 직렬화 대상이 되기 위해서는
SuperUserInfo클래스가 Serializable을 구현하던지
아니면 UserInfo의 상위클래스의 인스턴스변수들이 직렬 화 되도록 하는 코드를 추가해야 한다.
5.3 직렬화 가능한 클래스 만들기
아래의 UserInfo 클래스는 Serializable을 구현하고 있지만 이 클래스의 객체를 직렬화 하면 java.io.NotSerializableException이 발생한다.
class UserInfo implements java.io.Serailizable{
String name;
String password;
int age;
Object obj = new Object();
}
이는 직렬화 할 수 없는 클래스인 Object클래스의 객체인 obj를 포함하고 있기 때문이다.
모든 클래스의 조상인 Object클래스는 Serializable을 구 현하지 않았다.
당연한 얘기다 만일 그렇다면 모든 클래스의 조상은 Object가 아닌 Serializable이 되었을 것이다!
5.3 직렬화 가능한 클래스 만들기
그러나 다음의 경우에는 직렬화가 가능하다
class UserInfo implements java.io.Serailizable{
String name;
String password;
int age;
Object obj = new String(
“abc”);
}
인스턴스 변수의 타입이 아닌 연결된 객체의 종류에 의해 직렬화 여 부가 결정된다는 사실에 주목하자.
직렬화 하고자 하는 객체의 클래스에 직렬화가 안되는 객체에 대한 참조를 포함하고 있다면 transient 키워드를 붙여서 직렬화 대상에서 제외하면 된다.
또는 password같이 보안상 직렬화가 되면 안되는 멤버에 대해서도 역시 transient를 사용할 수 있다.
transient가 붙은 멤버는 직렬화시 그 타입의 기본값으로 직렬화된다.
즉, 기본형 타입은 기본형 타입대로의 기본값으로
참조형 타입읍 null로 그 값이 정해진다는 얘기다.
5.3 직렬화 가능한 클래스 만들기
public class UserInfo implements java.io.Serializable {
String name;
String password;
int age;
public UserInfo() {
this("Unknown", "1111", 0);
}
public UserInfo(String name, String password, int age) {
this.name = name;
this.password = password;
this.age = age;
}
public String toString() {
return "("+ name + "," + password + "," + age + ")";
}
}
5.3 직렬화 가능한 클래스 만들기
import java.io.*;
import java.util.ArrayList;
public class SerialEx1 {
public static void main(String[] args){
try {
String fileName = "UserInfo.ser";
FileOutputStream fos = new FileOutputStream(fileName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(bos);
UserInfo u1 = new UserInfo("JavaMan","1234",30);
UserInfo u2 = new UserInfo("JavaWoman","4321",26);
ArrayList list = new ArrayList();
list.add(u1);
list.add(u2);
// 객체를직렬화한다.
out.writeObject(u1);
out.writeObject(u2);
out.writeObject(list);
out.close();
System.out.println("직렬화가잘끝났습니다.");
} catch(IOException e) {
e.printStackTrace();
}
} // main
} // class
5.3 직렬화 가능한 클래스 만들기
생성한 객체를 직렬화하여 파일에 저장하는 예제이다.
버퍼를 이용한 FileOutputStream을 기반으로 하는 ObjectOutputStream을 생성한 다음
writeObject()를 이용해서 객체를 ObjectInputStream에 출력 하면 UserInfor.ser파일에 객체가 직렬화되어 저장된다.
얼핏보면 간단한 작업으로 보일 수는 있지만 객체에 정의된 모 든 인스턴스 변수에 대한 참조를 찾아 들어가기 때문에 상당히 복잡하고 시간이 걸리는 작업이 될 수 있다.
직렬화 하여 저장하는 파일의 확장자는 보통 ser로는 하지만 특별한 제약은 없다.
5.3 직렬화 가능한 클래스 만들기
import java.io.*;
import java.util.ArrayList;
public class SerialEx2{
public static void main(String[] args){
try {
String fileName = "UserInfo.ser";
FileInputStream fis = new FileInputStream(fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream in = new ObjectInputStream(bis);
// 객체를읽을때는출력한순서와일치해야한다.
UserInfo u1 = (UserInfo)in.readObject();
UserInfo u2 = (UserInfo)in.readObject();
ArrayList list = (ArrayList)in.readObject();
System.out.println(u1);
System.out.println(u2);
System.out.println(list);
in.close();
} catch(Exception e) {
e.printStackTrace();
}
} // main
} // class
5.3 직렬화 가능한 클래스 만들기
직렬화한 객체를 역직렬화하는 예제이다.
아까와는 반대로 FileInputStream과 ObjectInputStream을 그 리고 writeObject대신 readObject를 사용했다는 점을 제외하 고는 거의 같다.
다만 readObject의 return type이 Object이므로 원래의 타입 으로 형변환을 해주어야 하는 점만 주의하면 된다.
주의해야 할 점은 객체의 직렬화 순서와 맞게 역직렬화를 해주 어야 하는 점이다.
위의 예제처럼 u1, u2, list의 순서로 직렬화 했다면 역직렬화 할때도 u1, u2, list의 순서로 해주어야 한다.
그래서 객체가 많은 경우 각 객체를 개별적으로 하는 것보다는 ArrayList와 같은 컬렉션에 저장하여 직렬화 하는 것이 더 편리 하다.
5.3 직렬화 가능한 클래스 만들기
class SuperUserInfo {
String name;
String password;
SuperUserInfo() {
this("Unknown","1111");
}
SuperUserInfo(String name, String password) {
this.name = name;
this.password = password;
}
} // class SuperUserInfo
5.3 직렬화 가능한 클래스 만들기
import java.io.*;
public class UserInfo2 extends SuperUserInfo implements java.io.Serializable {
int age;
public UserInfo2() {
this("Unknown", "1111", 0);
}
public UserInfo2(String name, String password, int age) {
super(name, password);
this.age = age;
}
public String toString() {
return "("+ name + "," + password + "," + age + ")";
}
private void writeObject(ObjectOutputStream out)throws IOException {
out.writeUTF(name);
out.writeUTF(password);
out.defaultWriteObject();
}
private void readObject(ObjectInputStream in)throws IOException, ClassNotFoundException {
name = in.readUTF();
password = in.readUTF();
in.defaultReadObject();
}
} // class UserInfo2
5.3 직렬화 가능한 클래스 만들기
직렬화되지않은조상으로부터상속받은인스턴스벼수 에대하직렬화에대한처리방법이다.
직렬화할 객체의 클래스에 아래와 같이 writeObject와 readObject를 추가해서 조상으로부터 상속받은 변수인 name 과 password가 직접 직렬화 되도록 해야 한다.
name과 password의 타입이 String이기 때문에 writeUTF와 readUTF를 사용했다.
인스턴스 변수의 타입에 따라 다양한 메서드를 제공하기 때문에 그에 맞춰 호출하며 될것이다.
5.3 직렬화 가능한 클래스 만들기
직렬화한 객체를 역직렬화 할때는 직렬화 했을 때와 동일 한클래스를사용해야한다.
그러나 클래스명은 같지만 클래스의 내용이 변경된 경우 에는 역직렬화는 실패하게 된다.
다음과 같은 예외가 발생한다.
java.io.InvalidClassException: UserInfo; loc class incompatible: stream classdesc serialVersionUID = 695367358333894289, local class serialVersionUID = -6256164443556992367
이 예외의 내용은 직렬화 할때와 역직렬화 할때의 클래스 버전 이 다르다는 것이다.
5.4 직렬화 가능한 클래스의 버젼관리
객체가 직렬화 될때 클래스에 정의된 멤버들의 정보 를 이용해서 serialVersionUID라는 클래스의 버전을 자동생성해서 직렬화 내용에 포함한다.
그래서 역직렬화 할때 클래스의 버전을 비교함으로써 직렬화 할때의 클래스의 버전과 일치하는지 확인할 수 있는 것이다.
5.4 직렬화 가능한 클래스의 버젼관리
그러나 static변수나 상수 또는 transient가 붙은 인 스턴스 변수가 추가되는 경우에는 직렬화에 영향을 미치지 않기 때문에 클래스의 버전을 다르게 인식할 필요는 없다.
네트웍으로 객체를 직렬화하여 전송하는 경우
보내는 쪽과 받는 쪽이 모두 같은 버전의 클래스를 가지고 있어야 하는데
클래스가 조금만 변경되어도 클래스를 재배포하는 것은 프 로그램 관리상 무척 어려운일이다.
이럴 때는 클래스의 버전을 수동으로 관리하는것이 합리적 이다.
5.4 직렬화 가능한 클래스의 버젼관리
다음과 같이 MyData라는 직렬화가 가능한 클래스가 있을때 클래스의 버전을 수동으로 관리하고 싶다면
class MyData implements java.io.Serailizable{
String name;
String password;
}
다음과 같이 serialVersionUID를 추가해 준다.
class MyData implements java.io.Serailizable{
String name;
String password;
static final long serialVersionUID = 32492749243729384;
}
이렇게 해주면 클래스의 내용이 바뀌어도 클래스의 버전이 자동생성된 값으로 변경되지 않는다.
5.4 직렬화 가능한 클래스의 버젼관리
serialVersionUID의 값은 정수값이면 어떠한 값으로 도 지정할 수 있지만 서로 다른 클래스간에 같은 값 을 가지지 않도록 serialver.exe를 사용해서 생성된 값을 사용하는 것이 좋다.
serialver MyData
이렇게 하면 클래스의 멤버에 대한 정보를 바탕으로 한 serialVersionUID를 생성하기 때문에 클래스에 커다란 변 경이 없는 한 항상 같은 값을 유지하게 된다.
5.4 직렬화 가능한 클래스의 버젼관리