Java/자바의 신 VOL.2
09. Serializable과 NIO도 살펴봅시다
h2boom
2024. 7. 3. 20:55
Serializable
- java.io 패키지에 있는 인터페이스
- 클래스가 파일에 읽거나 쓸 수 있도록 하며 다른 서버로 보내거나 받을 수 있도록 하려면 구현해야하는 인터페이스
- Serializable 인터페이스 구현 시 JVM에서 해당 객체를 저장하거나 다른 서버로 전송할 수 있도록 직렬화, 역직렬화 해준다.
직렬화 : 객체들의 데이터를 연속적인 데이터(스트림)로 변형하여 전송 가능한 형태로 만드는 것
역직렬화 : 직렬화된 데이터를 다시 객체의 형태로 만드는 것
static final long serialVersionUID = 1L;
- 그렇다면 serialVersionUID는 어떻게 사용될까?
- ex) A서버에서 B서버로 SerialDTO 클래스 객체를 전송하려고 할 때
먼저 A서버에도 SerialDTO 클래스가 존재해야하고 B서버에도 SerialDTO 클래스가 존재해야한다.
그래야 그 클래스의 객체임을 알아차리고 데이터를 받을 수 있다. - 각 서버가 쉽게 해당 객체가 같은지를 확인할 수 있도록 하는 것이 serialVersionUID 다.
- 클래스 이름이 같더라도 ID가 다르면 다른 클래스로 인식한다.
- ID가 같더라도 변수 개수, 타입이 다르면 다른 클래스로 인식한다.
- 데이터가 바뀌면 serializableVersionUID 값도 변경하는 습관을 가져야한다.
- ex) A서버에서 B서버로 SerialDTO 클래스 객체를 전송하려고 할 때
- 객체를 저장하고 불러오기위한 클래스 ObjectInputStream, ObjectOutputStream
- writeObject(), readObject() 메소드로 객체를 저장하고 불러온다.
- readObject() 메소드로 객체를 불러올 때는 리턴 타입이 Object 타입이므로 원하는 타입으로 형 변환 필요
- transient 예약어
- 객체를 저장하거나 다른 JVM으로 보낼 때 transient 예약어를 사용해 선언한 변수는 Serializable의 대상에서 제외된다.
- 저장한 객체를 불러오면 지정한 값이 아닌 기본 값이 들어가있다.
- 해당 객체를 생성한 JVM에서는 사용할 때 전혀 문제가 없다.
- 다른 JVM , 객체 저장 시에만 제외가 된다.
- 보안상 중요한 변수 / 꼭 저장할 필요가 없는 변수에 대해서 사용한다.
- 객체를 저장하거나 다른 JVM으로 보낼 때 transient 예약어를 사용해 선언한 변수는 Serializable의 대상에서 제외된다.
transient private String name;
NIO
- NIO는 New IO의 약자로 속도 때문에 추가되었다.
- 스트림 대신 채널과 버퍼를 사용하는 IO
- 채널 : 물건을 중간에서 처리하는 도매상 역할로 디바이스, 파일, 네트워크 등과의 연결 상태를 나타내는 클래스
- 버퍼 : 도매상에서 물건을 사고 소비자에게 물건을 파는 소매상 역할
- NIO에서 데이터를 주고 받을 때는 버퍼를 통해서 처리한다.
- 파일을 쓰기 위한 순서
- FileOutputStream 클래스의 getChannel() 메소드로 FileChannel 객체를 생성
- ByteBuffer 클래스의 static wrap() 메소드 매개 변수로 파일에 저장할 byte 배열을 넘겨 호출해 객체를 생성
- FileChannel의 write() 메소드에 Bytebuffer 객체를 넘겨주면 파일에 쓰게 된다.
- 파일을 읽기 위한 순서
- FileInputStream 클래스에 선언된 getChannel() 메소드로 FileChannel 객체를 생성
- ByteBuffer 클래스의 allocate() 메소드로 기본 데이터 저장 크기를 매개 변수로 넘겨 객체를 생성
- FileChannel의 read() 메소드에 ByteBuffer 객체를 넘겨줌으로 데이터를 읽어 해당 버퍼에 담는다.
- ByteBuffer의 flip() 메소드로 데이터의 가장 앞으로 이동한다.
- ByteBuffer의 get() 메소드로 한 바이트씩 데이터를 읽는 작업을 수행한다.
NIO Buffer 클래스
- java.nio.Buffer 클래스를 확장한 클래스들이 많다.
- CharBuffer, DoubleBuffer, ByteBuffer 등등...
- Buffer 클래스의 메소드
- int capacity() : 버퍼에 담을 수 있는 크기를 리턴한다.
- int limit() : 버퍼에서 읽거나 쓸 수 없는 첫 위치를 리턴한다.
- int position() : 현 버퍼의 위치를 리턴한다.
- Buffer flip() : limit 값을 현재 position으로 지정 후 position을 가장 앞(0)으로 이동한다.
- Buffer mark() : 현재 position을 표시한다.
- Buffer reset() : 버퍼의 position을 mark 한 곳으로 이동한다.
- Buffer rewind() : 현 버퍼의 position을 0으로 이동한다.
- int remaining() : limit - position 계산 결과를 리턴한다.
- boolean hasRemaining() : position과 limit 값에 차이가 있을 경우 true를 리턴한다.
- Buffer clear() : 버퍼를 지우고 현재 position을 0으로 이동하고 limit 값을 버퍼 크기로 변경한다.
- 버퍼는 위치가 있다.
- 버퍼에 데이터를 담거나, 읽는 작업을 수행하면 현재 "위치"가 이동한다.
- flip()과 rewind()의 차이는 limit 값을 변경하는지 여부다.
- remaining()과 hasRemaining()은 limit까지만 데이터를 읽을 수 있다.
간단 내용 정리
1. java.io.Serializable을 import 하는 이유는 무엇인가요?
다른 JVM에게 객체를 전송하거나 객체를 파일로 저장하기 위해서 Serializable 인터페이스를 구현해 직렬화, 역직렬화하기 위해서 사용한다.
2. java.io.Serializable의 serialVersionUID 를 지정하는 이유는 무엇인가요?
서로 다른 JVM에서 객체를 주고 받을 때 버전을 확인함으로 같은 타입인지 확인하기 위해서
3. 자바에서 객체를 파일로 읽거나 쓸 때 사용하는 Stream 클래스 이름은 무엇인가요?
FileInputStream, FileOutputStream
4. transient 예약어의 용도는 무엇인가요?
변수를 Serializable의 대상(직렬화 대상)에서 제외하기 위해서
5. NIO가 생긴 이유는 무엇인가요?
채널과 버퍼를 사용하여 스트림 기반 기존 I/O의 속도를 개선하기 위해서
6. NIO에서 Channel의 용도는 무엇인가요?
데이터를 중간에 처리하는 역할
7. NIO에서 Buffer의 용도는 무엇인가요?
데이터를 담는 역할
8. NIO에서 Buffer의 상태를 확인하기 위한 메소드들에는 어떤 것들이 있나요?
capacity(), limit(), position()
9. NIO에서 Buffer의 position을 변경하기 위한 메소드들에는 어떤 것들이 있나요?
reset(), flip(), mark(), rewind(), remaining(), hasRemaining(), clear()