11-面向对象4
1. 类与类之间的关系#
1.1 依赖#
对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。
1.2 关联#
对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达,关联可以有方向,即导航。
关联表示类之间的“持久”关系,这种关系一般表示一种重要的业务之间的关系,需要保存的,或者说需要“持久化”的,保存到数据库中。另外,依赖表示类之间的是 一种“临时、短暂”关系,这种关系是不需要保存的。
1.3 聚合#
聚合(关联关系的一种):表示“has-a”的关系。与关联关系一样,聚合关系也是通过实例变量来实现这样关系的。“关联关系”和“聚合关系”来语法上是没办法区分的,从语义上才能更好的区分两者的区别。
如汽车类与引挚类,轮胎类之间的关系就是整体与个体的关系。与“关联关系”一样,“聚合关系”也是通过「实例变量」来实现的。
关联和聚集(聚合)的区别:
- “关联关系”所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种“关联关系”,而不是“聚合关系”,因为人不是由自行车组成的。
- “聚合关系”涉及的两个对象处于不平等的层次上,一个代表“整体”,一个代表“部分”。比如电脑和它的显示器、键盘、主板以及内存就是“聚集关系”,因为主板是电脑的组成部分。
1.4 组合#
对象 A 包含对象 B,对象 B 离开对象 A 没有实际意义,是一种更强的关联关系。比如,人的结构包含手, 手离开人的躯体就失去了它应有的作用。
表示 contains-a 的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。组合也使用 [属性] 表达组合关系,是“关联关系”的一种,是比“聚合关系”强的关系。
1.5 继承#
Generalization,又称为泛化,is-a 的关系。如:类与类的继承关系,类与接口的实现关系。
场景:父与子、动物与人、植物与树
2. 继承#
2.1 继承概述#
- 为什么要有继承?
- 类继承语法规则:
class Subclass extends SuperClass {...}
- Subclass:子类、派生类
- Superclass:父类、基类、超类
- 作用
- 减少了代码冗余,提高了代码的复用性
- 更有利于功能的扩展
- 继承的出现让类与类之间产生了关系,提供了多态的前提
- 注意
- 不要仅为了获取其他类中某个功能而去继承
- Java 中的继承反应的是我们现实世界中的继承关系吗?
- 不是!Java 中的继承,反应的是“一般到特殊的关系”
- 在 Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的"扩展"。
2.2 继承的规则#
1. 子类不能直接访问父类中私有的(private) 的成员变量和方法
- 【继承性】父类的私有成员在“物理上”已经被继承过来了
- 【封装性】只不过逻辑和语法上设置的不允许被访问
2. Java 只支持类的单继承和多层继承,不允许多重继承。
- 单继承就是一个类只能有一个父类;多继承就是一个类可以有多个父类
- 一个父类可以派生出多个子类
- 子类直接继承的父类,称为“直接父类”;由于多层继承而拥有的父类,称为“间接父类”。子类继承父类之后,就获取了“直接父类”以及“所有间接父类”中声明的属性和方法。
2.3 Object 类基本特性#
当创建一个类时,总是在继承;Object 类是所有 Java 类的根基类,也就意味着所有的 Java 对象都拥有 Object 类的属性和方法。因此,除非已明确指出要从其它类中继承,否则就是在隐式地从 Java 的标准根类 java.lang.Object 进行继承。
4. 方法重写/覆盖#
4.1 定义#
在子类中可以根据需要对从父类中继承来的 (!static) 方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
- 属性没有“覆盖”这一说
- 父类中「私有方法」不能被重写,就算方法声明相同,那这俩也互不相干
- 方法重写是实现“多态”的必要条件
4.2 要求#
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法使用的访问权限 ≥ 父类被重写的方法的访问权限(子类不能重写父类中声明为private权限的方法)
- 子类重写的方法的返回值类型 ≤ 父类被重写的方法的返回值类型(被重写方法若是void/基本类型,重写方法必须是void/基本类型)
- 子类重写的方法抛出的异常 ≤ 父类被重写方法的异常
4.3 补充#
1. 子类在继承父类时,对于方法而言,存在 2 种关系
- override(覆盖):这是对实例方法(instance method) 而言的;子类与父类中形构相同的方法会 override 父类中的那个方法。
- hide(隐藏):这是对类方法(class method) 即 static 方法而言的;如果子类中定义了静态方法,则它会隐藏父类中形构相同的所有方法。
2. static、private 修饰的父类方法不能覆盖
“覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为它的基类型并调用相同的方法。如果某方法为 private,他就不是基类的接口的一部分,它仅是一些隐藏于类中的程序代码,只不过是具有相同的名称而已。但如果在导出类中以相同的名称生成一个 public、protected 或包访问权限方法的话,此时你并没有覆盖该方法,仅是生成了一个新的方法。由于 private 方法无法触及而且能有效隐藏,所以除了把它看作是因为它所归属的类的组织结构的原因而存在外,其他任何事物都不需要考虑到它。
根据上面的规定,“覆盖”的前提是「实例方法」,只有「实例方法」在继承时才会出现 override 情况。 如果是 static 方法,在继承时出现的现象根本就不能用 override 这个词描述,如果 static 方法在父类和子类中形构一致,则被隐藏。那什么是隐藏呢?
隐藏 :child 隐藏了 parent 的变量和方法,那么,child 实例就不能访问 parent 中被隐藏的变量或者方法,但是,将 child 实例向上提升转换成 parent,可以访问 parent 中被隐藏的变量或者方法了。我的理解是被隐藏的东西就是只和类绑定的东西。当子类继承父类,在子类对象中是访问不到父类中的被隐藏的东西的。因为这些东西只和父类绑定了。
5. 测试4种权限修饰符#
- 同包下的不同类
- protected 也提供包访问权限,也就是说,相同包内的其他类可以访问 protected 元素
- 同一个包中的其他类,不可以访问 Order 类中的 private 成员
- 不同包下的其他类,只能访问 Order 类中的 public 成员
- 不同包下的子类
- 在不同包的子类中,不能调用 private 和缺省的成员
- 此时唯一可以访问的成员就是源包的 public 成员。但有时,基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要 protected 来完成这一工作
6. super 关键字#
6.1 super 概述#
有些人认为 super 和 this 引用是类似的概念,实际上,这样类比并不太恰当。这是因为 super 并不是一个对象的引用,不能将 super 赋给另一个对象变量,它只是一个指示编译器调用超类属性和方法的特殊关键字。super 代表的是 父类存储空间的标识。
使用 super 来调用父类中的指定操作:① 访问父类中定义的成员;② 在子类构造器中调用父类的构造器
- super 不能出现在静态方法中(static 是没有 this 的方法,既然没有 this,也就不会有 super)
- 尤其当子父类出现同名成员时,可以用 super 表明调用的是父类中的成员
- super 的追溯不仅限于直接父类,还有间接父类
6.2 调用父类构造器细节#
- 子类中所有的构造器默认都会访问父类中空参的构造器 // 子类初始化之前,一定要先完成父类结构的初始化
- 子类的构造器中,通过 "this(参数列表)" 或者 "super(参数列表)" 指定调用本类或者父类中相应的构造器。同时,只能 "二选一",且必须放在构造器的首行
- 如果子类构造器这两种都想用,可以先调用 "this(参数列表)",再在子类带参构造器中调用 "super(参数列表)"
- 在一个类中的多个构造器,至少有一个构造器是使用了 "super(参数列表)" 的
- 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
创建子类对象时并没有同时创建一个父类对象?可构造器首行为什么要隐式调用 super()?而且子类继承过来的父类属性,又都是存放在哪的?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?