多态(polymorphic)

本文详细介绍了Java中的多态概念,包括方法多态(通过重写和重载实现)和对象多态(编译类型与运行类型的区别)。还探讨了多态的转型规则,以及在实际应用中的示例,如数组中的多态调用和参数化多态。
摘要由CSDN通过智能技术生成

多态基本介绍

  方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

1、方法的多态(重写和重载体现多态)

// 例子
public class Poly {
    public static void main(String[] args) {
        // 方法重载体现多态
        A a = new A();
        // 传入不同的参数,会调用不同sum 方法,就体现多态
        System.out.println(a.sum(10, 20));
        System.out.println(a.sum(10, 20, 30));

        // 方法重写体现多态
        B b = new B();
        a.say();
        b.say();
    }
}

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

// 子类
class A extends B{
    public int sum(int n1, int n2){ // 和下面sum 构成重载
        return n1 + n2;
    }

    public int sum(int n1, int n2, int n3){
        return n1 + n2 + n3;
    }

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

2、对象的多态(核心、重点、难点)


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

// 父类的一个引用指向子类的一个对象
Animal animal = new Dog(); // animal 编译类型是 Animal,运行类型是 Dog

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

  3)运行类型时可以变化的,可以通过 getClass() 来查看运行类型;

animal = new Cat(); // animal 的运行类型变成了Cat,编译类型仍然是 Animal

  4)编译类型看定义时 " = " 的左边,运行类型时看 " = " 的右边;

// Animal
public class Animal {
    public void cry(){
        System.out.println("Animal cry() 动物在叫...");
    }
}
// Dog
public class Dog extends Animal{

    public void cry() {
        System.out.println("Dog cry() 小狗汪汪叫...");
    }
}
// Cat
public class Cat extends Animal{

    public void cry() {
        System.out.println("Cat cry() 小猫喵喵叫...");
    }
}
public class PolyObject {
    public static void main(String[] args) {
        // 体验对象多态特点

        // animal 编译类型就是 Animal,运行类型 Dog
        Animal animal = new Dog();
        // 运行时,执行到该行时,animal 运行类型是 Dog,所以 cry 就是 Dog 的 cry
        animal.cry();   // 小狗汪汪叫...

        // animal 编译类型就是 Animal,运行类型 Cat
        animal = new Cat();
        animal.cry();   // 小猫喵喵叫...
    }
}

多态的转型、注意事项和细节

多态的前提是: 两个对象(类)存在继承关系;

多态的向上转型:

  1)本质:父类的引用指向了子类的对象;

  2)语法:父类类型 引用名 = new 子类类型();

  3)特点:① 编译类型看左边,运行类型看右边;
        ② 可以调用父类中的所有成员(需遵守访问权限);
        ③ 不能调用子类中特有成员;
        ④ 最终运行效果看子类的具体实现!

// 父类
public class Animal {
    String name = "动物";
    int age = 10;
    public void sleep(){
        System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}
// Cat 类
public class Cat extends Animal{
    public void eat(){  // 方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){   // Cat特有方法
        System.out.println("猫抓老鼠");
    }
}
public class PolyDetail {
    public static void main(String[] args) {

        // 向上转型: 父类的引用指向了子类的对象
        // 语法: 父类类型 引用名 = new 子类类型();
        Animal animal = new Cat();
        Object object = new Cat(); // 可以运行, Object 也是 Cat 的父类
		
		// 向上转型调用方法的规则如下:
        // 1)可以调用父类中的所有成员 (需遵守访问权限)
        // 2)但是不能调用子类的特有成员
        // 3)因为在编译阶段, 能调用哪些成员, 是由编译类型决定的
        
        // animal.catchMouse(); 错误

        // 4)最终运行效果看子类 (运行类型) 的具体实现
        // 即调用方法时, 按照从子类 (运行类型) 开始查找方法, 然后调用
        animal.eat();   // 猫吃鱼...
        animal.run();   // 跑
        animal.show();  // hello,你好
        animal.sleep(); // 睡

    }
}

运行结果:
在这里插入图片描述

多态的向下转型:

  1)语法:子类类型 引用名 = (子类类型) 父类引用;

  2)规则:① 只能强转父类的引用,不能强转父类的对象;
        ② 要求父类的引用必须指向的是当前目标类型的对象;
        ③ 可以调用子类类型中所有的成员;

// Dog 类
public class Dog extends Animal{
}
public class PolyDetail {
    public static void main(String[] args) {

        Animal animal = new Cat();
        
		// 向下转型:
        // 1)子类类型 引用名 = (子类类型) 父类引用;
        // cat 的编译类型是 Cat, 运行类型是 Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();   // 猫抓老鼠

        // 2)要求父类的引用必须指向的是当前目标类型的对象
		// 报错,类转换异常,此时是将 Dog类 强转为了Animal类的Cat类
		Dog dog = (Dog) animal;
		 
		// 如果先 new 一个 Dog类 
		// Animal animal = new Dog();
		// 此时 Dog dog = (Dog) animal; 是正确的

    }
}

注意事项和细节

1、属性没有重写之说!属性的值看编译类型;

public class PolyDetail02 {
    public static void main(String[] args) {
        
        // 属性没有重写之说!属性的值看编译类型
        Base base = new Sub();  // 编译类型为 Base
        System.out.println(base.count);	// 10

        Sub sub = new Sub();    // 编译类型为 Sub
        System.out.println(sub.count);	// 20

    }
}

class Base{ // 父类
    int count = 10;	
}

class Sub extends Base{	// 子类
    int count = 20;
}

运行结果:
在这里插入图片描述
2、instanceof 比较操作符,用于判断对象的运行类型是否为 XX类型 或 XX类型的子类型;

public class PolyDetail03 {
    public static void main(String[] args) {
		
		// 判断对象的类型是否为 XX类型 或 XX类型的子类型
        BB bb = new BB();
        System.out.println(bb instanceof BB);   // ture
        System.out.println(bb instanceof AA);   // ture

        // aa 编译类型 AA,运行类型是 BB
        // 判断的是 AA 的运行类型 是否为 BB类型 或 BB类型的子类型
        AA aa = new BB();
        System.out.println(aa instanceof BB);   // ture
        System.out.println(aa instanceof AA);   // ture
		
		Object object = new Object();
        System.out.println(object instanceof AA);   // false

        String str = "hello";
        System.out.println(str instanceof Object);  // ture
    }
}

class AA{} // 父类
class BB extends AA{}  // 子类

运行结果:
在这里插入图片描述

多态的应用

实例

   1)创建1个Person对象、2个Student对象和2个Teacher对象;

   2)统一放在数组中,并调用每个对象say方法;

   3)子类特有的方法,比如Teacher有一个 teach,Student有一个 study。

在这里插入图片描述

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

    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 String say(){
        return name + " \t" + "age = " + age;
    }
}
// 学生 子类
public class Student extends Person{
    private double score;

    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    // 重写父类的 say() 方法
    @Override
    public String say() {
        return "学生 " + super.say() + "\t" + " score = " + score;
    }

    // 特有方法
    public void study(){
        System.out.println("学生" + getName() + "正在上课...");
    }
}
// 老师 子类
public class Teacher extends Person{
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    // 重写父类的 say() 方法
    @Override
    public String say() {
        return "老师 " + super.say() + "\t" + " salary = " + salary;
    }

    // 特有方法
    public void teach(){
        System.out.println("老师" + getName() + "正在授课...");
    }
}
// 运行
public class PolyArr {
    public static void main(String[] args) {

        // 创建1个Person对象、2个Student对象和2个Teacher对象
        // 统一放在数组中,并调用每个对象say方法
        Person[] persons = new Person[5];
        persons[0] = new Person("jack", 20);
        persons[1] = new Student("tom", 16, 95);
        persons[2] = new Student("lucy", 14, 87);
        persons[3] = new Teacher("smith", 27, 14000);
        persons[4] = new Teacher("Rosy", 45, 20000);

        // 循环遍历数组,调用 say()
        for (int i = 0; i < persons.length; i++) {
            // 提示: persons[i] 编译类型是 Person, 运行类型是根据实际情况由 JVM 来判断
            System.out.println(persons[i].say());   // 动态绑定机制
			
			// 使用类型判断 + 向下转型
            if (persons[i] instanceof Student){ // 判断persons[i] 的运行类型是不是 Student
            	// 可以缩写: ((Student)persons[i]).study();
                Student student = (Student)persons[i];  // 向下转型
                student.study();
            }else if (persons[i] instanceof Teacher){ // 判断persons[i] 的运行类型是不是 Teacher
                // 可以缩写: ((Teacher)persons[i]).teach();
                Teacher teacher = (Teacher)persons[i];   // 向下转型
                teacher.teach();
            }else if (persons[i] instanceof Person){            
            }else System.out.println("你的类型有误,请自己检查...");
        }
    }
}

运行结果:
在这里插入图片描述

多态参数

   1)定义员工类 Employee,包含姓名 name 和 月工资 [private],以及计算年工资 getAnnual;

   2)普通员工 worker 和经理 manager 继承了员工类,经理类多了奖金 bonus 属性和管理 manage 方法,普通员工多了 work 方法,普通员工和经理类要求分别重写 getAnnual 方法;

   3)测试类中添加一个方法 showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()];

   4)测试类中添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法。

// 父类
public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    // 得到年工资的方法
    public double getAnnual(){
        return 12 * salary;
    }
}
// worker 子类
public class Worker extends Employee{
    public Worker(String name, double salary) {
        super(name, salary);
    }

    public void work(){
        System.out.println("普通员工 " + getName() + " is working...");
    }

    @Override
    public double getAnnual() { // 普通员工没有其他收入,直接调用父类方法
        return super.getAnnual();
    }
}
// manager 子类
public class Manager extends Employee{
    private double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public void manage(){
        System.out.println("经理 " + getName() + " is managing...");
    }

    // 重写获取年薪的方法
    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}
// 测试类
public class Parameter {
    public static void main(String[] args) {

        Worker tom = new Worker("tom", 3200);
        Manager smith = new Manager("smith", 7000, 100000);

        Parameter parameter = new Parameter();

        Parameter.showEmpAnnual(tom);
        Parameter.showEmpAnnual(smith);

        Parameter.testWork(tom);
        Parameter.testWork(smith);

    }

    // showEmpAnnual(Employee e)
    // 实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()]
    public static void showEmpAnnual(Employee e){
        System.out.println(e.getAnnual());  // 动态绑定机制
    }

    //添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法
    public static void testWork(Employee e){
        if (e instanceof Worker){
            ((Worker) e).work();    // 向下转型
        }else if(e instanceof Manager){
            ((Manager) e).manage(); // 向下转型
        }else System.out.println("不做处理...");
    }
}

运行结果:
在这里插入图片描述

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值