用极端例子再探java”继承"
??? 好久好久的,没有发表blog,之前都一直是开快车,脑子里的知识一个劲的刷新,虽然装了很多,但依然感到大脑轻飘飘的。难得有个机会停下了,深刻的理解java基础,尝试着探究最简单的问题的深层机制,发现收获还颇多。不多说,来技术总结了。
???? 再探继承:
//父类构造器的隐式调用class grandFather {public grandFather(){System.out.println("这是祖父类");}}class father extends grandFather {public father(){System.out.println("这是父类!");}}class child extends father {public child(){System.out.println("这是子类");}public static void main(String [] args){new child();}}?
运行结果:
这是祖父类这是父类!这是子类
?
说明:
??? 只要在程序创建java对象,系统总是先调用最顶层父类的初始化操作,包括初始化块和构造器,然后依次向下调用所有父类的初始化操作,最终执行本类的初始化操作返回本类的实例。(至于为什么要保留隐式调用功能,至今我也没弄明白,也许是没用到这个功能,但是“存在的东西总有它的理由”,求高手解答了)
?
再看一例:
package cn.Sep.day24.InheritanceTest;//内存中子类的实例class Base{int count = 2;}class Mid extends Base{int count = 20;}public class subClass extends Mid {int count = 200;public static void main(String[] args) {subClass s = new subClass();Mid s2m = s;Base s2b = s;System.out.println(s.count);System.out.println(s2m.count);System.out.println(s2b.count);}}//这里有个问题,s,s2m,s2b三个变量不是指向同一个变量吗?为什么输出的数值不同呢//总结:当发生继承时,子类即使定义了与父类里同型同名的变量,但是父类里的该变量不是被覆盖,而是被隐藏了//当发生转型时,会根据声明的类型调用父类的变量(这个与方法的重写不同)?
来一极端例:
package cn.Sep.day24.InheritanceTest;//父类的内存控制class BaseClass {int count = 2;public void display() {System.out.println(this.count);}}class DerivedClass extends BaseClass {int count = 20;@Overridepublic void display() {System.out.println(this.count);}}public class FildAndMethodTest {public static void main(String[] args) {BaseClass b = new BaseClass();System.out.println(b.count);b.display();DerivedClass d = new DerivedClass();System.out.println(d.count);d.display();BaseClass bd = new DerivedClass();System.out.println(bd.count);bd.display();BaseClass d2b = d;System.out.println(d2b.count);}}//输出顺序分别为:2,2,20,20,2,20,2(不信自己可以运行看看)// 总结:如果在子类重写了父类的方法,就意味着子类里定义的方法彻底覆盖了父类的同名方法,系统将不可能把父类里的// 方法转移到子类中。对于实例变量则不存在这样的现象,即使子类中定义了与父类完全同名的实例变量,这个实例变量依// 然不可能覆盖父类中定义的事例变量?
?
?
?
又来一极端例:
package cn.Sep.day24.InheritanceTest;class Fruit {String color = "蓝色";public Fruit getThis() {return this;}public void info() {System.out.println("Fruit方法");}}public class Apple extends Fruit {@Overridepublic void info() {System.out.println("Apple方法");}public void AccessSuperInfo() {super.info();}public Fruit getSuper() {return super.getThis();}String color = "红色";public static void main(String[] args) {Apple a = new Apple();Fruit f = a.getSuper();System.out.println("a和f所引用的对象是否相同" + (a == f));System.out.println("访问a所引用的对象的color实例变量" + a.color);System.out.println("访问f所引用的对象的color实例变量" + f.color);a.info();f.info();a.AccessSuperInfo();}}//运行结果://a和f所引用的对象是否相同true//访问a所引用的对象的color实例变量红色//访问f所引用的对象的color实例变量蓝色//Apple方法//Apple方法//Fruit方法//解析:从运行结果可以看出,Apple对象的getSuper()方法所返回的实际是该Apple对象本身(此时super.getThis()方法//中的this指Apple对象),只是他的声明类型是Fruit//因此通过f变量访问color实例变量时,该实例变量的值由Fruit类决定;但通过f变量调用info()方法时,该方法的行为//由f变量实际所引用的java对象决定,因此程序输出"Apple方法"。//总结:至此,对父、子对象在内存中存储有了准确的结论:当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存//也会为其父类中定义的所有实例变量分配内存,即使子类定义了与父类中同名实例变量。也就是说,当系统创建一个java对象的//时候,如果该java类有两个父类(一个直接父类A,一个间接父类B),假设A类中定义了2个实例变量,B类中定义了3个实例变量,//当前类中定义了2个实例变量,那这个java对象将会保存2+3+2个实例变量//如果在子类里定义了与父类中已有的同名变量,那么子类中定义的变量会隐藏父类定义的变量。注意不是完全覆盖,因此系统为//创建子类对象事,依然会为父类中定义的、被隐藏的变量分配内存空间。?
?
到此,初探“继承”已经结束了,在这之中,收获的不只是技术,更重要的是懂得一种方法,有时候,学一样东西,切不可浮于表面,学习到的知识不可模模糊糊的,只知道怎么用是不够的,当自己知道了其中的实现的机制后,不仅知识更扎实,对于创新是很有用的,而且该知识点也会用得更灵活,不至于到后来出了错依然不知所以然,悲哉!不知所云……
1 楼 feargod 2011-09-27 嗯,同意。光是会用,是远远不够的,要了解深层的原理。