use*_*750 80 java dynamic-binding static-binding
我正在为我的一个类做一个任务,在其中,我必须使用Java语法给出静态和动态绑定的示例.
我理解基本概念,即静态绑定在编译时发生,动态绑定在运行时发生,但我无法弄清楚它们实际上是如何工作的.
我在网上找到了一个静态绑定的例子,给出了这个例子:
public static void callEat(Animal animal) {
System.out.println("Animal is eating");
}
public static void callEat(Dog dog) {
System.out.println("Dog is eating");
}
public static void main(String args[])
{
Animal a = new Dog();
callEat(a);
}
Run Code Online (Sandbox Code Playgroud)
并且这将打印"动物正在吃"因为调用callEat
使用静态绑定,但我不确定为什么这被认为是静态绑定.
到目前为止,我所看到的所有来源都没有设法以我能够遵循的方式解释这一点.
Mau*_*tel 95
以下是静态和动态绑定之间的一些重要区别:
- Java中的静态绑定在编译期间发生,而动态绑定在运行时期间发生.
private
,final
以及static
方法和变量使用静态绑定和由编译器所键合而虚拟方法基于运行时对象在运行期间接合.- 静态绑定使用
Type
(class
在Java中)用于绑定的信息,而动态绑定使用对象来解析绑定.- 重载方法使用静态绑定绑定,而重写方法在运行时使用动态绑定绑定.
下面是一个示例,它将帮助您理解Java中的静态和动态绑定.
Java中的静态绑定示例
Run Code Online (Sandbox Code Playgroud)public class StaticBindingTest { public static void main(String args[]) { Collection c = new HashSet(); StaticBindingTest et = new StaticBindingTest(); et.sort(c); } //overloaded method takes Collection argument public Collection sort(Collection c) { System.out.println("Inside Collection sort method"); return c; } //another overloaded method which takes HashSet argument which is sub class public Collection sort(HashSet hs) { System.out.println("Inside HashSet sort method"); return hs; } }
输出:内部集合排序方法
Java中动态绑定的示例
Run Code Online (Sandbox Code Playgroud)public class DynamicBindingTest { public static void main(String args[]) { Vehicle vehicle = new Car(); //here Type is vehicle but object will be Car vehicle.start(); //Car's start called because start() is overridden method } } class Vehicle { public void start() { System.out.println("Inside start method of Vehicle"); } } class Car extends Vehicle { @Override public void start() { System.out.println("Inside start method of Car"); } }
输出: Car的内部启动方法
Mr.*_*r.Q 17
将方法调用连接到方法体称为Binding.正如Maulik所说,"静态绑定使用Type(Java中的类)信息进行绑定,而动态绑定使用Object来解析绑定." 所以这段代码:
public class Animal {
void eat() {
System.out.println("animal is eating...");
}
}
class Dog extends Animal {
public static void main(String args[]) {
Animal a = new Dog();
a.eat(); // prints >> dog is eating...
}
@Override
void eat() {
System.out.println("dog is eating...");
}
}
Run Code Online (Sandbox Code Playgroud)
会产生结果:狗吃...因为它使用对象引用来查找使用哪种方法.如果我们将上面的代码更改为:
class Animal {
static void eat() {
System.out.println("animal is eating...");
}
}
class Dog extends Animal {
public static void main(String args[]) {
Animal a = new Dog();
a.eat(); // prints >> animal is eating...
}
static void eat() {
System.out.println("dog is eating...");
}
}
Run Code Online (Sandbox Code Playgroud)
它会产生:动物正在吃...因为它是一种静态方法,所以它使用Type(在本例中为Animal)来解析要调用的静态方法.除静态方法外,私有方法和最终方法使用相同的方法.
那么为了了解静态和动态绑定实际上是如何工作的?或者编译器和JVM如何识别它们?
让我们看下面的例子,其中Mammal
有一个父类,它有一个方法speak()
和Human
类 extends Mammal
,覆盖该speak()
方法,然后再次用speak(String language)
.
public class OverridingInternalExample {
private static class Mammal {
public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
}
private static class Human extends Mammal {
@Override
public void speak() { System.out.println("Hello"); }
// Valid overload of speak
public void speak(String language) {
if (language.equals("Hindi")) System.out.println("Namaste");
else System.out.println("Hello");
}
@Override
public String toString() { return "Human Class"; }
}
// Code below contains the output and bytecode of the method calls
public static void main(String[] args) {
Mammal anyMammal = new Mammal();
anyMammal.speak(); // Output - ohlllalalalalalaoaoaoa
// 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Mammal humanMammal = new Human();
humanMammal.speak(); // Output - Hello
// 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Human human = new Human();
human.speak(); // Output - Hello
// 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V
human.speak("Hindi"); // Output - Namaste
// 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
}
}
Run Code Online (Sandbox Code Playgroud)
当我们编译上面的代码并尝试使用 来查看字节码时javap -verbose OverridingInternalExample
,我们可以看到编译器生成了一个常量表,它将整数代码分配给我提取并包含在程序本身中的程序的每个方法调用和字节码(请参阅每个方法调用下方的注释)
通过看上面的代码中我们可以看到的字节码humanMammal.speak()
,human.speak()
并且human.speak("Hindi")
是完全不同的(invokevirtual #4
,invokevirtual #7
,invokevirtual #9
),因为编译器能够根据参数列表和类引用上区分它们。因为所有这些都在编译时静态解决,这就是方法重载被称为静态多态或静态绑定的原因。
但是字节码anyMammal.speak()
和humanMammal.speak()
是相同的(invokevirtual #4
),因为根据编译器这两种方法都要求Mammal
参考。
所以现在问题来了,如果两个方法调用都有相同的字节码,那么 JVM 怎么知道调用哪个方法呢?
嗯,答案隐藏在字节码本身中,它是invokevirtual
指令集。JVM 使用该invokevirtual
指令调用 Java 等效的 C++ 虚拟方法。在 C++ 中,如果我们想覆盖另一个类中的一个方法,我们需要将其声明为虚拟的,但在 Java 中,默认情况下所有方法都是虚拟的,因为我们可以覆盖子类中的每个方法(私有、最终和静态方法除外)。
在 Java 中,每个引用变量都包含两个隐藏的指针
因此,所有对象引用都间接地持有对表的引用,该表持有该对象的所有方法引用。Java 从 C++ 借用了这个概念,这个表被称为虚拟表(vtable)。
vtable 是一个类似数组的结构,它保存虚拟方法名称及其对数组索引的引用。JVM 在将类加载到内存中时,为每个类只创建一个 vtable。
因此,每当 JVM 遇到invokevirtual
指令集时,它都会检查该类的 vtable 以获取方法引用并调用特定方法,在我们的例子中是来自对象的方法而不是引用。
因为所有这些只在运行时得到解决,并且在运行时 JVM 知道要调用哪个方法,这就是方法覆盖被称为动态多态或简称为多态或动态绑定的原因。
您可以在我的文章“JVM 如何在内部处理方法重载和覆盖”中阅读更多详细信息。