Java 面向对象进阶:解锁多态、内部类与包管理
Java 面向对象进阶:解锁多态、内部类与包管理 🔑
在 Java 的面向对象编程中,多态赋予了对象“多种形态”的能力,内部类提供了更精细的代码组织方式,而包则帮助我们管理和组织大量的类。今天,我们将深入探讨这些概念,理解它们在实际开发中的应用。
🎭 多态:对象的多种身份
多态性是面向对象编程的三大特性之一(封装、继承、多态)。它允许我们使用一个父类引用来指向子类的对象,从而在运行时根据对象的实际类型执行不同的操作。
-
向上造型 / 自动类型转换 (Upcasting):
- 概念:超类型的引用指向派生类的对象。这是自动发生的,不需要强制转换。
- 规则:能“点”出来什么,看引用的类型。虽然对象实际是子类,但通过父类引用只能访问父类中定义的成员(包括继承自父类的)。子类特有的成员无法直接通过父类引用访问。这是一个重要的规定,请记住它。
- 可以向上造型成为的类型:一个对象可以向上转型成为它所继承的类,以及它所实现的接口。
Animal o2 = new Dog("小黑",2,"黑"); // 狗是动物 - 向上造型 // 通过 o2 只能访问 Animal 类中定义的成员,例如 o2.name, o2.eat() 等 // 无法直接访问 Dog 类特有的 lookHome() 方法 Swim o3 = new Dog("小黑",2,"黑"); // 狗会游泳 - 向上造型 // 通过 o3 只能访问 Swim 接口中定义的方法,例如 o3.swim()
-
向下转型 / 强制类型转换 (Downcasting):
- 概念:将一个超类型的引用强制转换为派生类的引用。
- 成功的条件:强制类型转换成功的条件只有两种:
- 引用实际指向的对象,就是该目标类型。
- 引用实际指向的对象,实现了该目标接口或继承了该目标类。
- 类型转换异常 (ClassCastException):如果强制类型转换不符合上述条件,就会在运行时发生
ClassCastException
异常。 - 建议:在进行向下转型之前,先使用
instanceof
关键字来判断引用所指向的对象是否是目标类型。 instanceof
:返回一个布尔值,表示引用所指向的对象是否是某个类型(类或接口)的实例。instanceof
返回true
的条件,就是强制类型转换成功的条件。
Animal o = new Dog("小黑",2,"黑"); // 向上造型 Dog g = (Dog)o; // 引用 o 所指向的对象就是 Dog 类型,强制转换成功 Swim s = (Swim)o; // 引用 o 所指向的对象实现了 Swim 接口,强制转换成功 // Fish f = (Fish)o; // 运行时会发生 ClassCastException,因为 o 实际指向的是 Dog 对象,而不是 Fish 对象System.out.println(o instanceof Dog); // true System.out.println(o instanceof Swim); // true System.out.println(o instanceof Fish); // falseif(o instanceof Fish){ // 先判断,避免异常Fish f = (Fish)o;// 可以安全地使用 f 引用访问 Fish 特有的成员 }
- 何时需要强转:当你需要访问对象中在超类中没有定义的,而只在派生类或实现的接口中特有的属性或行为时,就需要进行强制类型转换。
-
多态的实际应用场景:
- 统一处理不同子类对象:将不同子类对象统一封装到超类数组中进行遍历和操作,实现代码复用。例如,将不同类型的动物放入一个
Animal
类型的数组中,然后循环调用它们的eat()
和drink()
方法,即使具体的实现不同(重写),代码结构依然简洁。 - 方法的参数或返回值类型:将超类型(类或接口)作为方法的参数类型或返回值类型,可以使方法接收或返回各种不同的子类对象,扩大方法的应用范围,提高代码的灵活性和复用性。例如,
Master
类中的feed(Animal animal)
方法,可以喂养任何Animal
的子类对象。
// 演示向上造型(多态)的应用: Animal[] animals = new Animal[5]; animals[0] = new Dog("小黑",2,"黑"); // 向上造型 animals[1] = new Dog("小白",1,"白"); animals[2] = new Fish("小金",1,"金"); animals[3] = new Fish("小花",2,"花"); animals[4] = new Chick("小灰",3,"灰");for(int i=0;i<animals.length;i++){ // 遍历所有动物System.out.println(animals[i].name);animals[i].drink();animals[i].eat(); // 多态:根据实际对象调用对应的 eat() 方法if(animals[i] instanceof Dog){ // 判断是否为 Dog 对象,进行向下转型以访问特有方法Dog dog = (Dog)animals[i];dog.lookHome();}if(animals[i] instanceof Chick){ // 判断是否为 Chick 对象Chick chick = (Chick)animals[i];chick.layEggs();}if(animals[i] instanceof Swim){ // 判断是否实现了 Swim 接口Swim s = (Swim)animals[i];s.swim(); // 多态:调用实际对象实现的 swim() 方法} }
- 统一处理不同子类对象:将不同子类对象统一封装到超类数组中进行遍历和操作,实现代码复用。例如,将不同类型的动物放入一个
🚪 成员内部类:服务于外部类
成员内部类是定义在另一个类内部的非静态类。它通常只服务于其外部类,对外不可见。
- 类中套类:外面的类称为外部类,里面的类称为内部类。
- 可见性:内部类通常只服务于外部类,默认情况下对外不具备可见性。
- 创建对象:内部类对象通常在外部类中创建。
- 访问外部类成员:内部类可以直接访问外部类的所有成员(包括私有的)。在内部类中,有一个隐式的引用指向创建它的外部类对象。
- 这个隐式的引用可以通过
外部类名.this
来访问。 - 例如:
System.out.println(Mama.this.name);
- 这个隐式的引用可以通过
- 何时使用:当一个类 (A 类,例如
Baby
) 只应该被另一个类 (B 类,例如Mama
) 使用,并且 A 类还需要访问 B 类的成员时,可以考虑设计成员内部类。
// 成员内部类示例
public class InnerClassDemo {public static void main(String[] args) {Mama m = new Mama();// Baby b = new Baby(); // 编译错误,内部类对外不具备可见性}
}class Mama{ // 外部类String name = "我是妈妈";void create(){Baby b = new Baby(); // 正确,内部类对象通常在外部类中创建b.show();}class Baby{ // 成员内部类void show(){System.out.println(name); // 简写,内部类直接访问外部类成员System.out.println(Mama.this.name); // 完整写法,通过 Mama.this 指代外部类对象// System.out.println(this.name); // 编译错误,this 在这里指代当前的 Baby 对象,Baby 类没有 name 成员}}
}
🤫 匿名内部类:简化一次性使用
匿名内部类是一种特殊的内部类,它没有名字。主要用于创建一个派生类的对象,并且该对象只创建一次。使用匿名内部类可以大大简化代码。
- 何时使用:当你需要创建一个接口的实现类或一个类的子类的对象,并且只需要创建一次这个对象时,可以使用匿名内部类。
- 语法:
接口名/类名 对象名 = new 接口名/类名() {// 匿名内部类的类体// 实现接口中的抽象方法 或 重写父类方法// 可以有自己的成员,但通常不常见 };
- 注意:匿名内部类中不能修改外部局部变量的值。这是因为编译器会默认将匿名内部类中访问到的外部局部变量变为
final
的。 - 小面试题:内部类有独立的
.class
文件吗?- 答:有。编译器会为匿名内部类生成一个带有特殊命名规则的
.class
文件。
- 答:有。编译器会为匿名内部类生成一个带有特殊命名规则的
// 匿名内部类示例
public class AnonInnerClassDemo {public static void main(String[] args) {// 使用匿名内部类实现 InterInter 接口InterInter o3 = new InterInter(){public void show(){ // 实现 InterInter 接口中的抽象方法 show()System.out.println("showshow");// num = 6; // 编译错误,不能修改外部局部变量 num}};o3.show(); // 调用匿名内部类中实现的 show() 方法int num = 5;// num = 6; // 这里修改 num 是合法的}
}interface InterInter{void show();
}
📦 package 与 import:组织和引用代码
随着项目规模的增大,类会越来越多。package
和 import
关键字帮助我们组织和管理这些类,避免命名冲突。
-
package:声明包
- 作用:将相关的类组织在一起,形成一个“包”,从而避免类的命名冲突。
- 规定:同一个包中的类不能同名,但不同包中的类可以同名。
- 类的全称:一个类的完整名称包括包名和类名,格式为
包名.类名
。包名常常具有层次结构,用点 (.
) 分隔。 - 建议:包名所有字母都小写。
package
语句必须是 Java 源文件的第一条非注释语句。
-
import:导入类
- 当一个类需要使用不同包中的其他类时,不能直接访问,需要先导入。
- 方式 1 (建议):使用
import
关键字导入需要使用的类。import java.util.Scanner; // 导入 java.util 包下的 Scanner 类 // 现在可以直接使用 Scanner 类名 Scanner scan = new Scanner(System.in);
- 方式 2 (不建议):使用类的全称来访问。这种方式太繁琐,不建议在代码中大量使用。
java.util.Scanner scan = new java.util.Scanner(System.in); // 使用类的全称
- 导入同一个包下的多个类:可以使用通配符
*
来导入包下的所有类,例如import java.util.*;
。但这通常不如明确导入所需的类那样清晰。 import
语句必须放在package
语句之后,所有类定义之前。
补充:
- 隐式的引用总结:
this
:指代当前对象。super
:指代当前对象的超类对象。外部类名.this
:指代当前内部类对象所依赖的外部类对象。
- 关键字顺序:在一个 Java 源文件中,关键字的顺序通常是:先
package
,再import
,最后class
定义。
多态、内部类和包管理是 Java 中非常实用的特性。理解并熟练运用它们,能够帮助我们编写出更加灵活、可维护和结构清晰的代码。继续练习,你会越来越得心应手!
总结
- 多态:多种形态
- 向上造型:超类型的引用指向派生类的对象
- 向下转型,成功的条件:
2.1) 引用所指向的对象,就是该类型
2.2) 引用所指向的对象,实现了该接口或继承了该类
- 匿名内部类:
- 若想创建一个派生类的对象,并且对象只创建一次,可以设计为匿名内部类
可以大大简化代码
- 若想创建一个派生类的对象,并且对象只创建一次,可以设计为匿名内部类
- package 用于声明包,import 用于导入类
提示
- 什么是多态
- 多态的应用场景有哪些
- 匿名内部类的应用场景有哪些
- package 和 import 的作用
- 不同包中的类如何访问
相关文章:
Java 面向对象进阶:解锁多态、内部类与包管理
Java 面向对象进阶:解锁多态、内部类与包管理 🔑 在 Java 的面向对象编程中,多态赋予了对象“多种形态”的能力,内部类提供了更精细的代码组织方式,而包则帮助我们管理和组织大量的类。今天,我们将深入探讨…...

算法:分治法
实验内容 在一个2kⅹ2k个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为特殊方格,且称该棋盘为一特殊棋盘。 显然,特殊方格出现的位置有4k 种情况,即k>0,有4k 种不同的特殊棋盘 棋盘覆盖:…...

MySQL初阶:sql事务和索引
索引(index) 可以类似理解为一本书的目录,一个表可以有多个索引。 索引的意义和代价 在MySQL中使用select进行查询时会经过: 1.先遍历表 2.将条件带入每行记录中进行判断,看是否符合 3.不符合就跳过 但当表中的…...

docker部署第一个Go项目
1.前期准备 目录结构 main.go package mainimport ("fmt""github.com/gin-gonic/gin""net/http" )func main() {fmt.Println("\n .::::.\n .::::::::.\n :::::::::::\n …...
day27 python 装饰器
目录 一、装饰器的基本概念 示例:用装饰器优化质数查找函数 二、装饰器的高级用法 1. 支持任意参数的装饰器 2. 装饰器的返回值处理 在 Python 编程中,装饰器是一个非常强大的功能,它可以让其他函数或方法在不需要做任何代码修改的前提下…...

Visual Studio2022跨平台Avalonia开发搭建
由于我已经下载并安装了 VS2022版本,这里就跳过不做阐述。 1.安装 Visual Studio 2022 安装时工作负荷Tab页勾选 “.NET 桌面开发” 和“Visual Studio扩展开发” ,这里由于不是用的微软的MAUI,所以不用选择其他的来支持跨平台开发&a…...

css iconfont图标样式修改,js 点击后更改样式
背景: 在vue项目中,通过点击/鼠标覆盖,更改选中元素的样式,可以通过js逻辑,也可以根据css样式修改。包括以下内容:iconfont图标的引入以及使用,iconfont图标样式修改【导入文件是纯白࿰…...

开源项目实战学习之YOLO11:12.4 ultralytics-models-sam-memory_attention.py源码分析
👉 点击关注不迷路 👉 点击关注不迷路 👉 另外,前些天发现了一个巨牛的AI人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。感兴趣的可以点击相关跳转链接。 点击跳转到网站。 ultralytics-models-sam 1.sam-modules-memory_attention.pyblocks.py: 定义模…...

【沉浸式求职学习day42】【算法题:滑动窗口】
沉浸式求职学习 长度最小的子数组水果成篮 关于算法题:滑动窗口的几个题目 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组…...

LIIGO ❤️ RUST 12 YEARS
LIIGO 💖 RUST 12 YEARS 今天是RUST语言1.0发布十周年纪念日。十年前的今天,2015年的今天,Rust 1.0 正式发行。这是值得全球Rust支持者隆重纪念的日子。我借此机会衷心感谢Rust语言创始人Graydon Hoare,Mozilla公司,以…...

Linux基础开发工具二(gcc/g++,自动化构建makefile)
3. 编译器gcc/g 3.1 背景知识 1. 预处理(进行宏替换/去注释/条件编译/头文件展开等) 2. 编译(生成汇编) 3. 汇编(生成机器可识别代码) 4. 连接(生成可执行文件或库文件) 3.2 gcc编译选项 格式 : gcc …...
Linux zip、unzip 压缩和解压
zip 命令用于压缩文件,压缩后的文件后缀名为 .zip 。 对应的解压命令是 unzip 。 测试用的目录结构如下, userzn:~/test$ tree . ├── folder1 │ ├── folder111 │ │ └── file1.txt │ └── main1.c ├── folder2 │ ├── …...
muduo库TcpConnection模块详解——C++
muduo库中的TcpConnection模块详解 TcpConnection是muduo库中处理TCP连接的核心模块,负责管理单个TCP连接的生命周期、数据读写、状态转换以及事件回调。每个TCP连接对应一个TcpConnection对象,其设计体现了高性能、线程安全和灵活回调的特点。 一、核心…...
Node.js 源码架构详解
Node.js 的源码是一个庞大且复杂的项目,它主要由 C 和 JavaScript 构成。要完全理解每一部分需要大量的时间和精力。我会给你一个高层次的概述,并指出一些关键的目录和组件,帮助你开始探索。 Node.js 的核心架构 Node.js 的核心可以概括为以…...

全局异常处理:如何优雅地统一管理业务异常
在软件开发中,异常处理是保证系统健壮性的重要环节。一个良好的异常处理机制不仅能提高代码的可维护性,还能为使用者提供清晰的错误反馈。本文将介绍如何通过全局异常处理和业务异常统一处理来编写更加优雅的代码。 一、传统异常处理的痛点 1.1 典型问…...
分布式锁: Redis和ZooKeeper两种分布式锁对比
在分布式系统中,分布式锁是协调多节点共享资源访问的核心机制。Redis 和 ZooKeeper 是两种常用的分布式锁实现方案,但两者的设计理念、适用场景和优缺点存在显著差异。本文将从 一致性模型、性能、可靠性、实现原理 等维度进行对比,并提供技术…...

动态规划-LCR 166.珠宝的最大价值-力扣(LeetCode)
一、题目解析 frame二维矩阵中每个值代表珠宝的价值,现在从左上角开始拿珠宝,只能向右或向下拿珠宝,到达右下角时停止拿珠宝,要求拿的珠宝价值最大。 二、算法解析 1.状态表示 我们想要知道的是到达[i,j]为位置时的最大价值&am…...

JDBC实现模糊、动态与分页查询的详解
文章目录 一. 模糊查询1. Mysql的写法2. JDBC的实现 二. 动态条件查询1. 创建生成动态条件查询sql的方法2. 完整的动态条件查询类以及测试类 三. 分页查询1. 什么是分页查询?2. 分页查询的分类3. MySQL的实现4. JDBC实现4.1. 创建page页4.2. 分页的实现 本章来讲一下…...

域环境信息收集技术详解:从基础命令到实战应用
引言 在企业网络环境中,Active Directory (AD)域服务是微软提供的集中式目录服务,用于管理网络中的用户、计算机和其他资源。对于信息安全专业人员来说,熟练掌握域环境信息收集技术至关重要,无论是进行渗透测试、安全评估还是日常…...
nodejs特性解读
单线程和事件驱动架构 参考 程序员dd-事件驱动架构 it-老齐-事件驱动架构 总结...

【C++ Qt】布局管理器
每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 🤔绪论: 在Qt开发中,界面布局的合理设计是提升用户体验的关键。早期,开发者常采用绝对定位的方式摆放控件,即通…...

vscode用python开发maya联动调试设置
如何在VScode里编写Maya Python脚本_哔哩哔哩_bilibili1 包括1,maya的python全面在vscode支持,2,通过mayacode发送到maya,3同步调试 import maya.cmds as cmds 1、让 maya.cmds编译通过 下载Autodesk_Maya_2018_6_Update_DEVK…...

SLAM定位常用地图对比示例
序号 地图类型 概述 1 格栅地图 将现实环境栅格化,每一个栅格用 0 和 1 分别表示空闲和占据状态,初始化为未知状态 0.5 2 特征地图 以点、线、面等几何特征来描绘周围环境,将采集的信息进行筛选和提取得到关键几何特征 3 拓扑地图 将重要部分抽象为地图,使用简单的图形表示…...
Ubnutu ADB 无法识别设备的解决方法
1. 正确安装adb 下载地址 2. 检查 Linux 是否识别设备 lsusb通过上述指令,分别查询插入、断开设备的usb设备表,如下所示: # 插入设备 adbc:~$ lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 011:…...
前端-HTML元素
目录 HTML标签是什么? 什么是HTML元素? HTML元素有哪些分类方法? 什么是HTML头部元素 更换路径 注:本文以leetbook为基础 HTML标签是什么? HTML标签是HTML语言中最基本单位和重要组成部分 虽然它不区分大小写&a…...
dagster的etl实现
本文展示了如何使用Dagster框架实现一个动态ETL(Extract, Transform, Load)流程。通过定义多个操作(op),包括生成动态任务、处理单个任务、收集结果和汇总结果,构建了一个动态任务处理流程。generate_tasks…...

python的漫画网站管理系统
目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核…...

源码安装gperftools工具
源码安装gperftools工具 下载gperftools源码 https://github.com/gperftools/gperftools/releases/download/gperftools-2.16/gperftools-2.16.tar.gz 注:需要下载github上release版本,如果直接下载master分支上源码,将可能出现各种编译报错…...
QMK 宏(Macros)功能详解(实战部分)
QMK 宏(Macros)功能详解(实战部分) 一、宏的基本概念与作用 宏(Macros)是 QMK 固件中一项强大的功能,它允许您在按下单个按键时执行多个按键操作。通过宏,您可以: 输入常用短语或文本执行复杂的按键组合自动化重复性操作触发系统功能或快捷键🔔 安全提示:虽然可以…...

前端脚手架开发指南:提高开发效率的核心操作
前端脚手架通过自动化的方式可以提高开发效率并减少重复工作,而最强大的脚手架并不是现成的那些工具而是属于你自己团队量身定制的脚手架!本篇文章将带你了解脚手架开发的基本技巧,帮助你掌握如何构建适合自己需求的工具,并带着你…...