黑马程序员技术交流社区

标题: [多态]多态性理解 [打印本页]

作者: c8984771    时间: 2016-1-3 23:32
标题: [多态]多态性理解
本帖最后由 c8984771 于 2016-1-4 12:10 编辑

Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定它会自动发生。
final方法会使编译器生成更有效的代码,这也是为什么说声明为final方法能在一定程度上提高性能(效果不明显)。
如果某个方法是静态的,它的行为就不具有多态性:
<
class StaticSuper
{
    public static String staticGet()
        {
        return "Base staticGet()";
    }
    public String dynamicGet()
        {
        return "Base dynamicGet()";
    }
}
class StaticSub extends StaticSuper
{
    public static String staticGet()
        {
        return "Derived staticGet()";
    }
    public String dynamicGet()
        {
        return "Derived dynamicGet()";
    }
}
public class StaticPolymorphism
{
    public static void main(String[] args)
        {
        StaticSuper sup = new StaticSub();
        System.out.println(sup.staticGet());
        System.out.println(sup.dynamicGet());
    }
}
输出:
Base staticGet()
Derived dynamicGet()
>
构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override
在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。
<
class Glyph
{
    void draw()
        {
        System.out.println("Glyph.draw()");
    }
    Glyph()
        {
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }
}
class RoundGlyph extends Glyph
{
    private int radius = 1;
    RoundGlyph(int r)
        {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius);
    }
    void draw()
        {
        System.out.println("RoundGlyph.draw(). radius = " + radius);
    }
}
public class PolyConstructors
{
    public static void main(String[] args)
        {
        new RoundGlyph(5);
    }
}
输出:
Glyph() before draw()
RoundGlyph.draw(). radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(). radius = 5
>
为什么会这样输出?这就要明确掌握Java中构造函数的调用顺序:
(1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;
(2)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;
(3)按声明顺序调用成员的初始化方法;
(4)最后调用子类的构造函数。
只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。
java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下:
class Super
{
    public int field = 0;
    public int getField()
        {
        return field;
    }
}
class Sub extends Super
{
    public int field = 1;
    public int getField()
        {
        return field;
    }
    public int getSuperField()
        {
        return super.field;
    }
}
public class FieldAccess
{
    public static void main(String[] args)
        {
        Super sup = new Sub();
        System.out.println("sup.filed = " + sup.field +
                ", sup.getField() = " + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.filed = " + sub.field +
                ", sub.getField() = " + sub.getField() +
                ", sub.getSuperField() = " + sub.getSuperField());
    }
}
输出:
sup.filed = 0, sup.getField() = 1
sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0
Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。




作者: luyang18    时间: 2016-1-4 10:54
谢谢总结,分享!!!!!
作者: aiheima    时间: 2016-1-4 11:52
多态的前提前提:1:有有继承关系  2:父类引用指向子类对象  3.要有方法的重写
作者: AOHhao    时间: 2016-1-4 12:00
不错不错,就是排版的不太容易看
作者: c8984771    时间: 2016-1-4 12:11
AOHhao 发表于 2016-1-4 12:00
不错不错,就是排版的不太容易看

多谢提醒,已修改
作者: luyang18    时间: 2016-1-5 20:45
不错,不错,讲得很详细
作者: chengz    时间: 2016-1-5 21:03
学习学习
作者: ghb609840612    时间: 2016-1-6 11:53
谢谢分享
作者: 晓寒轻    时间: 2016-1-6 15:02
没耐心看完,之前自己简单理解多态就是一个方法名可以传递不同的参数,而实现不同的作用
作者: saury    时间: 2016-1-6 23:15
1024个赞
作者: 随缘的影子    时间: 2016-1-7 16:31
同没耐心看完,先收藏下,考完试再回来看。




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