欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?對象深拷貝工具類的實現(xiàn)

 更新時間:2022年07月20日 09:43:02   作者:zyqok  
本文主要介紹了Java?對象深拷貝工具類的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1. 使用場景

我們在Java編碼中,有時候可能會經(jīng)常遇到對象拷貝的場景。

1.1 場景一

當(dāng)我們更新一個對象的時候,如果要記錄對象屬性的前后變化,那么在更新對象之前,我們應(yīng)該首先將對象拷貝暫存起來,且這個時候的拷貝一定是深拷貝(內(nèi)存地址不同的兩個對象),因為Java存在對象引用,將一個對象賦值給另外一個對象,他是淺拷貝的(兩個不同變量名,但實際內(nèi)存地址一樣的兩個對象)的話,也就是說當(dāng)我們?nèi)ジ峦瓿蓪傩灾档臅r候,其實是設(shè)置的同一個對象,那么這個時候就會導(dǎo)致更新前后無變化的情況。

1.2 場景二

又比如,當(dāng)我們從數(shù)據(jù)庫中查詢出一個實體對象的時候,這個對象往往對應(yīng)的是和數(shù)據(jù)庫字段 一 一 對應(yīng)的實體,但這個實體往往又不會滿足我們的頁面需求。比如我們查詢學(xué)生課程表的時候,我們數(shù)據(jù)庫往往只是存的一個 id 對應(yīng)關(guān)系,但頁面往往是展示的名稱,那么這個名稱字段我們的表對應(yīng)的實體類是不應(yīng)該存在的,這個時候,我們應(yīng)該創(chuàng)建一個對應(yīng)的 VO (View Object)類,把額外的需要字段定義在這里面,同時可以去繼承原表實體類,這樣的一個對象就滿足了。到時候,我們把原表實體對應(yīng)的字段值拷貝到 VO 對象中后,再設(shè)置其他表額外的字段,這樣就可以返回給前端頁面進(jìn)行展示了。

綜上:所以,對象拷貝還是挺有用途的,但如果我們拷貝對象的時候,去一個一個字段挨著進(jìn)行取值拷貝的話,難免代碼看上去不夠優(yōu)雅。于是,搞一個對象拷貝工具類還是很有必要的。

2. Spring 中的對象拷貝

其實,在 Spring 中,也有類似的拷貝方法。他就是位于 org.springframework.beans.BeanUtils 工具類中的 copyProperties 方法。下面就簡單演示下這個方法的使用效果。

為了方便演示,我們創(chuàng)建兩個有部分相同屬性的對象 Cat 類和 Dog 類(都有 name 和 age 字段)。

Cat 類如下:

@Data
public class Cat {
 
    private String name;
    private Integer age;
    private String color;
 
}

Dog 類如下:

@Data
public class Dog {
 
    private String name;
    private Integer age;
    private String address;
 
}

測試代碼:

import org.springframework.beans.BeanUtils;
 
public class Test {
 
    public static void main(String[] args) {
        // 實例化一個 Cat 對象并賦值屬性
        Cat cat = new Cat();
        cat.setName("tom");
        cat.setAge(5);
 
        // 實例化一個 Dog 對象
        Dog dog = new Dog();
 
        // 將 cat 對象中的屬性值拷貝至 dog 中
        BeanUtils.copyProperties(cat, dog);
        System.out.println("拷貝后:" + dog);
    }
 
}

 測試效果:

可以看到,相同的 name 和 age 已經(jīng)復(fù)制過去了。

3. 本工具類中的對象拷貝

上面我們演示了 Spring 下 BeanUtils 工具類中的對象屬性拷貝,雖然他也可以成功拷貝對象中的屬性,但對于我個人來說,還是有點不適應(yīng)。

首先,Spring 去拷貝一個對象屬性的時候,需要先創(chuàng)建好另外一個對象,然后再進(jìn)行屬性拷貝,這一步對象創(chuàng)建是明顯可以放到工具方法中去的。

其次,如果只是本類復(fù)制的話,參數(shù)只需要傳一個源對象的實例就應(yīng)該夠了,而Spring就算拷貝本類,也得傳兩個參數(shù),即源實例對象和目標(biāo)實例對象。

另外,Spring 的對象拷貝不支持批量拷貝,比如我們將 List<Cat> 屬性拷貝后,生成一個 List<Dog> 中,只能自己循環(huán)去拷貝生成每個 Dog,然后添加到 List<Dog> 中。

于是,敝人針對個人習(xí)慣,編寫了一個適合自己的編碼習(xí)慣的對象拷貝工具類 BeanUtils(類名還是參照的 Spring),具體使用效果如下。

下面先做效果演示,工具類源碼放在文章最后。

3.1 拷貝對象本身(單個)

比如,我們想復(fù)制一個對象本身(如 cat),那么直接使用下面這個方法就可以了。

Cat newCat = BeanUtils.copy(cat);

測試代碼:

 測試效果:

從測試結(jié)果我們可以看到,源對象和復(fù)制對象的每個字段值已經(jīng)拷貝過去了,但兩個對象的內(nèi)存 hashCode 并不相同,說明并不是同一個對象,也就說我們是進(jìn)行深拷貝的,兩個對象是互不影響的。

另外,我們這個工具類不但支持類對象本身屬性拷貝,連父類屬性拷貝也是支持的。

比如,Cat類去繼承下面這個 Animal 類:

@Data
public class Animal {
 
    private Integer price;
    private Date birth;
 
}
@Data
public class Cat extends Animal {
 
    private String name;
    private Integer age;
    private String color;
 
}

我們再試試測試一下:

測試效果:

可以看到,我們的父類屬性字段值也確實復(fù)制成功了。

3.2 拷貝對象本身(批量)

工具類中不僅支對單個對象拷貝的,對多個對象的拷貝也是支持的。

List<Cat> newCatList = BeanUtils.copyList(catList);

測試代碼:

測試效果:

可以看到,批量屬性復(fù)制也是OK的,拷貝后的集合中每個對象新生成的深拷貝對象。

3.3 拷貝對象屬性至其他類(單個)

上面,我們演示了對象本身復(fù)制的效果,下面繼續(xù)演示下拷貝同名字段到其他屬性的效果。

Dog dog = BeanUtils.copy(cat, Dog.class);

我們把 Cat 中的同名字段屬性拷貝到 Dog 中去,我們讓 Dog 也去繼承下 Anima 類。

@Data
public class Dog extends Animal {
 
    private String name;
    private Integer age;
    private String address;
 
}

測試代碼:

因為拷貝前后是兩個完全不一樣的對象了,所以這里就不再打印地址 hashCode 來進(jìn)行說明是深拷貝了。

測試效果:

可以看到 cat 中的所有相同屬性已經(jīng)拷貝到 dog 中去了。

3.4 拷貝對象屬性至其他類(批量)

同理,我們拷貝對象屬性至其他類也是支持批量操作的。

測試代碼:

測試效果:

可以看到,批量復(fù)制也是OK的。

至此,整個對象的拷貝的四個常用方法已經(jīng)都已經(jīng)支持了。

4. 工具類源碼

下面就是整個工具類的源碼 BeanUtils :

package com.zyq.utils.common;
 
import java.lang.reflect.Field;
import java.util.*;
 
/**
 * @author zyqok
 * @since 2022/07/18
 */
@SuppressWarnings("unused")
public class BeanUtils {
 
    /**
     * 拷貝數(shù)據(jù)到新對象(單個)
     *
     * @param source 源實例對象
     * @return 拷貝后的新實例對象
     */
    public static <T> T copy(T source) {
        if (Objects.isNull(source)) {
            return null;
        }
        Class<?> c = source.getClass();
        List<Field> fields = getFields(c);
        return newInstance(source, c, fields);
    }
 
    /**
     * 拷貝數(shù)據(jù)到新對象(批量)
     *
     * @param sourceList 源實例對象集合
     * @return 拷貝后的新實例對象集合
     */
    public static <T> List<T> copyList(List<T> sourceList) {
        if (Objects.isNull(sourceList) || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        Class<?> c = getClass(sourceList);
        if (Objects.isNull(c)) {
            return Collections.emptyList();
        }
        List<Field> fields = getFields(c);
        List<T> ts = new ArrayList<>();
        for (T t : sourceList) {
            T s = newInstance(t, c, fields);
            if (Objects.nonNull(s)) {
                ts.add(s);
            }
        }
        return ts;
    }
 
    /**
     * 單個深度拷貝
     *
     * @param source 源實例化對象
     * @param target 目標(biāo)對象類(如:User.class)
     * @return 目標(biāo)實例化對象
     */
    public static <T> T copy(Object source, Class<T> target) {
        if (Objects.isNull(source) || Objects.isNull(target)) {
            return null;
        }
        List<Field> sourceFields = getFields(source.getClass());
        List<Field> targetFields = getFields(target);
        T t = null;
        try {
            t = newInstance(source, target, sourceFields, targetFields);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }
 
    /**
     * 批量深度拷貝(如果原集合中有null,則自動忽略)
     *
     * @param sourceList 源實例化對象集合
     * @param target     目標(biāo)對象類(如:User.class)
     * @return 目標(biāo)實例化對象集合
     */
    public static <T, K> List<K> copyList(List<T> sourceList, Class<K> target) {
        if (Objects.isNull(sourceList) || sourceList.isEmpty() || Objects.isNull(target)) {
            return Collections.emptyList();
        }
        Class<?> c = getClass(sourceList);
        if (Objects.isNull(c)) {
            return Collections.emptyList();
        }
        List<Field> sourceFields = getFields(c);
        List<Field> targetFields = getFields(target);
        List<K> ks = new ArrayList<>();
        for (T t : sourceList) {
            if (Objects.nonNull(t)) {
                try {
                    K k = newInstance(t, target, sourceFields, targetFields);
                    ks.add(k);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return ks;
    }
 
    /**
     * 獲取List集合中的類名
     *
     * @param list 對象集合
     * @return 類名
     */
    private static <T> Class<?> getClass(List<T> list) {
        for (T t : list) {
            if (Objects.nonNull(t)) {
                return t.getClass();
            }
        }
        return null;
    }
 
    /**
     * 實例化同源對象
     *
     * @param source 源對象
     * @param c      源對象類名
     * @param fields 源對象屬性集合
     * @return 同源新對象
     */
    @SuppressWarnings("unchecked")
    private static <T> T newInstance(T source, Class<?> c, List<Field> fields) {
        T t = null;
        try {
            t = (T) c.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                field.set(t, field.get(source));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }
 
    /**
     * 目標(biāo)實例化對象
     *
     * @param source       原對實例化象
     * @param target       目標(biāo)對象類
     * @param sourceFields 源對象字段集合
     * @param targetFields 目標(biāo)對象屬性字段集合
     * @return 目標(biāo)實例化對象
     */
    private static <T> T newInstance(Object source, Class<T> target, List<Field> sourceFields,
                                     List<Field> targetFields) throws Exception {
        T t = target.newInstance();
        if (targetFields.isEmpty()) {
            return t;
        }
        for (Field field : sourceFields) {
            field.setAccessible(true);
            Object o = field.get(source);
            Field sameField = getSameField(field, targetFields);
            if (Objects.nonNull(sameField)) {
                sameField.setAccessible(true);
                sameField.set(t, o);
            }
        }
        return t;
    }
 
    /**
     * 獲取目標(biāo)對象中同源對象屬性相同的屬性(字段名稱,字段類型一致則判定為相同)
     *
     * @param field  源對象屬性
     * @param fields 目標(biāo)對象屬性集合
     * @return 目標(biāo)對象相同的屬性
     */
    private static Field getSameField(Field field, List<Field> fields) {
        String name = field.getName();
        String type = field.getType().getName();
        for (Field f : fields) {
            if (name.equals(f.getName()) && type.equals(f.getType().getName())) {
                return f;
            }
        }
        return null;
    }
 
    /**
     * 獲取一個類中的所有屬性(包括父類屬性)
     *
     * @param c 類名
     * @return List<Field>
     */
    private static List<Field> getFields(Class<?> c) {
        List<Field> fieldList = new ArrayList<>();
        Field[] fields = c.getDeclaredFields();
        if (fields.length > 0) {
            fieldList.addAll(Arrays.asList(fields));
        }
        return getSuperClassFields(c, fieldList);
    }
 
    /**
     * 遞歸獲取父類屬性
     *
     * @param o         類名
     * @param allFields 外層定義的所有屬性集合
     * @return 父類所有屬性
     */
    private static List<Field> getSuperClassFields(Class<?> o, List<Field> allFields) {
        Class<?> superclass = o.getSuperclass();
        if (Objects.isNull(superclass) || Object.class.getName().equals(superclass.getName())) {
            return allFields;
        }
        Field[] fields = superclass.getDeclaredFields();
        if (fields.length == 0) {
            return allFields;
        }
        allFields.addAll(Arrays.asList(fields));
        return getSuperClassFields(superclass, allFields);
    }
 
}

到此這篇關(guān)于Java 對象深拷貝工具類的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java 對象深拷貝工具類的實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JFormDesigner(IDEA)下載方法

    JFormDesigner(IDEA)下載方法

    JFormDesigner是一種Java Swing GUI設(shè)計工具,可快速創(chuàng)建用戶界面,支持多種布局管理器,如GridBagLayout、SpringLayout等,本文給大家介紹JFormDesigner(IDEA)下載方法,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • Mybatis詳細(xì)對比一級緩存與二級緩存

    Mybatis詳細(xì)對比一級緩存與二級緩存

    MyBatis 包含一個非常強(qiáng)大的查詢緩存特性,它可以非常方便地配置和定制,緩存可以極大的提升查詢效率。MyBatis中默認(rèn)定義了兩級緩存,分別是一級緩存和二級緩存
    2022-10-10
  • 基于<aop:aspect>與<aop:advisor>的區(qū)別

    基于<aop:aspect>與<aop:advisor>的區(qū)別

    這篇文章主要介紹了<aop:aspect>與<aop:advisor>的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 微信公眾號測試賬號自定義菜單的實例代碼

    微信公眾號測試賬號自定義菜單的實例代碼

    這篇文章主要介紹了微信公眾號測試賬號自定義菜單的實例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-02-02
  • 最新評論