此文转载于知乎的一篇文章,看着写的非常全面,分享给大家。

先解释下什么是序列化

我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术。

Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制数据保存到磁盘或者传输网络,磁盘或者网络接收者可以在对象的属类的模板上来反序列化类的对象,达到对象持久化的目的。

更多序列化请参考:《关于Java序列化你应该知道的一切》这篇文章。

什么是 transient?

简单来说就是,被 transient 修饰的变量不能被序列化。

具体来看下面的示例1

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; /**
* @author 微信公众号:Java技术栈
*/
public class TransientTest { public static void main(String[] args) throws Exception { User user = new User();
user.setUsername("Java技术栈");
user.setId("javastack"); System.out.println("\n序列化之前");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId()); ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
os.writeObject(user);
os.flush();
os.close(); ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
user = (User) is.readObject();
is.close(); System.out.println("\n序列化之后");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId()); }
} /**
* @author 微信公众号:Java技术栈
*/
class User implements Serializable { private static final long serialVersionUID = 1L; private String username;
private transient String id; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} }

输出结果:

序列化之前
username: Java技术栈
id: javastack 序列化之后
username: Java技术栈
id: null

示例1在 id 字段上加了 transient 关键字修饰,反序列化出来之后值为 null,说明了被 transient 修饰的变量不能被序列化。

静态变量能被序列化吗?

这个话题也是最近栈长的Java技术栈vip群里面讨论的,大家对这个知识点比较模糊,我就写了这篇文章测试总结一下。

如果你也想加入我们的Java技术栈vip群和各位大牛一起讨论技术,那点击这个链接了解加入吧。

那么,到底静态变量能被序列化吗?废话少说,先动手测试下吧!

示例2:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; /**
* @author 微信公众号:Java技术栈
*/
public class TransientStaticTest { public static void main(String[] args) throws Exception { User2 user = new User2();
User2.username = "Java技术栈1";
user.setId("javastack"); System.out.println("\n序列化之前");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId()); ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
os.writeObject(user);
os.flush();
os.close(); // 在反序列化出来之前,改变静态变量的值
User2.username = "Java技术栈2"; ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
user = (User2) is.readObject();
is.close(); System.out.println("\n序列化之后");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId()); }
} /**
* @author 微信公众号:Java技术栈
*/
class User2 implements Serializable { private static final long serialVersionUID = 1L; public static String username;
private transient String id; public String getUsername() {
return username;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} }

输出结果:

序列化之前
username: Java技术栈1
id: javastack 序列化之后
username: Java技术栈2
id: null

示例2把 username 改为了 public static, 并在反序列化出来之前改变了静态变量的值,结果可以看出序列化之后的值并非序列化进去时的值。

由以上结果分析可知,静态变量不能被序列化,示例2读取出来的是 username 在 JVM 内存中存储的值。

transient 真不能被序列化吗?

继续来看示例3:

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream; /**
* @author 微信公众号:Java技术栈
*/
public class ExternalizableTest { public static void main(String[] args) throws Exception { User3 user = new User3();
user.setUsername("Java技术栈");
user.setId("javastack");
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File("javastack")));
objectOutput.writeObject(user); ObjectInput objectInput = new ObjectInputStream(new FileInputStream(new File("javastack")));
user = (User3) objectInput.readObject(); System.out.println(user.getUsername());
System.out.println(user.getId()); objectOutput.close();
objectInput.close();
} } /**
* @author 微信公众号:Java技术栈
*/
class User3 implements Externalizable { private static final long serialVersionUID = 1L; public User3() { } private String username;
private transient String id; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} @Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeObject(id);
} @Override
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
id = (String) objectInput.readObject();
} }

输出结果:

null
javastack

示例3的 id 被 transient 修改了,为什么还能序列化出来?那是因为 User3 实现了接口 Externalizable,而不是 Serializable。

在 Java 中有两种实现序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。

这两种序列化方式的区别是:实现了 Serializable 接口是自动序列化的,实现 Externalizable 则需要手动序列化,通过 writeExternal 和 readExternal 方法手动进行,这也是为什么上面的 username 为 null 的原因了。

transient 关键字总结

1)transient修饰的变量不能被序列化;

2)transient只作用于实现 Serializable 接口;

3)transient只能用来修饰普通成员变量字段;

4)不管有没有 transient 修饰,静态变量都不能被序列化;

笔者的微信公众号,每天一篇好文章:

关注公众号,回复synchronized获取《深入探讨synchronized实现原理》

Java基础-Java中transient有什么用-序列化有那几种方式的更多相关文章

  1. Java基础知识强化之IO流笔记30:字节流4种方式复制mp4并测试效率

    1. 需求:把e:\\哥有老婆.mp4 复制到当前项目目录下的copy.mp4中 字节流四种方式复制文件: • 基本字节流一次读写一个字节 • 基本字节流一次读写一个字节数组 • 高效字节流一次读写一 ...

  2. java基础---->java中正则表达式二

    跟正则表达式相关的类有:Pattern.Matcher和String.今天我们就开始Java中正则表达式的学习. Pattern和Matcher的理解 一.正则表达式的使用方法 一般推荐使用的方式如下 ...

  3. Java基础学习中一些词语和语句的使用

    在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...

  4. Java基础-Java中的堆内存和离堆内存机制

    Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

  5. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.

  6. Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock)

    Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在学习Java的之前,你可能已经听说过读 ...

  7. Java基础-Java中的并法库之线程池技术

    Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.

  8. Java基础-Java中23种设计模式之常用的设计模式

    Java基础-Java中23种设计模式之常用的设计模式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.设计模式分类 设计模式是针对特定场景给出的专家级的解决方案.总的来说设 ...

  9. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

随机推荐

  1. 2.Redis安装和简单使用

    (1)安装Redis Redis目前只支持Linux系统,因为开发此软件的创始者认为,Redis是为后台数据服务的,所以认为该软件使用在纯净的服务环境下,而不是应用型操作系统下,而Linux作为服务器 ...

  2. [PHP插件教程]003.PhpRedis

    PhpRedis 介绍 Mac安装步骤 安装Redis 安装PhpRedis 示例代码 介绍 Redis是一个高性能的key-value数据库. Redis提供了Java,C/C++,C#,PHP,J ...

  3. 初窥 BB-Framework

     

  4. JS代码静态分析及挖掘

    JavaScript 已经成为现代 Web 浏览器开发中最普遍的技术之一.使用客户端 JavaScript 框架(如 AngularJS,ReactJS 和 Vue.js)构建的应用程序已向前端输送了 ...

  5. 16 . PythonWeb框架之Django

    Web框架简介 Web开发是Python语言应用领域的重要部分,也是目前最主要的Web开发语言之一,在其二十多年的历史中出现了数十种Web框架,比如Django.Tornado.Flask.Twist ...

  6. Rocket - diplomacy - AddressDecoder

    https://mp.weixin.qq.com/s/UHGq74sEd9mcG5Q3f-g3mA   介绍AddressDecoder的实现.   ​​ 1. 基本定义   ​​ 每个Port包含多 ...

  7. Java实现 LeetCode 449 序列化和反序列化二叉搜索树

    449. 序列化和反序列化二叉搜索树 序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建. 设计一个算法 ...

  8. Java实现 蓝桥杯VIP 算法训练 FBI树

    问题描述 我们可以把由"0"和"1"组成的字符串分为三类:全"0"串称为B串,全"1"串称为I串,既含"0&q ...

  9. Java实现 LeetCode 39 组合总和

    39. 组合总和 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字 ...

  10. Java实现 LeetCode 22 括号生成

    22. 括号生成 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", &quo ...