이 프로세스는 또한 네트워크를 통해 구현 될 수 있으며, 먼저 Windows 시스템에서 객체를 생성하고 직렬화 한 다음 네트워크를 통해 UNIX 시스템으로 보내고 정확하게 다시 조립할 수 있습니다. RMI, 소켓, JMS 및 EJB가 물론 객체 직렬화 메커니즘으로 인한 이유는 무엇입니까?
Java 객체 직렬화 메커니즘에는 일반적으로 두 가지 용도가 있습니다.
Java JavaBeans : Bean 상태 정보는 일반적으로 설계 시간에 구성되어 있어야합니다. . 개체 상태를 읽고 객체를 재구성하고 프로그램 상태를 복원하십시오.
RMI를 사용하면 원격 기계의 객체를 네트워크에서 소켓을 사용하는 프로그램과 같이 작동 할 수 있습니다.
클래스가 java.io.serializable 인터페이스를 구현하여 클래스를 직렬화 할 수 있습니다. 이 인터페이스는 제조업체 인터페이스입니다. 즉, 클래스가이를 구현하려면 인터페이스가 어떠한 메소드도 구현할 필요가 없습니다. 주로 JVM (Java Virtual Machines)에게 객체를 직렬화해야한다는 것을 알리는 데 사용됩니다.
이것에 대해 명확히해야 할 몇 가지 요점이 있습니다.
CMD에 따라 모든 클래스를 직렬화 할 수는 없습니다.
Java에는 스트링, 벡터 등과 같은 직렬화 가능한 인터페이스를 구현 한 많은 기본 클래스가 있습니다. 그러나 예를 들어 Hashtable은 직렬화 가능한 인터페이스를 구현하지 않습니다.
스트림에 개체를 읽거나 쓰는 데 두 가지 주요 클래스가 있습니다 : ObjectOutputStream 및 ObjectInputStream.ObjectOutputStream은 출력 스트림에 객체를 쓰는 데 사용되는 WriteObject 메소드를 제공하며 ObjectInputStream은 입력 스트림에서 객체를 읽기위한 readObject 메소드를 제공합니다. 이 방법을 사용하는 객체는 직렬화되어야합니다. 즉, 직렬화 가능한 인터페이스가 구현되어야합니다. Hashtable 객체를 작성하려면 예외가 나타납니다.
직렬화 과정은 바이트 스트림에서 객체를 작성하고 읽는 것입니다. 객체 상태를 바이트 스트림으로 변환 한 후 java.io 패키지의 다양한 바이트 스트림 클래스를 사용하여 파일에 저장하거나 다른 스레드로 파이프하거나 네트워크 연결을 통해 다른 호스트로 객체 데이터를 보낼 수 있습니다. 객체 직렬화 기능은 매우 간단하고 강력하며 RMI, 소켓, JMS 및 EJB에서 사용됩니다. 객체 직렬화 문제는 네트워크 프로그래밍에서 가장 흥미로운 주제는 아니지만 상당히 중요하며 실질적인 의미가 있습니다.
객체 직렬화는 분산 객체를 구현할 수 있습니다. RMI와 같은 주요 응용 프로그램은 객체 직렬화를 사용하여 로컬 컴퓨터에서 객체를 실행할 때와 같이 원격 호스트에서 서비스를 실행합니다.
Java Object Serialization은 하나의 객체의 데이터를 유지할뿐만 아니라 객체에서 참조 된 각 객체의 데이터를 재귀 적으로 저장합니다. 전체 오브젝트 계층 구조는 바이트 스트림으로 기록하거나 파일에 저장하거나 네트워크 연결에 전달 될 수 있습니다. 객체 직렬화는 객체의 "깊은 복사", 즉 객체 자체와 참조 된 객체 자체를 복사하는 데 사용될 수 있습니다. 객체를 직렬화하면 전체 객체 시퀀스가 발생할 수 있습니다.
Java 직렬화는 비교적 간단하며 일반적으로 객체 상태를 저장하고 복원하기 위해 맞춤형 코드를 작성할 필요가 없습니다. java.io.serializable 인터페이스를 구현하는 클래스 객체는 클래스에 코드를 추가하지 않고도 바이트 스트림으로 변환하거나 복구 할 수 있습니다. 드문 경우에만 개체 상태를 저장하거나 복원하는 데 필요한 사용자 정의 코드가 있습니다. 참고 : 모든 클래스가 직렬화 될 수있는 것은 아니며 일부 클래스는 예를 들어 특정 JVM과 매우 복잡한 관계를 갖습니다.
직렬화 메커니즘 :
직렬화는 직렬화와 사막화의 두 부분으로 나뉩니다. 직렬화는이 프로세스의 첫 번째 부분이며 파일의 저장을 위해 데이터를 바이트 스트림으로 나누거나 네트워크를 통해 전송합니다. 사제화는 바이트 스트림을 열고 물체를 재구성하는 것입니다. 객체 직렬화는 기본 데이터 유형을 바이트 표현으로 변환해야 할뿐만 아니라 때로는 데이터를 복구해야합니다. 데이터 복구에는 데이터를 복원하는 객체의 인스턴스가 필요합니다. ObjectOutputStream의 직렬화 프로세스는 객체 유형 및 버전 정보를 포함한 바이트 스트림에 연결됩니다. 사형화 동안 JVM은 헤더 정보가있는 객체 인스턴스를 생성 한 다음 객체 바이트 스트림에서 객체 데이터 구성원으로 데이터를 복사합니다. 두 부분으로 설명해 봅시다 :
객체 흐름 처리 : ( 직렬화 프로세스 및 사막화 프로세스)
java.io 패키지에는 객체를 직렬화하는 두 가지 클래스가 있습니다. ObjectOutputStream은 바이트 스트림에 객체를 작성하고 ObjectInputStream은 바이트 스트림에서 객체를 재구성합니다.
먼저 ObjectOutputStream 클래스를 이해해 봅시다. ObjectOutputStream 클래스는 DataOutput 인터페이스를 확장합니다.
writeObject () 메소드는 객체 직렬화에 가장 중요한 방법입니다. 객체에 다른 객체에 대한 참조가 포함 된 경우 writeObject () 메소드는 이러한 객체를 재귀 적으로 직렬화합니다. 각 ObjectOutputStream은 동일한 객체의 여러 사본이 전송되는 것을 방지하기 위해 직렬화 된 객체 참조 테이블을 유지합니다. writeObject ()가 교차 참조 된 개체의 전체 세트를 직렬화 할 수 있으므로 동일한 ObjectOutputStream 인스턴스를 실수로 동일한 개체를 직렬화하도록 요청할 수 있습니다. 이 시점에서 객체 바이트 스트림에 다시 쓰는 대신 불평 한 직렬화가 수행됩니다.
다음으로 예제에서 ObjectOutputStream 클래스를 이해해 봅시다.
코드 사본은 다음과 같습니다.
// 오늘의 날짜를 파일로 시리얼링합니다.
fileoutputStream f = 새 FileOutputStream ( "TMP");
ObjectOutputStream S = 새로운 ObjectOutputStream (F);
s.writeobject ( "오늘");
s.writeobject (new date ());
s.flush ();
이제 ObjectInputStream 클래스를 이해해 봅시다. ObjectOutputStream과 유사합니다. Datainput 인터페이스를 확장합니다. ObjectInputStream의 방법은 DatainputStream에서 Java 기본 데이터 유형을 읽는 공개 방법을 반영합니다. readObject () 메소드는 바이트 스트림에서 객체를 절실화합니다. readObject () 메소드가 호출 될 때마다 스트림의 다음 객체가 반환됩니다. 객체 바이트 스트림은 클래스의 바이트 코드를 전송하지 않지만 클래스 이름과 서명을 포함합니다. readObject ()가 객체를 수신하면 JVM은 지정된 클래스를 헤더에로드합니다. 이 클래스를 찾을 수없는 경우 readObject ()는 객체 데이터와 바이트 코드가 필요한 경우 RMI 프레임 워크를 사용할 수 있습니다. ObjectInputStream 방법의 나머지 부분은 사막화 프로세스를 사용자 정의하는 데 사용됩니다.
예는 다음과 같습니다.
코드 사본은 다음과 같습니다.
// 파일에서 객체와 날짜 객체를 deserialize합니다
fileInputStream in = new FileInputStream ( "TMP");
ObjectInputStream S = New ObjectInputStream (in);
String today = (string) s.readobject ();
날짜 = (날짜) s.readobject ();
맞춤형 직렬화 프로세스 :
직렬화는 일반적으로 자동으로 수행 될 수 있지만 때로는 프로세스가 제어 될 수 있습니다. Java는 클래스를 직렬화 가능한 것으로 선언 할 수 있지만 여전히 정적 또는 과도로 선언 된 데이터 구성원을 수동으로 제어 할 수 있습니다.
예 : 매우 간단한 직렬화 클래스.
코드 사본은 다음과 같습니다.
공개 클래스 SIMPLESERIALIZABLECLASS는 직렬화 가능 {
문자열 stoday = "오늘 :";
과도 날짜 dttoday = 새 날짜 ();
}
직렬화 할 때, 클래스의 모든 데이터 구성원은 일시적이거나 정적으로 선언 된 것을 제외하고 직렬화 할 수 있어야합니다. 변수를 과도로 선언하면 JVM에 인수를 직렬화 할 책임이 있다고 말합니다. 데이터 멤버를 과도로 선언 한 후 직렬화 프로세스는 객체 바이트 스트림에 추가 할 수 없으며 과도 데이터 멤버의 데이터는 없습니다. 나중에 데이터를 제조 할 때 데이터 멤버를 재구성해야하지만 (클래스 정의의 일부이기 때문에)이 데이터 멤버가 스트림에 데이터를 작성하지 않기 때문에 데이터가 포함되지 않습니다. 객체 스트림은 정적 또는 과도를 직렬화하지 않습니다. 우리의 클래스는 이러한 데이터 구성원을 처리하기 위해 writeObject () 및 readObject () 메소드를 사용해야합니다. writeObject () 및 readObject () 메소드를 사용하는 경우,이 데이터 구성원이 작성된 순서대로 읽는 데주의를 기울여야합니다.
사용자 지정 직렬화를 사용하는 방법에 대한 일부 코드는 다음과 같습니다.
코드 사본은 다음과 같습니다.
// 과도 멤버를 처리하기 위해 writeObject () 메소드를 다시 작성하십시오.
public void writeObject (ObjectOutputStream outputStream)는 ioexception {
outputStream.defaultWriteObject (); // 사용자 정의 된 writeObject () 메소드를 만듭니다
// 자동 직렬화에서 내장 로직을 사용하십시오.
outputStream.writeObject (osocket.getInetAddress ());
outputStream.writeInt (osocket.getport ());
}
// 과도 멤버를 받으려면 readObject () 메소드를 다시 작성하십시오.
private void readObject (ObjectInputStream inputStream)는 ioexception을 던지고,
classNotFoundException {
inputStream.defaultreadObject (); // defaultreadObject () 보충 자동 직렬화
inetAddress oaddress = (inetAddress) inputStream.readObject ();
int iport = inputStream.readint ();
Osocket = New Socket (Oaddress, iport);
iid = getid ();
dttoday = 새로운 날짜 ();
}
직렬화 프로세스를 완전히 사용자 정의하십시오.
클래스가 자체 직렬화에 대해 완전히 책임이있는 경우 직렬화 가능한 인터페이스 대신 외부화 가능한 인터페이스를 구현합니다. 외부화 가능한 인터페이스 정의에는 두 가지 방법 writeExternal ()과 readExternal ()이 포함됩니다. 이 방법은 객체 데이터 구성원이 외부화를 구현하는 방법을 제어 할 수 있습니다 , 자동 직렬화는 전혀 없습니다. 여기에주의를 기울이십시오. 선언 클래스는 보안 위험이 심각한 외부화 가능한 인터페이스를 구현합니다. WriteExternal () 및 readexternal () 메소드는 공개적으로 선언되며 악의적 인 클래스는 이러한 방법을 사용하여 객체 데이터를 읽고 쓸 수 있습니다. 객체에 민감한 정보가 포함 된 경우 추가로주의하십시오. 여기에는 안전한 소켓 사용 또는 전체 바이트 스트림 암호화가 포함됩니다. 이 시점에서 우리는 직렬화에 대한 기본 지식을 배웠습니다.