JavaSE——类与对象(3)

一、方法重写

        简单来讲,方法重写就是一个子类有一个方法,和父类的某个方法的名称,返回类型,参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法。比如说:

class Animal {public void makeSound() {System.out.println("一些奇怪的声音");}
}class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("汪汪汪");}
}public class Test {public static void main(String[] args) {Animal myAnimal = new Dog();myAnimal.makeSound(); // 输出 "汪汪汪"}
}

        Dog类重写了Animal类的makeSound方法。当我们创建一个Dog对象并将其赋值给Animal类型的引用时,调用makeSound方法将输出"汪汪汪",这表明调用的是Dog类的makeSound方法,体现了多态性。注意,@Override注解是可选的,但它可以帮助编译器检查方法签名是否正确,确保方法确实被重写。 

注意,方法重写也叫方法覆盖需要满足以下条件:

  1. 子类的方法的参数和方法名称必须和父类的完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
  3. 子类方法不能缩小父类方法的访问权限。
名称发生范围方法名形参列表返回类型修饰符
重载(voerload)本类必须一样类型、个数或者顺序至少有一个不同无要求无要求
重写(override)父子类必须一样完全相同子类重写方法必须是父类方法的子类或者相同子类不能缩小父类的访问范围

 二、多态

2.1什么是多态

        多态是面向对象编程中的一个重要概念,它允许不同类型的对象对同一方法进行不同的实现。具体来说,多态性指的是通过父类的引用变量来引用子类的对象,从而实现对不同对象的统一操作。看这个例子:

// 定义一个基类 Animal
abstract class Animal {// 抽象方法 makeSoundpublic abstract void makeSound();
}// Dog 类继承自 Animal
class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("汪汪!");}
}// Cat 类也继承自 Animal
class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("喵喵~");}
}public class first {public static void main(String[] args) {Animal a1 = new Dog();Animal a2 = new Cat();a1.makeSound();a2.makeSound();// 输出:// 汪汪!// 喵喵~}
}

2.2对象的多态

  1. 一个对象的编译类型和运行类型可以不同
  2. 编译类型在定义对象时就确定了,不能改变。
  3. 运行类型是可以变换的。
  4. 初次实例化一个对象时,=左边为编译类型,=右边为运行类型。
public class first {public static void main(String[] args) {Animal a1 = new Dog();//a1的编译类型为Animal,运行类型为DogAnimal a2 = new Cat();//a2的编译类型为Animal,运行类型为Cata1 = a2;//a1的运行类型变为Cat}
}

2.3多态的条件

  1. 存在继承关系的类之间才能够使用多态性。多态性通常通过一个父类用变量引用子类对象来实现
  2. 子类必须重写(Override)父类的方法。通过在子类中重新定义和实现父类的方法,可以根据子类的特点行为改变这个方法的行为,如猫和狗吃东西的独特行为。
  3. 使用父类的引用变量来引用子类对象。这样可以实现对不同类型的对象的统一操作,而具体调用哪个子类的方法会在运行时多态决定。

        关于不能调用子类的特有成员这一点是因为在能够调用那些成员是在编译阶段由编译器根据编译类型决定的。最终运行时,要依据于子类的具体实现。

2.4多态的细节

  1. 属性没有重写的说法,属性的值看编译类型
  2. instanceOf操作符,用于判断对象的类型是否为XX类型或XX类型的子类型。

三、向上转型

3.1什么是向上转型

        向上转型是指将一个子类的对象引用赋值给其父类类型的引用变量。在向上转型中,子类对象可以被视为父类对象,可以使用父类类型的引用变量来引用子类对象。这样做的好处是可以以统一的方式处理不同类型的对象。

父类类型 引用名 = new 子类类型();

        编译类型看左边,运行类型看右边。通过这个引用名,可以调用父类所有成员(需要遵守访问权限),不能调用子类中的特有成员。  

3.2向上转型的规则

  1. 子类对象可以隐式地转型为父类对象,不需要任何显式的类型转换操作。

  2. 父类引用变量可以引用子类对象,但通过父类引用变量只能访问到子类对象中定义的父类成员,无法访问子类独有的成员。

  3. 子类对象中重写的方法,在通过父类引用变量调用时,会调用子类中的实现(动态绑定)。

  4. 向上转型是安全的操作,因为子类对象本身就是一个父类对象。

class Animal {public void eat() {System.out.println("Animal is eating.");}
}class Dog extends Animal {@Overridepublic void eat() {System.out.println("Dog is eating.");}public void bark() {System.out.println("Dog is barking.");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog();  // 向上转型animal.eat();  // 调用的是 Dog 类中的 eat() 方法// animal.bark();  // 错误:无法访问 Dog 类中独有的方法Dog dog = (Dog) animal;  // 向下转型dog.bark();  // 调用 Dog 类中的 bark() 方法}
}

四、向下转型

4.1什么是向下转型

        向下转型(Downcasting)是指将一个父类类型的引用变量转换为其子类类型的引用变量。它与向上转型相反,需要进行显式的类型转换操作。

        在某些情况下,当一个对象被向上转型后,它的具体类型信息会丢失,只保留了父类类型的信息。如果我们需要访问子类中特有的成员或调用子类重写的方法,就需要使用向下转型。

子类类型 引用名 = (子类类型) 父类引用;

4.2向下转型的规则

  1. 只能强转父类的引用,不能强转父类的对象。
  2. 要求当前被强转的父类的引用必须指向的是当前目标类型的对象。
  3. 可以调用子类类型中所有的成员。
class Animal {public void eat() {System.out.println("Animal is eating.");}
}class Dog extends Animal {@Overridepublic void eat() {System.out.println("Dog is eating.");}public void bark() {System.out.println("Dog is barking.");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog();  // 向上转型// 使用向下转型之前,需要先检查对象是否实际上是子类的实例if (animal instanceof Dog) {Dog dog = (Dog) animal;  // 向下转型dog.bark();  // 调用 Dog 类中的 bark() 方法} else {System.out.println("animal is not an instance of Dog");}}
}

五、动态绑定

5.1什么是动态绑定

        当一个方法被声明为非静态且非私有时,在继承结构中,如果子类重写了该方法,则具体执行哪个版本的方法是在运行时确定的。这与静态绑定不同,静态绑定(编译时绑定)发生在编译阶段,此时确定了方法的具体实现。这种机制是多态的核心,它使得基类的引用能够根据实际对象类型调用相应的方法实现。

5.2Java的动态绑定机制

  1. 当调用对象方法时,该方法会和该对象的运行类型绑定
  2. 当调用对象属性时,没有动态类型绑定,哪里声明,那里使用。

看这个例子:

class A{public int i = 10;public int sum(){return get() + 5;}public int get(){return i;}public int sum1(){return i + 5;}
}class B extends A{public int i = 20;public int sum(){return get()+ 20;}public int sum1(){return i + 10;}public int get(){return i;}
}public class first{public static void main(String[] args) {A a = new B();System.out.println(a.sum());//40System.out.println(a.sum1());//30}
}

        在这段代码中,a.sum()指令,直接调用了B类中的sum方法,在sum方法调用了B类的get方法,所以结果是40。如果将B类的sum方法注释掉,会输出什么呢?

        由于B类没有对应的sum方法,会根据继承的规则查找A类是否有sum方法。这里有一个问题,A类的sum方法调用了get方法,但是A类和B类都有get方法,这里应该调用哪一个get方法呢?根据动态绑定规则:当调用对象方法时,该方法会和该对象的运行类型绑定。所以应该优先调用其绑定类型即B类中的get方法,又根据动态绑定规则:当调用对象属性时,没有动态类型绑定,哪里声明,那里使用。所以返回的是B类的i值,输出应该为20+5 = 25。

        如果将B类中的get方法也注释掉,这时应当根据继承的规则去A类中查找get方法,但是这个A类的get方法应该返回A的i,所以输出应该为10+5 = 15。

        如果不注释掉B类中的get方法,转而注释掉B类的i,答案也会是10+5 = 15。

六、多态数组

        多态数组是指数组中的元素可以是某个基类的实例,也可以是该基类的任何子类的实例。通常情况是数组类型定义为父类类型,而里面保存的实际元素类型为子类类型实际达到的效果就是父类引用指向子类对象

// 基类 Shape
abstract class Shape {public abstract void draw();
}// 子类 Circle 继承自 Shape
class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a circle.");}
}// 子类 Rectangle 继承自 Shape
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a rectangle.");}
}public class PolymorphicArrayExample {public static void main(String[] args) {// 创建一个 Shape 类型的数组Shape[] shapes = new Shape[2];// 向上转型:将 Circle 和 Rectangle 的实例放入数组shapes[0] = new Circle();shapes[1] = new Rectangle();// 遍历数组并调用 draw 方法for (Shape shape : shapes) {shape.draw();  // 动态绑定,根据实际对象类型调用相应的方法}// 输出:// Drawing a circle.// Drawing a rectangle.}
}

七、多态参数

      多态参数是指在方法或构造函数中使用一个基类类型作为形参,但实际传递给该方法或构造函数的对象可以是这个基类的任何子类实例,每传入一个子类对象,都相当于形成了一次多态

// 基类 Shape
abstract class Shape {public abstract void draw();
}// 子类 Circle 继承自 Shape
class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a circle.");}
}// 子类 Rectangle 继承自 Shape
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a rectangle.");}
}public class PolymorphicParameterExample {// 方法接受一个 Shape 类型的参数public static void display(Shape shape) {shape.draw();  // 动态绑定,根据实际对象类型调用相应的方法}public static void main(String[] args) {// 创建 Circle 和 Rectangle 的实例Circle circle = new Circle();Rectangle rectangle = new Rectangle();// 传递不同类型的对象给 display 方法display(circle);  // 输出: Drawing a circle.display(rectangle);  // 输出: Drawing a rectangle.}
}

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

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

相关文章

RK3568平台开发系列讲解(DMA篇)什么是DMA

🚀返回专栏总目录 文章目录 一、什么是DMA二、DMA的产生:背景三、理解 DMA:协处理器沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将带领大家深刻理解DMA。 一、什么是DMA DMA (Direct Memory Access) is used to copy data directly between devices and R…

AD20使用操作第三部分

常见CHIP封装的创建 SOD-123 第一个,顶视图,第二个,侧视图,第三个,侧视图。 E表示:丝印的本体 D表示:宽度 b:焊盘的宽度 A:高度 L:管脚长度 PCB焊盘:焊接器…

flink学习(6)——自定义source和kafka

概述 SourceFunction:非并行数据源(并行度只能1) --接口 RichSourceFunction:多功能非并行数据源(并行度只能1) --类 ParallelSourceFunction:并行数据源(并行度能够>1) --接口 RichParallelSourceFunction:多功能并行数据源(并行度能够>1) --类 【建议使用的】 ——…

ubuntu安装chrome无法打开问题

如果在ubuntu安装chrome后,点击chrome打开没反应,可以先试着在terminal上用命令打开 google-chrome 如果运行命令显示 Chrome has locked the profile so that it doesnt get corrupted. If you are sure no other processes are using this profile…

Ansible--自动化运维工具

Ansible自动化运维工具介绍 1.Ansible介绍 Ansible是一款自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。…

软件工程相关-用PD画类图-设置方法的参数

前提:pd16安装完成,已经画好了类 1、为类添加方法。 2、双击方法,如下图所示 3、此时会有弹窗,选择参数栏,按需求进行设置。 (总是忘记,故记录下自用

IT人日常健康工作生活方案

1. 早餐(7:00-8:00) 早餐是一天中最重要的一餐,提供充足的能量来启动新的一天。根据亚洲饮食的特点,我们加入了米饭、豆腐、蔬菜等传统食材,同时保持高蛋白、低糖的原则。 糙米粥或小米粥(1碗):低GI碳水化合物,有助于稳定血糖,提供持久能量。可加入少量的红枣、枸杞…

Qt配置Opencv环境

下载opencv后配置环境变量(官网下载) 然后ok了

MongoDB相关问题

视频教程 【GeekHour】20分钟掌握MongoDB Complete MongoDB Tutorial by Net Ninja MongoDB开机后调用缓慢的原因及解决方法 问题分析: MongoDB开机后调用缓慢,通常是由于以下原因导致: 索引重建: MongoDB在启动时会重建索引…

目标检测之学习路线(本科版)

以下是为一名计算机科学与技术本科大四学生整理的“目标检测”学习路线,结合了从基础到高级的内容,适合初学者逐步深入。每个阶段都有明确的学习要求、学习建议和资源推荐。 阶段一:基础知识学习 学习要求: 掌握编程语言 Pytho…

网络安全在现代企业中的重要作用

网络安全是这个数字时代最令人担忧的事情之一。对技术的依赖性越来越强,使其同时面临多种网络威胁。其声誉和法律后果的大幅下降可能归因于一次妥协。 这使得良好的网络安全成为所有企业的选择和必需品。本文介绍了网络安全的重要性、企业中常见的网络威胁以及公司…

“harmony”整合不同平台的单细胞数据之旅

其实在Seurat v3官方网站的Vignettes中就曾见过该算法,但并没有太多关注,直到看了北大张泽民团队在2019年10月31日发表于Cell的《Landscap and Dynamics of Single Immune Cells in Hepatocellular Carcinoma》,为了同时整合两类数据&#xf…

《进程隔离机制:C++多进程编程安全的坚固堡垒》

在当今数字化时代,软件系统的安全性愈发成为人们关注的焦点。尤其是在 C多进程编程领域,如何确保进程间的安全交互与数据保护,是每一位开发者都必须面对的重要课题。而进程隔离机制,犹如一座坚固的堡垒,为 C多进程编程…

C++11 http服务端和客户端库cpp-httplib

C11 http服务端和客户端库cpp-httplib 环境: http: yhirose/cpp-httplib v0.18.1 json: nlohmann/json v3.11.31. 简介 cpp-httplib 是一个轻量级且易于使用的 C11 HTTP 库,由 yhirose 开发和维护,开源协议为MIT。它支持 HTTP/HTTPS 协议&…

怎样使用sys.dm_os_wait_stats

文章目录 sys.dm_os_wait_stats 支持诊断 SQL Server 性能问题的基本指标。如果在 SQL Server 引擎中遇到一些问题(CPU、内存、I/O、锁、闩锁等),sys.dm_os_wait_stats 数据揭示一些问题。SQL Server Management Studio 中的活动监视器&#…

Hbase2.2.7集群部署

环境说明 准备三台服务器,分别为:bigdata141(作为Hbase主节点)、bigdata142、bigdata143确保hadoop和zookeeper集群都先启动好我这边的hadoop版本为3.2.0,zookeeper版本为3.5.8 下载安装包 下载链接:In…

JVM 性能调优 -- CMS 垃圾回收器 GC 日志分析【Full GC】

前言: 上一篇我们分析了 Minor GC 的发生过程,因为 GC 日志没有按我们预估的思路进行打印,其中打印了 CMS 垃圾回收器的部分日志,本篇我们就来分析一下 CMS 垃圾收集日志。 JVM 系列文章传送门 初识 JVM(Java 虚拟机…

微信小程序中的WXSS与CSS的关系及使用技巧

微信小程序中的WXSS与CSS的关系及使用技巧 引言 在微信小程序的开发中,样式的设计与实现是构建用户友好界面的关键。微信小程序使用WXSS(WeiXin Style Sheets)作为其样式表语言,WXSS在语法上与CSS非常相似,但也有一些独特的特性。本文将深入探讨WXSS与CSS的关系,介绍WX…

自研芯片逾十年,亚马逊云科技Graviton系列芯片全面成熟

在云厂商自研芯片的浪潮中,亚马逊云科技无疑是最早践行这一趋势的先驱。自其迈出自研芯片的第一步起,便如同一颗石子投入平静的湖面,激起了层层涟漪,引领着云服务和云上算力向着更高性能、更低成本的方向演进。 早在2012年&#x…

掌上单片机实验室 — RT - Thread+ROS2 浅尝(26)

前面化解了Micro_ROS通讯问题,并在 RT-Thread Studio 环境下,使用Micro_ROS软件包中的例程,实现了STM32F411CE核心板和ROS2主机的通讯。之后还尝试修改例程 micro_ros_sub_twist.c ,实现了接收 turtle_teleop_key 所发出的 turtle…