Java解惑 55 -- 特創論

來源:互聯網
上載者:User

標籤:java解惑

某些時候,對於一個類來說,跟蹤其建立出來的執行個體個數會非常用有,其典型實現是通過讓它的構造器遞增一個私人靜態域來完成的。在下面的程式中,Creature類展示了這種技巧,而Creator類對其進行了操練,將列印出已經建立的Creature執行個體的數量。那麼,這個程式會列印出什麼呢?


<span style="font-size:18px;">public class Creator {    public static void main(String[] args) {        for (int i = 0; i < 100; i++)            Creature creature = new Creature();        System.out.println(Creature.numCreated());    }}class Creature {    private static long numCreated = 0;    public Creature() {        numCreated++;    }    public static long numCreated() {        return numCreated;    }}</span>


這是一個捉弄人的問題。該程式看起來似乎應該列印100,但是它沒有列印任何東西,因為它根本就不能編譯。如果你嘗試著去編譯它,你就會發現編譯器的診斷資訊基本沒什麼用處。下面就是javac列印的東西:

<span style="font-size:18px;">Creator.java:4: not a statement            Creature creature = new Creature();            ^Creator.java:4: ';' expected            Creature creature = new Creature();                            ^</span>


一個本地變數聲明看起來像是一條語句,但是從技術上說,它不是;它應該是一個本地變數聲明語句(local variable declaration statement)[JLS 14.4]。

Java語言規範不允許一個本地變數聲明語句作為一條語句在for、while或do迴圈中重複執行[JLS 14.12-14]。一個本地變數聲明作為一條語句只能直接出現在一個語句塊中。(一個語句塊是由一對花括弧以及包含在這對花括展中的語句和聲明構成的。)
有兩種方式可以訂正這個問題。最顯而易見的方式是將這個聲明至於一個語句塊中:


<span style="font-size:18px;">for (int i = 0; i < 100; i++) {     Creature creature = new Creature();}</span>

然而,請注意,該程式沒有使用本地變數creature。因此,將該聲明用一個無任何修飾的構造器調用來替代將更具實際意義,這樣可以強調對新建立對象的引用正在被丟棄:
for (int i = 0; i < 100; i++)
     new Creature();
無論我們做出了上面的哪種修改,該程式都將列印出我們所期望的100。
請注意,用於跟蹤Creature執行個體個數的變數(numCreated)是long類型而不是int類型的。我們很容易想象到,一個程式建立出的某個類的執行個體可能會多餘int數值的最大值,但是它不會多於long數值的最大值。
int數值的最大值是231-1,即大約2.1×109,而long數值的最大值是263-1,即大約9.2×1018。當前,每秒鐘建立108個對象是可能的,這意味著一個程式在long類型的對象計數器溢出之前,不得不運行大約三千年。即使是面對硬體速度的提升,long類型的對象計數器也應該足以應付可預見的未來。
還要注意的是,本謎題中的建立計數策略並不是安全執行緒的。如果多個線程可以並行地建立對象,那麼遞增計數器的代碼和讀取計數器的代碼都應該被同步:
<span style="font-size:18px;">// Thread-safe creation counterclass Creature {    private static long numCreated;    public Creature() {        synchronized (Creature.class) {            numCreated++;        }    }    public static synchronized long numCreated() {        return numCreated;    }}</span>


或者,如果你使用的是5.0或更新的版本,你可以使用一個AtomicLong執行個體,它在面臨並發時可以繞過對同步的需求。
<span style="font-size:18px;">// Thread-safe creation counter using AtomicLong;import java.util.concurrent.atomic.AtomicLong;class Creature {    private static AtomicLong numCreated = new AtomicLong();    public Creature() {        numCreated.incrementAndGet();    }    public static long numCreated() {        return numCreated.get();    }}</span>


請注意,把numCreated聲明為瞬時的是不足以解決問題的,因為volatile修飾符可以保證其他線程將看到最近賦予該域的值,但是它不能進行原子性的遞增操作。
總之,一個本地變數聲明不能被用作for、while或do迴圈中的重複執行語句,它作為一條語句只能出現在一個語句塊中。另外,在使用一個變數來對執行個體的建立進行計數時,要使用long類型而不是int類型的變數,以防止溢出。最後,如果你打算在多線程中建立執行個體,要麼將對執行個體計數器的訪問進行同步,要麼使用一個AtomicLong類型的計數器。

Java解惑 55 -- 特創論

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.