乐趣区

关于后端:Java-的多态

1.1 多态的模式

多态是继封装、继承之后,面向对象的第三大个性。

多态是呈现在继承或者实现关系中的

多态体现的格局

父类类型 变量名 = new 子类 / 实现类结构器;
变量名. 办法名();

多态的前提:有继承关系,子类对象是能够赋值给父类类型的变量。例如 Animal 是一个动物类型,而 Cat 是一个猫类型。Cat 继承了 Animal,Cat 对象也是 Animal 类型,天然能够赋值给父类类型的变量。

1.2 多态的应用场景

如果没有多态,在下图中 register 办法只能传递学生对象,其余的 Teacher 和 administrator 对象是无奈传递给 register 办法办法的,在这种状况下,只能定义三个不同的 register 办法别离接管学生,老师和管理员。

有了多态之后,办法的形参就能够定义为独特的父类 Person。

要留神的是:

  • 当一个办法的形参是一个类,咱们能够传递这个类所有的子类对象。
  • 当一个办法的形参是一个接口,咱们能够传递这个接口所有的实现类对象(前面会学)。
  • 而且多态还能够依据传递的不同对象来调用不同类中的办法。

代码示例:

父类:public class Person {
    private String name;
    private int age;

    空参结构
    带全副参数的结构
    get 和 set 办法

    public void show(){System.out.println(name + "," + age);
    }
}

子类 1:public class Administrator extends Person {
    @Override
    public void show() {System.out.println("管理员的信息为:" + getName() + "," + getAge());
    }
}

子类 2:public class Student extends Person{

    @Override
    public void show() {System.out.println("学生的信息为:" + getName() + "," + getAge());
    }
}

子类 3:public class Teacher extends Person{

    @Override
    public void show() {System.out.println("老师的信息为:" + getName() + "," + getAge());
    }
}

测试类:public class Test {public static void main(String[] args) {
        // 创立三个对象,并调用 register 办法

        Student s = new Student();
        s.setName("张三");
        s.setAge(18);


        Teacher t = new Teacher();
        t.setName("王建国");
        t.setAge(30);

        Administrator admin = new Administrator();
        admin.setName("管理员");
        admin.setAge(35);



        register(s);
        register(t);
        register(admin);


    }



    // 这个办法既能接管老师,又能接管学生,还能接管管理员
    // 只能把参数写成这三个类型的父类
    public static void register(Person p){p.show();
    }
}

1.3 多态的定义和前提

多态:是指同一行为,具备多个不同表现形式。

从下面案例能够看出,Cat 和 Dog 都是动物,都是吃这一行为,然而呈现的成果(表现形式)是不一样的。

前提【重点】

  1. 有继承或者实现关系
  2. 办法的重写【意义体现:不重写,无意义】
  3. 父类援用指向子类对象【格局体现】

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

1.4 多态的运行特点

调用成员变量时:编译看右边,运行看右边

调用成员办法时:编译看右边,运行看左边

代码示例:

Fu f = new Zi();// 编译看右边的父类中有没有 name 这个属性,没有就报错
// 在理论运行的时候,把父类 name 属性的值打印进去
System.out.println(f.name);
// 编译看右边的父类中有没有 show 这个办法,没有就报错
// 在理论运行的时候,运行的是子类中的 show 办法
f.show();

1.5 多态的弊病

咱们曾经晓得多态编译阶段是看右边父类类型的,如果子类有些独有的性能,此时 多态的写法就无法访问子类独有性能了

class Animal{public  void eat(){
        System.out.println("动物吃货色!")
    }
}
class Cat extends Animal {public void eat() {System.out.println("吃鱼");  
    }  
   
    public void catchMouse() {System.out.println("抓老鼠");  
    }  
}  

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

class Test{public static void main(String[] args){Animal a = new Cat();
        a.eat();
        a.catchMouse();// 编译报错,编译看右边,Animal 没有这个办法}
}

1.6 援用类型转换

1.6.1 为什么要转型

多态的写法就无法访问子类独有性能了。

当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误。也就是说,不能调用 子类领有,而父类没有的办法。编译都谬误,更别说运行了。这也是多态给咱们带来的一点 ” 小麻烦 ”。所以,想要调用子类特有的办法,必须做向下转型。

回顾根本数据类型转换

  • 主动转换: 范畴小的赋值给范畴大的. 主动实现:double d = 5;
  • 强制转换: 范畴大的赋值给范畴小的, 强制转换:int i = (int)3.14

​ 多态的转型分为向上转型(主动转换)与向下转型(强制转换)两种。

1.6.2 向上转型(主动转换)

  • 向上转型 :多态自身是子类类型向父类类型向上转换(主动转换)的过程,这个过程是默认的。
    当父类援用指向一个子类对象时,便是向上转型。
    应用格局:
父类类型  变量名 = new 子类类型();
如:Animal a = new Cat();

起因是:父类类型绝对与子类来说是大范畴的类型,Animal 是动物类,是父类类型。Cat 是猫类,是子类类型。Animal 类型的范畴当然很大,蕴含所有动物。所以子类范畴小能够间接主动转型给父类类型的变量。

1.6.3 向下转型(强制转换)

  • 向下转型 :父类类型向子类类型向下转换的过程,这个过程是强制的。
    一个曾经向上转型的子类对象,将父类援用转为子类援用,能够应用强制类型转换的格局,便是向下转型。

应用格局:

子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
   Cat c =(Cat) a;  

1.6.4 案例演示

当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误。也就是说,不能调用 子类领有,而父类没有的办法。编译都谬误,更别说运行了。这也是多态给咱们带来的一点 ” 小麻烦 ”。所以,想要调用子类特有的办法,必须做向下转型。

转型演示,代码如下:

定义类:

abstract class Animal {abstract void eat();  
}  

class Cat extends Animal {public void eat() {System.out.println("吃鱼");  
    }  
    public void catchMouse() {System.out.println("抓老鼠");  
    }  
}  

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

定义测试类:

public class Test {public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();                 // 调用的是 Cat 的 eat

        // 向下转型  
        Cat c = (Cat)a;       
        c.catchMouse();         // 调用的是 Cat 的 catchMouse}  
}

1.6.5 转型的异样

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

public class Test {public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        Dog d = (Dog)a;       
        d.watchHouse();        // 调用的是 Dog 的 watchHouse【运行报错】}  
}

这段代码能够通过编译,然而运行时,却报出了 ClassCastException,类型转换异样!这是因为,明明创立了 Cat 类型对象,运行时,当然不能转换成 Dog 对象的。

1.6.6 instanceof 关键字

为了防止 ClassCastException 的产生,Java 提供了 instanceof 关键字,给援用变量做类型的校验,格局如下:

变量名 instanceof 数据类型 
如果变量属于该数据类型或者其子类类型,返回 true。如果变量不属于该数据类型或者其子类类型,返回 false。

所以,转换前,咱们最好先做一个判断,代码如下:

public class Test {public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        if (a instanceof Cat){Cat c = (Cat)a;       
            c.catchMouse();        // 调用的是 Cat 的 catchMouse} else if (a instanceof Dog){Dog d = (Dog)a;       
            d.watchHouse();       // 调用的是 Dog 的 watchHouse}
    }  
}

1.6.7 instanceof 新个性

JDK14 的时候提出了新个性,把判断和强转合并成了一行

// 新个性
// 先判断 a 是否为 Dog 类型,如果是,则强转成 Dog 类型,转换之后变量名为 d
// 如果不是,则不强转,后果间接是 false
if(a instanceof Dog d){d.lookHome();
}else if(a instanceof Cat c){c.catchMouse();
}else{System.out.println("没有这个类型,无奈转换");
}

1.7 综合练习

需要:依据需要实现代码:
    1. 定义狗类
        属性:年龄,色彩
        行为:
            eat(String something)(something 示意吃的货色)
            看家 lookHome 办法(无参数)
2. 定义猫类
    属性:年龄,色彩
    行为:
        eat(String something)办法(something 示意吃的货色)
        逮老鼠 catchMouse 办法(无参数)
3. 定义 Person 类 // 饲养员
    属性:姓名,年龄
    行为:keepPet(Dog dog,String something)办法
            性能:喂养宠物狗,something 示意喂养的货色
    行为:keepPet(Cat cat,String something)办法
            性能:喂养宠物猫,something 示意喂养的货色
    生成空参有参结构,set 和 get 办法  
4. 定义测试类(实现以下打印成果):
    keepPet(Dog dog,String somethind)办法打印内容如下:年龄为 30 岁的老王养了一只黑色彩的 2 岁的狗
        2 岁的黑色彩的狗两只前腿死死的抱住骨头猛吃
    keepPet(Cat cat,String somethind)办法打印内容如下:年龄为 25 岁的老李养了一只灰色彩的 3 岁的猫
        3 岁的灰色彩的猫眯着眼睛侧着头吃鱼
5. 思考:1.Dog 和 Cat 都是 Animal 的子类,以上案例中针对不同的动物,定义了不同的 keepPet 办法,过于繁琐,是否简化,并领会简化后的益处?2.Dog 和 Cat 尽管都是 Animal 的子类,然而都有其特有办法,是否想方法在 keepPet 中调用特有办法?

画图剖析:

代码示例:

// 动物类(父类)public class Animal {
    private int age;
    private String color;


    public Animal() {}

    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    public int getAge() {return age;}

    public void setAge(int age) {this.age = age;}

    public String getColor() {return color;}

    public void setColor(String color) {this.color = color;}

    public void eat(String something){System.out.println("动物在吃" + something);
    }
}

// 猫类(子类)public class Cat extends Animal {public Cat() { }

    public Cat(int age, String color) {super(age, color);
    }

    @Override
    public void eat(String something) {System.out.println(getAge() + "岁的" + getColor() + "色彩的猫眯着眼睛侧着头吃" + something);
    }

    public void catchMouse(){System.out.println("猫抓老鼠");
    }

}

// 狗类(子类)public class Dog extends Animal {public Dog() { }

    public Dog(int age, String color) {super(age, color);
    }

    // 行为
    //eat(String something)(something 示意吃的货色)
    // 看家 lookHome 办法(无参数)
    @Override
    public void eat(String something) {System.out.println(getAge() + "岁的" + getColor() + "色彩的狗两只前腿死死的抱住" + something + "猛吃");
    }

    public void lookHome(){System.out.println("狗在看家");
    }
}


// 饲养员类
public class Person {
    private String name;
    private int age;

    public Person() {}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public int getAge() {return age;}

    public void setAge(int age) {this.age = age;}

    // 豢养狗
   /* public void keepPet(Dog dog, String something) {System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "色彩的" + dog.getAge() + "岁的狗");
        dog.eat(something);
    }

    // 豢养猫
    public void keepPet(Cat cat, String something) {System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "色彩的" + cat.getAge() + "岁的猫");
        cat.eat(something);
    }*/


    // 想要一个办法,能接管所有的动物,包含猫,包含狗
    // 办法的形参:能够写这些类的父类 Animal
    public void keepPet(Animal a, String something) {if(a instanceof Dog d){System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "色彩的" + a.getAge() + "岁的狗");
            d.eat(something);
        }else if(a instanceof Cat c){System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "色彩的" + c.getAge() + "岁的猫");
            c.eat(something);
        }else{System.out.println("没有这种动物");
        }
    }
}

// 测试类
public class Test {public static void main(String[] args) {
        // 创建对象并调用办法
       /* Person p1 = new Person("老王",30);
        Dog d = new Dog(2,"黑");
        p1.keepPet(d,"骨头");


        Person p2 = new Person("老李",25);
        Cat c = new Cat(3,"灰");
        p2.keepPet(c,"鱼");*/


        // 创立饲养员的对象
        Person p = new Person("老王",30);
        Dog d = new Dog(2,"黑");
        Cat c = new Cat(3,"灰");
        p.keepPet(d,"骨头");
        p.keepPet(c,"鱼");

    }
}
退出移动版