Java 多态之“绑定”

来源:互联网 发布:企业网络公关 编辑:程序博客网 时间:2024/06/03 18:56

写在前面:
    在刚开始学习JavaSE 时,学习多态时对下面几段程序输出的结果感觉到很诧异:

public class Sub extends Super{    public int num = 1;     public int getNum(){        return num;    }    public static void main(String[] args) {        Super sup = new Sub();        System.out.println("sup.num = "+sup.num);  //调用父类的属性        System.out.println("sup.getNum() = "+sup.getNum());  //调用子类的方法    }}class Super{    public int num = 0;    public int getNum(){        return num;    }}

输出:
sup.num = 0
sup.getNum() = 1


当将方法定义为静态时输出又会是什么样的呢?

public class Sub extends Super{    public static int num = 1;    public static int getNum(){        return num;    }    public static void main(String[] args) {        Super sup = new Sub();        System.out.println("sup.num = "+sup.num);  //调用父类的属性        System.out.println("sup.getNum() = "+sup.getNum());  //调用父类的方法    }}class Super{    public static int num = 0;    public static int getNum(){        return num;    }}

输出:
sup.num = 0
sup.getNum() = 0


如果将方法定义成私有的又会是什么结果呢?

public class Sub extends Super{    private void f(){        System.out.println("Sub f run........... ");    }}class Super{    private void f(){        System.out.println("Super f run........... ");    }    public static void main(String[] args) {        Super sup = new Sub();        sup.f();  //调用父类的方法    }}

输出:
Super f run………..


为什么会出现这样的输出结果呢?
    在当时刚开始学Java 时,当时的疑惑主要有:
      ①为什么第一个程序在向上转型的过程中调用的是父类的字段,以及子类的方法
      ②为什么第二个程序在向上转型的过程中调用的是父类的字段,以及父类的方法

后来在网上搜了一下知道了“绑定”这个概念,但是也只是很浅显的知道了有这个概念,最近在看《Java 编程思想》对“绑定”这个概念有了更深刻的认识,现在分享出来供大家参考:

绑定:将一个方法调用同一个方法主题关联起来被称为绑定。
    静态绑定:若程序执行前绑定(如果有的话,由编译器和链接程序实现),叫做前期绑定。
    后期绑定:在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或运行时绑定。

在Java 中除了static 方法和final 方法(private 方法属于final 方法)之外,其他的方法都属于后期绑定。为什么要将一个方声明为final 的呢?因为这样做可以有效“关闭”动态绑定,或者说告诉编译器不需要对其进行动态绑定。

    Java 中所有的方法都是通过动态绑定实现多态。因此子类在向上转型时调用的是子类的方法(此方法时非静态的),这就可以解释为什么在第一段程序中调用getNum() 方法输出的是子类的方法。

    只有普通的方法调用是多态的,因此如果你要访问某个作用域或静态方法,这个访问将在编译器进行解析。正如第一个程序中输出的是父类的成员,以及第二个程序中将方法修饰成static 时输出的就是父类的成员和父类的方法了(失去了多态的特性)。静态方法是与类,而并非与单个对象相关联的。如果你并不想将方法定义成静态的还想要得到Super.num,必须显示的指明super.num,就像下面这样:

public class Sub extends Super{    public int num = 1;    public int getNum(){        return super.num;    }    public static void main(String[] args) {        Super sup = new Sub();        System.out.println("sup.getNum() = "+sup.getNum());    }}class Super{    public int num = 0;    public int getNum(){        return num;    }}

输出:
sup.getNum() = 0


    对于第三段程序将方法定义成private 的,我们期望是输出的是子类的方法运行,但是结果却出我们所料,这是动态绑定的缺陷所造成的结果,以及包括上面的域与静态方法都是它的缺陷造成的(也就是说这两种缺陷是前期绑定)。
结论就是:
    只有非private 方法才可以被覆盖,但是还是要注意覆盖private 方法的现象,这时编译器虽然不会报错,但是它不会按照我们期望的那样执行。确切的说,在导出类中,对于基类中的private 方法最好不要采取一样的名字。在这里再强调一下这句话:在Java 中除了static 方法和final 方法(private 方法属于final 方法)之外,其他的方法都属于后期绑定。

其实在实践过程中,这种混淆的问题通常不会发生,因为你通常会将所有的域都设成private 的,因此不能直接访问它们,其副作用是只能通过方法获取它们。另外,你几乎不可能将基类中的域和导出类的域赋予相同的名字。

                                                                                                 参考资料《Java 编程思想》Bruce Eckel 著 陈昊鹏 译

原创粉丝点击