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

问题描述 投票:97回答:13

任何人都可以提供一个简单的例子来解释Java中动态和静态多态的区别吗?

java oop polymorphism overloading override
13个回答
170
投票

多态性

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

2
投票

方法重载称为静态多态,也称为编译时多态或静态绑定,因为重载方法调用在编译时由编译器根据参数列表和我们调用该方法的引用进行解析。

和方法重写称为动态多态或简单多态或运行时方法调度或动态绑定,因为重写的方法调用在运行时得到解析。

为了理解为什么会这样,让我们​​以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调用对象的方法,这就是为什么方法覆盖被称为动态多态。

因此,从上面的代码和字节码,很明显在编译期间,相位调用方法是从引用类型考虑的。但是在执行时,将从引用所持有的对象调用方法。

如果您想了解更多相关信息,请阅读How Does JVM Handle Method Overloading and Overriding Internally上的更多信息。


1
投票

编译时多态(Static Binding / Early Binding):在静态多态中,如果我们在代码中调用一个方法,那么实际调用该方法的哪个定义只能在编译时解析。

(要么)

在编译时,Java通过检查方法签名来了解要调用的方法。因此,这称为编译时多态或静态绑定。

动态多态(Late Binding / Runtime Polymorphism):在运行时,Java等待运行时确定引用实际指向哪个对象。方法解析是在运行时进行的,因为我们称之为运行时多态。


1
投票

请考虑以下代码:

public class X
{
    public void methodA() // Base class method
    {
        System.out.println ("hello, I'm methodA of class X");
    }
}

public class Y extends X
{
    public void methodA() // Derived Class method
    {
        System.out.println ("hello, I'm methodA of class Y");
    }
}
public class Z
{
public static void main (String args []) {

    //this takes input from the user during runtime
    System.out.println("Enter x or y");
    Scanner scanner = new Scanner(System.in);
    String value= scanner.nextLine();

    X obj1 = null;
    if(value.equals("x"))
        obj1 = new X(); // Reference and object X
    else if(value.equals("y"))
        obj2 = new Y(); // X reference but Y object
    else
        System.out.println("Invalid param value");

    obj1.methodA();
}
}

现在,查看代码,您永远无法确定将执行哪个methodA()实现,因为它取决于用户在运行时期间提供的值。因此,仅在运行时期间确定将调用哪个方法。因此,运行时多态性。


0
投票

方法重载是一个编译时多态,让我们举个例子来理解这个概念。

class Person                                            //person.java file
{
    public static void main ( String[] args )
    {
      Eat e = new Eat();
       e.eat(noodle);                                //line 6
    }

   void eat (Noodles n)      //Noodles is a object    line 8                     
   {

   }
   void eat ( Pizza p)           //Pizza is a object
  {

  }

}

在这个例子中,Person有一个eat方法,表示他可以吃披萨或面条。当我们编译这个Person.java时,该方法吃掉了,编译器解析方法调用“e.eat(noodles)[在第6行],使用第8行中指定的方法定义,即将面条作为参数的方法并且整个过程由编译器完成,因此它是编译时多态性。用方法定义替换方法调用的过程称为绑定,在这种情况下,它由编译器完成,因此它被称为早期绑定。


27
投票
  • 方法重载将是静态多态的一个例子
  • 而重写将是动态多态的一个例子。 因为,在重载的情况下,编译时编译器知道链接到调用的方法。但是,在运行时确定动态多态性

14
投票

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

例如,

  • 考虑一个序列化和反序列化不同类型文档的应用程序。
  • 我们可以将'Document'作为基类,并从中派生出不同的文档类型类。例如。 XMLDocument,WordDocument等
  • Document类将'Serialize()'和'De-serialize()'方法定义为virtual,每个派生类将根据文档的实际内容以自己的方式实现这些方法。
  • 当需要序列化/反序列化不同类型的文档时,文档对象将由“Document”类引用(或指针)引用,并且在调用“Serialize()”或“De-serialize()”方法时在其上,调用适当版本的虚拟方法。

静态(编译时)多态性是编译时显示的多态性。这里,Java编译器知道调用哪个方法。使用静态方法重载方法和方法重写;使用私有或最终方法重写的方法是静态多态的示例

例如,

  • 员工对象可能有两个print()方法,一个不带参数,另一个带前缀字符串以及员工数据。
  • 给定这些接口,当在没有任何参数的情况下调用print()方法时,查看函数参数的编译器知道要调用哪个函数,并相应地生成目标代码。

有关详细信息,请阅读“什么是多态”(Google it)。


10
投票

绑定是指方法调用和方法定义之间的链接。

这张照片清楚地显示了什么是约束力

binding

在这张图片中,“a1.methodOne()”调用绑定到相应的methodOne()定义,而“a1.methodTwo()”调用绑定到相应的methodTwo()定义。

对于每个方法调用,都应该有适当的方法定义。这是java中的一个规则。如果编译器没有为每个方法调用看到正确的方法定义,则会抛出错误。

现在,来看看java中的静态绑定和动态绑定。

Java中的静态绑定:

静态绑定是在编译期间发生的绑定。它也称为早期绑定,因为绑定发生在程序实际运行之前

.

静态绑定可以如下图所示进行演示。

enter image description here

在这张图片中,'a1'是类A的类型的引用变量,指向类A的对象。'a2'也是类A类的引用变量,但指向类B的对象。

在编译期间,在绑定时,编译器不会检查特定引用变量所指向的对象的类型。它只检查调用方法的引用变量的类型,并检查该类型中是否存在方法定义。

例如,对于上图中的“a1.method()”方法调用,编译器检查A类中是否存在method()的方法定义。因为'a1'是A类类型。类似地,对于“a2.method()”方法调用,它检查A类中是否存在method()的方法定义。因为'a2'也是A类类型。它不会检查“a1”和“a2”指向哪个对象。这种类型的绑定称为静态绑定。

Java中的动态绑定:

动态绑定是在运行时发生的绑定。它也称为后期绑定,因为程序实际运行时会发生绑定。

在运行时,实际对象用于绑定。例如,对于上图中的“a1.method()”调用,将调用'a1'指向的实际对象的method()。对于“a2.method()”调用,将调用'a2'指向的实际对象的method()。这种类型的绑定称为动态绑定。

以下示例的动态绑定可以如下所示。

enter image description here

参考static-binding-and-dynamic-binding-in-java


7
投票

Polymorphism:多态性是一种物体具有多种形式的能力。当父类引用用于引用子类对象时,OOP中最常见的多态性使用发生。

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

运行时多态性也称为方法覆盖。在此机制中,在运行时解析对重写函数的调用。

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

输出:

汽车的内部启动方法

静态绑定/编译时多态:

要调用哪个方法仅在编译时决定。

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

}

输出:内部集合排序方法


7
投票

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

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


6
投票

简单来说 :

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

动态多态性:在不同的类中使用相同的签名覆盖相同的方法。调用方法的对象类型在编译时是未知的,但将在运行时决定。

通常,重载不会被视为多态。

来自java教程page

类的子类可以定义它们自己的唯一行为,但仍然共享父类的一些相同功能


3
投票

静态多态性:在编译期间确定要解决哪个方法的决定。方法重载就是一个例子。

动态多态性:在运行时设置决定选择执行哪种方法的地方。方法覆盖可以是一个例子。


2
投票

多态性是指对象对同一触发器的行为不同的能力。

静态多态性(编译时多态性)

  • 静态多态性决定在编译期间执行哪种方法。
  • 方法重载是静态多态的一个例子,它需要发生静态多态。
  • 静态多态通过静态绑定实现。
  • 静态多态性发生在同一个类中。
  • 静态多态性不需要对象赋值。
  • 静态多态性不涉及继承。

动态多态性(运行时多态性)

  • Dynamic Polymorphism决定在运行时执行哪个方法。
  • 方法重写是动态多态的一个例子,它需要发生动态多态。
  • 动态多态通过动态绑定实现。
  • 动态多态性发生在不同的类之间。
  • 在将子类对象分配给超类对象以进行动态多态时,需要这样做。
  • 动态多态性涉及的继承。
© www.soinside.com 2019 - 2024. All rights reserved.