三、多态
主观意识上的类别与客观存在的对象具有is a 关系时,即形成多态
将子类对象当成父类类别看待
概念:
父类引用指向子类对象,从而产生多种多态。
Animal a = new Dog();
把狗当作动物来看待
父类引用? 父类类型的变量
子类对象? new 对象
二者具有直接或间接继承关系时,父类引用可指向子类对象,及形成多态
父类引用仅可调用父类声明的属性和方法,不可调用子类独有的属性和方法
main(){
Dog dog = new Dog();//把狗当狗看
System.out.println(dog.breed + dog.age+dog.sex+dog.furColor);
dog.eat();
dog.sleep();
dog.run();
Animal a =new Dog();//把狗当作动物来看
System.out.println(a.sex+a.breed+a.age);
a.sex();
}
class Animal{
String breed;
int age;
String sex;
public void eat(){
System.out.println(“吃”);
}
public void sleep(){
System.out.println(“睡”);
}
}
class Dog extends Animal{
String furColor;
public void run(){
System.out.pirntln();
}
}
多态中的方法覆盖
思考:如果子类中覆盖了父类中的方法,以父类型引用调用此方法时,优先执行父类还是子类中的方法?
实际运行过程中,依旧遵循覆盖原则,如果子类覆盖了父类中的方法,则执行子类中覆盖后的方法,否则执行父类中的方法。
main(){
Employee emp = new Employee();
emp.name= “Tom”;
Bus bus = new Bus();
bus.type= “gongjiaoche”;
bus.speed=23;
bus.price =21323;
bus.seatNum = 123;
emp.goHome(bus);
}
class Employee{
String name;
public void goHome(Bus bus){//接收一个bus作为参数
System.out.println(name);
bus.run();
}
}
class Vehicle{
String type;//小汽车
int speed;
double price;
public void run(){
System.out.println(price+type+speed);
}
}
class Car extends Vehicle{
String brand;//品牌
}
class Bus extends Vehicle{
int seetNum;//座位数
}
class Bicycle extends Vehicle{
String color;//颜色
}
方法重载可以解决接收不同对象参数的问题,但其缺点也比较明显
首先,随着子类的增加,Employee类需要继续提供大量的方法重载,多次修改重新编译文件。其次,每一个goHome方法与具体某一类型形成了密不可分的关系,耦合太高
main(){
Employee emp = new Employee();
emp.name= “Tom”;
Bus bus = new Bus();
bus.type= “gongjiaoche”;
bus.speed=23;
bus.price =21323;
bus.seatNum = 123;
emp.goHome(bus);//执行子类中的覆盖方法
}
class Employee{
String name;
public void goHome(Vehicle veh){//Vehicle veh = new Bus();
//只要是子类都可以成为参数
System.out.println(name);
veh.run();//}}
class Vehicle{
String type;//小汽车
int speed;
double price;
public void run(){
System.out.println(price+type+speed);
}
}
class Car extends Vehicle{
String brand;//品牌
}
class Bus extends Vehicle{
int seetNum;//座位数
}
class Bicycle extends Vehicle{
String color;//颜色
}
多态应用
一,使用父类作为方法形参实现多态,使用方法参数的类型更为宽泛
二,使用父类作为方法返回值实现多态,使用方法可以返回不同子类对象
main(){
Employee emp = new Employee();
emp.name= “Tom”;
emp.goHome(bus);//执行子类中的覆盖方法
Vehicle myVeh = emp.buyVehicle(888888);
if(myVeh != null){
emp.goHome(myVeh);
}}
class Employee{
String name;
public void goHome(Vehicle veh){//Vehicle veh = new Bus();
System.out.println(name);
veh.run();//
}
public void buyVehicle(int money){//预算
Vehicle myVehicle = null;
if(money > 1000000){
//买公交车
Bus mybus = new Bus();
mybus.type= “gongjiaoche”;
mybus.speed=23;
mybus.price =21323;
mybus.seatNum = 123;
myVehicle = mybus;//赋值 mybus 赋值给m’yVehicle
}else if(money > 450000){
myVehicle = new Car();
}else if (monet > 23333){
myVehicle = new Bicycle();
}
return myVehicle ;//返回值
}
class Vehicle{
String type;//小汽车
int speed;
double price;
public void run(){
System.out.println(price+type+speed);
}
}
class Car extends Vehicle{
String brand;//品牌
}
class Bus extends Vehicle{
int seetNum;//座位数
}
class Bicycle extends Vehicle{
String color;//颜色
}
向上转型 (装箱)
父类引用中保存真实子类对象,称为向上转型(即多态核心观念)
Animl a = new Dog();
注意:仅可调用Animal中所声明的属性和方法
向下转型(拆箱)
Animal a =new Dog();
Dog dog = (Dog)a; // 将a强转到Dog中
将父类引用中的真实子类对象,强转回子类本身类型,称为向下转型
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
Exception in thread “main” java.lang.ClassCastException
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常
instanceof关键字
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性
语法:引用instanceof类型//返回boolean类型结果
Animal a= new Dog();
if(a instanceof Dog){ Dog dog = (Dog)a; dog.eat();}
else if(a instanceof Cat){Cat cat = (cat)a; cat.eat();}
当“a”引用中存储的对象类型确实为Dog时,再进行类型转换,进而调用Dog中的独有方法
总结:
多态的两种应用场景
使用父类作为方法形参,实现多态
调用方法时,可传递的实参类型包括:本类型对象+其所有的子类对象
使用父类作为方法返回值,实现多态
调用方法后,可得到的结果类型包括:本类型对象+其所有的子类对象
多态的作用
屏蔽子类间的差异
灵活、耦合度低
复习三大特征:
私有属性private 封装 get/set方法调用
默认修饰符 同包可用
继承 is a 关系 estends
不可继承 构造方法,private修饰的属性和方法。父子类不在同包时,默认修饰符defalut修饰的属性和方法
当父类提供的方法无法满足子类需求时,可在子类中定义和父类相同的方法进行覆盖(Override)
方法名称,参数列表,返回值类型必须与父类一致,访问修饰符可以相同或更宽放
优先调用子类的覆盖方法。
super.父类属性和方法
this.
super();表示调用父类的无参构造,首行
super(实参);表示调用父类的有参构造
this()与super()不可共存
多态
三个修饰符 static abstract抽象 final 最终
abstract
抽象? 似是而非,像却又不是;具备某种对象的特征,但不完整
Aniaml a = new Animal();
Animal仅是一种会吃会睡的对象,再无其他行为,不够具体,不够完整。
程序是用来模拟现实世界,解决现实问题的,现实世界中存在的都是“动物”具体的子类对象,并不具体的子类对象,并不存在“动物”对象,所以,Animal不应该被独立创建成对象
如何限制这种对象的创建?
抽象类
应用 abstract 修饰类, 此类不能new对象
(标准:站在设计的角度,逻辑的角度,现实的角度,现实中没有这个对象)
public static void main(String[] args){
Aniaml a = new Animal();}
Animal 是抽象的,无法实例化
Animal a = new Animal();
abstract class Animal{}
被abstract修饰的类,被称为抽象类
抽象类意为不完整的类,不够具体的类。
抽象类对象无法独立存在,即不能new对象
a
public class TestAbstract{
public static void main(String[] arga){
Animal a1 = new Dog();
Animal a2 = new Cat();
}
}
经验: 抽象父类,可作为子类的组成部分,
依附于子类对象存在
由父类共性+子类独有组成完整的子类对象
不该被实现的方法
abstract class Animal{
public abstract void eat();
public void sleep(){ System.out.println(“动物在睡”);}}
需求:Dog中的eat()应输出“狗在吃骨头”
Cat中的eat()应输出“猫在吃鱼”
父类提供的方法很难满足子类不同需求,
如不定义,则表示所有动物都不会吃、睡。
如定义,略显多余,多数会被子类覆盖
方法声明必要,方法实现多余
抽象方法
抽象类中不一定有抽象方法
有抽象方法一定在抽象类中
(标准:1.父类提供的方法,多数无法满足子类的需求
2.当父类引用指向不同子类对象时,调用相同方法,得出不同结果,这也是多态(强制))
定义抽象方法;
目的:1.要求子类不再使用父类的共性实现过程,而提供独有的实现过程,(定义抽象方法,子类必须覆盖,一旦覆盖则与其他子类可能不一样)
父类不提供方法的实现过程,子类必须覆盖,
即不完整的父类抽象方法,不完整的类
abstract class Animal{
public abstract void eat();
public void sleep(){ System.out.println(“动物在睡”);}}
被abstract修改的方法,称为抽象方法,只有方法声明,没有方法实现({}的部分)。意为不完整的方法,必须包含在抽象类中。
class Dog extends Animal{
public void eat(){System.out.println(“狗在吃骨头”)}}
class Cat extends Animal{
public void eat(){System.out.println(“猫在吃鱼”)}}
产生继承关系后,子类必须覆盖父类中所有的抽象方法,否则子类还是抽象类
总结
abstract修饰类:不能new对象,但可以声明引用
abstract修饰方法:只有方法声明,没有方法实现。(需包含抽象类中)
抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
子类继承抽象类后,必须覆盖父类的所有抽象方法,否则子类还是抽象类
public abstract class Pet{ //抽象类
public abstract void toHospital();}} // 抽象方法
抽象类VS普通类
抽象类不能被实例化
但可以创建一个引用变量,其类型是一个抽象类,指向非抽象的子类实例
普通类可以被实例化
抽象方法VS普通方法
有无方法体
抽象类与抽象方法的使用
抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类
如果子类没有实现父类的所有抽象方法,子类必须被定义为抽象类
没有抽象构造方法,也没有抽象静态方法
抽象类中可以有非抽象的构造方法,创建子类的实例可能调用