概述
前言
常见的创建对象有两种方式: new 和 clone
当一个对象创建过程复杂,我们是否可以根据已有的对象直接来克隆一份,而不必关系创建的细节呢(原型模式)。
1、实现Cloneable接口,重写clone方法
Object默认的clone方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;
对于复杂类型的字段,则是指针地址的拷贝,clone后的对象和原对象指向的还是一个地址空间。
所以说默认的clone方法是浅克隆。我们用下面例子验证一下:
package com.dl.JavaBase;
class Car implements Cloneable{
private String brand;//品牌
private int maxSpeed;//最高时速
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", maxSpeed=" + maxSpeed +
'}';
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
}
public class Person implements Cloneable {
private String name;
private Car car;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", car=" + car +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, Car car) {
this.name = name;
this.car = car;
}
public static void main(String[] args) throws CloneNotSupportedException {
Car car = new Car("audi", 150);
Person person=new Person("ding",car);
Person person1= (Person) person.clone();
System.out.println("修改car之前:");
System.out.println(person);
System.out.println(person1);
System.out.println("修改car之后:");
car.setBrand("benchi");
car.setMaxSpeed(200);
System.out.println(person);
System.out.println(person1);
System.out.print("使用Object默认的clone方法:");
System.out.println(person.getCar()==person1.getCar());
}
}
执行结果:
这种克隆方式显然表示原始对象和克隆对象的Car是同一个 引用。也就是说,Car对象没有被克隆。如果修改了Car对象的值,原始对象和克隆对象都将会发生变化。这并不是我们希望看到的。
所以,我们需要连对象里面的对象也要是一个新的对象。每一个属性都被完全拷贝,这才是深克隆。
为了实现深度克隆,我们需要对Person中的clone方法进行改造一下,getCar()测试代码不变。
@Override
protected Object clone() throws CloneNotSupportedException {
Person person= (Person) super.clone();
person.setCar((Car) person.getCar().clone());
return person;
}
再次进行测试:
这么做就要在super.clone的基础上 继续对非基本类型的对象递归地再次clone.
显然这么方式是繁琐的且不可靠的。
2、实现序列化接口
2.1 使用java自身的序列化转为二进制数 ,再反序列化为对象
ObjectStream序列化的工具类
package com.dl.JavaBase;
import java.io.*;
public class SerialiazableUtil {
public SerialiazableUtil() {
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepCloneObject(Object object) throws IOException {
T deepClone = null;
ObjectInputStream ois = null;
try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
)
{
oos.writeObject(object);
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ois = new ObjectInputStream(bais);
deepClone = (T)ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
ois.close();
}
}
return deepClone;
}
}
测试类:
package com.dl.JavaBase;
import java.io.IOException;
import java.io.Serializable;
class Car implements Serializable {
private static final long serialVersionUID = 4982206063131788088L;
private String brand;//品牌
private int maxSpeed;//最高时速
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", maxSpeed=" + maxSpeed +
'}';
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
}
public class Person implements Serializable {
private static final long serialVersionUID = 6957528274628957691L;
private String name;
private Car car;
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", car=" + car +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, Car car) {
this.name = name;
this.car = car;
}
public static void main(String[] args) throws CloneNotSupportedException, IOException {
Person person=new Person("ding",new Car("audi",150));
Person person1= SerialiazableUtil.deepCloneObject(person);
System.out.print("Java默认序列化方式:");
System.out.println(person.getCar()==person1.getCar());
}
}
运行结果:
其他方式还可以是用序列化工具如fastjson进行序列化和反序列化进行对象clone。
2.2 fastjson序列化
Maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
Car类:
package com.dl.JavaBase;
import java.io.Serializable;
public class Car implements Serializable {
private static final long serialVersionUID = 4982206063131788088L;
private String brand;//品牌
private int maxSpeed;//最高时速
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", maxSpeed=" + maxSpeed +
'}';
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
}
Person类
package com.dl.JavaBase;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
public class Person implements Serializable {
private static final long serialVersionUID = 6957528274628957691L;
private String name;
private Car car;
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", car=" + car +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, Car car) {
this.name = name;
this.car = car;
}
public static void main(String[] args) throws CloneNotSupportedException, IOException {
Person person=new Person("ding",new Car("audi",150));
//Person person1 = JSONObject.parseObject(JSONObject.toJSONString(person), Person.class);
Person person1 = JSONObject.parseObject(JSONObject.toJSONBytes(person), Person.class);
System.out.println("fastjson方式:");
System.out.println(person.getCar()==person1.getCar());
}
}
运行结果:
总结:
实现对象克隆主要有两种方式:
1、实现Cloneable接口并重写其中的clone()方法完成对象的浅拷贝
- Object默认的clone方法实际是对域的简单拷贝,对于简单数据类型,是值的拷贝;
- 对于复杂类型的字段,则是指针地址的拷贝,clone后的对象和原对象指向的还是一个地址空间。
- 所以说默认的clone方法是浅克隆。
- 想要实现深克隆需要复杂类实现中为每个类都实现Cloneable接口并重写clone方法(复杂类中的对象也要是新的对象)这么做就要在super.clone的基础上 继续对非基本类型的对象递归的再次clone.
- 显然这么方式是繁琐的且不可靠的。
2、实现序列化接口Serializable,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
- 基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是通过编译器完成的,
- 不是在运行时抛出异常,这汇总方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来中是好过把问题留到运行时。
最后
以上就是淡淡香氛为你收集整理的【Java】实现对象克隆的三种方式(Cloneable接口、Java自身序列化、FastJson序列化)的全部内容,希望文章能够帮你解决【Java】实现对象克隆的三种方式(Cloneable接口、Java自身序列化、FastJson序列化)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复