面向对象的三大特性:封装、继承、多态。前两者都比较好理解,只有多态会比较难以理解一些。很早之前在初学java时使用了《码农翻身》中的有趣的例子,对多态进行过一些总结,写过一篇博客。但每学习一次就会有一次收获。现在根据再次的复习所学,加上自己的理解进行总结。
多态就是根据对象的不同而对同一消息所做的不同反应。
实际上,多态性的实现是在继承的基础上进行的。
多态性体现在两方面:方法的多态和对象的多态
1:方法的多态性
根据多态的概念,可以解释为:根据调用方法时传入的参数的不同,而调用不同的方法,继而做出不同的反应,就称为方法的多态。方法的多态可以分为两种:
1):方法的重载:(OverLoad)
重载是在同一个类中重复的进行定义相同的方法名称。但是方法的参数个数,类型,以及返回值等不作统一的要求。而多态性的体现在于:外界在调用方法时传入的参数不同最后调用的具体方法不同。
class Ball{
public void play(){
System.out.println("在玩Ball");
}
public void play(String player){
System.out.println(player + "在玩Ball");
}
}
2):方法的覆写:(OverRide)
覆写就涉及到了继承关系,在有继承关系的父子两个类中,子类根据自身的特殊的需要重新定义了父类中的方法。因此覆写发生在子类中
要求也和重载不同,覆写的方法他的方法名,返回类型,参数都要相同,访问权限的控制程度也不得超过父类的。
class Ball{
public void play(){
System.out.println("在玩Ball");
}
}
class BasketBall extends Ball{
public void play(){
System.out.println("在玩篮球");
}
}
2:对象的多态性
对象的多态性是在继承的基础上实现的。在子类对象实例化的时候也默认调用了父类的无参构造函数,因此在实例化对象的时候他也会实例化出来一个父类的对象。因此在多态性上就会涉及到父类子类对象的相互转换。
即多态性主要体现在父类和子类的统一性和差异化所造成的影响上。
父类变量指向子类实例
class Ball{
public void play(){
System.out.println("在玩Ball");
}
}
class BasketBall extends Ball{
public void play(){
System.out.print("在玩篮球");
}
}
class FootBall extends Ball{
public void play(){
System.out.print("在玩足球");
}
}
public class DemoTest{
public static void main(String args[]){
Ball ball1 = new BasketBall(); //向上转型
Ball ball2 = new FootBall();
playBall(ball1);
playBall(ball2);
}
public static void playBall(Ball ball){
ball.play();
}
}
执行结果是:在玩篮球 在玩足球
静态方法playBall中的参数类型是父类Ball。
因此将ball1和ball2这两个子类对象转换成父类的对象,并在统一调用方法playBall时就可以实现参数的统一性。而在方法playBall中调用play方法最后调用的也是子类中的覆写方法,而不会执行父类中的相关操作。
问题:为什么不根据不同类型的对象去定义不同的方法,进而分别调用play方法呢?
答:这个问题可以理解为是方法重载和向上类型转换的区别。目前写代码要求的不仅仅是能写出来有功能的,也要求代码的扩展性以及维护性阅读性也要好。因此假设一个极端情况:这个父类的子类有数十万个,那么需要重载的方法也会不计其数。如果子类也在不断的增加,那么调用处的定义的playBall方法也要没完没了的增加了。
因此需要明白:重载和向上类型转换实际上是不同的两个意义的内容,千万不要混为一谈。
父类对象转换成子类对象。(注意:是在向上类型转换的基础上)
class Ball{
public void play(){
System.out.println("在玩Ball");
}
}
class BasketBall extends Ball{
public void rule(){
System.out.println("篮球规则为:....... ");
}
public void play(){
System.out.println("在玩篮球");
}
}
class FootBall extends Ball{
public void play(){
System.out.println("在玩足球");
}
}
public class DemoTest{
public static void main(String args[]){
Ball ball = new BasketBall();//向上类型转换
playRule(ball);
}
public static void playRule(Ball ball){
BasketBall bb = (BasketBall)ball; //向下类型转换(强制转换)
bb.rule();
}
}
向上类型转换所带来的参数的统一处理,由此带来的便利。但是也随之引发了问题。由于继承相当于:父类是描述统一的功能属性,而子类是有特殊的一些功能属性。而使用了向上类型转换之后,子类实例转换成了父类的对象,如果使用这个父类对象去调用子类独有的一些方法,不会成立的。因此就必须要将这个披着羊皮的狼的羊皮给扒掉。就需要向下类型转换。
BasketBall bb = (BasketBall)ball;
常见的报错: ClassCastException
先看一段代码
public class DemoTest{
public static void main(String args[]){
Ball ball = new Ball();
playRule(ball);
}
public static void playRule(Ball ball){
BasketBall bb = (BasketBall)ball; //向下类型转换(强制转换)
bb.rule();
}
}
ClassCastException
表示类型不匹配;原因是将两个没有任何关系的类进行了向下类型转换(强制类型转换)。虽然知道这两个类存在父子关系,但是他们并没有在代码上建立联系!!!!
因此在转型之前一定要先建立父子对象的联系(先进行向上转型),
向上类型转换是向下类型转换的基础
为了不出现异常,需要进行一下判断。此时就需要instanceof
的使用
instanceof
public class DemoTest{
public static void main(String args[]){
Ball ball = new Ball();
playRule(ball);
}
public static void playRule(Ball ball){
if(ball instanceof BasketBall){
BasketBall bb = (BasketBall)ball;
bb.rule();
}
}
}
使用instanceof
判断一下传入的对象ball是不是类BasketBall的实例。
向上类型转换:在父子功能统一的地方做一个集中调用的处理。当前疫情状况下,我党集中力量办大事,进行宏观调控。
向下类型转换:是要求在我党的宏观调控的基础上各地发挥各地的特色,将子类增加的特殊功能调用出来。