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日

相关文章

  • JavaSpringBoot报错“BeanDefinitionStoreException”的原因和处理方法

    原因 “BeanDefinitionStoreException” 错误通常是以下原因引起的: 配置问题:如果您的配置不正确,则可能会出现此错误。在这种情况下,您需要检查您的配置并确保它们正确。 类型不匹配:如果您的代码中存在类型不匹配问题,则可能会出现此错误。在这种情况下,您需要检查您的代码并确保它们正确。 解决办法 以下是解决 “BeanDefiniti…

    Java 2023年5月4日
    00
  • 在Java的Struts框架下进行web编程的入门教程

    在Java的Struts框架下进行web编程的入门教程 什么是Struts框架? Struts框架是基于Java Servlet和Java Server Pages技术的Web应用框架,提供了处理用户请求和生成响应的框架。它是MVC开发模式的一种实现,能够更好地分离应用程序的模型、视图和控制器。Struts在Web应用开发中已经非常成熟,并且拥有丰富的生态圈…

    Java 2023年5月19日
    00
  • 使用JWT作为Spring Security OAuth2的token存储问题

    使用JWT(JSON Web Token)作为 Spring Security OAuth2 的 token 存储方案,可以避免服务器端存储 token 所带来的开销和管理复杂度,并且具有无状态、分布式、可扩展、自包含等优点,在实际开发中非常实用。下面是一份完整攻略: 1. 引入相关依赖 在 pom.xml 中添加 spring-security-jwt 依…

    Java 2023年6月3日
    00
  • 如何通过一张图搞懂springBoot自动注入原理

    下面是关于“如何通过一张图搞懂springBoot自动注入原理”的完整攻略。 1. 简介 在 Spring Boot 中,我们可以使用自动配置完成很多操作,其中最重要的一个就是通过自动注入来维护 Spring 应用程序之间的依赖关系。 Spring Boot 中自动注入的原理比较复杂,但我们可以用一张图来概述它的过程。 2. 图片介绍 下面这张图片展示了自动…

    Java 2023年5月15日
    00
  • 浅析Spring4新特性概述

    下面是关于“浅析Spring4新特性概述”的完整攻略,包含两个示例说明。 浅析Spring4新特性概述 Spring是一个流行的Java开发框架,它提供了许多功能和特性来简化Java应用程序的开发。Spring4是Spring框架的一个重要版本,它引入了许多新特性和改进。本文将介绍一些Spring4的新特性。 Java 8支持 Spring4引入了对Java…

    Java 2023年5月17日
    00
  • 使用Java实现qq邮箱发送邮件

    使用Java实现qq邮箱发送邮件的完整攻略 1. 前置条件 在使用Java编写发送邮件的程序之前,需要确保以下条件已经满足: 已经安装并配置好了Java开发环境。 有qq邮箱账号,并开启了SMTP服务。 2. 导入相应的依赖 在发送邮件之前,需要导入JavaMail API,可以在Maven中加入以下依赖: <dependency> <gr…

    Java 2023年6月16日
    00
  • Java实现学生管理系统详解

    Java 实现学生管理系统详解 本文将详细讲解如何使用 Java 编写学生管理系统,包括实现增、删、改、查等基本功能。 前置知识 在学习本篇攻略前,你需要了解以下 Java 的基础知识:- 面向对象的思想- 类的定义和属性、方法的声明- Java 集合- 文件的读写操作 实现步骤 1. 数据存储结构 我们需要将学生的基本信息存储起来,然后进行各种操作。这里使…

    Java 2023年5月18日
    00
  • Spring Security如何为用户示例添加角色详解

    为用户添加角色是 Spring Security 中常见的安全认证需求之一,下面是 Spring Security 如何为用户添加角色的完整攻略。 1. 添加角色 在 Spring Security 中,我们可以通过给用户添加角色来实现安全认证。为了演示,我们通过以下两个示例来说明: 1.1 示例1:自定义用户角色 我们首先需要定义一个用户角色,并将其作为权…

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