原型模式

1.定义

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

2.场景

在游戏中,经常会出现带有复制功能的BOSS,为了实现这个复制功能,一般都是先new出该对象然后依次设置其属性到当前状态。虽然功能确实实现了,但是其消耗比较大,尤其是该对象比较"重"的情况下。还好java提供了一种方式可以快速高效复制一个对象,实现方式:实现Cloneable然后重写clone方法。

2.1 实现Cloneable接口

@Data
public class Boss implements Cloneable {
    private int x;
    private int y;
    private String name;

    // 重写方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2.2 测试

public class TestPrototype {

    public static void main(String[] args) throws CloneNotSupportedException {
        Boss boss = new Boss();
        boss.setX(1);
        boss.setY(2);
        boss.setName("大怪物");
        Boss boss2 = (Boss) boss.clone();
        boss2.setName("大怪物-副本1");
        boss2.setX(10);
        boss2.setY(20);
        System.out.println(boss);
        System.out.println(boss2);
        System.out.println(boss == boss2);
 }
}


----------------------------console
Boss(x=1, y=2, name=大怪物)
Boss(x=10, y=20, name=大怪物-副本1)
false

虽然产生对象的地址和原对象的地址不一样,但是这种克隆方式,只能是一种浅克隆。为了证明这个,将x,y属性封装成一个对象。

3.浅克隆实验

3.1 克隆对象

/*
 * 位置
 */
@Data
@AllArgsConstructor
public class Point {
    private int x;
    private int y;
}

@Data
public class Boss implements Cloneable {
    private Point point;
    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

3.2 测试

public class TestPrototype {

    public static void main(String[] args) throws CloneNotSupportedException {
        Boss boss = new Boss();
        String bossName = "大怪物";
        boss.setName(bossName);
        Point bossPoint = new Point(1,2);
        boss.setPoint(bossPoint);
        Boss boss2 = (Boss) boss.clone();
        System.out.println(boss);
        System.out.println(boss2);


        // 修改克隆对象的坐标和名字
        Point boss2Point = boss2.getPoint();
        String boss2Name = "大怪物-副本1";
        boss2.setName(boss2Name);
        boss2Point.setX(10);
        boss2Point.setY(20);
        System.out.println(boss);
        System.out.println(boss2);
    }
}
----------------------------console
Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=10, y=20), name=大怪物)
Boss(point=Point(x=10, y=20), name=大怪物-副本1)

从上面可以看出,当修改boss2的pioint时,boss的point也跟着改变,即boss和boss2的point属性指向了同一个位置(地址)。同时也可以得出:如果某类只有基本数据类型或者String类型或者包装类型,使用浅克隆就可以复制另一个"不一样"的对象(虽然String和包装类型属性复制出来的对象和源对象地址是一样的即指向了同一对象,但是一般没办法修改其内部属性)。

4. 深克隆

4.1 引用属性实现Cloneable接口

@Data
@AllArgsConstructor
public class Point implements Cloneable{
    private int x;
    private int y;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

4.2 克隆对象修改clone方法

@Data
public class Boss implements Cloneable {
    private Point point;
    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Boss boss = (Boss) super.clone();
        // 该属性使用clone方法克隆对象
        boss.point = (Point)point.clone();
        return boss;
    }
}

打印结果将变为:

Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=10, y=20), name=大怪物-副本1)     // 说明已经深克隆了

4.3 使用序列化方式

如果引用类型的属性,包含引用类型的属性,并且层级比较多,使用上面的方法将会变得非常复杂,在这种情况下可以使用序列化的方式。

4.3.1 克隆对象和引用属性实现Serializable接口

@Data
public class Boss implements Serializable {
    private Point point;
    private String name;

}
@Data
@AllArgsConstructor
public class Point implements Serializable{
    private int x;
    private int y;

}

4.3.2 测试

public class TestPrototype {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Boss boss = new Boss();
        String bossName = "大怪物";
        boss.setName(bossName);
        Point bossPoint = new Point(1,2);
        boss.setPoint(bossPoint);
        // 写入对象
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(boss);
        oos.flush();
        // 读出对象
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Boss boss2=(Boss)ois.readObject();
        // 修改克隆对象的坐标和名字
        Point boss2Point = boss2.getPoint();
        String boss2Name = "大怪物-副本1";
        boss2.setName(boss2Name);
        boss2Point.setX(10);
        boss2Point.setY(20);
        System.out.println(boss);
        System.out.println(boss2);
    }
}
----------------------------console
Boss(point=Point(x=1, y=2), name=大怪物)
Boss(point=Point(x=10, y=20), name=大怪物-副本1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值