黑马程序员技术交流社区

标题: 还是关于方法复写,方法调用的深层探究 [打印本页]

作者: 李程    时间: 2013-4-14 17:42
标题: 还是关于方法复写,方法调用的深层探究
先上代码,然后提问
class A
{                // 定义父类
        private void print()
        {
                System.out.println("A") ;
        }
    public void fun()
        {        // 定义一个fun方法
                this.print() ;        // 调用print()方法
        }
}

class B extends A
{        // 定义继承关系
        void print()
        {        // 覆写父类中的方法
                System.out.println("B") ;
        }
}

class  Lun
{
        public static void main(String[] args)
        {
                B b = new B();
                b.fun();
                b.print();
        }
}

        这个程序是另外一个同学的,我做了一点点修改,他所提的问题是这样做为什么不算复写,我给他做了相对应的回答:private不是默认的权限,如果private就只有本类中可以访问。详情见那个同学的提问贴。
我想问的问题是:
1,复写是否也算是访问?也就是说,我的A类中的print方法是一个权限为private的方法,这样只有在本类中可以访问。那么在子类中对这个方法进行复写,没有成功,是为什么呢?我的理解就是,既然不能访问,那么也就不能复写。不知道这样理解是否正确???那么访问和复写有什么关系和区别呢?
2,在主函数中,我调用了b对象的两个方法,其中fun()方法调用的是A类的print方法,而b.print()调用的是B类的print方法!!这是为什么呢?
3,既然A类中的print方法是私有的,最后也没有被复写成功,而且b.fun()和b.print()结果不同,说明调用的是两个print方法,那么这样的话在内存中加载B类的时候就有两个print方法!!!!!!!!!继承了一个,自己有一个?我晕了。。。{:soso_e118:},类中的方法应该都是在内存中的方法区存储啊,如果真的同名又是怎么区分的呢?请求哪位大神能帮我从内存加载和函数调用机制的角度给我解释一下。。。。。。最好能用毕老师那种画图的方式









作者: 栗俊植    时间: 2013-4-14 18:12
1:能复写就是能访问 用private修饰的方法是不可以被复写的, 因为子类看不到父类的私有的方法,只能本类调用
2:因为子类继承了父类,所以父类的fun()方法可以直接被子类对象调用, 而父类的fun()又调用了本类的print()方法所以打印结果是A
   结合第一点,子类有自己的print()方法,并不是复写了父类的方法 所以b.print() 的打印结果是B
3:加载B类的时候就一个print()方法而不是两个, A内存的print()方法只有在A类加载的时候才会在内存中存在!
图真心不会画! 希望能够帮到你 !
作者: liuyangyang    时间: 2013-4-14 18:16
你好,
在子类中确实没有重写掉父类的Print方法(因为private的原因),子类把Print这个方法看做是子类特有的方法。
你用b.fun()这个方法时,等于super.this.fun,再去找父类的print()是调用父类的print的
作者: 王大斌    时间: 2013-4-14 19:01
要理解这个问题,首先理解继承的机制。
对于继承来说,是子类在创建自己的时候,创建 了一个父类的对象。
而这个父类对象中,一些成员对子类开放(public defalut protected),也就是可以在子类中调用父类中的方法。
而另外一些成员不能被访问。也就值对子类屏蔽。

当一个父类被子类继承时候,子类并不获取对父类的private成员调用权限,也就是子类不能调用父类的成员。
也就不存在覆盖,覆盖是针对子类可以调用父类成员才说的。

而对于父类中能被子类调用的成员,当然你在子类中可以使用并覆盖
作者: 李程    时间: 2013-4-14 19:27
栗俊植 发表于 2013-4-14 18:12
1:能复写就是能访问 用private修饰的方法是不可以被复写的, 因为子类看不到父类的私有的方法,只能本类调用
...

1,访问应该是把这个方法加载进栈内存中,那么复写又是什么样一种机制呢?A类和B类各有各的方法区?还是?
2,继承了父类的fun()方法,那么也就是B类也有fun()方法,fun()方法中的this.print(),这里的this应该是当前对象的引用啊,当前对象应该是B类对象,为什么会调用A类的方法?
3,我只建立了B类的对象,调用了两个类的同名的不同方法,明显这两个方法都被加载。。。
作者: 李程    时间: 2013-4-14 19:31
liuyangyang 发表于 2013-4-14 18:16
你好,
在子类中确实没有重写掉父类的Print方法(因为private的原因),子类把Print这个方法看做是子类特有 ...

也就是说父类对象也被建立?那么如果子类中成功将父类的prin方法复写掉,super.this.fun为什么调用的又不是父类的print了呢?
作者: 李程    时间: 2013-4-14 19:33
王大斌 发表于 2013-4-14 19:01
要理解这个问题,首先理解继承的机制。
对于继承来说,是子类在创建自己的时候,创建 了一个父类的对象。
...

你的意思是说,在建立B类对象的时候,B的祖先类的所有对象全被建立?不会吧。。。我感觉应该只有一个堆内存空间被开辟吧!!
作者: 王大斌    时间: 2013-4-14 21:52
子类构造函数为何调用super(),调着没事玩?
作者: 李程    时间: 2013-4-15 09:09
王大斌 发表于 2013-4-14 21:52
子类构造函数为何调用super(),调着没事玩?

super()应该是在方法区吧?不能说调用了某一类的方法就说对建立了这一类的对象吧?
我也想不清楚诶。。。难道是调用了父类的构造函数,但是建立的是子类对象?应该是这样吧。。。否则所有子类在实例化的时候都要从自己的父类开始一直实例化到Object类?
作者: HM李辉    时间: 2013-4-15 10:23
哈哈,李程,咱们真是一号人啊,昨天我因为那位同学的问题 也纠结了半夜,自己当时也越来越迷糊,所以特意写了一篇文章,刚发表!
http://blog.csdn.net/okerivy/article/details/8801901
由于内容偏多,就不再这里发表了,你直接点链接就是了。
或许能解决你的疑问!
有问题咱们再讨论。
版主看情况,也给点技术分。昨天写到一点半啊~!
作者: 李程    时间: 2013-4-15 13:24
HM李辉 发表于 2013-4-15 10:23
哈哈,李程,咱们真是一号人啊,昨天我因为那位同学的问题 也纠结了半夜,自己当时也越来越迷糊,所以特意 ...

刚才看了一下你的文章,可以解决那个同学提的问题,不过没有解决我的疑问,尤其是第二个和第三个问题
你再看下我的问题,thx
作者: HM李辉    时间: 2013-4-15 15:02
李程 发表于 2013-4-15 13:24
刚才看了一下你的文章,可以解决那个同学提的问题,不过没有解决我的疑问,尤其是第二个和第三个问题
你 ...

你还是没有仔细看。第二个问题在博文中已经论述的很详细了。
下面我对你说的第2和3 问题进行一下分析。

问题2,在主函数中,我调用了b对象的两个方法,其中fun()方法调用的是A类的print方法,而b.print()调用的是B类的print方法!!这是为什么呢?

答:如果父类 某个功能 增加了private修饰符,子类中方法是不能覆盖这个功能的:
什么叫覆盖,子类沿袭父类功能。但是父类的这个功能增加了private,子类就不知道父类有这个功能。也就无法沿袭。
所以子类既没有继承父类的这个print()方法,也没有覆盖父类的print()方法。

有了这个基础。咱们再来理解你的问题2.

当运行 b.fun() 时,由于fun是父类的方法,并且fun没有被私有化,所以子类继承了父类,那么 b就可以直接调用fun()方法。
在调用的时候,由于父类 的print功能被 私有化,没有被覆盖,所以fun方法在调用 print方法时,调用的还是 父类的print方法。
为什么?因为在fun()和print()本来就在一个类中(父类)。所以此时 就相当于一般 的类。为什么不能调用呢?
所以打印为A.

当运行 b.print ()时,子类对象b调用子类方法print,一个类中的 调用,这个没有什么疑问,结果是B。

3,既然A类中的print方法是私有的,最后也没有被复写成功,而且b.fun()和b.print()结果不同,说明调用的是两个print方法,那么这样的话在内存中加载B类的时候就有两个print方法?

答: 你说的很对,内存的确是加载了两个print方法。两个print方法都在方法区。
new对象时,先加载的是父类!在方法区开辟了两个空间,一个是父类的空间, 一个是子类的空间。所以两个print分别独立存在。

作者: Just_Only    时间: 2013-4-15 15:18
12楼正解  
作者: 李程    时间: 2013-4-15 18:49
本帖最后由 李程 于 2013-4-15 18:50 编辑
HM李辉 发表于 2013-4-15 15:02
你还是没有仔细看。第二个问题在博文中已经论述的很详细了。
下面我对你说的第2和3 问题进行一下分析。

哦哦哦,太感谢你谢这么多文字给我解释!!我好像真的明白了,我想,编译器编译的时候应该是这样的:读到fun()的时候发现他继承了父类的方法,就直接到加载父类fun(),读到print()方法的时候,编译器先判断所属对象所属的类是否对print()进行了复写,如果复写了,就加载对象所属类的print(),如果没复写,就调用fun()所在的类的print()。
可能编译器比这个复杂一些,但大体就是这个思路吧
作者: 李程    时间: 2013-4-15 19:01
求斑竹给12楼加分
作者: HM李辉    时间: 2013-4-15 19:33
李程 发表于 2013-4-15 18:49
哦哦哦,太感谢你谢这么多文字给我解释!!我好像真的明白了,我想,编译器在编译的时候应该是这样的:读 ...

对,大致就是这个思路。这样的话,至少咱们能读懂程序,预测结果。
不用客气,共同学习:handshake,在论坛解决问题,比看视频还有用呢,呵呵,毕竟自己主动去思考了!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2