1.多态
多态是面向对象三大特征之一,表示同类型的对象表现不同的形态
表现形式:
父类类型 对象名称 = 子类对象;
多态的前提:
-
有继承关系
-
有父类引用子类
Fu f = new Zi();
-
有方法重写
使用场景举例:
当需要写一个注册的方法,但是这个方法要能实现不同对象的注册
例如:老师、学生、管理员
就可以使用多态实现:使用父类person,来引用子类
前面我们说需要:
父类 对象名称 = 子类对象
父类引用子类
这里实际上是在方法传参的时候引用了preson p
,传入子类对象
2.多态调用成员变量的特点
- 成员变量:编译看左边,运行看左边
- 成员方法:编译看左边,运行看右边
//父类和子类
class person{
String name = "fu";
public void show(){}
}
class zi{
String name = "zi";
@Override
public void show(){}
}
//调用:
person p = new zi();
p.name;
p.show();
a. 成员变量
person p = new zi()
编译:编译看左边: 当javac
在编译的时候会看父类中有没有这个成员变量,如果有就编译成功,没有就报错
运行:运行看左边:当java
在运行代码的时候,实际获取的就是左边父类中成员变量的值
所以上述代码:p.name
的值是fu
- 实际也好理解:Javac在编译时会根据引用的类型来决定访问哪个变量。
- 子类中可以不写成员变量
b.成员方法
person p = new zi()
编译:编译看左边: javac
在编译的时候会看父类中有没有这个方法,如果有就编译成功,没有就报错
运行:运行看右边:实际上运行的是在子类中重写的方法
所以上述代码:p.show()
,调用的时子类中的方法
- 理解:从虚方法表的角度来看:子类的方法重写覆盖了父类虚方法表中的这个方法。
- 当子类中没有方法重写时,直接调用父类中的方法
c. 从内存的角度理解
- 加载测试类的字节码文件进入方法区
- 加载
main
方法进栈 - 加载子类和父类的字节码文件进方法区
实际上是从父类开始加载字节码文件的,例如:
person p = new zi()
先加载object.class
之后是:person.class
,然后是zi.class
- 创建对象:变量(对象变量/对象引用)在栈中,存储地址;对象的数据存储在堆内存中,把地址拿给变量
在对象中数据分为两块:一块是父类中的数据,另一块是子类中的数据
- 在访问同一个成员变量时:优先访问对象中父类的数据
- 在访问成员方法的时候,访问的时子类中的成员方法
图示:来自黑马程序员
3.多态的优势和弊端
a.优势
-
代码的可移植性高:例如:
person p = new student(); p.work();//调用的是子类中的方法
可以修改其中的子类,同时下面的方法调用也会改变
-
定义方法时,使用父类作为参数,可以接收所有的子类对象。
b.弊端
- 代码不能访问子类特有的方法
Fu f= new Zi();
我们说过子类在调用方法时:编译看父类,运行看子类。
当父类中没有这个方法时,子类不能调用
当然我们有对策:
可以使用强制类型转换来访问:
例如:
Zi z = (Zi) f;
f.show();
在实际编写代码时:完整:
if (a instanceof Zi1) {
Zi1 b = (Zi1) a;
b.show();
}
else if(a instanceof Zi2) {
Zi2 c = (Zi2) a;
c.show();
}
instanceof
关键字:左边是对象变量,右边是类名,判断变量类型是否和类一致,返回boolean类型- 在JDK14之后为代码做了优化
if (a instanceof Zi1 b) {
b.show();
}
else if(a instanceof Zi2 c) {
c.show();
}
//把判断和强制转换化作了一行
//如果是:进行转换并进入if语句,不是则返回false
补充:自动类型转换:类型小的自动转换为类型大的
parent p = new son()
父类:parent,子类:son
标签:Java,进阶,show,子类,多态,编译,new,父类,方法 From: https://blog.csdn.net/2302_80203877/article/details/143814161