一.定义
原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
二.特点
不需要知道任何创建的细节,不调用构造函数
三.使用场景
- 类初始化消耗较多资源
- 类产生的一个对象需要非常繁琐的过程
- 构造函数比较复杂(如属性比较多)
- 循环体中生产大量相同类的对象时
四、优缺点
1.优点
- 比直接new一个对象性能高
- 简化创建过程
2.缺点
- 必须配备clone方法
- 实现Cloneable接口
- 覆盖Object类的clone方法
- 对克隆复杂对象或克隆出的对象进行复杂改造时,容易引入风险
- 深拷贝、浅拷贝要运用得当
- 深拷贝 : 对引用类型的数据类型的属性也进行拷贝,而不是指向同一块内存空间
- 浅拷贝 : 只拷贝基本数据类型对应的属性
五.实例
1.浅拷贝实例
订单状态改变,发送短信的实例
- 短信内容实体
/**
* 短信内容实体(实现Cloneable接口标识它是可克隆的)
* @author shixinke
*/
public class Sms implements Cloneable {
/**
* 短信ID
*/
private Long id;
/**
* 接收者手机号
*/
private String receiver;
/**
* 短信内容
*/
private String message;
/**
* 短信模板
*/
private String template;
/**
* 短信发送时间
*/
private Integer createTime;
public Sms() {
}
public Sms(Long id, String receiver, String message, String template) {
this.id = id;
this.receiver = receiver;
this.message = message;
this.template = template;
this.createTime = Long.valueOf(System.currentTimeMillis() / 1000).intValue();
}
public void setId(Long id) {
this.id = id;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public void setMessage(String message) {
this.message = message;
}
public void setTemplate(String template) {
this.template = template;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Long getId() {
return id;
}
public String getReceiver() {
return receiver;
}
public String getMessage() {
return message;
}
public String getTemplate() {
return template;
}
public Integer getCreateTime() {
return createTime;
}
/*
* 重写clone方法
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆失败");
return null;
}
}
@Override
public String toString() {
return "Sms{" +
"id=" + id +
", receiver='" + receiver + '\'' +
", message='" + message + '\'' +
", template='" + template + '\'' +
", createTime=" + createTime +
'}';
}
}
- 用户实体
/**
* 用户实体
* @author shixinke
*/
public class User {
/**
* 用户ID
*/
private Long userId;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户手机号
*/
private String mobile;
public User(Long userId, String nickname, String mobile) {
this.userId = userId;
this.nickname = nickname;
this.mobile = mobile;
}
public Long getUserId() {
return userId;
}
public String getNickname() {
return nickname;
}
public String getMobile() {
return mobile;
}
}
- 短信服务
import java.util.List;
/**
* 发送短信服务
* @author shixinke
*/
public class SmsService {
/**
* 批量发送短信
* @param userList
* @param sms
*/
public void send(List<User> userList, Sms sms) {
for (User user : userList) {
Sms temp = (Sms)sms.clone();
if (temp != null) {
temp.setReceiver(user.getMobile());
temp.setId(sms.getId() + user.getUserId());
temp.setMessage(String.format(temp.getTemplate(), user.getNickname()));
System.out.println("发送短信:" + temp);
}
}
System.out.println("原始短信对象:" + sms);
}
}
- 测试代码
import java.util.ArrayList;
import java.util.List;
/**
* 短信测试
* @author shixinke
*/
public class SmsTest {
public static void main(String[] args) {
Sms sms = new Sms(
1000L,
null,
null,
"尊敬的%s,您的订单已开始发货,请耐心等候"
);
SmsService smsService = new SmsService();
List<User> userList = new ArrayList<User>(5);
userList.add(new User(1L, "张三", "158xxxx6666"));
userList.add(new User(2L, "李四", "159xxxx6666"));
userList.add(new User(3L, "王五", "150xxxx6666"));
userList.add(new User(4L, "赵六", "130xxxx6666"));
userList.add(new User(5L, "周七", "131xxxx6666"));
smsService.send(userList, sms);
}
}
运行结果:
发送短信:Sms{id=1001, receiver='158xxxx6666', message='尊敬的张三,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
发送短信:Sms{id=1002, receiver='159xxxx6666', message='尊敬的李四,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
发送短信:Sms{id=1003, receiver='150xxxx6666', message='尊敬的王五,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
发送短信:Sms{id=1004, receiver='130xxxx6666', message='尊敬的赵六,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
发送短信:Sms{id=1005, receiver='131xxxx6666', message='尊敬的周七,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
原始短信对象:Sms{id=1000, receiver='null', message='null', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
以上类的UML关系图:
2.深拷贝实例
(1)浅拷贝
- 原型对象
/**
* 用户实体(原型对象)
* @author shixinke
*/
public class User implements Cloneable {
/**
* 用户ID
*/
private Long userId;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户手机号
*/
private String mobile;
/**
* 收货地址
*/
private Address address;
public User(Long userId, String nickname, String mobile, Address address) {
this.userId = userId;
this.nickname = nickname;
this.mobile = mobile;
this.address = address;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("克隆失败");
return null;
}
}
}
注:address属性不是基本数据类型,在使用clone拷贝的时候是浅拷贝,克隆对象的address和原型对address是指向同一内存地址的
- 引用类型对象
/**
* 用户收货地址
* @author shixinke
*/
public class Address {
/**
* 地址ID
*/
private Long addressId;
/**
* 收货详细地址
*/
private String address;
public Address(Long addressId, String address) {
this.addressId = addressId;
this.address = address;
}
public Long getAddressId() {
return addressId;
}
public void setAddressId(Long addressId) {
this.addressId = addressId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 测试代码:
/**
* 用户类测试
* @author shixinke
*/
public class UserTest {
public static void main(String[] args) {
Address address = new Address(100L, "浙江杭州");
User user = new User(1L, "赵六", "15878451XXX", address);
User temp = (User)user.clone();
/**
* 两个User对象(user和temp)不是同一个对象
*/
System.out.println("user:" + user); //User@7adf9f5f
System.out.println("temp:" + temp); //User@85ede7b
System.out.println(user == temp); //false
/**
* user的address和temp的address是同一个对象
*/
System.out.println("user.address:" + user.getAddress());//Address@5674cd4d
System.out.println("temp.address:" + temp.getAddress());//Address@5674cd4d
System.out.println(user.getAddress() == temp.getAddress()); //true
}
}
(2)实现深拷贝
对引用类型(非基本类型)的数据类型的对象进行单独的拷贝(clone)
- Address类实现Cloneable接口,并重写clone方法
/**
* 用户收货地址
* @author shixinke
*/
public class Address implements Cloneable {
/**
* 地址ID
*/
private Long addressId;
/**
* 收货详细地址
*/
private String address;
public Address(Long addressId, String address) {
this.addressId = addressId;
this.address = address;
}
public Long getAddressId() {
return addressId;
}
public void setAddressId(Long addressId) {
this.addressId = addressId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
/**
* 重写clone方法
* @return
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
- User的clone方法,除了整体的clone,还要对非基本类型的数据进行克隆,即对address对象克隆
/**
* 用户实体(原型对象)
* @author shixinke
*/
public class User implements Cloneable {
/**
* 用户ID
*/
private Long userId;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户手机号
*/
private String mobile;
/**
* 收货地址
*/
private Address address;
public User(Long userId, String nickname, String mobile, Address address) {
this.userId = userId;
this.nickname = nickname;
this.mobile = mobile;
this.address = address;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Object clone() {
try {
User user = (User)super.clone();
//单独克隆address属性
user.address = (Address)this.address.clone();
return user;
} catch (CloneNotSupportedException e) {
System.out.println("克隆失败");
return null;
}
}
}
- 再次测试
user.address:Address@5674cd4d
temp.address:Address@63961c42
此时两个对象的address属性不再指向同一地址,实现了深拷贝