当前位置: 首页 > 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>…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...