博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 05OOP多态

Posted on 2020-12-07 21:47  Kingdomer  阅读(105)  评论(0)    收藏  举报

JavaEE - 05OOP多态

(1)面向对象特征之三: 多态性

(1.1)多态性概述

  • 对象的多态性: 父类的引用指向子类的对象(或子类的对象赋给了父类的引用);可以直接应用在抽象类和接口上
  • Java引用变量有两个类型: 编译时类型运行时类型
  • 编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称: 编译时,看左边;运行时,看右边。
    • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
    • 多态情况下,"看左边":看的是父类的引用(父类中不具备子类特有的方法);"看右边":看的是子类的对象(实际运行的是子类重写父类的方法)。
  • 理解多态性: 一个事物的多种形态。 Person p1 = new Person();   Person p2 = new Man(); Person p3 = new Woman();
  • 简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。

  • 运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:

    • 方法重写:子类继承父类并重写父类中已有的或抽象的方法;

    • 对象造型:用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为。

  • Java中的多态靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,
    • 而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法, 也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

(1.2)多态的使用

  • 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法  -- 虚拟方法调用
    • 有了对象的多态性以后,在编译期只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类的方法。
  • 多态性的使用前提: 1.类的继承关系     2.方法的重写
  • 对象的多态性,只适用于方法,不适用于属性。
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.eat();                 //人: 吃饭
        p1.walk();                //人: 走路
        Person p2 = new Man();
        p2.eat();                 //男人: 吃饭
        p2.walk();                //人: 走路
        ((Man) p2).earnMoney();   //男人: 要挣钱
        Person p3 = new Woman();
        p3.eat();                 //人: 吃饭
        p3.walk();                //女人: 逛街
((Woman) p3).makeUp(); //女人: 化妆 } }

 

public class AnimalTest {
    public static void main(String[] args) {
        AnimalTest test = new AnimalTest();
        test.func(new Dog());
        test.func(new Cat());
    }

    public void func(Animal animal){  // Animal animal = new Dog();
        animal.eat();
        animal.shout();
    }
    // 没有对象多态性的话,每个子类对象都需要定义重复的相同方法
//    public void func(Dog dog){
//        dog.eat();
//        dog.shout();
//    }
//    public void func(Cat cat){
//        cat.eat();
//        cat.shout();
//    }
}

 

(2)虚拟方法调用(Virtual Method Invocation)

  • 正常的方法调用
    • Person p = new Person();
    • p.getInfo();
    • Student e = new Student();
    • e.getInfo();
  • 虚拟方法调用(多态情况下)
    • 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法
    • 父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的调用在编译期是无法确定的。
    • Person e = new Student();
    • e.getInfo(); // 调用Student类的getInfo()方法
  • 编译时类型和运行时类型
    • 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。 -- 动态绑定

多态是编译时行为还是运行时行为?   运行时

public class Animal {
    protected void eat(){    System.out.println("动物吃东西");    }
}

public class Cat extends Animal {
    protected void eat(){    System.out.println("猫吃鱼");   }
}

public class Dog extends Animal {
    public void eat(){   System.out.println("狗吃骨头");  }
}

public class Sheep extends Animal{
    public void eat(){    System.out.println("羊吃草");  }
}

 

public class AnimalTest {
    public static void main(String[] args) {
        int key = new Random().nextInt(3);
        Animal animal = getInstance(key);
        animal.eat();
    }

    public static Animal getInstance(int key){
        switch (key) {
            case 0:
                return new Cat();
            case 1:
                return new Dog();
            default:
                return new Sheep();
        }
    }
}

 

(3)instanceOf 操作符

  • x instanceOf A: 检验x是否为类A的对象,返回值为boolean类型。
    • 要求x所属的类与类A必须是子类和父类的关系,否则编译报错。
    • 如果x属于类A的子类B,x instanceOf A 值也为true。
public class AnimalTest2 {
    public static void main(String[] args) {
        AnimalTest2 test2 = new AnimalTest2();
        test2.method(new Dog());
    }

    public void method(Animal e){
        if(e instanceof Cat){
            System.out.println("这是Cat类");
        }else if(e instanceof Dog){
            System.out.println("这是Dog类");
        }else{
            System.out.println("这是Animal类");
        }
    }
}

 

(4)向下转型

  • 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的。 
    • 由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
  • 如何才能调用子类特有的属性和方法
    • 向下转型: 使用强制类型转换符。 Person p = new Man();   Man m1 = (Man) p;
  • 为了避免在向下转型时出现ClassCastException的异常,先进行instanceOf判断,返回true,进行向下转型。返回false,不进行向下转型。
  • 较高级的基本数据类型 --强制类型转换--> 较低级的基本数据类型;   较低级的基本数据类型 --自动类型提升--> 较高级的基本数据类型;
  • 父类(如Person) --向下转型,使用instanceOf判断--> 子类(如Student);   子类 --向上转型(多态)--> 父类
        // 编译时通过,运行时不通过,ClassCastException
//        Animal a1 = new Cat();
//        Dog dog = (Dog)a1;
        // 编译时通过,运行时通过
        Object obj = new Cat();
        Animal a2 = (Animal) obj;

 

 

继承成员变量和继承方法的区别

public class Base {
    int count = 10;
    public void display(){
        System.out.println(this.count);
    }
}
public class Sub extends Base {
    int count = 20;
    public void display(){
        System.out.println(this.count);
    }
}
public class FieldMethodTest {
    public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.count);  //20
        s.display();  //20
        Base b = s;
        // ==: 对于引用数据类型,比较的是两个引用数据类型变量的地址值是否相同
        System.out.println(b == s);  //true
        System.out.println(b.count); //10  属性是声明变量的类
        b.display();  //20   方法还是调用的子类的方法
    }
}