Java几种深拷贝方式比较

Java的深度拷贝分为克隆(实现Java的Clone接口)和序列化(实现Java的Serializable接口)两种,由于序列化有不同的方式,下面分析一下每种的注意事项和性能对比。

一、继承Cloneable接口

   可以使用Java提供的Clone方式进行对象的拷贝,其性能是最佳的,甚至高过new 关键字。使用new关键字创建对象,如果是第一次创建则会经历类加载机制的双亲委派(加载、验证、准备、解析、初始化)。即使非第一次创建也会经历(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等)等过程。

    继承Cloneable接口,重写Object的clone方法。如下:

public class DeepCopyEntity implements Cloneable {
 
    @Override
    protected DeepCopyEntity clone() {
        try {
            return (DeepCopyEntity)super.clone();
        } catch (CloneNotSupportedException e) {
            log.info("没有实现克隆接口");
            return null;
        }
    }
}

但是我们在使用的时候,需要每个对象都编写这样的代码。可以优化为继承自类似下面的 CloneSupport<T> 类(前体是没有继承其他的类):

public class CloneSupport<T> implements Cloneable {
	
	@SuppressWarnings("unchecked")
	@Override
	public T clone() {
		try {
			return (T) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new CloneRuntimeException(e);
		}
	}
	
}

 但是即使是克隆之后的对象也是浅拷贝。即对象的属性如果是非基本数据类型和String的情况下,新老对象的对象属性的内存地址任然相同,则任何一个对象改变其值之后,另一个对象的值也就是改变了,这很多时候可能是我们不想要的。那么需要进行深度的拷贝。则需要其属性对象的类也继承自Clone接口,并且重新clone方法。如下(是我项目中使用的):

public class PurOrderSkuBO implements Serializable, Cloneable {
 
    @Override
    public PurOrderSkuBO clone() {
        try {
            final PurOrderSkuBO clone = (PurOrderSkuBO) super.clone();
            clone.purOrderSkuDTO = purOrderSkuDTO.clone();
            clone.productList = productList.stream().map(PurOrderItemBO::clone).collect(Collectors.toList());
            return clone;
        } catch (CloneNotSupportedException e) {
            return new PurOrderSkuBO();
        }
    }
 
    private PurOrderSkuDTO purOrderSkuDTO;
    
    private List<PurOrderItemBO> productList;
}

一个具体例子:IBook是课程接口,Physics和Mathematics是两个课程实现类。StudentCourseInShallowCopy和StudentCourseInDeepCopy是持有IBook变量的两个类,这两个类分别实现浅拷贝和深拷贝。

IBook.java

package course;


/**
 * @copyright 2003-2023
 * @package   course
 * @file      IBook.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     课程接口。
 * @history
 */
public interface IBook {
    
    void setBookName(String name);
    
    String bookName();
    
    void setBookISBN(String ISBN);
    
    String bookISBN();
    
    void setBookPrice(double price);
    
    double bookPrice();
}

Physics和Mathematics

package course;


import java.rmi.server.ExportException;

/**
 * @copyright 2003-2023
 * @package   course
 * @file      Physics.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     物理课程。继承Cloneable接口,IBook接口。clone方法对所有变量(基础数据)进行拷贝。
 * @history
 */
public class Physics implements Cloneable, IBook {
    
    public Physics(String name, String ISBN, double price) {
        this.name = name;
        this.ISBN = ISBN;
        this.price = price;
    }

    @Override
    protected Physics clone() {
        Physics cloned = null;
        
        try {
            cloned = (Physics) super.clone();
            cloned.name = this.name;
            cloned.ISBN = this.ISBN;
            cloned.price = this.price;
        } catch (CloneNotSupportedException exception) {
            exception.printStackTrace();
        } finally {
            return cloned;
        }
    }

    @Override
    public void setBookName(String name) {
        this.name = name;
    }

    @Override
    public String bookName() {
        return name;
    }

    @Override
    public void setBookISBN(String ISBN) {
        this.ISBN = ISBN;
    }

    @Override
    public String bookISBN() {
        return ISBN;
    }

    @Override
    public void setBookPrice(double price) {
        this.price = price;
    }

    @Override
    public double bookPrice() {
        return price;
    }

    private String name;

    private String ISBN;

    private double price;
}

package course;


/**
 * @copyright 2003-2023
 * @package   course
 * @file      Mathematics.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     数学课程。继承Cloneable接口,IBook接口。clone方法对所有变量(基础数据)进行拷贝。
 * @history
 */
public class Mathematics implements Cloneable, IBook {
    
    public Mathematics(String name, String ISBN, double price) {
        this.name = name;
        this.ISBN = ISBN;
        this.price = price;
    }

    @Override
    protected Mathematics clone() {
        Mathematics cloned = null;
        
        try {
            cloned = (Mathematics) super.clone();
            cloned.name = this.name;
            cloned.ISBN = this.ISBN;
            cloned.price = this.price;
        } catch (CloneNotSupportedException exception) {
            exception.printStackTrace();
        } finally {
            return cloned;
        }
    }

    @Override
    public void setBookName(String name) {
        this.name = name;
    }

    @Override
    public String bookName() {
        return name;
    }

    @Override
    public void setBookISBN(String ISBN) {
        this.ISBN = ISBN;
    }

    @Override
    public String bookISBN() {
        return ISBN;
    }

    @Override
    public void setBookPrice(double price) {
        this.price = price;
    }

    @Override
    public double bookPrice() {
        return price;
    }

    private String name;
    
    private String ISBN;
    
    private double price;
}

StudentCourseInShallowCopy和StudentCourseInDeepCopy

package course;


/**
 * @copyright 2003-2023
 * @package   course
 * @file      StudentCourseInShallowCopy.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     测试拷贝。继承Cloneable接口,实现clone方法,调用父类的clone方法,没有实现深拷贝。
 * @history
 */
public class StudentCourseInShallowCopy implements Cloneable {
    
    public StudentCourseInShallowCopy(IBook course) {
        this.course = course;
    }

    @Override
    protected StudentCourseInShallowCopy clone() {
        StudentCourseInShallowCopy studentCourse = null;
        
        try {
            // 调用父类的clone方法并进行类型转换,子类的引用变量仍指向同一个类实例。
            studentCourse = (StudentCourseInShallowCopy) super.clone();
        } catch (CloneNotSupportedException exception) {
            exception.printStackTrace();
        } finally {
            return studentCourse; 
        }
    }

    public IBook course() {
        return course;
    }
    
    public void setTestField(String field) {
        this.testField = field;
    }
    
    public String testField() {
        return testField;
    }
    
    public void setMoney(double money) {
        this.money = money;
    }
    
    public double money() {
        return money;
    }
    
    private IBook course;
    
    private String testField;
    
    private double money;
}

package course;


/**
 * @copyright 2003-2023
 * @package   course
 * @file      StudentCourseInDeepClone.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     深拷贝。
 * @history
 */
public class StudentCourseInDeepClone implements Cloneable {

    public StudentCourseInDeepClone(IBook course) {
        this.course = course;
    }
    
    /**
     * @class   StudentCourseInDeepClone
     * @date    2023-12-06
     * @author  qiao wei
     * @version 1.0
     * @brief   深拷贝。对引用变量course进行深拷贝。
     * @param   
     * @return  
     * @throws
     */
    @Override
    protected StudentCourseInDeepClone clone() {
        StudentCourseInDeepClone studentCourse = null;
        
        try {
            studentCourse = (StudentCourseInDeepClone) super.clone();
            
            String name = course.bookName();
            String ISBN = course.bookISBN();
            double price = course.bookPrice();
            
            if (Physics.class == course.getClass()) {
                System.out.println("Physics 实例:");
                studentCourse.course = new Physics(name, ISBN, price);
            }
            
            if (Mathematics.class == course.getClass()) {
                System.out.println("Mathematics 实例:");
                studentCourse.course = new Mathematics(name, ISBN, price);
            }
        } catch (CloneNotSupportedException exception) {
            exception.printStackTrace();
        } finally {
            return studentCourse;
        }
    }

    public IBook course() {
        return course;
    }

    public void setTestField(String field) {
        this.testField = field;
    }

    public String testField() {
        return testField;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public double money() {
        return money;
    }

    private IBook course;

    private String testField;

    private double money;
}

测试类MainClass:

package course;


/**
 * @copyright 2003-2023
 * @package   course
 * @file      MainClass.java
 * @date      2023-12-04
 * @author    qiao wei
 * @version   1.0
 * @brief     
 * @history
 */
public class MainClass {

    public static void main(String[] args) {
        test03();
    }
    
    /**
     * @class   Test01
     * @date    2023-12-04
     * @author  qiao wei
     * @version 1.0
     * @brief   测试引用类型变量。在浅拷贝前后修改引用类型变量,使得course01和course02的引用类型变量均发生改变。                       
     * @param   
     * @return  
     * @throws
     */
    private static void test01() {
        StudentCourseInShallowCopy course01 = new StudentCourseInShallowCopy(
            new Mathematics("数学", "1234", 55)
        );
        course01.setTestField("AAA");
        course01.setMoney(200);

        System.out.println(course01.course().bookPrice());
        System.out.println(course01.testField());
        System.out.println(course01.money());

        StudentCourseInShallowCopy course02 = course01;
        
        course02.course().setBookPrice(333);
        course02.setTestField("---");
        course02.setMoney(3000);

        System.out.println(course01.course().bookPrice());
        System.out.println(course01.testField());
        System.out.println(course01.money());
    }
    
    /**
     * @class   Test01
     * @date    2023-12-04
     * @author  qiao wei
     * @version 1.0
     * @brief   浅拷贝测试引用类型变量。。重写clone方法,course01和course02的引用类型变量没有数据分离。
     * @param   
     * @return  
     * @throws
     */
    private static void test02() {
        StudentCourseInShallowCopy course01 = new StudentCourseInShallowCopy(
            new Mathematics("数学", "1234", 55)
        );
        course01.setTestField("AAA");
        course01.setMoney(200);

        System.out.println(course01.course().bookPrice());
        System.out.println(course01.testField());
        System.out.println(course01.money());

        StudentCourseInShallowCopy course02 = course01.clone();
        
        course02.course().setBookPrice(333);
        course02.setTestField("---");
        course02.setMoney(3000);
        
        IBook book01 = course01.course();
        IBook book02 = course02.course();

        System.out.println(course01.course().bookPrice());
        System.out.println(course01.testField());
        System.out.println(course01.money());
    }
    
    /**
     * @copyright 2003-2023
     * @package   course
     * @file      MainClass.java
     * @date      2023-12-05
     * @author    qiao wei
     * @version   1.0
     * @brief     深拷贝测试引用类型变量。
     * @history
     */
    private static void test03() {
        System.out.println("测试StudentCourseInDeepClone深拷贝。继承Cloneable接口,在clone方法中实现拷贝实例变量。" +
            "变量一和变量二的实例变量完全分开。");
        StudentCourseInDeepClone deepClone01 = new StudentCourseInDeepClone(
            new Mathematics("数学", "1234", 55)
        );
        System.out.println(deepClone01.course().bookPrice());

        StudentCourseInDeepClone deepClone02 = deepClone01.clone();
        IBook book01 = deepClone01.course();
        IBook book02 = deepClone02.course();

        System.out.println("完成深拷贝。变量一和变量二中的实例变量IBook完全分开,修改值不会相互影响。");
        deepClone02.course().setBookPrice(333);
        System.out.println("变量一的实例变量:" + deepClone01.course().bookPrice());
        System.out.println("变量二的实例变量:" + deepClone02.course().bookPrice());
    }
}

 

二、序列化

另一种实现深度拷贝的方式就是序列化,无论是Jdk的序列化还是其他方式的序列化都需要实现自 java.io.Serializable接口,并且设置自己的serialVersionUID,并且保证项目中不能有相同的值(很多开发的时候,基于原来的类copy过来后需要进行修改),如下:

public class DeepCopyEntity implements Cloneable, Serializable {
    private static final long serialVersionUID = 6172279441386879379L;
}

三、序列化拷贝的方式

1 jdk序列化

    jdk序列化只需要基于ObjectOutputStream将原对象流写出去(写入本地磁盘),再基于ObjectInputStream将对象流读回来即可。如下:
 

/**
 * 深层拷贝 - 需要类继承序列化接口
 * @param <T> 对象类型
 * @param obj 原对象
 * @return 深度拷贝的对象
 * @throws Exception
 * @see java.io.Closeable
 * @see AutoCloseable 不用进行关闭
 */
@SuppressWarnings("unchecked")
public static <T> T copyImplSerializable(T obj) throws Exception {
    ByteArrayOutputStream baos = null;
    ObjectOutputStream oos = null;
 
    ByteArrayInputStream bais = null;
    ObjectInputStream ois = null;
 
    Object o = null;
    //如果子类没有继承该接口,这一步会报错
    try {
        baos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        bais = new ByteArrayInputStream(baos.toByteArray());
        ois = new ObjectInputStream(bais);
 
        o = ois.readObject();
        return (T) o;
    } catch (Exception e) {
        throw new Exception("对象中包含没有继承序列化的对象");
    }
}

优点:不需要像克隆和new一样单独开发,缺点:性能比较差

2 kyro序列化

    kyro需要单独引入maven依赖,如:

<dependency>
   <groupId>com.esotericsoftware</groupId>
   <artifactId>kryo</artifactId>
   <version>5.0.0-RC9</version>
</dependency>

使用时需要创建 Kryo对象【 Kryo kryo = new Kryo(); 】,只是该对象是非线程安全的,所有如果在项目中使用时,最好放到ThreadLocal中进行创建。使用就比较简单了:

public static <T> T copyByKryo(T source){
    return kryo.copy(source);
}

优点:性能较高, 缺点:需要单独引入maven,性能比new 和clone的低一点

3 Json序列化

    项目上使用Json 进行 redis、rpc调用(如 Spring Cloud Feign) 进行序列化和反序列化是比较常用的,但是如果仅仅是本地深度拷贝,则使用该方式性能是最差的。可以在下面进行比较,各种json框架的序列化方式都差不多。

四、性能对比

    创建一个50个字段的对象,并使用不同的深度拷贝方式,创建对象N多遍。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeepCopyEntity implements Cloneable, Serializable {
 
    /**
     * 序列化标识
     */
    private static final long serialVersionUID = 6172279441386879379L;
 
    @Override
    protected DeepCopyEntity clone() {
        try {
            return (DeepCopyEntity)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
 
    private String id;
 
    private String field1;
    private String field2;
    private String field3;
    private String field4;
    private String field5;
    private String field6;
    private String field7;
    private String field8;
    private String field9;
    private String field10;
    private String field11;
    private String field12;
    private String field13;
    private String field14;
    private String field15;
    private String field16;
    private String field17;
    private String field18;
    private String field19;
    private String field20;
    private String field21;
    private String field22;
    private String field23;
    private String field24;
    private String field25;
    private String field26;
    private String field27;
    private String field28;
    private String field29;
    private String field30;
    private String field31;
    private String field32;
    private String field33;
    private String field34;
    private String field35;
    private String field36;
    private String field37;
    private String field38;
    private String field39;
    private String field40;
    private String field41;
    private String field42;
    private String field43;
    private String field44;
    private String field45;
    private String field46;
    private String field47;
    private String field48;
    private String field49;
    private String field50;
}

package com.kevin.deepcopy;
 
import com.esotericsoftware.kryo.Kryo;
import net.sf.json.JSONObject;
import org.springframework.util.StopWatch;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
/**
 *  深度拷贝类型        循环次数[1000]      循环次数[10000]      循环次数[1000000]
 *  new               5 ms               14 ms              133 ms
 *
 *  Cloneable:        < 1 ms             7 ms               88 ms
 *
 *  Jdk序列化:         272 ms             1589 ms            66190 ms
 *
 *  Kryo序列化:        95 ms              123 ms             2438 ms
 *
 *  Json序列化:        1203 ms            3746 ms            163512 ms
 *
 *  总结: 1)、序列化性能   Clone > new > Kryo序列化 > Jdk序列化 > Json(各种Json类似)序列化
 *        2)、Clone深拷贝性能最高,但是如果属性中有特定的对象字段,则需要自己编写代码
 *        3)、new 性能仅次于Clone,因为需要执行Jvm过程(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等),并且主要是每个对象需要单独编写代码,当然也不建议使用反射
 *        4)、kryo 性能较高,并且不需要单独的开发,  若对性能不是特别高,可以考虑使用.(kryo是非线程安全的,项目中使用时可以放入ThreadLocal中)
 *        5)、Jdk序列化和Json序列化,性能太低,高性能项目不建议使用
 *
 *  总结的总结: 如果性能要求特别高(或者对象结构层次不深),可以使用Clone方式;否则可以考虑使用 Kryo序列化和反序列化实现对象深拷贝
 *
 * @author kevin
 * @date 2020/9/27 13:45
 * @since 1.0.0
 */
public class DeepCopyTest {
 
    /**
     * 循环的次数
     */
    private static final int LOOP = 1000;
 
    private static Kryo kryo = new Kryo();
 
    public static void main(String[] args) throws Exception {
        DeepCopyEntity demo = getInit();
 
        StopWatch stopWatch = new StopWatch("测试深拷贝");
        stopWatch.start();
        for (int i = 0; i < LOOP; i++) {
//            DeepCopyEntity deep = newObject(demo);
            final DeepCopyEntity deep = demo.clone();
//            final DeepCopyEntity deepCopyEntity = copyImplSerializable(demo);
//            final DeepCopyEntity deepCopyEntity = copyByKryo(demo);
//            final DeepCopyEntity deepCopyEntity1 = copyByJson(demo);
        }
        stopWatch.stop();
 
        System.out.println(stopWatch.prettyPrint());
    }
 
    /**
     * 深层拷贝 - 需要net.sf.json.JSONObject
     * @param <T>
     * @param obj
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyByJson(T obj) throws Exception {
        return (T) JSONObject.toBean(JSONObject.fromObject(obj),obj.getClass());
    }
 
    /**
     *
     * @param source
     * @return
     */
    public static DeepCopyEntity copyByKryo(DeepCopyEntity source){
 
        return kryo.copy(source);
    }
 
    /**
     * 深层拷贝 - 需要类继承序列化接口
     * @param <T>
     * @param obj
     * @return
     * @throws Exception
     * @see java.io.Closeable
     * @see AutoCloseable 不用进行关闭
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyImplSerializable(T obj) throws Exception {
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
 
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
 
        Object o = null;
        //如果子类没有继承该接口,这一步会报错
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            bais = new ByteArrayInputStream(baos.toByteArray());
            ois = new ObjectInputStream(bais);
 
            o = ois.readObject();
            return (T) o;
        } catch (Exception e) {
            throw new Exception("对象中包含没有继承序列化的对象");
        }
    }
 
 
    private static DeepCopyEntity newObject(DeepCopyEntity demo) {
        final DeepCopyEntity deepCopyEntity = new DeepCopyEntity();
        deepCopyEntity.setId(demo.getId());
        deepCopyEntity.setField1(demo.getField1());
        deepCopyEntity.setField2(demo.getField2());
        deepCopyEntity.setField3(demo.getField1());
        deepCopyEntity.setField4(demo.getField1());
        deepCopyEntity.setField5(demo.getField1());
        deepCopyEntity.setField6(demo.getField1());
        deepCopyEntity.setField7(demo.getField1());
        deepCopyEntity.setField8(demo.getField1());
        deepCopyEntity.setField9(demo.getField1());
        deepCopyEntity.setField10(demo.getField1());
        deepCopyEntity.setField11(demo.getField1());
        deepCopyEntity.setField12(demo.getField1());
        deepCopyEntity.setField13(demo.getField1());
        deepCopyEntity.setField14(demo.getField1());
        deepCopyEntity.setField15(demo.getField1());
        deepCopyEntity.setField16(demo.getField1());
        deepCopyEntity.setField17(demo.getField1());
        deepCopyEntity.setField18(demo.getField1());
        deepCopyEntity.setField19(demo.getField1());
        deepCopyEntity.setField20(demo.getField1());
        deepCopyEntity.setField21(demo.getField1());
        deepCopyEntity.setField22(demo.getField1());
        deepCopyEntity.setField23(demo.getField1());
        deepCopyEntity.setField24(demo.getField1());
        deepCopyEntity.setField25(demo.getField1());
        deepCopyEntity.setField26(demo.getField1());
        deepCopyEntity.setField27(demo.getField1());
        deepCopyEntity.setField28(demo.getField1());
        deepCopyEntity.setField29(demo.getField1());
        deepCopyEntity.setField30(demo.getField1());
        deepCopyEntity.setField31(demo.getField1());
        deepCopyEntity.setField32(demo.getField1());
        deepCopyEntity.setField33(demo.getField1());
        deepCopyEntity.setField34(demo.getField1());
        deepCopyEntity.setField35(demo.getField1());
        deepCopyEntity.setField36(demo.getField1());
        deepCopyEntity.setField37(demo.getField1());
        deepCopyEntity.setField38(demo.getField1());
        deepCopyEntity.setField39(demo.getField1());
        deepCopyEntity.setField40(demo.getField1());
        deepCopyEntity.setField41(demo.getField1());
        deepCopyEntity.setField42(demo.getField1());
        deepCopyEntity.setField43(demo.getField1());
        deepCopyEntity.setField44(demo.getField1());
        deepCopyEntity.setField45(demo.getField1());
        deepCopyEntity.setField46(demo.getField1());
        deepCopyEntity.setField47(demo.getField1());
        deepCopyEntity.setField48(demo.getField1());
        deepCopyEntity.setField49(demo.getField1());
        deepCopyEntity.setField50(demo.getField1());
 
        return deepCopyEntity;
    }
 
 
    /**
     * 获取初始化值
     * @return demo对象
     */
    private static DeepCopyEntity getInit() {
        final DeepCopyEntity deepCopyEntity = new DeepCopyEntity();
        deepCopyEntity.setId("测试字段进来撒个是个是个复活节快乐时刻六公里按时交付格拉斯可根据ask了接受了嘎嘎健康金克拉是个零售价格克拉斯关键时刻两个jklsghbld时间噶设立国家级法国设计规划拉萨尽快赶回监考老师的风格就是看来撒骨灰两个据类");
          
        // 省略后面所有字段的设置,都设置一样的字段 ......
 
        return deepCopyEntity;
    }
}

五、总结:


    1)、序列化性能   Clone > new > Kryo序列化 > Jdk序列化 > Json(各种Json类似)序列化
    2)、Clone深拷贝性能最高,但是如果属性中有特定的对象字段,则需要自己编写代码
    3)、new 性能仅次于Clone,因为需要执行Jvm过程(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等),
           并且主要是每个对象需要单独编写代码,当然也不建议使用反射
    4)、kryo 性能较高,并且不需要单独的开发,  若对性能不是特别高,可以考虑使用.
           kryo是非线程安全的,项目中使用时可以放入ThreadLocal中
    5)、Jdk序列化和Json序列化,性能太低,高性能项目不建议使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/228170.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基础50题: 21.实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值.

概述 实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值. 代码 public static void printArray(int[] array) {for (int i 0; i < array.length; i) {System.out.println(array[i] " ");}System.out.println();}public static…

class060 拓扑排序的扩展技巧【算法】

class060 拓扑排序的扩展技巧【算法】 算法讲解060【必备】拓扑排序的扩展技巧 2023-12-7 22:23:02 code1 P4017 最大食物链计数 // 最大食物链计数 // a -> b&#xff0c;代表a在食物链中被b捕食 // 给定一个有向无环图&#xff0c;返回 // 这个图中从最初级动物到最顶…

Atlassian Confluence 模板注入代码执行漏洞风险通告

近期&#xff0c;亚信安全CERT通过监控发现&#xff0c;Atlassian 公司发布了一则安全公告&#xff0c;针对 Confluence 数据中心和 Confluence 服务器存在的远程代码执行漏洞&#xff08;CVE-2023-22522&#xff09;进行了修复。该漏洞涉及 Confluence 页面中的模板注入问题&a…

加载类型库/dll 时出错

软件使用DataSnap数据库ERP管理系统&#xff0c;用户更换操作系统&#xff0c;出现“加载类型库/dll 时出错”信息。 通常思路&#xff0c;从大环境查找&#xff0c;怀疑操作系统中的C运行库出现错误&#xff0c;搜索一翻末果。百度搜索也找不到结果。 通过Dll修复大师、全能修…

ArcMap中构建金字塔详解

1.金字塔 1.1 定义 金字塔可用于改善性能。它们是原始栅格数据集的缩减采样版本&#xff0c;可包含多个缩减采样图层。金字塔的各个连续图层均以 2:1 的比例进行缩减采样。如下图所示。从金字塔的底层开始每四个相邻的像素经过重采样生成一个新的像素&#xff0c;依此重复进行…

【Lidar】Python实现点云CSF布料滤波算法提取地面点

这两天会持续更新一下Python处理点云数据的教程&#xff0c;大家可以点个关注。今天给大家分享一下点云的经典算法&#xff1a;CSF布料模拟算法。 1 CSF算法简介 CSF算法&#xff0c;全称为Cloth Simulation Filtering&#xff0c;是一种基于欧几里得空间中最小生成树思想的聚类…

什么是网站?

这篇文章是我学习网站开发&#xff0c;阶段性总结出来的。可以帮助你 通俗易懂 地更加深刻理解网站的这个玩意。 一&#xff0c;网站和网页的区别&#xff1f; 网站是由一个个网页组成。我们在浏览器上面看到的每一个页面就是网页&#xff0c;这些 相关的 网页组成一个网站。…

【Selenium+Webmagic】基于JAVA语言实现爬取js渲染后的页面,附有代码

事先声明 笔者最近需要查看一些数据&#xff0c;自己挨个找太麻烦了&#xff0c;于是简单的学了一下爬虫。笔者在这里声明&#xff0c;爬的数据只为学术用&#xff0c;没有其他用途&#xff0c;希望来这篇文章学习的同学能抱有同样的目的。 枪本身不坏&#xff0c;坏的是使用枪…

EOCR-CT电流互感器与SR-CT区别简介

电流互感器CT是&#xff08;Current Transformers&#xff09;的缩写&#xff0c;是将一次测的大电流&#xff0c;按比列变为适合通过测量仪表或保护装置的变换设备。 EOCR外部电流互感器3CT和SR-CT是专为保护大负载的组合使用&#xff0c;电流变比100&#xff1a;5&#xff0…

如何部署自己的服务渲染页面为Pdf文档

前言 相信大家都觉得官方发布的文档生成模块https://docs.mendix.com/appstore/modules/document-generation/很有用&#xff0c;它能把Mendix页面像素级导出到Pdf文件中&#xff0c;这对于归档等业务非常有价值。但部署依赖公有云提供的渲染服务&#xff0c;而中国本土用户对…

常用API(一)

API(全称 Application Programming Interface&#xff1a;应用程序编程接口) 就是别人写好的一些程序&#xff0c;给我们直接拿去调用即可解决问题的。 包 什么是包&#xff1f; 包是用来分门别类的管理各种不同程序的&#xff0c;类似于文件夹&#xff0c;建包有利于程序的管…

python数据分析总结(pandas)

目录 前言 df导入数据 df基本增删改查 数据清洗 ​编辑 索引操作 数据统计 行列操作 ​编辑 df->types 数据格式化 ​编辑 日期数据处理 前言 此篇文章为个人python数据分析学习总结&#xff0c;总结内容大都为表格和结构图方式&#xff0c;仅供参考。 df导入数…

在线教育小程序正在成为教育行业的新生力量

教育数字化转型是目前教育领域的一个热门话题&#xff0c;那么到底什么是教育数字化转型&#xff1f;如何做好教育数字化转型&#xff1f; 教育数字化转型是利用信息技术和数字工具改变和优化教育的过程。主要特征包括技术整合、在线学习、个性化学习、大数据分析、云计算、虚拟…

视频封面提取:精准截图,如何从指定时长中提取某一帧图片

在视频制作和分享过程中&#xff0c;一个有吸引力的封面或截图往往能吸引更多的观众点击观看。有时候要在特定的时间段内从视频中提取一帧作为封面或截图。如果每个视频都手动提取的话就会耗费很长时间&#xff0c;那么如何智化能批量提取呢&#xff1f;现在一起来看下云炫AI智…

VUE2+THREE.JS 按照行动轨迹移动人物模型并相机视角跟随人物

按照行动轨迹移动人物模型并相机视角跟随人物 1. 初始化加载模型2. 开始移动模型3. 人物模型启动4. 暂停模型移动5. 重置模型位置6. 切换区域动画7. 摄像机追踪模型8. 移动模型位置9.动画执行 人物按照上一篇博客所设定的关键点位置&#xff0c;匀速移动 1. 初始化加载模型 //…

选择护眼台灯的标准,符合国家最高标准的护眼台灯推荐

据中国国家卫生健康委员会发布的报告&#xff0c;2020年全国青少年近视率为53.6%&#xff0c;其中&#xff0c;小学生近视率为38.1%&#xff0c;初中生近视率为71.6%&#xff0c;高中生近视率为81.0%。这意味着中国青少年中&#xff0c;大多数人都存在不同程度的近视问题&#…

【Java用法】Lombok中@SneakyThrows注解的使用方法和作用

Lombok中SneakyThrows注解的使用方法和作用 一、SneakyThrows的作用二、SneakyThrows注解原理 一、SneakyThrows的作用 普通Exception类,也就是我们常说的受检异常或者Checked Exception会强制要求抛出它的方法声明throws&#xff0c;调用者必须显示的去处理这个异常。设计的目…

如何使用eXtplorer+cpolar内网穿透搭建个人云存储实现公网访问

文章目录 1. 前言2. eXtplorer网站搭建2.1 eXtplorer下载和安装2.2 eXtplorer网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1. 前言 通过互联网传输文件&#xff0c;是互联网最重要的应用之一&#xff0c;无论是…

Mybatis源码解析2:全局配置

Mybatis源码解析2&#xff1a;全局配置 1.项目结构2. 源码分析2.1.SqlSessionFactoryBuilder#build(java.io.InputStream)2.2 XMLConfigBuilder构造器2.3 解析XMLConfigBuilder#parse2.4 解析配置 XMLConfigBuilder#parseConfiguration 1.项目结构 源码地址&#xff1a; 项目结…

MySQL-日期时间函数详解及练习

目录 3.1 返回当前日期 3.2 提取日期部分 3.3 增加或减去时间 3.4 格式化时期或时间 3.5 牛客练习题 3.1 返回当前日期 1. CURDATE() 或 CURRENT_DATE() | 返回当前日期 select curdate();select current_date(); 结果&#xff1a; 2. CURTIME() 或 CURRENT_TIME() | 返…