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

先解释下什么是序列化

我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要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. json数据写入hbase

    package main.scala.com.web.zhangyong168.cn.spark.java; import org.apache.hadoop.hbase.HBaseConfigura ...

  2. 7.SortSet排序集合类型操作

    Sort Set排序集合类型 (1)介绍 和set一样sorted set也是string类型元素的集合,不同的是每个元素都会关联一个权.通过权值可以有序的获取集合中的元素 该Sort Set类型适合 ...

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

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

  4. 4.Linux的目录结构

    Linux的目录结构 (1)"/"目录 Linux文件系统的入口,也是出于最高一级的目录 (2)"/bin" 基础系统所需要的那些命令位于此目录.也是最小系统所 ...

  5. 一、【python】机器学习基础

    专有名词 机器学习 (machine learning) 预测分析 (predictive analytics) 统计学习 (statistical learning) 监督学习 (supervise ...

  6. jchdl - GSL实例 - MulC2(有符号数的乘法)

      这里的实现,先把符号位取出来,使用两个正数相乘,然后在把符号加到乘积上.   参考链接 https://github.com/wjcdx/jchdl/blob/master/src/org/jch ...

  7. (Java实现) 美元汇率

    时空限制 1000ms/128MB 题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后 ...

  8. Java实现 LeetCode 491递增子序列

    491. 递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, ...

  9. Java实现 LeetCode 423 从英文中重建数字

    423. 从英文中重建数字 给定一个非空字符串,其中包含字母顺序打乱的英文单词表示的数字0-9.按升序输出原始的数字. 注意: 输入只包含小写英文字母. 输入保证合法并可以转换为原始的数字,这意味着像 ...

  10. Java实现 蓝桥杯VIP 算法训练 P1102

    定义一个学生结构体类型student,包括4个字段,姓名.性别.年龄和成绩.然后在主函数中定义一个结构体数组(长度不超过1000), 并输入每个元素的值,程序使用冒泡排序法将学生按照成绩从小到大的顺序 ...