Телепортация обратно в город — « 100 случаев строительства фундамента JAVA»
Каталог статей
1. Описание темы
Тема: Клонирование объектов — это передовая технология в Java, которая позволяет получить другой объект, точно такой же, как данный объект.
Ранее были введены три способа реализации глубокого клонирования, а сейчас по этим трем способам клонируется 100 000 объектов, и выводится затраченное время. Таким образом, каждый может иметь интуитивное понимание.
2. Идеи решения проблем — сериализованное клонирование
Создайте еще один класс сотрудников Сотрудник
Определите три переменные-члены для представления: имя сотрудника, возраст
Используйте конструкторы для присвоения им значений.
И предоставьте соответствующий метод get и метод set.
Переопределите метод toString() и метод clone().
3. Подробное объяснение кода
Класс сотрудника:
public class Employee implements Cloneable, Serializable {
private static final long serialVersionUID = 5022956767440380940L;
private String name; // 表示员工的姓名
private int age; // 表示员工的年龄
public Employee(String name, int age) {
// 利用构造方法初始化各个域
this.name = name;
this.age = age;
}
public Employee() {
// 利用构造方法初始化各个域
super();
}
@Override
public String toString() {
// 重写toString()方法
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ", ");
sb.append("年龄:" + age + "\n");
return sb.toString();
}
@Override
protected Employee clone() {
// 使用父类的clone()方法实现深克隆
Employee employee = null;
try {
employee = (Employee) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}
тестовый класс
public class Test {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<Employee>();// 创建列表保存对象
Employee employee = new Employee("小虚竹", 25);// 创建Employee类的对象
long currentTime = System.currentTimeMillis();// 获得当前系统时间
for (int i = 0; i < 100000; i++) {
employees.add(employee.clone());// 使用克隆方式获得对象
}
System.out.println("clone()方法克隆花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
currentTime = System.currentTimeMillis();// 获得当前时间
for (int i = 0; i < 100000; i++) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();// 创建字节数组输出流
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(baos);// 创建对象输出流
out.writeObject(employee);// 将对象写入到输出流中
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();// 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 获得字节数组输出流内容
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream in = null;
try {
in = new ObjectInputStream(bais);// 创建对象输入流
employees.add((Employee) in.readObject());// 读取对象
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();// 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
System.out.println("序列化花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
currentTime = System.currentTimeMillis();// 获得当前时间
for (int i = 0; i < 100000; i++) {
Employee employee2 = new Employee();
BeanUtils.copyProperties(employee,employee2);
}
System.out.println("BeanUtils.copyProperties花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
}
}
Сравнение производительности родной сериализации и сериализации Kryo
Родная сериализация:
package com.xiaoxuzhu;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* Description: 原生序列化
*
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改记录:
* 修改后版本 修改人 修改日期 修改内容
* 2022/5/20.1 xiaoxuzhu 2022/5/20 Create
* </pre>
* @date 2022/5/20
*/
public class OriginalSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
long start = System.currentTimeMillis();
setSerializableObject();
System.out.println("java原生序列化时间:" + (System.currentTimeMillis() - start) + " ms" );
start = System.currentTimeMillis();
getSerializableObject();
System.out.println("java原生反序列化时间:" + (System.currentTimeMillis() - start) + " ms");
}
public static void setSerializableObject() throws IOException{
FileOutputStream fo = new FileOutputStream("D:/file.bin");
ObjectOutputStream so = new ObjectOutputStream(fo);
Employee employee = null;
for (int i = 0; i < 100000; i++) {
employee = new Employee("小虚竹", 25);// 创建Employee类的对象
so.writeObject(employee);
}
so.writeObject(null);//为了解决EOF异常
so.flush();
so.close();
}
public static void getSerializableObject(){
FileInputStream fi;
try {
fi = new FileInputStream("D:/file.bin");
ObjectInputStream si = new ObjectInputStream(fi);
Employee employee =null;
while((employee=(Employee)si.readObject()) != null){
}
fi.close();
si.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Кайро сериализация:
package com.xiaoxuzhu;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objenesis.strategy.StdInstantiatorStrategy;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
/**
* Description: Kyro序列化
* kryo.KryoException: Buffer underflow.
* @author xiaoxuzhu
* @version 1.0
*
* <pre>
* 修改记录:
* 修改后版本 修改人 修改日期 修改内容
* 2022/5/20.1 xiaoxuzhu 2022/5/20 Create
* </pre>
* @date 2022/5/20
*/
public class KyroSerializable {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
setSerializableObject();
System.out.println("Kryo 序列化时间:" + (System.currentTimeMillis() - start) + " ms" );
start = System.currentTimeMillis();
getSerializableObject();
System.out.println("Kryo 反序列化时间:" + (System.currentTimeMillis() - start) + " ms");
}
public static void setSerializableObject() {
Output output = null;
try {
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.setRegistrationRequired(false);
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
kryo.register(Employee.class);
output = new Output(new FileOutputStream("D:/file1.bin"));
Employee employee = null;
for (int i = 0; i < 100000; i++) {
employee = new Employee("小虚竹", 25);// 创建Employee类的对象
kryo.writeObject(output, employee);
}
output.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
if(output !=null){
output.close();
}
}
}
public static void getSerializableObject(){
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.setRegistrationRequired(false);
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
Input input = null;
try {
input = new Input(new FileInputStream("D:/file1.bin"));
Employee employee =null;
while((employee=kryo.readObject(input, Employee.class)) != null){
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(KryoException e){
}finally {
if(input !=null){
input.close();
}
}
}
}
100 000 результатов теста:
Собственное время сериализации Java: 2762 мс
Собственное время десериализации Java: 3405 мс
Время сериализации Kryo: 575 мс
Время десериализации Kryo: 156 мс
Производительность Kryo более чем в двадцать раз выше, чем у родной сериализации.
в заключение
1. Используйте метод клонирования для клонирования (нецелесообразно):
- 优点:最快的,10万条数据可以控制在100ms内
- 缺点:但这个的使用也是最麻烦的,类要重写clone方法,有引类类型的参数类也要重写clone方法
2. Используйте собственную сериализацию для клонирования (нецелесообразно):
- 优点:序列化不需要每个对象都重写clone方法,同时支持类里的引用类型参数的深克隆
- 缺点:花费的时间最长
3. Используя метод стороннего инструмента BeanUtils.copyProperties, время клонирования умеренное:
- 优点:使用简单方便
- 缺点:只能进行浅克隆,对于引用类型的参数是无法克隆的,只是复制引用,不是克隆值 ,所以要额外处理。
4. Использование сериализации Kryo лучше, чем нативная сериализация
В разработке корпоративных приложений:
-
Простое клонирование объекта (поверхностное клонирование), вы можете использовать BeanUtils.copyProperties
-
Серийное клонирование сложных объектов можно сериализовать с помощью Kryo.
4. Рекомендуемая колонка
«JAVA от нуля к единице» Лекция 4: Основы классов и объектов