當父類的對象引用沒有指向父類的對象,而是指向了子類的對象時,調用方法或訪問變量時會怎樣呢?
假設父類為Person,子類為Student,有下面的兩行定義:
Student sTest = new Student();
Person pTest = sTest;
其中,pTest就是父類的對象引用,sTest是子類的對象引用;pTest和sTest指向了同一個子類對象。
那么,
(1).如果子類的成員變量與父類的成員變量的類型及名稱都相同,則用sTest訪問時,訪問到的是子類的成員變量;用pTest訪問時,訪問到的是父類的成員變量;
(2).如果子類的靜態成員變量與父類的靜態成員變量的類型及名稱都相同,則用sTest訪問時,訪問到的是子類的靜態成員變量;用pTest訪問時,訪問到的是父類的靜態成員變量;
(3).如果子類的靜態成員方法重寫了父類的靜態成員方法,則用sTest調用時,調用的是子類的靜態成員方法;用pTest調用時,調用的是父類的靜態成員方法;
(1)、(2)、(3)都稱為隱藏,可以理解成父類的這些變量和靜態成員方法被放到抽屜里暫時藏起來了,當用父類對象引用訪問或調用時,把抽屜拉開就可以看到了;
(4).如果子類的成員方法重寫了父類的成員方法,則用sTest調用時,調用到的是子類的成員方法;用pTest調用時,調用的也是子類的成員方法;
此時稱為覆蓋,可以理解成父類的這些方法被子類重寫后的方法用膠帶給粘上了,撕不下來了,即使父類對象引用調用時也只能看到子類重寫后的方法;
(5).用sTest調用未覆蓋的父類成員方法時,該方法中如果使用到了被隱藏的變量或方法時,規則同上;
還是以簡單的示例來詳細說明。
Person類為父類,Student類為子類,TestMain類為測試類。代碼分別如下:
Person類的代碼為:
package human; public class Person { String name; int age; String gender; public String education; private String hobby; protected String residence; static String citizenship = "Chinese"; public Person() { } public void setName(String n) { this.name = n; } public String getName() { return name; } public void informationPrint() { System.out.println("My name is(getName) " + getName()); System.out.println("My name is(name) " + name); System.out.println("I am " + getAge() + " years old"); if(getGender() == "female") System.out.println("I am a girl"); else if(getGender() == "male") System.out.println("I am a boy"); else System.out.println("Something is wrong!"); System.out.println("My hobby is " + hobby); if(citizenship == "Chinese") System.out.println("I am a chinese"); //test:靜態變量是否在構造方法之前初始化 else if(citizenship == "US") System.out.println("I am an American"); else System.out.println("Oh,something is wrong"); } //test:覆蓋 public void testModifierPublic() { System.out.println("Person:Public"); } //test:隱藏 public static void staMethodHide() { System.out.println("Person:static Method"); } }
Student類的代碼為:
package human; public class Student extends Person { String stuNumber; int score; //test:隱藏 String name = "ha"; static String citizenship = "US"; public Student() { } public Student(String n, String g) { super(n,g); } //test:隱藏 public void setName(String n) { this.name = n; } //test:隱藏 public String getName() { return name; } //test:覆蓋 public void testModifierPublic() { System.out.println("Student:Public"); } //test:super public void testSuper() { System.out.println("Super:"); super.testModifierPublic(); } //test:隱藏 public static void staMethodHide() { System.out.println("Student:static Method"); } }
TestMain類的代碼為:
package human; public class TestMain { public static void main(String[] args) { Student sTest = new Student(); Person pTest = sTest; System.out.println("下面是以子類對象sTest來訪問變量、調用方法的結果:"); sTest.testModifierPublic(); System.out.println(sTest.name); System.out.println(sTest.getName()); System.out.println(sTest.citizenship); sTest.staMethodHide(); sTest.testSuper(); sTest.informationPrint(); System.out.println("下面是以父類對象pTest來訪問變量、調用方法的結果:"); pTest.testModifierPublic(); System.out.println(pTest.name); System.out.println(pTest.getName()); System.out.println(pTest.citizenship); pTest.staMethodHide(); } }
輸出結果為:
1 下面是以子類對象sTest來訪問變量、調用方法的結果: 2 Student:Public 3 ha 4 Student:getName() 5 ha 6 US 7 Student:static Method 8 Super: 9 Person:Public 10 Student:getName() 11 My name is(getName) ha 12 My name is(name) null 13 I am 0 years old 14 Something is wrong! 15 My hobby is null 16 I am a chinese 17 下面是以父類對象pTest來訪問變量、調用方法的結果: 18 Student:Public 19 null 20 Student:getName() 21 ha 22 Chinese 23 Person:static Method
下面對結果進行分析:
(1).前兩條語句為:
Student sTest = new Student();
Person pTest = sTest;
第一條語句定義了子類對象引用sTest,並指向了一個子類對象;第二條語句定義了父類對象引用pTest,並被賦值為sTest的值;大體的內存結構見圖1:

圖1
其中,子類的String型成員變量name與父類的name重名,子類的name值為“ha”,父類的name默認初始化為NULL;String型靜態成員變量citizenship與父類的citizenship重名,子類的citizenship值為“US”,父類的citizenship值為“Chinese”。
(2).第4條語句為:sTest.testModifierPublic();
輸出結果為第2行:Student:Public
第12行語句為:pTest.testModifierPublic();
輸出結果為第18行:Student:Public
sTest、pTest都調用了方法testModifierPublic(),子類重寫了父類的此方法,當sTest調用時,很顯然要調用子類重寫后的方法;pTest調用時,由於該方法已被子類的方法覆蓋,所以調用的也是子類重寫后的方法。
(3).第5、6條語句分別為:
System.out.println(sTest.name);
System.out.println(sTest.getName());
輸出結果為第3、4、5行:
ha
Student:getName()
ha
第13、14條語句分別為:
System.out.println(pTest.name);
System.out.println(pTest.getName());
輸出結果為第19、20、21行:
null
Student:getName()
ha
sTest.name是直接訪問成員變量name,sTest.getName()是通過調用getName()方法間接獲得name的值;兩種方式都輸出了“ha”;雖然name隱藏了父類的name,getName()重寫了父類的getName(),但調用者是sTest,所以使用的都是子類的變量和方法;
name雖然被隱藏,但pTest是父類對象引用,所以訪問是是父類的name,所以輸出為NULL;但父類的getName()被覆蓋,所以調用的是子類的方法。
(4).第7、8條語句分別為:
System.out.println(sTest.citizenship);
sTest.staMethodHide();
輸出結果為第6、7行:
US
Student:static Method
第15、16條語句分別為:
System.out.println(pTest.citizenship);
pTest.staMethodHide();
輸出結果為第22、23行:
Chinese
Person:static Method
對靜態成員變量和靜態方法而言,被隱藏時,由子類對象引用訪問或調用時,訪問或調用到的就是子類的變量或方法;由父類對象引用訪問或調用時,訪問或調用到的就是父類的變量或方法。
(5).第9條語句為:sTest.testSuper();
輸出結果為:
Super:
Person:Public
sTest調用子類成員方法testSuper(),方法體中用到了super.testModifierPublic();,用super來顯式調用父類的方法,所以輸出的是Person:Public。
(6).第10條語句為:sTest.informationPrint();
輸出結果為第10到16行:
Student:getName()
My name is(getName) ha
My name is(name) null
I am 0 years old
Something is wrong!
My hobby is null
I am a chinese
<1>.子類沒有重寫父類的informationPrint()這個成員方法。
<2>.輸出的第10、11行,通過getName()方法得到的name值是“ha”,輸出的第12行,通過直接訪問name得到的值為NULL;
說明調用的是子類getName(),但訪問的父類的name;
也就是說,子類調用父類未重寫的成員方法時,成員方法體中如果調用到某個方法被子類重寫了,則實際調用子類重寫后的方法;
如果訪問到某個被隱藏的成員變量,則實際訪問到的是父類的成員變量;
這時可以理解成,子類對象中包含了一個父類對象,由這個父類對象來訪問或調用其變量或方法,如果是隱藏的情況,則訪問到的是父類的值,如果是覆蓋的情況,則調用的是子類重寫后的方法。
<3>.輸出的第16行,可以看出訪問的靜態成員變量也是父類的變量。
