Java 列表复制与对象引用

Java 列表复制与对象引用

一、知识点

1. 对象引用的基本概念

在 Java 中,List<School> 这样的集合存储的并不是真正的对象,而是对象的“地址”(引用)。就好比你有一个文件柜,文件柜里放的不是文件本身,而是指向文件存放位置的标签。集合中的每个元素都指向堆内存中的实际对象实例。

2. 列表复制与对象引用

当你用 new ArrayList<>(schools) 复制一个列表时,新列表(比如 sortedSchools)只是把原列表(schools)中对象的“地址”复制了一遍,而不是复制对象本身。也就是说,两个列表中的元素都指向同一个对象实例,就像两个标签指向同一个文件。

3. 对象属性修改的影响

因为两个列表中的元素指向同一个对象,所以当你修改复制后的列表(sortedSchools)中对象的属性时,原列表(schools)中对应的对象属性也会跟着变。就好比你通过一个标签修改了文件内容,另一个标签指向的文件内容也会变。

4. 避免原列表被修改的方法

如果你希望修改复制后的列表时不影响原列表,可以在复制列表时创建新的对象,而不是直接复制引用。比如用构造函数重新创建对象:

List<School> sortedSchools = new ArrayList<>();
for (School school : schools) {
    sortedSchools.add(new School(school.abbreviation, school.totalStudents));
}

这种方法虽然能避免原列表被修改,但会增加内存开销(因为要创建新的对象实例)和处理时间(因为要调用构造函数)。


二、面试题

1. 基础概念题

题目:请解释在 Java 中 List<School> 存储的是 School 对象还是对象的引用?并说明当使用 new ArrayList<>(schools) 复制列表时会发生什么?

答案List<School> 存储的是 School 对象的引用,而不是对象本身。就好比你有一个文件柜,文件柜里放的是指向文件的标签,而不是文件本身。当使用 new ArrayList<>(schools) 复制列表时,新列表只是复制了原列表中对象的引用(标签),而不是创建新的对象实例。也就是说,新列表和原列表中的元素都指向堆内存中同一个对象。

2. 代码分析题

题目:以下是一段 Java 代码,请分析代码的输出结果,并解释原因。

import java.util.ArrayList;
import java.util.List;

class School {
    String abbreviation;
    int totalStudents;
    int unassignedStudents;
    int supervisors;

    public School(String abbreviation, int totalStudents) {
        this.abbreviation = abbreviation;
        this.totalStudents = totalStudents;
        this.unassignedStudents = totalStudents;
        this.supervisors = 0;
    }
}

public class Main {
    public static void main(String[] args) {
        List<School> schools = new ArrayList<>();
        School school1 = new School("ABC", 100);
        schools.add(school1);

        List<School> sortedSchools = new ArrayList<>(schools);
        for (School school : sortedSchools) {
            if (school.unassignedStudents > 0) {
                school.unassignedStudents -= 20;
                school.supervisors++;
            }
        }

        System.out.println("原列表 schools 中学校的未分配学生数: " + schools.get(0).unassignedStudents);
    }
}

答案:输出结果是 原列表 schools 中学校的未分配学生数: 80。原因如下:

  • sortedSchools 是通过 new ArrayList<>(schools) 复制的,它只是复制了 schools 中对象的引用,而不是创建新的对象实例。
  • sortedSchools 中修改对象的属性(比如 unassignedStudentssupervisors),因为引用的是同一个对象,所以 schools 中对应的对象属性也会被修改。因此,schools.get(0).unassignedStudents 的值会从 100 变为 80。
3. 优化策略题

题目:如果希望在上述代码中,对 sortedSchools 中 School 对象属性的修改不影响 schools 列表,应该如何修改代码?请说明这种修改的优缺点。

答案

  • 修改方法:在复制列表时创建新的 School 对象,而不是直接复制引用。修改后的代码如下:
    List<School> sortedSchools = new ArrayList<>();
    for (School school : schools) {
        sortedSchools.add(new School(school.abbreviation, school.totalStudents));
    }
    
  • 优点:这样可以保证原列表 schools 不会被 sortedSchools 的操作所影响,数据的独立性更好。就像你复制了一份文件,修改副本不会影响原始文件。
  • 缺点:会增加内存开销(因为需要创建新的对象实例),同时也会增加处理时间(因为每次复制都要调用构造函数来创建新对象)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值