本次只是围绕着多态性的概念来进行讲解,但是所讲解的代码与实际的开发几乎没有关系,而且多态一定是在继承性的基础上才可以操作的,
而本次将使用类继承的关系来描述多态的性质,实际的开发中不会出现普通类的继承关系(一个已经完善的类不应该再被继承),开发中都要求继承抽象类和接口
多态性要想实现有两个前提:继承,覆写
范例:引出代码
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } } public class polymorphism{ public static void main(String args[]){ B b = new B(); b.print(); } }
覆写调用的前提:看new 的是那个类的对象,而后看方法是否被子类所覆写
在java中多态性主要由两个方面组成:
方法的多态性:
方法重载:方法调用时根据不同的参数个数及类型可以实现不同的功能
方法覆写:不同的子类针对于同样的一个方法可以有不同的实现
对象的多态性:父类与子类对象间的转换操作;发生在继承关系之中
【自动】对象的向上转型:子类对象变为父类对象 父类 父类对象 = 子类实例,自动完成的
【强制】对象的向下转型;父类对象变为子类对象 子类 子类对象 = (子类)父类实例,强制转换
除了转型之外,还有一些操作是不转型的,例如:String
对象多态性基础实现
观察一道程序
范例:
class Member{ public String getInfo(){ return "Member:我是一个会员"; } } class VIPMember extends Member{ // 产生继承 public String getInfo(){ return "VIPMember:我是一个贵宾会员"; } } public class polymorphism{ public static void main(String args[]){ VIPMember mam = new VIPMember(); System.out.println(mem.getInfo()); } }
方法覆写观察:
观察现在 new 的是那个子类
观察这个子类调用的方法是否被覆写,如果被覆写了则调用的就是被覆写过的方法
范例:对象的向上转型
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } } public class polymorphism{ public static void main(String args[]){ A a = new B(); // 向上转型 a.print(); } }
可以发现向上转型是自动完成的,除了向上转型之外,也可以实现对象的向下转型操作
向上转型最大的特点在于:所有的子类对象按照统一的父类类型进行接收,但是由于实例化子类的不同,有可能同一个父类的方法会有不同的调用实现
为了更好的理解对象的向上转型问题就做一个简单的分析:要求设计一个方法,这个方法可以接收用户的参数信息
如果这个时候采用原始的技术:方法重载
范例:
class Member{ public String getInfo(){ return "Member:我是一个会员"; } } class VIPMember extends Member{ // 产生继承 public String getInfo(){ return "VIPMember:我是一个贵宾会员"; } } class CIPMember extends Member{ // 产生继承 public String getInfo(){ return "CIPMember:我是一个商务会员"; } } public class polymorphism{ public static void main(String args[]){ income(new Member()); income(new VIPMember()); income(new CIPMember()); } public static void income(Member mem){ System.out.println(mem.getInfo()); } public static void income(VIPMember mem){ System.out.println(mem.getInfo()); } public static void income(CIPMember mem){ System.out.println(mem.getInfo()); } }
缺点:
如果突然有一天你的用户类型分为了十万种,那么就需要有十万个 Member 子类,这个方法会重载十万次
所有的方法体执行的功能都一样,那么这样是一个明显重复
当子类众多又需要考虑参数统一的时候,按恶魔最好用的做法就是进行对象的向上转型(自动,调用被覆写过的方法)
class Member{ public String getInfo(){ return "Member:我是一个会员"; } } class VIPMember extends Member{ // 产生继承 public String getInfo(){ return "VIPMember:我是一个贵宾会员"; } } class CIPMember extends Member{ // 产生继承 public String getInfo(){ return "CIPMember:我是一个商务会员"; } } public class polymorphism{ public static void main(String args[]){ income(new Member()); income(new VIPMember()); income(new CIPMember()); } public static void income(Member mem){ System.out.println(mem.getInfo()); } }
发现利用对象的向上转型可以有效的实现参数的统一处理。这一点在程序中至关重要
对象向下转型
范例:对象的向下转型
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } } public class polymorphism{ public static void main(String args[]){ A a = new B(); // 向上转型 B b = (B)a; // 向下转型 b.print(); } }
具体的转型的概念没有什么难理解,那么程序的执行结果也很好理解,但是这样做有什么意义呢?
分析:向上转型的意义
现在要求定义一个 fun()方法,这个方法要求可以接收A以及A类所有子类的实例化对象
于是根据这样的描述可以有两种实现方案
方案一:使用方法重载的概念来完成
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } } class C extends A{ public void print(){ System.out.println("不好!"); } } public class polymorphism{ public static void main(String args[]){ fun(new A()); fun(new B()); fun(new C()); } public static void fun(A a){ a.print(); } public static void fun(B b){ b.print(); } public static void fun(C c){ c.print(); } } /* 结果: Hello 你好! 不好 */
如果说这个时候A类有3000W个子类,并且这个子类还在以倍数的方式增长
那么就表示:在设计之初此方法就需要重载3000W次,并且随着子类的追加,此方法继续重载,继续不断的修改类,于是你就疯了......
方案二:发现所有的子类调用的方法实际上都只是println()一个,那么现在可以利用对象自动向上转型的概念就直接使用A类接收
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } } class C extends A{ public void print(){ System.out.println("不好!"); } } public class polymorphism{ public static void main(String args[]){ fun(new A()); fun(new B()); fun(new C()); } public static void fun(A a){ a.print(); } } /* 结果: Hello 你好! 不好 */
所以对象的向上转型给开发者最大的帮助在于其数据操作的统一性上
分析:向下转型的意义
范例:观察问题
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a = new B(); // 向上转型 a.print(); a.funB();// 不能够调用 } } //结果:出错
一旦发生了向上转型之后,父类对象是不可能调用子类中新建的方法的,只能够调用父类自己本身所定义的方法名称,也就是说向上转型之后牺牲的是子类的个性化特征
但是如果说现在要想调用子类定义的特殊方法,那么就必须采用向下转型
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a = new B(); // 向上转型 (把A改为B??) a.print(); B b = (B)a; b.funB(); } } /* 结果: 你好! ****************** */
解释:为什么现在你的代码里面需要先向上转型,在进行向下转型,啰嗦
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } class C extends A{ public void print(){ System.out.println("不好!"); } public void funC(){ // 子类自己扩充的新方法 System.out.println("###################"); } } public class polymorphism{ public static void main(String args[]){ fun(new B()); fun(new C()); } public static void fun(A a){ a.print() // 由于某种特殊需求必须调用B类中的funB()方法,所以要进行向下转型 B b = (B)a; b.funB(); } } /* 结果: 你好! 不好 */
现在如果使用了向下转型,那么在之前好不容易建立起来的参数统一的局面就被打破了,所以这样的操作就属于不合理的操作形式
但是转型的意义却可以更加明确了:为了调用子类自己的特殊支持
但是在进行对象向下转型前也需要有一个注意点:
范例:错误的向下转型
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a == new A(); // 父类对象 B b = (B)a;// 强制转换 b.print() } } //结果:出错
此时出现了“java.langClassException”异常信息,,表示的是类转换异常,本质指的是两个没有关系的类对象发生了强制转换所带来的问题
所以要想进行向下转型操作之前一定要首先保证发生了向上转型,这样才可以建立父子对象的关系
但是可以发现这样的转型本身是会存在有安全隐患的,所以正在java中提供有一个关键字:instanceof,利用此关键字可以判断某一个对象是否是指定类的实例
对象 instanceof 类 返回boolean类型
范例:观察instanceof关键字的使用
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a == new A(); // 父类对象 System.out.println(a instanceof A); System.out.println(a instanceof B); //B b = (B)a;// 强制转换 //b.print() } } /* 结果: true false */
范例:如果发生了向上转型之后的判断
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a == new B(); // 父类对象 System.out.println(a instanceof A); System.out.println(a instanceof B); //B b = (B)a;// 强制转换 //b.print() } } /* 结果: true true */
范例:利用 instanceof 保证转型的正确性
class A { public void print(){ System.out.println("hello"); } } class B extends A{ public void print(){ System.out.println("你好!"); } public void funB(){ // 子类自己扩充的新方法 System.out.println("********************"); } } public class polymorphism{ public static void main(String args[]){ A a == new B(); // 父类对象 System.out.println(a instanceof A); System.out.println(a instanceof B); if(a instanceof B){ B b = (B)a;// 强制转换 b.print() } } } /* 结果: true true 你好 */
总结
向上转型(90%):为了实现参数类型的统一,但是向上转型一定要与方法覆写产生关联
向上转型(1%):为了调用子类特殊的方法实现,但是向下转型前必须要首先发生向上转型,会存在操作的安全性隐患,可以使用 instanceof 进行判断,但是不推荐这样使用
不转型(9%):为了方便操作直接使用系统类或者是一些功能类,例如String 简单java类