Java中的方法绑定及多态

程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。

静态绑定: 在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。 针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,java当中的方法只有final,static,private和构造方法和用super关键字调用的方法是前期绑定。

动态绑定: 后期绑定:在运行时根据具体对象的类型进行绑定。 若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确 的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。

动态绑定的过程: 虚拟机提取对象的实际类型的方法表; 虚拟机搜索方法签名; 调用方法。

关于绑定相关的总结: 在了解了三者的概念之后,很明显我们发现java属于后期绑定。在java中,几乎所有的方法都是后期绑定的,在运行时动态绑定方法属于子类还是基类。但是也有特殊,针对static方法和final方法由于不能被继承,因此在编译时 就可以确定他们的值,他们是属于前期绑定的。特别说明的一点是,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)。java中的后期绑定是有JVM来实现的,我们不用去显式的声明它,而C++则不同,必须明确的声明某个方法具备后期绑定。

转自通过实例说明Java中的多态

Java中的多态允许父类指针指向子类实例如:Father obj=new Child();(其中Child是Father的子类)。这样就产生了一个问题——使用这个父类型的指针访问类的属性或方法时,如果父类和子类都有这个名称的属性或方法,哪一个属性或方法会被调用呢?最好的办法是实验:

复制代码
class Father
 {
     int r;
     Father()
     {
         r=4;
     }
     void printname()
     {
         System.out.println("I'm father");
     }
 }
 class Child extends Father
 {
     int r;
     Child()
     {
         r=5;
     }
     void printname()
     {
         System.out.println("I'm Child");
     }
 }
 public class Test
 {
     public static void main(String[] args)
     {
         Father obj=new Child();
         System.out.println(obj.r);
         obj.printname();
     }
 }
复制代码

结果输出:

4
 I'm Child

实验证明。属性会访问父类的。方法会访问子类的。
支持将Child型的实例放在Father型的变量里,这就是多态了。
不要以为Father obj=new Child();这条语句一定会让obj.printname()指向Chlid定义的printname()。实际上,如果你把Father类中的printname()方法删除,这个程序将编译失败。因为Father中的并没有定义printname()这个函数。多态是后期动态绑定的,在Father obj=new Child();这个语句中,如果Father中没有printname()这个函数,就不会为obj建立一个用于调用printname()函数的指针。所以调用obj.printname()会出错。如果Father中有这个函数。指向printname()函数的指针会被创建。在调用obj.printname()时,不会出错,而且,因为obj指向的是new Child(),是个Chld类的实例。所以调用obj.printname()时调用了Child类中定义的printname()。这就是方法的动态绑定。
那么,刚才说到把Father类中的printname()方法删掉后,obj将无法调用Child类中的printname(),因为obj.printname()会编译失败。那么如果我就是需要调用要怎么办呢?其实虽然obj是Father类型的,但是它指向的是一个Child类的实例。那么可以将obj强制类型转换为Child。再调用printname()方法就可以了。
在上面程序中,把Father类中的printname()方法整个删掉,再将obj.printname() 改成 ((Child)obj).printname()后,编译成功,结果输出:

4
 I'm Child

两次输出的结果都是I'm Child。 那么如何可以运行Child类中的printname()来输出“I'm Father”呢? 其实只需要将Father obj=new Child();改成Father obj=new Father();就可以了,呵呵。另一个办法就是将Child类中定义的printname()整个删掉。为什么这样可以成功呢?自己想想,嘿嘿。最后会有个这样的思考题。

看到这儿你可能早就想问了: 为什么obj.r是4?为什么不是5?
呵呵。其实很简单。Java中的多态仅为方法而言,成员变量还是使用的父类的成员变量。也就是说,因为“Father obj =……”,所以obj是Father类型的,所以obj里面的r是Father里面的r,所以输出obj.r就是4了。
你又想问: 那么5去哪了?new Child()的时候,不是会把5放到Child的r中吗?
实际上5还是有的。只是obj.r是4而已。想访问Child中的r,把5读出来,可以这样写:

((Child)obj).r

就是把obj由Father型强制转换成了Child型。

OK,方法和属性在多态中是什么样的你都清楚了。现在做个题测试一下吧(这是J@Whiz1.4的一道题:):

复制代码
class Base {
         int i = 99;
         public void amethod() {
                 System.out.println("Base.amethod()");
         }
         Base() {
                 amethod();
         }
 }
 public class Derived extends Base {
         int i = -1;
         public static void main(String argv[]) {
                 Base b = new Derived();
                 System.out.println(b.i);
                 b.amethod();
        }
        public void amethod() {
                 System.out.println("Derived.amethod()");
        }
 
复制代码

会输出什么?
先想想,再看答案:

复制代码
答案:
 ========================
 Derived.amethod()
 99
 Derived.amethod()
 ========================
 讲解:
 
这个程序的执行过程是这样的:
 
第一行:Base b=new Derived();
 
执行这一行语句的过程中,要构造Derived这个类,而它有父类Base,所以先构造Base类。构造Base类的默认构造函数有定义。内容是执行amethod()方法。
 
实际上,Base类构造方法中的执行amethod(),相当于执行this.amethod(),在这个程序中,就相当于执行b.amethod()。而b是Base类型的,指向了Derived类的实例的指针。所以跟据上面我们的总结,实际上执行的是Derived类的amethod()函数。所以,第一行“Base b=new Derived();”执行完,输出"Derived.amethod()"。
 
第二行:System.out.println(b.i);
 
这个很简单,成员变量,不考虑多不多态,只看它定义时前面的类型。这个程序中是Base b,所以b.i就是Base类中的i。输出99
 
第三行:b.amethod();
 
调用Derived类中的amethod()方法。
 
其实这行就是迷惑你的,如果没有这一行。你可能会警觉起来——咦?为什么这儿定义一个amethod()呢?没有地方调用它啊?
 
有了这行代码,就会使你放松警惕。觉得,啊。定义了这个是用来让b.amethod();调用的。
复制代码
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值