Java静态绑定与动态绑定

绑定指的是:一个方法的调用和方法所在的类关联起来。

绑定分为:

1、静态绑定(又叫做前期绑定)

在程序执行前就已经绑定,即在编译期间就知道这个方法属于哪个类,即编译器就已经绑定。

在java中非常容易理解例子就是:方法中有static、final、private、构造函数。

首先static方法不能被继承,只能被子类隐藏,例如:如果引用类型是父类,会调用父类中的static方法,如果引用类型为子类又会调用子类的static方法,意味着static方法没有被继承,只能通过父类本身被调用。

final方法可以被继承,但是不能被覆盖(即子类可以调用,但是不能重写),子类可以调用此方法,但是都是调用的父类中所定义的那个final方法。由此可以确定这个方法是属于父类的。

private方法,不能被继承,如果不能被继承,意味着也只能被父类所调用。

构造函数,只能在子类中通过super关键字调用,只属于父类。除了这些是静态绑定外,其他的都是动态绑定。

2、动态绑定(又叫做后期绑定)

在运行时根据具体对象类型进行绑定。

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

  1. 虚拟机提取对象的实际类型的方法表;
  2. 虚拟机搜索方法签名;
  3. 调用方法。

Java字节码的执行有两种方式:

1.即时编译方式:解释器先将字节码编译成机器码,然后再执行该机器码。

2.解释执行方式:解释器通过每次解释并执行一小段代码来完成Java字节码程 序的所有操作。

通常采用的是第二种方法。由于JVM规格描述具有足够的灵活性,这使得将字节码翻译为机器代码的工作

具有较高的效率。对于那些对运行速度要求较高的应用程序,解释器可将Java字节码即时编译为机器码,从而很好地保证了Java代码的可移植性和高性能。

以下内容来自互联网:

而动态绑定的典型发生在父类和子类的转换声明之下:
比如:Parent p = new Children();
其具体过程细节如下:
1:编译器检查对象的声明类型和方法名。
假设我们调用x.f(args)方法,并且x已经被声明为C类的对象,那么编译器会列举出C 类中所有的名称为f 的方法和从C 类的超类继承过来的f 方法。
2:接下来编译器检查方法调用中提供的参数类型。
如果在所有名称为f 的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法,这个过程叫做“重载解析”。

3:当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同x所指向的对象的实际类型相匹配的方法版本。

假设实际类型为D(C的子类),如果D类定义了f(String)那么该方法被调用,否则就在D的超类中搜寻方法f(String),依次类推。

JAVA 虚拟机调用一个类方法时(静态方法),它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择所调用的方法,这就是动态绑定,是多态的一种。动态绑定为解决实际的业务问题提供了很大的灵活性,是一种非常优美的机制。

与方法不同,在处理java类中的成员变量(实例变量和类变量)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)。

public class Father {
    protected String name = "父亲属性";
}
  

public class Son extends Father {
    protected String name = "儿子属性";

    public static void main(String[] args) {
        Father sample = new Son();
        System.out.println("调用的属性:" + sample.name);
    }
}
结论,调用的成员为父亲的属性。
这个结果表明,子类的对象(由父类的引用handle)调用到的是父类的成员变量。 所以必须明确,运行时(动态)绑定针对的范畴只是对象的方法
现在试图调用子类的成员变量name,该怎么做?最简单的办法是 将该成员变量封装成方法getter形式
代码如下:
public class Father {
    protected String name = "父亲属性";

    public String getName() {
        return name;
    }
}  

public class Son extends Father {
    protected String name = "儿子属性";

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        Father sample = new Son();
        System.out.println("调用的属性:" + sample.getName());
    }
}
结果:调用的是儿子的属性
java因为什么对属性要采取静态的绑定方法。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态,多态是java的一大特色。多态也是面向对象的关键技术之一,所以java是以效率为代价来实现多态这是很值得的。
上面调用的是父类和子类都有的方法:它会调用子类的方法,如果引用类型是父类,调用子类有的方法,父类没有的会怎么样呢?

如果想要调用子类中有而父类中没有的方法,需要进行强制类型转换

public class Father {
    protected String name = "父亲属性";

    public String getName() {
        return name;
    }
}  

public class Son extends Father {
    protected String name = "儿子属性";

    public String getName() {
        return name;
    }
    
	public  void  Play(){
		
        System.out.println("我是爱玩的儿子");
	}
    
    
    public static void main(String[] args) {
        Father sample = new Son();
        System.out.println("调用的属性:" + sample.getName());
        Son  s=(Son)sample; //必须强制类型转换,向下转型
        s.Play();
    }
}
多态虽然很奇妙,但是有时很难理解,需要多努力啊;

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值