2012-08-15 107 views
15

我的代碼是:Java代碼片斷輸出解釋需要

class Foo { 
    public int a=3; 
    public void addFive() { 
    a+=5; 
    System.out.print("f "); 
    } 
} 

class Bar extends Foo { 
    public int a=8; 
    public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
    } 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    f.addFive(); 
    System.out.println(f.a); 
    } 
} 

輸出:

b 3 

請向我解釋,爲什麼是這個問題的 「B 3」,而不是「B 13的輸出「因爲該方法已被覆蓋?

+0

這是一個關於SCJP的問題,對不對? – 2012-08-15 17:58:10

+0

yep @PrakharMohan你是否也出現過相同的?我還沒有... :) – 2012-08-15 18:00:10

回答

14

不能覆蓋變量在Java中,因此你實際上有兩個a變量 - 一個在Foo,一個在Bar。另一方面,addFive()方法是多態的,因此它修改了Bar.a(調用了Bar.addFive(),儘管靜態類型fFoo)。

但最終您訪問f.a,並且在編譯期間使用已知類型的f(即Foo)解析此引用。因此從未觸及過Foo.a

Java中的BTW非最終變量應該是從不被公開。

+4

好了,除非你有'GridBagConstraints'或類似物體。但對於初學者,**從來沒有**是一個適當的建議:) – 2012-08-15 18:37:21

18

FFoo類型的引用,和變量不是多態的,以便f.a是指從Foo這是3

如何驗證它的變量?

爲了測試這一點,你可以從Foo刪除a變量,它會給你編譯時錯誤

注:製作成員變量private和使用訪問器來訪問他們


還見

+10

爲了擴展這個答案,這是爲什麼通常使用getter而不是直接訪問變量更安全的原因之一。 – 2012-08-15 17:55:29

+0

@DaveNewton請告訴我一些關於getters的內容?我以前沒有聽說過。 – 2012-08-15 17:59:10

+2

如果在Java中使用通用編程習慣,可以安靜地避免此問題:變量必須是私有的(只有自己的類可以使用它)。之後,如上所述,使用getters和setter方法來訪問/修改它。 – axcdnt 2012-08-15 18:00:49

1

由於您在做f.a,您將從類Foo中獲得值a。如果您已經調用了一種方法來獲取該值,例如getA()那麼您將從類Bar中獲得該值。

11

有了這樣一個問題,SCJP考試正在評估你所知的隱藏。審查員故意將事情複雜化,試圖讓你相信程序的行爲只依賴於多態,而事實並非如此。

讓我們試着讓事情更清晰一些,因爲我們刪除了addFive()方法。

class Foo { 
    public int a = 3; 
} 

class Bar extends Foo { 
    public int a = 8; 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    System.out.println(f.a); 
    } 
} 

現在事情有點混亂。main方法聲明類型Foo的變量,該變量在運行時分配了類型爲Bar的對象。這是可能的,因爲Bar繼承自Foo。然後程序參考Foo類型的變量的公共字段a

這裏的錯誤應該是相信被稱爲的同一種概念覆蓋適用於類字段。但有沒有這樣的字段的一個概念:公共領域Bar類的a壓倒一切Foo類的公共領域a但它確實是所謂隱藏。顧名思義,這意味着在Bara等級範圍內的Bar自己的領域與Foo無關。 (JLS 8.4.8 - Inheritance, Overriding, and Hiding

那麼,當我們在寫f.a時,我們所指的是a?回想一下,a字段的解析是在編譯時間完成的,使用對象f的聲明類型,即Foo。結果,程序打印'3'。

現在,讓我們在類Foo中添加addFive()方法,並在類Bar中覆蓋它,如同考試題。這裏應用了多態性,因此調用f.addFive()時不是使用編譯時間,而是使用對象f的運行時類型,即Bar來解析,因此打印爲'b'。

但是還有一點我們必須明白:爲什麼字段a增加了5個單位,仍然堅持值'3'?這裏隱藏正在玩耍。因爲這是所謂的Bar類的方法,並且因爲在類Bar中,每個a指的是Bar的公共字段a,所以這實際上是Bar字段,該字段遞增。

1)現在,一個補充問題:我們怎麼可能從main方法訪問Bar的公共領域a?我們能做到這一點的東西,如:

System.out.println(((Bar)f).a); 

迫使編譯器來解決字段成員afBara場。

這將在我們的示例中打印'b 13'。

2)另一個問題:我們如何能夠解決躲在BaraddFive()方法不是指Bara領域,但其超eponimous場?就在現場參考的前面加上super關鍵字的伎倆:

public void addFive() { 
    super.a += 5; 
    System.out.print("b "); 
} 

這將在我們的例子打印「B 8」。

注意,最初的聲明

public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
} 

可以細化到

,因爲當編譯器解析領域a,它將開始在最接近的封閉範圍看,從內方法addFive(),並找到Bar類的實例,無需使用明確地this

但是,嗯,this大概爲考生解決這個試題的線索!

+0

很好的解釋亞歷克斯 – Pratswinz 2015-05-29 20:33:23