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…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
