자바칩
[자바의 신] 27장. Serializable과 NIO: 문제 풀이 본문
728x90
1. java.io.Serializable을 import하는 이유는 무엇인가요?
객체를 파일에 읽거나 쓸 수 있도록 하거나, 다른 서버로 보내거나 받을 수 있도록 하려면 반드시 Serializable 인터페이스를 구현해야 한다. Serializable 인터페이스를 구현하면 JVM에서 해당 객체는 저장하거나 다른 서버로 전송할 수 있도록 해준다. 이 인터페이스는 java.io 패키지에 있으므로, java.io.Serializable을 import 해야 한다.
2. java.io.Serializable의 serialVersionUID를 지정하는 이유는 무엇인가요?
각 서버에서 쉽게 해당 객체가 같은지 다른지를 확인하기 위해 serialVersionUID을 지정해야 한다. 클래스 이름이 같더라도, serialVersionUID가 다르면 다른 클래스라고 인식한다. 게다가 같은 serialVersionUID라고 할지라도, 변수의 개수나 타입 등이 다르면 이 경우도 다른 클래스로 인식한다. 만약 serialVersionUID를 지정하지 않고 저장한 객체와 읽어온 객체에 선언된 상태가 다르다면 다른 객체로 인식하여 예외를 발생시킨다. 하지만 serialVersionUID를 지정해주면 객체에 선언된 상태가 다르더라도 예외를 발생시키지 않는다. 하지만 이럴 경우에는 운영 상황에서 데이터가 꼬일 수 있기 때문에 객체에 선언된 상태가 바뀌면 serialVersionUID의 값을 변경하는 습관을 가지는 것이 좋다. 반드시 static final long으로 선언해야 하며, 변수명도 serialVersionUID로 선언해 주어야만 한다.
*사용 예시
public class SerialDTO implements Serializable { // Serializable 인터페이스를 구현해야 객체 저장, 전송 가능
static final long serialVersionUID = 1L;
private String bookName;
private int bookOrder;
private boolean bestSeller;
private long soldPerDay;
public SerialDTO(String bookName, int bookOrder, boolean bestSeller, long soldPerDay) {
this.bookName = bookName;
this.bookOrder = bookOrder;
this.bestSeller = bestSeller;
this.soldPerDay = soldPerDay;
}
@Override
public String toString() {
return "SerialDTO [bookName " = " + bookName + " , bookOrder = " + bookOrder + " , bestSeller = " + bestSeller + " , soldPerDay = " + soldPerDay;
}
}
3. 자바에서 객체를 파일로 읽거나 쓸 때 사용하는 Stream 클래스 이름은 무엇인가요?
ObjectOutputStream: 자바에서 객체를 파일로 쓸 때(저장할 때) 사용
ObjectInputStream: 자바에서 객체를 파일로 읽을 때 사용
*ObjectOutputStream 사용 예시
String fileName = "C:\\godofjava\\text";
SerialDTO dto = new SerialDTO("GodOfJava", 1, true, 100);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(fileName); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
oos = new ObjectOutputStream(fos); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
oos.writeObject(dto); // 객체를 파일에 저장
System.out.println("파일에 객체 저장 완료");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 객체는 생성 역순으로 닫아줘야 함
if (oos != null) {
try {
oos.close(); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
} catch (Exception e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close(); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
} catch (Exception e) {
e.printStackTrace();
}
}
}
*ObjectInputStream 사용 예시
String fileName = "C:\\godofjava\\text";
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(fileName); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
ois = new ObjectInputStream(fis); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
Object obj = ois.readObject(); // 객체를 파일에서 읽어 옴
SerialDTO dto = (SerialDTO) obj; // Object 타입에서 SerialDTO 타입으로 형 변환
System.out.println(dto); // SerialDTO의 toString() 메소드 호출
} catch (Exception e) {
e.printStackTrace();
} finally {
// 객체는 생성 역순으로 닫아줘야 함
if (ois != null) {
try {
oos.close(); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
} catch (Exception e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fos.close(); // 예외를 발생시킬 수 있으므로 try-catch로 묶어줌
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. transient 예약어의 용도는 무엇인가요?
객체를 파일로 저장하거나 다른 JVM으로 보낼 때, transient 예약어를 사용하여 선언한 변수는 Serializable 대상에서 제외된다. 다시 말해서, 해당 객체는 저장 대상에서 제외시키는 용도이다. 만약 비밀번호를 저장하는 변수를 객체에 저장하거나, 다른 서버로 전송한다면 보안 상의 큰 문제가 발생할 수 있다. 이렇게 보안 상 중요한 변수나 꼭 저장해야 할 필요가 없는 변수에 대해서는 transient를 사용하면 된다.
5. NIO가 생긴 이유는 무엇인가요?
파일 읽기와 쓰기를 더 빠르게 하기 위해서이다. NIO에서는 스트림을 사용하지 않고, 채널(Channel)과 버퍼(Buffer)를 사용하여 데이터를 처리한다.
6. NIO에서 Channel의 용도는 무엇인가요?
물건을 중간에서 처리하는 도매상과 같은 역할을 한다. 파일을 쓰거나 읽기 위한 용도이다. 객체를 생성하여 read()나 write() 메소드만 불러주면 된다.
7. NIO에서 Buffer의 용도는 무엇인가요?
도매상에서 물건을 사고, 소비자에게 물건을 파는 소매상과 같은 역할을 한다. 파일에 저장하려고 하거나 읽으려고 하는 데이터를 담아 놓는 용도이다. NIO에서 데이터를 주고 받을 때 Buffer를 통해 처리한다.
8. NIO에서 Buffer의 상태를 확인하기 위한 메소드들에는 어떤 것들이 있나요?
메소드 | 역할 |
capacity() | 버퍼에 담을 수 있는 크기 리턴 |
limit() | 버퍼에서 읽거나 쓸 수 없는 첫 위치 리턴 |
position() | 현재 버퍼의 위치 리턴 |
이 3개 값의 관계는 다음과 같다.
0 <= position <= limit <= capacity(크기)
9. NIO에서 Buffer의 position을 변경하기 위한 메소드들에는 어떤 것들이 있나요?
메소드 | 역할 |
flip() | limit의 값을 현재 position으로 지정한 후, position을 0(가장 앞)으로 이동 |
mark() | 현재 position을 mark |
reset() | 버퍼의 position을 mark한 곳으로 이동 |
rewind() | 현재 버퍼의 position을 0으로 이동 |
remaining() | limit - position 계산 결과 리턴 |
hasRemaining() | limit와 position의 값에 차이가 있을 경우 true를 리턴 |
clear() | 버퍼를 지우고 현재 position을 0으로 이동하며, limit 값을 버퍼의 크기로 변경 |
위의 관계에서 mark를 추가하면 다음과 같다.
0 <= mark <= position <= limit <= capacity(크기)
'Study > Java' 카테고리의 다른 글
[자바의 신] 기말고사, 기말 실습문제 (0) | 2024.05.30 |
---|---|
[자바의 신] 28장. Socket과 Datagram: 문제 풀이 (0) | 2024.05.29 |
[자바의 신] 26장. I/O: 문제 풀이 (0) | 2024.05.26 |
[자바의 신] 25장. 쓰레드 (Thread): 문제 풀이 (0) | 2024.05.23 |
[Java] 상속과 조합 (0) | 2024.05.20 |