Java子类继承的小陷阱
//对于初学者来说,这个例子应该算是个比较典型的陷阱了。先把结论写在前面,看完例子可以回过来体会~
结论:【对象是承载方法的载体。句柄决定属性的访问。】
Exp 1:TestTree.java
1 class Tree{ 2 int Height = 10; 3 public void Watering(){ 4 Height ++; 5 } 6 } 7 8 public class TestTree extends Tree{ 9 int Height = 2; 10 public void Watering(){ 11 Height += 3; 12 } 13 public static void main(String[] args) 14 { 15 Tree t = new TestTree(); 16 t.Watering(); 17 System.out.println(t.Height); 18 } 19 }
运行结果:10
这里15行new了一个子类对象,但句柄是父类的(多态)。
父类和子类有同名属性Height(两者相互独立),有同名方法Watering。这时候,父类的方法相当于被子类覆盖,所以16行的执行会使得Height调用子类的方法。要小心的是:这里的Height是谁的属性取决于执行的是谁的方法,比如这里是子类的方法,那么就到子类里面找是否有Height属性,有的话就是使用这个子类属性。若没有则使用父类的Height属性。
但这里输出的结果是t.Height, 这个Height直接是访问父类的属性,所以输出的结果直接是10。
再来稍加改动,如果将第九行注释掉,即把子类里的Height属性删去,如下:
Exp 2:
1 class Tree{ 2 int Height = 10; 3 public void Watering(){ 4 Height ++; 5 } 6 } 7 8 public class TestTree extends Tree{ 9 //int Height = 2; 10 public void Watering(){ 11 Height += 3; 12 } 13 public static void main(String[] args) 14 { 15 Tree t = new TestTree(); 16 t.Watering(); 17 System.out.println(t.Height); 18 } 19 }
运行结果:13
这里子类的方法会使用父类的属性Height,然后执行子类的方法加3,最后输出父类的属性(此时已经被修改)即13。
回到初始结论,句柄决定了属性的访问,是父类的句柄最后输出一定是父类的属性。
至于方法,这里是因为父类的方法被覆盖,所以使用子类的方法。若不存在方法覆盖,仅修改第10行如下:
Exp 3:
1 class Tree{ 2 int Height = 10; 3 public void Watering(){ 4 Height ++; 5 } 6 } 7 8 public class TestTree extends Tree{ 9 int Height = 2; 10 public void Fertilizing(){ 11 Height += 3; 12 } 13 public static void main(String[] args) 14 { 15 Tree t = new TestTree(); 16 t.Watering(); 17 System.out.println(t.Height); 18 } 19 }
运行结果:11
这里的方法显然是(继承自)父类的,那么调用的属性是父类的Height=10。结果便为11了。
若将上例16行改为:t.Fertilizing(); 那么便会报错,猜测是父类里面并没有这个方法~具体为什么暂时不清楚~后续学习吧
当然,这里还可以扩展出许多情况,如修饰符、方法重载,大可自行修改,就暂时不说啦。