Java学习笔记(十):面向对象⑤ —— 多态性

简介: Java学习笔记(十):面向对象⑤ —— 多态性

@[toc]
  
  
  

  

为什么要有多态性

package 封装继承多态.多态性练习基本使用;

// 多态性的使用举例
public class AnimalTest {
    
    public static void main(String[] args) {
        AnimalTest test = new AnimalTest();
        test.func(new Dog());
        
        test.func(new Cat());
    }

    // 如果使用了多态性,就只用写这一个方法
    // 每个对象的eat、shout方法的不同取决于new的子类的差异
    // 如:Animal animal = new Dog();    
    // 如果Dog()换成Cat(),则下面eat、shout方法执行的结果就又不一样了    
    public void func(Animal animal){    
        animal.eat();
        animal.shout();
    }
    
    // 如果没有多态性,就会写很多如下的方法,去调用
    // 因为这些形参是写死的,是啥类型就得写啥
    public void func(Dog dog){
        dog.eat();
        dog.shout();
    }
    
    // 要想减少这样冗余的代码,就要用多态
    public void func(Cat cat){
        cat.eat();
        cat.shout();
    }
}

class Animal{
    
    public void eat(){
        System.out.println("动物,进食");
    }
    
    public void shout(){
        System.out.println("动物:叫");
    }
}

class Dog extends Animal{
    public void eat(){
        System.out.println("狗吃骨头");
    }
    
    public void shout() {
        System.out.println("汪!汪!汪!");
    }
}

class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    
    public void shout() {
        System.out.println("喵!喵!喵!");
    }
}

  

多态性的理解与使用

  

理解多态性

可以理解为一个事物的多种状态。

  

何为多态性

对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)

  

多态的使用(又叫虚拟方法调用)

有了对象的多态性后,我们在编译期,只能调用父类中声明的方法;但在运行期,我们实际执行的是子类重写父类的方法。

总结:编译,看左边;运行,看右边。

  

多态性的使用前提

  1. 类的继承关系

没有继承关系就没有子父类,进而没有多态性。

  1. 方法的重写

不重写就相当于造了个新方法,跟多态没关系。

  

用代码直观理解

  
现在我们已经造好三个类了,分别是:Person类、Man类、Woman类
  

对象的多态性(再次强调!)
子类的对象赋给父类的引用(这句好理解);父类的引用指向子类的对象

  
正常来说,new对象应该这样做:

Person p1 = new Person();

  
但多态性是这样:

Person p2 = new Man();
Person p3 = new Woman();

显然new Man()是创建了子类Man的对象,new Woman()是创建了子类Woman的对象。它们都分别赋给了父类的引用类型变量p2和p3。
这就是对象的多态性!

  
多态的使用:
当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ——这又叫虚拟方法调用。

p2.eat();
p2.walk();
  • 编译看左:在写代码时,当写出p2.时,点儿出来的方法全是父类中的方法。即 写代码(编译)时,看等号左边的父类结构来写程序。
  • 运行看右:程序运行时,真正执行的是子类重写后的方法。即 运行时,看等号右边的子类结构来判断执行结果。

  

多态性的范围

  
对象的多态性,只适用于方法,不适用于属性 (编译和运行都看左边)
  

多态性不适用于属性

  
之前多态性说的都是关于方法,为什么不说属性呢?因为属性不在多态性使用范围内!!!

  
重写:

  • 方法:若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
  • 属性:对于属性则不存在这样的现象,即使子类里定义了与父类完全相同的属性,这个属性依然不可能覆盖父类中定义的属性。

  
编译运行时:

  • 方法:编译看左,运行看右
  • 属性:编译运行都看左

  
  
  

虚拟方法调用

  

正常的方法调用

等号左右两边类相同

Person p = new Person();
p.walk();
Student s = new Student();
s.walk();

  

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

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

Person p = new Student();
p.walk();        // 调用Student类(子类)的walk方法

  

编译时类型和运行时类型

编译时 p为 Person类型(等号左边父类),而方法的调用是在运行时确定的,所以调用的是Student类(等号右边子类)的walk方法。—— 动态绑定

  
在这里插入图片描述
  
  
  

方法的重载与重写

  

1、二者的定义细节

看我之前的文章!

重载:Java学习笔记(七):面向对象② —— 类与对象的结构

重写:Java学习笔记(九):面向对象④ —— 继承性

  

2、从编译和运行的角度看

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法们不同的参数列表,对同名的方法进行区分。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名但不同参数的方法。
所以,对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为早绑定静态绑定

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为晚绑定动态绑定

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

  
  
  

强制类型转换(父类数据类型转子类数据类型)

  

为什么要有强制类型转换

有了对象的多态性后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。

Person p = new Man();

此时 p对象在编译时只能写 Person父类的方法,子类Man特有的方法,如:站着尿尿,不能调用。

  

如何才能调用子类特有的属性和方法?

  

向下转型:使用强制类型转换

// 把 Person类型的p,强制转换成了Man类型
Man m = (Man) p;

这样就能用Man子类特有的方法,如:m.站着尿尿();了。

  

异常情况:

之前刚才的强转,p 已由Person类型转为Man类型,如果再强转

Woman w = (Woman)p;

会出现 ClassCastException异常

这是由于强转只能发生在父类与子类之间,同级别的两个子类间不能强转。

  
  
  

instanceof关键字

  

instanceof关键字就是用来防止上面说的ClassCastException异常的。
  

格式:

a instanceof A

判断对象a是否是类A的实例对象或者A子类的实例对象。如果是,返回true;如果不是,返回false。

如果a所属的类与A类连子父类关系都没有,那编译就报错了。
  

使用情境:

为了避免在向下转型时出现 ClassCastException的异常,我们在向下转型之前,先进行 instanceof 的判断,一旦返回true,就进行向下转型;如果返回false,不进行向下转型。
  

举一反三:

如果 a instanceof A 返回true,且类B是类A的父类,则a instanceof B也返回true。

  

代码举例:

// if判断为false,不能强转
if (p instanceof Woman) {
    // 这不会执行
    Woman w = (Woman) p;
    w.goShopping();
    System.out.println("**********Woman*********");
}

// if判断为true,可以强转
if (p instanceof Man) {
    // 会执行
    Man m = (Man) p;
    m.earnMoney();
    System.out.println("*********Man************");
}

  

在这里插入图片描述

相关文章
|
3月前
|
Java API 微服务
2025 年 Java 从入门到精通学习笔记全新版
《Java学习笔记:从入门到精通(2025更新版)》是一本全面覆盖Java开发核心技能的指南,适合零基础到高级开发者。内容包括Java基础(如开发环境配置、核心语法增强)、面向对象编程(密封类、接口增强)、进阶技术(虚拟线程、结构化并发、向量API)、实用类库与框架(HTTP客户端、Spring Boot)、微服务与云原生(容器化、Kubernetes)、响应式编程(Reactor、WebFlux)、函数式编程(Stream API)、测试技术(JUnit 5、Mockito)、数据持久化(JPA、R2DBC)以及实战项目(Todo应用)。
201 5
|
6月前
|
存储 Java
# 【Java全栈学习笔记-U1-day02】变量+数据类型+运算符
本篇笔记主要围绕Java全栈学习的第二天内容展开,涵盖了变量、数据类型、运算符以及Scanner类的应用。首先介绍了变量的概念与命名规范,以及如何定义和使用变量;接着详细讲解了Java中的基本数据类型,包括整型、浮点型、字符型、布尔型等,并通过实例演示了数据类型的运用。随后,深入探讨了各类运算符(赋值、算术、关系、逻辑)及其优先级,帮助理解表达式的构成。最后,介绍了如何利用Scanner类实现用户输入功能,并通过多个综合示例(如计算圆面积、购物打折、变量交换及银行利息计算)巩固所学知识。完成相关作业将进一步加深对这些基础概念的理解与实践能力。
89 13
|
2月前
|
存储 Java 测试技术
Java基础 - 面向对象
面向对象编程是Java的核心,包含封装、继承、多态三大特性。封装隐藏实现细节,提升代码可维护性与安全性;继承实现类间IS-A关系,支持代码复用;多态通过继承、重写与向上转型,实现运行时方法动态绑定,提升系统扩展性与灵活性。
|
4月前
|
人工智能 Java 编译器
Java:面向对象
本文介绍了Java编程中的核心概念,包括包的命名规范与自动导入机制、构造方法的特点与使用、`this`和`super`关键字的作用、继承的基本规则、访问权限的设置、封装的意义、多态的实现原理以及`static`关键字的用法。通过详细解析每个知识点,并结合代码示例,帮助读者深入理解Java面向对象编程的核心思想与实践技巧。内容适合初学者及进阶开发者学习参考。
108 0
|
6月前
|
缓存 安全 Java
java面试-基础语法与面向对象
本文介绍了 Java 编程中的几个核心概念。首先,详细区分了方法重载与重写的定义、发生阶段及规则;其次,分析了 `==` 与 `equals` 的区别,强调了基本类型和引用类型的比较方式;接着,对比了 `String`、`StringBuilder` 和 `StringBuffer` 的特性,包括线程安全性和性能差异;最后,讲解了 Java 异常机制,包括自定义异常的实现以及常见非检查异常的类型。这些内容对理解 Java 面向对象编程和实际开发问题解决具有重要意义。
|
6月前
|
开发框架 Java 开发工具
【Java全栈学习笔记-U1-day01】Java介绍
本笔记整理了Java学习的基础内容,涵盖程序理解、Java语言特性、JDK安装与配置、Java程序开发工具及编写步骤。重点介绍了Java程序的基本结构、编译和运行过程,以及输出语句的使用。通过实例演示了IDEA创建Java程序的方法,并强调了编码规范和注意事项。适合初学者复习和交流学习。 主要内容: 1. 理解程序:计算机组成、程序定义。 2. 简介:Java语言特点、技术平台、JDK作用。 3. 编写Java程序:编写、编译、运行步骤,基本结构。 4. 输出语句 5. DEA使用:新建工程、保存位置、文件介绍、新建类。 6. 扩展:注释、代码规范、大小写敏感、缩进等。
|
9月前
|
Java 程序员 开发者
Java 中的多态性
Java中的多态性是面向对象编程的重要特性之一,允许一个接口或基类引用指向其不同子类的对象,实现“一种接口,多种方法”。通过方法重写和动态绑定,多态性提高了代码的灵活性和可扩展性。
239 3
|
9月前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
101 1
|
10月前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
87 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
Java 开发者
Java 面向对象新视界:揭秘子类如何“继承”父类精华,再添“创新”之笔
【6月更文挑战第16天】在Java的面向对象世界,子类继承父类的特性,如`Circle`继承`Shape`,展示“is-a”关系。子类不仅保留父类的`color`和`display`方法,还添加了`radius`属性及定制的显示逻辑。这种继承与创新允许代码复用,增强灵活性和可扩展性,使得构建复杂系统变得更加高效和模块化。通过持续的继承与定制,开发者能构建出一系列独具特色的类,充分展现面向对象编程的力量。
208 57