Java作为面向对象的语言,可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
定义:父类引用变量指向子类对象
多态的前提:必须有子父类关系 或者 类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
1定义与使用格式
父类类型 变量名 = new 子类类型();
变量名.方法名();
例:普通类多态:
public class Fu { int a=1; public void method(){ System.out.println("这是父类方法"); } }
public class Zi extends Fu{ int a=2; public void method(){ System.out.println("这是子类方法"); } public void zi(){ System.out.println("这是子类方法2"); } }
public class Test { public static void main(String[] args) { Fu f=new Zi(); System.out.println(f.a); f.method(); } }
抽象类多态:
public abstract class Animal { public abstract void sleep(); }
public class Dog extends Animal{ public void sleep(){ System.out.println("狗趴着睡觉"); }; }
public class Test { public static void main(String[] args) { Animal an=new Dog(); an.sleep(); } }
接口多态:
public interface JiDu { public abstract void jidu(); }
public class Pig implements JiDu{ public void jidu() { System.out.println("缉毒猪在缉毒"); } }
public class Test { public static void main(String[] args) { JiDu jd=new Pig(); jd.jidu(); } }
自己理解:
JiDu jd=new Pig();
正常建对象是 Pig p=new Pig();
这样jd和p都引用了Pig()的地址,也可以说pig()有了两种状态,这就是多态。
2 多态--成员的特点
2.1多态中成员变量的特点:
当子父类中出现同名的成员变量时,多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。(看父)
运行时期:调用引用型变量所属的类中的成员变量。(看父)
简单记:编译和运行都参考等号的左边。编译运行看左边。
2.2多态中成员方法的特点:
同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。
编译时期:参考引用变量所属的类,如果类中没有调用的方法,编译失败。(看父)
运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。(调用子)。子类独有的方法无法调用。
简单记:编译看左边,运行看右边。
3 instanceof关键字
可以通过instanceof关键字来判断某个对象是否属于某种数据类型。
例:
public class Animal{} //动物类 public class Cat extends Animal{} //猫类 public class Person extends Animal{} //人类 public class Pot{} //锅类
public class Test { public static void main(String[] args) { Animal an=new Person(); System.out.println(an instanceof Cat); System.out.println(an instanceof Person); System.out.println(an instanceof Object); } }
但是如果
System.out.println(an instanceof Pot); 就编译失败
所以只有同体系才能比较,不同的直接编译失败。
4多态转型
4.1向上转型
当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
格式:
父类类型 变量名 = new 子类类型();
个人理解:double b=100;(小转大)与这个同理
4.2向下转型
一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。
如果是直接创建父类对象,是无法向下转型的(先向上转了,才能向下转)
格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
例:
public abstract class Animal{ public abstract void eat(); }
public class Cat extends Animal{ public void eat() { System.out.println("猫吃鱼"); } public void cathMouse(){ System.out.println("猫抓老鼠"); } }
public class Dog extends Animal{ public void eat() { System.out.println("狗吃骨头"); } public void lookhome(){ System.out.println("狗看家"); } }
public class Test { public static void main(String[] args) { Animal d=new Dog(); d.eat(); Animal c=new Cat(); c.eat(); Dog d2=(Dog)d; Cat c2=(Cat)c; d2.lookhome(); c2.cathMouse(); } }
但是如果写成这样:
Cat c2=(Cat)d;
可以编译,但会出现异常:
所以,向下转时,必须用instanceof加个判断
public class Test { public static void main(String[] args) { Animal d=new Dog(); d.eat(); Animal c=new Cat(); c.eat(); method(d); method(c); } public static void method(Animal an){ if(an instanceof Dog){ Dog d2=(Dog)an; d2.lookhome(); }else if(an instanceof Cat){ Cat c2=(Cat)an; c2.cathMouse(); } } }
5多态的好处与弊端
好处:隐藏了子类类型,提高了代码的扩展性
弊端:只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。
6 什么时候使用
6.1什么时候使用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
6.2什么时候使用向下转型
当要使用子类特有功能时,就需要使用向下转型。
向下转型的好处:可以使用子类特有功能。
向下转型的弊端:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。
7多态举例
7.1传参
public interface USB { public abstract void openUsb(); public abstract void closeUsb(); }
public class Notebook{ public void run(){ System.out.println("笔记本运行"); } public void shutdown(){ System.out.println("笔记本关机"); } //usb方法,传一个usb对象 public void useUsb(USB u){ u.openUsb(); u.closeUsb(); } }
public class Mouse implements USB{ public void openUsb() { System.out.println("插上鼠标"); } public void closeUsb() { System.out.println("拔下鼠标"); } }
public class Keyboard implements USB{ public void openUsb() { System.out.println("插上键盘"); } public void closeUsb() { System.out.println("拔下键盘"); } }
public class Test { public static void main(String[] args) { Notebook n=new Notebook(); Mouse m=new Mouse(); Keyboard k=new Keyboard(); n.run(); n.shutdown(); n.useUsb(m); n.useUsb(k); } }
分析:这里最关键的就是:public void useUsb(USB u){}
这里参数是传一个USB类型的对象
假设当传值时,传入一个鼠标对象m(Mouse m=new Mouse();)
那么调用方法的同时,相当于实现了一个多态:
USB u=new Mouse();
这是多态最常见的应用。
之前集合ArrayList的方法Add(Object obj),参数是一个Object类型的对象,但是实际用时,是传入一个集合中存放的对象的类型的对象(就是存什么,就传什么),这也是一种多态。
7.2 返回值
如果一个方法的返回值类型是父类,那么可以返回一个子类对象。
例:
public class Father { public void test(){ System.out.println("父类方法"); } }
public class Son extends Father{ public void test(){ System.out.println("子类方法"); } }
public class Test { public static void main(String[] args) { Father f=methods(); f.test(); System.out.println(f instanceof Son); System.out.println(f instanceof Father); } public static Father methods(){ //返回值是父类 return new Son(); //返回子类对象 } }
8 总结:封装、继承、多态的作用
封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式
继承:子类会自动拥有父类所有可继承的属性和方法。(抽象类,接口都体现了继承)
多态:父类引用变量指向子类对象。配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。
扩展:java面试题,可以经常看看,例:
https://blog.csdn.net/linzhiqiang0316/article/details/80473906