2010-09-01 57 views
1

好的,noob問題。我正在爲SCJP學習,並收到3個關於對象引用錯誤的問題,這些問題似乎都指向了同樣的誤解。只是想確認什麼是正確的洞察力。對了,這裏有幾個問題:投射後目標對象會扮演什麼樣的角色?

    1.
 
1. class CodeWalkFour { 
2. public static void main(String[] args){ 
3.  Car c = new Lexus(); 
4.  System.out.print(c.speedUp(30) + " "); 
5.  Lexus l = new Lexus(); 
6.  System.out.print(l.speedUp(30, 40, 50)); 
7.  } 
8. } 
9. class Car { 
10.  private int i=0; 
11. int speedUp(int x){ 
12.  return i; 
13. } 
14. } 
15. class Lexus extends Car { 
16.  private int j = 1; 
17.  private int k = 2; 
18.  int speedUp(int y){ 
19.  return j; 
20.  } 
21.  int speedUp(int... z){ 
22.   return k; 
23.  } 
24. } 

我認爲3號線後,C將是一個汽車,沒有一輛雷克薩斯,所以Car.speedUp方法將被調用,而不是Lexus.speedUp方法。原來,這就是所謂的後者。

2.
 
1. class StudentProb { 
2. private int studentId = 0; 
3. void setStudentID(int sid) { 
4.  student_id = sid; 
5.  System.out.println("Student ID has been set to " + sid); 
6. } 
7. public static void main(String args[]) { 
8.  int i = 420; 
9.  Object ob1; 
10.  StudentProb st1 = new StudentProb(); 
11.  ob1 = st1; 
12.  st1.setStudentID(i); 
13. } 
14. } 

同樣的問題。我認爲第11行會讓st1成爲Object,而不是StudentProb。編譯器如何知道在哪裏可以找到setStudentID?

3.
 
1. LectureHall lh = new LectureHall(); 
2. Auditorium a1; 
3. Facilities f1; 
4. 
5. f1 = lh; 
6. a1 = f1; 

設施是一個接口。 ClassRoom類實現Facilities,Auditorium和LectureHall是ClassRoom的子類。同樣的問題:我認爲在第5行之後,f1和lh都是LectureHall。但f1仍然是設施。那麼在這裏做什麼鑄造?

謝謝大家!

PS:代碼格式化對我來說不起作用。隨意編輯。

+0

練習1:編譯器是否會在問題1的第18行的'Lexus#speedUp()'上接受'@ Override'註釋? – trashgod 2010-09-01 17:08:59

回答

2

在運行時,每個對象都知道它自己的類是什麼,也就是它實際創建的類。它可以分配給該類或任何超類的變量。當你執行一個函數時,你得到該對象被創建的類的該函數的「版本」,而不是保存該對象引用的變量被聲明爲的那個類。

也就是說,以您的Car/Lexus爲例。如果你寫的是「雷克薩斯mycar =新雷克薩斯(); mycar.speedUp();」,執行的是Lexus.speedUp,而不是Car.speedUp。也許這很明顯。但即使你寫「汽車mycar =新雷克薩斯(); mycar.speedUp();」執行的東西仍然是Lexus.speedUp,因爲那是實際對象的類。您可以將對象重新分配給不同類別的所有變量,但對象仍然知道它的「真實」類。

基本上,把它想象成每個對象都有一個隱藏變量來保存它自己的類類型,這就是它用來找到要執行的函數的東西。

在編譯時,編譯器不知道任何給定對象的類。喜歡如果你寫:

void speed1(Car somecar) 
{ 
    somecar.speedUp(1); 
} 

編譯器不知道汽車這裏是​​雷克薩斯還是本田還是什麼。它只知道它是一輛汽車,因爲它不知道這個功能在哪裏或如何被調用。直到運行時纔會知道實際的汽車類型。

這方面的一個含義是,如果你試着寫:

void speed1(Object somecar) 
{ 
    somecar.speedUp(1); 
} 

,編譯器將在此給出一個錯誤。它無法知道Object是Car,所以它不知道speedUp是一個有效的函數。

即使你寫道:

Object mycar=new Lexus(); 
mycar.speedUp(1); 

你會得到一個錯誤。作爲一個人閱讀代碼,你可以很容易地看到mycar必須是雷克薩斯,因此是汽車,但編譯器只是看到mycar被聲明爲Object,而Object沒有加速功能。 (我想,一個足夠聰明的編譯器可以找出這個微不足道的例子,即mycar必須是雷克薩斯,但編譯器無法處理它可能知道的有時或大部分時間,它必須處理絕對數。)

編輯:在評論中回答問題。

RE問題3:我看到你在這裏感到困惑。您需要將COMPILE TIME與RUNTIME區分開來。

當你在一個對象上執行一個函數時,你會得到該函數的「版本」,該特定於該對象的「真實」類。如果你寫:

Car car1=new Lexus(); 
Car car2=new Chrysler(); // assuming you defined this, of course 
car1.speedUp(1); // executes Lexus.speedUp 
car2.speedUp(2); // executes Chrysler.speedUp 

但這是一個RUNTIME的事情。在編譯時,所有編譯器都知道是保存引用的變量的類型。這可以與對象的「真實」類相同,或者它可以是任何直到對象的超類。在上述兩種情況下,它都是Car。由於Car定義了speedUp函數,因此對car1.speedUp的調用是合法的。但是這裏有一個啓發:在編譯時,Java知道Car有一個speedUp函數,所以對car對象調用speedUp是合法的。但它不知道或關心你會得到哪些speedUp功能。也就是說,當你說car2.speedUp時,Java知道car2是Car,因爲這是聲明的類型。它不知道 - 記住我們在編譯時說,而不是在運行時 - 它不知道它是雷克薩斯還是Chyrsler,只是它是一輛汽車。直到運行時,它才知道它是哪種類型的汽車。

+0

因此,您提到的「真實類型」是該對象首先聲明的類?無論您將對象重新分配給其他類型的其他對象的頻率如何,這將始終使用這個類? – 2010-09-01 19:09:54

+0

另外,請幫我澄清一下:你說: 「如果你寫」Car mycar = new Lexus(); mycar.speedUp();「執行什麼仍然是Lexus.speedUp」 因此,一直留在mycar中的類是「new」關鍵字之後的類。 – 2010-09-01 19:12:27

+0

但是你說: 對象mycar = new Lexus(); mycar.speedUp(1); 「編譯器只是看到mycar被聲明爲對象,而Object沒有加速功能」 mycar在這裏被聲明爲雷克薩斯就像前面的引用,但現在編譯器(或JVM?)不會不知道那是雷克薩斯。我能看到的兩個引號的唯一區別在於第一種情況是Car car mycar,第二種是Object mycar。這是如何改變的?謝謝! PS:對不起,由於字符限制,不得不分手評論。 – 2010-09-01 19:13:20

1

對象始終保持不變。如果將其分配給其他類型的其他變量,則不能訪問特殊成員而不將其重新轉換爲其原始類型。

您只能訪問正在使用的變量類型的成員和方法。但是這個變量引用的對象可以有更專門化的類型。對象存儲它自己的類型,因此運行時能夠識別它的真實類型。

2

對象始終是特定類的實例,您可以使用任何超類來引用實例,但實例不會更改。我認爲第二個文檔片斷說明了這個最好的,你不能寫ob1.setStudentID(i);因爲OB1是Object變量,即使實際類是StudentProb

在你的第3段,五號線是無效的,因爲設施是禮堂的超類,所以你可以將一個禮堂實例分配給一個設施變量,而不是相反。

+0

所以這是正確的:ob1包含對st1的引用,所以ob1指向一個StudentProb,但不會使ob1成爲StudentProb - ob1仍然只是一個Object?那麼轉換的效果是什麼 - 它只是將ob1中的引用更改爲指向st1?如果我使用明確的鑄造,例如ob1 =(StudentProb)st1? ob1實際上是一個StudentProb,那麼我能否調用ob1.setStudentID? 謝謝! – 2010-09-01 16:57:02

+0

ob1始終是'StudentProb'實例,但您只能以Object的形式迭代它(例如調用'toString()','hashCode()'等)。您不會更改實例的類,只有你如何引用它作爲一個變量。 – 2010-09-01 17:13:46

+0

因此,一旦ob1被聲明爲一個對象,沒有辦法讓它ob1.setStudentID運行? – 2010-09-01 19:01:32

1

這可以幫助我 - 它可能不會幫助你,但我會把它扔在那裏。

可視化投射物體作爲在該物體上放置新衣服。它有不同的外觀,但在你穿上它的任何衣服下面,它仍然是同一個物體。

例如,如果您採用String並將其轉換爲Object,它不會成爲對象 - 但它會穿着對象的衣服。您不能在對象上調用.length,因爲該方法不存在於「對象」的衣服中,但是如果您調用.equals或。hashCode會得到與你在字符串上調用它們時相同的確切答案,因爲它們通過衣服調用底層對象。

衣服只是隱藏了課堂上不可用的方法。 Object o = new String();隱藏在新衣服下的字符串但不是對象的所有方法,使其看起來像一個對象。

您可以稍後再將您的對象裝扮成字符串。

你穿的衣服與對象的操作無關,只與外界相互作用/看對象有關。

這可能有點遠,但也要注意,如果你的「衣服」有不存在於你的物體中的方法,試穿這些衣服沒有任何意義,所以你不能做到:

String s=new Object(); 

由於對象不能填寫字符串的西裝,字符串的訴訟中是否有「長度」和「追加」,而對象根本無法填補許多其他方法孔 - 就像有人沒有手試圖戴手套。

0

記住一件事。子類引用不能包含超類對象。 class A{} class B extends A{}在這裏您可以創建A a=new A(); A a=new B();B b=new B()但是您不能創建B b=new A()

相關問題