20-面向对象编程-多态

面向对象编程有三大特征:封装、继承和多态

 

访问修饰符可以控制属性的访问范围

public:表示公共访问级别,可以被任何类访问。

protected:表示受保护访问级别,可以被类本身、子类和同一包中的类访问。

default(缺省):表示默认访问级别,即如果没有使用访问修饰符,默认是此级别,可以被同一包中的类访问。

private:表示私有访问级别,只能在类内部访问。

修饰符
子类
其他包
public
protected
×
default
×
×
private
×
×
×

 

多态介绍

方法或对象具有多种形态,多态是建立在封装和继承基础之上的。

换句话说,多态可以理解为事物的多种形态,同一个行为具有不同的表现形式或形态的能力。

多态的本质

父类的引用指向了子类的对象

多态的语法

父类类型  引用名  =  new  子类类型();

编译类型看左边,运行类型看右边

 

多态实现的前提条件

在Java中要实现多态,必须要满足以下几个条件,缺一不可:

1) 继承或实现:在多态中必须存在有继承或实现关系的子类和父类

2) 方法的重写:子类必须要对父类中的某些方法进行重写(使用@Override注解进行重写)

3) 通过父类的引用调用重写方法

 

例如:打印机分为黑白打印机和彩色打印机,在黑白打印机情况下打出来为黑白,在彩色打印机情况下打印出来为彩色

 

1. 方法的多态(方法的重载和重写)

public class PolyMethod {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();

        //1.方法的重载体现多态
        //这里我们传入不同的参数,就会调用不同的sum方法
        System.out.println(b.sum(10,20));  //30
        System.out.println(b.sum(10,20,30));  //60

        //2.方法的重写体现多态
        a.say();  //父类A的say方法被调用
        b.say();  //子类B的say方法被调用
    }
}

class A {
    public void say(){
        System.out.println("父类A的say方法被调用");
    }
}

class B extends A {
    //方法重载
    public int sum(int n1, int n2){
        return n1 + n2;
    }
    public int sum(int n1, int n2, int n3){
        return n1 + n2 + n3;
    }
    //方法重写
    public void say(){
        System.out.println("子类B的say方法被调用");
    }
}

2. 对象的多态

1) 一个对象的编译类型和运行类型可以不一致

2) 编译类型在定义对象时,就确定了,不能改变

3) 运行类型是可以变化的

4) 编译类型是看定义时 “=” 等号的左边,运行类型是看 “=” 等号的右边

public class PolyMethod {
    public static void main(String[] args) {
        //对象的多态
        //1.animal的编译类型是 Animal,animal的运行类型是 Dog
        Animal animal = new Dog();
        //当代码执行到当前行时,animal的运行类型时Dog,所以cry方法是Dog类cry方法
        animal.cry();  //Dog  cry   小狗汪汪叫...

        //2.此时,animal的运行类型是 Cat,但编译类型仍然是 Animal
        animal = new Cat();
        //当代码执行到当前行时,animal的运行类型时Cat,所以cry方法是Cat类cry方法
        animal.cry();  //Cat   cry   小猫喵喵叫...
    }
}

class Animal{
    public void cry(){
        System.out.println("Animal   cry   一直在叫...");
    }
}
class Cat extends Animal{
    @Override
    public void cry(){
        System.out.println("Cat   cry   小猫喵喵叫...");
    }
}
class Dog extends Animal{
    @Override
    public void cry(){
        System.out.println("Dog  cry   小狗汪汪叫...");
    }
}

 

练习1. 请编写一个程序,Food类、Animal类和Master类,其中Master类中有feed方法,使用多态完成主人给动物喂食的信息

//请编写一个程序,Food类、Animal类和Master类,其中Master类中有feed方法,使用多态完成主人给动物喂食的信息

public class Test {
    public static void main(String[] args) {
        Master m = new Master("马铃薯");
        Animal animal_1 = new Dog("小狗");
        Food food_1 = new Bone("大棒骨");
        m.feed(animal_1,food_1);  //马铃薯正在给小狗喂食大棒骨...

        Animal animal_2 = new Cat("小猫");
        Food food_2 = new Fish("黄花鱼");
        m.feed(animal_2,food_2);  //马铃薯正在给小猫喂食黄花鱼...

    }
}

//食物类
class Food{
    public String name;
    //构造器
    public Food(String name){
        this.name = name;
    }
    //get() 和 set() 方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

class Bone extends Food{
    public Bone(String name){
        super(name);
    }
}

class Fish extends Food{
    public Fish(String name){
        super(name);
    }
}

//动物类
class Animal{
    public String name;
    //构造器
    public Animal(String name){
        this.name = name;
    }
    //get() 和 set()方法
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
}

class Cat extends Animal{
    public Cat(String name){
        super(name);
    }
}

//主人类
class Master{
    private String name;
    //构造器
    public Master(String name){
        this.name = name;
    }
    //get() 和 set() 方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //feed方法,使用多态完成主人给动物喂食的信息
    //1.使用多态机制,可以统一的管理主人喂食的问题
    //animal 编译类型是Animal,可以指向(接收)Animal子类的对象
    //food 编译类型是Food,可以指向(接收)Food子类的对象
    public void feed(Animal animal, Food food){
        System.out.println(name + "正在给" + animal.getName() + "喂食" + food.getName() + "...");
    }
}

 

多态的向上转型和向下转型

1. 向上转型

向上转型:父类的引用指向了子类的对象。

父类类型 引用名 = new 子类类型()

a) 可以调用父类中的所有属性和方法(需遵守访问权限),但不能调用子类中特有属性和方法。这是因为在编译阶段,能调用哪些成员,是由编译类型决定的。

b) 可以调用子类中重写的父类方法,但最终运行效果看子类的具体实现。这是因为在执行代码阶段,具体实现哪些内容,是由运行类型决定的。

也就是说,左边的编译类型决定调用哪些属性和方法,右边的运行类型决定具体实现哪些内容。

public class PolyDetail {
    public static void main(String[] args) {
        //向上转型:父类的引用指向了子类的对象
        //父类类型 引用名 = new 子类类型()
        Animal animal = new Dog();
        animal.eat();  //小狗在吃大棒骨
        //animal.cry(); 报错,无法调用子类的特有方法cry()
    }
}

class Animal{
    String name = "动物";
    int age = 10;
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("小狗在吃大棒骨");
    }
    public void cry(){
        System.out.println("小狗在一直哭");
    }
} 

2. 向下转型

向下转型:只有向上转型过的对象才能向下转型,向下转型为恢复子类所有功能。

子类类型 引用名 = (子类类型) 父类引用;

a) 只能强转父类的引用,不能强转父类的对象

b) 要求父类的引用,必须指向的是当前目标类型的对象

c) 当向下转型后,就可以调用子类类型中所有的属性和方法

public class PolyDetail2 {
    public static void main(String[] args) {
        //先向上转型
        Animal animal2 = new Dog();
        //再向下转型
     //这时dog的编译类型是Dog类,运行类型也是Dog类,所以能调用子类类型中的所有属性和方法
        Dog dog = (Dog)animal2;
        dog.eat();  //小狗在吃大棒骨
        dog.cry();  //小狗在一直哭
    }
}
class Animal{
    String name = "动物";
    int age = 10;
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("小狗在吃大棒骨");
    }
    public void cry(){
        System.out.println("小狗在一直哭");
    }
}

Java中为了提高向下转型的安全性,引入了 instanceof关键字,判断左边的对象是否为右边类型或子类型的实例,返回 boolean 的数据类型。

如果该表达式为true,则表示左边的对象是右边类或子类所创建的实例;否则,返回false。

Dog dog = new Dog();
Animal animal = dog;

if(animal instanceof Dog){
    dog = (Dog)animal;
    dog.cry();
}

 

Java的动态绑定机制

1. 当调用对象方法时,该方法会和该对象的 运行类型/内存地址 绑定

2. 当调用对象属性时,没有动态绑定机制,哪里声明就在哪里使用

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());
    }
}

class A{
    public int i = 10;
    public int sum(){
        System.out.println("我是父类A中的sum()方法");
        return getI();
    }
    public int getI(){
        System.out.println("我是父类A中的getI()方法");
        return i;
    }
}
class B extends A{
    public int i = 20;
    public int getI(){
        System.out.println("我是子类B中的getI()方法");
        return i;
    }
}

 

posted @ 2023-08-18 17:58  马铃薯1  阅读(14)  评论(0编辑  收藏  举报