Java 시리얼화.md

11640 단어

1. 객체 시리얼화된 정의


Java 플랫폼은 메모리에서 재사용 가능한 Java 대상을 만들 수 있지만, 일반적인 상황에서 JVM이 실행될 때만 이러한 대상이 존재할 수 있다. 즉, 이러한 대상의 생명주기는 JVM의 생명주기보다 길지 않다.그러나 실제 응용 프로그램에서는 JVM이 실행을 멈춘 후에 지정한 대상을 저장하고 나중에 저장된 대상을 다시 읽어야 할 수도 있다.자바 대상 서열화는 우리가 이 기능을 실현하는 데 도움을 줄 수 있다.
자바 대상을 서열화하면 대상을 저장할 때 상태를 한 조의 바이트로 저장하고 미래에 이 바이트를 대상으로 조립합니다.객체 시리얼화는 객체의 상태, 즉 객체의 구성원 변수를 저장합니다.이로써 대상 서열화는 클래스의 정적 변수에 주목하지 않는다는 것을 알 수 있다.
객체를 영구화할 때 객체 서열화를 사용하는 것 외에 RMI(원격 메소드 호출)를 사용하거나 네트워크에서 객체를 전달할 때 객체 서열화를 사용합니다.

2. 간단한 실례


자바에서 하나의 클래스만 자바를 실현합니다.io.Serializable 인터페이스는 서열화될 수 있습니다.여기에 시리얼화된 클래스 Person이 생성됩니다.Gender 클래스, 열거 유형, 성별
public enum Gender {  
    MALE, FEMALE  
}

매거 형식은 기본적으로 계승 클래스java를 사용합니다.lang.enum, 이 클래스는 Serializable 인터페이스를 실현하기 때문에 매거 유형 대상은 기본적으로 서열화될 수 있습니다
Serializable 인터페이스를 구현한 Person 클래스는 세 개의 필드를 포함합니다:name,String 유형.age, Integer 유형,gender, Gender 유형또한 Person 실례의 내용을 인쇄하기 편리하도록 이 종류의 toString () 방법을 다시 쓴다
class Person implements Serializable{
    private String name = null;
    private int age = 0;
    private Gender gender = Gender.FEMALE;

    public Person() {
        System.out.println("     ");
    }

    public Person(int age, Gender gender, String name) {
        System.out.println("    ");
        this.age = age;
        this.gender = gender;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                '}';
    }
}

테스트 프로그램
public static void main(String[] args) throws Exception {
      File person = new File("src\\main\\resources\\person.out");
      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(person));

      Person p = new Person(27, Gender.FEMALE, "hahah");
      out.writeObject(p);
      out.close();

      ObjectInput in = new ObjectInputStream(new FileInputStream(person));
      Object per = in.readObject();
      in.close();
      System.out.println(per);
  }

출력 결과는 다음과 같습니다.
참구조기
Person{age=27, name='hahah', gender=FEMALE}
이때 반드시 주의해야 할 것은 저장된 Person 대상을 다시 읽을 때 Person의 어떤 구조기도 호출되지 않았는데, 마치 바이트를 사용하여 Person 대상을 복원한 것처럼 보였다는 것이다.
Person 대상이 개인에 저장되면out 파일에 있는 후에 우리는 다른 곳에서 이 파일을 읽어서 대상을 복원할 수 있지만, 이 읽기 프로그램의 CLASSPATH에Person이 포함되어 있는지 확인해야 한다.class (Person 대상을 읽을 때 Person 클래스를 표시하지 않아도, 상례와 같이), 그렇지 않으면 ClassNotFoundException을 던집니다.

3. Serializable의 역할

private void writeObject0(Object obj, boolean unshared) throws IOException {
  // remaining cases
  if (obj instanceof String) {
      writeString((String) obj, unshared);
  } else if (cl.isArray()) {
      writeArray(obj, desc, unshared);
  } else if (obj instanceof Enum) {
      writeEnum((Enum>) obj, desc, unshared);
  } else if (obj instanceof Serializable) {
      writeOrdinaryObject(obj, desc, unshared);
  } else {
      if (extendedDebugInfo) {
          throw new NotSerializableException(
              cl.getName() + "
" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } }

상기 코드에서 알 수 있듯이, 쓰기 대상의 유형이 String, 또는 그룹, 또는 Enum, 또는 Serializable이면, 이 대상을 서열화할 수 있으며, 그렇지 않으면 NotSerializable Exception을 던질 수 있다

4. 기본 시리얼화 메커니즘


특정한 클래스만 Serializable 인터페이스를 실현하고 다른 처리가 없으면 기본 서열화 메커니즘을 사용합니다.기본 메커니즘을 사용하면 객체를 시리얼화할 때 현재 객체 자체뿐만 아니라 해당 객체가 참조하는 다른 객체도 시리얼화됩니다. 마찬가지로 다른 객체가 참조하는 다른 객체도 시리얼화됩니다.따라서 만약에 하나의 대상에 포함된 구성원 변수가 용기류의 대상이고 이런 용기에 포함된 원소도 용기류의 대상이라면 이 서열화 과정은 비교적 복잡하고 비용도 비교적 크다.

5. 서열화에 영향


현실 응용에서 일부 때는 기본 서열화 메커니즘을 사용할 수 없다.예를 들어 서열화 과정에서 민감한 데이터를 무시하거나 서열화 과정을 간소화하기를 바란다.다음은 서열화에 영향을 미치는 몇 가지 방법을 소개할 것이다.

5.1 transient 키워드


어떤 필드가transient로 성명되면 기본 서열화 메커니즘은 이 필드를 무시합니다.Person 클래스의 age 필드를 다음과 같이 transient로 선언합니다.
public class Person implements Serializable {  
    ...  
    transient private int age = 0;
    transient private Gender gender = Gender.FEMALE;
    ...  
}

출력
참구조기
Person{age=0, name='hahah', gender=null}
알 수 있듯이age,gender 필드는 서열화되지 않고 기본적으로 초기화된 값입니다

5.2 writeObject() 메서드와 readObject() 메서드


상기한transitive로 성명된 필드age에 대해transitive 키워드를 제거하는 것 외에 다시 서열화할 수 있는 다른 방법이 있습니까?방법 중 하나는 다음과 같이 Person 클래스에 두 가지 방법을 추가하는 것입니다. writeObject()와 readObject()입니다.
public class Person implements Serializable {  
    ...  
    transient private Integer age = null;  
    ...  

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(age);
        out.writeObject(gender);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        age = in.readInt();
        gender = (Gender) in.readObject();
    }
}

출력
참구조기
Person{age=27, name='hahah', gender=FEMALE}
write Object () 방법에서 Object OutputStream의defaultWrite Object () 방법을 먼저 호출합니다. 이 방법은 5.1절에서 설명한 바와 같이 기본 서열화 메커니즘을 실행하고,age 필드를 무시합니다.그리고 writeInt () 방법을 사용해서age 필드를 Object OutputStream에 표시합니다.readObject()는 writeObject() 메서드와 동일한 방법으로 객체를 읽는 역할을 합니다.
주의해야 할 것은, write Object () 와readObject () 는private 방법입니다. 그러면 어떻게 호출됩니까?의심할 여지없이 반사를 사용한다.자세한 내용은 Object Output Stream의 write Serial Data 방법과 Object Input Stream의read Serial Data 방법을 보십시오.

5.3 Externalizable 커넥터


transient 키워드를 사용하든 write Object () 와readObject () 방법을 사용하든 사실은serializable 인터페이스를 기반으로 한 서열화입니다.JDK에 또 다른 서열화 인터페이스인 Externalizable가 제공되었는데, 이 인터페이스를 사용하면 이전에 Serializable 인터페이스를 기반으로 한 서열화 메커니즘이 효력을 상실할 것이다.Person 클래스를 다음과 같이 수정합니다.
class PersonEx implements Externalizable{
    private String name = null;
    transient private int age = 0;
    transient private Gender gender = Gender.FEMALE;

    public PersonEx() {
        System.out.println("     ");
    }

    public PersonEx(int age, Gender gender, String name) {
        System.out.println("    ");
        this.age = age;
        this.gender = gender;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", gender=" + gender +
                '}';
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {

    }
}

테스트 프로그램 만들기
File personE = new File("src\\main\\resources\\personE.out");
ObjectOutputStream outE = new ObjectOutputStream(new FileOutputStream(personE));

PersonEx pE = new PersonEx(27, Gender.FEMALE, "hahah");
outE.writeObject(pE);
outE.close();

ObjectInput inE = new ObjectInputStream(new FileInputStream(personE));
Object perE = inE.readObject();
inE.close();
System.out.println(perE);

결과 내보내기
참구조기
무참구조기
Person{age=0, name='null', gender=FEMALE}
한편, Person 대상 중 어느 필드도 서열화되지 않았음을 알 수 있고, 다른 한편, 이번 서열화 과정은 Person 종류의 무참구조기를 호출한 것을 발견할 수 있다.
xternalizable는 Serializable에 계승되어 있으며, 이 인터페이스를 사용할 때, 서열화된 세부 사항은 프로그래머가 완성해야 합니다.위와 같은 코드는 writeExternal () 과readExternal () 방법이 처리되지 않았기 때문에, 이 서열화 행위는 필드를 저장하거나 읽지 않습니다.이것이 바로 출력 결과의 모든 필드의 값이 비어 있는 이유입니다.
또한 Externalizable를 사용하여 서열화할 때, 대상을 읽을 때, 서열화된 클래스의 무참구조기를 호출하여 새로운 대상을 만들고, 저장된 대상의 필드 값을 각각 새 대상에 채웁니다.이번 서열화 과정에서 퍼슨류의 무참구조기가 호출되는 이유다.이 때문에 Externalizable 인터페이스를 실현하는 클래스는 무참한 구조기를 제공해야 하며, 접근 권한은public입니다.
위의 Person 클래스를 더 수정하여 name과age 필드를 서열화할 수 있도록 하였으나, 다음 코드와 같이 gender 필드를 무시하였습니다.
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
   name = (String) in.readObject();
   age = in.readInt();
}

@Override
public void writeExternal(ObjectOutput out) throws IOException {
   out.writeObject(name);
   out.writeInt(age);
}

결과 내보내기
참구조기
무참구조기
Person{age=27, name='hahah', gender=FEMALE}

5.4 readResolve() 방법


Singleton 모드를 사용할 때, 어떤 종류의 실례가 유일해야 한다고 기대해야 하지만, 만약 이 종류가 서열화될 수 있다면 상황은 약간 다를 수 있습니다.이 때 2절에 사용된 Person 클래스를 수정하여 Singleton 모드를 실현하도록 한다. 다음과 같다.
class PersonS implements Serializable {

    private static class InstanceHolder {
        private static final PersonS instatnce = new PersonS("John", 31, Gender.MALE);
    }

    public static PersonS getInstance() {
        return InstanceHolder.instatnce;
    }

    private String name = null;

    private Integer age = null;

    private Gender gender = null;

    private PersonS() {
        System.out.println("none-arg constructor");
    }

    private PersonS(String name, Integer age, Gender gender) {
        System.out.println("arg constructor");
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

생성 테스트
System.out.println("---------------------------------------------------------");

File personS = new File("src\\main\\resources\\personS.out");
ObjectOutputStream outS = new ObjectOutputStream(new FileOutputStream(personS));

outS.writeObject(PersonS.getInstance());
outS.close();

ObjectInput inS = new ObjectInputStream(new FileInputStream(personS));
Object perS = inS.readObject();
inS.close();
System.out.println(perS);
System.out.println(PersonS.getInstance() == perS);

결과 내보내기
arg constructor
Person{age=31, name='John', gender=MALE}
false

주의해야 할 것은 파일에서person.out에서 가져온 Person 대상은 Person 클래스의 단일 대상과 같지 않습니다.시리얼화 프로세스에서도 단일 인스턴스의 특성을 유지하기 위해 다음과 같이 Person 클래스에 readResolve() 메서드를 추가하여 Person의 단일 인스턴스 객체를 직접 반환할 수 있습니다.
private Object readResolve() throws ObjectStreamException {
    return InstanceHolder.instatnce;
}

결과 출력:
arg constructor
Person{age=27, name='hahah', gender=FEMALE}
true

Serializable 인터페이스를 실현하든지 Externalizable 인터페이스를 실현하든지 입출력 흐름에서 대상을 읽을 때readResolve () 방법이 호출됩니다.실제로는 역서열화 과정에서 만들어진 객체를 readResolve()에서 반환된 객체로 직접 대체합니다.

좋은 웹페이지 즐겨찾기