问题:clone 一个 Car 对象 ,clone 多次。
1.传统模式:
直接通过 new 的方式创建多个 Car 对象,创建的时候把原来 Car 对象的属性赋给新的对象。
//实体类
public class Car {
private String name;
private String color;
private int size;
public Car() {
}
public Car(String name, String color, int size) {
this.name = name;
this.color = color;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", size=" + size +
'}';
}
}
//测试类
public class TradTest {
public static void main(String[] args) {
Car car = new Car("大众","黑色",18);
//1.传统模式复制对象
Car car1 = new Car(car.getName(),car.getColor(),car.getSize());
Car car2 = new Car(car.getName(),car.getColor(),car.getSize());
Car car3 = new Car(car.getName(),car.getColor(),car.getSize());
System.out.println(car1);
System.out.println(car2);
System.out.println(car3);
}
}
传统模式的优缺点:
- 优点是很好理解,代码简单
- 创建新对象时,总是抓取原对象的属性,如果对象复杂,效率会很低
- 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
2.原型模式
原型模式介绍:
- 原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
2.1 使用原型模式改进传统方式,让程序具有更高的效率和扩展性。
对象实体类需要实现 Cloneable 接口,并且重写 clone() 方法。
//实体类 Car
public class Car implements Cloneable{
private String name;
private String color;
private int size;
public Country country;
public Car() {
}
public Car(String name, String color, int size) {
this.name = name;
this.color = color;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", size=" + size +
", country=" + country +
'}';
}
@Override
public Object clone() {
Car car = null;
try{
car = (Car) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return car;
}
}
//实体类 Country ,Car 类的属性包含该对象
public class Country implements Cloneable{
private String name;
public Country(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Country{" +
"name='" + name + '\'' +
'}';
}
@Override
public Object clone() {
Country country = null;
try {
country = (Country) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return country;
}
}
//测试类
public class SimpleCopy {
public static void main(String[] args) {
//2.浅拷贝复制对象
Car c1 = new Car("大众","黑色",18);
c1.country = new Country("德国");
Car c2 = (Car) c1.clone();
System.out.println(c1);
System.out.println(c2);
System.out.println(c1.country.hashCode());
System.out.println(c2.country.hashCode());
}
}
2.2 浅拷贝和深拷贝
2.2.1 浅拷贝介绍
1.对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2.对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的该成员变量都指向同一个实例,在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3.上面的案例就是浅拷贝,浅拷贝使用默认的 clone() 方法实现。如果我们修改 c2 的 country 属性的数据,那么 c1 的 country 数据也会随之改变。
2.2.2 深拷贝介绍
1.复制对象的所有基本数据类型的成员变量值
2.为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
深拷贝方式 1:重写 clone() 方法来实现深拷贝
深拷贝方式 2:通过对象序列化实现深拷贝(推荐)
2.2.3 重写 clone() 方法实现深拷贝
//Car 实体类
public class Car implements Cloneable{
private String name;
private String color;
private int size;
public Country country;
public Car() {
}
public Car(String name, String color, int size) {
this.name = name;
this.color = color;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", size=" + size +
", country=" + country +
'}';
}
@Override
public Object clone() {
Car car = null;
try{
car = (Car) super.clone();
car.country = (Country) car.country.clone();
} catch (Exception e) {
e.printStackTrace();
}
return car;
}
}
//Country 实体类
public class Country implements Cloneable{
private String name;
public Country(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Country{" +
"name='" + name + '\'' +
'}';
}
@Override
public Object clone() {
Country country = null;
try {
country = (Country) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return country;
}
}
//测试类
public class DeepCopy1 {
public static void main(String[] args) {
//1.深拷贝方式一:重写clone()
Car car = new Car("五菱","红色",18);
car.country = new Country("中国");
Car clone = (Car) car.clone();
System.out.println(car);
System.out.println(clone);
System.out.println(car.country.hashCode());
System.out.println(clone.country.hashCode());
}
}
2.2.4 通过序列化实现深拷贝
public class Car implements Serializable,Cloneable {
private static final long serialVersionUID = 54645442L;
private String name;
private String color;
private int size;
public Country country;
public Car() {
}
public Car(String name, String color, int size) {
this.name = name;
this.color = color;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", size=" + size +
", country=" + country +
'}';
}
//自定义克隆方法
public Car cloneCar() {
Car car = null;
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
car = (Car) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return car;
}
}
public class Country implements Serializable, Cloneable{
private static final long serialVersionUID = 34545442L;
private String name;
public Country(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Country{" +
"name='" + name + '\'' +
'}';
}
@Override
public Object clone() {
Country country = null;
try {
country = (Country) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return country;
}
}
public class DeepCopy2 {
public static void main(String[] args) {
//2.深拷贝方式二:实现序列化
Car car = new Car("五菱","红色",18);
car.country = new Country("中国");
Car clone = car.cloneCar();
System.out.println(car);
System.out.println(clone);
System.out.println(car.country.hashCode());
System.out.println(clone.country.hashCode());
}
}
2.3 原型模式注意事项
- 创建新的对象比较复杂时,可以用原型模式简化对象的创建过程,能够提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,不用修改代码
- 在实现深拷贝时可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个clone() 方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则。