您的位置:首页 > 编程语言 > Java开发

原型模式(ProtoType) - Java里的对象复制

2014-12-21 15:04 429 查看

一, 引用的复制和对象复制.

在编程中, 我们有时会用两个引用指向同一个对象.

例如:

ArrayList a = new ArrayLIst();
ArrayList b = a;


看起来好像有a,b两个容器, 实际上a,b是两个引用, 它们都指向同1个Object的内存地址.

而对象复制是指:

在内存里划分一块与指定对象相同内容的内存.

也就是说内存里原理有1个Object的内存, 复制后就有两个了...

二, 对象复制的简便方法.

当然, 对象复制的实现十分简单, 只需要实例化1个具有相同属性的对象就ok了.

注意, 如果用 "==" 来讲比较两个引用是返回false, 因为它们的地址不同.

举个列子:

2.1 产品类Prod:

public class Prod {
private int prodID;
private String prodName;

public int getProdID() {
return prodID;
}
public void setProdID(int prodID) {
this.prodID = prodID;
}

public String getProdName() {
return prodName;
}
public void setProdName(String prodName) {
this.prodName = prodName;
}

@Override
public String toString(){
return "Prod: " + this.getProdID() + " , " + this.getProdName();
}
}


2.2 订单类Order:

public class Order {
private int orderID;
private Prod prod;
private int amount;

public int getOrderID() {
return orderID;
}
public void setOrderID(int orderID) {
this.orderID = orderID;
}

public Prod getProd() {
return prod;
}
public void setProd(Prod prod) {
this.prod = prod;
}

public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}

public String toString(){
return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();
}
}


2.3 客户端代码:

这里我先实例化1个order类 od1, 然后在复制1个od1 的对象od2

Prod p1 = new Prod();
p1.setProdID(1);
p1.setProdName("Hammer");

Order od1 = new Order();
od1.setOrderID(1);
od1.setProd(p1);
od1.setAmount(20);

Prod p2 = new Prod();
p2.setProdID(1);
p2.setProdName("Hammer");

Order od2 = new Order();
od2.setOrderID(1);
od2.setProd(p1);
od2.setAmount(20);

System.out.println(od1);
System.out.println(od2);


输出:
Order: 1 , 20 Prod: 1 , Hammer
Order: 1 , 20 Prod: 1 , Hammer


2.4 这种方法的缺点

首先这种写法不够优雅, 很多重复代码.

其次, 这种复制方法调用了类的构造函数.

就如上面的例子, 即使Order类的构造函数是空的, 但是实际上构造函数的执行机制很复杂,.

构造函数最重要的动作就是给类里的每个成员划分内存空间. 毕竟java是类c语言, 相当于执行了很多次malloc()函数.

如果某些业务类的构造函数写的很复杂, 就更耗费资源了.

而且

三, 利用Object.clone()方法来实现对象复制.

java里所有对象都是直接或简直继承自基类Object.

而clone()是基类的Native 方法, 所谓Native方法可以认为是java 内部特定方法, 它比非Native的执行效率要好.

也就是说, 利用Object.clone() 方法会比实例化1个相同内容对象效率要好.

3.1 Object.clone()方法和 Cloneable接口:

Object.clone() 方法有点特殊, 首先它是public方法, 也就是说它可以通过引用名和"."来调用.

但是它不能被子类继承, 也就说, 出了Object类外所有java里的class里面都没有clone()这个方法的.

如果要使用Object.clone()方法.

只能引用1个叫做 Cloneable里的Interface, 然后重写Cloneable接口里的clone()方法, 在里面调用object.clone().

这个接口就起了1个标记的作用, 帮组实现多态.


3.2 订单类Order:


我们按在这个思路来修改上面的订单类Order:

public class Order implements Cloneable{
private int orderID;
private Prod prod;
private int amount;

public int getOrderID() {
return orderID;
}
public void setOrderID(int orderID) {
this.orderID = orderID;
}

public Prod getProd() {
return prod;
}
public void setProd(Prod prod) {
this.prod = prod;
}

public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}

public String toString(){
return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();
}

@Override
public Object clone(){
Object o = null;
try{
o = super.clone();
}catch(Exception e){
e.printStackTrace();
}

return o;
}
}


值得注意的是, Object.clone() 回抛异常.

而, Prod类没有任何修改.

3.3 客户端代码:

Prod p1 = new Prod();
p1.setProdID(2);
p1.setProdName("knife");

Order od1 = new Order();
od1.setOrderID(2);
od1.setAmount(30);
od1.setProd(p1);

Order od2 = (Order)od1.clone();
System.out.println(od1);
System.out.println(od2);


可见, 只需要执行一句
Order od2 = (Order)od1.clone();

就相当于 复制了1个对象.

3.4 UML图

我们来看看这个例子的UML:



实际上这个就是原型模式(prototype)的UML图了,

原来我们不知不觉得使用了原型模式.

四, 原型模式的定义

原型模式(protoType), 用原型实例制定创建对象的种类, 并且通过copy这些原型创建新的对象.

总觉得设计模式的定义都太过于简单恶心了.

上面的

原型指的是 Cloneable这个接口.

原型实例指的是 Order这个类.

五, 浅复制和深复制

对象复制也分两种.

浅复制:
两个对象中的值类型成员属性相等, 对象成员成员属性实际上没有复制,都是同一个对象.

深复制:

两个对象中的值类型成员属性相等, 对象成员属性指向不同的 具有相同成员的 对象.

如上面的例子, 稍稍修改客户端代码:

		Prod p1 = new Prod();
p1.setProdID(2);
p1.setProdName("knife");

Order od1 = new Order();
od1.setOrderID(2);
od1.setAmount(30);
od1.setProd(p1);

Order od2 = (Order)od1.clone();
System.out.println(od1);
System.out.println(od2);
od1.setOrderID(3);
od1.setAmount(40);
od1.getProd().setProdName("blade");

System.out.println(od1);
System.out.println(od2);


上面 构建了对象od1,

然后复制出了另1个对象od2,

然后修改od1的 值成员 和 对象成员

然后输出od1, 和 od2 成员的值:

Order: 2 , 30 Prod: 2 , knife
Order: 2 , 30 Prod: 2 , knife
Order: 3 , 40 Prod: 2 , blade
Order: 2 , 30 Prod: 2 , blade


可见, 当od1 的id 和 amount的值被改成 3 和 40 后, od2 的id和 Amount的值并不受影响, 因为它们的值类型成员属性是相互独立的.

但是当od1 的 prod属性的内容被修改后, od2 的也被修改了(knife - > blade), 因为 od1 和 od2 的 prod成员指向的都是同1个对象.

这就证明了:

Java里的 Object.clone()方法实现的是浅复制.

六, 原型模式的深复制实现

接下来再次修改上面的例子, 令其实现成深复制

而且, 根据项目需要, 复制并不会复制Order的id, 也就是订单号码是唯一的, 只复制Prod和数量.

6.1 订单类Order:

public class Order implements Cloneable{
private int orderID;
private Prod prod;
private int amount;

public int getOrderID() {
return orderID;
}
public void setOrderID(int orderID) {
this.orderID = orderID;
}

public Prod getProd() {
return prod;
}
public void setProd(Prod prod) {
this.prod = prod;
}

public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}

public String toString(){
return "Order: " + this.getOrderID() + " , " + this.getAmount() + " " + this.getProd().toString();
}

@Override
public Object clone(){
Prod p = new Prod();
p.setProdID(this.getProd().getProdID());
p.setProdName(this.getProd().getProdName());

Order o = new Order();
o.setOrderID(this.getOrderID() + 1);
o.setAmount(this.getAmount());
o.setProd(p);

return o;
}
}


客户端输出:

Order: 2 , 30 Prod: 2 , knife
Order: 3 , 30 Prod: 2 , knife
Order: 3 , 40 Prod: 2 , blade
Order: 3 , 30 Prod: 2 , knife


可以, 无论od1修改了什么, od2 都不受影响

这种方法放弃了Object.clone(), Order类在重写clone()方法里还是使用了构造方法去实例化1个新的对象.

这种方法配置更加灵活(选择性地复制成员)

当时放弃了Object.clone()的效能优点.

但是这仍然实现了原型模式(protoType)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: