有人能提供一个简单的例子来解释Java中动态和静态多态的区别吗?
答案 0 :(得分:168)
<强> 多态性 强>
<强> 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
}
}
答案 1 :(得分:27)
方法重载将是静态多态的一个例子
而重写将是动态多态的一个例子。
因为,在重载的情况下,编译时编译器知道链接到调用的方法。但是,在运行时确定动态多态性
答案 2 :(得分:12)
动态(运行时)多态是运行时存在的多态。这里,Java编译器不了解在编译时调用哪个方法。只有JVM决定在运行时调用哪个方法。使用实例方法的方法重载和方法重写是动态多态的示例。
例如,
考虑一个序列化和反序列化不同的应用程序 文件类型。
我们可以将'Document'作为基类和不同的文档类型 从中衍生出来的类。例如。 XMLDocument,WordDocument等
文档类将定义'Serialize()'和'De-serialize()' 虚方法和每个派生类将实现这些方法 方法以自己的方式根据文件的实际内容。
需要不同类型的文件时 序列化/反序列化,文档对象将被引用 'Document'类引用(或指针)以及何时' 在它上面调用Serialize()'或'De-serialize()'方法, 调用适当版本的虚拟方法。
静态(编译时)多态性是编译时显示的多态性。这里,Java编译器知道调用哪个方法。使用静态方法重载方法和方法重写;使用私有或最终方法重写的方法是静态多态的示例
例如,
员工对象可能有两个print()方法,一个不带 参数和一个带前缀字符串的参数 员工数据。
给定这些接口,当调用print()方法时没有任何接口 参数,编译器,查看函数参数知道要调用哪个函数,并生成目标代码 相应
有关详细信息,请阅读“什么是多态”(Google it)。
答案 3 :(得分:10)
Binding是指方法调用和方法定义之间的链接。
这张照片清楚地显示了什么是约束力。
在这张图片中,“a1.methodOne()”调用绑定到相应的methodOne()定义,“a1.methodTwo()”调用绑定到相应的methodTwo()定义。
对于每个方法调用,都应该有适当的方法定义。这是java中的一个规则。如果编译器没有为每个方法调用看到正确的方法定义,则会抛出错误。
现在,来看看java中的静态绑定和动态绑定。
Java中的静态绑定:
静态绑定是在编译期间发生的绑定。它是 也称为早期绑定,因为绑定发生在程序之前 实际上运行
静态绑定可以如下图所示进行演示。
在这张图片中,'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()。这种类型的绑定称为动态绑定。
以上示例的动态绑定可以如下所示。
答案 4 :(得分:7)
方法重载是编译时/静态多态的一个示例,因为方法调用和方法定义之间的方法绑定在编译时发生,它取决于类的引用(在编译时创建的引用和去堆栈。)
方法覆盖是运行时/动态多态的一个示例,因为方法调用和方法定义之间的方法绑定在运行时发生,并且它取决于类的对象(在运行时创建的对象,并且到堆)。
答案 5 :(得分: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");
}
}
<强>输出:强>
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;
}
}
输出: 内部集合排序方法
答案 6 :(得分:6)
简单来说:
静态多态 :具有不同类型或数量的参数的方法名称 重载 同一类 (不同的签名)。目标方法调用在编译时解决。
动态多态 :同样的方法 重写 , 不同的类中具有相同的签名 即可。调用方法的对象类型在编译时是未知的,但将在运行时决定。
通常,重载不会被视为多态。
来自java教程page:
类的子类可以定义自己的独特行为,但共享父类的一些相同功能
答案 7 :(得分:3)
静态多态性:是在编译期间确定要解决哪个方法的决定。方法重载就是一个例子。
动态多态性:是在运行时设置选择执行哪个方法的决策。方法覆盖就是一个例子。
答案 8 :(得分:2)
方法重载称为静态多态,也称为编译时多态或静态绑定,因为已重载编译器在编译时根据参数列表和我们调用该方法的引用来解析方法调用。
方法覆盖称为动态多态或简单多态或运行时方法调度或动态绑定,因为重写的方法调用在运行时得到解析。
为了理解为什么会这样,让我们举一个Mammal
和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(&#34; Hindi&#34;)的字节码完全不同,因为编译器能够根据参数列表和类引用区分它们。这就是方法重载被称为静态多态的原因。
但anyMammal.speak()和humanMammal.speak()的字节码是相同的,因为根据编译器,两个方法都在Mammal引用上调用,但两个方法调用的输出都不同,因为在运行时JVM知道引用持有哪个对象和JVM在对象上调用方法,这就是为什么Method Overriding被称为动态多态。
因此,从上面的代码和字节码可以看出,在编译期间,相位调用方法是从引用类型中考虑的。但是在执行时,将从引用所持有的对象调用方法。
如果您想了解更多相关信息,请参阅How Does JVM Handle Method Overloading and Overriding Internally。
答案 9 :(得分:2)
多态性是指对象对同一触发器的行为能力不同。
静态多态性(编译时多态性)
动态多态性(运行时多态性)
答案 10 :(得分:1)
编译时多态(静态绑定/早期绑定):在静态多态中,如果我们在代码中调用一个方法,那么实际上要调用该方法的哪个定义只在编译时解析
(或)
在编译时,Java通过检查方法签名来了解要调用的方法。因此,这称为编译时多态或静态绑定。
动态多态性(后期绑定/运行时多态性):在运行时,Java会等到运行时才能确定引用实际指向了哪个对象。方法解析是在运行时进行的,因为我们将其称为运行时多态性。
答案 11 :(得分: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()实现,因为它取决于用户在运行时提供的值。因此,仅在运行时决定将调用哪种方法。因此,运行时多态性。
答案 12 :(得分: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行中指定的方法定义,即将面条作为参数的方法并且整个过程由Compiler完成,因此它是Compile time Polymorphism。 用方法定义替换方法调用的过程称为绑定,在这种情况下,它由编译器完成,因此它被称为早期绑定。
答案 13 :(得分:0)
根据Naresh的回答,动态多态在Java中只是“动态”的,因为虚拟机的存在及其在运行时解释代码的能力,而不是在本地运行的代码。
显然,在C ++中,如果使用gcc将其编译为本地二进制文件,则必须在编译时进行解析。但是,虚拟表中的运行时跳转和重击仍称为“查找”或“动态”。如果C继承了B,并且您声明了B* b = new C(); b->method1();
,则编译器将解析b指向C内部的B对象(对于简单类继承类的情况,C和C内部的B对象将以C开始。相同的内存地址,因此无需执行任何操作;它将指向它们都使用的vptr)。如果C继承了B和A,则method1的C条目内的A对象的虚函数表将有一个thunk,它将使指针偏移到封装C对象的开头,然后将其传递给真实的A :: method1()在C覆盖的文本段中。对于C* c = new C(); c->method1()
,c将已经指向外部C对象,并且该指针将被传递到文本段中的C :: method1()。请参阅:http://www.programmersought.com/article/2572545946/
在Java中,对于B b = new C(); b.method1();
,虚拟机能够动态检查与b配对的对象的类型,并可以传递正确的指针并调用正确的方法。虚拟机的额外步骤消除了对虚拟功能表或在编译时解析的类型的需求,即使在编译时就可以知道它也是如此。这只是一种不同的处理方式,当涉及虚拟机并且代码仅编译为字节码时,这才有意义。