java浅拷贝与深拷贝及拷贝工具推荐

浅拷贝介绍

拷贝出当前对象的一个副本,这个新对象和当前对象处于不同的堆内存中,两个对象的基本数据类型的值完全一样,但是引用数据类型还是指向同一个对象的。

深拷贝介绍

深拷贝拷贝出当前对象的一个副本,这个新对象和当前对象处于不同的堆内存中,两个对象的基本数据类型的值完全一样,引用数据类型指向的对象也拷贝出了一份一模一样的副本。

可以看到我们如果在拷贝对象时不清楚深拷贝和浅拷贝的话很容易出现一些未知的异常,接下来我们用代码来演示一下

public class User implements Cloneable {

    private String name;

    private Address address;

    public String getName() {
        return name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}
public class Address {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class CopyDemo {

    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address();
        address.setAddress("杭州市");
        User user = new User();
        user.setName("jack");
        user.setAddress(address);
        User newUser = (User) user.clone();
        System.out.println("拷贝后对象的address:"+newUser.getAddress().getAddress());
        user.getAddress().setAddress("湖州市");
        System.out.println("修改了拷贝前对象的address后,拷贝后对象的address:"+newUser.getAddress().getAddress());
    }

}

输出值:
拷贝后对象的address:杭州市
修改了拷贝前对象的address后,拷贝后对象的address:湖州市

可以看到这里我们修改了拷贝前对象的Address,但是拷贝后对象Address也被改变了,由此可得拷贝前和拷贝后的Address执行的是堆内存中的同一个地址
科普:这里浅拷贝使用的是Jdk提供的Cloneable,使用时需要拷贝的对象实现Cloneable接口并且重写Object的clone()方法,Cloneable没有任何定义,只是一种规范。

深拷贝最容易想到的是使用Serializable接口序列化

public class User implements Cloneable,Serializable{

    private String name;

    private Address address;

    public String getName() {
        return name;
    }

    public User deepCopy() throws IOException, ClassNotFoundException {
        // 写入字节流
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream obs = new ObjectOutputStream(out);
        obs.writeObject(this);
        obs.close();

        // 分配内存,写入原始对象,生成新对象
        ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(ios);
        // 返回生成的新对象
        User newUser = (User) ois.readObject();
        ois.close();
        return newUser;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}
public class Address implements Serializable {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class CopyDemo {

    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Address address = new Address();
        address.setAddress("杭州市");
        User user = new User();
        user.setName("jack");
        user.setAddress(address);
        // 浅拷贝
        User newUser = (User) user.clone();
        // 深拷贝
        User newUser2 = user.deepCopy();
        System.out.println("拷贝后对象的address:"+newUser.getAddress().getAddress());
        user.getAddress().setAddress("湖州市");
        System.out.println("修改了拷贝前对象的address后,拷贝后对象的address:"+newUser.getAddress().getAddress());
        System.out.println("修改了拷贝前对象的address后,深拷贝后对象的address:"+newUser2.getAddress().getAddress());

    }
}

输出:
拷贝后对象的address:杭州市
修改了拷贝前对象的address后,拷贝后对象的address:湖州市
修改了拷贝前对象的address后,深拷贝后对象的address:杭州市

使用序列化对象,然后在反序列化即可得到一个全新的对象,但是使用起来性能比较低,所以不推荐使用,作为了解即可。

拷贝的工具类推荐

1、commons-beanutils的介绍:commons-beanutils是Apache组织下的一个基础的开源库,其主要目的是利用反射机制对JavaBean的属性进行处理。

public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        Address address = new Address();
        address.setAddress("杭州市");
        User user = new User();
        user.setName("jack");
        user.setAddress(address);
        
        User newUser = new User();
        // 把user对象内容拷贝到newUser
        BeanUtils.copyProperties(newUser,user);
        user.getAddress().setAddress("湖州市");
        System.out.println("修改了拷贝前对象的address后,拷贝后对象的address:"+newUser.getAddress().getAddress());

    }

输出:修改了拷贝前对象的address后,拷贝后对象的address:湖州市
可见BeanUtils.copyProperties是浅拷贝

2、Orika(极力推荐):Orika底层采用了javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件,因此在速度上比使用反射进行赋值会快很多,下面详细介绍Orika的使用方法

定义工具类:

public class OrikaMapperUtils {

    private static final MapperFacade mapperFacade;

    static {
        MapperFactory mapperFactory = new DefaultMapperFactory.Builder().useAutoMapping(true).mapNulls(true).build();
        mapperFacade = mapperFactory.getMapperFacade();
    }

    public static <S, D> void map(S from, D to) {
        mapperFacade.map(from, to);
    }

    public static <S, D> D map(S from, Class<D> clazz) {
        return mapperFacade.map(from, clazz);
    }

    public static MapperFacade getMapperFacade() {
        return mapperFacade;
    }

    public static <S, D> List<D> mapAsList(Iterable<S> source, Class<D> destinationClass) {
        return mapperFacade.mapAsList(source, destinationClass);
    }

}
public class CopyUtilsDemo {

    public static void main(String[] args) {
        Address address = new Address();
        address.setAddress("杭州市");
        User user = new User();
        user.setName("jack");
        user.setAddress(address);
        List<User> users = new ArrayList<>();
        users.add(user);
        // 浅拷贝
        User newUser = new User();
        // 1、把user对象内容拷贝到newUser
        OrikaMapperUtils.map(user,newUser);
        // 2、把user对象内容拷贝到newUser
        User newUser2 = OrikaMapperUtils.map(user,User.class);
        // 3、深拷贝集合
        List<User> users2 = OrikaMapperUtils.mapAsList(users,User.class);

        user.getAddress().setAddress("湖州市");
        System.out.println("newUser的address:"+newUser.getAddress().getAddress());
        System.out.println("newUser2的address:"+newUser.getAddress().getAddress());
        System.out.println("users2的address:"+users2.get(0).getAddress().getAddress());
    }
}

由上可见Orika包含了平时对拷贝的常用功能
1、对象到对象的的深拷贝
2、对象到Class类的深拷贝,即不同类型对象也可以拷贝
3、集合的深拷贝

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值