文章目錄[隱藏]
for-each 與引用類型、參數傳遞組合考點
- for-each loop
- for-each loop traverse array
- for-each 循環的等價代碼
- 原始類型值的複製
- 自定義類對象作為參數傳遞
- 自定義 toString 方法
- 普通 for 循環遍曆對象數組
- 對象的別名 alias name
- for-each 與保存原始類型的數組
- 對象數組與 for-each 循環
- 等效代碼
- ArrayList 使用 for-each 的行為和 array 相同
for-each loop
for-each loop traverse array
當我們(men) 想要訪問數組或者列表中的每一個(ge) 元素,但是又不關(guan) 心索引的時候,適合使用 for-each 循環。
int[] hs = {1, 2, 3, 4, 5}; for(int element : hs) { System.out.println(element); }
1
2
3
4
5
for-each 循環的等價(jia) 代碼
對於(yu) 原始類型的數組,利用 for-each loop 無法改變元素的值。
int[] hs = {1, 2, 3, 4, 5}; // for-each loop 相當於(yu) 下麵的代碼 // int element = hs[0]; // 把 hs[0] 的副本賦值給 element // // do something // element 的變化,不影響 hs[0] // elment = hs[1]; // // do something // elment = hs[2]; // // do something // elment = hs[3]; // // do something // elment = hs[4]; // // do something for(int element : hs) { element = 0; // 試圖把所有元素都變成 0 } for(int element : hs) { System.out.println(element); } // 輸出的內(nei) 容還是數組原來的內(nei) 容
1
2
3
4
5
但是結果沒有變成 0,這是因為(wei) ,hs 是一個(ge) 整數類型的數組,int 是原始類型,賦值是建立一個(ge) 副本,然後再賦值給 element,相當於(yu) element = hs[0], element = hs[1],但是因為(wei) 是整數,所以 element 中保存的是 hs[0] 中的數值的副本,所以修改 element 不能修改索引為(wei) 0 的值,或者不能修改數組對應的值。
原始類型值的複製
需要注意的是,參數傳(chuan) 遞的過程就是把實際參數的值賦值給形式參數的過程。原始類型的複製是創建副本 copy 然後傳(chuan) 遞給形式參數;引用類型(指向對象的變量)裏麵保存的是地址,所以引用類型實際參數賦值給形式參數把對象的地址複製了一份傳(chuan) 遞給形式參數,這就是為(wei) 什麽(me) 說對象傳(chuan) 遞的是索引。
for-each 會(hui) 依次把數組中的元素賦值給 element 循環變量,但是這個(ge) 賦值是創建了副本的,element 的變化不影響數組中原來的值。
int a = 2; int b = a; // b 中保存的是 a 的副本,原始類型賦值會(hui) 創建副本 // b 的變化不影響 a b = 10; System.out.println(a); // a 的值仍然是 2
2
這個(ge) 結論,對於(yu) 任意的原始類型的數組,都是成立的。
自定義類對象作為參數傳遞
class Student { private String name; public Student(String n) { name = n; } // accessor public void setName(String n) {name = n;} // mutator public String getName() { return name;} }
數組中除了原始類型,還可以保存引用類型reference type,或者說可以保存對象。
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")};
for (Student stu : guys)
{
System.out.println(stu); // 輸出的是默認 toString 的結果 }
下麵輸出的是 jupyter notebook 環境中,對象 toString 方法的默認的實現,我們(men) 可以自定義(yi) toSring 方法,自行定義(yi) 對象的字符串表示形式。
REPL.$JShell$16C$Student@4af3eec1 REPL.$JShell$16C$Student@537aa7fe REPL.$JShell$16C$Student@60221fa7
class Student {
private String name;
public Student(String n) {
name = n;
}
public void setName(String n) {name = n;}
public String getName() { return name;}
public String toString() {return "[Student: " + name + "]";}
}
自定義(yi) toString 方法
我們(men) 定義(yi) 了自己的版本的 toString 然後我們(men) 在看打印的效果:
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")}; for (Student stu : guys) { System.out.println(stu); // 輸出的是默認 toString 的結果 }
[Student: lucy]
[Student: lily]
[Student: tom]
普通 for 循環遍曆對象數組
我們(men) 發現,對於(yu) 對象數組,我們(men) 仍然可以方便的使用 for-each 循環麽(me) ,我們(men) 看下用普通 for 循環達到相同效果:
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")}; for (int i = 0; i < guys.length; i++) { System.out.println(guys[i]); // 輸出的是默認 toString 的結果 }
[Student: lucy]
[Student: lily]
[Student: tom]
如果是要訪問方法就更麻煩。
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")}; for (int i = 0; i < guys.length; i++) { System.out.println(guys[i].getName()); // 輸出的是默認 toString 的結果 }
對象的別名 alias name
我們(men) 知道,對於(yu) 對象來說,變量其實是指向對象在內(nei) 存的地址,所以我們(men) 也可以把這些變量叫做對象的引用。
比如說:Student guy = new Student("zhangsan");,guy 其實就是對 Student 對象的引用,guy 引用的那個(ge) 對象的名字是 zhangsan。如果我們(men) 執行這樣代碼:Student s = guy 那麽(me) 我們(men) 知道,guy 中地址,創建一個(ge) 副本,然後賦值給 s,所以引用類型其實傳(chuan) 遞的是對象在內(nei) 存中的地址。
Student guy = new Student("zhangsan"); Student s = guy; // s is alias name of guy object // guy 和 s 中保存了對象在內(nei) 存的地址,是一個(ge) 對對象的引用
如果我在 s 上調用方法,同時會(hui) 修改 guy 指向的對象:
s.setName("laohuang"); System.out.println(guy.getName());
laohuang
我們(men) 雖然沒有在 guy 上調用 name 屬性的修改器,但是 s 和 guy 其實指向的是同一個(ge) 對象,所以 s 上調用方法,和在 guy 上調用方法是等效的。
guy.setName("lisi");System.out.println(s.getName());
lisi
for-each 與(yu) 保存原始類型的數組
對於(yu) 原始類型來說,我們(men) 無法通過 for-each 循環來修改數組中的元素:
int[] hs = {1, 2, 3, 4, 5}; for(int element : hs) { element = 0; // 試圖把所有元素都變成 0 } for(int element : hs) { System.out.println(element); }
1
2
3
4
5
對象數組與(yu) for-each 循環
但是,對於(yu) 對象數組來說,利用 for-each 循環,通過循環變量是可以修改數組中元素的屬性的,舉(ju) 例如下:
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")}; for (Student stu : guys) { stu.setName("lisi"); // stu 其實依次是每個(ge) 元素的別名,所以可以修改屬性 } for (Student stu : guys) { System.out.println(stu); // 輸出的是默認 toString 的結果 }
[Student: lisi]
[Student: lisi]
[Student: lisi]
等效代碼
對於(yu) for-each 循環來說,相當於(yu) 下麵的代碼:
Student[] guys = {new Student("lucy"), new Student("lily"), new Student("tom")}; for (Student stu : guys) { System.out.println(stu); // 輸出的是默認 toString 的結果 } Student stu = guys[0]; // stu 實際上 new Student("lucy") 的別名 alias name stu.setName("lisi"); stu = guys[1]; stu.setName("lisi"); stu = guys[2]; stu.setName("lisi"); for (Student stu : guys) { System.out.println(stu); // 輸出的是默認 toString 的結果 }
[Student: lucy]
[Student: lily]
[Student: tom]
[Student: lisi]
[Student: lisi]
[Student: lisi]
ArrayList 使用 for-each 的行為(wei) 和 array 相同
對於(yu) 對象來說,無論是 array 還是 ArrayList,利用 for-each 的循環變量,都是可以修改數組或者列表中的元素的。
ArrayList<Student> guys = new ArrayList<>();guys.add(new Student("zhangsan")); guys.add(new Student("lucy")); guys.add(new Student("wangwu")); for (Student stu : guys) // stu 是引用類型,賦值傳(chuan) 遞的是引用,而非副本 { stu.setName("lisi"); // stu 其實依次是每個(ge) 元素的別名,所以可以修改屬性 } for (Student stu : guys) { System.out.println(stu); // 輸出的是默認 toString 的結果 }
[Student: lisi]
[Student: lisi]
[Student: lisi]
可以看到,利用循環變量 stu 依次調用方法,可以修改列表對象的屬性;這是因為(wei) ,對於(yu) 對象來說,引用的賦值創建的地址的副本,stu 依次是列表中每個(ge) 元素別名,從(cong) 而使得這個(ge) 時候可以修改元素。
歡迎關(guan) 注公眾(zhong) 號。最後添加兩(liang) 個(ge) 關(guan) 於(yu) 嵌套循環 nested loop 講解的視頻,希望對大家有幫助。(應該放數組相關(guan) 的題目的,但是我還沒有錄製,所以上麵放了幾張圖)。
巴朗計算機是一本非常好的教材,事無巨細。但是,缺點也是太詳細了,有些枯燥。學生並不能看到 Java 在實際場景中的應用,所以如果能過結合 processing 或者代碼本色,其實是個(ge) 非常好的選擇。
這本書(shu) 裏麵的案例都非常的酷炫,適合真正對編程感興(xing) 趣和隊員仿真感興(xing) 趣的學生。
評論已經被關(guan) 閉。