多态:可以理解为事物存在的多种体现形态。再设计一个方法时,通常希望该方法具备一定的通用性。例如要实现一个动物叫的方法,由于每种动物的叫声不同,因此可以在方法中定义一个接收动物类型的参数。当传入猫类对象时,运行猫叫的方法,传入狗类对象时就运行狗叫的方法。在同一个方法中,这种由于参数类型不同而引起的执行效果不同的现象就是多态。
多态实现的三个必要条件:
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
Java中多态的实现方式:
1. 接口实现 :一个类实现多个接口。(常用)
2. 继承父类进行方法重写 :多个子类重写父类方法,并且各子类的方法体不同
3. 同一个类中进行方法重载 :一个方法由于参数类型和个数不同,重载出多个函数实现多个功能。
4. 父类引用子类对象 :将子类向上转型(转成父类),利用父类引用,可以同时操作多种不同对象。(常用)
父类引用的格式:
在多态中成员函数的特点:
Fu f = new zi();
f.方法1;
f.方法2;
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过。
如果没有,则编译失败。
在运行期间:参阅对象所属的类中是否有调用的方法,有就运行,没有就运行错误。
简单来说:成员函数在多态调用时,编译看左边运行看右边。
在多态中成员变量的特点:
无论编译还是运行,都参考左边(引用型变量所属的类)
在多态中静态成员函数的特点:
无论编译还是运行,都参考左边(引用型变量所属的类)
也就是说父类引用不能访问子类成员变量,不然会报错。
注意:多态的弊端:只能使用父类的引用去访问父类中的成员,如需使用子类成员强制将父类引用转成子类类型。
例一:接口实现
//定义一个人抽烟的接口
interface Smoke{
public String smoking();
}
//定义一个人喝酒的接口
interface Drink{
public String drinking();
}
//定义Person类
class Person{
Person(String a){
name = a;
}
String name;
void speak(){
System.out.println("my name is "+ name);
}
}
//定义一个子类,既抽烟又喝酒的人
class Person1 extends Person implements Smoke,Drink{
super("张三");//当实例化多个对象时 super的参数可以手动输入
public String smoking(){
return "抽烟";
}
public String drinking(){
return "喝酒";
}
void speak(){
System.out.println("my name is "+ this.name+". I like "+ smoking()+" and " + drinking());
}
}
class Runing{
public static void main(String[] args){
Person1 p = new Person1();
p.speak();
}
}
/*
运行结果:
my name is 张三. I like 抽烟 and 喝酒
*/
例二.继承父类重写方法
//定义抽象类
abstract class Animal{
//定义抽象方法
abstract void shut();
}
//继承于Animal的猫类
class Cat extends Animal{
//重写父类的shut()方法
void shut(){
System.out.println("喵喵......");
}
}
//继承于Animal的狗类
class Dog extends Animal {
//重写shut()方法
void shut(){
System.out.println("汪汪......");
}
}
class RunAnimal{
public static void main(String[] args){
Cat c = new Cat();
Dog d = new Dog();
c.shut();
d.shut();
}
}
/*
运行结果:
喵喵......
汪汪......
*/
例三:重载
class Demo {
void speak(){
System.out.println("1");
}
int speak(int a){
int b = a;
return b;
}
int speak(int a,int b){
int c = a;
int d = b;
}
}
class Runing{
public static void main(String[] args){
Demo d = new Demo();
d.speak(); //调用无参speak函数
System.out.println();
System.out.println(d.speak(3)); //调用有一个参数的speak函数
System.out.println(d.speak(4,5)); //调用有两个参数的speak函数
}
}
/*
运行结果:
1
3
5
*/
例四:父类引用
/*需求:
主板上有两个PCI接口,PCI接口可以插网卡或者声卡等
模拟主板运行,程序要有扩展性
*/
interface PCI{
public void open();
public void close();
}
interface PCI1 extends PCI{
}
interface PCI2 extends PCI{
}
class Mainboard {
public void open(){
System.out.println("主板运行");
}
public void close (){
System.out.println("主板关闭");
}
//定义方法用来主板使用网卡声卡等
public void usePCI(PCI c){
c.open();
}
public void closePCI(PCI c){
c.close();
}
}
//声卡使用PCI1接口
class Voiceboard implements PCI1{
public void open(){
System.out.println("Voiceboard run");
}
public void close (){
System.out.println("Voiceboard close");
}
}
//网卡使用PCI2接口
class Netboard implements PCI2{
public void open(){
System.out.println("netceboard run");
}
public void close (){
System.out.println("netceboard close");
}
}
class Runing{
public static void main(String[] args){
Mainboard M1 = new Mainboard();
M1.open();
Voiceboard v = new Voiceboard();
M1.usePCI(v);
//等价于 M1.usePCI(new Voiceboard());
Netboard n = new Netboard();
M1.usePCI(n);
//M1.usePCI(new Netboard());
System.out.println("使用一段时间后...\n主板即将关闭......");
M1.closePCI(v);
M1.closePCI(n);
M1.close();
}
}
/*
运行结果:
主板运行
Voiceboard run
netceboard run
使用一段时间后...
主板即将关闭......
Voiceboard close
netceboard close
主板关闭
*/
父类引用,运行期间执行的是对象的父类中被复写过的子类方法。