Java多态

Java多态

一、多态概述

​ 多态作为面向对象程序设计的三大支柱其中之一(另外两个分别为封装和继承),在Java中同样占有很重要的位置。下面就将简要介绍多态的含义以及使用的注意事项。

​ 多态:基类型对象访问派生类重写的方法。由此可以看出要想实现多态,必须满足三个条件:

1、存在继承关系

2、派生类中要有重写的基类的方法(且只能调用重写的方法)

3、基类的引用对象指向派生类

提供一个多态的例子:

public class PolymorphisDemo{
    /** 主方法 **/
    public static void main(String[] args){
        displayObject(new Circle(1,"red",false));
        displayObject(new Rectangle(1,1,"black",true));
    }
    
    public static void diplayObject(GeometricObject object){
        System.out.println("Created on " + object.getDateCreated() + ". Color is " + object.getColor());
    }
}

输出为:

Created on Mon May 09 19 : 25 : 20 EDT 2022. Color is red

Created on Mon May 09  19 : 25 : 20 EDT 2022. Color is black

Circle类和Rectangle类均为子类,而GeometricObject类为父类,当使用displayObject方法时,父类型变量引用子类型变量便意味着多态

二、多态的动态绑定机制

1、动态绑定

Object o = new GeometricObject();
System.out.println(o.toString());

在上述代码段中,o应该调用Object类的toString方法还是GeometricObject类的toString方法呢?我们先搞清楚两个概念:声明类型实际类型

声明类型即为变量在声明时的类型,o的声明类型为Object类

实际类型为变量被引用时实际的类型,o的实际类型为GeometricObject类

而o调用的方法是由其的实际类型决定的,即o调用GeometricObject类的toString方法,此便是动态绑定。

动态绑定:父类引用指向的或者调用的方法是子类的方法

2、向上转型

public class Human {

    public void sleep() {
        System.out.println("Human sleep..");
    }

    public static void main(String[] args) {
        Male m = new Male();
        m.sleep();

        Human h = new Male();// 向上转型
        h.sleep();           // 动态绑定
      	// h.speak();  此方法不能编译,报错说Human类没有此方法
    }
}

class Male extends Human {
    @Override
    //重写sleep方法
    public void sleep() {
        System.out.println("Male sleep..");
    }
	//子类独有的speak方法(父类引用不能调用)
    public void speak() {
        System.out.println("I am Male");
    }
}

输出结果为

//子类本身使用sleep方法
Male sleep..
//动态绑定后父类引用使用子类重写的sleep方法
Male sleep..

由此可以看出向上转型父类引用不能调用子类自己的方法,只能调用子类重写的父类的方法。

3、向下转型

public class Human {
    public void sleep() {
        System.out.println("Human sleep..");
    }
    public static void main(String[] args) {
    //向上转型
        Male m1 = new Male();
        Human h1 = m1;      
        h1.sleep();
        //h1.speak();  // 此时需要向下转型,否则不能调用speak方法。
        
//        //向下转型
//        Male m2 = new Male();
//        Human h2 = m2;  
//        m2 = (Male) h2;   //即为显式转换      
//        m2.speak(); 
        
//        //向下转型:失败
//        Human h3 = new Human();
//        Male m3 = (Male)h3;
//        m3.speak();               //此时会出现运行时错误,所以可以用instanceOF判断 
//        
//        //向下转型:类型防护
//        Human h4 = new Human();
//        if (h4 instanceof Male){  // 因为h4不是Male的实例,所以不执行if内部代码
//             Male m4 = (Male)h4;
//             m4.speak();
//        }
    }
}

class Male extends Human {
    @Override
    //重写sleep方法
    public void sleep() {
        System.out.println("Male sleep..");
    }
	
    //独有的speak方法
    public void speak() {
        System.out.println("I am Male");
    }
}

三、多态的主要应用场景

1、循环调用基类对象,访问不同派生类对象

我们用一个例子来表示:

Grade[] tests = new Grade[3];

// 第一次考试采用五级计分制,考了75
tests[0] = new Grade();
tests[0].setScore(75);

// 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
// 通过的最低分数线是60分
tests[1] = new PassFailExam(20, 5, 60);

// 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
tests[2] = new FinalExam(50, 7);

// 显示每次考试的分数和等级
for(int i=0; i<tests.length; i++){
System.out.println("Score: " + tests[i].getScore()+ " " + 
"Grade: " + tests[i].getGrade());
}

2、实参作为派生类,形参作为基类

我们也用一个例子表示一下:

public static void main(String[] args) {

Grade[] tests = new Grade[3];

// 第一次考试采用五级计分制,考了75
tests[0] = new Grade();
tests[0].setScore(75);

// 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
// 通过的最低分数线是60分
tests[1] = new PassFailExam(20, 5, 60);

// 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
tests[2] = new FinalExam(50, 7);

// 显示每次考试的分数和等级
for(int i=0; i<tests.length; i++){
   showValue(tests[i]);
}
}
//形参为基类
public static void showValue(Grade exam){
System.out.println("Score: " + exam.getScore()+ " " + 
"Grade: " + exam.getGrade());
}

四、多态的对象转换

对象转换:一个对象的引用类型转换为另一个对象的引用

m(new Student());

等价于

Object o = new Student();//隐式转换
m(o);

而显式转换则在阐述向下转型时已经详细演示了

	   Male m2 = new Male();
       Human h2 = m2;  
       m2 = (Male) h2;   //即为显式转换      
       m2.speak(); 
        
	   Human h3 = new Human();
	   Male m3 = (Male)h3;
	   m3.speak();               //此时会出现运行时错误,所以在显式转换之前可以用instanceOF判断 

  	   Human h4 = new Human();
 	   if (h4 instanceof Male){  // 因为h4不是Male的实例,所以不执行if内部代码
       		Male m4 = (Male)h4;
       		m4.speak();
      	}

五、instanceof关键字

instanceof的作用:用来判断一个对象是否是另一个对象的实例

下面我们看一段代码

Object o = new GeometricObject();
if(o instanceof GeometricObject){
    System.out.print("yes");
}

在这里我们用instance判断o是不是son的实例,可以看出,o指向的是子类对象GeometricObject,所以返回为true,可以实现输出yes。

下面再看一段和上面相似的代码

Object o = new Object();
if(o instanceof GeometricObject){
    System.out.print("yes");
}

可以看出,这个o声明和实际类型都是Object,o指向的是父类对象Object,与GeometricObject没有关系,因此这里返回false,不会输出yes,如果不判断,就会抛出ClassCastException异常

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值