Java 传递和拷贝

对值传递和引用传递、深拷贝浅拷贝的记录

我觉得对于值传递引用传递和深拷贝浅拷贝可以看作是一类问题,下面大体记录一下,如有错误,请指正。

值传递:形参和实参的内容不同,传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值

引用传递:形参和实参的内容相同,传递过程就是将实参的地址传到函数中,形参和实参始终指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象

看下面例子:

public class Student {

    public String id;

    public Student(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                '}';
    }
}

public class MyTest {
    public static void main(String[] args) {
        int mInt = 1;
        Student student = new Student("1");
        test(mInt, student);
        System.out.println(mInt);
        System.out.println(student);

        Integer integer = 1;
        String string = "1";
        test2(integer, string);
        System.out.println(integer);
        System.out.println(string);
    }

    private static void test(int mInt, Student student) {
        mInt = 5;
        student.id = "123";
    }

    private static void test2(Integer mInteger, String mString) {
        mInteger = 5;
        mString = "123";
    }

}

输出:
1
Student{id='123'}
1
1

通过上面代码输出可以看到,上面两个方法Student在方法中改变值后其外面的值也发生了改变,int、Integer、String在方法中修改后外面的值没发生改变。得出如下结论:

基本数据类型就是值传递
引用数据类型是引用传递
对于String、基本数据类型的包装类等immutable类型可以理解为值传递,但我觉得也是引用传递,但是在修改新对象的值时,会重新创建一个对象,原有对象保持不变。

浅拷贝:如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是引用数据类型(内存地址),拷贝的就是内存地址即数据的引用。而其所指向对象的引用仍然指向原来的对象。

深拷贝:复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。深拷贝相比于浅拷贝速度较慢并且花销较大。

看下面代码。

public class People {
    public String name;
    public int age;
    
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
        @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class MyTest {
    public static void main(String[] args) {
        int mInt1 = 5;
        int mInt2 = mInt1;
        mInt2 = 10;
        System.out.println(mInt1);
        System.out.println(mInt2);

        String mString1 = "5";
        String mString2 = mString1;
        mString2 = "10";
        System.out.println(mString1);
        System.out.println(mString2);
    
        People srcPeople = new People("Bill", 26);
        People destPeople = srcPeople;
        destPeople.age = 30;
        System.out.println(srcPeople);
        System.out.println(destPeople);
    }
}

输出:
5
10
5
10
People{name='Bill', age=30}
People{name='Bill', age=30}

通过输出可以得到如下结论:

int和String赋值后改变新的变量的值,原来的值并没有发生改变。而对于People对象,赋值后改变新变量的值原来的值也发生了改变。说明对于基本数据类型拷贝的是基本数据类型的值;对于String类型,拷贝的是其引用地址,但在修改新对象的值时,会从字符串池中生成一个新的字符串,原有对象保持不变;对于引用数据类型,拷贝的是其引用地址。

那对于上面例子我想改变destPeople的值而不影响srcPeople的值该如何做呢。

方案一 对于要拷贝的对象实现Cloneable接口,并重现Object类的clone()方法,调用super.clone(),然后赋值是使用x…clone():

public class People implements Cloneable {
    public String name;
    public int age;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected People clone() {
        try {
            return (People) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class MyTest {
    public static void main(String[] args) {
        People srcPeople = new People("Bill", 26);
        People destPeople = srcPeople.clone();
        destPeople.age = 30;
        System.out.println(srcPeople);
        System.out.println(destPeople);
    }
}

输出:
People{name='Bill', age=26}
People{name='Bill', age=30}

上面People中的成局变量只有String和int类型,如果People的成员变量有其他对象(比如下面Student对象)或集合的话,Student也需要实现Cloneable接口重现clone()方法,集合的话需要重新创建一个对象重新赋值,具体看下面代码:

public class Student implements Cloneable {

    public String id;

    public Student(String id) {
        this.id = id;
    }

    @Override
    protected Student clone() {
        try {
            return (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                '}';
    }
}

public class People implements Cloneable {
    public String name;
    public int age;
    public Student student;
    public List<Student> list;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void setList(List<Student> list) {
        this.list = list;
    }

    @Override
    protected People clone() {
        try {
            People people = (People) super.clone();
            people.student = student.clone();
            List<Student> newList = new ArrayList<>();
            for (Student s : list) {
                newList.add(s.clone());
            }
            people.list = newList;
            return people;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", student=" + student +
                ", list=" + list +
                '}';
    }
}

方案二,通过序列化实现对象的拷贝,对要拷贝的对象实现Serializable接口,然后通过调用CloneUtils的clone()即可,看下面代码:

// 拷贝工具类
public class CloneUtils {

    public static <T extends Serializable> T clone(T srcData) {
        T cloneData = null;

        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(srcData);

            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            cloneData = (T) objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(objectInputStream);
            close(byteArrayInputStream);
            close(byteArrayOutputStream);
            close(objectOutputStream);
        }
        return cloneData;
    }

    private static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
public class Student implements Serializable {

    private static final long serialVersionUID = -7477774394301373131L;

    public String id;

    public Student(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                '}';
    }
}

public class People implements Serializable {

    private static final long serialVersionUID = 7638893927457830038L;

    public String name;
    public int age;
    public Student student;
    public List<Student> list;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void setList(List<Student> list) {
        this.list = list;
    }


    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", student=" + student +
                ", list=" + list +
                '}';
    }
}

public class MyTest {
    public static void main(String[] args) {
        People srcPeople = new People("Bill", 26);
        People destPeople = CloneUtils.clone(srcPeople);
        System.out.println(srcPeople);
        System.out.println(destPeople);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值