深入理解Java序列化:从入门到实践

2024-09-01 03:04

本文主要是介绍深入理解Java序列化:从入门到实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在前面的学习中我们简单的学习到了对象流的使用,我们先来回顾一下

对象流

在Java中,对象流是一种特殊的输入输出流,用于处理对象的序列化和反序列化操作。对象流主要包括ObjectOutputStreamObjectInputStream两个类。

ObjectOutputStream

这是一个输出流,用于将对象序列化后写入到目标输出流中。它可以将Java对象转换为一系列字节,并写入到底层的输出流(如文件、网络套接字等)中。通过调用writeObject(Object obj)方法,可以将指定的对象序列化后写入流中。

ObjectInputStream

这是一个输入流,用于从源输入流中读取字节序列,并将其反序列化为Java对象。它允许从文件、网络等源中读取经过序列化的对象数据,并将其恢复为原始的对象形式。通过调用readObject()方法,可以从流中读取并返回下一个对象

序列化与反序列化 

序列化 

序列化是指将对象的状态信息转换为可以存储或传输的形式(如字节序列)的过程。在Java中,这通常是通过实现java.io.Serializable接口来完成的。当一个对象被序列化时,其状态被保存为一系列字节,这些字节可以被写入到文件、通过网络发送,或者存储在内存中以便稍后使用。序列化后的对象可以被完全重建,以恢复其原始状态。 

反序列化 

反序列化是序列化的逆过程,即将字节序列转换回Java对象的过程。在Java中,这通常是通过ObjectInputStream类来完成的。当从文件、网络等源中读取到序列化后的对象数据时,可以使用ObjectInputStreamreadObject()方法将其反序列化为原始对象。 

 实践

先先来看看今天的实践案例

向文件中读出对象 

 当前程序启动后要求用户顺序输入以上四个信息然后实例化一个User对象保存着四个信息并将该对象序列化到文件中。

文件名的命名规则:用户名.obj 比如该用户输入用户名为张三,那么这个对象 序列化后的文件名为:张三.obj

用户输入信息后做必要的格式验证,比如4个信息是否都输入内容了。不能有空的。 用户名只能包含字母数字下划线,并且1-32位

    public static void main(String[] args) throws IOException {Scanner scanner = new Scanner(System.in);String name = inputString("请输入你的名字(只能包含字母数字下划线,并且1-32位",input -> !input.isEmpty() && input.matches("^[a-zA-Z0-9_]{1,32}$"));//使用这个 Predicate 实例来检查任意字符串是否满足这个条件String nick = inputString("请输入你的昵称", input -> !input.isEmpty());String psd = inputString("请输入你的密码", input -> !input.isEmpty());int age = inputInt("请输入你的年龄", input -> input > 0 && input < 150);inputObj(name, psd, nick, age);}/*** @param name* @param psd* @param nick* @param age* @throws IOException*/public static void inputObj(String name, String psd, String nick, int age) throws IOException {User u = new User(name, psd, nick, age);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(name + ".obj"));oos.writeObject(u);oos.close();}/*** 判断关于字符串的输入是否合法** @param prompt* @param validator* @return*/public static String inputString(String prompt, Predicate<String> validator) {//定义一个 Predicate<String>,检查字符串Scanner scanner = new Scanner(System.in);String input;while (true) {System.out.println(prompt);input = scanner.nextLine();if (validator.test(input)) {return input;} else {System.out.println("输入有误,请重新输入");}}}/*** 判断关于int类型的输入是否合法** @param prompt* @param validator* @return*/public static int inputInt(String prompt, Predicate<Integer> validator) {Scanner scanner = new Scanner(System.in);int input;while (true) {System.out.println(prompt);input = scanner.nextInt();if (validator.test(input)) {return input;} else {System.out.println("输入有误,请重新输入");}}}

从文件中写出对象 

 将当前目录下的所有obj文件获取到,并进行 反序列化后输出每个用户的信息(直接输出反序 列化后的User对象即可)

        File dir = new File(".");if (dir.isDirectory()) {File[] files = dir.listFiles(f -> f.getName().endsWith(".obj"));for (int i = 0; i < files.length; i++) {File sub=files[i];ObjectInputStream ois = new ObjectInputStream(new FileInputStream(sub));Object obj = ois.readObject();User user = (User) obj;System.out.println(user);ois.close();}}

注意事项 

在开发这个简单的小功能时我还是遇到了许多小问题,总结給大家看看,以后避免犯错

1、流的正确使用 (正确的序列化)

 因为一次学习了几个不同的流使用,所以开始我有点混乱,一开始我用PrintWriter来写入文件中导致没有序列化,所以自己打开文件就发现能够正常打开并且里面的内容我都能看懂,其实这是错误的,在使用对象流把对象存入文件中时是要将对象序列化的,因此你正常打开文件其实应该是乱码,必须要反序列化后才能看懂

2、确保 User 类实现了 Serializable 接口

在对象类中必须保证实现Serializable 接口,给这个类打上标签,尽管这个接口中并没有任何的内容,但是这是序列化对象的必要条件。

public class User implements Serializable {

}

3、确保类的稳定性(在写入到读出之间没有改变类的内容)

比如我在最后输出时发现我的user类没有重写toString方法跑去重写后在运行就出现了下面错误

java.io.InvalidClassException

这个异常通常发生在序列化和反序列化对象时,类的 serialVersionUID 不匹配。serialVersionUID 是一个用于版本控制的标识符,它帮助 Java 序列化机制判断两个类的版本是否兼容。 

4、流的关闭(资源管理) 

 记得close你的流

如果不关闭流,可能会导致资源泄露或文件损坏(尤其是在写入模式下)。

就像我没有关闭流,导致写入失败 

 

 

 

 

 

这篇关于深入理解Java序列化:从入门到实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1125760

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory