Re: [問題] 請教String的問題

看板java作者 (小天)時間10年前 (2014/12/18 20:00), 10年前編輯推噓4(4077)
留言81則, 4人參與, 最新討論串6/6 (看更多)
非常感謝大家的熱心回應,我完全理解了 現在只剩一個小問題想問... ex1: String a = "Hello"; a = "kdok123"; 這個結果是new了兩個String的空間在Heap ex2: Interger a = 3; a = 4; 結果是new了一個Interger的空間在Heap,a指向的值從3改成了4 以上兩個例子體現了String的不可變性 請問例子的觀念是對的嗎? 另外還有一個小疑惑:為什麼java要定義String的不可變性呢? 除了不斷的增加heap和GC的負擔之外我想不到其他好處欸... --------------------------------------------------------- 補充:想了一下我好像還沒完全理解 以dark大&Chikei大的觀念來看我的ex1: 輸出兩次會是一樣的原因是因為String的不可變性 "所以str2指向的地方因為不可改變,只能另外再開一個空間去存新的值" 但以這樣的觀念把String改成Integer,似乎就應該得到改變過後的結果 (因為Integer可以變) 但實際測試發現Interger得到的結果也是兩個一樣的輸出 到底是為什麼呢? 2次補充: java的所有 primitive wrapper class都是 immutable的 https://en.wikipedia.org/wiki/Primitive_wrapper_class 所以這樣觀念就完全通了 補充一點: 上面所有提到call by value的大大, java只有call by value是沒錯的 但如果是以String或是Integer傳入的value會是address,而不像一般傳入primitive 的value 所以ex1的call by value會新複製一個address指向本來的位置, 這個時候的修改應該是可以成功的 但因為此時Immutable的特性,所以會新開一份空間來存值,才保留了原本的值 若沒有這樣的特性,值是會變的,跟call by value沒有關係 以上是我整理的觀念,若有錯誤煩請討論指正 再次感謝大家的熱心回應! ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 60.250.185.98 ※ 文章網址: http://www.ptt.cc/bbs/java/M.1418904044.A.327.html ※ 編輯: kdok123 (60.250.185.98), 12/18/2014 20:12:09 ※ 編輯: kdok123 (60.250.185.98), 12/18/2014 20:15:14

12/18 20:26, , 1F
輸出2次一樣是因為pass by reference,和不可變無關
12/18 20:26, 1F

12/18 20:28, , 2F
ex2的a會指到另一個不同的位置,用任何class都會這樣
12/18 20:28, 2F

12/18 20:33, , 3F
你根本沒有懂,就說你的結果跟不可變性"完全"沒關係
12/18 20:33, 3F

12/18 20:34, , 4F
String不可變是String不提供任何方法修改它裡面char array
12/18 20:34, 4F

12/18 20:35, , 5F
存的值,要修改一定是產生新的String object
12/18 20:35, 5F
※ 編輯: kdok123 (61.221.50.98), 12/18/2014 20:49:04

12/18 20:39, , 6F
剛進method的str2指向的就是完全同一個String
12/18 20:39, 6F

12/18 20:40, , 7F
並沒另外開一個空間,是之後你自己開新的空間放新的String
12/18 20:40, 7F

12/18 20:43, , 8F
="abc"相當於=new String(new char[] {'a','b','c'})
12/18 20:43, 8F

12/18 20:44, , 9F
可變性的意義在於如果有個MutableString class
12/18 20:44, 9F

12/18 20:45, , 10F
你可以用str2.setValue("abc")去改object的內容
12/18 20:45, 10F

12/18 20:45, , 11F
而不用寫成str2 = new MutableString("abc")
12/18 20:45, 11F

12/18 20:47, , 12F
只要用的是 = new XXX(),不管哪個class都是一樣的結果
12/18 20:47, 12F

12/18 20:52, , 13F
只要你用的是 = 都是不會變的,因為 = 不是改object裡的值
12/18 20:52, 13F

12/18 20:52, , 14F
而是改reference指向的位址
12/18 20:52, 14F

12/18 20:52, , 15F
只有primitive type的時候用 = 才是改值
12/18 20:52, 15F

12/18 20:53, , 16F
String="abc"的意思是若"String pool沒有"abc",則創造
12/18 20:53, 16F

12/18 20:54, , 17F
一個新空間存放abc,否則指向abc
12/18 20:54, 17F

12/18 20:55, , 18F
String ob = new String("abc")的意思是"不管String pool
12/18 20:55, 18F

12/18 20:55, , 19F
裡有沒有abc,都創造一個空間存放abc
12/18 20:55, 19F

12/18 20:56, , 20F
因為immutable的特性,才會造成上面的結果,否則任何的
12/18 20:56, 20F

12/18 20:56, , 21F
賦值動作都會覆蓋原本的值
12/18 20:56, 21F

12/18 20:57, , 23F
覆蓋原本的值也是覆蓋reference,而不是覆蓋value
12/18 20:57, 23F

12/18 20:57, , 24F
參考連結的觀念的
12/18 20:57, 24F

12/18 20:57, , 25F
上面我簡化沒有考慮string pool的問題,但重點就是你用 =
12/18 20:57, 25F

12/18 20:57, , 26F
永遠改不到原本那個object的值
12/18 20:57, 26F

12/18 20:59, , 27F
任何reference type變數的賦值動作(=)都只是改指向不同物件
12/18 20:59, 27F

12/18 21:02, , 28F
這邊我認為是immutable的關係,若今天是mutable就改得到
12/18 21:02, 28F

12/18 21:02, , 29F
或許等我試試上面連結的atomic wrapper class?
12/18 21:02, 29F

12/18 21:03, , 30F
android studio沒有把那個class import進來不能測試...
12/18 21:03, 30F

12/18 21:05, , 31F
我是不知道你打算怎麼試,但是除了primitive的autowrapper
12/18 21:05, 31F

12/18 21:06, , 32F
還有String literal("...")以外,其他type都很清楚就是換了
12/18 21:06, 32F

12/18 21:07, , 33F
物件啊...
12/18 21:07, 33F

12/18 21:10, , 34F
如果你有學過C++,java的reference type基本上就是pointer
12/18 21:10, 34F

12/18 21:14, , 35F
如果是C++的pass by reference(&),即使物件是mutable
12/18 21:14, 35F

12/18 21:15, , 36F
還是可直接改caller func中那個reference的值(指向的位址)
12/18 21:15, 36F

12/18 21:17, , 37F
或是你可以去用C#試,C#的reference type跟java一樣
12/18 21:17, 37F

12/18 21:17, , 38F
String也是Immutable,但是方法可以pass by reference
12/18 21:17, 38F

12/18 21:23, , 39F
你的ex1用C# pass by ref版 http://ideone.com/Miptpm
12/18 21:23, 39F

12/18 21:43, , 40F
Hi,因為java這邊是auto-boxing的,所以以c++的觀念來說
12/18 21:43, 40F

12/18 21:43, , 41F
pointer是指向同一個地方,要修改值的
12/18 21:43, 41F

12/18 21:43, , 42F
但因為immutable的特性,才沒有修改成功
12/18 21:43, 42F

12/18 21:44, , 43F
我只是想說,如果你認為java的reference就是c++的pointer
12/18 21:44, 43F

12/18 21:45, , 44F
那在auto-boxing的結果下,你應該會覺得值要被修改才對
12/18 21:45, 44F

12/18 23:20, , 45F
immutable不是不能修改reference值,是不能修改object裡面
12/18 23:20, 45F

12/18 23:22, , 46F
的data,你還是直接看上面的C#的範例...
12/18 23:22, 46F

12/18 23:37, , 47F
既然會C++也給你一個C++版 http://ideone.com/DSzyGI
12/18 23:37, 47F

12/18 23:37, , 48F
不是我覺得,是java的reference變數真的就是pointer
12/18 23:37, 48F

12/18 23:38, , 49F
改是改pointer指的位址,但是在call by value的時候改不動
12/18 23:38, 49F

12/18 23:38, , 50F
caller func裡面的pointer變數
12/18 23:38, 50F

12/18 23:46, , 51F
很明顯就call by value/call by reference的差別啊..
12/18 23:46, 51F

12/18 23:49, , 52F
btw auto-boxing是指對只吃Object子class的地方(如list)
12/18 23:49, 52F

12/18 23:50, , 53F
自動包wrapper,unboxing是在assign給primitive變數的時候
12/18 23:50, 53F

12/18 23:51, , 54F
自動改為取值(.xxxValue)而不是assign reference
12/18 23:51, 54F

12/18 23:51, , 55F
如果全程都是直接用Integer這個class,那與一般class沒差別
12/18 23:51, 55F

12/18 23:53, , 56F
把Integer assign給Integer變數是不會有auto-box/unbox行為
12/18 23:53, 56F

12/19 07:28, , 57F
看完這整個討論串,好難過,想去renew Java 7的認證。
12/19 07:28, 57F

12/19 09:22, , 58F
我很想講得更清楚一點,但前面darkk6大已經畫得很清楚了
12/19 09:22, 58F

12/19 09:22, , 59F
這邊的重點應該是immutable/mutable的差別才對
12/19 09:22, 59F

12/19 09:23, , 60F
如果這邊是mutable的class,你說的caller func的pointer
12/19 09:23, 60F

12/19 09:24, , 61F
變數就會被改到
12/19 09:24, 61F

12/19 09:25, , 62F
因為我認為這邊的觀念很重要,才會一直強調,或許其他
12/19 09:25, 62F

12/19 09:25, , 63F
版友可以加入一起討論,來釐清這個問題?
12/19 09:25, 63F

12/19 09:41, , 64F
就算是mutable class,caller func的pointer還是不會改到
12/19 09:41, 64F

12/19 09:42, , 65F
因為是pass by value,在傳入時只是複製pointer的值
12/19 09:42, 65F

12/19 09:43, , 66F
d大的圖我只能說 // 因為字串的 Immutable... 這句完全錯誤
12/19 09:43, 66F

12/19 09:44, , 67F
不管是什麼class,用assign (=)的方式,改的永遠都是str2指
12/19 09:44, 67F

12/19 09:45, , 68F
向的位址(pointer value)而已,原本的那個object不會被動到
12/19 09:45, 68F

12/19 09:48, , 69F
要動原本的object就要用它的method/field
12/19 09:48, 69F

12/19 09:48, , 70F
immutable意義在於原本的object沒有公開的改值method/field
12/19 09:48, 70F

12/19 09:50, , 71F
用pointer來說的話就是要改object的值一定要先dereference
12/19 09:50, 71F

12/19 09:51, , 72F
用=只會改pointer存的位址
12/19 09:51, 72F

12/19 10:05, , 73F
你好像幻想lhs如果是個mutable reference type變數
12/19 10:05, 73F

12/19 10:06, , 74F
用=會自動把lhs指向的物件中代表value的field拿出來,然後
12/19 10:06, 74F

12/19 10:07, , 75F
把rhs expression物件中代表value的field裡存的值放進去
12/19 10:07, 75F

12/19 10:10, , 76F
從你上面提auto-boxing可以看出來,可是實際上沒這回事
12/19 10:10, 76F

12/19 10:11, , 77F
=只會把lhs reference直接改成指向rhs的結果物件
12/19 10:11, 77F

12/19 10:11, , 78F
lhs原本指向什麼完全沒差,在=的動作中那個物件根本沒出場
12/19 10:11, 78F

12/19 10:22, , 79F
=只有在lhs是primitive type的時候才是賦值(因為primitive)
12/19 10:22, 79F

12/19 10:22, , 80F
type才有所謂value,如果lhs是reference type,他指向的物
12/19 10:22, 80F

12/19 10:24, , 81F
件不一定有所謂的value,這時做的都只是改指向的物件
12/19 10:24, 81F
文章代碼(AID): #1Kai7iCd (java)
文章代碼(AID): #1Kai7iCd (java)