Java 浅拷贝以及深拷贝

Java 浅拷贝以及深拷贝

1. Java数据类型

在Java程序中,存在基本类型以及引用类型两种数据类型结构
- 基本类型
- 基本类型分为3类:
- 数值性
- 整数型:byte, int, short, long
- 浮点数:float, double
- 字符型:char
- 布尔型:boolean
- 引用类型
- 引用类型分为3类:
- 对象:class
- 接口:interface
- 数组: T []

2.不同类型数据的处理方式

2.1 基本数据类型处理方法

在Java程序设计中,基本的数据类型值都是通过重新赋值的方式进行传递

public static int addSum(int a, int b){
    a = a + b;
    return a;
}

public static void main(String[] args) {
    int a1 = 10;
    int b1 = 5;
    System.out.println(addSum(a1, b1));
    System.out.println(a);
}

result : 
15 // a+b
10 // a

可以看出在方法addSum中对a的修改不会影响到a1的值(a表示形参,a1表示实参,修改形参不会变更实参的值)

2.2引用类型处理方式

引用类型和基本类型的处理方式是不同的,Java中的对象可以包含基本类型和引用数据类型,使用关键字new进行创建,例如:

// 国家类
class Country{
    //国家名称
    private String name;

    public String getName() {
        return name;
    }

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

// 城市类
class City {
    //国家名称
    private Country country;
    // 城市名称
    private String name;

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

    public String getName() {
        return name;
    }

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

创建对象,并对对象进行赋值操作

Country china = new Country();//创建国家对象
china.setName("china");//赋值
City c1 = new City();//创建城市对象
c1.setName("hz");//赋值
c1.setCountry(china);//赋值

引用类型传参处理方式和基础类不同,具体参见以下代码:

public static void main(String[] args) {
    Country china = new Country();
    china.setName("china");
    City c1 = new City();
    c1.setName("hz");
    c1.setCountry(china);
    immigrant(china, "England");
    System.out.println(china.getName());
}
public static void immigrant(Country country, String countryName) {
    country.setName(countryName);
}

result://输出结果信息
England

上述代码的输出结果是England,可以发现在方法内对形参进行处理时,会影响到实际变量china的值,而在之前的addSum方法中,对形参进行处理是不会影响到实际变量的值,实际上,引起上述不同的原因的数据存储方式不同,

3.JVM数据存储方式

在Java程序的运行环境是JVM,在JVM中使用两种内存来处理数据(当然还有其它的内存用于存储特定数据,在这里暂不讨论),栈内存和对内存,
- 栈内存:
栈内存用于存储函数中定义的基础数据变量和引用变量(基础数据变量指基础类型变量,例如 int i = 1, 引用变量指的就是引用类型变量,例如 City city = new City())
- 堆内存:
堆内存用于存储引用变量所指向的实际数据信息(可以把引用变量理解为存储内存地址信息,而实际的对象数据存储在引用变量所指向的地址),创建对象时,会在JVM的堆内存区域分配一块内存用于存储对象信息,china代表一个引用,这个引用指向内存中存储的对象数据,可以通过这个引用访问数据

graph LR
china引用-->JVM内存区域存储对象数据

为了说明引用变量存储的是对象的引用信息,而不是实际的对象数据信息,进一步修改上述代码

public static void main(String[] args) {
    Country china = new Country();
    china.setName("china");
    City c1 = new City();
    c1.setName("hz");
    c1.setCountry(china);
    immigrant(china, "England");
    System.out.println(china.getName());
}
public static void immigrant(Country country, String countryName) {
    country = new Country();
    country.setName(countryName);
}

result:
china

从上述结果可以看出,对country变量赋值一个新对象,修改之后不会影响到原先的china变量的值,可以用以下图片说明:
1.刚开始时变量china和country都指向内存中的对象Country1

graph LR
china-->Country1对象
country-->Country1对象

2.执行country=new Country时,china变量和country变量指向了两个不同的对象

graph LR
china-->Country1对象
country-->Country2对象
4.深拷贝和浅拷贝

通过上述说明,我们可以了解到,如果两个引用变量通过’a=b’的方式进行赋值时,通过任何一个引用变量修改对象数据时,将会影响到另外一个引用的数据,若果采用创建新对象的方式进行赋值时,修改一个引用变量的值将不会影响到另外一个引用变量的值。

4.1 重写方法实现深拷贝

通过实现Cloneable接口来申明该类是可拷贝的,重写clone方法来深拷贝对象信息

class Country implements Cloneable, Serializable {

    private static final long serialVersionUID = 11111L;
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public Country clone() {
        Country country = null;
        try {
            country = (Country) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return country;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Country{");
        sb.append("name='").append(name).append('\'');
        sb.append('}');
        return sb.toString();
    }
}


class City implements Cloneable, Serializable {

    private static final long serialVersionUID = 22222L;

    private Country country;
    private String name;

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public City clone() {
        City cy = null;
        try {
            cy = (City) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        cy.setCountry(country.clone());
        return cy;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("City{");
        sb.append("country=").append(country);
        sb.append(", name='").append(name).append('\'');
        sb.append('}');
        return sb.toString();
    }
}
4.2 通过序列化的方式实现深拷贝
public static <T> T cloneBeanObjectByIO(T obj) {
        T retVal = null;
        try {

            // 将对象写入流中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);

            // 从流中读出对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);

            retVal = (T) ois.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return retVal;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值