18. 데이터 입출력
18.1 입출력 스트림
대상이 파일일뿐이지 네트워크나 데이터 등과 다르지 않다.
17장에서는 컬렉션이 흘럿고 여기서는 데이터 가 스트림에서 흐른다.
17장은 무조건 입력만 받는데 여기서는 입력스트림과 출력 스트림이 있는것이다.
데이터는 키보드를 통해 입력될수도이속 파일또는 프로그램으로부터 입력될 수도 잇다.
반대로 데이터는 모니터로 출력될수도잇고 파일에 저장되거나 다른 프로그램으로 전송될 수 있다.
프로그램을 기준으로 데이터가 들어오는 것이 입력스트림 나가는것이 출력스트림. 따로 따로 존재한다.
스트림은 단방향으로 데이터가 흐른다.
데이터는 키보드 파일 프로그램에서 읽을 수 잇고 출력은 모니터(콘솔) 파일 프로그램 등이 있다.
항상프로그램입장에서 생각해서 들어오는건 입력 나가는건 출력이다.
두 프로그램이 데이터를 교환한다면? 프로그램 기준에서 생각하면된다.
어떤 데이터를 입출력하느냐에 따라 스트림은 다음 두 종류로 구분가능하다.
바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할때 사용
문자스트림 : 문자만 입출력할때 사용
그러나 결국 흘러가는 데이터는 모두 바이트이다. 문자또한 바이트배열로 바뀌어서 흘러감.
이는 java.io 패키지에서 제공하고잇다.
InputStream OutputStream / Reader Writer 앞에 접두사가 붙어 특화된 스트림이 있다.
바이트스트림으로도 문자열을 읽을 수 잇으나 바이트로 읽은후 변환이 필요하기 때문에
문자 스트림을 쓰는게 조금더 편리하다.
18.2 바이트 출력 스트림
OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스이다. 모든 바이트 출력 스트림클래스는 이 OutputStream를 상속받아 만들어진다.
따라서 OutputStream을 직접 사용하는 것이 아니라 밑에걸 쓴다.
FileOutputStream, PirntStream, BufferedOutputStream, DataOutputStream 등 이 있다.
메소드는 다음과 같다
write(int b) 1개의 바이트출력
write(byte[] b) 바이트배열 전체출력
write(byte[] b, int off, int len) 바이트배열의 지정된 만큼 출력
write()는 바로 출력이 아니라 나갈 준비를 하는 것임. flush()를 사용해야 출력된다.
close()데이터를 다 받고 닫아야한다. 닫지 않으면 불안정해짐.
18.2.1 1바이트 출력
write(int b)메소드는 매개값 int(4byte)에서 끝 1byte만 출력한다. 매개변수가 itn타입이므로 4byte를 모두 보내는 것은 아니다.
예를들어 00000000 / 00000000/ 00000000/ 00000001 이 있으면 00000001이 출력되는것이다.
public class WriteExample {
public static void main(String[] args) {
try
(OutputStream os = new FileOutputStream("C:/Temp/test1.db")) {
//절대 경로
// "C:/Temp/test1.db", true 기존파일 뒤에다 저장
// "C:/Temp/test1.db", false 기존파일 덮어쓰기
//파일에 저장하는데 없으면 파일생성해줌. 그러나 폴더가 없으면 예외가 발생함
byte a = 10; //00001010 8비트 = 1바이트
byte b = 20;
byte c = 30;
os.write(a); // byte가 write로 들어가면 int로 자동타입변환 byte는 -128~127이므로 초과하면 원래 그대로가 안됨
os.write(b);
os.write(c);
os.flush(); // write는 '버퍼'에서 준비만 하는 것이고 버퍼에서 바이트를 쏟아내기 위해 flush를 사용해줘야한다.
//os.close();
//위에서 클로즈하는것은 예외발생하면 클로즈가 안되서 좋은 것은 아님..! 그래서 finally에 close해주면 좋다
//FileOutputStream 가 자동리소스닫기를 구현하고있어서 자동닫기를 사용함.
} catch (Exception e) { //fileNotFoundException | IOException 두개발생가능
e.printStackTrace();
}
/*
finally {
os.close();
}
*/
}}18.2.2 바이트 배열 출력
writ(byte[] b)사용
일반적으로 1바이트를 출력하는 경우는 드물고 보통 바이트 배열을 통채로 출력하는 경우가 많다.
public class WriteExample {
public static void main(String[] args) {
try
(OutputStream os = new FileOutputStream("C:/Temp/test2.db")){
//자동닫기사용
byte[] array = {10 , 20 , 30};
os.write(array);
os.flush();
//os.close();
//간단한 프로세스가 끝나는거라 여기서 닫아도되는데 finally에서 닫던지 자동닫기를 사용애야함.
} catch (Exception e) {
e.printStackTrace();
}
/*
finally {
os.close();
}
*/
}}18.2.3 바이트배열의 일부분만 출력하기
바이트배열은똑같고 위치정보를 제공하기
os.write(byte[] b, int off(시작인덱스), int len(몇개까지 할지));
b[off]부터 len개의 바이트 출력
public class WriteExample {
public static void main(String[] args) {
try (OutputStream os = new FileOutputStream("C:/Temp/test3.db")){
//자동닫기 사용
byte[] array = {10 , 20 , 30, 40 ,50};
os.write(array,1 ,3); //1번인덱스부터 3개까지만 출력
os.flush();
// os.close(); 간단한 프로세스가 끝나는거라 여기서 닫아도되는데 ui만들때 켜져있는 동시에 이런거 할때 여기서 닫는거 좋지않다는 걸 인지하자.
//finally를 쓰거나 자동닫기사용
} catch (Exception e) {
e.printStackTrace();
}
/*
finally {
os.close();
}
*/
}}18.3 바이트 입력 스트림
InputStream은 바이트 입력스트림의 최상위 클래스로 추상클래스이다.
InputStream를 상속받아 FileInputStream BufferedInputStream DataInputStream이 있다.
메소드는 다음과 같다.
read() 1바이트읽고 바이트리턴 데이터를 리턴
read(byte[] b) 읽은 바이트를 매개값으로 주어진 배열에 저장후 읽은 바이트 수를 리턴 . 실제 읽은 데이터는 바이트배열에 저장 바이트배열만큼 읽을수잇다.
18.3.1 1바이트 읽기
입력스트림에서 5개의 바이트가 들어오면 read()메소드를 5번사용해서 읽어야한다.
더이상 입력스트림으로부터 바이트를 읽을 수 없다면 read() 는 -1을 리턴한다.
5번읽고 한번더읽고 -1리턴 하고 끝
public class ReadExample {
public static void main(String[] args) {
InputStream is = null;
try {
is = new FileInputStream("C:/temp/test1.db");
while(true) {
int data = is.read();
if(data == -1) break;
System.out.println(data);
}
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {}
}}}18.3.2 바이트배열로 읽기
read(byte[] b) 메소드는 입력 스트림으로부터 주어진 배열의 길이만큼 바이트를 읽고 배열에 저장한 다음 읽은 바이트 수를 리턴한다.
예를들어 입력스트림에 5개의 바이트가 들어오면 길이 3인배열로 두번읽을수있다.
int num = read(byte[] b) 배열 읽은 수만큼 리턴
한번에 100바이트가 들어온다면 read()메소드는 100번을 반복해서 읽어야하지만 read(byte[]0 b는 한번읽을때배열길이만큼 읽는다.
103잇으면 100 1번읽고 3한번읽고 -1리턴 3번돔
public class ReadExample {
public static void main(String[] args) {
InputStream is = null;
try {
is = new FileInputStream("C:/temp/test2.db");
//읽은 바이트를 저장할 배열생성
byte[] data = new byte[100];
while(true) {
int num = is.read(data); //최대 읽을수있는 바이트를 읽고 배열에 저장 읽은바이트는 리턴 지금은 num = 3
if(num == -1) break; //읽을게없으면 -1리턴
for(int i =0 ; i<num ; i++) { //num만큼 읽은 만큼 도니가 3번
System.out.println(data[i]);
}
}
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {}
}}}18.3.3 파일복사
파일복사의 원리는 FileInputStream에서 읽은 바이트를 바로 FileOutputStream으로 출력하는 것이다.
임의의 JPEG 그림파일을 하나 준비해 파일이름을 test.jpg로 변경한다음 C:/Temp폴더에 저장한다.
파일을 읽고 다시 저장하면 복사임.
public class CopyExample {
public static void main(String[] args) {
String originalFileName = "C:/Temp/test.png";
String targetFileName = "C:/Temp/test2.png";
try (InputStream is = new FileInputStream(originalFileName);
OutputStream os = new FileOutputStream(targetFileName)) {
byte[] data = new byte[1024];
while(true) {
int num = is.read(data); //최대 1024바이트를 읽고 배열에 저장 읽은 바이트는 리턴
if (num == -1) break; //76.5kb라고 가정하면 76번실행하고 512바이트남음
os.write(data, 0 , num); //짜투리가 남아서 마지막엔 os.write(data, 0 , 512)가 됨
//읽은 바이트 수만큼 출력 짜투리가 남기때문에 이걸 사용해야함.
//읽고 쓰고 읽고 쓰고 이걸 반복하다가 없으면 -1 리턴하고 종료
}
os.flush(); //내부버퍼잔류바이트를 출력하고 버퍼를 지움
System.out.println("복사가 잘되었습니다.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } }18.4 문자 입출력 스트림
바이트 입출력 스트림인 InputStream과 OutputStream에 대응하는 문자 입출력 스트림으로 Reader 와 Writer가 있다.
입출력 단위가 문자인것을 제외하고는 바이트 입출력 스트림과 사용방법은 동일하다.
18.4.1 문자출력
Writer는 문자 출력 스트림의 최상위 클래스로 추상클래스이다.
FileWriter BufferedWriter PirntWriter OutputStreamWriter가 있다.
write(int c) 매개값으로 주어진 한 문자를 출력 write(char[] cbuf)char 배열 모든 문자 출력
write(String str) 문자열 전체를 출력

write는 OutputStream과 사용방법은 동일하지만 출력 단위가 문자(char)단위이다. 그리고 문자열을 출력하는 write(String str) 메소드를 추가로 제공한다.
public class WriteExample {
public static void main(String[] args) {
try //문자기반 출력 스트림 생성 + 자동닫기
(Writer writer = new FileWriter("C:/Temp/test.txt")) //확장명 중요 x
{
//1문자씩 출력
char a = 'A';
writer.write(a);
char b = 'B';
writer.write(b);
//char배열출력
char[] arr = { 'C', 'D', 'E' };
writer.write(arr);
//문자열출력
writer.write("FGH");
//버퍼에 잔류한 문자열 출력 버퍼를 비움
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} }}18.4.2 문자 읽기
Reader는 문자 입력 스트림의 최상위 클래스로 추상클래스이다.
FileReader BufferdReader InputStreamReader가 있다.
Reader가 가진 메소드는 다음과같다.
같은데 차이점은 바이트가 아닌 한 문자를 읽은것임.
char를 사용한다는 것 유의하자!!!
public class ReadExample {
public static void main(String[] args) {
Reader reader = null;
try{
//1문자씩 읽기
reader = new FileReader("C:/Temp/test.txt");
while (true) {
int data = reader.read();
if(data == -1 ) break;
System.out.print((char) data + " "); //data int니까 강제변환
}
reader.close();
System.out.println();
//문자 배열로 읽기
reader = new FileReader("C:/Temp/test.txt");
char[] data = new char[100];
while (true) {
int num = reader.read(data);
if(num == -1 ) break;
for(int i =0; i< num ; i++) {
System.out.print(data[i] + " ");
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}}}18.5 보조스트림
바이트스트림은 바이트를 다루는데 애플리케이션은 바이트를 다루지않아서 변환하는 작업이 필요하다.
int long double등 사용하고 문자열을 사용한다. 실제 애플리케이션에서 불편하다.
보조스트림을 보통 결합해서 사용한다.
좀 더 편리하게 애플리케이션에서 데이터를 입출력하기 위해, 성능을 향상시키기위해 사용한다.
대부분 같이쓰기 때문에 잘사용해야함.
보조스트림이란 다른 스트림과 연결되어 여러가지 편리한 기능을 제공해주는 스트림을 말한다.
혼자서는 사용하지 못하기때문에 주 스트림이 있어야한다. 단독으로는 사용불가 결합해야함.
사용하는 이유?
1.애플리케이션에서 다루는 데이터와 실제로 입출력하는 데이터 형태가 다를 수있기때문
애플리케이션은 int double 등을 다루는데 InputStream과 OutputStream은 byte를 사용하기 때문이다.
int double을 byte로 변환하는게 불편한데 보조스트림이 있으면 바로 int double값들을 입출력할수 있다.
2.성능이 좀 더 빠르다.
입출력스트림에 보조스트림을 연결하려면 보조스트림을 생성할때 생성자 매개값으로 입출력 스트림을 제공하면된다.
보조스트림 변수 = new 보조스트림(입출력스트림);
InputStream is = new FileInputStream("...");
InputStreamReader reader = new InputStreamReader(is);
보조스트림은 또 다른 보조스트림과 연결되어 스트림 체인으로 구성할 수도 있다.
보조스트림1 성능향상용 보조스트림2 편리하게 데이터 입출력받기위한 용
InputStream is = new FileInputStream("...");
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
자주사용하는 보조스트림은 다음과 같다.
18.6 문자변환 스트림
바이트에서 입출력할 데이터가 문자라면 문자스트림으로 변환해서 사용하는 것이 좋다.
문자로 바로 입출력하는 편리함이 있고 문자셋의 종류를 지정할수 있기 때문이다.
18.6.1 InputStream 을 Reader로 변환
InputStream을 Reader로 변환하려면 InputStreamReader을 보조스트림연결하면된다.
InputStream is = new FileInputStream("파일")
Reader reader = new InputStreamReader(is);
바이트를 문자스트림으로 변환시켜줌.
FileReader의 원리
FileInputStream 에 InputStreamReader를 연결하지 않고 FileReader를 직접생성할수있다.
FileReader는 InputStreamReader의 자식클래스로 이것은 FileReader가 FileInputStream에
InputStreamReader보조스트림연결한것이라고 볼수있다.
18.6.2 OutStream을 Writer로 변환
OutputStream을 writer로 변환하려면 OutStreamWriter을 보조스트림연결하면된다.
바이트배열을 얻어서 getbyte를 얻어서 사용해도되는데 당장 출력하고자하면 보조스트림을 달면된다.
OutputStream os = new FileOutputStream("파일")
Writer writer = new OutputStreamWriter(is);
문자 -> writer ->바이트 -> 아웃풋스트림
public class CharacterConvertStreamExample2 {
public static void main(String[] args) {
try {
OutputStream os = new FileOutputStream("C:/Temp/test.txt");
Writer writer = new OutputStreamWriter(os, "UTF-8");
String str = "문자 변환 스트림을 사용합니다.";
//바이트배열만들기
//byte[] data = str.getBytes("UTF-8"); 바이트를 utf-8로 변환하기
//os.write(data);
//이렇게 하면 바이트배열을 만들고 변환하는 과정이 필요하기때문에 너무 복잡하다.
//os.flush();
//os.close();
writer.write(str);
writer.flush();
writer.close();
InputStream is = new FileInputStream("C:/Temp/test.txt");
//txt라 FileReader하는게 빠르지만 몰라서 인풋스트림사용햇다면
Reader reader = new InputStreamReader(is, "UTF-8");
//바이트배열만들고 읽기
//byte[] data = new byte[100];
//int num = is.read(data);
//받은 바이트배열을 스트링으로 디코딩하기
//String str = new String(바이트배열, "저장형식");
//String str2 = new String(data, "UTF-8");
//System.out.println(str2);
//이러면 가독성이 너무떨어짐
char[] data = new char[20]; //바이트수가 아닌 문자 수 최대 20자까지
int num = reader.read(data); //실제로 읽은 문자 수 리턴
String str2 = new String(data, 0 , num); //인덱스0부터 읽은 수까지 리턴
System.out.println(str2);
} catch (Exception e) {
e.printStackTrace();
} }18.7 성능향상 스트림
하드웨어가 좋으면 성능이 좋아지지만 이것을 좀더 좋게 만들기 위해 프로그램에서 조절할 수 있다.
cpu와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행성능은 하드디스크의 처리속도에 맞춰진다.
네트워크로 데이터를 전송할때도 느린 네트워크 환경이면 컴퓨터 사양이 좋아도 메신저와 게임의 속도는 느릴수밖에없다.
이문제에 대한 완전한 해결책은 될 수 없지만 프로그램 이 입출력소스(파일,네트워크)와 직접작업하지 않고 중간에 메모리 버퍼와 작업함으로써 실행성능을 향상할수있다.
출력 스트림의 경우 직접 하드 디스크에 데이터를 보내지 않고 메모리 버퍼에 데이터를 보냄으로써 출력속도를 향상시킬수있다.
버퍼는 데이터가 쌓이기를 가다렷다가 꽉차게 되면 데이터를 한꺼번에 하드디스크로 보냄으로써 출력 횟수를 줄여준다.
반대의 경우도 같다. 하드디스크에서 버퍼에 데이터를 미리 채워놓고 프로그램이 데이터가 필요할때 한번에 받아오면 더빨라진다.
입력스트림에서도 버퍼를 사용하면 읽기 성능이 좋아진다.
BufferedInputStream bis = new BufferedInputStream(바이트 입력스트림);
BufferedOutputStream bos = new BufferedOutputStream(바이트 출력스트림);
BufferedReader br = new BufferedReader(문자 입력스트림);
BufferedWriter bw = new BufferedWriter(문자 입력스트림);
보조스트림에도 사용가능하다.
public class BufferExample {
public static void main(String[] args) {
try {
//상대경로로 파일 위치 얻기 이 클래스에 있는 리소스 경로 얻기
String originalFilePath1 = BufferExample.class.getResource("originalFile1.jpg").getPath();
String targetFilePath1 = "C:/Temp/targetFile1.jpg"; //얻은 파일을 복사
//입출력 스트링생성
FileInputStream fis1 = new FileInputStream(originalFilePath1);
FileOutputStream fos1 = new FileOutputStream(targetFilePath1);
//buffered사용
String originalFilePath2 = BufferExample.class.getResource("originalFile2.jpg").getPath();
String targetFilePath2 = "C:/Temp/targetFile2.jpg"; //얻은 파일을 복사
FileInputStream fis2 = new FileInputStream(originalFilePath2);
BufferedInputStream bis = new BufferedInputStream(fis2);
FileOutputStream fos2 = new FileOutputStream(targetFilePath2);
BufferedOutputStream bos = new BufferedOutputStream(fos2);
long nonbufferTime = copy(fis1, fos1);
System.out.println("nonbufferTime: " + nonbufferTime + "ns");
long bufferTime = copy(bis, bos);
System.out.println("bufferTime: " + bufferTime + "ns");
fis1.close();
fos1.close();
bis.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static long copy(InputStream is, OutputStream os) throws Exception{
long start = System.nanoTime();
//시간측정위해 1바이트씩 읽고쓰기
while(true) {
int data = is.read();
if(data == -1 ) break;
os.write(data);
}
os.flush();
long end = System.nanoTime();
return (end - start);
}}BufferedReader를 연결하면 성능향상 뿐만아니라 좋은점이 한가지 더있는데 행단위로 문자열을 읽는 매우 편린한 readLine()메소드를 제공한다.
파일에서 한행씩 읽기
BufferedReader br = new BufferedReader(new FileReader("");
while (true){
String str = br.readLine();
if(str== null) break; }
이 보조스트림은 Reader와만 결합이 가능하다.
이전장에서 밑의 행위를 한것은 Reader와만 결합이 가능하니가 해준거임.
InputStream is = new FileInputStream("C:/Temp/test.txt"); //txt라 FileReader하는게 빠르지만 몰라서 인풋스트림사용햇다면
Reader reader = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(reader);
문자열 입력스트림 Reader에 BufferdReader를 연결하면 성능향상뿐만아니라 좋은점이 한가지 더있는데 행 단위로 문자열을 읽는 매우 편리한 readLine()메소드를 제공한다.
BufferedReader br = new BufferedReader(new FileReader("");
while (true){
String str = br.readLine();
if(str== null) break; }
이 보조스트림은 Reader와만 결합이 가능하다.
이 전장에서 밑의 행위를 한것은 Reader와만 결합이 가능하니가 해준거임.
InputStream is = new FileInputStream("C:/Temp/test.txt"); //txt라 FileReader하는게 빠르지만 몰라서 인풋스트림사용햇다면
Reader reader = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(reader);
소스코드를 읽고 뱉어내는 예제
보조스트림을 어디에 결합할수 있는지는 도큐먼트에서 생성자의 매개타입을 보면 알 수 있다.
데이터 대상이 파일일뿐 네트워크에서도 똑같이 쓴다.
public class ReadLineExample {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("src/ch18/sec07/exam02/ReadLineExample.java")
//앞에는 이프로젝트 기준이니 ㄱㅊ음. 좋은방법은 아님. 간단한게 사용하기 위해 일단 넣음.
);
int lineNo = 1;
while(true) {
String str = br.readLine();
//더이상읽을게 없으면 null을 뱉어냄.
if(str == null) break;
System.out.println(lineNo + "\t" + str);
lineNo++;
}
br.close();}}18.8 기본타입 스트림
바이트 스트림에 DataInputStream과 DataOutputStream 보조스트림을 연결하면 기본타입인 boolean char short int long float double값을 입출력할 수잇다.
정수값하나를 입출력하는데 바이트로 바꾸고 다시 복원하는게 귀찮으니 이걸 위해 사용한다.
바이트기반 스트림만 연결될 수 있다.
DataInputStream dis = new DataInputStream(바이트 입력 스트림);
DataOutputStream dos = new DataOutputStream(바이트 출력 스트림);
다음은 DataInputStream과 DataOutputStream 이 제공하는 메소드이다.
readUTF() 문자열을 읽기 writeUTF()문자열을 보내기
이 메소드를 사용해 입출력할때 주의점이 있다.
주의할점은 데이터 타입의 크기가 모두 다르므로 출력한 데이터를 다시 읽어올때는 출력한 순서와 동일한 순서로 읽어야한다는 것이다
public class DatatInputOutputStreamExample {
public static void main(String[] args) {
// DatatOutputStream 생성
try {
FileOutputStream fos = new FileOutputStream("C:/Temp/primitive.db");
DataOutputStream dos = new DataOutputStream(fos);
//기본타입출력
dos.writeUTF("홍길동");
dos.writeDouble(95.5);
dos.writeInt(1);
dos.writeUTF("김자바");
dos.writeDouble(90.3);
dos.writeInt(2);
dos.flush(); //write다음 무조건 flush
dos.close(); //보조스트림을 닫으면 주스트림도 닫혀서 fos.close(); 필요 x
FileInputStream fis = new FileInputStream("C:/Temp/primitive.db");
DataInputStream dis = new DataInputStream(fis);
//기본타입입력
for(int i = 0; i<2 ; i++) {
String name= dis.readUTF();
double score = dis.readDouble();
int order = dis.readInt();
System.out.println(name + ": " + score +": " + order);
}
dis.close();
} catch (Exception e) {
e.printStackTrace();
}}}18.9 프린트 스트림
PrintStream과 PrintWriter는 프린터와 유사하게 출력하는 스트림으로 print(), println() printf()메소드를 가지고 있는 보조스트림이다.
지금까지 우리는 콘솔에 출력하기 위해 System.out.println()을 사용하였는데 그 이유는 out이 PrintStream타입이기때문이다.
생성자에 File file도가능한데 이것을 주 스트림으로도 사용가능하다는 뜻임.
PrintStream ps = new PrintStream( 바이트 출력스트림);
PrintWriter pw = new PrintWriter(바이트, 문자 출력 스트림);
주의! 얘는 바이트든 문자든 다 결합가능함.
선택기준은 주 스트림에 따라 선택하다,.
둘다 메소드를 같게 가지고 있다.
printf()메소드는 형식화된 문자열출력하는데 2장 12절에서 이미 배웠다.
다음예제는 printf()메소드도 사용해본다.
public class PrintStreamExample {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("C:/Temp/printstream.txt");
PrintStream ps = new PrintStream(fos);
ps.print("마치 ");
ps.println("프린터가 출력하는 것처럼");
ps.println("데이터를 출력합니다.");
ps.printf("| %6d | %-10s | %10s | \n", 1, "홍길동", "도적"); //%-10s좌측정렬 10자리문자
ps.printf("| %6d | %-10s | %10s | \n", 2, "김자바", "학생"); //%10s우측정렬 10자리문자
ps.flush();
ps.close();
//파일 입력
FileInputStream fis = new FileInputStream("C:/Temp/printstream.txt");
Reader reader = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(reader);
while(true) {
String str = br.readLine();
if(str == null) break;
System.out.println(str);
}
/*
FileInputStream fis = new FileInputStream("C:/Temp/printstream.txt");
Reader reader = new InputStreamReader(fis);
char[] data = new char[50];
while(true) {
int num = reader.read(data);
if(num == -1) break;
String str = new String(data, 0 , num);
System.out.println(str);
}
FileInputStream fis = new FileInputStream("C:/Temp/printstream.txt");
byte[] data = new byte[100];
while(true) {
int num = fis.read(data);
if (num == -1 ) break;
String str = new String(data, "UTF-8");
System.out.println(str);
}
*/
} catch (Exception e) {
e.printStackTrace();
}}}18.10 객체 스트림
객체를 입출력하는 스트림. 지금까지 문자열 숫자 등 만 입출력했었다. 객체 그 자체도 입출력대상이 될 수 있다.
객체를 바이트로 바꿔야 파일 또는 네트워크로 출력할 수 있다.
객체를 출력하려면 필드값을 일렬로 늘어선 바이트로 변경해야하는데 객체자체는 문자가 아니니 이것을 바이트 기반스트림을 이용해야한다.
이것을 '직렬화'라고 한다. Serialization
반대로 직렬화된 바이트를 객체의 필드값으로 복원하는 것을 '역직렬화'이다.
ObjectInputStream과 ObjectOutputStream은 객체를 입출력할 수 있는 보조스트림이다.
ObjectOutputStream은 바이트 출력스트림과 연결되어 객체를 직렬화하고 ObjectInputStream는 바이트입력스트림과 연결되어 객체로 복원하는 역직렬화를 한다.
직렬화 역직렬화는 보조스트림이 하기때문에 코드상으로 어렵지는 않고 인풋아웃풋스트림에 연결만 해주면된다.
ObjectInputStream ois = new ObjectInputStream(바이트 입력스트림)
ObjectOutputStream oos = new ObjectOutputStream(바이트 출력스트림)
ObjectOutputStream로 객체를 직렬화하기 위해서는 writeObject(객체); 메소드를 사용한다.
반대로 readObject(객체)메소드는 읽은 바이트를 역질렬화 해서 객체로 생성한다.
readObject()메소드의 리턴타입은 Object 이므로 구체적인 타입으로 강제타입변환해야한다.
객체타입 변수 = (객체타입) ois.readObject();
그렇다면 모든 객체를 출력할수 있을까? 불가능함. 객체를 프로그램내부에서 사용한다면 다르다.
그런데 출력한다는 것은 외부로 보내는 것이다. 즉 다른쪽으로 보낸다는 것은 보안이 중요하다.
어떤객체든 출력하면 문제가 있다. 클래스를 설계할때 출력해도 좋다 라는 것을 나타내는 것이 있다.
public class 클래스이름 implements Serializable
아무것도 없는 인터페이스인데 그냥 직렬화 할수 있다라고만 나타내는 기능임.
역직렬화는 데이터를 복원하는 것이지 객체를똑같이 만드는것은아님.
결론적으로 m1번지 != m2번지 이다.
18.10.2 Serializable 인터페이스
Serializable가 무조건 있어야한다. 아무것도 없는데 어떤 역할을 하나?
안하면 java.io.NotSerializableException: 예외발생
비어있는 인터페이스 이고 그냥 객체를 직렬화 할 수 있다고 표시하는 역할을 한다.
데이터부분이 바이트배열로 만들어진다.
인스턴스 필드값은 직렬화 대상이지만 정적필드값과 transient로 선언된 필드값은 직렬화에서 제외된다.
String, Data 클래스타입도 직렬화 가능하다. 이 타입들이 Serializable구현되어있기때문임.
18.10.3 serialVesionUID필드
직렬화할때의 클래스와 역직렬화할때 클래스가 기본적으로 동일한 클래스여야하는데 같지 않는 경우가 발생하면 역직렬화에 실패한다.
완전하게 동일하지 않는 경우도 발생한다.
개발하는 사람이 다르면 발생한다.
클래스내용이 다르다할지라도 직렬화된 필드를 공통으로 포함하고 있다면 역직렬화할수있는 방법이 있다.
두 클래스가 동일한 serialVesionUID 상수값을 가지고 있으면 된다.
serialVesionUID의 값은 개발자가 임의로 줄 수 있지만 가능하다면 클래스마다 다른 유일한 값을 갖도록하는 것이 좋다.
이클립스는 serialVesionUID필드를 자동생성하는 기능을 제공한다.
implements Serializable를 붙인다음 마우스를 클래스 이름에 갖다대면 Add generated serial Vesion ID 링크가 나온다.
public class Member implements Serializable {
private static final long serialVersionUID = 6237230879659378976L;
private String id;
private String name;
public Member(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() { return id + ": " + name; }
}public class Product implements Serializable {
private static final long serialVersionUID = 2093895522785110099L;
private String name;
private int price;
public Product(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public String toString() { return name + ": " + price; }
}public class ObjectInputOutputStreamExample {
public static void main(String[] args) {
try {
//FileOutputStream에 ObjectOutputStream 보조스트림연결
FileOutputStream fos = new FileOutputStream("C:/Temp/object.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//객체생성
Member m1 = new Member("fall", "단풍이");
Product p1 = new Product("노트북", 1500000);
int[] arr = {1, 2, 3};
oos.writeObject(m1);
oos.writeObject(p1);
oos.writeObject(arr);
oos.flush();
oos.close();
//FileInputStream에 ObjectInputStream에 연결
FileInputStream fis = new FileInputStream("C:/Temp/object.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
//파일을 읽고 역직렬화해서 객체로 복원
Member m2 = (Member) ois.readObject();
Product p2 = (Product) ois.readObject();
int[] arr2 = (int[]) ois.readObject();
ois.close();
//toString재정의 되어있기때문에 읽기 가능
System.out.println(m2);
System.out.println(p2);
System.out.println(Arrays.toString(arr2));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}}}18.11 File과 File클래스
사실String클래스는아니지만 파일입출력과 많은 관련이 있다. java.nio.file임
nio는 new io 로 io보다 성능을 향상시키거나 좀 더 다양한 것을 제공한다.
파일 디렉토리 정보를 가지고있다.
18.11.1 File클래스
File 클래스로부터File객체를 생성하는 방법은 다음과같다.
File file = new file("경로"); 꼭 파일일 필요는없음. 해당 파일에 대한 객체 디렉토리 정보를 가지고 있는 객체임.
경로 구분자는 운영체재마다 조금씩 다르다. 윈도우에서는 \또는 /를 사용한다.
맥os및 리눅스는 /를 사용한다.
"C:/Temp/file.txt" "C:\Temp\file.txt"
File 객체를 왜만드나?
1.주어진 파일의 정보를 얻기 2.도큐먼트에 있는 생성자나 메소드를 사용할때 사용한다.
ex) PrintStream(File file)일때 File객체를 만들어서 제공
File 객체를 생성했다고 해서 파일이나 디렉토리가 생성되는 것은 아니다. 그리고 경로에 실제 파일이나 디렉토리가 없더라도 예외가 발생하지 않는다.
파일이나 디렉토리가 실제 있는지 확인하고싶다면 File객체를 생성하고나서 exist()메소드를 호출해보면 된다. 있다면 true리턴
boolean isExist = file.exist();

false로 리턴할 경우 메소드로 파일 또는 폴더를 생성할 수 있다.
creatNewFile()새로운 파일을 생성
boolean mkdir() 새로운 디렉토리를 생성
boolean mkdirs() 경로상에 없는 모든 디렉토리를 생성
"C:/Temp/dir1/dir2" 이게 없어도 파일객체가 만들어지는데 만들고 싶으면 위 메소드 사용
mkdir() 앞에는 존재할경우 마지막 파일만 생성 mkdirs() 경로상에 없는 디렉토리 전부생성
true인경우 사용할 수 있는 메소드는 다음과같다.
boolean canExecute()실행할수 있는 파일인지 여부
boolean canRead() 읽을수있는 파일인지 여부
String getName() 파일이름 리턴
Stirng getParent() 부모디렉토리 리턴
String getPaht()전체 경로를 리턴
boolean delete() 파일 또는 디렉토리삭제
boolean isDirectory() 디렉토리인지 여부
boolean isFile()파일인지여부
long lenght()파일의 크기리턴
boolean mkdirs() 경로상에 없는 모든 디렉토리를 생성
boolean delete() 파일 또는 디렉토리삭제
boolean isDirectory() 디렉토리인지 여부
boolean isFile()파일인지여부
long lenght()파일의 크기리턴
위 5가지는 꼭 알아두기
public class FileExample {
public static void main(String[] args) throws Exception {
//File 객체 생성
File dir = new File("C:/Temp/images"); //디렉토리
File file1 = new File("C:/Temp/file1.txt");
File file2 = new File("C:/Temp/file2.txt");
File file3 = new File("C:/Temp/file3.txt");
//존재하지 않으면 디렉토리 또는 파일 생성
if(dir.exists() == false) { dir.mkdirs(); } //없다면 모든 디렉토리 생성
if(file1.exists() == false) { file1.createNewFile(); } //없다면 파일 만들기 보통은 FileOutputStream으로 만듬
if(file2.exists() == false) { file2.createNewFile(); }
if(file3.exists() == false) { file3.createNewFile(); }
/*
FileOutputStream fos = new FileOutputStream("C:/Temp/file1.txt");
fos.write(); fos.flush();...
*/
//Temp 폴더의 내용을 출력
File temp = new File("C:/Temp");
File[] contents = temp.listFiles(); //Temp파일에 있는거를 배열에 넣어서 보기
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm");//날짜출력하기위한거
for(File file : contents) {
System.out.printf("%-25s", sdf.format(new Date(file.lastModified())));
if(file.isDirectory()) { //CMD창에 dir쳤을때 나오는것
System.out.printf("%-10s%-20s", "<DIR>", file.getName()); //디렉토리라면 DIR이라고
} else {
System.out.printf("%-10s%-20s", file.length(), file.getName()); //파일사이즈
}
System.out.println();
}}}입출력스트림을 생성할때 file객체 활용하기
사용하는 이유중 2. 도큐먼트 매개값으로 제공해야할때 활용한다.
FileOutputStream fos = new FileOutputStream("C:/Temp/file1.txt");
이거보단
File file = new File("C:/Temp/file1.txt");
FileOutputStream fos = new FileOutputStream(file);
18.11.2 Files클래스
Files클래스는 File클래스보다 좀 더 편리한 기능이고 정적메소드로 구성되어있어 객체로 만들 필요가 없다.
정적메소드는 다음과 같다.
파일 경로를 문자열이 아닌 Path라는 객체 형태로 받는다.
Path path = Paths.get(String first, String...);
get()메소드의 매개값은 파일 경로인데 전체 경로를 한꺼번에 저장해도 좋고 상위디렉토리와 하위 디렉토리를 나열해서 지정해도 좋다.
Path path = Paths.get("C:/Temp/dir/file.txt");
Path path = Paths.get("C:/Temp/dir", "file.txt");
Path path = Paths.get("C:", "Temp", "dir", "file.txt");
통문자열로 얻을 수도 있다 디렉토리는 디렉토리별로 파일은 파일별로 패스를 얻을 수도 있다.
결국 매개변수가 가변적임. 특이하게 상대경로도 표현가능하다.
현재경로에서 상대경로
Path path = Paths.get("dir/file.txt"); == Path path = Paths.get("./dir/file.txt"); .은 현재 폴더를 의미
Path path = Paths.get("../dir/file.txt"); ..은 상위 디렉토리로 가라
File이나 Files나 어떤걸 사용해도 좋음
Files가 더 많은 메소드를 제공하니 copy()와 같은 경우 이게 더편하니 이거 사용해도 된다.
public class FilesExample {
public static void main(String[] args) {
try {
String data = "" +
"id: winter\n" +
"email: winter@mycompany.com\n" +
"tel: 010-123-1234";
//Path 객체 생성
Path path = Paths.get("C:/Temp/user.txt");
//파일 생성 및 데이터 저장 입출력 스트링을 사용해도되는데 이걸 사용한 것임.
//path, data, 파일 저장 유형
Files.writeString(path, data, Charset.forName("UTF-8"));
//파일 정보 얻기
System.out.println("파일 유형: " + Files.probeContentType(path));
//MimeType 대분류/중분류 text/plain
//웹에서 매우 중요함.
//브라우저가 파일을 받고나서 어케처리할지를 이것을 보고 결정함.
System.out.println("파일 크기: " + Files.size(path) + " bytes");
System.out.println();
//파일 읽기
String content = Files.readString(path, Charset.forName("UTF-8"));
System.out.println(content);
} catch (IOException e) {
e.printStackTrace();
}}}System.out.println("파일 유형: " + Files.probeContentType(path));
이것은 파일의 MimeType뱉어냄
MimeType 대분류/중분류 text/plain
웹에서 매우 중요함.
브라우저가 파일을 받고나서 어케처리할지를 이것을 보고 결정함.
2022.12.21 리뷰
파일 입출력 이 부분부터는 계속 이어진다.
잘 이해하도록 하자.
'기초단계 > JAVA' 카테고리의 다른 글
| 2022.12.23 JAVA 복습 네트워크 입출력 (0) | 2022.12.25 |
|---|---|
| 2022.12.22 JAVA 복습 네트워크 입출력 (0) | 2022.12.22 |
| 2022.12.20-2 JAVA 복습 스트림 요소 처리 (1) | 2022.12.20 |
| 2022.12.20-1 JAVA 복습 람다식 (0) | 2022.12.20 |
| 2022.12.19-1 JAVA 복습 컬렉션 자료구조 (0) | 2022.12.19 |