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
中修改對象的屬性(比如unassignedStudents
和supervisors
),因為引用的是同一個對象,所以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
的操作所影響,數據的獨立性更好。就像你復制了一份文件,修改副本不會影響原始文件。 - 缺點:會增加內存開銷(因為需要創建新的對象實例),同時也會增加處理時間(因為每次復制都要調用構造函數來創建新對象)。