Java 动态绑定(多态)和静态绑定,隐藏

在这篇 《访问者模式讨论篇:java的动态绑定与双分派》,中看到作者说的,
Java的动态绑定就是在运行期间判断做引用对象的实际类型,根据实际类型调用对应的方法。继承体系中的重写就是动态绑定的。
静态绑定是指编译期就确定执行哪一个方法,Java 中,方法的重载就是静态绑定(根据不同的方法签名)

多态

多态是同一个行为具有多个不同表现形式或形态的能力。Java中所有对象都具有多态性,因为都有一个超类 Object。
比如“宠物”这个对象有很多不同的表达或实现。比如有小猫,小狗,兔子等等,小猫小狗等都是宠物的子类,通过“宠物”就可以引用不同的子类,就称“宠物”这个对象就有多态性。看到一个不严谨的说法:继承是子类使用父类的方法,而多态是父类使用子类的方法。

package com.something;

public class Ch4_plomorphism {

    public static void main(String[] args) {
         Pet dog1 = new Dog();  
         Dog dog2 = new Dog();  

         dog1.show(dog2);      //show:Dog:pet(改为dog1.show(dog1) 输出相同)
         dog1.display(dog2);   // display:Pet:pet
         dog2.show(dog1);      //show:Dog:pet
    }
}
class Pet{
    void show(Pet pet){
            System.out.println("show:Pet:pet");
    }

   static void display(Pet Pet){
            System.out.println("display:Pet:pet");
    }
}
class Dog extends Pet{
    //子类重写父类方法
    void show(Pet pet){
            System.out.println("show:Dog:pet");
    }
   void show(Dog dog){
            System.out.println("show:Dog:dog");
    }
    static void display(Pet pet){
            System.out.println("display:Dog:pet");
    }
}

上面的例子中Pet dog = new Dog();等价于

 Pet dog1 = new Pet();
         Dog dog = new Dog();
         dog1=dog;//所有的dog都是pet,所以是dog1=dog,而不是dog=dog1

声明是父类,实际指向的是子类的一个对象,初始化了一个Pet类型的引用,指向一个Dog类型的对象,但在变量dog看来它指向的堆空间其实是一个 Pet(不明白。。。。)

静态类型:Pet dog = new Dog(); 中Pet 就是静态类型,Dog 为实际类型。
静态类型是编译期可知的,实际类型运行期可知的。

编译期,编译器会根据参数的静态类型决定使用哪个重载版本。比如dog2.show(dog1); 参数dog1 的静态类型是Dog ,所以与show(Dog dog) 绑定。
如果参数类型不完全一致,遵从:
最终方法调用的优先级:this.show(O)、super.show(O)(过程2中重载解析,先寻找参数类型完全匹配的方法)、this.show((super)O)、super.show((super)O)。

上个例子的调用对象方法的执行过程:

  1. 首先编译器查看对象的声明类型和方法
    dog1.show(dog2),dog1声明为父类Pet 对象,编译器会将Pet对象中的名为 show 的方法和其超类中访问属性为public且名为show 的方法一一列举。 至此编译器获得所有可能被调用的候选方法。

  2. 接下来,编译器将查看调用方法提供的参数类型。根据静态类型决定选择哪个重载方法。(show(Dog dog),show(Pet pet)这两个就是重载方法),选择show(Pet pet)

  3. 如果是private,static,final 方法或者构造器,编译器可以准确的知道应该调用哪个方法,这种调用方式称为静态绑定。 与之对应是,调用的方法依赖于dog1的实际类型,在运行时实现的动态绑定

  4. 程序运行,并采用动态绑定调用方法时 ,虚拟机根据变量的实际类型来实现重写。dog1.show(dog2) dog2 的实际类型是Dog,Dog中有对show(Pet pet)的重写。

上面例子中在编译时,dog1.show(dog2),首先查询dog1所指向对象的函数列表,发现只有show(Pet pet),并且形参类型符合转型要求,与之绑定。运行时由于多态机制,发现show(Pet pet) 在被重写,于是动态的与子类重写方法绑定。而display是类方法,所有类方法都在编译时绑定,所以类方法都是静态绑定的。display 在编译时期就已经静态绑定,没有后面的动态绑定。

附上这个无语的例子:

package com.something;

public class Ch4_test {
     public static void main(String[] args) {
            A a1 = new A();
            A a2 = new B();
            B b = new B();
            C c = new C();
            D d = new D();

            System.out.println("1--" + a1.show(b));
            System.out.println("2--" + a1.show(c));
            System.out.println("3--" + a1.show(d));
            System.out.println("4--" + a2.show(b));
            System.out.println("5--" + a2.show(c));
            System.out.println("6--" + a2.show(d));
            System.out.println("7--" + b.show(b));
            System.out.println("8--" + b.show(c));
            System.out.println("9--" + b.show(d));     

        }
}
class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(B obj) {
        return ("A and B");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

class B extends A{
    public String show(B obj){
        return ("B and B");
    }

    public String show(A obj){
        return ("B and A");
    } 
}

class C extends B{

}

class D extends B{

}

理解方法调用的优先级即可知道输出:

1--A and B
2--A and B
3--A and D
4--B and B
5--B and B
6--A and D
7--B and B
8--B and B
9--A and D

变量隐藏

看下面的代码

public class Variable {

    public static void main(String[] args) {
            Foo f = new Bar();
            f.addFive();
            System.out.println("  f.a="+f.a);
    }

}
class Foo {
      public int a=3;
      public void addFive() {
        a+=5;
        System.out.print("f ");
        System.out.print("a  in addFive="+a);
      }
    }

    class Bar extends Foo {
      public int a=8;
      public void addFive() {
        this.a += 5;
        System.out.print("b ;");
        System.out.print("in addFive a="+a);
      }
    }

输出b ;in addFive a=13 f.a=3 (如果将Foo 中的变量 a 删除,System.out.println(f.a);会报错)
看到的解释是:Java中实例变量不可以被重载。 只有方法可以被重载。

其中f 是 Foo 类型的引用,变量不是多态的,因此 f.a 指向的是 Foo 的变量 3。f.addFive(); 由于多态,动态绑定到子类的addFive() 方法。

子类中定义一个和父类中名字相同的字段时会覆盖父类中的字段,即使他们的类型不同。子类中只能通过super来访问父类中的字段。

参考链接:
http://www.douban.com/note/272520236/
http://alexyyek.github.io/2015/03/04/Polymorphism/
http://blog.csdn.net/ns_code/article/details/17965867
http://www.cnblogs.com/chenssy/p/3372798.html
http://stackoverflow.com/questions/12086298/why-instance-variable-of-super-class-is-not-overridden-in-sub-class-method
http://stackoverflow.com/questions/11974428/java-code-snippet-output-explanation-required

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值