이 기사에는 주로 다음과 같은 문제가 포함됩니다.
1. Java 객체의 직렬화
Java 플랫폼을 사용하면 메모리에서 재사용 가능한 Java 객체를 만들 수 있지만 일반적으로 이러한 객체는 JVM이 실행 중일 때만 존재할 수 있습니다. 그러나 실제 응용 분야에서는 JVM이 중지 된 후 지정된 객체를 저장 (지속)해야 할 수도 있습니다. Java Object Serialization 은이 기능을 구현하는 데 도움이 될 수 있습니다.
Java 객체 직렬화를 사용하여 객체를 저장하면 상태가 바이트 세트로 저장되며 향후 이러한 바이트는 객체에 조립됩니다. 객체 직렬화는 객체, 즉 멤버 변수의 "상태"를 저장한다는 점에 유의해야합니다. 이것으로부터 우리는 객체 직렬화가 클래스의 정적 변수에 초점을 맞추지 않는다는 것을 알 수 있습니다.
객체를 지속시 객체 직렬화를 사용하는 것 외에도 RMI (원격 메소드 호출) 또는 네트워크에서 객체를 통과 할 때 객체 직렬화가 사용됩니다. Java Serialization API는 객체 직렬화를 처리하기위한 표준 메커니즘을 제공합니다. 이 API는 간단하고 사용하기 쉽고이 기사의 후속 장에서 논의 할 것입니다.
Public Class ArrayList <E>는 AbstractList <e> implements List <e>, RandomAccess, clonable, java.io.serializable {private static final long serialversionuid = 8683452581122892189L; 과도 객체 [] elementData; // 중첩 클래스 액세스 개인 int 크기를 단순화하기위한 비 프리티브;}2. Java 객체를 직렬화하고 실질화하는 방법
Java에서는 클래스가 java.io.serializable 인터페이스를 구현하는 한 직렬화 될 수 있습니다. 코드는 다음과 같습니다.
코드 1 직렬화 및 사막화를위한 사용자 클래스 생성
com.hollis; import java.io.serializable; import java.util.date;/*** hollis가 16/2/2에 작성했습니다. */public class 사용자는 직렬화 가능한 {개인 문자열 이름을 구현합니다. 사적인 int 연령; 개인 데이트 생일; 개인 과도 문자열 성별; 개인 정적 최종 최종 긴 SerialVersionUID = -684979470754667710L; 공개 문자열 getName () {return name; } public void setName (문자열 이름) {this.name = 이름; } public int getage () {반환 연령; } public void 설정 (int Age) {this.age = age; } 공개 날짜 getBirthday () {return birthday; } public void setbirthday (날짜 생일) {this.birthday = 생일; } public String getGender () {return gender; } public void setgender (문자열 성별) {this.gender = 성별; } @Override public String toString () {return "user {" + "name = ' + name +'/' +", age = " + age +", gender = " + gender +", 생일 = " + 생일 +'} '; }}사용자를 직렬화하고 사막화하기위한 코드 2 데모
package com.hollis; import org.apache.commons.io.fileutils; import org.apache.commons.io.ioutils; import java.io.*; import java.util.date;/*** 16/2/2에 생성. */public class serializabledemo {public static void main (string [] args) {// 객체 사용자 user = new User (); user.setName ( "Hollis"); user.setgender ( "male"); user.setage (23); user.setbirthday (새 날짜 ()); System.out.println (사용자); // object obj를 써서 objectOutputStream oos = null; try {oos = new ObjectOutputStream (new FileOutputStream ( "tempfile")); oos.writeobject (사용자); } catch (ioexception e) {e.printstacktrace (); } 마침내 {ioutils.closequietly (oos); } // 파일 파일에서 obj를 읽습니다. 파일 = 새 파일 ( "tempfile"); ObjectInputStream OIS = NULL; try {OIS = new ObjectInputStream (new FileInputStream (file)); user newuser = (user) ois.readobject (); System.out.println (Newuser); } catch (ioexception e) {e.printstacktrace (); } catch (classNotFoundException e) {e.printstacktrace (); } 마침내 {ioutils.closequietly (OIS); try {fileUtils.forcedElete (파일); } catch (ioexception e) {e.printstacktrace (); }}}}} // output // user {name = 'hollis', age = 23, gender = male = 남성, 생일 = tue 2 월 17:37:38 CST 2016} // user {name = 'hollis', age = 23, gender = null, 생일 = tue feb 02 17:37:38 cst 2016}3. 직렬화 및 사막화와 관련된 지식
1. 자바에서는 클래스가 java.io.serializable 인터페이스를 구현하는 한 직렬화 될 수 있습니다.
2. ObjectOutputStream 및 ObjectInputStream을 통해 객체를 직렬화하고 사로화하십시오
3. 가상 머신이 사막화를 허용하는지 여부는 클래스 경로 및 함수 코드가 일관된 지 여부에 달려 있습니다. 매우 중요한 점은 두 클래스의 직렬화 ID가 일관성이 있는지 여부입니다 (즉, 개인 정적 최종 Long SerialversionUid)
4. 직렬화는 정적 변수를 저장하지 않습니다.
5. 부모 클래스 객체를 직렬화하려면 부모 클래스가 직렬화 가능한 인터페이스를 구현하도록해야합니다.
6. 과도 키워드의 기능은 변수의 직렬화를 제어하는 것입니다. 변수 선언 전에이 키워드를 추가하면 변수가 파일에 직렬화되는 것을 방지 할 수 있습니다. 사형화 된 후, 과도 변수의 값은 int 유형이 0이고 객체 유형은 null과 같은 초기 값으로 설정됩니다.
7. 서버는 직렬화 된 객체 데이터를 클라이언트에게 보냅니다. 암호 문자열 등과 같이 객체의 일부 데이터는 민감합니다. 비밀번호 필드는 직렬화 될 때 암호화되기를 바랍니다. 클라이언트에 해독 된 키가있는 경우 클라이언트를 실시 할 때만 암호를 읽을 수 있으며, 이는 직렬화 된 객체의 데이터 보안을 어느 정도 보장 할 수 있습니다.
4. ArrayList의 직렬화
ArrayList 직렬화를 도입하기 전에 질문을 고려해 봅시다.
직렬화 및 사막화 전략을 사용자 정의하는 방법
이 질문으로 java.util.arraylist의 소스 코드를 살펴 보겠습니다.
코드 3
Public Class ArrayList <E>는 AbstractList <e> implements List <e>, RandomAccess, clonable, java.io.serializable {private static final long serialversionuid = 8683452581122892189L; 과도 객체 [] elementData; // 중첩 클래스 액세스 개인 int 크기를 단순화하기위한 비 프리티브;}저자는 다른 멤버 변수를 생략합니다. 위의 코드에서 ArrayList가 java.io.serializable 인터페이스를 구현하므로 직렬화 및 사형화 할 수 있음을 알 수 있습니다. ElementData는 일시적 이므로이 멤버 변수는 직렬화 및 유지되지 않을 것이라고 생각합니다. 우리의 아이디어를 확인하기 위해 데모를 작성합시다.
코드 4
public static void main (String [] args)은 ioexception, classNotFoundException {list <string> stringList = new ArrayList <string> (); StringList.add ( "Hello"); StringList.add ( "World"); StringList.add ( "Hollis"); StringList.add ( "chuang"); System.out.println ( "Init StringList" + StringList); ObjectOutputStream ObjectOutputStream = 새 ObjectOutputStream (새 FileOutputStream ( "StringList")); ObjectOutputStream.writeObject (StringList); ioutils.close (ObjectOutputStream); 파일 = 새 파일 ( "StringList"); ObjectInputStream ObjectInputStream = new ObjectInputStream (new FileInputStream (file)); list <string> newstringList = (list <string>) ObjectInputStream.readoBject (); ioutils.close (ObjectInputStream); if (file.exists ()) {file.delete (); } system.out.println ( "New StringList" + NewStringList); } // init StringList [Hello, World, Hollis, Chang] // New StringList [Hello, World, Hollis, Chang]ArrayList를 아는 사람은 ArrayList의 기본 레이어가 배열을 통해 구현된다는 것을 알고 있습니다. 그런 다음 배열 ElementData는 실제로 목록에 요소를 저장하는 데 사용됩니다. 이 속성의 선언 방법을 통해 직렬화를 통해 지속될 수 없다는 것을 알고 있습니다. 그렇다면 코드 4의 결과는 왜 직렬화 및 사막화를 통해 목록의 요소를 유지합니까?
5. writeObject 및 readObject 메소드
ArrayList에서 메소드가 정의됩니다 : WriteObject 및 readObject.
결론은 다음과 같습니다.
직렬화 프로세스 중에 WriteObject 및 readObject 메소드가 직렬화 된 클래스에서 정의되면 가상 시스템은 객체 클래스에서 writeObject 및 readObject 메소드를 호출하여 사용자 정의 직렬화 및 사막화를 수행합니다.
이러한 메소드가 없으면 기본 호출은 ObjectOutputStream의 DefaultWriteObject 메소드 및 ObjectInputStream의 DefaultReadoBject 메소드입니다.
사용자 정의 WriteObject 및 readObject 메소드를 사용하면 직렬화 프로세스 중에 직렬화 된 값을 동적으로 변경하는 등 직렬화 프로세스를 제어 할 수 있습니다.
이 두 가지 방법의 구체적인 구현을 살펴 보겠습니다.
코드 5
private void readObject (java.io.objectinputStream s)는 java.io.ioexception, classNotFoundException {elementData = empty_elementData; // 크기를 읽고 숨겨진 물건 S.DefaultReadObject (); // 용량으로 읽기 s.readint (); // if (size> 0) {// clone ()과 비슷하게 무시하십시오. 객체 [] a = elementData; // 모든 요소에서 올바른 순서로 읽습니다. for (int i = 0; i <size; i ++) {a [i] = s.readobject (); }}}코드 6
private void writeObject (java.io.objectoutputstream s)는 java.io.ioexception {// 요소 수를 쓰고 숨겨진 물건 int exploymodcount = modcount; s.defaultWriteObject (); // clone () s.writeint (size)와의 행동 적 호환성 용량으로 크기를 작성합니다. // 모든 요소를 올바른 순서로 기록합니다. for (int i = 0; i <size; i ++) {s.writeObject (elementData [i]); } if (modCount! = excliteModCount) {새 동시 동의 modificationException (); }}그렇다면 왜 ArrayList가 직렬화를 달성하기 위해이 방법을 사용해야합니까?
왜 과도
ArrayList는 실제로 동적 배열입니다. 채워질 때마다 설정 길이 값이 자동으로 증가합니다. 배열의 자동 성장 길이가 100으로 설정되고 실제로 하나의 요소 만 배치되면 99 개의 널 요소가 직렬화됩니다. 직렬화 중에 많은 널이 동시에 직렬화되지 않도록하기 위해 ArrayList는 요소 배열을 과도로 설정합니다.
WriteObject 및 readObject
앞서 언급했듯이 많은 수의 빈 객체가 포함 된 배열이 직렬화되는 것을 방지하고 스토리지를 최적화하기 위해 ArrayList는 DERFIENT에 DECLARE ELEMENTDATA를 사용합니다.
그러나 컬렉션으로서 직렬화 프로세스 중에 요소를 지속 할 수 있도록해야합니다. 따라서 그 요소는 WriteObject 및 readObject 메소드를 다시 작성하여 보존됩니다.
writeObject 메소드는 ElementData 배열의 요소를 출력 스트림 (ObjectOutputStream)에 저장합니다.
readObject 메소드는 입력 스트림 (ObjectInputStream)에서 객체를 읽고 할당을 ElementData 배열에 저장합니다.
이 시점에서 우리가 방금 물었던 질문에 대답 해 봅시다.
1. 직렬화 및 사막화 전략을 사용자 정의하는 방법
답변 : 직렬화 된 클래스에 WriteObject 및 readObject 메소드를 추가 할 수 있습니다. 2. 그러면 질문이 다시 온다 :
WriteObject 및 readObject 메소드는 ArrayList로 작성되지만이 두 가지 방법은 표시되지 않으며 호출됩니다.
따라서 클래스에 writeObject 및 readObject 메소드가 포함되어 있다면이 두 가지 방법은 어떻게 호출됩니까?
6. ObjectOutputStream
코드 4에서 객체의 직렬화 프로세스가 ObjectOutputStream 및 ObjectInputStream을 통해 구현된다는 것을 알 수 있습니다. 그래서 지금 질문으로 ArrayList의 WriteObject 및 readObject 메소드가 어떻게 호출되는지 분석해 봅시다.
공간을 절약하기 위해 다음은 ObjectOutputStream의 writeObject의 통화 스택입니다.
writeObject ---> writeObject0 ---> writeDordinaryObject ---> writeserialData ---> invokeWriteObject
다음은 InvokeWriteObject를 살펴 보는 것입니다.
void invokeWriteObject (Object OBJ, ObjectOutputStream out)는 ioException, UnSupportedOperationException {if (writeObjectMethod! = null) {try {writeObjectMethod.invoke (obj, new Object [] {out}); } catch (invocationTargeteXception ex) {Throwable th = ex.getTargetexception (); if (ioexception의 인스턴스) {trash (ioexception) th; } else {ThrowMiscexception (th); }} catch (불법 행위 지출 EX) {// 액세스 점검이 억압되었으므로 새로운 내부 메러 (예 :); }} else {새로운 unsupportedOperationException (); }}writeObjectMethod.invoke (obj, new Object [] {out}); 핵심이며 WriteObjectMethod 메소드는 반사를 통해 호출됩니다. 이것이 공무원이 WriteObjectMethod를 설명하는 방법입니다.
클래스 정의 된 writeObject 메소드 또는 NULL이 없으면 NULL
이 예제 에서이 방법은 ArrayList에서 정의하는 WriteObject 메소드입니다. 반사를 통해 불려졌습니다.
이 시점에서 우리가 방금 물었던 질문에 대답 해 봅시다.
클래스에 writeObject 및 readObject 메소드가 포함 된 경우이 두 가지 방법은 어떻게 호출됩니까?
답변 : ObjectOutputStream의 WriteObject 메소드와 ObjectInputStream의 readObject 메소드를 사용하면 반사가 호출됩니다.
지금까지 Arraylist의 직렬화 방법을 도입했습니다. 그래서 누군가가 그런 질문을 제기했는지 궁금합니다.
직렬화 가능한 것은 분명히 빈 인터페이스입니다. 이 인터페이스를 구현하는 메소드 만 직렬화되고 사막화 될 수있는 방법은 무엇입니까?
직렬화 가능한 인터페이스의 정의 :
공개 인터페이스 직렬화 가능 {}독자는 코드 1에서 시리얼 리화 가능한 코드를 제거한 다음 코드 2를 실행하여 java.io.notserializableException을 던질 수 있습니다.
실제로이 질문은 대답하기 쉽습니다. 지금 막 ObjectOutputStream의 WriteObject의 통화 스택으로 돌아가 봅시다.
writeObject ---> writeObject0 ---> writeDordinaryObject ---> writeserialData ---> invokeWriteObject
WriteObject0 메소드에는 코드가 있습니다.
if (obj instanceof string) {writestring ((string) obj, 샤워 형); } else if (cl.isArray ()) {writeArray (obj, desc, Unshared); } else if (obj instanceof enum) {writeEnum ((enum <?>) obj, desc, 샤워 형); } else if (serializable의 obj instance) {Writ } else {if (extendedDebuginfo) {새로운 notserializableException (cl.getName () + "/n" + debuginfostack.toString ()); } else {새로운 NOTSERIALIZALEException (cl.getName ()); }}직렬화 작업을 수행 할 때 직렬화 될 클래스가 열거, 어레이 및 직렬화 가능한 유형인지 여부를 결정합니다. 그렇지 않은 경우, 공개적으로 예고가 직접 던져집니다.
요약
1. 클래스가 직렬화 되려면 직렬화 가능한 인터페이스를 구현해야합니다. 그렇지 않으면, 직렬화 작업 중에 유형이 점검되므로, 직렬화 된 클래스는 열거, 어레이 및 직렬화 가능한 유형에 속해야하기 때문에 유형이 확인되기 때문에 공개 된 예고가 발생합니다.
2. 변수 선언 전에이 키워드를 추가하면 변수가 파일에 직렬화되는 것을 방지 할 수 있습니다.
3. 클래스에 WriteObject 및 readObjec 방법을 추가하면 사용자 정의 직렬화 전략을 구현할 수 있습니다.
위의 내용은이 기사에 관한 모든 것입니다. 모든 사람의 학습에 도움이되기를 바랍니다.