Java学习笔记12---向上转型-父类的对象引用指向子类对象
当父类的对象引用没有指向父类的对象,而是指向了子类的对象时,调用方法或访问变量时会怎样呢?
假设父类为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行,可以看出访问的静态成员变量也是父类的变量。