在面向对象设计中多态性描述的是同一结构在执行时会根据不同的形式展现出不同的效果,在java中 多态性可以分为两种不同的展现形式。
展现形式1:方法的多态性
方法的重载:同一个方法可以根据传入的参数的类型或个数的不同实现不同功能
方法的覆写:同一个方法可能根据实现子类的不同有不同的实现。
方法的重载多态性的意义在于一个方法名称有不同的实现;方法覆写多态性的实现在于,父类的一个方法,不同的子类可以有不同的实现。
展现形式2:对象的多态性
对象向上转型: 父类 父类实例=子类实例,自动完成转换(人是动物,看new后面的类)
对象向下转型:子类 子类实例=(子类)父类实例,强制完成转换(超人是人)
8.5.1对象向上转型
在子类对象实例化之前一定会自动实例化父类,所以此时将子类对象的实例通过父类进行接收即可实现对象的自动向上转型。而此时的本质还是子类实例,一旦子类中覆写了父类方法,并且调用该方法时,所调用的一定是被子类覆写过得方法。
范例:对象向上转型
public class Eight176father {
public void print() {
System.out.println("连接池");
}
}
public class Eight176father01 extends Eight176father{
public void print() {
System.out.println("mysql连接池"); //子类方法覆写
}
}
public class Eight176father02 extends Eight176father{
public void print() {
System.out.println("orcal连接池");
}
}
public class Eight176 {
public static void main(String[] args) {
Eight176father m1=new Eight176father01(); //向上转型
m1.print(); //调用被覆写的方法
Eight176father m2=new Eight176father02();//向上转型
m2.print();
}
}
执行结果
mysql连接池
orcal连接池
提示:不要看类名称,而是你要看实例化对象的类
整个操作不需要关心对象的声明l类型,关键就在于实例化新对象时所调用的是哪个子类的构造。
范例:统一参数方法
public class Eight176father {
public void print() {
System.out.println("连接池");
}
}
public class Eight176father01 extends Eight176father{
public void print() {
System.out.println("mysql连接池"); //子类方法覆写
}
}
public class Eight176father02 extends Eight176father{
public void print() {
System.out.println("orcal连接池");
}
}
public class Eight177 {
public static void send(Eight176father m) {
m.print();
}
}
public class Eight176 {
public static void main(String[] args) {
// fun(new Eight176father01());
// fun(new Eight176father02());
Eight177.send(new Eight176father01());
Eight177.send(new Eight176father02());
}
// public static void fun(Eight176father m1) {
// m1.print();
// }
}
执行结果
mysql连接池
orcal连接池
本程序定义的Eight177.send()方法,接收的参数类型为Eight176father,这样就意味着所有的Eight176father及其子类对象都可以接收,相当于统一了参数类型。
提问:可以使用重载解决问题吗
回答:可以,但是如果有1万个子类,那要写一万个重载方法吗,太麻烦了
8.5.2对象向下转型
子类继承父类后可以对已有的父类功能进行扩充。除了采用方法覆写这一机制外,子类也定义属于自己新的方法。而对于子类扩充的方法只有具体的子类实例才可以调用。在这样的情况下,如果子类已经发生了向上转型后就需要通过强制性向下转型来实现子类扩充方法调用。
范例:子类对象向下转型
public class Person1 {
public void sun() {
System.out.println("用力奔跑");
}
}
public class Superman extends Person1 {
public void fly() {
System.out.println("向上飞"); //子类扩充方法
}
public void fire() {
System.out.println("喷火");//子类扩充方法
}
}
public class Flow {
public static void main(String[] args) {
System.out.println("正常状态下超人是普通人");
Person1 per=new Superman(); //超人是正常人的时候,调用人的跑步方法
per.sun();
System.out.println("怪兽来了");//per是普通人,怪兽来了需要调用子类方法
/**
* 这样也可以用子类方法
*/
/*Superman sqm=new Superman();
sqm.fire();
sqm.fly();*/
/**
* 父类调用子类方法,必须强制转换
*/
Superman sqm=(Superman)per;
sqm.fly();
sqm.fire();
}
}
本程序中Superman子类利用对象向上转型实例化了Person1类对象,此时Person1类只能调用本类或父类定义的方法,如果此时需要调用子类中扩充的方法时,就必须要强制性的将其转换为指定的子类类型。
注意:必须先发生向上转型,之后才可以进行向下转型
在对象向下转型中,父类实例是不可能强制转换为任意子实例,必须先通过子类实例化,利用向上转型让父类对象与具体子类实例之间发生联系之后才可以向下转型,否则将出现异常
范例:错误的向下转型
public class Flow02 {
public static void main(String[] args) {
Person1 per=new Person1();
Superman sqm=(Superman )per;
}
}
执行结果
Exception in thread "main" java.lang.ClassCastException: com.lxh.eightchapter.Person1 cannot be cast to com.lxh.eightchapter.Superman
at com.lxh.eightchapter.Flow02.main(Flow02.java:6)
本程序父类与子类并没有发生联系,所以无法强制转换,既向下转型永远都会存在异常安全隐患
8.5.3instanceof关键字使用
对象的向下转换存在安全隐患,为了保证转换的安全性,可以在转换前通过instanceof关键字进行对象所属类型的判断,该关键字的使用语法如下!
对象 instanceof 类
该判断将返回一个boolean类型数据,如果是ture表示实例是指定类对象
范例:观察instanceof关键字使用
public class Useinstanceof {
public static void main(String[] args) {
System.out.println("-------------------不转型时instanceof判断---------------------");
Person1 per=new Person1(); //父类对象实例化
System.out.println(per instanceof Person1);//实例类型判断:ture
System.out.println(per instanceof Superman);//实例类型判断:false
System.out.println("-------------------向上转型时instanceof判断-------------------");
Person1 per1=new Superman(); //对象向上转型
System.out.println(per1 instanceof Person1);//实例类型判断:ture
System.out.println(per1 instanceof Superman);//实例类型判断:ture
}
}
执行结果
-------------------不转型时instanceof判断---------------------
true
false
-------------------向上转型时instanceof判断-------------------
true
true
通过本程序的执行结果可以发现,如果一个父类对象没有通过子类实例化,则使用instanceof的实例判断结果返回就是false,所以在实际开发中,就可以采用先判断后转型的方式来回避异常。
范例:安全的转型操作
public final class Flow2 {
public static void main(String[] args) {
System.out.println("---------正常情况下,超人是普通人------------------");
Person1 per=new Superman();//超人是人,向上转型
per.sun();//调用人的跑步方法
System.out.println("--------怪兽来了------------");
if(per instanceof Superman) { //判断实例类型
Superman sqm=(Superman)per; //强制向下转型
sqm.fire();
sqm.fly();
}else { //不是超人
System.out.println("找死");
}
}
}
执行结果
---------正常情况下,超人是普通人------------------
用力奔跑
--------怪兽来了------------
喷火
向上飞
本程序在进行对象向下转换前,为了防止可能出现的异常,所有通过instanceof进行判断,如果确定为Superman子类实例,则进行向下转型后调用子类扩充方法。
提示:null的实例判断会返回false
在使用instanceof进行实例判断是,如果判断的对象内容为null,则返回的内容为false.
范例:null判断
public class Flow3 {
public static void main(String[] args) {
Person1 per=null;
Superman man=null;
System.out.println(per instanceof Person1);
System.out.println(man instanceof Superman);
}
}
执行结果
false
false
由于null没有对应的堆内存空间,所以无法确定出具体类型,这样instanceof的判断结果就是false;