有一只羊,名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10只羊。
public class Test {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
}
}
上述写法太死板了,不够灵活,我们可以做一些改进。
改进思路:Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制 一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 => 原型模式
所以,原型模式的核心就是一个clone()方法的操作。
步骤:
Sheep.java
/**
* 克隆羊
* 实现Cloneable接口
*/
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
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;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", hashCode='" + this.hashCode() + '\'' +
'}';
}
//重写clone()
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //通过super.clone()返回该对象的克隆实例,以Object的形式
}
}
Test.java
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep2 = (Sheep) sheep.clone(); //通过调用clone方法获取克隆对象
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
// 输出结果:
// Sheep{name='tom', age=1, color='白色', hashCode='1625635731'}
// Sheep{name='tom', age=1, color='白色', hashCode='1580066828'}
// Sheep{name='tom', age=1, color='白色', hashCode='491044090'}
// Sheep{name='tom', age=1, color='白色', hashCode='644117698'}
}
}
接下来,我们来思考一个问题。
刚刚我们克隆对象的时候,对象的属性都是基本数据类型,那么如果是一个引用类型的话?会是一个什么样的效果??
我们做一个简单的示范,步骤:
private Sheep friend;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep friend = new Sheep("Bob", 1, "灰色"); //朋友羊,作为引用
Sheep sheep = new Sheep("tom", 1, "白色", friend);
Sheep sheep2 = (Sheep) sheep.clone(); //通过调用clone方法获取克隆对象
System.out.println(sheep.getFriend().hashCode()); // 1625635731
System.out.println(sheep2.getFriend().hashCode()); // 1625635731
}
}
复制对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
深拷贝实现方式1:重写clone方法来实现深拷贝
深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)
需求,sheep羊有一个家,克隆羊不要引用sheep的家,要一个属于自己的家。
步骤:
huose类
public class House implements Cloneable {
private String name;
public House(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Sheep类
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private House house;
public Sheep(String name, int age, String color, House house) {
this.name = name;
this.age = age;
this.color = color;
this.house = house;
}
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;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public House getHouse() {
return house;
}
public void setHouse(House house) {
this.house = house;
}
//重写clone()
@Override
protected Object clone() throws CloneNotSupportedException {
//这里的克隆羊的house引用的是原先的地址
Sheep sheep = (Sheep) super.clone();
//把house单独拿出来克隆一份
House house = (House) sheep.getHouse().clone();
//再对其赋值新house的地址
sheep.setHouse(house);
return sheep;
}
}
上面的重写clone方法对应的图示。
先clone()一个sheep,但是此时的sheep的引用地址是原先的。
所以我们要继续开辟一个house的克隆,再将引用替换。
测试类
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
House house = new House("新疆"); //房子在新疆
Sheep sheep = new Sheep("tom", 1, "白色", house);
Sheep sheep2 = (Sheep) sheep.clone(); //通过调用clone方法获取克隆对象
System.out.println(sheep.getHouse().hashCode()); // 1625635731
System.out.println(sheep2.getHouse().hashCode()); // 1580066828
//两个对象的hashcode不一样,所以引用的是两个对象
}
}
原理:
就是将对象通过序列化,以流的方式输出(这时会将引用类型拷贝一个新的地址出来),再用输入流去接收。
步骤:
1、sheep类以及house类要实现Serializable接口,才能做一个序列化的操作。
2、在sheep类里编写deepClone()方法。
3、客户端调用deepClone()方法返回新的深拷贝对象。
public Sheep deepClone() {
//创建流对象
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);
return (Sheep) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}