>  기사  >  Java  >  13.자바 기초 - 직렬화

13.자바 기초 - 직렬화

黄舟
黄舟원래의
2017-02-27 10:53:471109검색


기본 개념


직렬화를 소개하기 전에 먼저 다음 개념을 알아야 합니다.

  • 비지속성: JVM(Java Virtual Machine)에 존재하는 객체의 경우 내부 상태는 JVM이 실행될 때 메모리에서만 유지될 수 있습니다. 작동을 멈추면 내부 상태가 사라지므로 비지속적입니다.

  • 지속성: 개체를 영구적으로 저장하려는 경우(예: 지속성) 일반적인 접근 방식은 개체를 파일 또는 데이터베이스.

  • 직렬화: Java에서 객체 지속성을 달성하려면

    직렬화해야 합니다. 직렬화를 통해 활성 JVM에서 객체를 쉽게 직렬화할 수 있습니다. 객체는 저장을 위해 바이트 배열(스트림)로 변환됩니다.

  • 역직렬화: 파일이나 데이터베이스의 바이트 배열(스트림)을 JVM 활성 개체로 변환합니다.

Java에서는 직렬화 가능 및 외부화 가능 인터페이스를 구현하여 클래스를 직렬화할 수 있습니다.


직렬화 가능

클래스가 직렬화 가능 인터페이스를 구현하면 직렬화가 가능하다는 의미입니다.

특정 직렬화/역직렬화 작업은 객체 스트림(ObjectOutputStream/ObjectInputStream)을 통해 구현되어야 합니다.

구체적인 예를 살펴보겠습니다.

class Person implements Serializable {    
private String name;    
private int age;    
public Person(String name ,int age){        
this.name = name;        
this.age =age;
    }    
    public String getName() {        
    return name;
    }    public int getAge() {        
    return age;
    }    
    @Override
    public String toString() {        
    return "name is " + name + " , age is " + age;
    }
}public class Test {

    private final static String TEMPFILE = "E:" + File.separator + "test.txt";    
    public static void main(String[] args) {
        Person person = new Person("test",100);

        write(person);        // 关键 -> 序列化之后重新对对象进行赋值
        person = new Person("hello",999);

        read(person);        // 输出结果:
        // name is test , age is 100,序列化成功
        // 反序列化成功,name is test , age is 100
    }    // 通过 ObjectOutputStream 进行对象的序列化操作
    private static void write(Person person) {        try {
            ObjectOutputStream oos = 
                new ObjectOutputStream(new FileOutputStream(TEMPFILE));
            oos.writeObject(person);
            oos.close();
            System.out.println(person+",序列化成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }    // 通过 ObjectInputStream 进行对象的反序列化操作
    private static void read(Person person) {        try {
            ObjectInputStream ois = 
                new ObjectInputStream(new FileInputStream(TEMPFILE));
            person = (Person) ois.readObject();
            ois.close();
            System.out.println("反序列化成功.,"+person);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

출력 결과를 관찰합니다. 객체가 직렬화된 후 값을 다시 할당했는데 역직렬화 결과는 여전히 객체 이전의 값과 동일합니다. 일관성이 있다는 것은 객체가 영구적으로 저장되고 지속성이 달성된다는 것을 간접적으로 증명하는 것이기도 합니다.


직렬화 실패

직렬화를 원하지 않거나 직렬화할 수 없는 두 가지 상황이 있습니다.

  • 클래스에서 임시 및 정적 키워드로 수정된 멤버 변수는 직렬화를 원하지 않음을 나타냅니다. 부분 직렬화 오류가 발생합니다.

  • 클래스에 Thead 유형의 멤버 변수가 있으면 클래스를 직렬화할 수 없으며 그 영향이 전반적으로 나타납니다.

다음 상황을 살펴보겠습니다.

1.transient

변수가 수정될 때마다 이는 다음을 의미합니다. 매개변수는 일시적이며 직렬화를 원하지 않습니다(

직렬화 해제할 수 없음 ).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 age 属性进行修改class Person implements Serializable {    
// 用 transient 修该变量
    private transient int age;    
    // 省略部分代码...}
    // 调用后的输出结果:
    // name is test , age is 100,序列化成功
    // 反序列化成功.,name is test , age is 0

출력을 관찰하여 직렬화 전 연령은 100이고 역직렬화를 통해 읽은 연령은 0임을 확인합니다.

int 유형 매개변수의 초기값은 0입니다. 이는 매개변수가 직렬화되지 않았음을 의미합니다.


2.static

수정된 변수가 전역 변수를 나타내는 한, 클래스 객체에 의존하지 않고 전역 변수에 접근할 수 있습니다. 직렬화 작업은 개체를 저장하는 것입니다. 전역 변수가 기본적으로 직렬화되지 않는 것은 바로 이 기능과 직렬화 사이의 모순 때문입니다(

가 직렬화할 수 없다는 의미는 아닙니다 ).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable {    
// 用 static 修该变量
    private static String name;    
    // 省略部分代码...}
    // 输出结果:
    // name is test , age is 100,序列化成功//反序列化成功.,name is hello , age is 100

출력 결과를 관찰해 보면 name의 값이 직렬화 전 테스트가 아닌 hello라는 것을 알 수 있었습니다.

앞과 뒤의 값이 다른 것은 연재되지 않았음을 나타냅니다. 그리고 static의 특성은 클래스와 관련이 있기 때문에 직렬화 작업 후에 변수를 다시 할당하여 그 값이 null이 아닌 hello가 되었습니다.


3.Thread

객체의 멤버 변수에 Thread 유형이 포함되어 있으면

직렬화할 수 없습니다.

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable {    
//新增成员变量
    private Thread myThread = new Thread();    
    //省略部分代码...}
    // 输出结果(抛出异常):
    // Caused by: java.io.NotSerializableException: java.lang.Thread


사용자 정의 직렬화

위에서는 직렬화가 실패하는 상황을 설명합니다. 어떤 경우에는 직렬화가 구현되어야 합니다. 전역 변수의 직렬화). 그런 다음 클래스에서 직렬화/역직렬화 프로세스를 사용자 정의해야 합니다. 다음 예를 살펴보세요.

// 序列化操作代码与上面一致...
// 这里只对内部类 Person 的属性进行修改。class Person implements Serializable {    
private static String name;    
private transient int age;    
// 自定义序列化操作
    private void writeObject(ObjectOutputStream out) throws IOException{
       out.defaultWriteObject(); 
       out.writeObject(name);
       out.writeInt(age);

    }    // 自定义反序列化操作
    private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ 
        in.defaultReadObject();
        name = (String) in.readObject();
        age = in.readInt();
    }    
    // 省略部分代码...}
    // 输出结果:
    // name is test , age is 100,序列化成功
    // 反序列化成功,name is test , age is 100


Externalized

클래스는 외부화 가능을 구현합니다. 인터페이스는 직렬화할 수 있다는 의미이기도 하지만 두 가지 제한 사항이 있습니다.

  • 인터페이스는 사용자 정의 직렬화를 위해

    writeExternal 및 readExternal 메서드 구현을 강제합니다. 역직렬화 프로세스.

  • 에서는 클래스에

    매개변수 없는 생성자 가 포함되어야 합니다.

동시에 직렬화 과정이 공개적인 방식으로 정의되어 있기 때문에 안전하지 않습니다.

다음 예를 보세요:

// 序列化操作代码与上面一致,这里只对内部类 Person 的进行修改。
class Person implements Externalizable {    private static  String name;    private transient int age;    // 重点 ->必须有无参构造函数
    public Person(){

    }    
    public Person(String name ,int age){        
    this.name = name;        
    this.age =age;
    }    
    public String getName() {        
    return name;
    }    
    public int getAge() {        
    return age;
    }    
    @Override
    public String toString() {        
    return "name is " + name + " , age is " + age;
    }    
    // 实现接口的方法
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);  

    }    
    // 实现接口的方法
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = in.readInt();
    }
}
// 输出结果:
// name is test , age is 100,序列化成功
// 反序列化成功,name is test , age is 100


serialVersionUID

1. 개념

serialVersionUID는 직렬화된 버전 번호입니다.

그 역할은 직렬화 중에 버전 호환성을 유지하는 것입니다. 즉, 역직렬화는 버전이 업그레이드될 때에도 개체의 고유성을 유지합니다.

serialVersionUID를 생성하는 방법에는 두 가지가 있습니다.

  • 하나는 고정 값입니다: 1L

  • 하나는 JVM 컴퓨팅을 사용하는 것입니다. , 서로 다른 JVM은 서로 다른 컴퓨팅 알고리즘을 채택할 수 있습니다.

JVM 생성 방식의 경우 다음과 같은 특징이 있습니다.

  • 객체의 코드가 변경되지 않은 경우 serialVersionUID가 여러 번 생성됩니다. 또한 변함이 없습니다.

  • 메서드 가 수정되면 serialVersionUID **는 변경되지 않습니다**.

  • 객체의

    속성 이 수정되면 재생성된 serialVersionUID**가 변경됩니다**.

因此说明序列化是作用于对象属性上的


2.实例

下面通过实例来探究下 serialVersionUID 的具体作用:

  • 首先我们对对象进行序列化操作(这里取消了反序列化的操作)

// 序列化操作代码与上面例子一致...// 这里取消了反序列化的操作public static void main(String[] args) {
    Person person = new Person("test",100);
    write(person);
    person = new Person("java", 200);    //read(person);}
  • 然后新增一个对象的属性,再进行反序列化操作

// 省略部分代码,与上面的代码一致...class Person implements Serializable {    private String name;    private age;    //新增成员变量
    private int phone;    //省略部分代码...

    }
}public static void main(String[] args) {
    Person person = new Person("test",100);    //write(person);
    person = new Person("java", 200);
    read(person);
}// 输出结果(抛出异常):// java.io.InvalidClassException: Person;

观察代码,在序列化对象并没有添加 serialVersionUID 的情况,在对象序列化之后如果改变了对象的属性,反序列化就会抛出异常。如果对象添加了 serialVersionUID 就不会出现这种情况,这里就不验证了。

 以上就是13.Java 基础 - 序列化的内容,更多相关内容请关注PHP中文网(www.php.cn)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.