JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Java SE 討論區  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友   
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 [轉錄]全世界所有程式員都會犯的錯誤(初始化) [精華]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-14 19:23 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
http://www.oreilly.com.tw/sleepless2/mistake.html

有點舊的文章, 提供給大家參考參考
(我覺得後面的style和duncan好像啊....)

[技術短文]
全世界所有程式員都會犯的錯誤

當年,國際巨星成龍的「龍種」曝光,眾人指責他對不起嬌妻林鳳嬌,逼得他出面召開記者會,向世人自白他犯了「全世界所有男人都會犯的錯誤」。從來沒犯過這種錯誤的我,也因此常常認為自己不是個男人。

雖然沒犯過「全世界所有男人都會犯的錯誤」,但是我倒是曾經犯了「全世界所有程式員都會犯的錯誤」。不管使用何種語言,全世界所有程式員都一定犯過這種錯誤,那就是:太依賴編譯器,卻不知道編譯器做了哪些事。

一般來說,越高階的程式語言,會提供越多語法上的便利,以方便程式撰寫,這就俗稱為 syntax sugar,我稱其為「語法上的甜頭」。雖說是甜頭,但是如果你未能瞭解該語法的實質內涵,很可能會未嘗甜頭,卻吃盡苦頭。

不久前,我收到一個電子郵件,讀者列出下面的 Java 程式,向我求救。看過這個程式之後,我確定這又是一個「全世界所有程式員都會犯的錯誤」。

// 程式 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Singleton {
 
 private static Singleton obj = new Singleton();
 
 public static int counter1;
 
 public static int counter2 = 0;
 
 private Singleton() {
 
  counter1++;
 
  counter2++;
 
 }
 
 public static Singleton getInstance() {
 
  return obj;
 
 }
 
}

// 程式 2
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyMain {
 
 public static void main(String[] args) {
 
  Singleton obj = Singleton.getInstance();
 
  System.out.println("obj.counter1=="+obj.counter1);
 
  System.out.println("obj.counter2=="+obj.counter2);
 
 }
 
}

執行結果是:

obj.counter1==1
obj.counter2==0

你有沒有被此結果嚇一跳?乍看程式碼,你很可能會認為 counter1 和 counter2 的值一定會相等,但執行結果顯然不是如此。其實,程式 1 被編譯後的程式應該等同於下面的程式 3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 程式 3
class Singleton {
 private static Singleton obj;
 public static int counter1;
 public static int counter2;
 static { // 這就是 class constructor
  // 在進入此 class constructor 之前,class 已經被 JVM
  // 配置好記憶體,所有的 static field 都會被先設定為 0,
  // 所以此時 counter1 和 counter2 都已經是 0,且 singleton 為 null
  obj = new Singleton(); // 問題皆由此行程式產生
  // counter1 不會在此被設定為 0
  counter2 = 0; // counter2 再被設定一次 0(其實是多此一舉)
 }
 private Singleton() { // 這是 instance constructor
  counter1++;
  counter2++;
 }
 public static Singleton getInstance() {
  return obj;
 }
}

這是因為:當 class 具有 static field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到 class constructor 內。同樣地,當 class 具有 instance field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到 instance constructor 內。

此程式在 class constructor 內,還未將 static field 初始化時(這時候,counter1 和 counter2 都是 0),就呼叫 instance constructor,而 instance constructor 竟然還會去更動 static field 的值,使得 counter1 和 counter2 都變成 1。然後 instance constructor 執行完,回到 class constructor,再把 counter2 的值設為 0(但是 counter1 維持不變)。最後的結果:counter1 等於 1,counter2 等於 0。

欲改正程式 1,方法有三:

* 方法一:將 singleton field 的宣告調到 counter1 與 counter2 field 之後。這是最好的作法。
* 方法二:將 counter2=0 的宣告中,「=0」的部分刪除。這種作法只有在希望 counter2 的初始值是 0 時才有效。
* 方法三:將初始化的動作搬到 class constructors 內,自行撰寫,而不依賴編譯器產生。這是最保險的作法。

如何避免犯下「全世界所有程式員都會犯的錯誤」,我給各位 Java 程式員的建議是:

* 熟讀 Java Language Specification
* 在有疑問時,使用 J2SDK 所提供的 javap 來反組譯 Java Bytecode,直接觀察編譯後的結果。

下面是我用 javap 來反組譯程式 1 的示範:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
C:\>javap -c -classpath . Singleton
 
Compiled from MyMain.java
class Singleton extends java.lang.Object {
 public static int counter1;
 public static int counter2;
 public static Singleton getInstance();
 static {};
}
 
Method Singleton()
 0 aload_0
 1 invokespecial #1 <Method java.lang.Object()>
 4 getstatic #2 <Field int counter1>
 7 iconst_1
 8 iadd
 9 putstatic #2 <Field int counter1>
 12 getstatic #3 <Field int counter2>
 15 iconst_1
 16 iadd
 17 putstatic #3 <Field int counter2>
 20 return
 
Method Singleton getInstance()
 0 getstatic #4 <Field Singleton obj>
 3 areturn
 
Method static {}
 0 new #5 <Class Singleton>
 3 dup
 4 invokespecial #6 <Method Singleton()>
 7 putstatic #4 <Field Singleton obj>
 10 iconst_0
 11 putstatic #3 <Field int counter2>
 14 return

其實 Java 的 syntax sugar 並不算多,C# 的 syntax sugar 才真的是無所不在,也因此 C# 的初學者更容易犯了「全世界所有程式員都會犯的錯誤」。許多 C# 的書都會一邊介紹 C# 語法,一邊介紹編譯之後 MSIL(.NET 的中間語言,類似 Java 的 Bytecode)的結果,然而 Java 的書卻鮮少這麼做。

雖說是「全世界所有程式員都會犯的錯誤」,但是這不代表你犯了此錯誤之後,仍可以和老是愛借錢的曹啟泰一般地「抬頭挺胸、理直氣壯」。只要有心,其實這一類的錯誤仍是可以避免的。



本文作者:蔡學鏞
文章出處:Sleepless 2.0
張貼日期:03/10/2003
Sleepless2.0
Sleepless in Java
 


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-14 23:40 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
* 在有疑問時,使用 J2SDK 所提供的 javap 來反組譯 Java Bytecode,直接觀察編譯後的結果。

下面是我用 javap 來反組譯程式 1 的示範:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
C:\>javap -c -classpath . Singleton
 
Compiled from MyMain.java
class Singleton extends java.lang.Object {
 public static int counter1;
 public static int counter2;
 public static Singleton getInstance();
 static {};
}
 
Method Singleton()
 0 aload_0
 1 invokespecial #1 <Method java.lang.Object()>
 4 getstatic #2 <Field int counter1>
 7 iconst_1
 8 iadd
 9 putstatic #2 <Field int counter1>
 12 getstatic #3 <Field int counter2>
 15 iconst_1
 16 iadd
 17 putstatic #3 <Field int counter2>
 20 return
 
Method Singleton getInstance()
 0 getstatic #4 <Field Singleton obj>
 3 areturn
 
Method static {}
 0 new #5 <Class Singleton>
 3 dup
 4 invokespecial #6 <Method Singleton()>
 7 putstatic #4 <Field Singleton obj>
 10 iconst_0
 11 putstatic #3 <Field int counter2>
 14 return
 

可能是我等級太低Dead,反組譯 Java Bytecode也看不太出來其中的涵義…


reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-15 00:43 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list

可能是我等級太低,反組譯 Java Bytecode也看不太出來其中的涵義…

compare source code and javap's output, it is easy to understand what's going wrong...
1
2
3
4
5
6
class Singleton extends java.lang.Object { 
  public static int counter1; 
  public static int counter2; 
  public static Singleton getInstance();
  static {};
}

this code defines counter1, counter2, method getInstance() and then call static{},
at this time counter1 == counter2 == 0

OK , now let's move on static{} call
1
2
3
4
5
6
7
8
Method static {} 
  0 new #5 <Class Singleton> 
  3 dup 
  4 invokespecial #6 <Method Singleton()> 
  7 putstatic #4 <Field Singleton obj> 
  10 iconst_0 
  11 putstatic #3 <Field int counter2> 
  14 return

this is because "private static Singleton obj = new Singleton();"
so it new object (0) and call constrcutor(4) and save the object to obj (7)
After (4) counter1==counter2==1;
Now, the problem, because we have "public static int counter2 = 0;"
declare and intialize, declare already did, now it is initialze part,
the line (10) is initialize the static the counter2 to zero
...
etc
Got the light ?


T55555 edited on 2003-09-15 00:47
reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-15 01:29 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
謝謝你的解說,我大概知道了Wink
But still have one problem , i don't know what mean of dup is...

--
多看多用更能了解它,順便練練 E Big Smile


UndeadJ edited on 2003-09-15 01:51
reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-15 02:16 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
dup = duplicate, why to do that ... etc
check the java bytecode explanation.
( if you are interesting in bytecode stuff. BTW there are also bytecode compiler, you can program java by bytecode instructions :) )

This is clasic problem, order is important, method override is important
(expectially, method override is be called by constructor) ... etc
"Thinking in Java" already point out longtime ago.

Here is my example to show you more clear what happen...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class A {
  public static A a = new A();
  public static int i = 5;
  public A() {
    System.out.println(a);
    System.out.println(i);
  }
  public void print() {
    System.out.println(a);
    System.out.println(i);
  }
  public static void main(String[] args) {
    A.a.print();
  }
}

See the output result, and you know what's going wrong...
It is kind of like you are written the example like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class A {
  public static A a;  // declare, and by default init to null
  public static int i; // declare, and by default init to zero
  static {
    a = new A();  // change the a
    i = 5;            //  change the i
  }
  public A() {
    System.out.println(a);
    System.out.println(i);
  }
  public void print() {
    System.out.println(a);
    System.out.println(i);
  }
  public static void main(String[] args) {
    A.a.print();
  }
}

And for the overriden method problem, there are sure examples on Thinking in Java, I want not say more.


T55555 edited on 2003-09-15 02:23
reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-15 02:33 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
非常感謝,我已經了解了Smile

UndeadJ edited on 2003-09-15 02:47
reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:T55555]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-15 16:10 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
T55555 wrote:
This is clasic problem, order is important, method override is important
(expectially, method override is be called by constructor) ... etc
"Thinking in Java" already point out longtime ago.


請問這段話是什麼意思?
我搜尋了一下Thinking in Java...可是找不到好的關鍵字
找出來的result太多了, 所以直接請教你


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-15 17:17 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
請問這段話是什麼意思?
我搜尋了一下Thinking in Java...可是找不到好的關鍵字
找出來的result太多了, 所以直接請教你


不就是一本書的書名嗎?還是我會錯意What are you talking about?


UndeadJ edited on 2003-09-15 17:22
reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:UndeadJ]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-15 17:22 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
UndeadJ wrote:
不就是一本書的書名嗎?還是我會錯意What are you talking about?


我指的是這句話
"expectially, method override is be called by constructor"


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-15 17:46 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
我指的是這句話
"expectially, method override is be called by constructor"


sorry,我會錯意了~Dead

我想是這句話再加上上面的那段話,所以讓您不知道整句的意思Smile

我這句話(only)自己翻的意思是"預期的到,method override將會被constructor呼叫"(我想這句你也知道,應該也翻的比較好Big Smile),但整句(加上面)其中的內浛我也不太清楚(他下面有說Thinking in Java 中有提到,不過我還沒看到)

--
又丟人了Cry


reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:UndeadJ]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-15 17:54 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
UndeadJ wrote:
sorry,我會錯意了~Dead

我想是這句話再加上上面的那段話,所以讓您不知道整句的意思Smile

我這句話(only)自己翻的意思是"預期的到,method override將會被constructor呼叫"(我想這句你也知道,應該也翻的比較好Big Smile),但整句(加上面)其中的內浛我也不太清楚(他下面有說Thinking in Java 中有提到,不過我還沒看到)


對啊...我也是翻的和你一樣
只是翻成中文也看不懂啊...


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-15 17:55 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
請問這段話是什麼意思?
我搜尋了一下Thinking in Java...可是找不到好的關鍵字
找出來的result太多了, 所以直接請教你

後來又看了一下你問題,我想我可以去參加中文死當班了Dead(之前以為search
Thinking in Java...是用google…)
上面的回答就當我沒回過…Tongue

--
離題了,還是留給T55555前輩(or 其它知道的前輩回吧)


UndeadJ edited on 2003-09-15 17:58
reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-15 21:18 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
唉... 真是對不起.
本人英文差, 又懶得打中文...
希望大家只對我的 Java 來討論, 不要在意我的 English.
(我正在學習 English... 唉, 現在我的 English, Francais
都少過半半桶水 ...)

"Thinking in Java" 是大家都知道的 Free Book.

這個帖子讓我想起了 method overriden been called in constructor problem.
我只是覺得 method overriden been called in constructor problem 好像
在 "Thinkg in Java" book 中提到過...

想要討論 "method overriden been called in constructor problem" 嗎?
If yes, open a new thread.

再一次,
Sorry for my poor English.
( My English sometime is just borrow my poor French,
Je suis desole pour ma mauvais Francais.)
-------------------------------------------------------------------------------
"method overriden been called in constructor problem" 是指:
如果super constructor call method A, and you overriden method A
in the inheritance class, you may got trouble.
Again, this is out of subject of this thread, If you like to discuss more,
open new thread.
-------------------------------------------------------------------------------
不要叫我"前輩"
我還沒有到那個水平, 而且也不老. Smile


T55555 edited on 2003-09-15 21:32
reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:T55555]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-15 22:55 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
這一篇是我以前的助教寫的(蔡寶進先生),老實說我並不認同其標題的說法『全世界所有程式員都會犯的錯誤』,說的好像非掉進不可的陷阱一樣。

換做是我,我會說這是全世界懶惰或粗心的 Java 程式員都會犯的錯,真的犯了這個錯,並沒有什麼大不了,但是個改掉壞習慣的好時機。真要說起來是 Java 編譯器把程式員寵壞了,程式員吃糖吃習慣了才會犯這樣的錯。

Java bytecode 裡紀錄 filed info 的部分是有一欄可以放 primitive/String type 的 constant value 用來充 static final filed(當然只能是 primitive or String type) 的初值註一。這個欄位的用途在程式員寫出這樣的碼時會用到:
1
2
3
4
public class foo implements java.io.Serializable {
  private static final long serialVersionUID = -6849794470754667710;
  //...
}


這樣的寫法來給定一 static final filed 初值是大家很習慣也很正確的寫法,但是如果這個 filed 不是 primitive/String type 的話,你就不應該這樣寫,以 Java language spec 來說,這樣寫是正確的,但是一定要知道這是一種方便的用法而已(compiler sugar),編譯器實際上是幫你寫 class initializer,然後在裡面幫你設定 static final filed 的值,這是因為 object reference type 的 field 除非是預設為 null,否則要 assign 一個 object reference 給 field 一定要透過一些 instructions 的組合先取得某個 valid object reference(不外乎是建構一個 object,或是存取某個 Class 之 static field,或是 call 某個會傳回 reference 的 static method),所以沒有辦法把 static final field(object reference type)的初值直接放在 bytecode 裡面。

前一段提及的 ConstantValue attribute 是設計給 static final primitive/String type field 使用的註二。那麼對於不是 primitive/String type 的 static final field 以及任何 type 的 static filed,我認為最好是不要養成寫在 filed 宣告之處的習慣,一來是因為這樣你才能掌握每個 filed 設定初值的時機,寫在宣告處就會由編譯器幫你作初始化的動作在 class initializer 裡,如果初始化的順序很重要,由自己來寫不是可以安心一點;二來,可能因個人習慣不同與 IDE tool 的使用而顯得不是那麼重要,我個人的習慣是不會把 field 集中寫在一起,所以對我來說如果要更改某個 static field 的初值時尋找 field 的定義之處是個麻煩,找 class initializer 會比較方便(當然有 IDE tool 就沒這個問題啦)。同樣的是也會發生在 non-static field 身上,直接設定初始值在 field 定義之處,一定要知道是由編譯器幫你寫在 constructor 裡,如果初始化的順序很重要,不妨改成自己在 constructor 裡寫初始化的動作。

註一
關於 static final primitive/String field 有初始值放在 class bytecode 裡這一點,和我先前的一篇"static final field 帶來的隱憂"有關聯,編譯器在為 static final primitive/String field 作 inline 的動作時,只要從 bytecode 讀取 field info 部分就可以知道 static final field 的值,然後產生將相同的值推入堆疊的 instructions 就可以替換掉原本應該透過 getstatic instruction 來取得 static field 的方式,提昇效率。如果 static final field 的初始化動作同 static non-final field 一般,都是在 class initializer 裡透過 putstatic instruction 來設值,那麼編譯器還得去分析 class initializer(其實是一個 static method named: <clinit>)的 instructions 去找出將 assign 給某個 static final field 的值。

註二
將 ConstantValue attribute 裡參考的值 assign 給 static final field 是發生在執行 class initializer 之前。雖然 ConstantValue attribute 是設計給 static final filed 用,但我私底下測試過,如果一個 static field 的 flag 沒有 setup ACC_FINAL bit,這個 ConstantValue attribute 還是有用(編譯器是只有在一個 field 為 static final 又有設初值時,才會幫你在 bytecode 裡 field info 部分產生 ConstantValue attribute)。


Duncan edited on 2003-09-16 00:46
reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Duncan]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-16 02:30 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Duncan wrote:
這一篇是我以前的助教寫的(蔡寶進先生),老實說我並不認同其標題的說法『全世界所有程式員都會犯的錯誤』,說的好像非掉進不可的陷阱一樣。

換做是我,我會說這是全世界懶惰或粗心的 Java 程式員都會犯的錯,真的犯了這個錯,並沒有什麼大不了,但是個改掉壞習慣的好時機。真要說起來是 Java 編譯器把程式員寵壞了,程式員吃糖吃習慣了才會犯這樣的錯。


我說句老實話
duncan這篇我看不懂...


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-16 02:40 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
這是因為:當 class 具有 static field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到 class constructor 內。同樣地,當 class 具有 instance field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到 instance constructor 內。


我有二個問題想要請教大家
1.
1
static { ....} 這稱為class constructor
1
public ClassName() {...} 這稱為instance constructor
1
{ ...} 那這個應該稱做什麼? 我以為這個是instance constructor...


2.上文提到編繹器會自動將instance field移到instance constructor之內
指的是1.中提到的第二個? 這要怎麼移啊....? 總覺得是第三個比較合理耶....

thx.


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-16 03:04 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
2.上文提到編繹器會自動將instance field移到instance constructor之內
指的是1.中提到的第二個? 這要怎麼移啊....? 總覺得是第三個比較合理耶....


I think the {...} is still part of instance constructor, and it is exec before
the constructor "public X() { ... } "
As normal, write a little code to check it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Demo {
  int a;
  public Demo() {
    System.out.println("in F(), a = " + a);
    a = 4;
  }
 
  {
    System.out.println("{...}, a = " + a);
    a = 3;
  }
 
  public static void main(String[] args) {
    new Demo();
  }
    
}

You can see even the {...} is after public Demo() {...}
it still exec first! (of course, if you have many {...} {...},
that will exec in the order you write them )

Why we need {...}, why not just move it in public Demo() {...},
My personal opinion is, this is useful for anonymous inner class.
Anonymous ==> How can you write a "Anonymouse" constructor ??
So use {...} for having like constructor effect!


T55555 edited on 2003-09-16 03:17
reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-16 03:14 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
2.上文提到編繹器會自動將instance field移到instance constructor之內
指的是1.中提到的第二個? 這要怎麼移啊....? 總覺得是第三個比較合理耶....


Answer: that do not matter! It is still part of constructor!
(All will be part of constructor! )

Check the source code :
1
2
3
4
5
6
7
8
9
public class F {
  int a = 3;
  public F() {
    a = 4;
  }
  {
    a = 5;
  }
}


And javap -c F

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Compiled from F.java
public class F extends java.lang.Object {
    int a;
    public F();
}
 
Method F()
   0 aload_0
   1 invokespecial #1 <Method java.lang.Object()>
   4 aload_0
   5 iconst_3
   6 putfield #2 <Field int a>
   9 aload_0
  10 iconst_5
  11 putfield #2 <Field int a>
  14 aload_0
  15 iconst_4
  16 putfield #2 <Field int a>
  19 return

See the bytecode! a = 3 or { a = 5 } or a init in constructor,
after compile, all are in constructor! ( but order is important! )


reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:T55555]
Yoshi

塵世中一個迷途小書僮

版主

發文: 874
積分: 22
於 2003-09-16 03:29 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
T55555 wrote:
See the bytecode! a = 3 or { a = 5 } or a init in constructor,
after compile, all are in constructor! ( but order is important! )


多謝!
javap是個好用的工具, 受教了!


reply to postreply to post
YOSHI!
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Yoshi]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-16 11:03 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Yoshi wrote:
我有二個問題想要請教大家
1.
1
static { ....} 這稱為class constructor
1
public ClassName() {...} 這稱為instance constructor
1
{ ...} 那這個應該稱做什麼? 我以為這個是instance constructor...


2.上文提到編繹器會自動將instance field移到instance constructor之內
指的是1.中提到的第二個? 這要怎麼移啊....? 總覺得是第三個比較合理耶....

thx.


我當初第一次知道有 {} 這種 instance initializer 的用法時,真是有點驚訝,Java compiler 糖未免太多了一點。就像 T55555 說,instance initializer 的部分其實會成為 constructor 的一部份。(所以你說 field initializer 會移到 instance initializer 也沒錯,但最終而言,instance initializer 其實也是"各個" constructor 的一部份。)

比較需要注意的是,有寫初值的 field 宣告和 instance initializer 的真正的動作是在 constructor,編譯器會將這些動作一一寫到每一個 overloading constructor 裡,這些動作的順序是 field initializer, instance initializer > constructor。field initialzer 與 instance initializer 則是依照 definition 在 source code 的先後順序。

另外 instance/class initializer(block) 是可以拆開寫的,編譯器會將他們合併在一起產生 instructions 在 constructor/class constructor(initializer), respectively。如果拆開成好幾個 block 來寫,block 的順序會是他們出現的 constructor/class constructor 裡的順序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// com.sun.image.codec.jpeg.JPEGQTable
package com.sun.image.codec.jpeg;
 
public class JPEGQTable {
 
  /** Quantization step for each coefficient in zig-zag order */
  private int quantval[];
 
  /** The number of coefficients in a DCT block */
  private static final byte QTABLESIZE = 64;
  
  /** 
   * This is the sample luminance quantization table given in the
   * JPEG spec section K.1, expressed in zigzag order. The spec says
   * that the values given produce "good" quality, and when divided
   * by 2, "very good" quality.
   */
  public static final JPEGQTable StdLuminance = new JPEGQTable();
  static {
    int [] lumVals = {
      16,   11,  12,  14,  12,  10,  16,  14,
      13,   14,  18,  17,  16,  19,  24,  40,
      26,   24,  22,  22,  24,  49,  35,  37,
      29,   40,  58,  51,  61,  60,  57,  51,
      56,   55,  64,  72,  92,  78,  64,  68,
      87,   69,  55,  56,  80, 109,  81,  87,
      95,   98, 103, 104, 103,  62,  77, 113,
      121, 112, 100, 120,  92, 101, 103,  99
    };
    
    StdLuminance.quantval = lumVals;
  }
 
  /** 
   * This is the sample luminance quantization table given in the
   * JPEG spec section K.1, expressed in zigzag order. The spec says
   * that the values given produce "good" quality, and when divided
   * by 2, "very good" quality.
   */
  public static final JPEGQTable StdChrominance = new JPEGQTable();
  static {
    int [] chromVals = {
      17,  18,  18,  24,  21,  24,  47,  26,
      26,  47,  99,  66,  56,  66,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99,
      99,  99,  99,  99,  99,  99,  99,  99
    };
    StdChrominance.quantval = chromVals;
  }
    
 
  /** 
   * Constructs an empty quantization table. This is used to create
   * the Std Q-Tables.
   */
  private JPEGQTable() {
    quantval = new int[QTABLESIZE];
  }
    // ....略
}



Duncan edited on 2006-04-11 17:41
reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:Duncan]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-09-16 22:42 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Duncan wrote:
我當初第一次知道有 {} 這種 instance initializer 的用法時,真是有點驚訝,Java compiler 糖未免太多了一點。

Like I said, if Java has Anonymous Inner Class, it seems to me, that the instance initializer must to have! it is not compiler's sugar.
(Do not argue here about inner class, please Smile )
Again, a little "artificial" Demo... ( and Swing )
1
2
3
4
5
JFrame f = new JFrame();
f.setContentPane(new JTable(5,5) {
   public boolean isCellEditable(int row, int column) { return false; }
   { setBackground( Color.RED ); } // How can you put this in "constructor"?
});

Please, it is just "artificial" demo, don't tell me should not use inner class...
The main idea is, sometime, you need an Anonymous Inner Class,
and you need do some actions in constructor time,
the only way is using instance initializer, it is not sugar, because,
there are no other way in this situation to achieve the same effect!
(As I said, how can you create an "Anonymous" constructor ?!)
(Of couse, any anonymous classes can be rewrite to "no-anonymous" classes Smile )
當然, 如果你說 Inner Class 是大糖, 相對的, instance initilializer 就是小糖, 我無話可說.


T55555 edited on 2003-09-16 23:27
reply to postreply to post
作者 Re:[轉錄]全世界所有程式員都會犯的錯誤(初始化) [Re:T55555]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-17 01:31 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
T55555 wrote:
當然, 如果你說 Inner Class 是大糖, 相對的, instance initilializer 就是小糖, 我無話可說.


我瞭解你要表達的。Smile

Inner Class 不是大糖,是脂肪,他的出現讓 Java compiler 胖了不少。如果沒有 inner class 的用法,Java 會更簡潔,一大堆 sugar 也不需要存在了,最重要的是為 inner class,其外覆 class 不得不開後門。


reply to postreply to post

給我
辣味豆腐 其餘免談
» JWorld@TW »  Java SE 討論區

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8