Modo de protótipo
O modo de protótipo é usar o objeto copiado para criar um novo objeto e não é necessário conhecer os detalhes da criação (como a atribuição de atributos da classe etc.).
Uso básico (cópia rasa)
É comum usar o método clone de Object. Você precisa fazer a classe implementar Cloneable e substituir o método clone para usá-lo.
public class A implements Cloneable{
private String code;
private String name;
public A(String code, String name) {
this.code = code;
this.name = name;
}
public void setCode(String code) {
this.code = code;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A("00","name1");
A a1 = (A)a.clone();
a1.setCode("01");
A a2 = (A)a.clone();
a2.setCode("02");
System.out.println(a.hashCode());
System.out.println(a1.hashCode());
System.out.println(a2.hashCode());
}
}
Como resultado, fica claro que os três objetos são diferentes.
596512129
824318946
930990596
Cópia profunda
O método acima é uma cópia superficial, ou seja, apenas os tipos de dados básicos e String na Classe A podem ser copiados. Se houver outros tipos de referência na classe A, a cópia superficial não poderá realizar a cópia dos tipos de referência.
Portanto, você precisa executar uma cópia profunda de A. Geralmente, existem dois métodos (substituindo o método Clone e a serialização).
Método 1. Reescreva o método clone de A e copie B no método (é claro que a cópia superficial também deve ser implementada na classe B).
public class B implements Serializable,Cloneable{
private String id;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class A implements Serializable,Cloneable{
private String code;
private String name;
private B b;
public A(String code, String name, B b) {
this.code = code;
this.name = name;
this.b = b;
}
public void setCode(String code) {
this.code = code;
}
@Override
public Object clone() throws CloneNotSupportedException {
A a = (A)super.clone();
// 在A的clone方法中,对A的B引用进行clone
a.b = (B)a.b.clone();
return a;
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A("00","name1",new B());
A a1 = (A)a.clone();
a1.setCode("01");
A a2 = (A)a.clone();
a2.setCode("02");
System.out.println(a.b.hashCode());
System.out.println(a1.b.hashCode());
System.out.println(a2.b.hashCode());
}
}
执行结果:
596512129
824318946
930990596
Observando o resultado da execução, a referência B de cada instância A é um valor hashCode diferente, indicando que o B em A também é copiado.
Se você comentar ab = (B) abclone (); no método clone em A, poderá obter que os três resultados impressos sejam os mesmos.
Método 2. Serialização
Premissa de serialização, A e B devem implementar a interface serializável.
As definições de A e B são as mesmas do método 1, apenas reescrevemos o método clone em A.
@Override
public Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化,以对象的方式输出去
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化,再以对象的方式写回来,所有的引用类型自然都会带上了
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
A copyResult = (A)ois.readObject();
return copyResult;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
O efeito final também é com o clone de A e a referência B em A também é clonada.
A serialização acima é apenas uma ideia de implementação.Você também pode usar o fastjson para converter o objeto A em uma string json e, em seguida, reverter para A. para retornar.De fato, o princípio é o mesmo.
Cenários de aplicativos reais
De fato, é fácil encontrar: desde que você descubra quais classes implementam a interface Cloneable, o chamado modo protótipo é usado com maior probabilidade.
Por exemplo, na criação do Spring's Bean, todos sabemos que o padrão é o modo singleton; na verdade, você também pode saber que o Bean é multi-case, isso usa o modo prototype.