1.简介
1.1 深拷贝和浅拷贝
**深拷贝:**创建一个新的对象,将源对象的各项属性的 "值" 拷贝过来,是 "值" 而不是 "引用" ,新对象跟源对象不共享内存,修改新对象不会影响源对象
**浅拷贝:**将源对象的引用直接赋给新对象,新对象只是源对象的一个引用,修改新对象会影响到源对象
2.实体类
2.1 资料类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Document {
/**
* 资料id
*/
private Integer id;
/**
* 资料名称
*/
private String name;
/**
* 资料价格
*/
private BigDecimal price;
/**
* 资料提供者
*/
private User user;
}
2.2 用户类(资料提供者)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/**
* 用户id
*/
private Integer id;
/**
* 用户名称
*/
private String name;
}
2.3 注意事项
如果下面的实现方式中对Document类和User类有特殊要求,会将实现后的代码贴出来,如果不需要,那么使用的就是上面简单的Document类和User类
3.实现方式
3.1 手动赋值
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Document {
/**
* 资料id
*/
private Integer id;
/**
* 资料名称
*/
private String name;
/**
* 资料价格
*/
private BigDecimal price;
/**
* 资料提供者
*/
private User user;
/**
* 克隆资料对象
*
* @param document 源资料对象
* @return 克隆资料对象
*/
public Document cloneDocument() {
return Document.builder()
.id(this.getId())
.name(this.getName())
.price(this.getPrice())
.user(new User(this.getUser().getId(), this.getUser().getName()))
.build();
}
}
//测试
public static void main(String[] args) throws CloneNotSupportedException {
Document document = Document.builder()
.id(1)
.name("五年高考,三年模拟")
.price(new BigDecimal("29.9"))
.user(new User(1, "韩非子"))
.build();
Document cloneDocument = document.cloneDocument();
System.out.println(document == cloneDocument);
System.out.println(document);
System.out.println(cloneDocument);
}
3.2实现Cloneable, 重写clone
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Document implements Cloneable{
/**
* 资料id
*/
private Integer id;
/**
* 资料名称
*/
private String name;
/**
* 资料价格
*/
private BigDecimal price;
/**
* 资料提供者
*/
private User user;
/**
* 重写clone
*
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Document clone() throws CloneNotSupportedException {
return (Document) super.clone();
}
}
//测试
public static void main(String[] args) throws CloneNotSupportedException {
Document document = Document.builder()
.id(1)
.name("五年高考,三年模拟")
.price(new BigDecimal("29.9"))
.user(new User(1, "韩非子"))
.build();
Document cloneDocument = document.clone();
System.out.println(document == cloneDocument);
System.out.println(document);
System.out.println(cloneDocument);
}
3.3 序列化→反序列化
3.3.1 Jdk序列化
将对象进行Jdk序列化,然后进行反序列化生成新对象,拷贝类(包括其成员变量)需要实现Serializable接口(以Apache Commons Lang为例)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable{
/**
* 用户id
*/
private Integer id;
/**
* 用户名称
*/
private String name;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Document implements Serializable {
/**
* 资料id
*/
private Integer id;
/**
* 资料名称
*/
private String name;
/**
* 资料价格
*/
private BigDecimal price;
/**
* 资料提供者
*/
private User user;
}
//测试
public static void main(String[] args) throws CloneNotSupportedException {
Document document = Document.builder()
.id(1)
.name("五年高考,三年模拟")
.price(new BigDecimal("29.9"))
.user(new User(1, "韩非子"))
.build();
Document cloneDocument = SerializationUtils.clone(document);
System.out.println(document == cloneDocument);
System.out.println(document);
System.out.println(cloneDocument);
}
3.3.2 Json序列化
将对象进行Json序列化,然后进行反序列化生成新对象(以FastJson为例 )
//测试
public static void main(String[] args) throws CloneNotSupportedException {
Document document = Document.builder()
.id(1)
.name("五年高考,三年模拟")
.price(new BigDecimal("29.9"))
.user(new User(1, "韩非子"))
.build();
String documentString = JSON.toJSONString(document);
Document cloneDocument = JSON.parseObject(documentString, Document.class);
System.out.println(document == cloneDocument);
System.out.println(document);
System.out.println(cloneDocument);
}
3.4 反射
- 以Spring BeanUtils.copyProperties(source, target)为例
- 调用原理:target.set + source的属性名(source.get + source的属性名):所有source必须有get方法,target必须有set方法
public static void main(String[] args) throws CloneNotSupportedException {
Document document = Document.builder()
.id(1)
.name("五年高考,三年模拟")
.price(new BigDecimal("29.9"))
.user(new User(1, "韩非子"))
.build();
Document cloneDocument = new Document();
BeanUtils.copyProperties(document, cloneDocument);
System.out.println(document == cloneDocument);
System.out.println(document);
System.out.println(cloneDocument);
}
4.总结
| 深拷贝实现方式 | 优点 | 缺点 |
手动赋值 | 1. 实现简单 2. 系统开销小 3. 不需要引入第三方的库 | 1. 可用性差,每次成员变量变更都需要修改方法 |
重写clone | 1. 实现简单 2.系统开销小 3. 不需要引入第三方的库 4. 可用性强,成员变量变更不需要修改方法 | 1. 拷贝类(包括其成员变量),需要实现Cloneable接口 |
序列化→反序列化 | 1. 可用性强,成员变量变更不需要修改方法 | 1. 底层实现复杂 2. 需要引入第三方的包 3. 如果是Jdk序列化,拷贝类(包括其成员变量)需要实现Serializable接口 3. 序列化与反序列化存在一定的系统开销 |
反射 | 1. 可用性强,成员变量变更不需要修改方法 | 1. 底层实现复杂 2. 需要引入第三方的包 3. 反射存在一定的系统开销 |