任何人都可以提供一个简单的例子来解释Java中动态和静态多态的区别吗?
多态性
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
}
}
方法重载称为静态多态,也称为编译时多态或静态绑定,因为重载方法调用在编译时由编译器根据参数列表和我们调用该方法的引用进行解析。
和方法重写称为动态多态或简单多态或运行时方法调度或动态绑定,因为重写的方法调用在运行时得到解析。
为了理解为什么会这样,让我们以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(“Hindi”)的字节码完全不同,因为编译器能够根据参数列表区分它们和班级参考。这就是方法重载被称为静态多态的原因。
但是anyMammal.speak()和humanMammal.speak()的字节码是相同的,因为根据编译器,两个方法都在Mammal引用上调用,但两个方法调用的输出都不同,因为在运行时JVM知道引用持有的对象和JVM调用对象的方法,这就是为什么方法覆盖被称为动态多态。
因此,从上面的代码和字节码,很明显在编译期间,相位调用方法是从引用类型考虑的。但是在执行时,将从引用所持有的对象调用方法。
如果您想了解更多相关信息,请阅读How Does JVM Handle Method Overloading and Overriding Internally上的更多信息。
编译时多态(Static Binding / Early Binding):在静态多态中,如果我们在代码中调用一个方法,那么实际调用该方法的哪个定义只能在编译时解析。
(要么)
在编译时,Java通过检查方法签名来了解要调用的方法。因此,这称为编译时多态或静态绑定。
动态多态(Late Binding / Runtime Polymorphism):在运行时,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方法,表示他可以吃披萨或面条。当我们编译这个Person.java时,该方法吃掉了,编译器解析方法调用“e.eat(noodles)[在第6行],使用第8行中指定的方法定义,即将面条作为参数的方法并且整个过程由编译器完成,因此它是编译时多态性。用方法定义替换方法调用的过程称为绑定,在这种情况下,它由编译器完成,因此它被称为早期绑定。
动态(运行时)多态性是运行时存在的多态性。这里,Java编译器不了解在编译时调用哪个方法。只有JVM决定在运行时调用哪个方法。使用实例方法的方法重载和方法重写是动态多态的示例。
例如,
静态(编译时)多态性是编译时显示的多态性。这里,Java编译器知道调用哪个方法。使用静态方法重载方法和方法重写;使用私有或最终方法重写的方法是静态多态的示例
例如,
有关详细信息,请阅读“什么是多态”(Google it)。
绑定是指方法调用和方法定义之间的链接。
这张照片清楚地显示了什么是约束力
在这张图片中,“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()。这种类型的绑定称为动态绑定。
以下示例的动态绑定可以如下所示。
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;
}
}
输出:内部集合排序方法
方法重载是编译时/静态多态的一个例子,因为方法调用和方法定义之间的方法绑定在编译时发生,它取决于类的引用(在编译时创建的引用并转到堆栈)。
方法重写是运行时/动态多态的一个示例,因为方法调用和方法定义之间的方法绑定在运行时发生,它取决于类的对象(在运行时创建的对象并转到堆)。
简单来说 :
静态多态性:相同的方法名称在同一个类(不同的签名)中使用不同类型或数量的参数进行重载。目标方法调用在编译时解决。
动态多态性:在不同的类中使用相同的签名覆盖相同的方法。调用方法的对象类型在编译时是未知的,但将在运行时决定。
通常,重载不会被视为多态。
来自java教程page:
类的子类可以定义它们自己的唯一行为,但仍然共享父类的一些相同功能
静态多态性:在编译期间确定要解决哪个方法的决定。方法重载就是一个例子。
动态多态性:在运行时设置决定选择执行哪种方法的地方。方法覆盖可以是一个例子。
多态性是指对象对同一触发器的行为不同的能力。
静态多态性(编译时多态性)
动态多态性(运行时多态性)