【快速了解JAVA多态的详细知识(多态调用成员变量和方法讲解超详细版)】
一、多态的概念(什么是多态)
今天我们要讲解的是面向对象语言三大特征之一的多态。
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性是对象多种表现形式的体现。
比如我们说"宠物"这个对象,它就有很多不同的表达或实现,比如有小猫、小狗、蜥蜴等等。那么我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性。
接下来让我们了解多态的表现形式。
ini
复制代码
父类类型 对象名称 = 子类对象; //将猫这个对象赋给宠物这个形态 Pet p = new Cat(); //同时猫这个对象也可以作为猫的形态 Cat c = new Cat();
由上面的例子可知 猫这个对象具有多态的性质,他可以作为宠物这个形态也可以作为猫这个形态。
那么我们可以想想为什么猫可以有多态呢。
多态的前提
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法的重写
继承关系很简单,宠物就是猫的父类。
父类引用指向子类对象就是:
ini
复制代码
Fu f = new Zi();
f就是父类的引用 等于号就等同于指向 new Zi 就是创建子类的对象。
那什么是方法的重写呢?
方法的重写
当父类的方法不能满足子类现在的需求,需要进行方法的重写。
书写格式:
在继承体系中,子类出现了和父类一样的方法,我们就称子类这个方法为重写的方法。
@Override重写注解
注解其实和注释差不多,注释是给程序员看的,注解是给程序员和虚拟机看的。
当虚拟机看到这个注解,那么它就知道这个方法是要重写父类的方法,它会检查该方法的语法错误。
- @Override是放在重写后的方法上,检验子类重写时语法是否正确。
- 加上注解后如果有红色波浪线,表示语法错误
我们来给个例子加深理解
csharp
复制代码
public class Pet { //定义俩个成员变量 private String color; private String name; //定义俩个成员方法 public void eat(){ System.out.println("宠物在吃东西"); } public void drink(){ System.out.println("宠物在喝水"); } } class Cat extends Pet{ //重写后的方法 @Override public void eat(){ System.out.println("猫在吃猫条"); } //重写后的方法 @Override public void drink(){ System.out.println("猫在喝凉水"); } } class Dog extends Pet{ //重写后的方法 @Override public void eat(){ System.out.println("狗在吃骨头"); } //重写后的方法 @Override public void drink(){ System.out.println("狗在喝温水"); } }
在这里Dog和Cat俩个类中都重写了父类Pet中的eat和drink方法。
二、多态的意义(为什么要用多态)
倘若现在有一个需求:需要我们写一个教务系统。
那么这个教务系统里肯定有用户的注册吧,用户的类型有学生,教学老师,辅导员,系统管理员。
typescript
复制代码
public void register(形参){.....}
有这么多用户的类型,那登录方法里的形参要放什么对象呢?
如果只放学生对象,老师,辅导员,管理员对象就都不能调用register方法,这肯定不行啊。
那放其他类型的肯定也不行,这时候方法的形参就需要放他们的父类。
typescript
复制代码
public void register(Person p){.... p.show } //此时无论是Student对象 Teacher对象 都可以调用注册方法
由于JAVA中的多态,所以无论是学生,教学老师,辅导员,管理员都可以调用方法注册这个系统。
其实多态还允许调用不同类里重写的方法。
假如你是Student对象 注册学生就会调用Student里的show方法。
其他对象同理。
这样是有好处的。
多态的好处:
可以使用父类型作为参数,可以接受所有子类对象,体现多态的扩展性与遍历。
三、多态的用法(多态如何使用)
其实多态调用成员变量和成员方法是不同的。
变量调用:编译看左边,运行也看左边。
方法调用,编译看左边,运行看右边。
那么我们通过一个例子来理解一下。
typescript
复制代码
public class PetTest { public static void main(String[] args) { //用多态创建对象 Pet p = new Cat(); //调用的为成员变量,所以我们编译看左边,运行也看左边 //意思就是javac编译代码时会首先看父类Pet中有没有name这个变量,如果有那么就编译成功 //运行也会运行Pet中的name; System.out.println(p.name);//打印 宠物 //调用的为成员方法,所以我们编译看左边,运行看右边 //意思就是编译代码时还是会首先看父类Pet中有没有name这个变量,如果有那么就编译成功 //但是运行会在Cat类中找eat方法,如果有那么就运行Cat里的eat方法 p.eat();//打印 猫在吃猫条 } } class Pet { //定义一个成员变量 String name = "宠物"; //定义俩个成员方法 public void eat() { System.out.println("宠物在吃东西"); } public void drink() { System.out.println("宠物在喝水"); } } class Cat extends Pet { //重新定义了成员变量 String name = "猫咪"; //重写后的方法 @Override public void eat() { System.out.println("猫在吃猫条"); } //重写后的方法 @Override public void drink() { System.out.println("猫在喝凉水"); } } class Dog extends Pet { //重新定义了成员变量 String name = "小狗"; //重写后的方法 @Override public void eat() { System.out.println("狗在吃骨头"); } //重写后的方法 @Override public void drink() { System.out.println("狗在喝温水"); } }
那么我们再来理解一下为什么会有这样的规则呢?
ini
复制代码
Pet p = new Cat();
现在是用p来调用变量和方法的,而p是Pet类型的,所以默认都会在Pet中找。
这就是为什么编译要看左边的原因。
接下来我们看看成员变量和成员方法的不同点。
成员变量:在子类的对象中,会把父类的成员变量也继承下来。用父类对象调用肯定就是父类里的name了。
成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法覆盖的
所以子类虚方法表中只有重写后的eat方法,当然运行的也是这个方法了。
如果有宝子们不知道什么是虚方法可以看我的上一篇博客,里面有详细讲解.
恭喜你! 当你看到这里就已经完全掌握多态这个知识点了,如果你有什么疑问和意见,欢迎在评论区询问或者私信我。
看完麻烦各位哥哥姐姐们点点赞收收藏,你们的支持就是我分享的最大动力。
那么这一篇博客就到为止了,我们下篇再见!