当前位置: 首页 > news >正文

Java内部类、匿名内部类、嵌套类详解

CONTENTS

    • 1. 创建内部类
    • 2. 内部类到外部类的连接
    • 3. 在内部类中生成外部类对象的引用
    • 4. 匿名内部类
    • 5. 嵌套类
    • 6. 接口中的类

1. 创建内部类

创建内部类的方式就是把类定义放在一个包围它的类之中:

package com.yyj;public class Parcel1 {class Contests {private int x = 1;public int value() { return x; }}class Destination {private String dest;Destination(String dest) {this.dest = dest;}String getDest() { return dest; }}public void ship(String dest) {Contests c = new Contests();Destination d = new Destination(dest);System.out.println(d.getDest());}public static void main(String[] args) {
//        Contests c = new Contests();  // 不能这么定义Parcel1 p = new Parcel1();p.ship("XDU");  // XDU}
}

2. 内部类到外部类的连接

到目前为止,内部类看上去就是一种名称隐藏和代码组织机制。当创建一个内部类时,这个内部类的对象中会隐含一个链接,指向用于创建该对象的外围对象。通过该链接,无须任何特殊条件,内部类对象就可以访问外围对象的成员。此外,内部类拥有対外围对象所有元素的访问权:

package com.yyj;interface Selector {boolean end();  // 是否到尾部Object current();  // 返回当前对象void next();  // 指向下一个对象
}public class Sequence {private Object[] objs;private int now = 0;public Sequence(int size) {objs = new Object[size];}public void add(Object x) {  // 添加对象xif (now < objs.length)objs[now++] = x;}private class SequenceSelector implements Selector {  // 用于移动和选择每个元素private int i = 0;@Overridepublic boolean end() { return i == objs.length; }@Overridepublic Object current() { return objs[i]; }@Overridepublic void next() {if (i < objs.length) i++;}}public Selector getSelector() {return new SequenceSelector();}public static void main(String[] args) {Sequence seq = new Sequence(10);for (int i = 0; i < 10; i++)seq.add(Integer.toString(i));  // 向Sequence中加入String对象Selector s = seq.getSelector();while (!s.end()) {System.out.print(s.current() + " ");s.next();}}
}

Sequence 是以类的形式包装起来的定长 Object 数组,可以调用 add() 向序列末尾增加一个新的 Object(如果还有空间)。要取得 Sequence 中的每一个对象,可以使用名为 Selector 的接口,这是迭代器(Iterator)设计模式的一个例子。

SequenceSelector 中的 end()current()next() 这些方法中的每一个都用到了外部类的字段 objs,这个引用并不是 SequenceSelector 的一部分,而是外围对象的一个 private 字段。然而,内部类可以访问外围对象的所有方法和字段,就好像拥有它们一样。

这是怎么做到的呢?对于负责创建内部类对象的特定外围类对象而言,内部类对象偷偷地获取了一个指向它的引用。然后,当你要访问外围类的成员时,该引用会被用于选择相应的外围类成员。幸运的是,编译器会为你处理所有的这些细节。

3. 在内部类中生成外部类对象的引用

要生成外部类对象的引用,可以使用外部类的名字,后面加上 .this。这样生成的引用会自动具有正确的类型,而且是可以在编译时确定并检查的,所以没有任何运行时开销,如下所示:

package com.yyj;public class DotThis {void f() {System.out.println("DotThis.f()");}public class Inner {public DotThis outer() {return DotThis.this;  // 如果直接写this,引用的是Inner的this}}public Inner getInner() { return new Inner(); }public static void main(String[] args) {DotThis dt = new DotThis();DotThis.Inner dti = dt.getInner();  // 使用外部类的对象来创建内部类dti.outer().f();  // DotThis.f()}
}

有时我们想让其他某个对象来创建它的某个内部类的对象,要实现这样的功能,可以使用 .new 语法,在 new 表达式中提供指向其他外部类对象的引用,就像下面这样:

// 在内部类中生成外部类对象的引用package com.yyj;public class DotNew {public class Inner {void f() {System.out.println("Inner.f()");}}public static void main(String[] args) {DotNew dn = new DotNew();DotNew.Inner dni = dn.new Inner();dni.f();  // Inner.f()}
}

我们要使用外部类的对象来创建内部类的对象,正如我们在示例代码中所看到的那样,这也解决了内部类的名字作用域问题。

除非已经有了一个外部类的对象,否则创建内部类对象是不可能的,这是因为内部类的对象会暗中连接到用于创建它的外部类对象。然而,如果你创建的是嵌套类(static 修饰的内部类),它就不需要指向外部类对象的引用。

4. 匿名内部类

我们先来看一下如何创建匿名内部类:

package com.yyj;interface AnonymousIf {int value();
}public class AnonymousClass {public AnonymousIf getAnanymousIf() {return new AnonymousIf() {private int x = 1;@Overridepublic int value() {return x;}};}public static void main(String[] args) {AnonymousClass ac = new AnonymousClass();AnonymousIf ai = ac.getAnanymousIf();System.out.println(ai.value());}
}

这段代码的意思是创建一个继承自 AnonymousIf 的匿名类的对象,通过 new 表达式返回的引用会被自动地向上转型为一个 AnonymousIf 引用。

在这个匿名内部类中,AnonymousIf 是用无参构造器创建的,如果基类需要带一个参数的构造器,可以这么做:

package com.yyj;class BaseAnonyClass {private int x;BaseAnonyClass(int value) {x = value;System.out.println("Construct BaseAnonyClass with " + value);}int value() { return x; }
}public class AnonymousClass {public BaseAnonyClass getBaseAnonyClass(int value, final String str) {  // 用到匿名类之外的对象需要用final修饰return new BaseAnonyClass(value) {  // 基类构造器的调用private String name;{  // 匿名内部类的构造器name = str;System.out.println("Anonymous class constructor");}@Overridepublic int value() {return super.value() * 2;}@Overridepublic String toString() {return name;}};}public static void main(String[] args) {AnonymousClass ac = new AnonymousClass();BaseAnonyClass b = ac.getBaseAnonyClass(10, "AsanoSaki");System.out.println(b.value());System.out.println(b);/** Construct BaseAnonyClass with 10* Anonymous class constructor* 20* AsanoSaki*/}
}

也可以在定义匿名类中的字段时执行初始化,如果你正在定义一个匿名类,而且一定要用到一个在该匿名类之外定义的对象,编译器要求参数引用需要用 final 修饰,或者是“实际上的最终变量”(也就是说,在初始化之后它永远不会改变,所以它可以被视为 final 的)。这里变量 value 被传给了匿名类的基类构造器,并没有在匿名类的内部被直接用到,因此不是必须为 final 变量。

如果必须在匿名类中执行某个类似构造器的动作,该怎么办呢?因为匿名类没有名字,所以不可能有命名的构造器。我们可以借助实例初始化,使用 {} 语句块在效果上为匿名内部类创建一个构造器。

5. 嵌套类

如果不需要内部类对象和外部类对象之间的连接,可以将内部类设置为 static 的,我们通常称之为嵌套类。要理解 static 应用于内部类时的含义,请记住,普通内部类对象中隐式地保留了一个引用,指向创建该对象的外部类对象,对于 static 的内部类来说,情况就不是这样了,嵌套类意味着:

  • 不需要一个外部类对象来创建嵌套类对象。
  • 无法从嵌套类对象内部访问非 static 的外部类对象。

从另一方面来看,嵌套类和普通内部类还有些不同。普通内部类的字段和方法,只能放在类的外部层次中,所以普通内部类中不能有 static 数据、static 字段,也不能包含嵌套类,但是嵌套类中可以包含所有这些内容:

package com.yyj;public class NestedClass {private static class StaticInner {static int x = 10;public static void f() {System.out.println("StaticInner static f()");}StaticInner() {System.out.println("Construct StaticInner");}static class AnotherStaticClass {public static void f() {System.out.println("AnotherStaticClass static f()");}}}public static void main(String[] args) {StaticInner si = new StaticInner();StaticInner.f();StaticInner.AnotherStaticClass.f();/** Construct StaticInner* StaticInner static f()* AnotherStaticClass static f()*/}
}

可以看到我们在 main() 中并不需要创建 NestedClass 对象就能直接创建 static 的内部类对象。

6. 接口中的类

嵌套类可以是接口的一部分,放到接口中的任何类都会自动成为 publicstatic 的,因为类是 static 的,所以被嵌套的类只是放在了这个接口的命名空间内,甚至可以在内部类内实现包围它的这个接口,就像这样:

package com.yyj;public interface ClassInInterface {void f();class Inner implements ClassInInterface {@Overridepublic void f() {System.out.println("ClassInInterface.Inner f()");}public static void main(String[] args) {new Inner().f();}}
}

相关文章:

Java内部类、匿名内部类、嵌套类详解

CONTENTS 1. 创建内部类2. 内部类到外部类的连接3. 在内部类中生成外部类对象的引用4. 匿名内部类5. 嵌套类6. 接口中的类 1. 创建内部类 创建内部类的方式就是把类定义放在一个包围它的类之中&#xff1a; package com.yyj;public class Parcel1 {class Contests {private i…...

【兔子王赠书第3期】《案例学Python(进阶篇)》

文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展&#xff0c;Python将会得到越来越多开发者的喜爱和应用。因为Python语法简单&#xff0c;学习速度快&#xff0c;大家可以用更短的时间掌握这门语…...

【C刷题】day6

一、选择题 1、以下叙述中正确的是&#xff08; &#xff09; A: 只能在循环体内和switch语句体内使用break语句 B: 当break出现在循环体中的switch语句体内时&#xff0c;其作用是跳出该switch语句体&#xff0c;并中止循环体的执行 C: continue语句的作用是&#xff1a;在…...

MySQL精髓:如何使用ALL一次找到最大值

题目来自LeetCode 题目 表&#xff1a;Project -------------------- | Column Name | Type | -------------------- | project_id | int | | employee_id | int | -------------------- (project_id, employee_id) 是该表的主键(具有唯一值的列的组合)。 employee_id 是该表…...

安全设备

一.防火墙 5层应用层 防火墙 4层 udp tcp 协议 华为 厂商 华为 h3 1.区域划分 Dmz 停火区 Untrust 不安全区域 Trust 安全区域 防火墙 默认禁止所有 二.Waf Web 应用防火墙 放到web前面 产品 雷池 绿盟 软件 安…...

基于Java的足球赛会管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

如何确定Apache Kafka的大小和规模

调整或扩展Kafka以获得最佳成本和性能的第一步是了解数据流平台如何使用资源。这里给一些实用的建议。 实现Apache Kafka的团队&#xff0c;或者扩展他们对强大的开源分布式事件流平台的使用&#xff0c;通常需要帮助理解如何根据他们的需求正确地调整和扩展Kafka资源。这可能…...

项目总结-新增商品-Pagehelper插件分页查询

&#xff08;1&#xff09;新增商品 工具类&#xff1a; /** * Title: FileUtils.java * Package com.qfedu.common.utils * Description: TODO(用一句话描述该文件做什么) * author Feri * date 2018年5月29日 * version V1.0 */ package com.gdsdxy.common.u…...

java基础篇-环境变量

java基础 编程学习的关键点、重点1.环境变量设置待续 编程学习的关键点、重点 输入输出 Java语言、C语言、Python语言、甚至SQL语言&#xff0c;都需要实战、做大量输入输出等 1.环境变量设置 1.下载jdk安装 jdk官网下载直达链接&#xff1a;https://www.oracle.com/java/te…...

API自动化测试:如何构建高效的测试流程!

一、引言 在当前的软件开发环境中&#xff0c;API&#xff08;Application Programming Interface&#xff09;扮演了极为重要的角色&#xff0c;连接着应用的各个部分。对API进行自动化测试能够提高测试效率&#xff0c;降低错误&#xff0c;确保软件产品的质量。本文将通过实…...

MySQL8锁的问题

关键字 mysql 8、lock 问题描述 项目上反馈&#xff0c;一个简单的提交操作需要 40 秒。 抓取 SQL 发现 update gl_credit_bill set verifystate2 where id2761279790403840 执行耗时近40秒解决问题思路 手动执行 SQL&#xff0c;发现非常快&#xff0c;基本排除数据库本身…...

进阶JAVA篇-深入了解 Stream 流对象的创建与中间方法、终结方法

目录 1.0 Stream 流的说明 2.0 Stream 流对象的创建 2.1 对于 Collection 系列集合创建 Stream 流对象的方式 2.2 对于 Map 系列集合创建 Stream 流对象的方式 2.3 对于数组创建 Stream 流对象的方式 3.0 Stream 流的中间方法 3.1 Stream 流的 filter() 中间方法 3.2 Stream 流…...

原型制作的软件 Experience Design mac( XD ) 中文版软件特色

​XD是一个直观、功能强大的UI/UX开发工具&#xff0c;旨在设计、原型、用户之间共享材料以及通过数字技术进行设计交互。Adobe XD提供了开发网站、应用程序、语音界面、游戏界面、电子邮件模板等所需的一切。xd mac软件特色 体验设计的未来。 使用 Adobe XD 中快速直观、即取即…...

Kotlin中使用ViewBinding绑定控件并添加点击事件

文章目录 效果1、加入依赖2、与控件进行绑定在 Activity 中使用视图绑定 3、监听控件 效果 实现源码 class MainActivity : AppCompatActivity() {lateinit var binding:ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstan…...

Node学习笔记之MongoDB

一、简介 1.1 Mongodb 是什么 MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 MongoDB: The Developer Data Platform | MongoDB 1.2 为什么选择 Mongodb 操作语法与 JavaScript 类似&#xff0c;容易上手&#xff0c;学习成本低 二、核心概念 Mongodb 中…...

awtk用C语言开发串口通信示例

awtk开发工具封装了串口&#xff0c;可以方便的异步调用&#xff0c;就做个程序试一下吧 在deepinlinux20.9版本调试通过&#xff0c;开始第一步先给系统增加usb串口线驱动 https://download.csdn.net/download/qiaozhangchi/87463972 串口控件ide里没有&#xff0c;需要自己…...

CICD 流程学习(五)Jenkins后端工程构建

案例1&#xff1a;数据库服务部署 MySQL部署 #安装MySQL服务 [rootServices ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootServices ~]# yum -y install mysql.x86_64 mysql-server.x86_64 mysql-devel.x86_64 ... Complete! [rootServices ~]# #启动…...

NSS [SWPUCTF 2021 新生赛]sql

NSS [SWPUCTF 2021 新生赛]sql 很明显是sql&#xff0c;有waf。 参数是wllm get型传参&#xff0c;有回显&#xff0c;单引号闭合&#xff0c;回显位3 跑个fuzz看看waf 过滤了空格 and 报错注入 空格->%09 ->like and->&&爆库&#xff1a;test_db -1%27uni…...

【Python机器学习】零基础掌握RandomTreesEmbedding集成学习

如何在高维数据中找到隐藏的结构? 面临大量复杂、高维的数据,例如社交网络分析、电子商务推荐系统或医疗诊断,如何有效地分析和解读这些数据成为一大挑战。一个有效的方法是使用嵌入技术将高维数据转化为低维形式,同时保留其内在结构。这次将介绍一种称为“随机树嵌入”(…...

【C++基础入门】42.C++中同名覆盖引发的问题

一、父子间的赋值兼容 子类对象可以当作父类对象使用&#xff08;兼容性) 子类对象可以直接赋值给父类对象子类对象可以直接赋值给父类对象父类指针可以直接指向子类对象父类引用可以直接引用子类对象 下面看一个子类对象兼容性的代码&#xff1a; #include <iostream>…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

JDK 17 序列化是怎么回事

如何序列化&#xff1f;其实很简单&#xff0c;就是根据每个类型&#xff0c;用工厂类调用。逐个完成。 没什么漂亮的代码&#xff0c;只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...