java 多态

1.面向对象的特征---封装、继承、多态

2.多态代码分析

多态是对象在不同时刻表现出来的多种状态,是一种编译时期状态和运行时期状态不一致的现象。我们在编写或者分析代码时记住如下口诀:

  • 成员变量:编译看左,运行看左(因为无法重写);

  • 成员方法:编译看左,运行看右(因为普通成员方法可以重写,变量不可以);

  • 静态方法:编译看左,运行看左(因为属于类);

public class Test {

    public static void main(String[] args) {
        A aa =new A();
        A ab =new B();
        B b =new B();
        C c =new C();
        D d =new D();

        System.out.println(aa.run(b));// 1, A & A
        System.out.println(aa.run(c));// 2, A & A
        System.out.println(aa.run(d));// 3, A & D
System.out.println(ab.run(b));// 4, B & A System.out.println(ab.run(c));// 5, B & A System.out.println(ab.run(d));// 6, A & D
System.out.println(b.run(b));// 7, B & B System.out.println(b.run(c));// 8, B & B System.out.println(b.run(d));// 9, A & D } } class A { public String run(D obj){ return("A & D"); } public String run(A obj) { return("A & A"); } } class B extends A { public String run(B obj){ return("B & B"); } public String run(A obj) { return("B & A"); } } class C extends B {} class D extends B {}

注释1、2、3中 aa 在编译时取决于左边 A 的类型,所以包含了参数为 A、D 的 run 方法

注释1中 运行时传递给 run 方法的参数类型为 B,而此时只支持参数为 A、D 的 run 方法,而  B 继承自 A,所以执行了 A 类中参数为 A 类型的 run 方法。

注释2中 运行时传递给 run 方法的参数类型为 C,而此时只支持参数为 A、D 的 run 方法,而  C 又继承自 B,B 继承自 A,所以执行了 A 类中参数为 A 类型的 run 方法。

注释3中 运行时传递给 run 方法的参数类型为 D,而此时恰巧支持参数为 A、D 的 run 方法,所以直接执行了 A 类中参数为 D 类型的 run 方法。

注释4、5、6中 ab 在编译时取决于左边 A 的类型,所以包含了参数为 A、D 的 run 方法。由于运行时为右边 B 的类型,所以要注意重写问题,如果有重写,调用重写的方法。

注释4中 运行时传递给 run 方法的参数类型为 B,所以对应的方法为 A 类中参数为 A 类型的 run 方法,而由于 ab 在运行时右侧的 B 类中重写了 A 类中参数为 A 类型的 run 方法,所以运行时最终执行了 B 类中重写的参数为 A 类型的 run 方法。

注释5中 运行时传递给 run 方法的参数类型为 C,C 又最终继承自 A,所以对应的方法为 A 类中参数为 A 类型的 run 方法,而由于 ab 在运行时右侧的 B 类中重写了 A 类中参数为 A 类型的 run 方法,所以运行时最终执行了 B 类中重写的参数为 A 类型的 run 方法。

注释6中运行时传递给 run 方法的参数类型为 D,所以对应的方法为 A 类中参数为 D 类型的 run 方法。

注释7、8、9中在编译时取决于左边 B 的类型,运行时为右边 B 的类型,所以编译时包含了参数为 A、B、D 的 run 方法

注释7中 运行时传递给 run 方法的参数类型为 B,所以对应的方法为 B 类中参数为 B 类型的 run 方法。

注释8中 运行时传递给 run 方法的参数类型为 C,而 C 的第一父类是 B,此时恰巧 B 中有支持参数为 B 的 run 方法(所以不用再往上找),所以对应的方法为 B 类中参数为 B 类型的 run 方法。

注释9中 运行时传递给 run 方法的参数类型为 D,所以对应的方法为 B 类中从 A 类继承来的参数为 D 类型的 run 方法。

 3.Java 重载与重写

重载与重写是 Java 多态性的不同表现,重写是父类与子类之间多态性的表现,在运行时起作用,而重载是一个类中多态性的表现,在编译时起作用。

重载规则:必须具有不同的参数列表; 可以有不同的返回类型;可以有不同的访问修饰符;可以抛出不同的异常。

重写规则:参数列表必须完全与被重写的方法相同;返回类型必须一直与被重写的方法相同;访问修饰符的限制一定要大于等于被重写方法的访问修饰符;重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常,譬如父类方法声明了一个检查异常 IOException,在重写这个方法时就不能抛出 Exception,只能抛出 IOException 的子类异常,可以抛出非检查异常。

4.Java 构造方法能否被重写和重载

重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能被重写的。而重载是针对同一个的,所以构造方法可以被重载。

5.重写代码分析

public class Demo {

    public boolean equals(Demo other) {
        return true;
    }

    public static void main(String[] args) {
        Object o1 = new Demo();
        Object o2 = new Demo();
        Demo o3 = new Demo();
        Demo o4 = new Demo();
        if (o1.equals(o2)) {
            System.out.println("o1 is equal with o2.");
        }
        if (o3.equals(o4)) {
            System.out.println("o3 is equal with o4.");
        }
    }
}

运行结果是:o3 is equal with o4.

因为 Demo 类中的 public boolean equals(Demo other) 方法并没有重写 Object 类中的 public boolean equals(Object obj) 方法,原因是其违背了参数规则。

当调用 o1.equals(o2) 时,o2 是 Object 类型参数,实际上调用了 Object 类中的 public boolean equals(Object obj) 方法,而 Object 类的 equals 方法是通过比较内存地址才返回 false;当调用 o3.equals(o4) 时,实际上调用了 Demo 类中的 equals(Demo other) 方法,所以才有上面的打印。

原文地址:https://www.cnblogs.com/mcahkf/p/9062732.html