java基础-序列化和拷贝

1.序列化

1.1.定义

如果我们需要持久化Java对象,或者在⽹络传输Java对象,这些场景都需要⽤到序列化,简单来说序列化就是将数据结构或对象转换成⼆进制字节流的过程,反序列化就是将在序列化过程中所⽣成的⼆进制字节流转换成数据结构或者对象的过程

对于Java这种⾯向对象编程语⾔来说,我们序列化的都是对象,保存的是对象的状态,即它的成员变量,因此不会关注类中的变量

Serializable实现序列化

在Java中,只要一个类实现了java.io.Serializable接口,那么它就可以被序列化,在类中增加writeObject和readObject方法可以实现自定义序列化策略

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化ID是否一致(就是private static final long serialVersionUID)

transient

如果有些字段不想进⾏序列化怎么办?对于不想进⾏序列化的变量,使⽤transient关键字修饰

transient关键字的作⽤是阻⽌实例中那些⽤此关键字修饰的的变量序列化,当对象被反序列化时,被transient修饰的变量值会被设置为初始值,如int为0,对象为null,transient只能修饰变量,不能修饰类和⽅法

序列化评价指标

性能及效率:指将一个对象序列化所花费的时间

空间开销:指的是序列化对象后所占用的空间

安全性:如果安全性存在问题,则容易被攻击入侵

通用性和兼容性:是否支持跨语言、跨平台

1.2.序列化比较

1.2.1.jdk序列化

不支持跨语言,性能差,序列化后的对象较大

1.2.2.jackson序列化

1.2.3.protobuf序列化

protobuf的一个缺点是要编写.proto格式的配置文件,由于java具有反射和动态代码生成的能力,这个预编译过程不是必须的,可以在代码执行时来实现,protostuff已经实现了这个功能

具体细节可以参考:https://zhuanlan.zhihu.com/p/537871378

1.2.4.kryo序列化

类注册机制:可以将类全路径名替换为数字,但数字的分配与注册顺序相关,所以序列化和反序列化的时候要确保注册顺序一致,对于对象类型的序列化,需要将对象类型和对象内部field的类型都注册

节省空间:对于值来说,int和long用可变长度编码,string取消了长度字段,用特殊符号记录string结束;类型利用类注册机制用数字替代全限定名

序列化过程:先序列化类型,再序列化该类型的值,如果自定义类型,例如(cn.uce.demo.Student),也是先类型再值的模式,只不过值是一个字段一个字段的递归进行

循环依赖:引入了对象图的概念来消除循环依赖的序列化,已序列化的对象,在循环引用时只是用一个int类型来表示该对象值,否则的话,循环引用可能会导致栈溢出

非线程安全:kryo实例非线程安全是因为完成一次写入后,需要重置Kryo中的临时数据结构(包括注册类型,序列化上下文,序列化过程中使用的缓存),可以用ThreadLocal来解决线程安全

2.拷贝

2.1.定义

一共有三种方式,第一种方式是引用拷贝,第二种方式是浅拷贝,第三种是深拷贝

引用拷贝

在Java中,A a1 = a2,我们需要理解的是这实际上复制的是引用,也就是说a1和a2指向的是同一个对象。因此当a1变化的时候,a2里面的成员变量也会跟着变化

浅复制

浅拷贝会在堆上创建一个新的对象,不过如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象

 

class Resume implements Cloneable{
    public Object clone() {
    	try {
    		return (Resume)super.clone();
        } catch (Exception e) {
    		e.printStackTrace();
            return null;
    	}
    }
}

深复制

深拷贝会完全复制整个对象,包括这个对象所包含的内部对象

class Student implements Cloneable {
	String name;
	int age;
    Professor p;
	Student(String name, int age, Professor p) {
        this.name = name;
		this.age = age;
        this.p = p;
    }
	public Object clone() {
        Student o = null;
        try {
			o = (Student) super.clone();
		} catch (CloneNotSupportedException e) {
            System.out.printIn(e.toString());
        }
		o.p = (Professor) p.clone();
		return o;
    }
}

也可以使用序列化,在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象

 

 

posted @ 2023-04-09 12:20  下雨天打盹  阅读(105)  评论(0)    收藏  举报