Java多态性理解

目录

多态性:

对象的多态性:

方法的多态性:(调用谁的方法?)

属性没有多态性:(子类赋给父类不会覆写属性)

属性不重定义,构造方法也可以决定属性值

 子类不重定义属性

不在子类重新定义int age 则不构成多态,任何方法都能访问修改唯一的age值

 对象多态性总结:

 虚拟方法调用VMI

虚拟方法调用(多态情况下):


多态性:

多态性是一个运行期行为,不是编译期行为,编译器看不出来有没有多态,总结会有详细解释

对象的多态性:

先定义一个父类Person—>子类Man—>“孙类”Boy

public class Person {
    int age;
    String name;
	
    public void eat(){
	System.out.println("人要吃饭");
    }
}
public class Man extends Person{
    boolean isSmoking;
	
    public void eat(){
	System.out.println("男人吃的多");
    }
	public void Drink(){
	System.out.println("男人要喝酒");
    }
}
public class Boy extends Man{
    public void eat(){
	System.out.println("小孩要吃的有营养");
    }
	public void Drink(){
	System.out.println("小孩不能喝酒");
}
}

然后Test一下

方法的多态性:(调用谁的方法?)

  • 编译看左,运行看右:(虚拟方法调用)

Person A = new Man();  左边决定能用什么属性or方法,右边决定用谁的属性or方法

在编译期只能调用父类中声明的方法,但在执行期实际执行的是子类重写的方法

  • 左边父类,右边子孙:

父类引用指向子类对象,子类对象赋给父类引用,左边范围小,右边范围大

如果左边是子类,右边是父类就会报错

public class Test {
public static void main(String[] args) {
	
Person A = new Man();
A.eat();  //男人吃的多
//A.Drink();报错
//在编译期只能调用父类中声明的方法,但在执行期实际执行的是子类重写的方法
//编译看左,执行看右


Person B = new Boy();
B.eat();//小孩要吃的有营养
//B.Drink();报错
//在编译期只能调用父类中声明的方法,但在执行期实际执行的是子类重写的方法
//编译看左,执行看右

Man C = new Boy();
C.eat();//小孩要吃的有营养
C.Drink();//小孩不能喝酒
//Boy D = new Person();报错
//在编译期只能调用父类中声明的方法,但在执行期实际执行的是子类重写的方法
//编译看左,执行看右
}
}

如果没有对象多态性,则只能在三个类中创建三个不同的eat(){}  drink(){}方法,通过形参不同来实现重载。多态性的存在使相似功能的实现变得简洁。

属性没有多态性:(子类赋给父类不会覆写属性)

class Person{
int age = 1;
public Person(){
age = 2222;
}
}

class Man extends Person{
int age ;
//只要在子类又定义了age,则触发“编译运行都看左”的规律
//因为此时堆空间中有两个age,在实际调用的过程中调用父类的age
public Man(){
//这个构造函数无论怎么写,都无法改变调用结果
}
}

属性不重定义,构造方法也可以决定属性值

 下面这个例子在子类重定义了age,所以调用值调用父类的属性or方法

class Person{
int age = 1;
//构造略
}

class Man{
int age = 2;
//构造略
}

class Test{
public static void main(String[] args){
    Person A = new Man();//构造函数调用的是左边Person类的
    System.out.println(A.age);//结果为1
}
}

 子类不重定义属性

则堆空间只有唯一一个age,这个age受到所有能影响他的因素改变

Person A = new Man();调用的是Person的构造方法,原理如下:

 

不在子类重新定义int age 则不构成多态,任何方法都能访问修改唯一的age值

class Person{
int age;
//空参构造可以省
}

class Man extends Person{
public Man(){
//此处系统自动补全super();
this.age = 99;
}
}

class Boy extends Man{//构造可省
}

class Test{
public static void main(String[] args){
Person A = new Boy();//实例化一个Boy,把值赋给Person
System.out.println(A.age)//结果为99
}
}

无关构造函数带不带参,总之就是super()之后的this.属性=XXX操作会直接影响最终属性值

 对象多态性总结:

new的是谁就调用谁的构造方法,只是构造方法内部会调用上一级的构造方法(Boy调用Man调用Person),如果不在super()语句之后再添语句加以修改属性值(age=99),最终体现出来的就是最后一次调用的结果(Person的默认age=0)

由此可知,子类实例化赋值给父类,子类的属性,方法都可以覆盖父类,方法比较浅显易懂,而属性的覆盖需要额外在super()之后声明

对象多态性≈重定义

重定义方法:编译看左,执行看右

重定义属性:编译执行都看左

堆空间生成两个同名属性age,父类方法决定父类age,子类方法决定子类age,最终调用的是父类的age

 虚拟方法调用VMI

虚拟方法调用(Virtual Method Invocation)正常的方法调用

Person A = new Person(); A.eat();

Man B = new Man(); B.eat();

虚拟方法调用(多态情况下):

Person A = new Man(); A.eat();

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

编译时A为Person类型,而方法的调用是在运行时确定的,所以调用的是Man类的eat()方法。——动态绑定

多态性是一个运行期行为,编译器看不出来

从编译和运行的角度看:

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法法名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法这称为“晚绑定”或“动态绑定”。

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值