Seralizable
class CSer {
private String name;
private int age;
public CSer() {
}
public CSer(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "CSer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[] args) {
// 初始化
CSer player = new CSer();
player.setName("niko");
player.setAge(18);
System.out.println(player);
// 把对象写到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("major")));) {
oos.writeObject(player);
} catch (IOException e) {
e.printStackTrace();
}
// 从文件中读出对象
try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(new File("major").toPath()));) {
CSer player1 = (CSer) ois.readObject();
System.out.println(player1);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
由于 CSer
没有实现 Serializbale
接口,所以在运行测试类的时候会抛出异常,堆栈信息如下:
java.io.NotSerializableException: org.example.CSer
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.example.Main.main(Main.java:46)
ObjectOutputStream
的 writeObject0()
方法。其部分源码如下:
// remaining cases
// 判断对象类型,调用对应方法进行序列化
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
// 如果对象不能被序列化,则抛出 NotSerializableException 异常
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
ObjectOutputStream
在序列化的时候,会判断被序列化的对象是哪一种类型,字符串?数组?枚举?还是 Serializable
,如果全都不是的话,抛出 NotSerializableException
。
Seralizable
CSer
实现了 Serializable
接口,就可以序列化和反序列化了
class CSer implements Serializable {
以 ObjectOutputStream
为例,它在序列化的时候会依次调用 writeObject()
→writeObject0()
→writeOrdinaryObject()
→writeSerialData()
→invokeWriteObject()
→defaultWriteFields()
。
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {
// 获取对象的类,并检查是否可以进行默认的序列化
Class<?> cl = desc.forClass();
desc.checkDefaultSerialize();
// 获取对象的基本类型字段的数量,以及这些字段的值
int primDataSize = desc.getPrimDataSize();
desc.getPrimFieldValues(obj, primVals);
// 将基本类型字段的值写入输出流
bout.write(primVals, 0, primDataSize, false);
// 获取对象的非基本类型字段的值
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
// 循环写入对象的非基本类型字段的值
for (int i = 0; i < objVals.length; i++) {
// 调用 writeObject0 方法将对象的非基本类型字段序列化写入输出流
try {
writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());
}
// 如果在写入过程中出现异常,则将异常包装成 IOException 抛出
catch (IOException ex) {
if (abortIOException == null) {
abortIOException = ex;
}
}
}
}
以 ObjectInputStream
为例,它在反序列化的时候会依次调用 readObject()
→readObject0()
→readOrdinaryObject()
→readSerialData()
→defaultReadFields()
。
private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException {
// 获取对象的类,并检查对象是否属于该类
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
// 获取对象的基本类型字段的数量和值
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
// 从输入流中读取基本类型字段的值,并存储在 primVals 数组中
bin.readFully(primVals, 0, primDataSize, false);
if (obj != null) {
// 将 primVals 数组中的基本类型字段的值设置到对象的相应字段中
desc.setPrimFieldValues(obj, primVals);
}
// 获取对象的非基本类型字段的数量和值
int objHandle = passHandle;
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
// 循环读取对象的非基本类型字段的值
for (int i = 0; i < objVals.length; i++) {
// 调用 readObject0 方法读取对象的非基本类型字段的值
ObjectStreamField f = fields[numPrimFields + i];
objVals[i] = readObject0(Object.class, f.isUnshared());
// 如果该字段是一个引用字段,则将其标记为依赖该对象
if (f.getField() != null) {
handles.markDependency(objHandle, passHandle);
}
}
if (obj != null) {
// 将 objVals 数组中的非基本类型字段的值设置到对象的相应字段中
desc.setObjFieldValues(obj, objVals);
}
passHandle = objHandle;
}
Serializable
接口之所以定义为空,是因为它只起到了一个标识的作用,告诉程序实现了它的对象是可以被序列化的,但真正序列化和反序列化的操作并不需要它来完成。
static
和 transient
修饰的字段是不会被序列化。
ObjectStreamClass
部分源码:
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
// 获取该类中声明的所有字段
Field[] clFields = cl.getDeclaredFields();
ArrayList<ObjectStreamField> list = new ArrayList<>();
int mask = Modifier.STATIC | Modifier.TRANSIENT;
// 遍历所有字段,将非 static 和 transient 的字段添加到 list 中
for (int i = 0; i < clFields.length; i++) {
Field field = clFields[i];
int mods = field.getModifiers();
if ((mods & mask) == 0) {
// 根据字段名、字段类型和字段是否可序列化创建一个 ObjectStreamField 对象
ObjectStreamField osf = new ObjectStreamField(field.getName(), field.getType(), !Serializable.class.isAssignableFrom(cl));
list.add(osf);
}
}
int size = list.size();
// 如果 list 为空,则返回一个空的 ObjectStreamField 数组,否则将 list 转换为 ObjectStreamField 数组并返回
return (size == 0) ? NO_FIELDS :
list.toArray(new ObjectStreamField[size]);
}
Externalizable
除了 Serializable
之外,Java 还提供了一个序列化接口 Externalizable
。
实现 Externalizable
接口的 CSer
类和实现 Serializable
接口的 CSer
类有一些不同:
1)新增了一个无参的构造方法。
使用 Externalizable
进行反序列化的时候,会调用被序列化类的无参构造方法去创建一个新的对象,然后再将被保存对象的字段值复制过去。否则的话,会抛出异常 InvalidClassException
2)新增了两个方法 writeExternal()
和 readExternal()
,实现 Externalizable
接口所必须的。如果不重写具体的 writeExternal()
和 readExternal()
方法,反序列化后得到的对象字段都变成了默认值。
重写两个方法:
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
Externalizable 和 Serializable 都是用于实现 Java 对象的序列化和反序列化的接口,但是它们有以下区别:
①、Serializable 是 Java 标准库提供的接口,而 Externalizable 是 Serializable 的子接口;
②、Serializable 接口不需要实现任何方法,只需要将需要序列化的类标记为 Serializable 即可,而 Externalizable 接口需要实现 writeExternal 和 readExternal 两个方法;
③、Externalizable 接口提供了更高的序列化控制能力,可以在序列化和反序列化过程中对对象进行自定义的处理,如对一些敏感信息进行加密和解密。
transient 关键字用于修饰类的成员变量,在序列化对象时,被修饰的成员变量不会被序列化和保存到文件中。其作用是告诉 JVM 在序列化对象时不需要将该变量的值持久化,这样可以避免一些安全或者性能问题。但是,transient 修饰的成员变量在反序列化时会被初始化为其默认值(如 int 类型会被初始化为 0,引用类型会被初始化为 null),因此需要在程序中进行适当的处理。
transient 关键字和 static 关键字都可以用来修饰类的成员变量。其中,transient 关键字表示该成员变量不参与序列化和反序列化,而 static 关键字表示该成员变量是属于类的,不属于对象的,因此不需要序列化和反序列化。
在 Serializable 和 Externalizable 接口中,transient 关键字的表现也不同,在 Serializable 中表示该成员变量不参与序列化和反序列化,在 Externalizable 中不起作用,因为 Externalizable 接口需要实现 readExternal 和 writeExternal 方法,需要手动完成序列化和反序列化的过程。
serialVersionUID
serialVersionUID
被称为序列化 ID,它是决定 Java 对象能否反序列化成功的重要因子。在反序列化时,Java 虚拟机会把字节流中的 serialVersionUID
与被序列化类中的 serialVersionUID
进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常。
1)添加一个默认版本的序列化 ID:
private static final long serialVersionUID = 1L。
2)添加一个随机生成的不重复的序列化 ID。
private static final long serialVersionUID = -2095916884810199532L;
3)添加 @SuppressWarnings
注解。该注解会为被序列化类自动生成一个随机的序列化 ID。
@SuppressWarnings("serial")
Seralizable的更多相关文章
- java笔记--关于克隆技术
关于克隆 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3884817.html"谢谢-- 1.假克隆 如: ObjectA ...
- .Net的基础概念
1,参数传递. 默认都是按值传递(无论引用还是值类型),也就意味着传递参数的一个副本给方法.之后在方法体内对参数的更改,对原始参数没有影响. 使用ref/out可以按引用传递,直接影响原始参数变量.两 ...
- JAVA序列化与反序列化三种格式存取(默认格式、XML格式、JSON格式)
什么是序列化 java中的序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输.或者持久化存储到数据库或文件系统中:然后在需要的时候 ...
- Android开发艺术探索——第二章:IPC机制(上)
Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...
- spark之JDBC开发(实战)
一.概述 Spark Core.Spark-SQL与Spark-Streaming都是相同的,编写好之后打成jar包使用spark-submit命令提交到集群运行应用$SPARK_HOME/bin#. ...
- Hibernate 再接触 悲观锁和乐观锁
为什么取1248 二进制 CRUD 移位效率高 在并发和效率选择一个平衡点 一般不会考虑幻读 因为我们不会再一个事务里查询两次,(只能设置为seralizable) 悲观锁和乐观锁的前提是read-u ...
- 两种序列化的方法 Serializable Parcelable
原文:https://blog.csdn.net/hacker_crazy/article/details/80840868 一.Serializable 1.Serializable 是java的序 ...
- mysql事务隔离级别测试
隔离性mysql提供了4种不同的隔离级别以支持多版本并发控制(MVCC)较低级别的隔离通常可以执行更高的并发,系统的开销也更低read uncommited(未提交读)read commited(提交 ...
- Android中传递对象的三种方法
Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! Android中,Activity和Fragment之间传递对象,可以通过将对象序列化并存入Bundle或者I ...
- SSH框架之Hibernate第一篇
1.2Hibernate的概述: 1.2.1 什么Hibernate? Hibernate(开发源代码的对象关系映射框架)是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它 ...
随机推荐
- python中多进程下通信使用管道Pipe与队列 Queue 的区别: Multiprocessing - Pipe vs Queue
参考: https://stackoverflow.com/questions/8463008/multiprocessing-pipe-vs-queue ====================== ...
- Windows使用命令行终止任务
在Windows操作系统中,可以使用命令提示符(cmd)或Windows PowerShell来查看运行的任务并终止指定的任务.以下是一些常用的命令: 使用命令提示符(cmd) 查看运行的任务: 打开 ...
- quartz监控日志(三)查看卡死线程堆栈
转
我们经常碰到一些定时任务卡死或者执行时间很长,这样的问题我们排查手段比较常用的是jstack命令 来查看线程堆栈,然后根据我们监控中的threadId或者threadName来查找线程详细堆栈看卡在哪 ...
- spring同时集成mybatis和ibatis
最近来了一个新项目,说是新的项目,但是需要用到以前旧的模块代码,旧的模块使用架构为ssi 而新项目使用spring mvc +mybatis,考虑到工作量的问题,所以决定使用spring mvc +m ...
- 「TCP/UDP」一个端口号可以同时被两个进程绑定吗?
一.1个端口号可以同时被两个进程绑定吗? 根据端口号的绑定我们分以下几种情况来讨论: 2个进程分别建立TCP server,使用同一个端口号8888 2个进程分别建立UDP server,使用同一个端 ...
- mmdetection使用未定义backbone训练
首先找到你需要用到的 backbone,一般有名的backbone 都会在github有相应的代码开源和预训练权重提供 本文以mobilenetv3 + fastercnn 作为举例,在mmdetec ...
- 卷积神经网络CNN实战:MINST手写数字识别——数据集下载与网络训练
数据集下载 这一部分比较简单,就不过多赘述了,把代码粘贴到自己的项目文件里,运行一下就可以下载了. from torchvision import datasets, transforms # 定义数 ...
- 玩客云安装hassio
docker版 安装hass镜像,首次安装比较慢 需要等几分钟启动(10分钟左右) docker run -d --restart=always --name="home-assistant ...
- java_可变参数&增强for循环
代码比较无厘头,记录看懂的意思 在可变参数的构造方法中,需要使用增强for循环遍历 public class name { String sex; public static void main(St ...
- 总结:redis 突然变慢
用户量暴增,无法下单,凌晨的夜,静悄悄... 经过查找发现Redis. 获取不到连接资源,并且集群中的单台 Redis 连接量很高. 大量的流量没了 Redis 的缓存响应,直接打到了 MySQL,最 ...