Cópia profunda e cópia zero são dois conceitos amplamente utilizados em Java para cópia de objetos e otimização de transferência de dados, respectivamente. Os princípios desses dois conceitos serão apresentados em detalhes abaixo e serão fornecidos exemplos de código Java correspondentes.
cópia profunda
- Princípio da cópia profunda (cópia profunda): a cópia profunda consiste em criar uma cópia completamente independente de um objeto, incluindo o próprio objeto, atributos de tipo de referência e subobjetos. A cópia profunda pode ser obtida por meio de serialização e desserialização.
Primeiro, você precisa garantir que o objeto a ser copiado e as classes referenciadas nele implementem a interface Serializable. Em seguida, as operações de serialização e desserialização são concluídas gravando objetos no fluxo de saída e lendo no fluxo de entrada. Dessa forma, você pode obter uma cópia totalmente nova do objeto, e o objeto original e o objeto copiado não têm influência um sobre o outro.
Uma maneira comum de implementar cópia profunda é por meio da serialização e desserialização. As etapas específicas são as seguintes:
- Primeiro, você precisa gravar o objeto original em um fluxo de saída (como ObjectOutputStream), convertendo o objeto em uma sequência de bytes.
- Em seguida, a sequência de bytes é lida no fluxo de saída e desserializada em um novo objeto por meio do fluxo de entrada (como ObjectInputStream). Este novo objeto é independente do objeto original e suas propriedades e subobjetos são copiados de forma independente.
Este método pode garantir que o objeto de cópia profunda e suas propriedades e subobjetos referenciados sejam todos novos, mas também pode envolver problemas como referências circulares no gráfico do objeto, que requerem tratamento especial. Além disso, o objeto copiado e a classe a que ele se refere precisam implementar a interface Serializable para realizar operações de serialização e desserialização.
import java.io.*;
class Student implements Serializable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Getters and setters here...
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class DeepCopyExample {
public static void main(String[] args) {
Student original = new Student("John", 20);
// 深拷贝
Student copy = deepCopy(original);
// 改变原始对象的属性值
original.setName("Tom");
original.setAge(25);
System.out.println("Original: " + original); // 输出 Original: Student{name='Tom', age=25}
System.out.println("Copy: " + copy); // 输出 Copy: Student{name='John', age=20}
}
public static <T extends Serializable> T deepCopy(T object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
T copy = (T) ois.readObject();
ois.close();
return copy;
} catch (Exception e) {
throw new RuntimeException("Deep copy failed", e);
}
}
}
No exemplo acima, uma Student
classe é criada como o objeto a ser copiado. deepCopy()
O método usa serialização e desserialização para realizar cópia profunda. Primeiro, o objeto original é gravado no fluxo de saída da matriz de bytes ( ) e, em seguida, desserializado ByteArrayOutputStream
por meio do fluxo de entrada da matriz de bytes ( ) para obter uma cópia totalmente nova do objeto.ByteArrayInputStream
cópia zero
-
Princípio de cópia zero: cópia zero é uma tecnologia de otimização usada para reduzir ou evitar cópias desnecessárias de dados durante a transmissão de dados. Em Java, os métodos comuns de cópia zero incluem o uso de arquivos mapeados em memória e NIO.
-
Cópia zero: Cópia zero é uma tecnologia de otimização usada para reduzir ou evitar operações desnecessárias de cópia de dados durante a transmissão de dados. Ele melhora a eficiência e o desempenho da transferência de dados, transferindo dados diretamente de um espaço de endereço para outro, sem copiar no meio.
Em Java, a cópia zero geralmente é usada para lidar com operações de E/S, como transferência de arquivos, transferência de rede, etc. Seu princípio é utilizar as características do sistema operacional para acessar diretamente a memória onde os dados estão localizados através da memória compartilhada (arquivos mapeados em memória) ou utilizar a tecnologia DMA (Direct Memory Access), reduzindo assim a cópia de dados entre o modo kernel e o modo kernel. modo de usuário.
A forma específica de implementar cópia zero depende do cenário e da API utilizada. A seguir estão duas implementações comuns de cópia zero:
- Arquivos mapeados na memória: use FileChannel e MappedByteBuffer para mapear arquivos diretamente na memória para obter um efeito de cópia zero. O conteúdo dos arquivos pode ser manipulado diretamente na memória, evitando a cópia de dados durante a leitura e gravação.
- Transmissão de rede com cópia zero: usando bibliotecas NIO (E/S sem bloqueio), como SocketChannel, combinadas com ByteBuffer, a transmissão de rede com cópia zero pode ser alcançada. Os dados podem ser lidos diretamente do buffer de rede para o buffer de memória direto do aplicativo ou gravados diretamente do buffer de memória para a rede, evitando a cópia de dados entre o modo de usuário e o modo kernel.
O uso da tecnologia de cópia zero pode melhorar significativamente a eficiência da transmissão de dados e reduzir a carga de trabalho da CPU. Especialmente em transmissão de dados em grande escala e ambientes de alta simultaneidade, a melhoria de desempenho é muito significativa.
- Arquivos mapeados na memória: ao mapear arquivos diretamente na memória, você pode evitar a cópia de dados entre o modo de usuário e o modo kernel. Especificamente, ele pode ser usado
FileChannel
e implementado, e osMappedByteBuffer
métodos relacionados incluemmap()
,,get()
etc.put()
Aqui está um exemplo:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class ZeroCopyMemoryMappedFileExample {
public static void main(String[] args) throws IOException {
File file = new File("data.txt");
String content = "This is the content to be written.";
// 写入数据
try (FileChannel channel = new RandomAccessFile(file, "rw").getChannel()) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, content.length());
buffer.put(content.getBytes());
}
// 读取数据
try (FileChannel channel = new FileInputStream(file).getChannel()) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
byte[] data = new byte[(int) channel.size()];
buffer.get(data);
System.out.println(new String(data));
}
}
}
No exemplo acima, um objeto de arquivo file
e o conteúdo a ser gravado são criados primeiro content
. Use FileChannel
para abrir um canal de arquivo e use map()
o método para mapear parte ou todo o conteúdo do arquivo em um buffer na memória MappedByteBuffer
. Em seguida, put()
o conteúdo é gravado no buffer por meio do método. Em seguida, reabra o canal do arquivo e use map()
o método para mapear todo o conteúdo do arquivo para outro buffer na memória MappedByteBuffer
. Finalmente, get()
o conteúdo é lido do buffer em uma matriz de bytes por meio do método e a string é gerada.