Day09-面向对象-多态

news2024/9/21 23:44:43

文章目录

  • Day09-面向对象-多态
    • 学习目标
    • 1对象数组
      • 1.1 对象数组的声明和使用
      • 1.2 对象数组的内存图分析
    • 2. 多态
      • 2.1 前提
      • 2.2 多态的意义
      • 2.3 多态的特点
        • 2.3.1 成员方法的特点
        • 2.3.2 成员变量的特点
      • 2.4 引用数据类型转换
        • 2.4.1 类型转换的意义
        • 2.4.2 转型的异常
    • 3. final关键字的使用
      • 3.1 修饰类
      • 3.2 修饰方法
      • 3.3 修饰局部变量
      • 3.4 修饰成员变量

Day09-面向对象-多态

学习目标

  • 对象数组
  • 能够说出final关键字的使用
  • 能够说出什么是多态
  • 能够说出多态的特点以及作用
  • 能够说出引用数类型转换方式作用以及注意事项
  • 能够使用instanceof关键字判断一个对象是否属于某一个类

1对象数组

数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。

即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组。

注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。

1.1 对象数组的声明和使用

案例:

(1)定义矩形类,包含长、宽属性,area()求面积方法,perimeter()求周长方法,String getInfo()返回圆对象的详细信息的方法

(2)在测试类中创建长度为5的Rectangle[]数组,用来装3个矩形对象,并给3个矩形对象的长分别赋值为10,20,30,宽分别赋值为5,15,25,遍历输出

package com.atguigu.test08.array;

public class Rectangle {
    double length;
    double width;

    double area(){//面积
        return length * width;
    }

    double perimeter(){//周长
        return 2 * (length + width);
    }

    String getInfo(){
        return "长:" + length +
                ",宽:" + width +
                ",面积:" + area() +
                ",周长:" + perimeter();
    }
}

package com.atguigu.test08.array;

public class ObjectArrayTest {
    public static void main(String[] args) {
        //声明并创建一个长度为3的矩形对象数组
        Rectangle[] array = new Rectangle[3];

        //创建3个矩形对象,并为对象的实例变量赋值,
        //3个矩形对象的长分别是10,20,30
        //3个矩形对象的宽分别是5,15,25
        //调用矩形对象的getInfo()返回对象信息后输出
        for (int i = 0; i < array.length; i++) {
            //创建矩形对象
            array[i] = new Rectangle();

            //为矩形对象的成员变量赋值
            array[i].length = (i+1) * 10;
            array[i].width = (2*i+1) * 5;

            //获取并输出对象对象的信息
            System.out.println(array[i].getInfo());
        }
    }
}

1.2 对象数组的内存图分析

对象数组中数组元素存储的是元素对象的地址。

在这里插入图片描述

2. 多态

多态是继封装、继承之后,面向对象的第三大特性。

生活中,比如一条叫旺财的狗,我们可以说它是一条狗,也可以说它是一个动物;一个叫张三的男性,我们可以称它为男人,也可以称它为人,同时还能称呼他是一个动物。

多态: 就是指同一事物,具有多个不同表现形式。

2.1 前提

  1. 继承或者实现【二选一】
  2. 方法的重写【意义体现:不重写,无意义】
  3. 父类引用指向子类对象【格式体现】
class Test {
    public static void main(String[] args) {
        // 父类的引用 stu 指向了一个 new Student() 子类对象
        Person stu = new Student();
        stu.sleep();  // 子类重写了父类的方法,执行的是子类自己的方法
    }
}

class Person {
    void sleep() {
        System.out.println("人正在睡觉");
    }
}

class Student extends Person {
    @Override
    void sleep() {
        System.out.println("学生正在教室里睡觉");
    }
}

2.2 多态的意义

查看一下代码,思考这段代码是否可以进行优化:

class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();
        func(dog);
        func(cat);
    }
    private static void func(Dog dog) {
        dog.eat();
    }
    private static void func(Cat cat) {
        cat.eat();
    }
}
class Dog {
    public void eat() {
        System.out.println("小狗正在啃骨头");
    }
}
class Cat {
    public void eat() {
        System.out.println("小猫正在吃鱼");
    }
}

上述代码中,根据不同的参数类型,我们定义了不同的func方法,这个方法出现了重载。但是,我们从func方法的内部实现上来看,这两个方法都是调用对象的eat方法。

其实,在上述代码里,我们可以考虑给Dog类和Cat类定义一个父类,通过传递父类参数的形式,来简化代码。

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。

class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Cat cat = new Cat();
        func(dog);
        func(cat);
    }
    private static void func(Animal animal) { // 参数只需要指定 Animal 父类即可
        animal.eat();
    }
}
class Animal{  // 定义一个 Animal 类,让Dog和Cat都继承自它
    public void eat() {
        System.out.println("动物正在吃东西");
    }
}
class Dog extends Animal {
    public void eat() {
        System.out.println("小狗正在啃骨头");
    }
}
class Cat extends Animal {
    public void eat() {
        System.out.println("小猫正在吃鱼");
    }
}

上述代码的简化,并不只是简单的替换,同时还是一种扩展。当我们再出现新类型(例如Pig类)的对象时,我们只需要让Pig类继承自Animal类,就可以直接把这个Pig类型的对象传给func方法作为参数使用。这样就大大的提高了代码的灵活性,便于代码后期的扩展。

实际上,多态在我们程序中是大量存在的,因为有了多态的存在,才使得Java语言变得更加的灵活以扩展,实现了低耦合高内聚的编码思想。

2.3 多态的特点

父类引用指向子类对象的这种多态形式,在调用方法和使用属性时,有一些细节需要我们注意。

2.3.1 成员方法的特点

观察一下代码,思考代码的执行结果。

package com.atguigu.java;

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();  // 会调用 Dog 类的 eat 方法
//        animal.watchDog();    编译会报错
        ((Dog) animal).watchDog();
    }
}

class Animal {  // 定义一个 Animal 类,让Dog和Cat都继承自它
    public void eat() {
        System.out.println("动物正在吃东西");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("小狗正在啃骨头");
    }
    public void watchDog() {
        System.out.println("小狗正在看家");
    }
}

对于调用方法,记住一点编译看左边,运行看右边。

也就是说,在编译时要看等号左边声明的类型里是否有这个方法,如果有这个方法就编译通过,反之不通过。而在运行时,是看等号右边到底创建的是一个什么类型的对象,会直接调用这个对象具体的方法。

2.3.2 成员变量的特点

直接通过对象名称访问成员变量,等号左边是谁,优先用谁

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        System.out.println(animal.type);  // 动物
    }
}

class Animal {  // 定义一个 Animal 类,让Dog和Cat都继承自它
    String type = "动物";
    public void eat() {
        System.out.println("动物正在吃东西");
    }
}

class Dog extends Animal {
    String type = "狗";
    @Override
    public void eat() {
        System.out.println("小狗正在啃骨头");
    }
}

2.4 引用数据类型转换

多态的转型分为向上转型与向下转型两种:

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型。

    使用格式:

    父类类型  变量名 = new 子类类型();
    如:Animal a = new Cat();
    
  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

    使用格式:

    子类类型 变量名 = (子类类型) 父类变量名;:Cat c =(Cat) a; 
    
2.4.1 类型转换的意义

调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

public class Test {
    public static void main(String[] args) {
        Animal dog = new Dog();
//        dog.watchDoor();  编译报错,调用方法时,编译时看左边的类型,是Animal,Animal类里没有 watchDoor方法
        ((Dog) dog).watchDoor();
    }
}
class Animal {
    String type = "动物";
}
class Dog extends Animal {
    public void watchDoor() {
        System.out.println("狗正在看门");
    }
}
2.4.2 转型的异常

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        Dog d = (Dog)a;       
        d.watchHouse();        // 调用的是 Dog 的 watchHouse 【运行报错】
    }  
}

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

变量名 instanceof 数据类型 
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false

所以,转换前,我们最好先做一个判断,代码如下:

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        if (a instanceof Cat){
            Cat c = (Cat)a;       
            c.catchMouse();        // 调用的是 Cat 的 catchMouse
        } else if (a instanceof Dog){
            Dog d = (Dog)a;       
            d.watchHouse();       // 调用的是 Dog 的 watchHouse
        }
    }  
}

3. final关键字的使用

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了final 关键字,用于修饰不可改变内容。(最终的)

final: 不可改变。可以用于修饰类、方法和变量。

  • 类:被修饰的类,不能被继承。
  • 方法:被修饰的方法,不能被重写。
  • 变量:被修饰的变量,不能被重新赋值。

3.1 修饰类

final class Person {}

// 报错,被 final 修饰的类不能被继承
// class Student extends Person {}

查询API发现像 public final class Stringpublic final class Mathpublic final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是仅供我们使用,而不让我们所以改变其内容。

3.2 修饰方法

被final修饰的方法继承后不允许重写,否则会报错。

class Person {
    final void demo(){
        System.out.println("我是Person类里的demo方法");
    }
}

class Student extends Person {
    // void demo(){}  // 报错,被final修饰的方法不允许被重写
}

3.3 修饰局部变量

  • 如果局部变量时基本数据类型,被final修饰后,只能赋值一次,再次赋值会报错。

    public class FinalDemo1 {
        public static void main(String[] args) {
            // 声明变量,使用final修饰
            final int a;
            // 第一次赋值 
            a = 10;
            // 第二次赋值
            a = 20; // 报错,不可重新赋值
            // 声明变量,直接赋值,使用final修饰
            final int b = 10;
            // 第二次赋值
            b = 20; // 报错,不可重新赋值
        }
    }
    
  • 如果局部变量是引用数据类型,在被final修饰后,只能指向一次某个对象,不允许再修改指向。但是不影响对象内部的成员变量值的修改。

    // 被 final 修饰的变量只能指向一次内存地址
    final Person p = new Person("zhangsan", 18);
    // p = new Person("Jack", 20);   不能够再次指向其他位置,否则会报错
    p.setName("李四");  // 但是可以修改这个对象的属性
    

3.4 修饰成员变量

  • 显式初始化,即在定义时就指定变量的值。

    class Test {
        public static void main(String[] args) {
            Person p = new Person();
            // p.ID = 1;  报错,ID被final修饰,不允许修改
        }
    }
    
    class Person {
        final int ID = 10;
    }
    
  • 构造方法初始化。

    class Test {
        public static void main(String[] args) {
            Person p = new Person("张三",8);
            // p.ID = 3;  报错,被final修饰的ID不允许再修改
        }
    }
    class Person {
        private String name;
        final int ID;
        Person(String name,int ID) {
            this.name = name;
            this.ID = ID;
        }
    }
    

被 final 修饰的常量通常都使用全大写,例如: Math.PI,Math.E

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1468149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PMP认证有什么用?含金量高吗?如何备考?

PMP备考多久能参加PMP考试&#xff0c;培训机构是关键点 依我这几年的持证体验来看&#xff0c;PMP认证的用处还是比较多的&#xff0c;也有一定的含金量&#xff0c;这两个方面基本都是随便一百度就能得到结果的&#xff0c;在考PMP的人群中唯一不同的可能就是备考方面的问题…

谷歌发布开源大模型 Gemma,评测+最佳微调实践来啦!

Gemma 是由 Google 推出的一系列轻量级、先进的开源模型&#xff0c;他们是基于 Google Gemini 模型的研究和技术而构建。它们是一系列text generation&#xff0c;decoder-only的大型语言模型&#xff0c;对英文的支持较好&#xff0c;具有模型权重开源、并提供预训练版本&…

探索 LRU 算法的缺陷与解决方案

LRU算法 Linux 的 Page Cache 和 MySQL 的 Buffer Pool 的大小是有限的&#xff0c;并不能无限的缓存数据&#xff0c;对于一些频繁访问的数据我们希望可以一直留在内存中&#xff0c;而一些很少访问的数据希望可以在某些时机可以淘汰掉&#xff0c;从而保证内存不会因为满了而…

【新手易错点】golang中byte和rune

1 总体区别 在Golang中&#xff0c;byte和rune是两种不同类型的数据。简单来说&#xff0c;byte是一个8位的无符号整数类型&#xff0c;而rune则是一个32位的Unicode字符类型。 Byte: 在Golang中&#xff0c;byte类型实际上是uint8的别名&#xff0c;它用来表示8位的无符号整…

神经网络系列---常用梯度下降算法

文章目录 常用梯度下降算法随机梯度下降&#xff08;Stochastic Gradient Descent&#xff0c;SGD&#xff09;&#xff1a;随机梯度下降数学公式&#xff1a;代码演示 批量梯度下降&#xff08;Batch Gradient Descent&#xff09;批量梯度下降数学公式&#xff1a;代码演示 小…

机器视觉选型:如何选择一个合适光源控制器

在机器视觉系统中&#xff0c;选择合适的光源及其控制器对于确保高质量图像捕获和处理至关重要。本文会提供一些建议&#xff0c;以便于引导您了解如何基于应用需求选择最合适的光源和光源控制器。 1. 理解光源的功率需求 不同类型的光源具有不同的功率需求&#xff0c;这直接…

Linux:gcc的基本知识

gcc 是一个将C语言文件变成可执行文件的工具。 在Linux中&#xff0c;如果需要将一个C语言文件变得可以执行&#xff0c;那么除了这个文件本身的内容是C语言编写的内容外&#xff0c;还需要gcc这个编译工具进行编译才行。 gcc 使用的格式方法:gcc 要编译的文件 //在该代码下…

Jenkins常规配置(0)

Jenkins常规配置 20211005 1、配置中文 需要先安装插件&#xff0c;然后进入到全局配置中设置&#xff1a;zh_CN 2、Jenkins访问地址 安装的时候&#xff0c;会让设置访问路径&#xff0c;这里可以进行修改&#xff0c;进入配置全局变量中设置 3、全局配置go环境&#xff0…

二级等保需要什么样的SSL证书?

根据等级保护对象在国家安全、经济建设、社会生活中的重要程度&#xff0c;以及一旦遭到破坏、丧失功能或者数据被篡改、泄露、丢失、损毁后&#xff0c;对国家安全、社会秩序、公共利益以及公民&#xff0c;法人和其他组织的合法权益的侵害程度等因素&#xff0c;等级保护对象…

js 文件预览 在窗口设置“自定义名称”

1. 最近需要做一个点击表格某一列的标题&#xff0c;预览当前文件的一个小功能。本身功能很简单&#xff0c;点击该标题&#xff0c;预览文件&#xff0c;那么拿到他对应的文件地址&#xff0c;在浏览器打开就行了。 2. 事实如此&#xff0c;使用window.open(url, _blank);就行…

Flink/flinksql 语法 窗口与join 一文全 相关概念api汇总总结,底层process算子总结,与数据延迟处理,超时场景解决方案

Flink 窗口概念与join汇总总结 1 SQL语法中窗口语法相关&#xff08;仅仅是flinksql中 窗口的语法&#xff09;1.1 sql窗口1.2 window topN 2 java/SQL join语法与介绍2.1 有界join2.1.1 Window Join2.1.2 Interval Join2.1.3 Temporary Join2.1.4 LoopUp Join2.2 无界join2.2.…

基于协同过滤算法的体育商品推荐系统

摘要 本文深入探讨了基于协同过滤算法的体育商品推荐系统的构建方法及其在电子商务中的重要性。首先&#xff0c;介绍了协同过滤算法的基本原理&#xff0c;包括用户-商品矩阵、相似度度量和推荐生成。其次&#xff0c;探讨了协同过滤算法在体育商品推荐中的两种主要应用方式&a…

OpenCV 4基础篇| OpenCV像素的编辑

目录 1. 前言1. 像素的访问1.1 数组索引访问1.2 img.item() 2. 像素的修改2.1 数值索引修改2.2 img.itemset() 1. 前言 像素是构成数字图像的基本单位&#xff0c;像素处理是图像处理的基本操作。 对像素的访问、修改&#xff0c;可以使用 Numpy 方法直接访问数组元素。 1. 像…

专项:PID控制方法深究

1.前言 PID在工业界随处可见。其的原理是什么&#xff1f; 2.数学物理代表意义 PID全名为比例积分微分控制器。顾名思义&#xff0c;表明其由三个控制器组成。 一是P&#xff0c;其代表比例&#xff08;Proportional&#xff09;; 二是I&#xff0c;其代表积分&#xff08;I…

【Java程序设计】【C00267】基于Springboot的在线考试系统(有论文)

基于Springboot的在线考试系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 本系统是基于Springboot的在线考试系统&#xff1b;本系统主要分为管理员、教师和学生三种角色&#xff1b; 管理员登录系统后&#xff0c;可以对首页&#x…

C# (WebApi)整合 Swagger

SpringBoot-整合Swagger_jboot整合swagger-CSDN博客 C# webapi 也可以整合Swagger webapi运行其实有个自带的HELP页面 但是如果觉得UI不好看&#xff0c;且没办法显示方法注释等不方便的操作&#xff0c;我们也可以整合Swagger 一、使用NuGet控制台安装Swagger 在菜单中选择…

day41打卡

day41打卡 46. 携带研究材料&#xff08;第六期模拟笔试&#xff09; 状态表示 ​ 二维&#xff1a;dp[i] [j] 表示从下标为[0-i]的物品里任意取&#xff0c;放进容量为j的背包&#xff0c;价值总和最大是多少。 一维&#xff1a; ​ dp[j]表示&#xff1a;容量为j的背包&a…

第三百六十五回

文章目录 1. 概念介绍2. 方法与信息2.1 获取方法2.2 详细信息 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取设备信息"相关的内容&#xff0c;本章回中将介绍如何获取App自身的信息.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本…

在 where子句中使用子查询(一)

目录 子查询返回单行单列 查询公司工资最低的员工信息 查找公司雇佣最早的员工信息 子查询返回单行多列 查询与 ALLEN 工资相同&#xff0c;职位相同的所有员工信息 子查询返回多行单列 IN 操作 查询职位是“MANAGER”的所有员工的薪水 Oracle从入门到总裁:https://bl…

Xcode中App图标和APP名称的修改

修改图标 选择Assets文件 ——> 点击Applcon 换App图标 修改名称 点击项目名 ——> General ——> Display Name