精華區beta java 關於我們 聯絡資訊
※ 引述《SuperNeo (潛水初號機)》之銘言: : ∣________∣ new : (指標?)c1--> ∣_Circle_∣--------- : ↑ ∣________∣ ∣ : ∣ ∣________∣ ∣ : ∣ ↓ : ∣ Circle的物件 : ∣ ________ : ∣ | | : ---------------------| | : 把Circle物件指向c1 (=?) |________| : 我理解是這個樣子 不知是不是了-_-" 雖然你想畫圖,但我覺得你的觀念好像有嚴重的錯誤。囧。 下面是非常低階的碎碎念,講的是現在市面上一般的程式書籍愈來愈少提到的 部份,至於看不看得完,看完後有沒有用,就要看造化了。XD 我只能說,自從我建立了以下的 Mental Model 後,基本上學任何 OO 或 程序導向語言都沒遇到過什麼困難(學 functional programming 的時候倒 是問題一堆),所以有用沒用就要看個人了。 看完一整個討論串,我先說結論吧: 1. 個人認為你需要的是一本入門的程式設計書籍,一整個討論串下來感覺你 很多基本觀念都沒有就想一步登天搞到進階的觀念。 2.老實說,我還是覺得把基礎功練好一點比較實在。orz... 什麼是基礎功? * 小人電腦 / Native Binary / VM / Bytecode / Memory Model 間的關聯 * Pointer (真正可以給你胡亂操作的指標,C 是最好的範例,但也最讓人害怕) * 如果你想學任何 OO 語言,先搞懂類別和物件的關聯吧 雖然就算講到嘴破了,還是有很多人覺得不懂第一點和第二點的那一大堆東 西無傷大雅,一樣可以寫程式。但我還是堅持這是基礎中的基礎,搞懂了學 什麼語言都不會有大問題。 回到正題,先不論多型的部份(你的程式碼裡 Shape a = new Circle 的那一 部份),單看下面這隻完整的 Java 程式好了。 ===== ClassExample.java ===== 1 public class ClassExample 2 { 3 int x = 3; 4 5 public static void main (String [] args) 6 { 7 int stackVar = 10; 8 9 ClassExample ex = new ClassExample (); 10 System.println ("ex.x = " + ex.x); 11 12 return; 13 } 14 } ============================ 我知道,接下來講的東西對很多初學者來說很瑣碎,但我認為從一開始就建立 正確並且一致的 Mental Model 會讓以後的學習順利的很多,很多進階的觀念 也比較容易吸收。 這隻程式在做什麼?『建立一個 ClassExample 物件,並且把這個物件裡面的 x 成員函數印出』,好簡單的一句話,好複雜的程式。 首先,你要了解一個程式可以大致區分成兩個部份:CODE 與 DATA,CODE 指 的是程式的『指令』,也就是大致相當於你寫出來的程式碼,告知電腦每一步 該如何做。 這個部份是永恆不變的,不管你執行幾次,只要你沒更動過你的程式碼,就永 永不會變。 DATA 是你程式裡放在『記憶體』裡的『資料』的部份,是會隨著環境和你程 式執行的過程中而更動的,例如程式裡的『變數』、『物件』都是屬於 DATA 的部份。 舉例來說 x = 10 這個敘述,『把 x 指定成 10』的動作是『CODE』,而 x 就 是 DATA,而這個 DATA 是放在記憶體中的。 『記憶體』,關鍵字出來了,所有『變數』、『物件』都是放在記憶體中的。 但『記憶體』並沒那麼簡單,『記憶體』又可以分成兩部份,一個是 stack,另 一個是 Heap ,請簡單記住以下幾個觀念: 1. Java 裡所有的區域變數、函數的所傳入的參數都放在 Stack。 2. Stack 的生命週期只有從『進入函式』到『離開函式』這段期間。 3. 所有物件都在 Heap 以上是關於 Stack 和 Heap 的原則,至於到底什麼是 Stack 和 Heap,請 Google 一下唄,了解這兩者是很有幫助的。 接下來,要了解的是關於『變數』這回事,Java 裡你會在函式裡看到三種型態的 變數宣告,分別如下: 1 void func () 2 { 3 int x = 10; // Type 1 4 Object object; // Type 2 6 int [] array; // Type 3 7 Object [] array2 // Type 4 8 9 object = new Object (); 11 array = new int[2] 11 array2 = new Object[2] 12 } 第一種(第三行的部份),資料型態小寫開頭的,那叫『基礎資料型態』,包括 內建的 int/double 這種東西,遇到這種的請記住這個口訣:『Stack 上長了一 塊叫做 x 的東西,他的內容是整數十(或浮點數 3.14 等)』。 第二種(第四行的部份),資料型態是大寫開頭的,也就是 Class 的部份,注 意,這是陷阱,請謹記這個口訣:『Stack 上長了一個叫做 object 的東西, 他的內容是「記憶體地址」,而且這個地址應該指到 Heap 裡面一個類別為 Object 的物件。』 第三種是第二種的特例,因為實際上 Java 的陣列也是物件,所以請記得以下的 口訣:『Stack 上長了一個叫做 array 的東西,他的內容是記憶體地址,而且這個 地址應該指到 Heap 裡面一個陣列物件,而這個陣列裡面的每個原素應該要是 int。』 第四種又是第三種的特例,不同的地方在於陣列裡每個原素存的也是『記憶體地 址』,口訣:『Stack 上長了一個叫做 array2 的東西,他的內容是記憶體地址, 而且這個地址應該指到 Heap 裡面一個陣列物件,而這個陣列裡面的每個原素也 是一個記憶體地址,而這些記憶體地址應該指到類別為 Object 的物件。』 ========================================================================= 請覆誦: Type 1:『Stack 上長了一塊叫做 x 的東西,他的內容是整數十。』 Type 2:『Stack 上長了一個叫做 object 的東西,他的內容是「記憶體地址」, 而且這個地址應該指到 Heap 裡面一個類別為 Object 的物件。』 Type 3:『Stack 上長了一個叫做 array 的東西,他的內容是記憶體地址,而且 這個地址應該指到 Heap 裡面一個陣列物件,而這個陣列裡面的每個原 素應該要是 int。』 Type 4:『Stack 上長了一個叫做 array2 的東西,他的內容是記憶體地址,而且 這個地址應該指到 Heap 裡面一個陣列物件,而這個陣列裡面的每個原 素也是一個記憶體地址,而這些記憶體地址應該指到類別為 Object 的 物件。』 PS. 上述口訣裡的『應該』都是有理由的,請不要省略,將來遇到多型的部份就不 會覺得奇怪了! ========================================================================= 累了,等下再繼續寫畫圖的部份。 -- ~ 白馬帶著她一步步地回到中原。白馬已經老了,只能慢慢地走, 'v' Brian Hsu 但終是能回到中原的。江南有楊柳、桃花,有燕子、金魚…… // \\ ( 墳 墓 ) /( )\ 但這個美麗的姑娘就像古高昌國人那樣固執。 【白馬嘯西風】 ^`~'^ http://bone.twbbs.org.tw/blog 『那都是很好很好的,可我偏不喜歡。』 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 59.120.199.114 ※ 編輯: brianhsu 來自: 59.120.199.114 (01/01 20:01)
dendrobium:好長的口訣XD 01/01 20:12
dendrobium:建議不要幾乎整段都畫重點,這樣等於沒畫...而且很傷眼 01/01 20:14
※ 編輯: brianhsu 來自: 59.120.199.114 (01/01 20:24)
ogamenewbie:其實我覺得整段畫有他的優點在, 但是顏色的確有點太亮 01/01 20:25
johnhmj:Stack長了一塊青春痘… XD 哈哈哈… 01/01 20:39
adrianshum:我覺得先不需要談stack和heap, 不然太多恐怕吸收不了 01/01 20:39
brianhsu:先談 Stack/Heap 才有辦法建立一致,將來也不會變的 01/01 20:43
brianhsu:Mental Model 啊,這是兩難。XD 01/01 20:44
brianhsu:例如 scope,GC 等,都和是在 Stack 或 Heap 有關的。 01/01 20:46
vencees:推好長的口訣XD 01/01 23:11
brianhsu:獨孤九劍光總訣式就三千字啦!這一點都不長。XD 01/01 23:13
> -------------------------------------------------------------------------- < 作者: brianhsu (墳墓) 看板: java 標題: Re: 搞懂變數、物件、參考。(Re: new 語法 … 時間: Fri Jan 1 20:55:28 2010 ======================================================= 1 void func () 2 { 3 int x = 10; // Type 1 4 Object object; // Type 2 6 int [] array; // Type 3 7 Object [] array2 // Type 4 9 10 object = new Object (); 11 array = new int[2] 12 array2 = new Object[2] 13 } ======================================================= 繼續,照上面的那四個口訣,進入這個函式之後的每一步,記憶體長 什麼樣子呢?其中問號代表不知道實際的值是什麼,每一次執行都有 可能不一樣(危險的事情,驚)! 另外,下面所有的圖你都不會看到 CODE 的部份,因為『CODE 永遠 不會變,會變的只有 DATA!』 1. inx x = 10; 我是 Stack 右邊都是 Heap +-----------+ 我叫 x | 10 | +-----------+ 2. Object object; 我是 Stack 右邊都是 Heap +-----------+ 我叫 object | ??? | +-----------+ 我叫 x | 10 | +-----------+ 3. int [] array; 我是 Stack 右邊都是 Heap +-----------+ 我叫 array | ??? | +-----------+ 我叫 object | ??? | +-----------+ 我叫 x | 10 | +-----------+ 4. Object [] array2; 我是 Stack 右邊都是 Heap +-----------+ 我叫 array2 | ??? | +-----------+ 我叫 array | ??? | +-----------+ 我叫 object | ??? | +-----------+ 我叫 x | 10 | +-----------+ 以上,是程式執行到第 7 行時記憶體裡的狀況,我們的人腦 CPU 總 算把變數給宣告和初始化完了(淚)! 接著來看 new 這個關鍵字吧,這個關鍵字其實說起來很簡單的。 ====================================================================== 程式碼 口訣 new Object (); 請在 Heap 裡生一個 Object 物件給我,並且返回這個物 件的『記憶體地址』 new int [2]; 請在 Heap 裡生一個長度為 2 的陣列物件給我,其中每 個元素存的應該是個整數,並且返回這個陣列物件的『          憶體地址』 new Object[2]; 請在 Heap 裡生一個長度為 2 的陣列物件給我,其中每 個元素存的是記憶體地址,而這些記憶體地址應該要指到 Heap 裡類別為 Object 的物件。最後,請返回這個陣列 物件的記憶體地址。 ====================================================================== 所以,以下的程式碼就很簡單地可以了解了吧!? 注意!以下的記憶體地址只是概念,實際上執行時由 VM 決定到底在哪,每一次 執行都有可能在不同的位值。 1. object = new Object (); // 請在 Heap 裡生一個 Object 物件給我,並且把 // 返回的記憶體地址塞到 Stack 上名為 object // 的東西裡。 我是 Stack 右邊都是 Heap +-----------+ 我叫 array2 | ??? | +-----------+ 我叫 array | ??? | +-----------+ +-----------------+ 我叫 object | 0x1234 | -----------> | Object 物件 | 我活在 0x1234 +-----------+ +-----------------+ 我叫 x | 10 | +-----------+ 2. array = new int [2]; // 請在 Heap 裡生一個長度為 2 的陣列物件給我,其中每個元素存的 // 應該是個整數。最後把這個陣列物件的記憶體地址塞給 Stack 上叫 // array 的東西裡。 我是 Stack 右邊都是 Heap +-----------+ +--------+--------+ 我叫 array2 | ??? | +->| 整數 | 整數 | 我活在 0x3456 +-----------+ | +--------+--------+ 我叫 array | 0x3456 | ----------+ +-----------+ +-----------------+ 我叫 object | 0x1234 | -----------> | Object 物件 | 我活在 0x1234 +-----------+ +-----------------+ 我叫 x | 10 | +-----------+ 3. array2 = new Object[2]; // 請在 Heap 裡生一個長度為 2 的陣列物件給我,其中每個元素存的 // 應該是個整數。最後把這個陣列物件的記憶體地址塞給 Stack 上叫 // array 的東西裡。 我是 Stack 右邊都是 Heap +--------+--------+ +----> | ??? | ??? | 我住在 0xA000,但 | +--------+--------+ 我沒有名字。 | +-----------+ | +--------+--------+ 我叫 array2 | 0xA000 | ------+ +->| 整數 | 整數 | 我住在 0x3456,但 +-----------+ | +--------+--------+ 我沒有名字。 我叫 array | 0x3456 | ----------+ +-----------+ +-----------------+ 我叫 object | 0x1234 | -----------> | Object 物件 | 我住在 0x1234,但 +-----------+ +-----------------+ 我沒有名字。 我叫 x | 10 | +-----------+ 到這邊,上面這個函式結束前,記憶體內的狀況長得就是這樣。 接下來換你囉,請問文章一開頭的這個程式,執行到第 10 行為止,記憶體 應該長什麼樣子呢? ===== ClassExample.java ===== 1 public class ClassExample 2 { 3 int x = 3; 4 5 public static void main (String [] args) 6 { 7 int stackVar = 10; 8 9 ClassExample ex = new ClassExample (); 10 System.println ("ex.x = " + ex.x); 11 12 return; 13 } 14 } ============================ 提示,你的 Heap 裡的 ClassExample 物件會長得像下面一樣。 我是 Stack 右邊都是 Heap +------------+ x | | +-----+--------------+ | | | 3 | ........... | 假設我活在 0x1234 ^^^^^^^^^^^^^^ +-----+--------------+ ^^^^^^^^^^^^^^ | | +------------+ 如果你能正確畫出來左邊 Stack 的圖,那基本上你對於 Java 裡面的 變數/參考/物件的觀念應該沒有太大的問題。 最後,提醒一下,所謂『內容是記憶體位址的變數』在 Java 裡就叫做 reference(參考),在其他程式語言裡有時會叫做 pointer(指標)。 -- ~ 白馬帶著她一步步地回到中原。白馬已經老了,只能慢慢地走, 'v' Brian Hsu 但終是能回到中原的。江南有楊柳、桃花,有燕子、金魚…… // \\ ( 墳 墓 ) /( )\ 但這個美麗的姑娘就像古高昌國人那樣固執。 【白馬嘯西風】 ^`~'^ http://bone.twbbs.org.tw/blog 『那都是很好很好的,可我偏不喜歡。』 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 59.120.199.114
starericc:推 01/01 21:00
dendrobium:應該把某位置再指到對應的物件上,不過可能畫不下XD 01/01 21:07
※ 編輯: brianhsu 來自: 59.120.199.114 (01/01 21:11) ※ 編輯: brianhsu 來自: 59.120.199.114 (01/01 21:26)
sbrhsieh:這樣子的觀念講的很清楚很好。為了完整性可能要在圖上 01/02 16:33
sbrhsieh:加註 stack 中的值與 heap 中物件的位置不見得是直接的。 01/02 16:35
sbrhsieh:或是使用這樣子的線條 -----//----->(有比較好嗎?) 01/02 16:38
> -------------------------------------------------------------------------- < 作者: brianhsu (墳墓) 看板: java 標題: Re: 搞懂變數、物件、參考。(Re: new 語法 … 時間: Fri Jan 1 21:16:53 2010 : 3. array2 = new Object[2]; : : // 請在 Heap 裡生一個長度為 2 的陣列物件給我,其中每個元素存的 : // 應該是個整數。最後把這個陣列物件的記憶體地址塞給 Stack 上叫 : // array 的東西裡。 : : 我是 Stack 右邊都是 Heap : +--------+--------+ : +----> | ??? | ??? | 我住在 0xA000,但 : | +--------+--------+ 我沒有名字。 : | : +-----------+ | +--------+--------+ : 我叫 array2 | 0xA000 | ------+ +->| 整數 | 整數 | 我住在 0x3456,但 : +-----------+ | +--------+--------+ 我沒有名字。 : 我叫 array | 0x3456 | ----------+ : +-----------+ +-----------------+ : 我叫 object | 0x1234 | -----------> | Object 物件 | 我住在 0x1234,但 : +-----------+ +-----------------+ 我沒有名字。 : 我叫 x | 10 | : +-----------+ : : 推 dendrobium:應該把某位置再指到對應的物件上,不過可能畫不下XD 01/01 21:07 不,這沒有錯,是故意這樣畫的。 實際上某地址並不會指到有效的問件。 object = new Object[2]; 只會產生『長度為 2 的陣列』這個物件而且,其中的『某地址』在這個 階段指到的是無效的記憶體位址,或乾脆就是 null。(在 JVM 1.6 裡 的行為是指到 null) 除非明再另外產生出物件給他們,例如: object[0] = new String ("1234"); object[1] = new Object (); 這樣『某地址』才會實際指到存在的物件。 -- ~ 白馬帶著她一步步地回到中原。白馬已經老了,只能慢慢地走, 'v' Brian Hsu 但終是能回到中原的。江南有楊柳、桃花,有燕子、金魚…… // \\ ( 墳 墓 ) /( )\ 但這個美麗的姑娘就像古高昌國人那樣固執。 【白馬嘯西風】 ^`~'^ http://bone.twbbs.org.tw/blog 『那都是很好很好的,可我偏不喜歡。』 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 59.120.199.114
dendrobium:那為了保持一致性,應該也改成 ??? 或 null 吧 01/01 21:23
你是對的,我把他改過來了。:p ※ 編輯: brianhsu 來自: 59.120.199.114 (01/01 21:27)
dendrobium:因為我不會java,所以寫某位置我會以為有新物件產生 01/01 21:29
johnhmj:布萊恩老師說:「我快要掛點了… X0」~ 01/01 21:39
sbrhsieh:array element 會 initialize 成 element type 對應的 01/02 16:56
sbrhsieh:預設值,應該是從 Java 最初的版本就有 01/02 16:58