Java中动态多态和静态多态有什么区别?

149

有人能提供一个简单的例子来说明Java中动态静态多态之间的区别吗?


5
覆盖有时被称为“静态多态”。这有点牵强,但这就是所发生的事情。 - Sergey Kalinichenko
@dasblinkenlight,谢谢你的信息。有没有相关的示例?? - Prabhakar Manthena
搜索"方法重载"和"方法覆盖"。 - Sergey Kalinichenko
7
我不理解重载是如何表现多态性的。多态性是对象的一个概念。我们应该能够把对象B展示为对象A。你在下面的例子中展示了Dog作为Animal,因此它是多态性的。但是在重载中,你调用的是不同的方法,但是使用了“相同的名称”。这怎么能是多态性呢?因此,“静态绑定”是正确的术语,但在重载的情况下,静态多态性并不存在。 - Punith Raj
@PunithRaj 你可能指的是子类型多态性。还有一种叫做特设多态性,适用于重载。 - Kelvin
显示剩余6条评论
14个回答

229

多态性

1. 静态绑定/编译时绑定/早期绑定/方法重载(在同一类中)。

2. 动态绑定/运行时绑定/晚期绑定/方法覆盖(在不同的类中)。

方法重载示例:

class Calculation {  
  void sum(int a,int b){System.out.println(a+b);}  
  void sum(int a,int b,int c){System.out.println(a+b+c);}  

  public static void main(String args[]) {  
    Calculation obj=new Calculation();  
    obj.sum(10,10,10);  // 30
    obj.sum(20,20);     //40 
  }  
}  

重写示例:

class Animal {    
   public void move(){
      System.out.println("Animals can move");
   }
}

class Dog extends Animal {

   public void move() {
      System.out.println("Dogs can walk and run");
   }
}

public class TestDog {

   public static void main(String args[]) {
      Animal a = new Animal(); // Animal reference and object
      Animal b = new Dog(); // Animal reference but Dog object

      a.move();//output: Animals can move

      b.move();//output:Dogs can walk and run
   }
}

6
作为Java的新手,我好奇“Animal引用但Dog对象”背后的基本概念是什么,为什么我们不能使用“Dog引用和dog对象”? 为了实现多态性和灵活性,Java允许使用父类类型的变量引用其子类类型的对象。因此,使用Animal类的引用来引用Dog对象是合法的。这种做法有助于编写通用代码,提高可重用性和可扩展性。如果使用Dog类的引用来引用Dog对象,则会限制代码的灵活性,使得以后难以将代码扩展到涉及其他动物类型的情况。 - lu5er
3
在上面的例子中,我试图展示多态的概念。我们可以创建同一类的引用和对象,但是无法实现方法重写。请查看以下帖子:https://dev59.com/i2ct5IYBdhLWcg3wWMFF - KhAn SaAb
方法重载是编译时多态。同样的方式,构造函数重载也是编译时多态吗? - Gaali Prabhakar

35
  • 方法重载是静态多态性的一个例子。

  • 而覆盖则是动态多态性的一个例子。

    因为在重载的情况下,编译器在编译时知道要链接到哪个方法。然而,在动态多态性中,这是在运行时确定的。


19

动态(运行时)多态性是在运行时存在的多态性。在这里,Java编译器不知道哪个方法在编译时被调用。只有JVM决定在运行时调用哪个方法。方法重载和实例方法使用的方法覆盖是动态多态性的示例。

例如

  • 考虑一个序列化和反序列化不同类型文档的应用程序。

  • 我们可以将‘ Document’作为基类,并从中派生不同的文档类型类。例如:XML文档、Word文档等

  • Document类将定义‘ Serialize()’和‘ De-serialize()’方法为虚拟方法,每个派生类将根据文档的实际内容以自己的方式实现这些方法。

  • 当需要序列化/反序列化不同类型的文档时,将通过‘ Document’类引用(或指针)引用文档对象,当对其调用‘ Serialize()’或‘ De-serialize()’方法时,将调用适当版本的虚拟方法。

静态(编译时)多态性是在编译时表现出的多态性。在这里,Java编译器知道哪个方法被调用。使用静态方法的方法重载和方法覆盖;使用私有或final方法的方法覆盖是静态多态性的示例。

例如

  • 一个员工对象可能有两个print()方法,一个不带参数,另一个带有前缀字符串以与员工数据一起显示。

  • 给定这些接口,当没有任何参数调用print()方法时,编译器根据函数参数知道应该调用哪个函数,并相应地生成对象代码。

有关更多详细信息,请阅读“什么是多态性”(在Google上搜索)。


8
这个答案有错误:(1) 方法重载不是动态多态性,而是静态多态性。(2) 静态方法永远不会被覆盖,它们被隐藏或遮蔽。(3) 私有方法不会被“覆盖”。它们从一开始就没有被继承。 - John Red
"只有JVM在运行时决定调用哪个方法。" JVM如何知道要调用哪个方法?它是否进行额外的编译? - Mehdi Charife

17
绑定是指方法调用和方法定义之间的链接。这张图片清晰地展示了什么是绑定。

binding

在这张图片中,“a1.methodOne()”调用绑定到相应的methodOne()定义,“a1.methodTwo()”调用绑定到相应的methodTwo()定义。
对于每个方法调用都应该有适当的方法定义。这是Java中的一个规则。如果编译器没有看到每个方法调用的适当方法定义,则会抛出错误。
现在,来介绍Java中的静态绑定和动态绑定。 Java中的静态绑定: 静态绑定是在编译期发生的绑定。它也称为早期绑定,因为绑定发生在程序实际运行之前。

.

静态绑定可以像下面的图片一样演示。

enter image description here

在这张图片中,“a1”是指向Class A对象的Class A类型的引用变量。 “a2”也是Class A类型的引用变量,但指向Class B的对象。
在编译时,在绑定时,编译器不会检查特定引用变量指向的对象类型。它只检查通过哪个引用变量调用方法并检查该类型中是否存在方法定义。
例如,在上面的图片中,“a1.method()”方法调用,编译器检查Class A中是否存在method()的方法定义。因为‘a1’是Class A类型。同样,对于“a2.method()”方法调用,它检查Class A中是否存在method()的方法定义。因为‘a2’也是Class A类型。它不检查‘a1’和‘a2’指向哪个对象。这种绑定称为静态绑定。
Java中的动态绑定:
动态绑定是一种发生在运行时的绑定。它也被称为晚期绑定,因为绑定发生在程序实际运行时。
在运行时,实际对象用于绑定。例如,在上面的图片中,“a1.method()”调用,将调用‘a1’指向的实际对象的method()。对于“a2.method()”调用,将调用‘a2’指向的实际对象的method()。这种绑定称为动态绑定。
以上示例的动态绑定可以如下所示。

enter image description here

参考 Java中的静态绑定和动态绑定


比以前更好。 - Anjan Biswas

13

方法重载是编译时/静态多态性的一个例子,因为方法调用和方法定义之间的绑定发生在编译时,并且它取决于类的引用(引用在编译时创建并进入堆栈)。

方法重写是运行时/动态多态性的一个例子,因为方法调用和方法定义之间的绑定发生在运行时,并且它取决于类的对象(对象在运行时创建并进入堆内存)。


(在运行时创建的对象并存储在堆中),它应该在运行时执行。 - Meet

11
方法重载也被称为静态多态性,也被称为编译时多态性静态绑定,因为重载的方法调用在编译时由编译器基于参数列表和我们调用方法的引用解析。
方法覆盖也被称为动态多态性或简单的多态性运行时方法分派动态绑定,因为被覆盖的方法调用在运行时解析。
为了理解这个原因,让我们举一个MammalHuman类的例子。
class Mammal {
    public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
}

class Human extends Mammal {

    @Override
    public void speak() { System.out.println("Hello"); }

    public void speak(String language) {
        if (language.equals("Hindi")) System.out.println("Namaste");
        else System.out.println("Hello");
    }

}

我已经在下面的代码行中包含了输出和字节码

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
通过观察上述代码,我们可以看到 humanMammal.speak()、human.speak() 和 human.speak("Hindi") 的字节码完全不同,因为编译器能够根据参数列表和类引用进行区分。这就是为什么 方法重载 被称为 静态多态性 的原因。
但是 anyMammal.speak() 和 humanMammal.speak() 的字节码相同,因为根据编译器,两种方法都在 Mammal 引用上调用,但是两种方法的输出不同,因为在运行时 JVM 知道引用持有的对象,并在对象上调用方法,这就是为什么方法重写被称为动态多态性的原因。
因此,从上述代码和字节码中可以清楚地看出,在编译阶段调用方法是从引用类型考虑的。但是在执行时,方法将从引用所持有的对象上调用。
如果想了解更多信息,请阅读 JVM 如何内部处理方法重载和覆盖

11

静态多态性:在同一类中,使用不同的类型或数量的参数重载了相同的方法名(不同的签名)。目标方法调用在编译时解析。

动态多态性:在不同的类中使用相同的签名 覆盖了相同的方法。在编译时无法确定调用该方法的对象类型,但会在运行时决定。

通常情况下,重载不被认为是多态性。

来自Java教程页面

一个类的子类可以定义其自己独特的行为,同时共享父类的某些功能


通常情况下,重载不会被视为多态性。您能否详细说明这一点? - prime
1
动态绑定和覆盖是多态的突出特点。 - Ravindra babu

8

多态性: 多态性是指一个对象具有多种形式的能力。在面向对象编程中,最常见的多态性使用方式是使用父类引用来引用子类对象。

动态绑定/运行时多态性:

运行时多态性也称为方法重写。在这种机制中,对重写函数的调用是在运行时解决的。

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的start方法内部

静态绑定/编译时多态:

方法调用的决定只在编译时进行。

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;
    }

}

输出: 集合排序方法内部

6

静态多态:是指在编译时确定要执行的方法决策。方法重载可以作为此类的一个例子。

动态多态:是指在运行时确定要执行的方法决策。方法重写可以作为此类的一个例子。


4
多态性指的是对象在相同触发器下表现出不同的行为。
静态多态性(编译时多态性):在编译时就决定要执行哪个方法,例如方法重载就是静态多态性的一个例子,并且需要使用静态多态性。
静态多态性通过静态绑定实现,在同一类中发生。不需要对象赋值。不涉及继承。
动态多态性(运行时多态性):在运行时决定要执行哪个方法,例如方法重写就是动态多态性的一个例子,并且需要使用动态多态性。
动态多态性通过动态绑定实现,在不同的类之间发生,需要将子类对象分配给超类对象进行动态多态性。涉及继承。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接