java浅克隆和深克隆,序列化和反序列化实现深克隆(封装序列化和反序列化操作)

本篇博客内容:

  一、浅克隆(ShallowClone)和深克隆(DeepClone)

  二、序列化和反序列化实现深克隆

  三、封装序列化和反序列化操作

    ObjectOutputStream + 内存流ByteArrayOutputStream

  四、对象持久化到文件或从文件中读取对象

    ObjectOutputStream + FileOutputStream

一、浅克隆(ShallowClone)和深克隆(DeepClone)   <=返回目录

1.1、浅克隆和深克隆区别

package com.oy.shallowclone;

/*
浅克隆(ShallowClone)和深克隆(DeepClone)。 在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,
值类型包括int、double、byte、boolean、char等简单数据类型,
引用类型包括类、接口、数组等复杂类型。
浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。 实现clone方法的步骤
(1)实现Cloneable接口
(2)重写Object类中的clone()方法,重写时需定义为public
(3)在重写方法中,调用super.clone()
*/
public class Student implements Cloneable {
private int number; public int getNumber() {
return number;
} public void setNumber(int number) {
this.number = number;
} @Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
} /*
@Override
public Object clone() {
Student stu = null;
try {
stu = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
*/
}

  

  测试

package com.oy.shallowclone;

public class StudentTest {
public static void main(String args[]) throws CloneNotSupportedException {
Student stu1 = new Student();
stu1.setNumber(12345); // stu1克隆出stu2
Student stu2 = (Student) stu1.clone(); System.out.println("学生1:" + stu1.getNumber()); //
System.out.println("学生2:" + stu2.getNumber()); // stu2.setNumber(54321); // 修改stu2 System.out.println("学生1:" + stu1.getNumber()); //
System.out.println("学生2:" + stu2.getNumber()); //
}
}

1.2、浅克隆

  Cat类

package com.oy.shallowclone;

public class Cat implements Cloneable {
private String name;
// getter和setter省略 @Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

  Person类

package com.oy.shallowclone;

public class Person implements Cloneable {
private String name;
private Integer age;
private Cat cat;
// getter和setter省略 @Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

  

  测试类

package com.oy.shallowclone;

public class PersonTest {
public static void main(String[] args) throws CloneNotSupportedException {
Cat cat = new Cat();
cat.setName("狸花"); Person per1 = new Person();
per1.setName("张三");
per1.setAge(10);
per1.setCat(cat); // per1克隆出per2
Person per2 = (Person) per1.clone(); System.out.println(per1.getName() + "-" + per1.getAge() + "-" + per1.getCat().getName()); // 张三-10-狸花
System.out.println(per2.getName() + "-" + per2.getAge() + "-" + per2.getCat().getName()); // 张三-10-狸花
System.out.println(per1.getName() == per2.getName()); // true
System.out.println(per1.getAge() == per2.getAge()); // true
System.out.println(per1.getCat() == per1.getCat()); // true // 修改per1
per1.setName("李四"); // per1的name属性保存另一个"字符串"的地址
per1.setAge(20);
cat.setName("小橘"); System.out.println(per1.getName() + "-" + per1.getAge() + "-" + per1.getCat().getName()); // 李四-20-小橘
System.out.println(per2.getName() + "-" + per2.getAge() + "-" + per2.getCat().getName()); // 张三-10-小橘
System.out.println(per1.getName() == per2.getName()); // false
System.out.println(per1.getAge() == per2.getAge()); // false
System.out.println(per1.getCat() == per1.getCat()); // true
}
}

  理论上String和Integer类型的属性的克隆也是浅克隆。但是,String和Integer是不可变的。所以,实际上当String和Integer类型的属性改变时,克隆对象不会跟着改变。

package com.oy.shallowclone;

/**
* 理论上String和Integer类型的属性的克隆也是浅克隆。
* 但是,String和Integer是不可变的。所以,实际上当String和Integer类型的属性改变时,克隆对象不会跟着改变。
*
* @author oy
*/
public class PersonTest2 {
public static void main(String[] args) throws CloneNotSupportedException {
Cat cat = new Cat();
cat.setName("狸花"); Person per1 = new Person();
String name = new String("张三");
per1.setName(name);
Integer age = new Integer("10");
per1.setAge(age);
per1.setCat(cat); // per1克隆出per2
Person per2 = (Person) per1.clone(); System.out.println(per1.getName() + "-" + per1.getAge() + "-" + per1.getCat().getName()); // 张三-10-狸花
System.out.println(per2.getName() + "-" + per2.getAge() + "-" + per2.getCat().getName()); // 张三-10-狸花
System.out.println(per1.getName() == per2.getName()); // true 说明是浅克隆
System.out.println(per1.getAge() == per2.getAge()); // true 说明是浅克隆
System.out.println(per1.getCat() == per1.getCat()); // true 说明是浅克隆 // 修改per1
name = new String("李四"); // 只是修改了name变量里面保存的地址值
age = new Integer("20"); // 只是修改了age变量里面保存的地址值
cat.setName("小橘"); System.out.println(per1.getName() + "-" + per1.getAge() + "-" + per1.getCat().getName()); // 张三-10-小橘
System.out.println(per2.getName() + "-" + per2.getAge() + "-" + per2.getCat().getName()); // 张三-10-小橘
System.out.println(per1.getName() == per2.getName()); // true
System.out.println(per1.getAge() == per2.getAge()); // true
System.out.println(per1.getCat() == per1.getCat()); // true
}
}

1.3、深克隆

  Teacher类

package com.oy.deepclone;

class Teacher implements Cloneable {
private int age;
private String name; // getter和setter省略 @Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

  

  Student类

package com.oy.deepclone;

public class Student implements Cloneable {
private int age;
private String name;
private Teacher teacher; // getter和setter省略 @Override
public Object clone() throws CloneNotSupportedException {
// 这一步返回的这个student还只是一个浅克隆,
Student student = (Student) super.clone();
// 然后克隆的过程中获得这个克隆的student,然后调用这个getTeacher这个方方法得到这个Teacher对象。
// 然后实现克隆。在设置到这个student中的Teacher。
// 这样实现了双层克隆使得那个teacher对象也得到了复制。
student.setTeacher((Teacher) student.getTeacher().clone());
// 双层克隆使得那个teacher对象也得到了复制
return student;
}
}

  测试类

package com.oy.deepclone;

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher = new Teacher();
teacher.setAge(40);
teacher.setName("teacher zhang"); Student stu1 = new Student();
stu1.setAge(10);
stu1.setName("张三");
stu1.setTeacher(teacher); //stu1深克隆出stu2
Student stu2 = (Student) stu1.clone(); // 这里是深复制,所以这时候Student中的teacher就是teacher这个对象的一个复制,就和student2是Student的一个复制
// 所以下面teacher.setName只是对他原来的这个对象更改,但是复制的那个并没有更改
System.out.println(stu1.getName() + "-" + stu1.getAge() + "-" + stu1.getTeacher().getName());//张三-10-teacher zhang
System.out.println(stu2.getName() + "-" + stu2.getAge() + "-" + stu2.getTeacher().getName());//张三-10-teacher zhang // 修改stu1
stu1.setName("李四");
stu1.setAge(20);
teacher.setName("teacher wang"); System.out.println(stu1.getName() + "-" + stu1.getAge() + "-" + stu1.getTeacher().getName());//李四-20-teacher wang
System.out.println(stu2.getName() + "-" + stu2.getAge() + "-" + stu2.getTeacher().getName());//张三-10-teacher zhang
}
}

二、序列化和反序列化实现深克隆   <=返回目录

  Car类

public class Car implements Serializable {
private static final long serialVersionUID = -7633040136520448512L;
private String name;
private String color; public Car() {} public Car(String name, String color) {
this.name = name;
this.color = color;
} // getter和setter方法省略 @Override
public String toString() {
return "Car [name=" + name + ", color=" + color + "]";
}
}

  Person类

public class Person implements Serializable {
private static final long serialVersionUID = 4792126594710124401L;
private String name;
private int age;
private Car car; public Person() {} public Person(String name, int age) {
this.name = name;
this.age = age;
} public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
} // getter和setter方法省略 @Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}

  工具类:使用序列化和反序列化实现对象的克隆

package com.oy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; /**
* 工具类:使用序列化和反序列化实现对象的克隆
*
* @author oy
* @version 1.0
* @date 2018年8月9日
* @time 下午8:59:23
*/
public class cloneUtil {
private cloneUtil() {
} @SuppressWarnings("unchecked")
public static <T extends Serializable> T cloneObject(T obj) {
T cloneObj = null; // 序列化
ByteArrayOutputStream bout = null;
ObjectOutputStream oos = null;
try {
bout = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
} finally {
close(oos);
close(bout);
} // 反序列化
ByteArrayInputStream bin = null;
ObjectInputStream ois = null;
try {
bin = new ByteArrayInputStream(bout.toByteArray());
ois = new ObjectInputStream(bin);
cloneObj = (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
close(ois);
close(bin);
} return cloneObj;
} private static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

  测试

/**
* 序列化和反序列化实现深克隆
*
* @author oy
* @version 1.0
* @date 2018年8月9日
* @time 下午8:58:53
*/
public class Test {
public static void main(String[] args) {
Car car = new Car("BYD", "black");
Person p1 = new Person("张三", 20, car);
Person p2 = cloneUtil.cloneObject(p1); System.out.println("P1:" + p1);//P1:Person [name=张三, age=20, car=Car [name=BYD, color=black]]
System.out.println("p2:" + p2);//p2:Person [name=张三, age=20, car=Car [name=BYD, color=black]] if (p2 != null) {
car.setName("宝马");
car.setColor("red");
} System.out.println("P1:" + p1);//P1:Person [name=张三, age=20, car=Car [name=宝马, color=red]]
System.out.println("p2:" + p2);//p2:Person [name=张三, age=20, car=Car [name=BYD, color=black]]
}
}

三、封装序列化和反序列化操作   <=返回目录

3.1、封装序列化和反序列化操作

package com.oy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List; /**
* 序列化,反序列化工具类。用于转换byte[]和对象
*
* @author oy
* @date 2019年6月3日 下午11:46:53
* @version 1.0.0
*/
public class SerializeUtil { public static byte[] serialize(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
return bytes;
} catch (Exception e) {
e.printStackTrace();
} finally {
close(oos);
close(baos);
}
return null;
} public static Object unserialize(byte[] bytes) {
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
close(bais);
close(ois);
}
return null;
} /**
* 序列化 list 集合
*
* @param list
* @return
*/
public static byte[] serializeList(List<?> list) { if (list == null || list.size() <= 0) {
return null;
}
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
byte[] bytes = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
for (Object obj : list) {
oos.writeObject(obj);
}
bytes = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
close(oos);
close(baos);
}
return bytes;
} /**
* 反序列化 list 集合
*
* @param lb
* @return
*/
public static List<?> unserializeList(byte[] bytes) {
if (bytes == null) {
return null;
} List<Object> list = new ArrayList<Object>();
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bais);
while (bais.available() > 0) {
Object obj = (Object) ois.readObject();
if (obj == null) {
break;
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(bais);
close(ois);
}
return list;
} /**
* 关闭io流对象
*
* @param closeable
*/
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

  

3.2、cloneUtil类改为:

public class cloneUtil {
private cloneUtil() {} @SuppressWarnings("unchecked")
public static <T extends Serializable> T cloneObject(T obj) {
// 序列化
byte[] bs = SerializeUtil.serialize(obj); // 反序列化
return (T) SerializeUtil.unserialize(bs);
}
}

四、对象持久化到文件或从文件中读取对象   <=返回目录

package com.oy;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; /**
* 序列化和反序列化
*
* @author oy
* @version 1.0
* @date 2018年8月9日
* @time 下午8:58:32
*/
public class serialDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建Person对象
Car car = new Car("BMW", "black");
Person p = new Person("张三abc", 20, car); write(p);
Object obj = read(); // Person [name=张三abc, age=20, car=Car [name=BMW, color=black]]
System.out.println(obj); } private static Object read() throws FileNotFoundException, IOException, ClassNotFoundException {
// 创建反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\object.txt"));
// 反序列化
Object obj = ois.readObject();
// 释放资源
ois.close();
return obj;
} private static void write(Object obj) throws FileNotFoundException, IOException {
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.txt"));
// 序列化
oos.writeObject(obj);
// 释放资源
oos.close();
}
}

参考:

  (1)SerializeUtil 序列化,反序列化工具类

  (2)Java基础学习总结——Java对象的序列化和反序列化

上一篇:redis在Windows下以后台服务一键搭建哨兵(主从复制)模式(单机)


下一篇:【spark 深入学习 05】RDD编程之旅基础篇-01