Heim  >  Artikel  >  Java  >  13.Java-Grundlagen – Serialisierung

13.Java-Grundlagen – Serialisierung

黄舟
黄舟Original
2017-02-27 10:53:471075Durchsuche


Grundlegende Konzepte


Bevor Sie die Serialisierung einführen, müssen Sie zunächst die folgenden Konzepte kennen:

  • Nichtpersistenz: Für Objekte, die in JVM (Java Virtual Machine) vorhanden sind, kann ihr interner Zustand nur im Speicher beibehalten werden funktioniert nicht mehr, der interne Zustand verschwindet, sodass er nicht dauerhaft ist.

  • Persistenz: Wenn Sie das Objekt dauerhaft speichern möchten (d. h. Persistenz), besteht der übliche Ansatz darin, es in einer Datei oder Datenbank.

  • Serialisierung: Wenn Sie Objektpersistenz in Java erreichen möchten, müssen Sie es

    serialisieren. Durch Serialisierung können Sie es einfach in der JVM serialisieren Das Objekt wird zur Speicherung in ein Byte-Array (Stream) konvertiert.

  • Deserialisierung: Konvertieren Sie ein Byte-Array (Stream) in einer Datei oder Datenbank in ein aktives JVM-Objekt.

In Java können Klassen durch Implementierung der Schnittstellen Serializable und Externalizable serialisiert werden.


Serialisierbar

Sobald eine Klasse die Schnittstelle Serialisierbar implementiert, bedeutet dies, dass sie serialisiert werden kann.

Spezifische Serialisierungs-/Deserialisierungsvorgänge müssen über Objektströme (ObjectOutputStream/ObjectInputStream) implementiert werden.

Sehen wir uns ein konkretes Beispiel an:

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();
        }
    }
}
Beobachten Sie die Ausgabeergebnisse. Wir weisen das Objekt nach der Serialisierung neu zu, und das Ergebnis der Deserialisierung ist immer noch dasselbe wie vor der Serialisierung. Die Werte sind konsistent, was auch indirekt beweist, dass das Objekt dauerhaft gespeichert ist und Persistenz erreicht wird.


Serialisierung fehlgeschlagen

Serialisierung fehlgeschlagen. Es gibt zwei Situationen: Sie möchten nicht serialisiert werden oder können nicht serialisiert werden.

  • In einer Klasse zeigen Mitgliedsvariablen, die durch die Schlüsselwörter transient und static geändert wurden, an, dass sie nicht serialisiert werden möchten. Dies führt zu einem teilweisen Serialisierungsfehler.

  • Wenn in einer Klasse eine Mitgliedsvariable vom Typ Thead vorhanden ist, kann die Klasse nicht serialisiert werden und ihre Auswirkung ist umfassend.

Sehen wir uns diese Situationen an:

1.transient

Immer wenn eine Variable dadurch geändert wird, bedeutet das das Parameter sind transient und möchten nicht serialisiert werden (

nicht unserialisierbar ).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 age 属性进行修改class Person implements Serializable {    
// 用 transient 修该变量
    private transient int age;    
    // 省略部分代码...}
    // 调用后的输出结果:
    // name is test , age is 100,序列化成功
    // 反序列化成功.,name is test , age is 0
Beobachten Sie die Ausgabeergebnisse und stellen Sie fest, dass das Alter vor der Serialisierung 100 beträgt, während das durch die Deserialisierung gelesene Alter 0 beträgt.

Der Anfangswert des Parameters vom Typ int ist 0, was bedeutet, dass der Parameter nicht serialisiert wurde.


2.static

Solange die dadurch geänderten Variablen globale Variablen darstellen, können Sie auf globale Variablen zugreifen, ohne sich auf Klassenobjekte verlassen zu müssen. Der Serialisierungsvorgang besteht darin, das Objekt zu speichern. Gerade wegen des Widerspruchs zwischen dieser Funktion und der Serialisierung werden globale Variablen standardmäßig nicht serialisiert (

bedeutet nicht, dass sie nicht serialisiert werden können ).

// 序列化过程、调用过程与上述例子一致,省略代码...
// 这里只对内部类 Person 的 name 属性进行修改class Person implements Serializable {    
// 用 static 修该变量
    private static String name;    
    // 省略部分代码...}
    // 输出结果:
    // name is test , age is 100,序列化成功//反序列化成功.,name is hello , age is 100
Beobachten Sie die Ausgabeergebnisse und stellen Sie fest, dass der Wert von name „Hallo“ lautet und nicht „Test vor der Serialisierung“.

Die unterschiedlichen Werte davor und danach zeigen an, dass es nicht serialisiert wurde. Und da die Eigenschaften von static mit Klassen zusammenhängen, wurde die Variable nach der Serialisierungsoperation neu zugewiesen, was dazu führte, dass ihr Wert nicht null, sondern „Hallo“ war.


3.Thread

Wenn die Mitgliedsvariable des Objekts den Thread-Typ enthält, kann es

nicht serialisiert werden.

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

Benutzerdefinierte Serialisierung

Das Obige beschreibt die Situation, in der die Serialisierung in einigen Fällen fehlschlägt (z. B. unbedingt). Serialisierung globaler Variablen implementieren). Dann müssen Sie den Serialisierungs-/Deserialisierungsprozess in der Klasse anpassen. Sehen Sie sich das folgende Beispiel an:

// 序列化操作代码与上面一致...
// 这里只对内部类 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

Externalisierbar

Die Klasse implementiert Externalisierbare Schnittstelle bedeutet auch, dass sie serialisiert werden kann, hat aber zwei Einschränkungen:

  • Die Schnittstelle erzwingt die Implementierung der Methoden

    writeExternal und readExternal für benutzerdefinierte Serialisierung und Deserialisierungsprozess.

  • erfordert, dass die Klasse einen

    parameterlosen Konstruktor enthalten muss.

Gleichzeitig ist es nicht sicher, da sein Serialisierungsprozess in einer öffentlichen Methode definiert ist.

Sehen Sie sich das folgende Beispiel an:

// 序列化操作代码与上面一致,这里只对内部类 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. Konzept

serialVersionUID, die serialisierte Versionsnummer.

Seine Aufgabe besteht darin, die Versionskompatibilität während der Serialisierung aufrechtzuerhalten, d. h. die Deserialisierung behält weiterhin die Einzigartigkeit des Objekts bei, wenn die Version aktualisiert wird.

Es gibt zwei Möglichkeiten, serialVersionUID zu generieren:

  • Eine ist ein fester Wert: 1L

  • Eine ist durch JVM-Computing Verschiedene JVMs können unterschiedliche Rechenalgorithmen verwenden.

Für die JVM-Generierungsmethode weist sie die folgenden Merkmale auf:

  • Wenn der Code des Objekts unverändert bleibt, wird die serialVersionUID mehrmals generiert Es ist auch unverändert.

  • Wenn die

    -Methode geändert wird, bleibt die serialVersionUID ** unverändert **.

  • Wenn das

    -Attribut des Objekts geändert wird, ändert sich die neu generierte 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)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn