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

Java?對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn)

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

1. 使用場(chǎng)景

我們?cè)贘ava編碼中,有時(shí)候可能會(huì)經(jīng)常遇到對(duì)象拷貝的場(chǎng)景。

1.1 場(chǎng)景一

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

1.2 場(chǎng)景二

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

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

2. Spring 中的對(duì)象拷貝

其實(shí),在 Spring 中,也有類(lèi)似的拷貝方法。他就是位于 org.springframework.beans.BeanUtils 工具類(lèi)中的 copyProperties 方法。下面就簡(jiǎn)單演示下這個(gè)方法的使用效果。

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

Cat 類(lèi)如下:

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

Dog 類(lèi)如下:

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

測(cè)試代碼:

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

 測(cè)試效果:

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

3. 本工具類(lèi)中的對(duì)象拷貝

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

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

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

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

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

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

3.1 拷貝對(duì)象本身(單個(gè))

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

Cat newCat = BeanUtils.copy(cat);

測(cè)試代碼:

 測(cè)試效果:

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

另外,我們這個(gè)工具類(lèi)不但支持類(lèi)對(duì)象本身屬性拷貝,連父類(lèi)屬性拷貝也是支持的。

比如,Cat類(lèi)去繼承下面這個(gè) Animal 類(lèi):

@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;
 
}

我們?cè)僭囋嚋y(cè)試一下:

測(cè)試效果:

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

3.2 拷貝對(duì)象本身(批量)

工具類(lèi)中不僅支對(duì)單個(gè)對(duì)象拷貝的,對(duì)多個(gè)對(duì)象的拷貝也是支持的。

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

測(cè)試代碼:

測(cè)試效果:

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

3.3 拷貝對(duì)象屬性至其他類(lèi)(單個(gè))

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

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

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

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

測(cè)試代碼:

因?yàn)榭截惽昂笫莾蓚€(gè)完全不一樣的對(duì)象了,所以這里就不再打印地址 hashCode 來(lái)進(jìn)行說(shuō)明是深拷貝了。

測(cè)試效果:

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

3.4 拷貝對(duì)象屬性至其他類(lèi)(批量)

同理,我們拷貝對(duì)象屬性至其他類(lèi)也是支持批量操作的。

測(cè)試代碼:

測(cè)試效果:

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

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

4. 工具類(lèi)源碼

下面就是整個(gè)工具類(lèi)的源碼 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ù)到新對(duì)象(單個(gè))
     *
     * @param source 源實(shí)例對(duì)象
     * @return 拷貝后的新實(shí)例對(duì)象
     */
    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ù)到新對(duì)象(批量)
     *
     * @param sourceList 源實(shí)例對(duì)象集合
     * @return 拷貝后的新實(shí)例對(duì)象集合
     */
    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;
    }
 
    /**
     * 單個(gè)深度拷貝
     *
     * @param source 源實(shí)例化對(duì)象
     * @param target 目標(biāo)對(duì)象類(lèi)(如:User.class)
     * @return 目標(biāo)實(shí)例化對(duì)象
     */
    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,則自動(dòng)忽略)
     *
     * @param sourceList 源實(shí)例化對(duì)象集合
     * @param target     目標(biāo)對(duì)象類(lèi)(如:User.class)
     * @return 目標(biāo)實(shí)例化對(duì)象集合
     */
    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集合中的類(lèi)名
     *
     * @param list 對(duì)象集合
     * @return 類(lèi)名
     */
    private static <T> Class<?> getClass(List<T> list) {
        for (T t : list) {
            if (Objects.nonNull(t)) {
                return t.getClass();
            }
        }
        return null;
    }
 
    /**
     * 實(shí)例化同源對(duì)象
     *
     * @param source 源對(duì)象
     * @param c      源對(duì)象類(lèi)名
     * @param fields 源對(duì)象屬性集合
     * @return 同源新對(duì)象
     */
    @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)實(shí)例化對(duì)象
     *
     * @param source       原對(duì)實(shí)例化象
     * @param target       目標(biāo)對(duì)象類(lèi)
     * @param sourceFields 源對(duì)象字段集合
     * @param targetFields 目標(biāo)對(duì)象屬性字段集合
     * @return 目標(biāo)實(shí)例化對(duì)象
     */
    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)對(duì)象中同源對(duì)象屬性相同的屬性(字段名稱(chēng),字段類(lèi)型一致則判定為相同)
     *
     * @param field  源對(duì)象屬性
     * @param fields 目標(biāo)對(duì)象屬性集合
     * @return 目標(biāo)對(duì)象相同的屬性
     */
    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;
    }
 
    /**
     * 獲取一個(gè)類(lèi)中的所有屬性(包括父類(lèi)屬性)
     *
     * @param c 類(lèi)名
     * @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);
    }
 
    /**
     * 遞歸獲取父類(lèi)屬性
     *
     * @param o         類(lèi)名
     * @param allFields 外層定義的所有屬性集合
     * @return 父類(lèi)所有屬性
     */
    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 對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java 對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JFormDesigner(IDEA)下載方法

    JFormDesigner(IDEA)下載方法

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

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

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

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

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

    微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼

    這篇文章主要介紹了微信公眾號(hào)測(cè)試賬號(hào)自定義菜單的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-02-02
  • 最新評(píng)論