360面试:可以说说原型模式的浅复制与浅复制吗

  本期要和大家分享的是自己对于原型模式的理解,原型模式是创建型模式,所谓创建型模式可以简单理解为,是创建对象模式,创建型模式分享完后,从下篇文章开始,分享结构型模式。

什么是原型模式

  **原型模式复制已有对象,而又无需使代码依赖它们所属的类。**在Java中只要使用了new关键字,那么这个就是定义中所说的依赖。依赖意味着耦合,而原型模式整好解决了这个问题。

入门小案例

  该案例代码比较简洁,意让大家快速理解原型模式。Object这个类大家并不既陌生,它是所有类的父类,在Object中有个clone()方法,这个方法的功能就是拷贝对象。先来看看这个方法:

在这里插入图片描述

  这是一个本地方法且抛出了一个异常,关于这个方法的作用,注释已经说的非常好了,英语好的小伙伴可以直接看,英语不好的只能吃下我的回锅肉了。注释的几个意思是该方法创建并返回此对象的副本,类的方法执行特定的克隆操作必须实现Cloneable接口,如果不实现,默认会抛出CloneNotSupportedException,数组对象默认实现了Cloneable接口。clone方法执行的是‘浅复制’,而不是‘深复制’。我们先来看看没实现Cloneable抛出异常的情况:
在这里插入图片描述
  接着看下如何理解clone方法的浅拷贝以及如何实现深拷贝:

  案例非常简单,定义一个类ShapeTest,两个普通类型的属性,一个对象类型属性,实现了Cloneable接口,重写clone方法。

@Data
public class ShapeTest implements Cloneable {
    private int int1;
    private int int2;
    private Person person;

    @Override
    public Object clone() throws CloneNotSupportedException {
        ShapeTest clone = (ShapeTest)super.clone();
        return clone;
    }
}
public class Person {


}

测试类

public class ShapeMainTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 定义一个原型
        ShapeTest shapeTest = new ShapeTest();
        shapeTest.setInt1(1234);
        shapeTest.setInt2(1234);
        shapeTest.setPerson(new Person());

        // 调用clone方法克隆新的对象
        ShapeTest clone = (ShapeTest) shapeTest.clone();

        // 比较地址值
        System.out.println(shapeTest.getInt2() == clone.getInt2());
        System.out.println(shapeTest.getInt1() == clone.getInt1());
        System.out.println(shapeTest.getPerson() == clone.getPerson());
    }
}

测试结果

true
true
true

  我们看到三个结果都是true,那么对于简单类型来说,原型与克隆出来的对象是相互独立的,比如我修改克隆对象的简单属性的值,是不影响原型的。但是对于Person这种引用类型的属性,clone方法只是复制了引用的地址,而不是像普通类型进行逐位复制。

  所以前两个true是没问题的,第三个就会出现问题,只要原型或者克隆的任一对象对person属性进行修改,那么所有的对象都会随之改变,这就是浅复制。

如何解决浅拷贝呢?

  解决浅拷贝的方式其实很简单,就是让Person类也实现Cloneable接口,重写clone()方法,这样就能实现原型与克隆对象中的引用类型的属性相互独立。修改后的代码如下:

public class Person implements Cloneable {

    @Override
    public Object clone() throws CloneNotSupportedException {
        Person clone = (Person)super.clone();
        return clone;
    }

}
@Data
public class ShapeTest implements Cloneable {
    private int int1;
    private int int2;
    private Person person;

    @Override
    public Object clone() throws CloneNotSupportedException {
        ShapeTest clone = (ShapeTest)super.clone();
        clone.setPerson((Person) clone.getPerson().clone());
        return clone;
    }
}

测试结果

true
true
false

  最后一个比较结果是false,这就是我们所说的深复制,深复制需要我们对引用类型的属性进行再克隆,这样才能达到我们想要的结果。如果是多层引用类型嵌套可能就需要使用递归来进行处理,但是递归比较消耗性能。

拓展知识点

原型模式一定要实现Cloneable接口吗?

  设计模式是一套方法论,每个模式都在介绍一种思想,原型模式的核心思想就是在原型类中提供一个clone方法来克隆对象。JDK中的clone方法可以复制很多对象,但是我们也可以自定义接口或者抽象类,而不去实现Cloneable接口,具体的我们在下面这个知识点中体现。

原型工厂

  在入门小案例中的原型是在我们测试里手动new的,并且手动克隆的。实际上我们可以定义一个原型工厂,在原型工厂类中定义一个缓存各种原型的容器,通过传递原型名称或者其他参数。工厂通过搜索参数返回克隆的对象。

下面的案例是原型是一个形状抽象类,创建圆形类与矩形类继承形状抽象类

形状原型类

public abstract class BaseShape {
    public int x;
    public int y;
    public String color;

    public BaseShape() {

    }

    public BaseShape(BaseShape target) {
        if (target != null) {
            this.x = target.x;
            this.y = target.y;
            this.color = target.color;
        }
    }

    /**
     * 克隆一个对象
     *
     * @return Object
     */
    @Override
    protected abstract BaseShape clone();

    @Override
    public boolean equals(Object shape2) {
        if (!(shape2 instanceof BaseShape)) {
            return false;
        }
        BaseShape baseShape = (BaseShape) shape2;
        return (baseShape.y == y) && (baseShape.x == x) && Objects.equals(baseShape.color, color);
    }
}

圆形类

@NoArgsConstructor
public class Circle extends BaseShape {

    /**
     * 半径
     */
    public int radius;

    public Circle(Circle target) {
        super(target);
        if (target != null) {
            this.radius = target.radius;
        }
    }

    @Override
    protected Circle clone() {
        return new Circle(this);
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof Circle)) {
            return false;
        }
        Circle circle = (Circle) object;
        return circle.radius == radius;
    }
}

矩形类

@NoArgsConstructor
public class Rectangle extends BaseShape {
    public int width;
    public int height;

    public Rectangle(Rectangle target) {
        super(target);
        if (target != null) {
            this.width = target.width;
            this.height = target.height;
        }
    }

    @Override
    protected Rectangle clone() {
        return new Rectangle(this);
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof Rectangle)) {
            return false;
        }
        Rectangle rectangle = (Rectangle) object;
        return rectangle.width == width && rectangle.height == height;
    }
}

原型工厂

public class BundledShapeCache {
    public Map<String, BaseShape> cache = new HashMap<>();;

    /**
     * 原型工厂
     */
    public BundledShapeCache() {
        Circle circle = new Circle();
        circle.x =35;
        circle.y = 36;
        circle.radius = 6;
        circle.color = "pink";
        cache.put("pink", circle);

        Rectangle rectangle = new Rectangle();
        rectangle.width = 10;
        rectangle.height = 20;
        rectangle.color = "blue";
        rectangle.width = 32;
        rectangle.height = 21;
        cache.put("blue", rectangle);
    }

    public BaseShape get(String color) {
        return cache.get(color).clone();
    }
}

测试

public class Demo {
    public static void main(String[] args) {
        BundledShapeCache cache = new BundledShapeCache();

        BaseShape pink = cache.get("pink");
        BaseShape blue1 = cache.get("blue");
        BaseShape blue2 = cache.get("blue");

        if (pink != blue1) {
            System.out.println("它们不是一种形状!");
        }

        if (blue1 != blue2) {
            System.out.println("它们不是同一个对象!");
            if (blue1.color.equals(blue2.color)) {
                System.out.println("它们的值是相等的!");
            }
        }
    }
}

结果

它们不是一种形状!
它们不是同一个对象!
它们的值是相等的!

总结

  原型模式的应用在真实世界其实也是随处可见的,打印复印相比大家都知道。在打印店中复印比打印快,成本低。假设公司要打印1000份公司同事名单。那么打印1张复印999张,还是打印1000张?这就是原型模式,它的核心是在原型类中提供一个clone方法,但是需要注意浅复制与深复制的的问题。

  设计模式学的是思想,在新学一种设计模式时,需要考虑到是否能和其他模式联系起来,比如抽象工厂,工厂方法,原型模式都可以与单例模式结合。有些模式是否还有递进的关系,比如创建型模式,我是从简单工厂演化成工厂方法,从工厂方法演化成抽象工厂。有兴趣的可以看看往期文章。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jayden 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值