Java 每日一刊(第15期):内部类

在这里插入图片描述

文章目录

    • 前言
    • 内部类
      • 成员内部类(Member Inner Class)
      • 静态内部类(Static Nested Class)
      • 局部内部类(Local Inner Class)
      • 匿名内部类(Anonymous Inner Class)
    • 内部类的详细对比
    • 内部类字节码文件
      • 文件命名规则
      • 字节码结构上的区别
    • 内部类的典型使用场景
      • GUI 事件监听器
      • 数据封装与实现隐藏
      • 构建复杂对象(Builder 模式)
      • 访问外部类的私有成员
      • 封装特定行为的组合逻辑
    • 本期小知识

前言

这里是分享 Java 相关内容的专刊,每日一更。

本期将为大家带来以下内容:

  1. 内部类
  2. 内部类的详细对比
  3. 内部类字节码文件
  4. 内部类的典型使用场景

内部类

Java 内部类是一种将类嵌套在另一个类内部的编程结构。内部类可以访问外部类的成员(包括私有成员),从而形成了类与类之间更紧密的关联。Java 内部类有四种主要类型:

  1. 成员内部类(Member Inner Class)
  2. 静态内部类(Static Nested Class)
  3. 局部内部类(Local Inner Class)
  4. 匿名内部类(Anonymous Inner Class)

成员内部类(Member Inner Class)

成员内部类是指在另一个类中定义的类,并且不使用 static 修饰。它作为外部类的一个成员,可以和外部类的成员变量、方法共存。

特点:

  • 成员内部类拥有外部类的所有非静态成员的访问权,包括 private 成员。
  • 成员内部类的实例与外部类的实例相关联,只有在 外部类的实例 存在的情况下才能创建成员内部类的实例。

使用场景: 适用于某些内部逻辑只和外部类强相关的情况。通过成员内部类,内部的逻辑更加封闭且紧密绑定外部类。

语法:

class Outer {private String name = "Outer";class Inner {void display() {System.out.println("Outer name: " + name);}}
}

实例化: 成员内部类需要通过外部类的对象进行实例化:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();

静态内部类(Static Nested Class)

静态内部类是用 static 关键字修饰的内部类。由于它是静态的,它独立于外部类的实例存在。

特点:

  • 静态内部类不能访问外部类的非静态成员,只能访问外部类的静态成员。
  • 它的实例不依赖外部类的实例,可以直接创建。

使用场景: 当内部类逻辑与外部类没有强依赖关系时,可以使用静态内部类。典型场景包括实现工具类、嵌套数据结构等。

语法:

class Outer {private static String name = "Outer";static class StaticInner {void display() {System.out.println("Outer name: " + name);}}
}

实例化: 静态内部类可以直接通过类名实例化:

Outer.StaticInner inner = new Outer.StaticInner();
inner.display();

局部内部类(Local Inner Class)

局部内部类是定义在方法或代码块中的类,它的作用域仅限于方法或代码块内。

特点:

  • 局部内部类可以访问方法中的局部变量,前提是这些变量必须被声明为 final 或“事实上是 final”。
  • 它的生命周期与方法的执行周期一致,方法执行完毕后,局部内部类将不再可用。

使用场景: 局部内部类适用于仅在特定方法中使用,逻辑较为局限的场景,如处理复杂算法的临时类。

语法:

class Outer {void method() {final String greeting = "Hello";class LocalInner {void printGreeting() {System.out.println(greeting);}}LocalInner localInner = new LocalInner();localInner.printGreeting();}
}

注意: 由于局部内部类的作用范围仅限于定义它的代码块,因此它 只能 在方法或代码块内部使用。

匿名内部类(Anonymous Inner Class)

匿名内部类是一种特殊的内部类,它没有名字,通常用于简化代码,尤其是在需要临时实现一个接口或者继承一个类时。匿名内部类直接创建类的实例,并实现或继承该类。

特点:

  • 匿名内部类是一次性的,不能重复使用。
  • 可以继承一个类或实现一个接口。
  • 它通常用于简化代码,在特定的上下文中快速定义并使用一个类。

使用场景: 常见于回调机制、事件监听器等场景,或者需要快速实现某个类或接口的地方。

语法:

interface Greeting {void sayHello();
}public class Test {public static void main(String[] args) {// 在这里,匿名类实现了 Greeting 接口,并在创建时就直接定义了它的行为。Greeting greeting = new Greeting() {@Overridepublic void sayHello() {System.out.println("Hello, World!");}};greeting.sayHello();}
}

此处有点难以理解,我们进行进一步解释:

new Greeting() 表示创建对象,可是 Greeting 是接口,所以单独使用 new Greeting() 是错误的 。我们仔细观察上面的代码,发现在 new Greeting() 的后面紧跟着一个 {}。这个紧跟着的 {} 表示的是一个匿名类(没有名字的类)。为什么呢?

class Hello {}

上面是一个标准类的定义,有 class 关键字和类名,而前面的匿名类没有使用 class 关键字和类名,所以被叫做匿名类。

注意:只有在 创建对象时 紧跟着的 {} 才被称作匿名类,其他地方的 {} 并不是。

内部类的详细对比

特性成员内部类(Member Inner Class)静态内部类(Static Nested Class)局部内部类(Local Inner Class)匿名内部类(Anonymous Inner Class)
类名有类名有类名有类名无类名
访问外部类成员可以访问外部类的所有成员只能访问外部类的静态成员可以访问外部类的所有成员可以访问外部类的所有成员
实例化方式需要通过外部类实例不需要外部类实例在方法中实例化创建时直接实例化
使用场景用于强关联类用于工具类或静态数据结构局限于局部方法快速实现接口或继承类
生命周期与外部类的实例相同与外部类无关与方法的生命周期一致生命周期短,随用随销毁

内部类字节码文件

Java 编译器在编译内部类时会生成与普通类不同的字节码文件。具体区别如下:

文件命名规则

普通类:外部类和普通类的 .class 文件直接使用类名命名,如 Outer.class

内部类:编译器为内部类生成的 .class 文件使用 外部类名$内部类名.class 的格式。例如:

  • 对于成员内部类 Outer.Inner,生成的字节码文件是 Outer$Inner.class
  • 对于匿名内部类,Java 编译器会生成类似 Outer$1.class 这样的文件,其中的数字表示匿名类出现的顺序。
  • 对于局部内部类,它的 .class 文件命名也是基于外部类的名称,但同样会根据顺序编号来标识不同的局部类。

字节码结构上的区别

内部类访问外部类的成员:当内部类访问外部类的成员(尤其是私有成员)时,Java 编译器在生成字节码时会自动为内部类生成一个指向外部类的隐式引用。因此,内部类的构造函数中会隐含一个指向外部类的引用,这样才能在运行时通过这个引用访问外部类的成员。

例如,成员内部类 Inner 会持有对外部类 Outer 的引用(Outer.this),这是字节码文件中的一个额外字段。

  • 静态内部类与普通类没有区别:静态内部类由于没有外部类的隐式引用,因此它的 .class 文件与普通类的结构更为相似。
  • 匿名内部类:匿名内部类的 .class 文件中不会有类的名称,它直接在生成的字节码中定义类的行为。此外,由于匿名内部类通常用来实现接口或继承类,它会包含接口或父类的方法实现。

内部类的典型使用场景

GUI 事件监听器

在 Java 的 GUI 编程中(如 SwingAWT),事件监听器通常用匿名内部类来实现。事件监听器需要实现一个单一的方法,如按钮点击事件。

button.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("Button clicked!");}
});

使用匿名内部类可以避免定义一个新的类,并且保持代码的简洁和紧凑。

数据封装与实现隐藏

内部类经常用于隐藏一些不需要对外公开的实现细节。例如,在一个复杂的数据结构如 HashMap 中,内部使用了很多私有类(如 Entry),这些类只为外部类服务,并不需要单独暴露给外部用户。

class HashMap {private static class Entry {final int key;String value;Entry next;Entry(int key, String value) {this.key = key;this.value = value;}}
}

通过将 Entry 作为静态内部类,HashMap 可以封装其内部实现,保证用户无法直接操作这些内部类的结构。

构建复杂对象(Builder 模式)

静态内部类常用于构建者模式(Builder Pattern),通过静态内部类实现链式调用以构建复杂对象。这种模式提高了代码的可读性和灵活性。

class Product {private String name;private int price;private Product(Builder builder) {this.name = builder.name;this.price = builder.price;}public static class Builder {private String name;private int price;public Builder setName(String name) {this.name = name;return this;}public Builder setPrice(int price) {this.price = price;return this;}public Product build() {return new Product(this);}}
}

访问外部类的私有成员

当内部类需要访问外部类的私有成员时,使用成员内部类是最直接的方式。由于内部类持有对外部类实例的隐式引用,它可以直接操作外部类的成员,无需通过 getter 或 setter 方法。

class Outer {private int data = 10;class Inner {void display() {// 直接访问外部类的私有成员System.out.println("Data from outer: " + data); }}
}

封装特定行为的组合逻辑

在面向对象设计中,有时内部类用于封装某些行为,它们仅仅是外部类逻辑的一部分。这种模式可以帮助保持外部类的清晰,而将复杂的逻辑隔离到内部类中。

class TaskManager {class Task {String name;Task(String name) {this.name = name;}void execute() {System.out.println("Executing task: " + name);}}void runTask(String name) {Task task = new Task(name);task.execute();}
}

本期小知识

由于 成员内部类 会持有外部类的引用,如果内部类对象的生命周期比外部类对象长,可能会导致 内存泄漏

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1542930.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

新增用户 开发

原型分析 接口设计 数据库设计 代码开发 controller /*** 新增员工** param employeeDTO* return*/ApiOperation("新增员工")PostMappingpublic Result<String> save(RequestBody EmployeeDTO employeeDTO) {log.info("新增员工&#xff1a;{}", emp…

C++离线查询

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 概念及原理 离线算法( offline algorithms)&#xff0c;离线计算就是在计算开始前已知所有输入数据&#xff0c;输入数据不会产生变化&#xff0c;且在解决一个问题后就要立即得出结果的前提下进行的计算。 通俗的说&a…

智能优化算法-遗传算法(GA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 遗传算法 (Genetic Algorithm, GA) 是一种基于自然选择和遗传学原理的元启发式优化算法&#xff0c;它模仿了生物进化过程中的选择、交叉和变异操作来搜索最优解。 GA的工作机制主要包括&#xff1a; 选择&am…

73 矩阵置零

解题思路&#xff1a; \qquad 原地算法&#xff0c;指除原有输入资料所占空间外&#xff0c;使用额外空间尽可能少(常数空间)的算法。本题容易想到的一种解法是&#xff0c;对于m x n的矩阵&#xff0c;一次遍历把含有0元素的行号、列号记录下来&#xff0c;然后再一次遍历把对…

中序遍历二叉树全过程图解

文章目录 中序遍历图解总结拓展&#xff1a;回归与回溯 中序遍历图解 首先看下中序遍历的代码&#xff0c;其接受一个根结点root作为参数&#xff0c;判断根节点是否为nil&#xff0c;不为nil则先递归遍历左子树。 func traversal(root *TreeNode,res *[]int) {if root nil …

阿⾥编码规范⾥⾯Manager分层介绍-专⽤名词和POJO实体类约定

开发⼈员&#xff1a;张三、李四、王五 ⼀定要避免单点故障 ⼀个微服务起码两个⼈熟悉&#xff1a;⼀个是主程⼀个是技术leader 推荐是团队⾥⾯两个开发⼈员 N⽅库说明 ⼀⽅库: 本⼯程内部⼦项⽬模块依赖的库(jar 包)⼆⽅库: 公司内部发布到中央仓库&#xff0c;可供公司…

计算机毕业设计推荐-基于python的白酒销售数据可视化分析

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、白酒销售数据…

记一次Meilisearch轻量级搜索引擎使用

以前使用的是mysql的全文索引、最开始还行。后续觉得就不好用了&#xff0c;但是服务器资源有限&#xff0c;没法上ES&#xff0c;只好找一个轻量级的搜索引擎、找了半天&#xff0c;决定使用这一个&#xff0c;目前效果还不错的。 参考网址 官网&#xff1a;https://www.meil…

基于单片机的智能小车的开发与设计

摘要&#xff1a;本文论述了基于 STC89C52 单片机的智能小车的开发与设计过程。该设计采用单片机、电机驱动及光电循迹等技术&#xff0c;保证小车在无人管理状态下&#xff0c;能按照预先设定的线路实现自动循迹功能。在电路结构设计中力求方便&#xff0c;可操作&#xff0c;…

麦克斯韦方程组

目录 1. 高斯定律&#xff08;电场部分&#xff09; 2. 高斯定律&#xff08;磁场部分&#xff09; 3. 法拉第电磁感应定律 4. 安培定律&#xff08;带有位移电流项&#xff09; 5.麦克斯韦方程组的物理意义 麦克斯韦方程组为我们提供了一个完整的电磁场理论框架&#xff…

[Meachines] [Medium] Querier XLSM宏+MSSQL NTLM哈希窃取(xp_dirtree)+GPP凭据泄露

信息收集 IP AddressOpening Ports10.10.10.125TCP:135, 139, 445, 1433, 5985, 47001, 49664, 49665, 49666, 49667, 49668, 49669, 49670, 49671 $ nmap -p- 10.10.10.125 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 135/tcp open msrp…

CentOS 7 YUM源不可用

CentOS 7 操作系统在2024年6月30日后将停止官方维护&#xff0c;并且官方提供的YUM源将不再可用。 修改&#xff1a;nano /etc/yum.repos.d/CentOS-Base.repo # CentOS-Base.repo [base] nameCentOS-$releasever - Base baseurlhttp://mirrors.aliyun.com/centos/$rel…

【Unity Shader】Special Effects(九)Vortex 旋涡(UI)

源码:[点我获取源码] 索引 Vortex 旋涡思路分析旋涡中心旋涡旋转旋涡强度旋涡动画Vortex 旋涡 旋涡效果可以将一张图像以指定点作为旋涡中心,呈顺时针旋涡动画效果,使用动画播放器: 思路分析 首先,旋涡特效的核心也即是旋转(特别是uv坐标的旋转); 在此基础上,旋涡中…

二叉搜索树(BSTree)原理及应用场景

目录 引言 二叉搜索树的基本概念 常见算法 插入节点 查找节点 删除节点 二叉搜索树的应用场景 1. 数据库索引 2. 符号表 3. 字典和词汇表 4. 动态集合 结论 引言 二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;是一种特殊的二叉树&#xff0c;其每个节…

C语言 | Leetcode C语言题解之第429题N叉树的层序遍历

题目&#xff1a; 题解&#xff1a; #define MAX_LEVE_SIZE 1000 #define MAX_NODE_SIZE 10000int** levelOrder(struct Node* root, int* returnSize, int** returnColumnSizes) {int ** ans (int **)malloc(sizeof(int *) * MAX_LEVE_SIZE);*returnColumnSizes (int *)mal…

【Android】DataBinding的运用

引言 之前对databinding有了基础的运用与介绍&#xff0c;但databinding的用处不单单在于Text的绑定&#xff0c;接下来就一起看看吧&#xff01; 意义&#xff1a;让布局文件承担了部分原本属于页面的工作&#xff0c;使页面与布局耦合度进一步降低。允许用户界面&#xff0…

计算机的错误计算(一百零一)

摘要 展示 在0附近数的函数值的计算精度问题。 计算机的错误计算&#xff08;一百&#xff09;探讨了 在一般情形下的计算精度问题。本节讨论其在0附近的数的函数值的计算精度问题。 例1. 已知 计算 不妨在Python 3.12.5下计算&#xff0c;则有 若在线运行R代码&#x…

使用 Higress AI 插件对接通义千问大语言模型

前言 什么是 AI Gateway AI Gateway 的定义是 AI Native 的 API Gateway&#xff0c;是基于 API Gateway 的能⼒来满⾜ AI Native 的需求。例如&#xff1a; 将传统的 QPS 限流扩展到 token 限流。将传统的负载均衡/重试/fallback 能力延伸&#xff0c;支持对接多个大模型厂…

0基础学习PyTorch——最小Demo

大纲 环境准备安装依赖 训练和推理训练生成数据加载数据TensorDatasetDataLoader 定义神经网络定义损失函数和优化器训练模型 推理 参考代码 PyTorch以其简洁直观的API、动态计算图和强大的社区支持&#xff0c;在学术界和工业界都享有极高的声誉&#xff0c;成为许多深度学习爱…

C++入门基础知识80(实例)——实例5【查看 int, float, double 和 char 变量大小】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 实例 【查看 int, float, double 和 c…