好。所以, 當你擁有類如對象類型聲明
public class A {...}
和層次結構,
public class B extends A {...}
...當你創建對象,是有什麼區別:
A object = new A();
A object = new B();
B object = new B();
謝謝你的時間。
好。所以, 當你擁有類如對象類型聲明
public class A {...}
和層次結構,
public class B extends A {...}
...當你創建對象,是有什麼區別:
A object = new A();
A object = new B();
B object = new B();
謝謝你的時間。
A object = new B();
此聲明object
將指A
類的對象或其任何亞類的(當它不是null
)。編譯器會將它視爲A
類型的對象,因此您只能訪問爲A
(或其超類之一)聲明的方法和字段。這也意味着,你可以在以後將其分配給任何其他對象是A
類或子類:
A object1 = new B();
B object2 = new B();
// reassign later
object1 = new A(); // legal
object2 = new A(); // ILLEGAL
class C extends A { ... }
object1 = new C(); // legal
object2 = new C(); // ILLEGAL
因此,最初的聲明聲明object
具有類型A
。但其初始值是B
類型的對象,這是可以的,因爲B
是A
的子類。
這應該解釋你的第二個和第三個例子的區別。第一個和第二個之間的區別僅僅是(在運行時)第一個創建了一個類型爲A
的新對象,第二個創建了一個類型爲B
的新對象。
public class A
{
public void methodA(){}
}
public class B extends A
{
public void methodB(){}
}
我希望這可以證明不同之處。
A obj = new A();
a.methodA(); //works
A obj = new B();
obj.methodA(); //works
obj.methodB(); //doesn't work
((B)obj).methodB(); //works
B obj = new B();
obj.methodA(); //works
obj.methodB(); //works
A object = new A();
A
類型的object
(你可以訪問從A
字段或方法)
A object = new B();
A
類型的object
(你不能從B
訪問場或方法中,只有從A
)
B object = new B();
object
B
類型(可以從A
和B
訪問場或方法)
A object1 = new A();
A object2 = new B();
B object3 = new B();
object1
的被聲明爲到A對象的引用。由於B類延伸了A類,因此可以將其設置爲或((new A()
或new B()
將是有效的)。
object2
被聲明爲對A對象的引用,但實際上是B對象。假設B類有一個名爲eatFood()
的方法。如果您嘗試使用object2.eatFood()
來訪問該方法,編譯器會拋出一個錯誤,因爲eatFood方法僅在B類中。即使對象實際上是一個B對象,由於類型聲明,編譯器認爲它是一個A對象。要訪問eatFood方法,您必須註明它:((B)object2).eatFood()
。
object3
只是對B對象的引用,實際上是B對象。它可以訪問A方法以及B方法。
我相信應該是'((B)object2).eatFood()',因爲演員操作符的優先級...我自己一直在遇到這個問題。 – ajb
A object = new A();
您在類型A的引用中創建A instance
。您可以僅訪問方法/屬性和父項方法/屬性。
A object = new B();
你正在創造型A的參考B instance
這樣object
可能表現在多態的方式,例如,如果你讓object.method()
和method
在B重寫,然後它會調用這個重寫方法。你必須小心不要打破Liskov Substitution Principle。您只能訪問A方法/屬性和父項方法/屬性。當您只需要超類型合同時,這是首選方式。
B object = new B();
你正在創建在B
類型的參考變量B instance
。您只能訪問B方法/屬性和父項方法/屬性。
像
A var = new B();
的線是一種用於兩個單獨的步驟的速記的。
A var; // (1) Make a variable of TYPE A.
var = new B(); // (2) Make an object of CLASS B, that from now on may be
// referred to by the variable var.
所以一個變量有一個TYPE,一個對象有一個CLASS。他們經常匹配。變量的類型通常實際上是一個類,儘管不一定。理解變量類型和變量引用的對象的類別之間的區別很重要。
一個對象通常屬於多個類。如果B類擴展A類,這意味着B類的所有對象也是A類的對象。任何類的所有對象也都是類Object
的對象。換句話說,當我們說對象是一個B時,這比說它是一個A更具體。就像當我們說瑜珈是一個熊時,這比說瑜珈是動物更具體,因爲所有熊是動物。
因此,A類型的變量確實可以引用類B的對象,如果A是B延伸的類。但是如果你有一個A類型的變量,你不能用它來做特定於B類對象的事情。例如,假設A類有一個叫display()
的方法,而B類有一個叫做explain()
的方法。編譯器會讓你在A類型的變量上調用display()
,但它不會讓你調用explain()
。如果是這樣,那麼試圖撥打explain()
這個實際上不是B的對象會冒失敗的風險。
因此,無論何時有類B定義的方法,您都需要一個B類型的變量才能調用它們。當然,你也可以使用同一個變量來調用在A類中定義的方法。從某種意義上說,如果B類擴展了A類,那麼B類型的變量比A類型的變量更強大 - 你可以用它做更多的事情。
於是問題出現了 - 爲什麼我曾經想要寫
A var = new B();
當B類型的變量會比在這個例子中var
更厲害?
簡單的答案是它可以與查看代碼的人交流。它說,「是的,我知道這個變量是指B,但實際上我只打算使用A類提供的方法。這對於試圖瞭解您的代碼或維護它的人來說實際上會有幫助。」
還有一些情況下,它可以使涉及該變量的方法調用真正的改變。假設還有另外一個C類,它有兩個方法名稱相同但略有不同的簽名,是這樣的。
public class C {
public void process(A arg){
// Do some stuff
}
public void process(B arg){
// Do some other stuff
}
}
在這特殊情況下,被調用的版本process
取決於變量的類型,而不是對象的類別。因此,如果您編寫
C processor = new C();
A var = new B();
processor.process(var);
這將調用process
的第一個版本 - 簽名中帶A的那個版本。由於變量的類型。但是,如果你寫
C processor = new C();
B var = new B();
processor.process(var);
這將調用process
第二個版本 - 一個爲B的簽名。
當您創建('new')一個A時,您創建了一個A.當您創建一個B時,您創建了一個B.但B包含了A的所有功能,並且在大多數情況下可以像A一樣使用。如果在一個帶有「A」類型的變量中引用B,則不能訪問該對象的「B-ness」,而只能訪問「A-ness」。但是,您可以將參考「轉換」回「B」以重新獲得它的「B-ness」:'B refB =(B)refA;'但演員('(B)')在運行時會失敗如果refA實際上沒有引用B. –