Java中对象的序列化详解及实例

Java中对象的序列化详解及实例攻略

什么是序列化

序列化是将对象转换为字节序列的过程,以便将其存储到文件或内存缓冲区中,也可以通过网络传输到另一个计算机中。反序列化则是从字节序列中重构对象的过程。

在Java中,序列化是通过实现Serializable接口来实现的。该接口中没有方法,只是用来指示该类是可序列化的。

序列化的作用

序列化在实际开发中非常有用。以下是序列化的主要用途:

  1. 持久化:序列化对象并将其保存到文件或数据库中以供以后使用。
  2. 交换数据:跨操作系统平台的应用程序可以使用序列化来交换数据。
  3. 远程调用:远程调用(RMI)机制使用序列化来传递对象参数和返回值。

序列化的基本用法

下面有一个简单的示例,展示了如何序列化和反序列化一个对象:

import java.io.*;

public class SerializationDemo {
    public static void main(String[] args) {
        // 序列化一个对象到文件中
        Student student = new Student(101, "John Doe");
        try {
            FileOutputStream fileOut = new FileOutputStream("student.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(student);
            out.close();
            fileOut.close();
            System.out.println("Serialized data is saved as student.ser");
        } catch (IOException i) {
            i.printStackTrace();
        }

        // 从文件中反序列化对象
        Student deserializedStudent = null;
        try {
            FileInputStream fileIn = new FileInputStream("student.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            deserializedStudent = (Student) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException | ClassNotFoundException i) {
            i.printStackTrace();
        }

        // 打印反序列化后的对象
        System.out.println("ID: " + deserializedStudent.id);
        System.out.println("Name: " + deserializedStudent.name);
    }
}

class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    int id;
    String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

上述代码需要先定义一个Student类,然后使用FileOutputStreamObjectOutputStream将对象序列化为student.ser文件,再使用FileInputStreamObjectInputStream将对象从文件中反序列化。最后,打印反序列化后的对象。

序列化的问题和解决方法

虽然序列化看起来很简单,但是它有很多问题,我会在下面的几个例子中进行说明。

示例1:序列化带有transient字段的对象

假设我们有以下Person类:

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    transient String password;

    public Person(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public String toString() {
        return "Name: " + name + ", Password: " + password;
    }
}

其中,password字段被标记为transient,表示它不应被序列化。这是因为密码通常是机密信息,不应该保存在磁盘上。

我们可以使用以下代码序列化和反序列化该对象:

import java.io.*;

public class SerializationTransientDemo {
    public static void main(String[] args) {
        // 序列化带transient字段的对象
        Person person = new Person("John Doe", "123456");
        try {
            FileOutputStream fileOut = new FileOutputStream("person.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(person);
            out.close();
            fileOut.close();
            System.out.println("Serialized data is saved as person.ser");
        } catch (IOException i) {
            i.printStackTrace();
        }

        // 反序列化带transient字段的对象
        Person deserializedPerson = null;
        try {
            FileInputStream fileIn = new FileInputStream("person.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            deserializedPerson = (Person) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException | ClassNotFoundException i) {
            i.printStackTrace();
        }

        System.out.println(deserializedPerson);
    }
}

然而,当你运行上面的代码后,你会发现输出为:Name: John Doe, Password: null。这是因为密码字段被标记为transient,它不会被序列化,所以在反序列化时,密码字段被设置为null

为了解决这个问题,可以在Person类中添加以下方法:

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();
    oos.writeObject(password);
}

private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
    ois.defaultReadObject();
    password = (String) ois.readObject();
}

这两个方法将会在序列化和反序列化时得到调用,writeObject()方法会自动对transient字段进行处理,并序列化其它non-transient字段,readObject()方法会自动将序列化后的transient字段恢复成原来的值。

现在,在你运行上述代码后,输出应为:Name: John Doe, Password: 123456

示例2:序列化一个继承自非实现Serializable接口的类的对象

假设我们有以下Animal接口和Dog类:

interface Animal {
    int getAge();
}

class Dog implements Animal {
    int age;

    public Dog(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

这里我们使用了interface来定义Animal类型,Dog类实现了该接口并定义了一个age字段。

我们可以使用以下代码将Dog对象序列化和反序列化:

import java.io.*;

public class SerializationNonSerializableDemo {
    public static void main(String[] args) {
        // 序列化继承自非实现Serializable接口的类的对象
        Dog dog = new Dog(2);
        try {
            FileOutputStream fileOut = new FileOutputStream("dog.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(dog);
            out.close();
            fileOut.close();
            System.out.println("Serialized data is saved as dog.ser");
        } catch (IOException i) {
            i.printStackTrace();
        }

        // 反序列化继承自非实现Serializable接口的类的对象
        Dog deserializedDog = null;
        try {
            FileInputStream fileIn = new FileInputStream("dog.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            deserializedDog = (Dog) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException | ClassNotFoundException i) {
            i.printStackTrace();
        }

        System.out.println("Age: " + deserializedDog.getAge());
    }
}

然而,你会发现在反序列化时会出现java.io.NotSerializableException。这是因为Dog类没有实现Serializable接口。

为了解决这个问题,我们可以让Dog类继承Serializable接口:

class Dog implements Animal, Serializable {
    int age;

    public Dog(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

现在,你再运行上述代码,你会发现输出是:Age: 2

总结

在Java中,序列化是一种非常重要的技术,因为它可以将对象转换为字节序列以便持久化、交换数据或进行远程调用。在使用序列化时要注意一些问题,比如如何序列化transient字段、如何序列化继承自非实现Serializable接口的类的对象等,但是我们可以通过一些技巧来解决这些问题。在实际开发中,应用程序需要考虑如何正确地使用序列化来确保程序的正确性和稳定性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中对象的序列化详解及实例 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java中的继承是什么?

    Java中的继承是面向对象编程中很重要的一种机制。通过继承,我们可以创建一个新类,从已有的类中继承属性和方法,并且可以对这些属性和方法进行修改、扩展或重写。继承可以提高代码的复用性,减少代码冗余,简化程序设计。 Java中,继承是通过使用 extends 关键字来实现的。下面是一个简单的示例: public class Animal { public voi…

    Java 2023年4月27日
    00
  • JDK14的新特性NullPointerExceptions的使用

    下面是详细讲解“JDK14的新特性NullPointerExceptions的使用”的完整攻略。 什么是NullPointerExceptions NullPointerExceptions 是 Java 程序中最常见的错误之一,它通常会在代码中使用空引用时发生。在 JDK14 中,对于这个问题已经进行了一些新的改进,我们可以更加方便地处理这个问题。 如何使…

    Java 2023年5月27日
    00
  • Sprint Boot @NegativeOrZero使用方法详解

    @NegativeOrZero是Spring Boot中的一个注解,用于标记一个字段或方法参数必须为非正数。在本文中,我们将详细介绍@NegativeOrZero注解的作用和使用方法,并提供两个示例。 @NegativeOrZero注解的作用 @NegativeOrZero注解用于标记一个字段或方法参数必须为非正数。当使用@NegativeOrZero注解标…

    Java 2023年5月5日
    00
  • 详解http请求中的Content-Type

    下面是关于“详解HTTP请求中的Content-Type”的完整攻略: 什么是Content-Type? 在HTTP请求中,Content-Type是一个HTTP头部字段,用于描述HTTP请求或响应中实际的内容类型。Content-Type的值是由MIME规范定义的。 Content-Type有哪些常见的类型? Content-Type有很多种类型,这里列举…

    Java 2023年6月15日
    00
  • Spring Mvc下实现以文件流方式下载文件的方法示例

    下面是针对“Spring MVC下实现以文件流方式下载文件的方法示例”的完整攻略: 1. 需求分析 我们需要实现一个以文件流方式下载文件的功能,具体来说,就是用户在调用该接口时,能够将指定文件以文件流的形式返回浏览器端,让用户下载文件。 2. 实现步骤 2.1 定义接口 我们需要在Controller中定义一个接口来实现文件下载的功能,具体的代码如下: @R…

    Java 2023年6月15日
    00
  • Java spring 通过注解方式创建对象的示例详解

    Java spring 通过注解方式创建对象的示例详解 前言 在Java Spring框架中创建对象可以使用XML配置或者注解方式。其中注解方式比较方便快捷,并且代码可读性更好。在本文中,将详细讲解如何使用Java Spring框架通过注解方式创建对象。 环境 JDK版本:1.8+ Spring版本:5.0+ 使用注解方式创建对象 @Component注解 …

    Java 2023年5月26日
    00
  • Java解析使用JSON的多种方法

    以下是详细讲解“Java解析使用JSON的多种方法”的完整攻略: 什么是JSON? JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON格式的数据通常用于异步浏览器/服务器数据交换。 JSON格式的数据由键值对组成,类似于JavaScript中的对象(对象是由编号的…

    Java 2023年5月20日
    00
  • Java中super关键字介绍以及super()的使用

    当子类需要引用父类的构造方法、成员变量或成员方法时,需要使用Java中的super关键字。super也可以理解为是当前对象的父类对象。 super的使用有以下几种形式: 使用super引用父类的成员变量和成员方法 在子类中可以使用super关键字来引用父类的成员变量和成员方法。例如: public class Parent { private int age…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部