보조스트림은 기존 노드스트림에다가 확장해서 연결할 때 씀.
보조스트림 종류
- byte 스트림을 char로 스트림으로 변환
InputStreamReader, OutputStreamWriter
- 버퍼링을 통한 속도 향상
- byte기반: BufferedInputStream, BufferedOutputStream
- char기반: BufferedReader, BufferedWriter
- 객체전송
ObjectInputStream
ObjectOutputStream
사용할 스트림 결정해보자
다음 순서로 결정하면 된다.
노드가 무엇인가? -> 타입은 문자열 or 바이트인가? -> 방향은? -> 추가기능은(보조스트림)?
- 영화 파일을 빠른 속도로 이동시키고 싶다면?
File -> byte -> 읽기 -> FileInputStream -> BufferedInputStream
File -> byte -> 쓰기 -> FileOutputStream -> BufferedOutputStream
- 키보드에서 유니코드 문자를 안전하고 빠르게 읽고 싶다면?
Keyboard -> byte -> 읽기 -> InputStream, System.in -> InputStreamReader -> BufferedReader
- 메모리의 객체를 파일로 저장하고 싶다면?
File -> byte -> 쓰기 -> FileOutputStream -> ObjectOutputStream
BufferedInputStream 사용해보기
체크포인트:
다음처럼 써야 함. 노도스트림을 정하고, 이걸 넣어주는 거임.
보조스트림 = new 보조스트림(노드스트림);
public class 보조스트림 {
public static void main(String[] args) {
File src = new File("C:"+File.separator+"SSAFY"+File.separator+"ipsum.txt");
File target = new File("C:"+File.separator+"SSAFY"+File.separator+"copy.txt");
try(FileInputStream fi = new FileInputStream(src);
FileOutputStream fo = new FileOutputStream(target);){
copy("노드만 사용: ", fi, fo);
}catch (Exception e) {
// TODO: handle exception
}
try(BufferedInputStream fi = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream fo = new BufferedOutputStream(new FileOutputStream(target));){
copy("보조도 사용: ", fi, fo);
}catch (Exception e) {
// TODO: handle exception
}
}
public static void copy(String type, InputStream input, OutputStream output)throws IOException{
long start = System.nanoTime();
byte[] cart = new byte[1024];
int read = -1;
while((read = input.read(cart))>0) {
output.write(cart, 0, read);
}
long end = System.nanoTime();
System.out.println(type+",소유시간: "+(end-start) + "ns");
}
}
출력결과:
=>
노드만 사용: ,소유시간: 245800ns
보조도 사용: ,소유시간: 109200ns
BufferedReader 사용해보기
기존의 일반 노드스트림만을 활용한 것: scanner와 FileWriter, FileReader를 씀.
public class Diary {
public static void main(String[] args) {
Diary diary = new Diary();
diary.writeDiary();
}
private void writeDiary() {
File target = new File("C:"+File.separator+"SSAFY"+File.separator+"diary.txt");
try(Scanner scanner = new Scanner(System.in);
FileWriter writer = new FileWriter(target, true);
FileReader reader = new FileReader(target);){
System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요");
String str = null;
while(true) {
str = scanner.nextLine();
if(str.equals("x")) {
break;
}else {
writer.write(str+"\n");
}
}
char[] buffer = new char[10];
int read = -1;
while((read = reader.read(buffer))>0) {
System.out.println(String.valueOf(buffer,0,read));
}
}catch (Exception e) {
// TODO: handle exception
}
}
}
=> 개선해보자
체크포인트:
1. Scanner를 InputStreamReader로 바꿈. 여기서 InputStreamReader도 보조스트림인데, 키보드에서 오는 byte를 char로 바꿔주는 역할을 함(이름을 보면 각이 나옴. stream은 바이트하는앤데 그 뒤에 char을 읽는 reader가 붙었으니까 유추가능)
2. Scanner의 nextline()대신 BufferedReader의 readLine()을 사용하였음.
3. 기본적으로 Reader를 상속한 것이기에 FileReader나 BufferedReader나 모두 .read()를 동일하게 사용한다.
public class Diary {
public static void main(String[] args) {
Diary diary = new Diary();
diary.writeDiary();
}
private void writeDiary() {
File target = new File("C:"+File.separator+"SSAFY"+File.separator+"diary.txt");
try(BufferedReader fromKeyBoard = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new FileWriter(target, true));
BufferedReader reader = new BufferedReader(new FileReader(target));){
System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요");
String str = null;
while(true) {
str = fromKeyBoard.readLine();
if(str.equals("x")) {
break;
}else {
writer.write(str+"\n");
}
}
char[] buffer = new char[10];
int read = -1;
while((read = reader.read(buffer))>0) {
System.out.println(String.valueOf(buffer,0,read));
}
}catch (Exception e) {
// TODO: handle exception
}
}
}
객체 직렬화
메모리에 객체가 특정주소에 뭉텅이로 저장되어있지. 근데 그걸 쭈우우욱 펴서 늘어뜨려 놓는다는 것임. 그렇게 쭈우우욱 늘어뜨려놔야 네트워크로 전송이 된다는것임. Person객체에 name, age 등등이 있으면 name - age - hometown - job - height 이렇게 일렬로 연속적인 데이터로 변환한다.
단, 모든 객체가 직렬화가 되는 것이 아님.
- Serializable 인터페이스를 구현하해야 함.
- 클래스의 모드 멤버가 Serializable 인터페이스를 구현해야 함.
- 직렬화에서 제외하려는 멤버는 transient 선언을 해야 함.
SerialVersionUID
클래스의 변경여부를 파악하기 위한 유일 키.
직렬화를 했다가 나중에 다시 역직렬화를 하려는데 클래스 구조가 바뀌었다면(속성 하나 더 추가된다든지..) UID가 바뀐다. 이러면 예외발생
즉, 직려화 할때의 UID랑 역직렬화할때의 UID가 다르면 예외가 발생한다.
- 직렬화되는 객체에 UID가 설정되어있지 않으면 컴파일러가 자동으로 생성해줌(그러니까 직렬화되는 객체에 serialVersionUID를 설정하지 않고 멤버를 변경해서 클래스 구조가 바뀌면 UID가 다르게 자동으로 생성되서 다시 역직렬화할 때 예외가 발생하는 것임)
- 따라서 직렬화되는 객체에 serialVersionUID를 설정하는 것이 권장됨
예제
Person객체 직렬화 해보자.
체크포인트
1. 직렬화할 대상인 Person은 당연하고, 멤버변수로 오는 Address클래스도 무조건 implements Serializable 해야 한다.
2. 웬만하면 UID를 설정해줄 것
3. 보조스트림 = new 보조스트림(노드스트림)
4. read()할 때 Object로 받았는데, 걍 바로 쓰지 않고 null체크하고 instanceof로 확인 후 사용
5. toString() 오버라이딩해서 객체 프린트되도록 해줌
public class ObjectStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// write();
read();
}
private static File target = new File("C:"+File.separator+"SSAFY"+File.separator+"objPerson.dat");
private static void write() {
Person person = new Person("홍길동", "pass123", "010-1234", "seoul");
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(target))){
oos.writeObject(person);
System.out.println("person 저장완료!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void read() {
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(target))){
Object obj = ois.readObject();
if(obj !=null && obj instanceof Person) {
Person casted = (Person)obj;
System.out.println(casted);
}
}catch (Exception e) {
// TODO: handle exception
}
}
}
-직렬화 할 객체에 UID넣기
UID를 넣으면 속성이 달라져도 역직렬화 시 예외가 발생하지 않는다.
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2514785375074402994L;
private String id;
private transient String pass;
private Address addr;
public Person(String id, String pass, String zipCode, String city) {
this.id = id;
this.pass = pass;
this.addr = new Address(zipCode, city);
}
@Override
public String toString() {
return "[id=" + id + ", pass ="+pass+", addr="+addr+"]";
}
}
Address 클래스
public class Address implements Serializable{
private String zipCode;
private String city;
public Address(String zipCode, String city) {
this.zipCode = zipCode;
this.city = city;
}
public String toString() {
return "Addres [zipCode="+zipCode+", city="+city+"]";
}
}
'개발삽질 > SSAFY하는 동안 기록' 카테고리의 다른 글
[알고리즘] 분할정복!(BOJ1992쿼드트리,BOJ1074Z) (0) | 2022.02.17 |
---|---|
[알고리즘]순열 - NextPermutation (0) | 2022.02.14 |
[Java] File IO, 노드스트림 (0) | 2022.02.06 |
[일기] 싸피 트랙 변경 스토리(비전공 파이썬 트랙에서 전공자 자바트랙으로) (8) | 2022.02.05 |
[SSAFY] 오티 : 마음가짐 (2) | 2022.01.07 |
댓글