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


118

谁能提供一个简单的示例来说明Java中动态静态多态性之间的区别?


4
覆盖有时被称为“静态多态性”。这有点麻烦,但这就是正在发生的事情。
dasblinkenlight 2013年

@dasblinkenlight感谢您的信息。有什么例子吗?
Prabhakar Manthena 2013年

搜索“方法重载”和“方法重载”。
dasblinkenlight 2013年

5
我不明白重载是多态的。多态是对象的概念。我们应该能够从下面的(答案)示例中将对象B显示为对象A。您将狗显示为动物,因此它是多态的。但是在重载中,您正在调用不同的方法,但使用“相同名称”。这怎么可能是多态性。因此,“静态绑定”是正确使用的术语,但在过载的情况下则不是静态多态性。
Punith Raj

@PunithRaj您可能是指子类型多态性。还有另一种称为Ad hoc的类型,适用于重载。
开尔文

Answers:


196

多态性

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 reference but Dog object,我们为什么不能使用Dog reference and dog object
pratyay

3
在上面的示例中,我试图显示多态性的概念。我们可以创建相同类的引用和对象,但无法实现方法覆盖。请与下面的后经过: stackoverflow.com/questions/12159601/...
汗萨博

方法重载是编译时多态。是一样的 构造函数重载也是编译时多态吗?
加利(Gaali Prabhakar)'18年

29
  • 方法重载将是静态多态的一个示例

  • 而重载则是动态多态性的一个示例。

    因为在发生重载的情况下,编译器会在编译时知道链接到该调用的方法。但是,它是在运行时确定的动态多态性


17

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

例如,

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

  • 我们可以将“文档”作为基类,并派生不同的文档类型类。例如XMLDocument,WordDocument等。

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

  • 当需要对不同类型的文档进行序列化/反序列化时,文档对象将由“文档”类引用(或指针)进行引用,并且在调用“ Serialize()”或“ De-serialize()”方法时在其上,将调用适当版本的虚拟方法。

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

例如,

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

  • 给定这些接口,当在不带任何参数的情况下调用print()方法时,编译器在查看函数参数时便知道要调用哪个函数,并相应地生成目标代码。

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


2
这个答案充满了错误:(1)方法重载不是动态多态。这是静态多态性。(2)静态方法永远不会被覆盖,它们是隐藏/阴影的。(3)私有方法不被“重写”。它们从来没有被继承过。
约翰·雷德

14

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

这张图片清楚地显示了绑定。

捆绑

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

对于每个方法调用,都应该有正确的方法定义。这是Java中的规则。如果编译器没有为每个方法调用看到正确的方法定义,则将引发错误。

现在,使用Java进行静态绑定和动态绑定。

Java中的静态绑定:

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

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

在此处输入图片说明

在此图片中,“ a1”是指向类A的对象的类A的引用变量。“ a2”也是指向类B的对象的类A的引用变量。

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

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

Java中的动态绑定:

动态绑定是在运行时发生的绑定。之所以称为后期绑定,是因为绑定是在程序实际运行时发生的。

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

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

在此处输入图片说明

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


比以前好。
AnBisw

8

多态性: 多态性是对象采取多种形式的能力。当使用父类引用来引用子类对象时,会在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;
    }

}

输出:内部集合排序方法


8

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

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


*(对象在运行时创建并进入堆),它应该在运行时
见面

7

简单来说 :

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

动态多态性:在不同的类中,相同的方法相同的签名覆盖。在编译时不知道要在其上调用方法的对象的类型,但将在运行时确定。

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

从Java教程页面

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


Generally overloading won't be considered as polymorphism.请您详细说明一下。
总理

1
动态绑定和重载是多态的显着点
Ravindra babu,

5

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

方法覆盖被称为动态多态性或简单的多态性运行方法分派动态绑定,因为重写的方法调用在运行时得到解决。

为了理解为什么会这样,让我们​​以Mammaland Human类为例

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调用对象上的方法,这就是为什么方法覆盖被称为动态多态性的原因。

因此,从上面的代码和字节码可以清楚地看出,在编译阶段,调用方法是从引用类型考虑的。但是在执行时,将从引用所在的对象中调用method。

如果您想了解更多有关此内容,可以阅读有关JVM如何内部处理方法重载和覆盖的更多信息


3

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

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


3

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

静态多态(编译时多态)

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

动态多态(运行时多态)

  • 动态多态性决定了在运行时执行哪种方法。
  • 方法覆盖是动态多态性的一个示例,它要求发生动态多态性。
  • 通过动态绑定实现动态多态。
  • 动态多态性发生在不同的类之间。
  • 对于动态多态,将子类对象分配给超类对象是必需的。
  • 动态多态性涉及的继承。

1

编译时多态性(静态绑定/早期绑定):在静态多态性中,如果我们在代码中调用一个方法,那么实际上将要调用该方法的哪个定义仅在编译时解决。

(要么)

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

动态多态性(后期绑定/运行时多态性):在运行时,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方法,表示他可以吃Pizza或Noodles。当我们编译此Person.java时,eat方法被重载了,编译器使用第8行中指定的方法定义来解析方法调用“ e.eat(noodles)[位于第6行],它是将面条作为参数的方法整个过程由编译器完成,因此称为编译时多态性,用方法定义替换方法调用的过程称为绑定,在这种情况下,由编译器完成,因此称为早期绑定。


0

根据Naresh的回答,动态多态在Java中只是“动态”的,因为虚拟机的存在及其在运行时解释代码的能力,而不是在本地运行的代码。

在C ++中,如果使用gcc将其编译为本地二进制文件,则必须在编译时将其解析。但是,虚拟表中的运行时跳转和跳转仍然被称为“查找”或“动态”。如果C继承了B,并且您声明,则c将已经指向外部C对象,并且该指针将传递到文本段中的C :: method1()。请参阅:http : //www.programmersought.com/article/2572545946/B* b = new C(); b->method1();,则b将由编译器解析为指向C内的B对象(对于简单类继承类的情况,C和C内的B对象将在同一内存地址处开始,因此没有任何操作必须完成;它将指向它们都使用的vptr)。如果C继承了B和A,则method1的C条目内的A对象的虚函数表将有一个重击,它将指针指向封装C对象的开头,然后将其传递给真实的A :: method1()在C覆盖的文本段中。对于C* c = new C(); c->method1()

在Java中,对于B b = new C(); b.method1();,虚拟机能够动态检查与b配​​对的对象的类型,并可以传递正确的指针并调用正确的方法。虚拟机的额外步骤消除了对虚拟功能表或在编译时解析的类型的需求,即使在编译时就可以知道它也是如此。这只是一种不同的处理方式,当涉及虚拟机并且代码仅编译为字节码时才有意义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.