实现前提:1.要有继承关系 2.要有方法覆盖 3.父类引用指向了子类的实例
1.继承关系好理解,
有 extends ,对类进行继承(假设 class A extends B{}即A是子类,B是父类,子类拥有父类的所有成员)
2.方法覆盖稍微不好理解,
首先它的定义是:在子类中替换 父类或祖先类 中的某个方法。
实现方法:通过子类重新定义 与 父类或祖先类中具有 同样签名 和 返回类型 的方法。
覆盖的时候要注意访问权限:覆盖后不能比原先的方法的访问权限小(可相等,可大于)
权限大小: priavte < default < protected < public
注意点:
1.父类中被final修饰的不能被子类覆盖,其他修饰符可以被子类覆盖
2.只有在子类类体中可以访问到的父类或祖先类才能被覆盖
3.静态方法不能被覆盖。
4.构造方法不属于成员,所以也不能被覆盖,也不能被隐藏。
3.父类引用指向子类实例(在main方法中来执行)
语法:父类类型 a = new 子类类型();
理解:1.子类类型可以被看成是父类类型
2.子类继承父类,子类就拥有了父类的所有成员
运行时多态的含义:当同一个引用变量(父类引用)指向不同的子类实例,然后访问 引用变量 . 成员方法 , 方法会有不同的 表现
成员访问的特点:
成员变量:编译看左边,运行看左边
成员方法:编译看左边,运行看右边。
以Person s=new Student();为例。左边用以声明类型,右边用以创建对象。
而编译器编译时会查看左边的声明中是否有编译错误(在多态中尤为重要,看是否左边的类型中是否缺少右边类型的方法,否则报错。右边的类型通常为左边类型的子类)。
即使没有报错,一旦右边实际真正运行起来也有可能会出现错误,这时就要查看右边类中具体实现的代码。所以编译时错误看左边,运行时错误看右边。
一个例子:
- //毕业后,找到一份工作,前期买了一辆大众车,实现代码如下:
- public class test{
- public static void main(String[] args){
- DZ dz =new DZ("大众");
- people p =new people();
- p.drive(dz);
- }
- }
- class people{
- public String name;
- public DZ dz;
- public void drive(DZ dz){
- this.dz=dz;
- dz.run();
- }
- }
- class DZ{
- public String name ;
- public DZ(String name){
- this.name=name;
- }
- public void run(){
- System.out.print("上海大众在已120迈的速度在跑");
- }
- }
- //后期如果我有钱以后又买了一辆奔驰,我要换奔驰 ,不用多态的情况下代码如下:
- public class test{
- public static void main(String[] args){
- Benz benz=new Benz("奔驰车");
- people p =new people();
- p.drive(benz);
- }
- }
- class people{
- public String name;
- public DZ dz;
- public Benz benz;
- public void drive(DZ dz){
- this.dz=dz;
- dz.run();
- }
- public void drive(Benz benz){
- this.benz=benz;
- benz.run();
- }
- }
- class DZ{
- public String name ;
- public DZ(String name){
- this.name=name;
- }
- public void run(){
- System.out.print(name+"已在路上飞快的奔跑");
- }
- }
- class Benz{
- public String name;
- public Benz(String name){
- this.name=name;
- }
- public void run(){
- System.out.print(name+"已在路上飞快的奔跑");
- }
- }
- //这样是不是程序粘连性太强,不利于扩展,而且Java特性是对扩展开放,对修改关闭 我每换一辆车就需要改代码。
- //那样代码修改太多,也不安全,用了多态以后,效果就很不一样了,妈妈在也不用担心我换车了,so easy!!
- public class test{
- public static void main(String[] args){
- Benz benz=new Benz("奔驰车");
- DZ dz=new DZ("大众车");
- BMW bmw=new BMW("宝马车");
- people p =new people();
- p.drive(benz);
- p.drive(dz);
- p.drive(bmw);
- }
- }
- class people{
- public String name;
- public Car car;
- public void drive(Car car){
- this.car=car;
- car.run();
- }
- }
- class Car{
- public String name;
- public Car(String name){
- this.name=name;
- }
- public void run(){
- System.out.println(name+"已在路上飞快的奔跑");
- }
- }
- class DZ extends Car{
- public DZ(String name){
- super(name);
- }
- }
- class Benz extends Car{
- public Benz(String name){
- super(name);
- }
- }
- class BMW extends Car{
- public BMW(String name){
- super(name);
- }
- }
深入理解多态执行的时候的编译看左边,运行看右边
老毕在讲到多态执行问题时,结合下面的例子,给我们总结了一套口诀:“成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边。”意思是:当父类变量引用子类对象时(Fu f = new Zi();
),在这个引用变量f指向的对象中,他的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类一致(发生了复写)。
例:
class Fu {
intnum = 5;
static void method4() {
System.out.println("fu method_4");
}
void method3() {
System.out.println("fu method_3");
}
}
class Zi extends Fu {
intnum = 8;
static void method4() {
System.out.println("zi method_4");
}
void method3() {
System.out.println("zi method_3");
}
}
class DuoTaiDemo4 {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);//与父类一致
f.method4();//与父类一致
f.method3();//编译时与父类一致,运行时与子类一致
Ziz = new Zi();
System.out.println(z.num);
z.method4();
z.method3();
}
}
输出结果:
5
fu method_4
zi method_3
8
zi method_4
zi method_3
个人分析:
Fu f = new Zi();----------首先了解变量F到底是什么,把这句子分2段:Fu f;这是声明一个变量f为Fu这个类,那么知道了f肯定是Fu类。然后我们f=newZi();中建立一个子类对象赋值给了f,结果是什么??
结果是,拥有了被Zi类函数覆盖后的Fu类对象----f------。
-------------------------------------------也就是说:
只有子类的函数覆盖了父类的函数这一个变化,但是f肯定是Fu这个类,也就是说f不可能变成其他比如Zi这个类等等(突然f拥有了Zi类特有函数,成员变量等都是不可能的)。所以f所代表的是函数被复写后(多态的意义)的一个Fu类,而Fu类原来有的成员变量(不是成员函数不可能被复写)没有任何变化----------------获得结论:A:成员变量:编译和运行都看Fu。
但是f的Fu类函数被复写了。--------------获得结论:B:非静态方法:编译看Fu,运行看Zi
对于静态方法:编译和运行都看Fu!!
其实很简单,首先我们要理解静态情况下发生了什么?
----------------当静态时,Fu类的所有函数跟随Fu类加载而加载了。也就是Fu类的函数(是先于对象建立之前就存在了,无法被后出现的Zi类对象所复写的,所以没发生复写,那么获得:C:静态方法:编译和运行都看Fu。