精华内容
下载资源
问答
  • 哪些可以实现对象的复制
    千次阅读
    2019-06-05 14:14:25

    最近遇到一个坑,之前的代码将一个订单对象中的明细,一个list,进行了分组。然后这个list就改变了。再往后,他们认为这个list没变,又将这个list作为最终数据进行了发送。这就导致这个明细回传错误。

    查出问题后,我就想,将这个对象如果赋值一份的吧。应该就没问题了。

    这里,一定要实现深复制,不然只进行浅复制的话,list内的值还是使用的同一块内存中的。进行分组后,原参数还是会被改变。

    所以这里我想到用fastjson提供的序列化能力,先将对象序列化成一个json字符串,然后再反序列化回来。

    这样,相当于按照一个字符串序列化了一个新对象,开辟了一个新的内存用于存放这个对象的堆内存内容。这就不会导致原有对象的改变了。

    具体代码很简单:

    String listStr = JSONObject.toJSONString(param.getDetailItems());
    List<Dto> list = JSONObject.parseArray(listStr,Dto.class);
    

    目前这方案是没有问题的。这期间我也了解了很多关于对象复制的问题。后续会找机会把最近找到的对象复制的相关内容写一个博客。

    更多相关内容
  • Java对象复制

    万次阅读 2022-02-11 11:26:57
    Java对象复制(拷贝)


    前言

    在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息。比如DTO数据传输对象和数据对象DO,我们需要将DO对象进行属性复制到DTO,但是对象格式又不一样,所以我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型。

    这种转换最原始的方式就是手动编写大量的get/set代码,属性少的时候还好,属性多的时候就非常繁琐了,一个合格的程序员显然不会仅仅局限于get/set。

    针对这个问题,市场上诞生了很多方便的类库,用于对象拷贝。常用的有apache BeanUtils、spring BeanUtils、Dozer、Orika等拷贝工具。

    何不可变类

    当类的实例一经创建,其内容便不可改变,即无法修改其成员变量。

    Java中有一些特殊的类是不可变类,八个基本类型的包装类和String类都属于不可变类。

    不可变类的特殊性:
    有两个不可变类对象,当两个对象指向同一引用时,修改某一对象的值,不会对另一个对象造成影响。

    下面举例说明

    • 源码:

      public static void main(String[] args) {
          Integer a = 1;
          String aa = "1";
          Integer b = a;
          String bb = aa;
          System.out.println("a修改前b:"+b);
          System.out.println("aa修改前bb:"+bb);
          a = 2;
          aa = "2";
          System.out.println("a修改后b:"+b);
          System.out.println("aa修改后bb:"+bb);
      }
      
    • debug:
      在这里插入图片描述

    • 日志:

      a修改前b:1
      aa修改前bb:1
      a修改后b:1
      aa修改后bb:1
      

    由此可见,a和aa的修改不会影响b和bb的值。

    方法参数的传递:

    • 基本类型传递的是值;
    • 引用类型传递的是对象的引用。
      • 不可变类型,在方法里修改了对象的值,也不会影响到原对象(不可变性);
      • 可变类型,在方法里修改了对象的值,原对象相应的值也会变动。

    对象复制方式

    对象复制有三种方式:直接赋值、浅拷贝、深拷贝。

    1.直接赋值

    • 基本数据类型复制的是值;
    • 引用数据类型复制的是对象的引用,原始对象及目标对象引用的是同一个对象。

    2.浅拷贝

    创建一个新对象,然后将当前对象的非静态字段复制到该新对象。

    • 基本数据类型复制的是值;
    • 引用数据类型复制的是对象的引用(不可变类型特殊)。

    注意:String类型、Integer等基本数据类型的包装类型,因为时不可变类型,所以即使进行的是浅拷贝,原始对象的改变并不会影响目标对象。

    3.深拷贝

    创建一个新对象,然后将当前对象的非静态字段复制到该新对象。

    • 无论该字段是基本类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。

    对象复制方案

    一个好用的属性复制方案,需要有哪些特性:

    1. 支持基本的属性复制;
    2. 支持不同类型的属性赋值,比如基本类型与其包装类型等;
    3. 支持不同字段名的属性赋值,当然字段名应该尽量保持一致,但是实际业务中,确实会有字段名不一致的情况;
    4. 浅拷贝/深拷贝,浅拷贝会引用同一对象,如果稍微不慎,同时改动对象,就会踩到意想不到的坑。

    市场上的对象转换方案主要分类:

    • 直接编写get、set代码(硬编码);
    • 通过反射实现;
    • 编译期生成get、set代码;
    • 基于AOP、ASM、CGlib等技术实现。

    12种对象转换方案归纳:

    方案推荐指数性能指数原理点评
    get/set★★★☆☆★★★★★手写get、set日常使用最多,性能好,只是较麻烦,需要手写。
    Spring BeanUtils★★★☆☆★★★★☆基于反射日常使用较多,性能较好,推荐使用
    Apache BeanUtils☆☆☆☆☆★☆☆☆☆基于反射兼容性较差,性能差,不推荐使用
    BeanCopier★★★☆☆★★★★☆基于CGlib性能较好,使用也不复杂,可以使用
    Orika★★☆☆☆★★★☆☆基于Javasisst字节码增强性能不太突出
    Dozer★☆☆☆☆★★☆☆☆基于反射的属性映射(递归映射)性能较差,不太推荐使用
    MapStruct★★★★★★★★★★编译期生成get、set性能好,结合到框架中使用方便,推荐使用
    Bean Mapping★★☆☆☆★★★☆☆基于反射性能一般,不太推荐使用
    Bean Mapping ASM★★★☆☆★★★★☆基于ASM字节码增强性能较好,但暂时不够灵活,可以使用
    ModelMapper★★★☆☆★★★☆☆基于反射性能一般,不太推荐使用
    JMapper★★★★☆★★★★★映射器方式实现性能较好,使用略微麻烦,可以使用
    Json2Json☆☆☆☆☆★☆☆☆☆基于JSON序列化和反序列化野路子,性能较差,不推荐使用

    分别测试这12种属性转换操作分别在一百次、一千次、一万次、十万次、一百万次时候的性能时间对比。
    在这里插入图片描述

    • BeanUtils.copyProperties是大家代码里最常出现的工具类,但只要你不把它用错成Apache包下的,而是使用Spring提供的,就基本还不会对性能造成多大影响。
    • 但如果说性能更好,可替代手动get、set的,还是MapStruct更好用,因为它本身就是在编译期生成get、set代码,和我们写get、set一样。
    • 其他一些组件包主要基于AOP、ASM、CGlib等技术手段实现的,所以也会有相应的性能损耗。

    1.get/set

    直接手写get/set方法实现数据的复制。

    这种方式也是日常使用的最多的,性能较好,就是操作起来有点麻烦。尤其是当对象属性较多的时候。

    减少手写代码的方式:

    1. 通过一些快捷的操作方式,比如你可以通过 Shift+Alt 选中所有属性,Shift+Tab 归并到一列,接下来在使用 Alt 选中这一列,批量操作粘贴 userDTO.set 以及快捷键大写属性首字母,最后切换到结尾补充括号和分号,最终格式化一下就搞定了。
    2. 通过IDEA的插件,如GenerateAllSetter插件。光标移至需生成get/set方法的对象名称,alt+enter出现快捷选项,选择需要生成的选项,自动生成相应的代码。
      在这里插入图片描述

    2.Spring BeanUtils

    同样是基于反射的属性拷贝(Introspector机制获取到类的属性来进行赋值操作)。Spring 提供的copyProperties要比Apache好用得多,这也是大家用得比较多的一种复制方式。

    Spring BeanUtils的实现方式非常简单,就是对两个对象中相同名字的属性进行简单的get/set,仅检查属性的可访问性。成员变量赋值是基于目标对象的成员列表,并且会跳过ignoreProperties的以及在源对象中不存在,不会因为两个对象之间的结构差异导致错误,但是必须保证同名的两个成员变量类型相同。

    Introspector
    是一个专门处理bean的工具类,用来获取Bean体系里的propertiesDescriptor、methodDescriptor利用反射获取Method信息,是反射的上层。只进行一次反射解析,通过WeakReference静态类级别缓存Method,在jvm不够时会被回收。

    特点:

    • 字段名不一致,属性无法复制;
    • 类型不一致,属性无法复制。但是注意,如果类型为基本类型以及基本类型的包装类,这种可以转化;
    • 浅拷贝

    依赖:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.8.RELEASE</version>
    </dependency>
    

    方法:

    /**
     * source:源对象
     * target:目标对象
     * editable:目标对象类的Class对象(需复制的属性基于该Class,当该值为null时需复制的属性基于目标对象的Class)
     * ignoreProperties:需忽略的属性列表
     */
    BeanUtils.copyProperties(Object source, Object target)
    
    BeanUtils.copyProperties(Object source, Object target, Class<?> editable)
    
    BeanUtils.copyProperties(Object source, Object target, String... ignoreProperties)
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
    
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDTO, userVO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    3.Apache BeanUtils

    推荐☆☆☆☆☆
    性能★☆☆☆☆
    手段:Introspector机制获取到类的属性来进行赋值操作
    点评:兼容性交差,效率较低,不建议使用

    Apache BeanUtils使用起来很方便,不过其底层源码为了追求完美,加了过多的包装,使用了很多反射,做了很多校验,做了类型的转换,甚至还会检验对象所属的类的可访问性,可谓相当复杂,过度的追求完美反而导致兼容性变差,也导致了性能较低,所以阿里巴巴开发手册上强制避免使用Apache BeanUtils。

    特点:

    • 字段名不一致的属性无法被复制;
    • 类型不一致的字段,将会进行默认类型转化;
    • 浅拷贝

    依赖:

    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.4</version>
    </dependency>
    

    方法:

    /**
     * dest:目标对象
     * orig:源对象
     */
    BeanUtils.copyProperties(Object dest, Object orig)
    

    实现:

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
    
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userVO, userDTO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    4.BeanCopier

    Cglib BeanCopier的原理与上面两个Beanutils原理不太一样,其主要使用CGlib字节码技术动态生成一个代理类,代理类实现get和set方法。生成代理类过程存在一定开销,但是一旦生成,我们可以缓存起来重复使用,所有Cglib性能相比以上两种Beanutils性能比较好。

    特点:

    • 字段名不一致,属性无法复制
    • 类型不一致,属性无法复制。如果类型为基本类型/基本类型的包装类型,这两者也无法被拷贝。但可自定义转换器实现不同类型的拷贝
    • 浅拷贝

    依赖:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    

    方法:

    /**
     * source   源Class
     * target   目Class
     * useConverter 是否使用转换器
     * from 源对象
     * to   目标对象
     * converter    转换器
     */
    BeanCopier beanCopier = BeanCopier.create(Class source, Class target, boolean useConverter);
    beanCopier.copy(Object from, Object to, Converter converter);
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
    
        UserVO userVO = new UserVO();
        BeanCopier beanCopier = BeanCopier.create(UserDTO.class, UserVO.class, false);
        beanCopier.copy(userDTO, userVO, null);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    5.Orika

    Orika也是一个跟Dozer类似的重量级属性复制工具类,也提供诸如Dozer类似的功能。但是Orika无需使用繁琐 XML配置,它自身提供一套非常简洁的 API 用法,非常容易上手。

    Orika底层基于Javassist生成字段属性的映射的字节码,然后直接动态加载执行字节码文件,相比于Dozer的这种使用反射原来的工具类,速度上会快很多。Orika的整个流程其实是需要使用到Java的反射的,只是在真正拷贝的属性的时候没有使用反射。

    Orika的执行流程:

    1. 先通过内省(反射)把JavaBean的属性(getset方法等)解析出来;
    2. 进而匹配目标和源的属性;
    3. 接着根据这些属性和目标/源的匹配情况基于Javasisst生成一个 GeneratedMapper的代理对象(真正的执行复制的对象)并放到缓存中;
    4. 接着就基于这个对象的 mapAtoB和mapBtoA方法对属性进行复制。

    Orikade的使用需要创建两个对象MapperFactory与MapperFacade,其中MapperFactory 可以用于字段映射,配置转换器等,而MapperFacade 的作用就与Beanutils一样,用于负责对象的之间的映射。

    特点:

    • 默认支持类型不一致(基本类型/包装类型)转换
    • 指定不同字段名映射关系,属性可以被成功复制
    • 深拷贝

    依赖:

    <dependency>
        <groupId>ma.glasnost.orika</groupId>
        <artifactId>orika-core</artifactId>
        <version>1.5.4</version>
    </dependency>
    

    方法:

    /**
     * sourceObject 源对象
     * destinationClass 目标Class
     * targetObject 目标对象
     */
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    MapperFacade mapper = mapperFactory.getMapperFacade();
    D targetObject = mapper.map(S sourceObject, Class<D> destinationClass);
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
    
        MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        MapperFacade mapper = mapperFactory.getMapperFacade();
        UserVO userVO = mapper.map(userDTO, UserVO.class);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    6.Dozer

    Dozer相对BeanUtils这类工具类来说,拥有许多高级功能,所以相对来说这是一个重量级工具类。其底层本质上还是使用了反射完成属性的复制(属性映射,递归的方式复制对象),所以执行速度并不是那么理想。

    Dozer需要我们新建一个DozerBeanMapper,这个类作用等同与BeanUtils,负责对象之间的映射,属性复制。
    生成DozerBeanMapper实例需要加载配置文件,随意生成代价比较高。因此在我们应用程序中,应该尽量使用单例模式,重复使用DozerBeanMapper。

    另外,强大的配置功能,我们可以通过XML、API或注解的方式配置源对象和目标对象属性映射关系和类型转换。

    特点:

    • 类型不一致的字段,属性被复制
    • 通过配置字段名的映射关系,不一样字段的属性也被复制
    • 深拷贝

    依赖:

    <dependency>
      <groupId>net.sf.dozer</groupId>
      <artifactId>dozer</artifactId>
      <version>5.4.0</version>
    </dependency>
    

    方法:

    /**
     * source   源对象
     * destinationClass 目标Class
     * target   目标对象
     */
    DozerBeanMapper mapper = new DozerBeanMapper();
    T target = mapper.map(Object source, Class<T> destinationClass);
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        DozerBeanMapper mapper = new DozerBeanMapper();
        UserVO userVO = mapper.map(userDTO, UserVO.class);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    7.MapStruct

    MapStruct运行速度与硬编码差不多,这是因为他在编译期间就生成了Java Bean属性复制的代码(属性对应的get、set),运行期间就无需使用反射或者字节码技术,所以确保了高性能。

    与硬编码方式相比,不管使用反射,还是使用字节码技术,这些都需要在代码运行期间动态执行所以它们的执行速度都会比硬编码慢很多。

    特点:

    • 名不一致,默认不支持复制
    • 类型不一致,默认不支持复制(但支持基本类型与包装类型、基本类型的包装类型与String的自动转换)
    • 可通注解配置实现名称不一致、类型不一致的复制
    • 深拷贝

    依赖:

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.3.1.Final</version>
    </dependency>
    

    插件:
    由于MapStruct需要在编译器期间生成代码,所以我们需要maven-compiler-plugin插件中配置。

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
            <source>1.8</source> <!-- depending on your project -->
            <target>1.8</target> <!-- depending on your project -->
            <annotationProcessorPaths>
                <path>
                    <groupId>org.mapstruct</groupId>
                    <artifactId>mapstruct-processor</artifactId>
                    <version>1.3.1.Final</version>
                </path>
                <path>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>1.18.12</version>
                </path>
                <!-- other annotation processors -->
            </annotationProcessorPaths>
        </configuration>
    </plugin>
    

    方法:

    @Mapper
    public interface UserMapper {
    
        UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    
        @Mappings({
                @Mapping(source = "id", target = "id"),
                @Mapping(source = "createTime", target = "createTime")
        })
        UserVO dtoToVo(UserDTO userDTO);
    }
    

    编译:
    MapStruct没有想象中的神奇,其实就是在编译期生成了接口的实现类,里面的转换方法实现了转换功能。相当于帮我们手写get/set设值,所以它的性能会很好。

    public class UserMapperImpl implements UserMapper {
        public UserMapperImpl() {
        }
    
        public UserVO dtoToVo(UserDTO userDTO) {
            if (userDTO == null) {
                return null;
            } else {
                UserVO userVO = new UserVO();
                userVO.setIdd(userDTO.getId());
                userVO.setCreateTime(userDTO.getCreateTime());
                userVO.setUserName(userDTO.getUserName());
                return userVO;
            }
        }
    }
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        UserVO userVO = UserMapper.INSTANCE.dtoToVo(userDTO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    可能出现的问题:

    • 如果我们对象使用 Lombok 的话,使用 @Mapping指定不同字段名,编译期间可能会抛出如下的错误
      在这里插入图片描述
      原因主要是因为Lombok也需要编译期间自动生成代码,这就可能导致两者冲突,当MapStruct生成代码时,还不存在Lombok生成的代码。解决办法可以在 maven-compiler-plugin插件配置中加入Lombok。
      在这里插入图片描述

    8.Bean Mapping

    基于反射的属性拷贝。0.0.2版本引入了@BeanMapping,通过@BeanMapping注解可实现灵活的复制方式。

    注解定义在 bean-mapping-api 模块中,bean-mapping-core 会默认引入此模块。

    特点:

    • 名不一致,不支持复制
    • 类型不一致,不支持复制(但支持基本类型转为包装类型,反过来不支持)
    • 通过@BeanMapping注解来灵活控制复制,支持名称不一致的复制、类型不一致的复制和控制是否复制。
    • 浅拷贝

    依赖:

    <dependency>
        <groupId>com.github.houbb</groupId>
        <artifactId>bean-mapping-core</artifactId>
        <version>0.2.5</version>
    </dependency>
    

    注解:

    @Inherited
    @Documented
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BeanMapping {
    
        /**
         * 字段别名
         * 如果不填,则默认使用字段的名称
         * 会将source的属性值赋值给target和当前name属性一致的属性
         * @return 字段别名
         */
        String name() default "";
    
        /**
         * 生效条件(默认为生效)
         * 1.当放在source字段上时,表示是否将值赋给target字段
         * 2.当放在target字段上时,表示是否接受赋值。
         * 3.source+target只有同时生效时,才会发生赋值。
         * @return 具体的生效实现
         */
        Class<? extends ICondition> condition() default ICondition.class;
    
        /**
         * 类型转换(默认不进行转换)
         * 当source的值转换后可以设置给target,才会将source转换后的值赋值给target对应属性,其他情况不会对值产生影响。
         * @return 具体的转换实现
         */
        Class<? extends IConvert> convert() default IConvert.class;
    
    }
    

    方法:

    /**
     * source   源对象
     * target   目标对象
     */
    BeanUtil.copyProperties(Object source, Object target)
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(userDTO, userVO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    9.Bean Mapping ASM

    推荐★★★☆☆
    性能★★★★☆
    手段:基于ASM字节码框架实现
    点评:与普通的Bean Mapping 相比,性能有所提升,可以使用。

    Bean Mapping基于ASM的字节码增强技术的复制方式要比Bean Mapping普通的方式新能要提升不少,但有个缺点就是暂不支持@BeanMapping注解等更加丰富的功能。

    特点:

    • 名不一致,不支持复制
    • 类型不一致,不支持复制(但支持基本类型转为包装类型,反过来不支持)
    • 效率比传统的Bean Mapping要好些,但暂不支持@BeanMapping注解的灵活复制
    • 浅拷贝

    依赖:

    <dependency>
        <groupId>com.github.houbb</groupId>
        <artifactId>bean-mapping-asm</artifactId>
        <version>0.2.5</version>
    </dependency>
    

    方法:

    /**
     * source   源对象
     * target   目标对象
     */
    AsmBeanUtil.copyProperties(Object source, Object target)
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        UserVO userVO = new UserVO();
        AsmBeanUtil.copyProperties(userDTO, userVO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    10.ModelMapper

    ModelMapper是利用反射的原理实现的。转换对象数量较少时性能不错,如果同时大批量转换对象,性能有所下降。

    依赖:

    <dependency>
        <groupId>org.modelmapper</groupId>
        <artifactId>modelmapper</artifactId>
        <version>2.3.0</version>
    </dependency>
    

    实现:
    简单使用

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        UserVO userVO = new UserVO();
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.map(userDTO, userVO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    ModelMapper的具体使用可参考文章:实体映射类库(modelmapper和MapStruct)

    11.JMapper

    JMapper通过映射器方式实现。

    依赖:

    <dependency>
        <groupId>com.googlecode.jmapper-framework</groupId>
        <artifactId>jmapper-core</artifactId>
        <version>1.6.0</version>
    </dependency>
    

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        JMapper<UserVO, UserDTO> jMapper = new JMapper<>(UserVO.class, UserDTO.class, new JMapperAPI()
                .add(JMapperAPI.mappedClass(UserVO.class)
                        .add(JMapperAPI.attribute("id").value("id"))
                        .add(JMapperAPI.attribute("userName").value("userName"))
                        .add(JMapperAPI.attribute("createTime").value("createTime"))
                ));
        UserVO userVO = jMapper.getDestination(userDTO);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    12.Json2Json

    这种通过JSON序列化和反序列化的方式,把源对象转为JSON串,再把JSON串转为目标对象,虽然也能达到复制的目的,但不推荐使用。

    实现:

    public static void main(String[] args) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(1);
        userDTO.setUserName("哈哈");
        userDTO.setCreateTime(new Date());
        UserVO userVO = JSON.parseObject(JSON.toJSONString(userDTO), UserVO.class);
        System.out.println(JSON.toJSONString(userVO));
    }
    

    复制方案选择

    1. 手写get/set肯定是效率最高的;
    2. 不要使用Apache Beanutils,因为效率低,阿里巴巴规范都直接禁止使用;
    3. 一般情况使用Spring Beanutils就可以了,效率OK,且本来就是Spring中的东西,不用依赖其它包。


    参考文章:对比 12 种 Bean 自动映射工具

    展开全文
  • 对象复制的六种方法

    千次阅读 2019-12-23 15:42:36
    对象复制可以分为:(地址复制),(实现Cloneable的方法),(使用BeanUtils.copyProperties() ),(PropertyUtils.copyProperties()),(序列化),(反射) 这里先总结一下浅克隆和深克隆:...

    题记:项目中用到对象的复制功能,自己写了一个工具类,使用的浅克隆(当时根本不懂什么浅克隆,深克隆),后期代码评审被替换,虽潜心研究!特总结如下!

    对象复制可以分为:(地址复制),(实现Cloneable的方法),(使用BeanUtils.copyProperties() ),(PropertyUtils.copyProperties()),(序列化),(反射)

    这里先总结一下浅克隆和深克隆:

    Java的数据类型分为:基本数据类型(byte,short,int,long,float,double,boolean,char)和引用数据类型(数组,类,接口)。

    在数据做为参数传递的时候,基本数据类型是“值传递”,引用数据类型是“引用传递”(地址传递)。

    这里有一个特例:String是一个类,类是引用数据类型,做为参数传递的时候,应该是引用传递。但是从结果看起来却是值传递。

    原因:String是被final修饰的,String的API中有这么一句话:“their values cannot be changed after they are created”,
               意思是:String的值在创建之后不能被更改。
               API中还有一段:

              String str = "abc";
              等效于:
              char data[] = {'a', 'b', 'c'};
              String str = new String(data);
              也就是说:对String对象str的任何修改 等同于 重新创建一个对象,并将新的地址值赋值给str。

    浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。这里说的复制是吧引用类型全部复制给对象,而不是把对象地址复制一份给对象。

    一、浅克隆(地址复制)

    public class ShallowCopy {
    
        @Data
        class Student {
            private String name;
        }
    
        //直接将对象的地址复制给另一个对象,但是这样改变一个另一个也会改变!
        @Test
        public void test1() {
            Student stu1 = new Student();
            stu1.setName("aaaa");
            Student stu2 = stu1;
            stu1.setName("bbbb");
            stu2.setName("cccc");
            System.out.println("学生1:" + stu1.getName());
            System.out.println("学生2:" + stu2.getName());
        }
    }

    打印结果:

    学生1:cccc
    学生2:cccc
    Disconnected from the target VM, address: '127.0.0.1:0', transport: 'socket'
    
    Process finished with exit code 0

    总结:这里stu1和stu2在对内存中创建了两个对象,两个对象地址不同,Student  stu2=stu1;这段代码将stu1的地址赋值给stu2,这是stu1和stu2指向同一个地址。当改变任意一个时两个对象的值都会被改变。

    二、深克隆(实现Cloneable的方法)

    @Data
        class Student3 implements Cloneable {
            private int name;
    
            @Override
            public Object clone() {
                Student3 stu = null;
                try {
                    stu = (Student3) super.clone();
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                return stu;
            }
        }
    
        @Test
        public void test3() {
            Student3 stu1 = new Student3();
            stu1.setName(12345);
            Student3 stu2 = (Student3) stu1.clone();
    
            System.out.println("学生1:" + stu1.getName());
            System.out.println("学生2:" + stu2.getName());
    
            stu2.setName(54321);
    
            System.out.println("学生1:" + stu1.getName());
            System.out.println("学生2:" + stu2.getName());
        }

    打印结果:

    Connected to the target VM, address: '127.0.0.1:0', transport: 'socket'
    学生1:12345
    学生2:12345
    学生1:12345
    学生2:54321
    Disconnected from the target VM, address: '127.0.0.1:0', transport: 'socket'
    
    Process finished with exit code 0

    总结:可以看到这里复制完以后对str2进行修改,但stu1的值没有被修改,说明复制stu2时进行了深克隆,而不是地址复制。

           这里Student3实体实现了一个标记接口CloneAble,复写的时Object方法的clone()方法。

    延申:

    如果对象中包含对象使用clone方法克隆。

    @Data
        class Address implements Cloneable {
            private String add;
    
            @Override
            public Object clone() {
                Address stu = null;
                try {
                    stu = (Address) super.clone();   //浅复制
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                return stu;
            }
        }
    
        @Data
        class Student2 implements Cloneable {
            private int name;
            private Address addr;
            @Override
            public Object clone() {
                Student2 stu = null;
                try {
                    stu = (Student2) super.clone();   //浅复制
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                //这里需要添加这一条才能实现深克隆,否则即便实现了CloneAble接口依然是浅克隆
                stu.addr = (Address) addr.clone(); //深度复制
                return stu;
            }
        }
    
        @Test
        public void test4() {
            Address addr = new Address();
            addr.setAdd("北京");
            Student2 stu1 = new Student2();
            stu1.setName(123);
            stu1.setAddr(addr);
    
            Student2 stu2 = (Student2) stu1.clone();
    
            System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
            System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
    
            addr.setAdd("上海");
    
            System.out.println("学生1:" + stu1.getName() + ",地址:" + stu1.getAddr().getAdd());
            System.out.println("学生2:" + stu2.getName() + ",地址:" + stu2.getAddr().getAdd());
        }

    总结:也就是两个实体类对象都要实现serizable接口,另外在包含对象中要添加被包含对象的复制语句,否则深克隆不生效

    //这里需要添加这一条才能实现深克隆,否则即便实现了CloneAble接口依然是浅克隆
    stu.addr = (Address) addr.clone(); //深度复制

    三、深克隆(使用BeanUtils.copyProperties() )

        @Data
        class Student {
            private String name;
        }
         @Test
        public void test5() {
            Student stu1 = new Student();
            stu1.setName("aaaa");
            Student stu2 = new Student();
            BeanUtils.copyProperties(stu1, stu2);
    //        stu2.setNumber(3456);
            System.out.println("stu1:" + stu1.getName());
            System.out.println("stu2:" + stu2.getName());
        }

    四、深克隆(PropertyUtils.copyProperties())这里对象复制完为什么String类型为null,int类型为0,有知道的留个言!

        @Data
        class Student {
            private String name;
        }
        @Test
        public void test6() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Student stu1 = new Student();
            stu1.setName("aaaa");
            Student stu2 = new Student();
            PropertyUtils.copyProperties(stu2, stu1);
    //        stu2.setNumber(2222);
            System.out.println("stu1:" + stu1.getName());
            System.out.println("stu2:" + stu2.getName());
        }

    五、深克隆(序列化)

        @SuppressWarnings("unchecked")
        public static <T extends Serializable> T clone(T obj) {
            T cloneObj = null;
            try {
                //写入字节流
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                ObjectOutputStream obs = new ObjectOutputStream(out);
                obs.writeObject(obj);
                obs.close();
    
                //分配内存,写入原始对象,生成新对象
                ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(ios);
                //返回生成的新对象
                cloneObj = (T) ois.readObject();
                ois.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cloneObj;
        }
        @Test
        public void test2() {
            Country country = new Country(1, "china");
            Person person = new Person(country, 1, "test");
            //引用传递
            Country country1 = country;
            Person person1 = person;
            //序列化和反序列化
            Person person2 = (Person) CloneUtils.clone(person);
            Country country2 = (Country) CloneUtils.clone(country);
            person1.setName("bbbbPerson");
            country1.setName("bbbContry");
    
            //修改序列化复制对象
            person2.setName("aaaaPerson");
            country2.setName("aaaaContry");
    
            System.out.println("创建国家       :" + country);
            System.out.println("引用传递国家  :" + country1);
            System.out.println("序列化复制国家  :" + country2);
            System.out.println("创建人         :" + person);
            System.out.println("引用传递人:" + person1);
            System.out.println("序列化复制人:" + person2);
        }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person implements Serializable {
    
        private static final long serialVersionUID = 1L;
        int id;
        String name;
        int age;
        Country country;
    
        public Person(Country country, int age, String name) {
            this.country = country;
            this.age = age;
            this.name = name;
        }
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Country implements Serializable {
    
        private static final long serialVersionUID = 1L;
        int code;
        String name;
    }

    总结:序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

    Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。字节码可以存储,无状态,而对象在内存中开辟空间,有地址。

    由此,可以把对象序列化后反序列化。相当于破碎重组。

    前提是:实体类需要实现序列化接口

    六、深克隆(反射)

        @Test
        public void test1(){
            //2.创建一个静态的Student对象
            Student stu1=new Student("ST20161282","Henry",22);
            try {
                Student stu2=(Student)ReflactCopyUtils.copyObj(stu1);
                stu2.setName("aaaa");
                System.out.println("复制对象成功");
                System.out.println(stu1.toString());
                System.out.println(stu2.toString());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    public class ReflactCopyUtils {
        public static Object copyObj(Object obj) throws Exception {
            //3. 获取已有Student对象的Class对象
            Class<?> classType = obj.getClass();
            //4. 通过Class.newinstance方法动态构建一个副本对象
            Object stu1 = classType.newInstance();
            //由于不知道源对象的具体属性和属性值,通过反射机制,先得到属性名称,再拼接字段得到属性对应的get,set方法
            for (Field field : classType.getDeclaredFields()) {
                //5. 获取副本对象的get,set方法
                String getMethodName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                String setMethodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
                //6. 调用源对象的get方法获取属性值
                Method getMethod = classType.getDeclaredMethod(getMethodName, new Class[]{});
                Object value = getMethod.invoke(obj, new Object[]{});
                //7. 调用副本对象的set方法把属性值复制过来
                Method setMethod = classType.getDeclaredMethod(setMethodName, new Class[]{field.getType()});
                setMethod.invoke(stu1, new Object[]{value});
            }
            return stu1;
        }
    }
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
        private String id;
        private String name;
        private int age;
    }

    总结:

    案例介绍:

    Student类,有属性id, name, age, 还有对应的get,set方法和构造方法。
    现产生一个Student对象。通过反射复制此Student对象。复制时,并不知道源对象具体的属性名称。

    案例设计

    通过反射机制来获取类的属性和方法
    通过反射生成对象,并通过反射来调用其set方法对属性进行复制以达到复制对象的目的
    最后对赋值成功的对象信息进行打印


    方案实施

    创建一个Student类
    创建一个静态的Student对象
    获取已有Student对象的Class对象
    通过Class.newinstance方法动态构建一个副本对象
    获取副本对象的get,set方法
    调用源对象的get方法获取属性值
    调用副本对象的set方法把属性值复制过来
    打印输出

     

    展开全文
  • 在JS中复制对象

    千次阅读 2021-04-01 11:34:21
    在这篇文章中,我将会讲到几种在JS中复制对象的方式,我们将会关注到浅复制和深复制。 在开始之前,值得一提的是一些基础知识:JS中的对象只是对内存中某个位置的引用。这些引用是可以更改的。即他们可以被重新分配...

    原文地址:Copying objects in Javascript, 原文作者:Victor Parmar
    渣翻译,有英文阅读能力的可以去原网址阅读,正文部分的括号内是译者的尝试补充说明
    自豪地采用谷歌翻译

    转载:张子虚的博客:https://blog.csdn.net/ccaoee/article/details/86510718

     

    在这篇文章中,我将会讲到几种在JS中复制对象的方式,我们将会关注到浅复制和深复制。
    在开始之前,值得一提的是一些基础知识:JS中的对象只是对内存中某个位置的引用。这些引用是可以更改的。即他们可以被重新分配。从而,简单的复制引用的操作在结果上仅仅是将两个引用指向了内存中的同一位置。

    var foo = {
      a: 'abc'
    }
    console.log(foo.a) // abc
    
    var bar = foo
    console.log(bar.a) // abc
    
    foo.a = 'yo foo'
    console.log(foo.a) // yo foo
    console.log(bar.a) // yo foo
    
    bar.a = 'whatup bar?'
    console.log(foo.a) // whatup bar?
    console.log(bar.a) // whatup bar?
    

    正如你从上面例子中看到的,foo和bar都反映了同一个对象的变化。从而,在JS中复制对象需要小心,具体取决于您的用例。

    浅复制

    如果你的对象的属性的类型仅仅只是值类型(译者注:基本类型)的。你可以使用扩展运算符语法或者Object.assign(...)

    var obj = { foo: 'foo', bar: 'bar' }
    var copy = { ...obj } // Object { foo: 'foo', bar: 'bar' }
    
    var obj = { foo: 'foo', bar: 'bar' }
    var copy = Object.assign({}, obj) // Object { foo: 'foo', bar: 'bar' }
    

    请注意,上述两种方法都可用于将属性值从多个源对象复制到目标对象:

    var obj1 = { foo: 'foo' }
    var obj2 = { bar: 'bar' }
    
    var copySpread = { ...obj1, ...obj2 } // Object { foo: 'foo', bar: 'bar' }
    var copyAssign = Object.assign({}, obj1, obj2) // Object { foo: 'foo', bar: 'bar' }
    

    事实上,上述方法的问题在于对象的属性如果是一个对象,则只会复制该属性对象在内存中的引用。即它相当于var bar = foo,如同第一个代码例子:

    var foo = { a: 0, b: { c: 0 } }
    var copy = { ...foo }
    
    copy.a = 1
    copyb.c. = 2
    
    console.dir(foo) // { a: 0, b: { c: 2 } }
    console.dir(copy) // { a: 0, b: { c: 2 } }
    

    深复制(有缺陷)

    为了对对象进行深复制操作,一个潜在的解决方案是序列化对象为一个字符串,然后反序列化,生成一个新对象:

    var obj = { a: 0, b: { c: 0 } }
    var copy = JSON.parse(JSON.stringify(obj))
    

    不幸的是,这个方法仅仅适用于当源对象包含可序列化的值类型并且没有循环引用的情况。不能序列化的值的类型,比如Date对象,即使它在字符串化上以ISO格式打印。JSON.parse仅仅会将它解释为一个字符串,而不是Date对象。

    深复制(更少的缺陷)

    对于更复杂的对象,可以使用更新的HTML5structured clone克隆算法。不幸的是,在撰写本文时,它仍局限于某些内置类型,但它支持的内容类型比JSON.parse更多。比如:DateRegExpMapSetBlobFileListImageData、稀疏和类型化数组。它还在克隆对象中保留了引用关系。允许它支持不适用于上述序列化方法的循环和递归结构。

    当前还没有直接调用结构化克隆算法的方法,但一些新的浏览器特性可以被用来间接使用这个方法。从而,我们会得到一些可能用于深度复制对象的变通方法

    使用 MessageChannel:这背后的想法是利用MessageChannel通信功能使用的序列化算法。这个功能是基于事件的,因此获取克隆结果是一个异步的操作。

    class StructruedCloner {
      constructor() {
        this.pendingClones_ = new Map()
        this.nextKey_ = 0
        
        const channel = new MessageChannel()
        this.inPort_ = channle.port1
        this.outPort_ = channel.port2
        
        this.outPort_.onmessage = ({data: {key, value}}) => {
          const resolve = this.pendingClones_.get(key)
          resolve(value)
          this.pendingClones_.delete(key)
        }
        this.outPort_.start()
      }
      
      cloneAsync(value) {
        return new Promise(resolve => {
          const key = this.nextKey_++
          this.pendingClones_.set(key, resolve)
          this.inPort_.postMessage({key, value})
        })
      }
    }
    
    const structuredCloneAsync = window.structuredCloneAsync = 
          StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner)
    
    const main = async () => {
      const original = { date: new Date(), number: Math.random() } // 译者注释:添加一些JSON方法不能解释的对象
      original.self = original // 译者注释:添加循环引用
      
      const clone = await structuredCloneAsync(original)
      
      // 不同的对象
      console.assert(original !== clone)
      console.assert(original.date !== clone.date)
      
      // 循环
      console.assert(original.self === original)
      console.assert(clone.self === clone)
      
      // 等价值
      console.assert(original.number === clone.number)
      console.assert(Number(original.date) === Number(clone.date))
      
      console.log('Assertions complete.')
    }
    
    main()
    

    使用historyAPIhistory.pushState()history.replaceState()两个方法会创建它们第一个参数的结构化对象。注意这个方法是同步的,操纵浏览器历史记录不是一个快速的操作并且反复调用此方法可能导致浏览器无响应。

    const structureClone = obj => {
      const oldState = history.state
      history.replaceState(obj.null)
      const clonedObj = history.state
      history.replaceState(oldState, null)
      return clonedObj
    }
    

    使用notificationAPI:当创建一个新的提醒(译者注:notification),构造函数会从它所关联的数据中创建一份结构化的克隆副本。注意,这么做浏览器会尝试将提醒显示给用户。但是这将会静默失败。除非应用已经请求到显示提醒的权限。万一权限存在,提醒会立即关闭。

    const structuredClone = obj => {
      const n = new Notification('', {data: obj, silent: true})
      n.onshow = n.close.bind(n)
      return n.data
    }
    

    在NodeJS中进行深复制

    在NodeJS的8.0.0版本中,它提供了一个序列化的API,它是兼容结构化克隆的。注意:这个API在本文撰写时(译者注:原文发表于2018.11.1)还是标记为实验性的:

    const v8 = require('v8')
    const buf = v8.serialize({a: 'foo', b: new Date()})
    const cloned = v8.deserialize(buf)
    cloned.b.getMonth()
    

    对于版本低于8.0.0或者更稳定的实现,一种方法是:可以使用lodashcloneDeep方法。该方法也基于结构化克隆算法

    结论

    总而言之,在JS中复制对象最佳的算法是严重依赖于你所复制对象的上下文和类型的。而lodash是通用深复制函数最安全的选择。也许你会给出更高效的实现呢。下面是一个对Date对象也起效的深复制的函数:

    function deepClone(obj) {
      var copy
      
      // 处理3种基础类型,和null、undefined
      if (obj === null || typeof obj !== 'object') return obj
      
      // 处理日期
      if (obj instanceof Date) {
        copy = new Date()
        copy.setTime(obj.getTime())
        return copy
      }
      
      // 处理数组
      if (Array instanceof Array) {
        copy = []
        for (var i = 0, len = obj.length; i < len; i++) {
          copy[i] = deepClone(obj[i])
        }
        return copy
      }
      
      // 处理函数
      if (obj instanceof Function) {
        copy = function() {
          return obj.apply(this, arguemnts)
        }
        return copy
      }
      
      // 处理对象
      if (obj instance Object) {
        copy = {}
        for (var attr in obj) {
          if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr])
        }
        return copy
      }
      
      throw new Error("Unable to copy obj as type isn't suported" + obj.constructor.name)
    }
    

    个人而言,我期待能够在任何地方使用结构化克隆,最后让这个问题得到休息,快乐的克隆:)

    展开全文
  • Java对象复制三种方式

    千次阅读 2021-02-12 09:36:34
    Java对象复制三种方式概述在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也...
  • js实现对象复制,不影响原对象

    千次阅读 2019-08-29 10:03:31
    发布时间:2018-10-23 10:52:14 ...假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。 eg: var obj1 = { a: 0 , b: { c: 0}}; var obj2 = Object.assign({}, obj1);//将obj的可枚举的属...
  • 正文: 对象复制工具类,简单好用。但是使用场景好像不多,先收藏 例子1 复制学生类到另一个学生类 import java.time.LocalDate; ​ public class BeanUtilTest { public static void main(String[] args) { ...
  • C# 实现一个对象的深复制的方法

    千次阅读 2019-01-08 17:20:37
    /// 使用序列化的方法实现复制 /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;&lt;/typeparam&gt; /// &lt;param name="source"&gt;&lt;/param&...
  • Android或java中对象复制

    千次阅读 2019-06-27 17:51:37
    做项目过程中有需求用到对象复制内容, 在此随手记录一下. 为何要复制对象, 在此就不做细数, 下面简单说明文字+代码. 对象复制, 总体来将, 分为三种方法. 方法一: 用getter和setter挨个进行赋值, 这种方式简单, ...
  • SpringBoot 如何进行对象复制,老鸟们都这么玩的!

    千次阅读 多人点赞 2021-09-23 14:36:08
    今天带来SpringBoot老鸟系列的第四篇,来聊聊在日常开发中如何优雅的实现对象复制。 首先我们看看为什么需要对象复制? 为什么需要对象复制 如上,是我们平时开发中最常见的三层MVC架构模型,编辑操作时Controller...
  • C# 对象复制 拷贝

    万次阅读 2018-06-13 10:23:20
    在我们工作中经常会用到对象复制的情况,比如 A对象为原有对象,把A对象赋值给B对象,记录原有数据,然后对A对象开始操作改变值,接着想知道 A都改变了那些值 都会先这样写: A a = new A(); a.ID = 10; a.Name = ...
  • 对象的“复制”,指的是把一个现有的对象“克隆(clone)”一份,得到一个新对象,这个新对象与老对象“一模一样”,但……新、老对象是完全独立的。 1.对象的”浅复制” 使用简单的字段值拷贝方法复制一个对象...
  • JS 对象复制

    千次阅读 2022-03-14 18:27:16
    使用扩展运算符可以实现部分对象的深复制,若对象中有数组或其他引用类型,则扩展运算符不能实现复制。 成功案例: let obj1 = { name: "rain", age: 18 } let obj2 = { ...obj1 } obj2.name = "snow" console....
  • python:对象复制

    千次阅读 2018-12-22 00:19:54
    对象都是可以赋值给变量的2、列表、字典、集合为可变对象,这些对象的有些方法是在对象本身上操作的 ⑴直接在对象本身上进行修改:改变的是对象本身,而不是生成一个修改后的新对象 ⑵如append()、remove()方法...
  • Nodejs 实现对象的深度复制

    千次阅读 2018-03-15 14:49:18
    nodejs中不提供clone方法,解决方法是:let tmpPackage = JSON.parse(JSON.stringify(API_ROUTES.trademark));或者let cloned = Object.assign({}, source);let cloned = { ... source }; // 仅限ES6...
  • Java Bean对象复制的三种方式详解

    万次阅读 2020-04-25 22:58:18
    一般Java对象复制包含**浅拷贝、深度复制、BeanUtils.copyProperties()**等三种方式。 对象的克隆是指创建一个新的对象,且新的对象的状态与原始对象的状态相同且我们对新对象修改时,不影响原对象的状态。 原理...
  • golang 复制对象

    千次阅读 2020-11-20 13:40:39
     实际运用种,传参是一对象指针,现在如何最简便地复制对象实现 错误方法:&* 先拿到值再指针? package main import ( "time" "log" ) func main() { T1 := &TestS{1} T2 := &*T1 ...
  • js实现复制对象、扩展对象 方法

    千次阅读 2018-03-04 13:22:48
    jquery里有extend方法来扩展对象,但如果我们只用js,怎么来实现扩展对象呢? 不废话直接上代码,看了就懂。 源代码://******** 深复制对象 ******** function cloneObj(oldObj) { if(typeof(oldObj) != 'object...
  • 实现对象深拷贝的五种方法

    千次阅读 多人点赞 2021-08-27 21:41:35
    对象深拷贝的五种方法 1.json暴力转化 通过JSON.stringify() 和 JSON.parse() 将对象转为字符串之后在转为对象。 var obj = {name:'123'} var obj2 = JSON.parse(JSON.stringify(obj 这种简单粗暴的方式有局限性,...
  • OC中对象复制:一

    千次阅读 2021-12-16 06:31:41
    看官们,我们在前面章回中介绍了OC中的属性特性,本章回中将介绍OC中对象复制。 什么是复制 这里说的复制是copy的翻译,因为复制操作使用了copy这个方法。这里说的复制主要指对象复制,不包括基本类型值的复制...
  • js中复制对象的属性值给新的对象

    万次阅读 2019-07-05 17:10:32
    我们有一个对象,且包含很多属性值和方法,但是我们想把它的内部属性复制给一个新的对象时,我们如何去做呢? 你可能会说直接 a = b就可以了。 no no no,这样两个对象其实指针指向的还是一个内存中的对象,当一个...
  • js中数组和对象复制

    千次阅读 2017-09-11 22:50:59
    一、数组 1、浅复制和深度复制的区别 (1) 浅复制:浅复制只会将对象的各个属性进行...深复制:而深复制则不同,它会将原对象的各个属性复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制
  • 深拷贝:复制对象方法一JSON.parse(JSON.stringify())示例:computed: {data: function () {var obj={};obj=JSON.parse(JSON.stringify(this.templateData)); //this.templateData是父组件传递的对象return obj}}...
  • js实现复制文本内容功能

    千次阅读 2022-03-29 10:13:32
    Clipboard 对象 Clipboard.writeText() Clipboard.writeText()方法用于将文本内容写入剪贴板 document.body.addEventListener( 'click', async (e) => { await navigator.clipboard.writeText('Yo') } ) ...
  • html 实现复制功能

    千次阅读 2019-07-24 18:40:45
    今天有这样一个需求,点击按钮后复制某段文字,操作如下图:点击复制按钮,将邮箱复制。... //执行浏览器复制命令 解决方法: /*复制邮箱*/ function copyEmail(){ var oInput = document.getE...
  • js如何实现复制功能

    千次阅读 2022-05-17 15:11:25
    用原生的document.execCommand(‘copy’)的方式实现,移动端(安卓,iOS兼容)和PC端都可以使用 方法二: 使用第三方框架clipboard的方式实现,移动端默认浏览器不兼容,PC端可以使用 PS:如有错误,请多...
  • js实现点击复制链接功能

    千次阅读 2021-11-23 10:42:49
    像这种复制链接的功能一般我们把他放在utils文件中. 代码如下: //文本复制 exportfunctioncopyText(value){ returnnewPromise((resolve)=>{ varinput=document.createElement("input");//js创建一个input...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 814,446
精华内容 325,778
热门标签
关键字:

哪些可以实现对象的复制