Java 多态

多态:多态分为两种
(1)编译时多态:方法的重载
(2)运行时多态:Java虚拟机根据调用该方法实例的类型来决定选择调用哪个方法,被称为运行时多态,我们所说的多态是指运行时多态。

1、多态是OOP(面向对象编程)的三大特性之一(封装、继承、多态)。
所谓封装:就是指把现实需求中的事务抽象成一个对象,并且对自己的内部的域和方法不同情况具有不同的访问权限。隐藏类的内部实现机制,可以在不影响使用的情况下,改变类的内部结构(比如:增加方法),同时也可以保护数据。对外界而言它的内部细节是隐藏的,暴露给外界的只有它的访问方法。
继承:是指类具有继承父类的方法和域的能力,并可以根据需要扩展功能,从而具有一定的代码复用功能。同时也为多态做了铺垫。
多态:就是父类引用指向其任何一种子类,jvm根据不同的指向子类在运行时绑定对应的函数,从而调用相应类中的同名方法。

比如:A a=new B()  (B是A的子类,或者A是一个接口,B或它的一个父接口实现A)
在上面的代码中表示:赋值a一个B的实例,a的类型为A。这意味着不能在**B中**调用A中没有定义过的方法。但如果输出a.getClass().getName()的值,将会得到“B”而不是“A”。这是因为在编译时a的类型为A,因此编译器将不允许调用B中没有在A中定义的方法。另一方面,运行时a的类型为B,如a.getClass().getName()的值。
多态本质,如果B中覆盖了A中的方法play,那么调用a.play()将是调用B中的play;如果方法stop不是在B中实现,那么JVM足够智能根据继承结构去查找,然后参数集是否正确,从而在编译的时候检查是否正确。
package polymorphismandreflect;
class A {
    public void play(){
        System.out.println("I am a A");
    }
    public void stop(){
        System.out.println("I am stop A");
    }
}
class B extends A{
    public void play(){
        System.out.println("I am a B");
    }
public void voice(){
        System.out.println("I am a voice");
    }
}
public class testpolymorphism  {
 public void test(){
     System.out.println("I am a test");
 }
 public static void main(String[] args) {
    A a =new B();
    a.play();
    a.stop();
    //a.voice();
    //A test=new testpolymorphism();
    //test.test();
}
}

I am a B
I am stop A
从结果中可以看出在编译的时候,由于把a当做A类型,所以在test中a.voice();是不能调用的。即在继承子类中不能调用父类中没有的方法,在别的类中也不可以。但在运行时a是B类型,这种方法绑定是发生在运行时而不是编译时,区别于静态方法和final,静态方法就是在编译时把方法调用和方法体绑定在一起。
多态的好处是在比如做参数类型的时候,父类做参数,实际传值时可以使其任意子类。如object, 同时作为引用变量可以指向不同的对象。
进一步了解:
多态存在的三个必要条件:
继承
要有方法重写
父类引用指向子类对象(所以父类引用指向父类就不算多态了)

public class Letter {
   public static void main(String[] args) {
    A aa=new A();
    A ab=new B();
    B bb=new B();
    C c=new C();

    ab.show(bb);
    System.out.println(ab.getClass());
    ab.show(aa);
}
}
class A{
    public void show(A obj){
        System.out.println("A-A");
    }
//  public void show(B obj){////如果不注释则会输出“B-B” 具体请看下面解析
//      System.out.println("A-B");
//  }
    public void show(C obj){
        System.out.println("A-C");
    }

}
class B extends A{
    public void show(B obj){
        System.out.println("B-B");
    }
    public void show(A obj){
        System.out.println("B-A");
    }

}
class C extends B{
    public void show(C obj){
        System.out.println("C-C");
    }
    public void sshow(C obj){
        System.out.println("CC");
    }
}

输出结果:

B-A
class polymorphism.B
B-A

对于第一条输出结果可能会有疑问:为什么不是“B-B”那?想知道请先理解下面这句话:
当超类(父类)对象引用变量引用子类对象时,决定调用哪个类的成员方法的是引用变量指向的对象的类型,而不是引用变量定义的类型,这种情况下,这个被调用的方法必须是在超类中定义过(即不能执行子类中独有的方法)。
当超类(父类)对象(A)引用变量(ab)引用子类对象(new B())时,决定调用哪个类的成员方法(show())的是引用变量指向的对象的类型(B),而不是引用变量定义的类型(A),这种情况下,这个被调用的方法必须是在超类中定义过(即不能执行子类中独有的方法)。
这个情况比较符合ab.show(aa);
虽然ab类型是A,但ab指向的类型是B,所以决定执行方法show()的是类型B,根据优先级执行顺序:this.show(o),super.show(o),this.show((super)o),super.show((super)o);
这里的this就是B对象(创建子类只会调用父类无参构造器来初始化父类非私有属性和方法,这样子类才会有父类的方法和属性,并不会创建父类对象)但在java编译的时候就认为是类型A,所以只能调用在A中定义的的方法和属性。所以当一个子类对象被实例化后,该实例对象会包含两部分:一部分是子类(B)中定义的属性和方法(包含重写父类中的方法),还会包含父类(A)中的属性和方法。由于会先去从父类中继承部分查找show(A obj),发现父类中定义了该方法,且B重写了该方法所以执行子类B中的方法输出B-A。
但对于ab.show(bb);根据顺序,先执行this.show(B obj),但是类B实例化对象继承父类方法中没有该方法ab.show(B obj);且没有超类 所以执行第三个向上转(this.show(super B))this.show(A obj)但这个却被子类B重写了 所以这个执行子类的方法show(A obj)。输出B-A

### Java 多态的概念 Java 中的多态是指同一个接口或类可以有多种不同的实现方式。它允许程序在运行时决定调用哪个方法,从而提高代码的灵活性和可扩展性。多态的核心机制依赖于继承、重写以及动态绑定。 #### 动态绑定 当子类覆盖父类的方法时,在运行期间会根据对象的实际类型来决定执行哪一个版本的方法[^1]。这是多态的关键特性之一。 ### 实现多态的方式 Java多态可以通过以下两种主要形式实现: 1. **方法重写(Override)** 2. **接口实现** 以下是具体示例说明如何利用 `instanceof` 运算符避免潜在异常并展示多态的应用场景。 --- ### 示例代码:Java 多态的具体应用 下面是一个完整的例子,展示了如何通过多态性和 `instanceof` 来处理不同类型的对象实例。 ```java // 定义一个基类 Animal class Animal { void makeSound() { System.out.println("Some generic sound"); } } // 子类 Dog 继承自 Animal 并重写了 makeSound 方法 class Dog extends Animal { @Override void makeSound() { System.out.println("Bark"); } // 额外的功能只属于狗 void fetchStick() { System.out.println("Fetching stick..."); } } // 子类 Cat 继承自 Animal 并重写了 makeSound 方法 class Cat extends Animal { @Override void makeSound() { System.out.println("Meow"); } // 额外的功能只属于猫 void climbTree() { System.out.println("Climbing tree..."); } } public class PolymorphismExample { public static void main(String[] args) { // 创建多个动物对象并通过向上转型存储它们 Animal myDog = new Dog(); Animal myCat = new Cat(); // 调用各自的 makeSound 方法 myDog.makeSound(); // 输出 Bark myCat.makeSound(); // 输出 Meow // 如果需要访问特定子类功能,则需使用 instanceof 和强制转换 if (myDog instanceof Dog) { ((Dog) myDog).fetchStick(); // 正确调用了 Dog 类中的特有方法 } if (myCat instanceof Cat) { ((Cat) myCat).climbTree(); // 正确调用了 Cat 类中的特有方法 } Object cValue = 42; // 假设我们有一个未知类型的变量 // 判断其实际类型并打印相应消息 System.out.println("The type of value is " + (cValue instanceof Double ? "Double" : (cValue instanceof Integer ? "Integer" : "Unknown"))); // 输出 Integer [^2] } } ``` 上述代码片段中: - 我们定义了一个通用的 `Animal` 类作为超类。 - 然后创建两个派生类 `Dog` 和 `Cat`,分别实现了自己的行为逻辑。 - 在主函数里演示了即使将这些对象赋给更广泛的父类引用 (`Animal`),仍然能够正确表现出各自的行为特征——这就是所谓的“编译看声明类型, 执行找真实类型”。 另外还加入了关于 `instanceof` 关键字使用的部分,用于确认某个对象的确切类别以便安全地进行向下造型操作而不会引发 ClassCastException 错误。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值