多态:
同类中的不同对象接收同一消息,响应是不一样的
举例:
发生地震时,有的动物在飞,有的动物在跑,有的动物在游
(动物:能动的物体)
java中多态的前提:
A:有继承关系
B:有方法重写
C:有父类引用指向子类对象
Father f = new Son();
A:继承
继承的格式:
public class 子类名 extends 父类名{}
注意:
Objet 是所有Java类的最终父类
类如果没有显示继承父类,则默认继承Objet
this : 代表本类对象的引用
super: 代表父类对象的属性和方法的一种访问方式
区别:
this是引用类型 是当前对象的内存地址 可以通过它访问本类对象的成员
super不是引用类型 不是父类对象的内存地址 只是一种可以访问父类对象的方式
用法:
访问成员变量
this.成员变量
super.成员变量
访问构造方法:
this.(...)
super.(...)
访问成员方法:
this.()
super.()
java继承中构造方法的访问特点:
A:子类构造方法执行前
一定会先执行父类的构造方法
默认执行父类的无参构造方法
B:为什么?
子类继承父类,会继承父类的非私有成员
子类要使用这些数据,则父类要先初始化
注意:
子类构造方法前,默认第一行 super();
super()构造方法只是用来初始化父类,但并没有创建父类对象
父类中如果没有无参构造方法,怎么办?
A:在父类中添加无参构造方法(推荐:每个类手动添加无参构造方法)
B:可以通过super去访问父类的带参构造方法
java继承中成员变量的使用特点:
A:成员变量名称不同时,各行其是
B:成员变量名称相同时:就近原则
在子类方法中访问变量:
在方法局部范围找,有就用
在子类的成员范围找,有就用
在父类的成员范围找,有就用
如果还找不到,就报错
Java继承中成员方法的调用特点:
A:子类中方法和父类方法中的声明不一样,互不相干
B:子类中方法和父类方法中的声明一样,调用谁?
通过子类对象调用方法:
先在子类中找,有就用
然后在父类中找,有就用
都没有就报错
总结:
父类只能访问本类成员,无法访问子类成员
子类访问父类成员,就近原则,逐层向上
B:方法重写
方法重写:子类在父类的基础上添加自己的特点
格式:方法声明与父类相同
注意:
A:父类私有方法不能被重写
B:子类重写父类方法时,访问权限不能更低,可以相同或更高
建议:子类和父类权限相同
public->protect->默认->private
方法重写与方法重载的区别:
方法重载:再同一类中,方法名相同,参数列表不同
方法重写:子类中,除方法体和访问权限可以不同,其它都要相同,访问权限不能更低,可以相同或更高
@Override 注释标记 表示方法重写
C:父类引用指向子类对象
Father f = new Son();
多态中成员的访问特点:
A:成员变量
编译看左边,执行看左边
B:成员方法
编译看左边,执行看右边
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,成员变量没有
多态转型:
A:向上转型:
从子到父
父类引用指向子类对象
B:向下转型:
从父到子
父类引用强转为子类引用,并赋值给子类引用
使子类引用指向的子类对象
此时,两个引用同时存在,并都指向同一个内存区域,同一个子类对象
/*
* 向上转型:
* 从子到父
* 父类引用指向子类对象
* Animal animal = new Cat();
* 向下转型:
* 从父到子
* 父类引用强转为子类引用
* 两个引用同时存在,并指向同一个内存区域,同一个对象
* Cat cat = (Cat)animal;
*
public class PolymorphismDemo {
public static void main(String[] args) {
//常规操作
Animal animal2 = new Animal();
animal2.eat();//吃啥
//多态转型
System.out.println("---------1向上转型---------");
//向上转型
//父类引用指向子类对象
//Animal类型的a引用指向Cat对象
//父类引用指向子类对象
Animal animal = new Cat();
animal.eat();//猫吃鱼 调用子类重写的方法
animal.move();//move 调用父类方法
//不能通过父类引用调用子类特有方法
//a.playGame();//he method playGame() is undefined for the type Animal
System.out.println("---------2向下转型---------");
//向下转型
//父类引用强转为子类引用 两个引用同时存在,并指向同一个内存区域,同一子类个对象
//向下转型:方便调用子类特有方法
//Animal类型的animal引用指向Cat对象
//Animal类型的animal引用强转为Cat引用
//并赋值给Cat类型的cat引用
//使Cat类型的cat引用指向Cat对象 (类型转换正确)
//两个引用同时存在,并指向同一个内存区域,同一个对象
Cat cat = (Cat)animal;//本质上:animal指向Cat对象 所以可以转型为Cat引用
cat.move();//move 子类引用调用父类方法
cat.eat();//猫吃鱼 子类引用调用本类重写的方法
cat.playGame();//猫捉老鼠 子类引用调用本类特有的方法
//向上转型
System.out.println("---------3向上转型---------");
animal = new Dog();
animal.eat();//狗啃骨头
//向下转型失败
//Animal类型的animal引用指向Dog对象
//Animal类型的animal引用强转为Cat引用
//赋值给Cat类型的cat2引用
//使Cat类型的cat2引用指向Dog对象(类型转换错误/ClassCastException)
//狗不能转换成猫
//ClassCastException:
//com.itheima_polymorphism_03.Dog cannot be cast to com.itheima_polymorphism_03.Cat
//Cat cat2 = (Cat)animal;//本质上:animal指向Dog对象 所以不可以转型为Cat引用
//改进
System.out.println("---------4向下转型---------");
if(animal instanceof Cat){
System.out.println("向下转型成功");
Cat cat2 = (Cat)animal;
cat2.move();//move 子类引用调用父类方法
cat2.eat();//猫吃鱼 子类引用调用本类重写的方法
cat2.playGame();//猫捉老鼠 子类引用调用本类特有的方法
}else{
System.out.println("向下转型失败");
}
// Dog dog = (Dog)a;
// dog.eat();//狗啃骨头
// dog.lookDoor();//狗看门
//改进
if(animal instanceof Dog){
System.out.println("向下转型成功");
Dog dog = (Dog)animal;
dog.eat();//狗啃骨头
dog.lookDoor();//狗看门
}else{
System.out.println("向下转型失败");
}
}
@Test
public void test(){
/*
注意:
能否转型,关键看内存中真实创建的对象是够与引用类型匹配
*/
//Animal animal = new Animal();
//Animal cannot be cast to Cat
//animal类型指向Animal对象,所以不能转换为Cat类型
//Cat cat = (Cat)animal;
//cat.eat();
Animal animal = new Cat();
//animal类型指向Cat对象,所以能转换为Cat类型
Cat cat2 = (Cat)animal;
cat2.eat();
/*
注意:
父类引用可以指向子类对象
但子类引用不可以指向父类对象
*/
//Type mismatch: cannot convert from Animal to Cat
//Cat cat = new Animal();
}
}
public class Animal {
public void eat(){
System.out.println("吃啥");
}
public void move(){
System.out.println("move");
}
}
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("猫捉老鼠");
}
}
public class Dog extends Animal{
@Override
public void eat(){
System.out.println("狗啃骨头");
}
public void lookDoor(){
System.out.println("狗看门");
}
}
多态的好处:
提高程序扩展性(向上转型)
具体体现:
定义方法时,使用父类引用作为形式参数
调用方法时,使用子类对象作为实际参数
多态的弊端:
不能使用子类特有功能
解决方法
向下转型:
把父类引用强转为子类引用
两个引用同时存在,并指向同一个内存区域,同一子类个对象
通过子类引用调用子类特有方法
public class AnimalOperator {
// public void useAnimal(Cat c){//Cat c = new Cat();
// c.eat();
// }
// public void useAnimal(Dog d){//Dog d = new Dog();
// d.eat();
// }
public void useAnimal(Animal a){//Animal a = new Cat();
a.eat();
}
}
public class AnimalOperatorTest {
public static void main(String[] args) {
AnimalOperator ao = new AnimalOperator();
Cat c = new Cat();
ao.useAnimal(c);
Dog d = new Dog();
ao.useAnimal(d);
System.out.println("-----------------");
Animal a = new Cat();
ao.useAnimal(a);
a = new Dog();
ao.useAnimal(a);
}
}
多态总结
函数本身就具备多态性,某一种事物有不同的具体的体现。
体现:父类引用或者接口的引用指向了自己的子类对象。//Animal a = new Cat();
多态的好处:提高了程序的扩展性。
多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
多态的前提:
1:必须要有关系,比如继承、或者实现。
2:通常会有覆盖操作。
多态的出现思想上也做着变化:以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。
/**
如果想用子类对象的特有方法,如何判断对象是哪个具体的子类类型呢?
可以可以通过一个关键字 instanceof ;//判断对象是否实现了指定的接口或继承了指定的类
格式:<对象 instanceof 类型> ,判断一个对象是否所属于指定的类型。
Student instanceof Person = true;//student继承了person类
多态在子父类中的成员上的体现的特点:
1,成员变量:在多态中,子父类成员变量同名。
在编译时期:参考的是引用型变量所属的类中是否有调用的成员。(编译时不产生对象,只检查语法错误)
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
简单一句话:无论编译和运行,成员变量参考的都是引用变量所属的类中的成员变量。
再说的更容易记忆一些:成员变量 --- 编译运行都看 = 左边。
2,成员函数。
编译时期:参考引用型变量所属的类中是否有调用的方法。
运行事情:参考的是对象所属的类中是否有调用的方法。
为什么是这样的呢?因为在子父类中,对于一模一样的成员函数,有一个特性:覆盖。
简单一句:成员函数,编译看引用型变量所属的类,运行看对象所属的类。
更简单:成员函数 --- 编译看 = 左边,运行看 = 右边。
3,静态函数。
编译时期:参考的是引用型变量所属的类中是否有调用的成员。
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
为什么是这样的呢?因为静态方法,其实不所属于对象,而是所属于该方法所在的类。
调用静态的方法引用是哪个类的引用调用的就是哪个类中的静态方法。
简单说:静态函数 --- 编译运行都看 = 左边。
*/
class Teacher{
void teach(){
System.out.println("讲课");
}
void fish(){
System.out.println("钓鱼");
}
}
class JavaTeacher extends Teacher{
void teach(){
System.out.println("JAVA课");
}
void movie(){
System.out.println("看电影");
}
}
class PolymorphismTest{
public static void main(String[] args) {
Teacher teacher = new JavaTeacher(); //JavaTeacher对象被提升为了Teacher类型。
teacher.teach();//JAVA课
teacher.fish();//钓鱼
//编译错误The method movie() is undefined for the type Teacher
//teacher.movie();
JavaTeacher javaTeacher = (JavaTeacher)teacher;//将Teacher类型强制转换成JavaTeacher类型。
//在多态中,自始自终都是子类对象在做着类型的变化
//即自始自终都是通过引用类型调用真实对象(new JavaTeacher())
javaTeacher.movie();//看电影
javaTeacher.teach();//JAVA课
javaTeacher.fish();//钓鱼
//对象 instanceof 类型 判断一个对象是否所属于指定的类型
if(javaTeacher instanceof Teacher){
System.out.println("javaTeacher 继承了Teacher类");
}
}
}