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);}} }…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
