Java 面向对象(OOP)的三大特性
封装
所谓封装,意思就是隐藏内部细节,在编程中,指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,并尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
封装的最大特点就是降低了耦合性,外部无需知道内部的实现细节,只需使用就行了。
比如,在 Java 中,方法和类等都是一种封装。
public class Cat {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
继承
事物有分类的概念,比如动物、植物、猫、狗等,分类有个根,可以向下不断细化,形成一个层次分类体系。
编程语言使用继承来描述这种概念,在继承中,有父类(基类)和子类(派生类),比如动物类 Animal 和狗类 Dog,Animal 是父类,Dog 是子类。
继承实现了 IS-A 关系,比如 Animal 和 Dog,Dog is Animal 就是一种 IS-A 关系。
Java 继承是使用已经存在的类作为基础建立新类的技术,新类可以继承父类的数据或功能,还可以增加子类特有的数据或功能。
这样,公共的属性和行为可以放到父类中,而子类只需要关注子类特有的就可以了;另一方面,遵循里氏替换原则,不同的子类对象可以当成父类对象(即父类变量可以指向子类对象,称为向上转型),方便统一处理。
基本语法
Java 使用 extends
关键字表示继承关系,一个类最多只能有一个父类(单继承),默认父类是 java.lang.Object。
子类继承了父类的非私有属性和方法(构造方法除外),子类可以重写继承的父类方法(注意重写不能降低方法的访问权限),可以增加新的属性,新的方法,即子类可以在父类基础上进行重新实现和扩展。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,可以抛出 IOException 异常或者 IOException 的子类异常。
注意:为什么不能方法重写时不能降低访问权限?因为继承体现的是 IS-A 关系,降低可见性会减少可以对外的行为,从而破坏了 IS-A 关系。
创建子类对象时,会先初始化父类的部分,子类构造方法默认会调用父类的无参构造方法,除非通过 super 手动调用父类的有参构造方法,父类必须要有无参构造方法。
super
:在类的内部,可以通过 super 关键字明确访问父类的非私有属性、成员方法、构造方法,在子类构造方法中调用父类构造方法时,super
必须放在第一行。与 this 不同的是,super 不能指代具体的父类对象,不能作为参数值或返回值。
如果类被 final 修饰,则表示最终类,不能被继承,如 String 类;如果方法被 final 修饰,则该方法不能被子类重写。
public class Animal {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void eat(){System.out.println("吃...");}
}class Cat extends Animal {// @Override 注解,表示重写父类方法@Overridepublic void eat(){System.out.println(super.getName() + "猫猫爱吃小鱼鱼");}
}
谨慎继承
和分类体系相似,继承是一种逐级向上抽象的体系,因此继承是非常强大的,广泛应用于各种 Java API、框架和类库之中,提供了大量基类和基础公共代码。但继承也存在一些问题。
- 继承可能破坏封装
封装就是隐藏实现细节,提供简化接口。使用者只需要关注怎么用,而不需要关注内部是怎么实现的。实现细节可以随时修改,而不影响使用者。
继承可能破坏封装是因为子类和父类之间可能存在着实现细节的依赖,更具体地说,子类需要知道父类的可重写方法之间的依赖关系,如果父类的方法之间存在调用依赖,而子类只重写了某个方法,就可能导致不可预测的错误。
父类也不能随意增加公开方法,因为给父类增加就是给所有子类增加,而子类可能必须要重写该方法才能确保方法的正确性。
- 继承没有完全体现 IS-A 关系
比如,绝大部分鸟都会飞,可能就想给鸟类增加一个方法 fly() 表示飞,但有一些鸟就不会飞,比如企鹅。
- 优先使用组合而非继承
使用组合可以抵挡父类变化对子类的影响,从而保护子类,应该优先使用组合。
public class Child {private Base base;private long sum;public Child(){base = new Base();}public void add(int number) {base.add(number);sum+=number;}public void addAll(int[] numbers) {base.addAll(numbers);for(int i=0; i<numbers.length; i++){sum+=numbers[i];}}public long getSum() {return sum;}
}
多态
多态分为编译时多态和运行时多态,编译时多态主要指方法的重载(overload),运行时多态指对象变量所指向的具体类型在运行期间才确定。
运行时多态有三个条件: 继承/实现;重写(覆盖);向上转型。
在 Java 中有两种形式实现运行时多态:继承和接口。
通过多态,Java 可以实现对不同类型的子类对象统一处理。
转型
转型分向上转型(子类转父类)和向下转型(父类转子类,需强转),实质是数据类型转换,当声明的数据类型与实际数据的类型不一致时,需要转型,而向上转型存在丢失数据或操作的问题。
Extend extend = new Extend();
Base base = extend; // 向上转型
extend = (Extend) base; // 向下转型,需强转
向下转型时,需要判断对象数据的实际类型是否与目标类型兼容(目标类型与实际类型一致,或者是两者某个中间层级的类型),如果不兼容,则无法转换,可以使用 instanceof
关键字判断。
if( base instanceof Extend) {extend = (Extend) base;
}
静态绑定和动态绑定
-
静态类型:声明变量时的数据类型,通常是某个父类或接口。
-
动态类型:创建对象时的数据类型。
在继承/实现体系中,存在成员重名的问题,可以认为此时子类对象有两份同名的成员变量或方法(父类部分和子类部分)。
对于 private 变量和方法,由于只能在类内访问,在父类中访问的是父类的,子类中访问的是子类的。
对于 public 成员,则看如何访问。在类的内部,默认带参数 this,访问的是类自身的,但子类可以通过 super 明确指定访问父类。
在类的外部,静态变量、静态方法、实例变量根据对象的静态类型判断,静态绑定,这符合数据类型向上转换后的特点。
对于实例方法,则根据对象的动态类型判断,动态绑定,因为 JVM 在实现多态的时候用的是 invokevirtual
指令,它会从子类部分开始往父类去寻找在所有重载版本中最匹配的方法。
静态绑定在程序编译阶段即可决定,而动态绑定则要等到程序运行时。
注意:动态绑定确认方法后,方法中对变量的访问由于默认带 this.
,此时访问的是实际类中的变量。
class Base {public static int NUM;private int num;static {System.out.println("基类静态初始化代码块,NUM = "+ NUM);NUM = 10;}{System.out.println("基类实例初始化代码块,num = "+ num);num = 1;}public Base() {System.out.println("基类构造方法,num = " + num);num = 2;}protected void step() {System.out.println("基类,NUM = " + NUM + ",num = "+ num);}public void action(){step();}
}class Extend extends Base {public static int NUM;private int num;static {System.out.println("子类静态初始化代码块,NUM = "+ NUM);NUM = 100;}{System.out.println("子类实例初始化代码块,num = "+ num);num = 10;}public Extend() {System.out.println("子类构造方法,num = " + num);num = 20;}@Overrideprotected void step() {System.out.println("子类,NUM = " + NUM + ",num = "+ num);}}public class Test {public static void main(String[] args) {System.out.println("创建子类 === ");Extend extend = new Extend();System.out.println("子类 action === ");extend.action();Base base = extend;System.out.println("基类 action === ");base.action();System.out.println("基类访问类变量 === " + base.NUM);System.out.println("子类访问类变量 === " + extend.NUM);}
}
输出如下:
创建子类 ===
基类静态初始化代码块,NUM = 0
子类静态初始化代码块,NUM = 0
基类实例初始化代码块,num = 0
基类构造方法,num = 1
子类实例初始化代码块,num = 0
子类构造方法,num = 10
子类 action ===
子类,NUM = 100,num = 20
基类 action ===
子类,NUM = 100,num = 20
基类访问类变量 === 10
子类访问类变量 === 100
虚方法表
动态绑定的实现机制是从对象的实际类型开始往上逐级查找要执行的方法,如果继承的层次比较深,要调用的方法位于比较上层的父类,则每次调用都要进行多次查找,效率较低。
为此,大多数系统使用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候为每个类创建一个表,记录该类的对象所有动态绑定的方法(包括父类的方法)及其地址,一个方法只有一条记录,子类重写了父类方法后只会保留子类的。
这样,当动态绑定方法的时候,只需要查找实际类型的虚方法表就可以了。
参考:《Java 编程的逻辑》 马俊昌
相关文章:
Java 面向对象(OOP)的三大特性
封装 所谓封装,意思就是隐藏内部细节,在编程中,指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,并尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。…...
Java:openjdk: error: Student is abstract; cannot be instantiated;java编译环境
文章目录编译环境jdkopenjdk错误代码小心javac -verbos编译环境 jdk 需要安装的javac 在java-devel 包里 [root10 ~]# rpm -qf /usr/bin/javac file /usr/bin/javac is not owned by any package [root10 ~]# ll /usr/bin/javac lrwxrwxrwx. 1 root root 23 Jun 15 09:52 /us…...
28个案例问题分析---019---临时解决方案和最终解决方案--思想
临时解决方案与最终解决方案一:背景介绍二:临时解决方案?最终解决方案?概念如何选择三:总结一:背景介绍 项目中,出现了一个线上问题。 用户登陆之后看不到课程。重新登陆就可以看到课程。出现这…...

计算机网络的166个概念你知道几个 第四部分
HTML:HTML 称为超文本标记语言,是一种标识性的语言。它包括一系列标签.通过这些标签可以将网络上的文档格式统一,使分散的 Internet 资源连接为一个逻辑整体。HTML 文本是由 HTML 命令组成的描述性文本,HTML 命令可以说…...

Lenovo 联想-IdeaPad-Y530电脑 Hackintosh 黑苹果efi引导文件
原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板联想-IdeaPad-Y530处理器Intel 酷睿2双核 T9400已驱动内存2GB已驱动硬盘2TB HP EX950 PCI-E Gen3 x4 NVMe SSD已驱动显卡NVIDIA GeForce 9300M GS无法驱动声卡Realtek ALC888无法驱动网卡RTL8168H Giga…...

mac M1 nvm安装教程,避坑
mac M1 nvm 安装问题 新款的mac搭载了苹果自研的芯片,放弃了intel的x86芯片,那之前的软件难免会存在兼容性问题。 鄙人有幸踩了第一个坑。 在通过nvm 安装不同版本的node 时,出现了问题。 问题一:先说一下 nvm的安装问题&#…...

【项目精选】基于网络爬虫技术的网络新闻分析(视频+论文+源码)
点击下载源码 基于网络爬虫技术的网络新闻分析主要用于网络数据爬取。本系统结构如下: (1)网络爬虫模块。 (2)中文分词模块。 (3)中3文相似度判定模块。 (4)数据结构化存…...

【Python - Matplotlib】P2 plot 折线图
Matplotlib绘制折线图折线图完整代码与效果基础折线图设定横纵坐标设置中文显示添加网格添加描述信息再添加一个城市设置两个折线图前言 上一节内容主要围绕介绍 Matplotlib 的画板结构。 链接:https://blog.csdn.net/weixin_43098506/article/details/129331576 本…...

【Verilog】——模块,常量,变量
目录 1.模块 1.描述电路的逻辑功能 2. 门级描述 3.模块的模板编辑 2.关键字 3.标识符 4.Verilog源代码的编写标准 5.数据类型 1.整数常量 2.参数传递的两种方法 3.变量 4.reg和wire的区别 5.沿触发和电平触发的区别 6.memory型变脸和reg型变量的区别 1.模块 1.描…...

论文投稿指南——中文核心期刊推荐(电影、电视艺术)
【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...
Pip install 和Conda install 总结
版本一 conda install xxx:这种方式安装的库都会放在/Users/orion-orion/miniforge3/pkgs目录下。我们在我们的虚拟环境中要用到或下载时先到该路径下去找,若有则直接将其复制到我们的虚拟环境中包得存放位置: ~/site-packages/。若没有,则先…...

嵌入式系统实验——【玄武F103开发板】实现两个LED小灯闪烁
目录一、实验文件main.cstm32f10x.h二、实验思路(一)打开两个LED小灯1.在玄武F103开发板上找到LED0、LED1对应的GPIO控制寄存器2.找到GPIOB、GPIOE的地址3.打开APB2外设时钟的使能寄存器4.对GPIO寄存器进行设置对端口输出寄存器进行设置(二&a…...

数组之双指针题
文章目录一、最长连续不重复子序列1.题目介绍2.思路3.二、长度最小的子数组1.题目介绍2.思路3.代码三、数组元素的目标和1.题目介绍2.思路3.代码总结其实在之前我写过不少双指针得题解,刷题专练之数组移除元素 刷题专练之翻转题练习这两篇文章的题解基本就是双指针法…...

真实需求和梦想实现满足
多少的时光和岁月中都不曾认真系统的深度思考自己的真实需求和欲望之间是否一致,随着时间的流逝才发现自己追求的是一场空,自己的真实需求并不是苦苦追求的东西,这也是当梦想照进现实!欲望是无善无恶的,不必为了满足自…...

[ant-design-vue] tree 组件功能使用
[ant-design-vue] tree 组件功能使用描述环境信息相关代码参数说明描述 是希望展现一个树形的菜单,并且对应的菜单前有复选框功能,但是对比官网的例子,我们在使用的过程中涉及到对半选中情况的处理: 半选中状态: 选中…...
QT父子窗口事件传递与事件过滤器
处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现了事件方法,就直接处理掉事件了,没有进到Main…...
【2.4 golang中的循环语句for】
- 循环语句for 1. 循环语句for 1.1.1. Golang for支持三种循环方式,包括类似 while 的语法。 for循环是一个循环控制结构,可以执行指定次数的循环。 语法 Go语言的For循环有3中形式,只有其中的一种使用分号。 for init; condition; pos…...
Windows 系统下 Apache 和 php 环境怎么搭建?
传统搭建方法在 Windows 系统下,可以通过以下步骤来搭建 Apache 和 PHP 环境:1.下载 Apache 在 Apache 官网下载最新版本的 Apache,例如下载地址为 https://httpd.apache.org/download.cgi。2.安装 Apache 将下载的 Apache 压缩包解压到某个目…...

Python基础知识——字符串、字典
字符串 在Python中,字符和字符串没有区别。可能有些同学学过其他的语言,例如Java,在Java中,单引号’a’表示字符’a’,双引号"abc"表示字符串"abc",但在Python当中,它们没…...

JVM常用指令
JVM常用指令1.准备工作2.jps3. jconsole4.jstat5.jstack6.jmap7.jvisualvm工具8.自动dump内存信息1.准备工作 在idea中编写代码 public class JVMTest {Testpublic void test() throws InterruptedException {while (true) {Thread.sleep(1000);System.out.println(123);}} }…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...