谁能提供一个简单的示例来说明Java中动态和静态多态性之间的区别?
谁能提供一个简单的示例来说明Java中动态和静态多态性之间的区别?
Answers:
多态性
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
}
}
Animal reference but Dog object
,我们为什么不能使用Dog reference and dog object
?
动态(运行时)多态是运行时存在的多态。在这里,Java编译器无法理解在编译时调用哪种方法。只有JVM决定在运行时调用哪种方法。使用实例方法的方法重载和方法重写是动态多态性的示例。
例如,
考虑一个可以对不同类型的文档进行序列化和反序列化的应用程序。
我们可以将“文档”作为基类,并派生不同的文档类型类。例如XMLDocument,WordDocument等。
文档类将把'Serialize()'和'De-serialize()'方法定义为虚拟方法,每个派生类将根据文档的实际内容以自己的方式实现这些方法。
当需要对不同类型的文档进行序列化/反序列化时,文档对象将由“文档”类引用(或指针)进行引用,并且在调用“ Serialize()”或“ De-serialize()”方法时在其上,将调用适当版本的虚拟方法。
静态(编译时)多态是编译时表现出的多态。在这里,Java编译器知道调用哪个方法。使用静态方法的方法重载和方法重写;使用私有或最终方法的方法重载是静态多态性的示例
例如,
一个雇员对象可能有两个print()方法,一个不带任何参数,另一个带前缀字符串,与雇员数据一起显示。
给定这些接口,当在不带任何参数的情况下调用print()方法时,编译器在查看函数参数时便知道要调用哪个函数,并相应地生成目标代码。
有关更多详细信息,请阅读“什么是多态”(Google it)。
绑定是指方法调用和方法定义之间的链接。
这张图片清楚地显示了绑定。
在此图中,“ 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()。这种类型的绑定称为动态绑定。
以上示例的动态绑定可以如下所示。
多态性: 多态性是对象采取多种形式的能力。当使用父类引用来引用子类对象时,会在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;
}
}
输出:内部集合排序方法
简单来说 :
静态多态性:同一方法名称重载了同一类中不同类型或数量的参数(不同的签名)。目标方法调用在编译时解决。
动态多态性:在不同的类中,相同的方法被相同的签名覆盖。在编译时不知道要在其上调用方法的对象的类型,但将在运行时确定。
通常,重载不会被视为多态。
从Java教程页面:
一个类的子类可以定义自己的独特行为,但可以共享父类的某些相同功能
Generally overloading won't be considered as polymorphism.
请您详细说明一下。
方法重载称为静态多态性,也称为编译时多态性或静态绑定因为重载的方法调用在编译时由编译器根据参数列表和我们调用该方法的引用来解析。
和方法覆盖被称为动态多态性或简单的多态性和运行方法分派或动态绑定,因为重写的方法调用在运行时得到解决。
为了理解为什么会这样,让我们以Mammal
and 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如何内部处理方法重载和覆盖的更多信息。
多态性是指对象对于同一触发器具有不同行为的能力。
静态多态(编译时多态)
动态多态(运行时多态)
编译时多态性(静态绑定/早期绑定):在静态多态性中,如果我们在代码中调用一个方法,那么实际上将要调用该方法的哪个定义仅在编译时解决。
(要么)
在编译时,Java通过检查方法签名知道要调用的方法。因此,这称为编译时多态或静态绑定。
动态多态性(后期绑定/运行时多态性):在运行时,Java等到运行时确定引用实际指向的对象。方法解析是在运行时进行的,因为我们称其为运行时多态。
考虑下面的代码:
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()实现,因为它取决于用户在运行时提供的值。因此,仅在运行时决定将调用哪种方法。因此,运行时多态。
方法重载是编译时的多态性,让我们以一个例子来理解这个概念。
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行],它是将面条作为参数的方法整个过程由编译器完成,因此称为编译时多态性,用方法定义替换方法调用的过程称为绑定,在这种情况下,由编译器完成,因此称为早期绑定。
根据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配对的对象的类型,并可以传递正确的指针并调用正确的方法。虚拟机的额外步骤消除了对虚拟功能表或在编译时解析的类型的需求,即使在编译时就可以知道它也是如此。这只是一种不同的处理方式,当涉及虚拟机并且代码仅编译为字节码时才有意义。