Diretório de artigos
Herdar a interface Cloneable e reimplementar o método clone
A classe de entidade precisa herdar a interface Cloneable e reimplementar o método clone() .
Deve-se observar que os elementos do tipo ponteiro referenciados na classe de entidade também herdam a interface Cloneable e reimplementam o método clone() ou criam um novo objeto e o atribuem ao elemento.
@Data
public class CloneableEntity implements Cloneable{
private String stringValue;
private PointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
//填充列表 用来比较克隆效率
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
@Override
public CloneableEntity clone() throws CloneNotSupportedException {
CloneableEntity clone = (CloneableEntity)super.clone();
// 指针类型的元素要做clone一份新的
if(null!=this.pointerEntity) {
clone.setPointerEntity(this.pointerEntity.clone());
}
// 创建一个新的对象给元素
clone.setListValue(new ArrayList<>(listValue));
return clone;
}
}
@Data
public class PointerEntity implements Cloneable{
private String uuid;
public PointerEntity(){
}
public PointerEntity(String uuid){
this.uuid = uuid;
}
@Override
protected PointerEntity clone() throws CloneNotSupportedException {
return (PointerEntity)super.clone();
}
}
clonar instância
Qualquer modificação nos elementos do novo objeto clonado não afetará o valor dos elementos no objeto original.
Gson gson = new Gson();//用来打印对象
// Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.setStringValue("1");
entity1.setPointerEntity(new PointerEntity(UUID.randomUUID().toString()));
entity1.setListValue(new ArrayList<String>(){
{
add("1");add("2");}});
CloneableEntity clone1 = entity1.clone();
//对克隆后的对象进行修改
clone1.setStringValue("1--1");
clone1.getListValue().add("3");
clone1.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity1: "+gson.toJson(entity1));
System.out.println("clone1: "+gson.toJson(clone1));
-------------------------------
entity1: {
"stringValue":"1","pointerEntity":{
"uuid":"27bb47c6-1ad3-4b79-a408-509f2163b5fa"},"listValue":["1","2"]}
clone1: {
"stringValue":"1--1","pointerEntity":{
"uuid":"a3495d1d-e32c-4dee-be57-0a8a3f30678c"},"listValue":["1","2","3"]}
Serialização e desserialização usando streams
A classe clonada deve oferecer suporte à serialização e os elementos da classe também devem oferecer suporte à serialização. Caso contrário, uma exceção que não oferece suporte à serialização aparecerá durante a clonagem.
@Data
public class Serialzable2Entity implements Serializable {
private String stringValue;
private SerialzablePointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
//填充列表 用来比较克隆效率
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
}
@Data
public class SerialzablePointerEntity implements Serializable {
private String uuid;
public SerialzablePointerEntity(){
}
public SerialzablePointerEntity(String uuid){
this.uuid = uuid;
}
}
clonar instância
Gson gson = new Gson();//用来打印对象
//流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.setStringValue("41");
entity4.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity4.setListValue(new ArrayList<String>(){
{
add("41");add("42");}});
Serialzable2Entity clone4 ;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(entity4);
oos.flush();
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
clone4 = (Serialzable2Entity) ois.readObject();
}
}
clone4.setStringValue("41--1");
clone4.getListValue().add("43");
clone4.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity4: " + gson.toJson(entity4));
System.out.println("clone4: " + gson.toJson(clone4));
---------------------------------------
entity4: {
"stringValue":"41","pointerEntity":{
"uuid":"af3c287f-1289-4fb3-947f-f5b7684ef07e"},"listValue":["41","42"]}
clone4: {
"stringValue":"41--1","pointerEntity":{
"uuid":"0549e071-d7df-469f-baae-578e9aa89194"},"listValue":["41","42","43"]}
Usando o Serializador Apache Commons Lang
O Apache Commons Lang vem com uma classe de ferramenta de serialização SerializationUtils , na qual o método clone() nos permite fazer uma cópia profunda do objeto.
Na verdade, seu princípio é o mesmo da serialização e desserialização do stream acima, exceto que há mais conversões de tipo e outras operações nele. Portanto,
também requer que a classe clonada ofereça suporte à serialização e os elementos da classe também ofereçam suporte à serialização. Caso contrário, uma exceção que não oferece suporte à serialização aparecerá durante a clonagem.
clonar instância
Gson gson = new Gson();//用来打印对象
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.setStringValue("11");
entity2.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity2.setListValue(new ArrayList<String>(){
{
add("11");add("12");}});
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
//对克隆后的对象进行修改
clone2.setStringValue("11--1");
clone2.getListValue().add("13--1");
clone2.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity2: "+gson.toJson(entity2));
System.out.println("clone2: "+gson.toJson(clone2));
------------------------------------------
entity2: {
"stringValue":"11","pointerEntity":{
"uuid":"303e0bff-ad81-4b56-a1db-311cf894b189"},"listValue":["11","12"]}
clone2: {
"stringValue":"11--1","pointerEntity":{
"uuid":"bb42fd5b-d0ce-483a-9f34-d32e1b0eab5c"},"listValue":["11","12","13--1"]}
Use Gson para serializar e desserializar
Use serialização e desserialização Gson para clonar, e o objeto clonado não precisa de nenhuma extensão.
@Data
public class NormalEntity{
private String stringValue;
private NormalPointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
/**
* 初始化列表
*/
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
}
@Data
public class NormalPointerEntity{
private String uuid;
public NormalPointerEntity(){
}
public NormalPointerEntity(String uuid){
this.uuid = uuid;
}
}
Gson gson = new Gson();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.setStringValue("21");
entity3.setPointerEntity(new NormalPointerEntity(UUID.randomUUID().toString()));
entity3.setListValue(new ArrayList<String>(){
{
add("21");add("22");}});
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
//修改克隆后的对象
clone3.setStringValue("21--1");
clone3.getListValue().add("23");
clone3.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity3: "+gson.toJson(entity3));
System.out.println("clone3: "+gson.toJson(clone3));
---------------------------
entity3: {
"stringValue":"21","pointerEntity":{
"uuid":"b81d7ee9-54db-4d51-b09d-0a61adce4518"},"listValue":["21","22"]}
clone3: {
"stringValue":"21--1","pointerEntity":{
"uuid":"276de4f8-0b23-4435-9225-dfc836311064"},"listValue":["21","22","23"]}
Compare os prós e contras
Em primeiro lugar, comparamos a eficiência
StopWatch stopWatch = new StopWatch();
// Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.initListValue();
stopWatch.start("Cloneable 深拷贝");
CloneableEntity clone1 = entity1.clone();
stopWatch.stop();
// 流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.initListValue();
Serialzable2Entity clone4 ;
stopWatch.start(" 流深拷贝");
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(entity4);
oos.flush();
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
clone4 = (Serialzable2Entity) ois.readObject();
}
}
stopWatch.stop();
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.initListValue();
stopWatch.start("Apache Commons Lang序列化深拷贝");
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
stopWatch.stop();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.initListValue();
stopWatch.start(" Gson 序列化反序列化深拷贝");
Gson gson = new Gson();//用来打印对象
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
-----------------------------------
---------------------------------------------
ns % Task name
---------------------------------------------
000029100 000% Cloneable 深拷贝
048922700 023% 流深拷贝
017047500 008% Apache Commons Lang序列化深拷贝
150727000 070% Gson 序列化反序列化深拷贝
De acordo com o consumo de tempo, pode-se observar que a clonagem por herança da interface Cloneable é a mais eficiente, seguida pela serialização de stream e SerializationUtils , sendo a serialização Gson a mais lenta.
Deve-se observar que a eficiência da cópia profunda de streaming e do SerializationUtils deve ser semelhante. Porque essencialmente todos os fluxos são usados para serialização e desserialização. No entanto, como uma parte do tempo é consumida quando a classe IO é carregada pela primeira vez após o programa ser iniciado, ao executar a clonagem SerializationUtils , o consumo de tempo é menor que a serialização do fluxo.
Quando trocamos a ordem de execução de serialização de fluxo e SerializationUtils , descobriremos que o tempo consumido pela serialização de fluxo é menor que o de SerializationUtils .
Resumo das vantagens e desvantagens
método | Vantagem | desvantagem |
---|---|---|
Herdar a interface Cloneable | Alta eficiência, sem necessidade de consultar outros pacotes jar e baixa sobrecarga do sistema | A classe clonada deve reescrever a interface Cloneable, optar por clonar manualmente os elementos de ponteiro na classe e, quando os campos da classe forem atualizados, também precisamos atualizar o método de clonagem a tempo de evitar falhas de cópia profunda |
serialização de fluxo | Não há necessidade de se referir a outros pacotes jar e não há necessidade de ajustar o código para adicionar e modificar campos | A eficiência é média, a classe deve oferecer suporte à serialização e o processo de serialização e desserialização tem uma grande sobrecarga do sistema |
SerializationUtils.clone() | O código é conciso e fácil de usar, adicionar e modificar campos não precisa ajustar o código | A eficiência é média, o pacote apache common jar precisa ser introduzido, a classe deve suportar a serialização e o processo de serialização e desserialização tem uma grande sobrecarga do sistema |
Serialização Gson | Não requer nenhum pré-processamento da classe | A eficiência é muito baixa, o pacote jar precisa ser introduzido e o processo de serialização e desserialização tem uma grande sobrecarga do sistema |