java内部类详解
文章目录
- 一、介绍
- 二、为什么要使用内部类
- 三、非静态内部类
- 四、静态内部类
- 五、局部内部类
- 六、匿名内部类
- 七、lambda表达式内部类
- 八、成员重名
- 九、序列化
- 十、如何选择内部类
一、介绍
在java中,我们被允许在编写一个类(外部类OuterClass
)时,在其内部再嵌套一个类(嵌套类NestedClass
),java将嵌套类分为两种:非静态内部类(简称内部类) 和 静态内部类,如下所示
public class OuterClass {class InnerClass {}static class StaticInnerClass {}
}
嵌套类作为外部类的一个成员,无论其是否为静态内部类,我们都可以对其添加任何的访问修饰符(public
、protected
、private
、default
),如下所示
public class OuterClass {public class InnerClass {}protected static class StaticInnerClass {}
}
非静态内部类对其外部类中的所有成员变量和方法都具有访问权,即便它被private
修饰也可以。但是静态内部类对外部类中的成员变量和方法没有访问权。
二、为什么要使用内部类
内部类为我们提供了以下便利:
-
它对只在一个地方使用的类进行逻辑分组
如果一个类
A.java
只对另一个类B.java
有用,那么将它嵌入到那个类中并把两个类放在一起岂不合理?即把A.java
作为内部类,而将B.java
作为外部类。 -
它增加了封装性
考虑两个类
A.java
和B.java
,其中B.java
需要访问A.java
的被声明为private
的成员变量或方法。通过将类B.java
隐藏在类A.java
中,A.java
的成员变量或方法可以被声明为private
,B.java
也可以访问它们。另外,B.java
本身也可以对外界隐藏。 -
它使代码更加可读和可维护
将内部类嵌套在外部类中使代码更接近使用它的地方。
三、非静态内部类
非静态内部类有以下特点:
- 对其外部类中的所有成员变量和方法都具有访问权,即便它被
private
修饰也可以。 - 不允许定义任何
static
修饰的成员变量和方法。 - 外部类必须通过非静态内部类的实例对象访问其内部的属性和方法。
- 编译后生成两个
class
文件:外部类.class
和外部类$内部类.class
。
当我们实例化一个非静态内部类的对象时,使用以下方法
-
在外部类中实例化内部类
public class OuterClass {public void initInnerClass() {InnerClass innerClass = new InnerClass();innerClass.innerClassMethod_1();}class InnerClass {public void innerClassMethod_1() {System.out.println("调用内部类方法");}} }
-
在其他类中实例化内部类
class InnerClassTest {public static void main(String[] args) {// 方式一:创建内部类innerClass实例OuterClass outerClass = new OuterClass();OuterClass.InnerClass innerClass1 = outerClass.new InnerClass();innerClass1.innerClassMethod_1();// 方式二:创建内部类innerClass实例OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();innerClass2.innerClassMethod_1();} }
非静态内部类还有两种特殊的类型:局部内部类 和 匿名内部类。后面细说。
四、静态内部类
静态内部类在行为上与其他类相似
- 对外部类成员的访问只能通过外部类的实例对象。
- 编译后生成两个
class
文件:外部类.class
和外部类$内部类.class
。
当我们实例化一个静态内部类的对象时,使用以下方法
-
在外部类中实例化内部类
public class OuterClass {public void initStaticInnerClass() {StaticInnerClass staticInnerClass = new StaticInnerClass();staticInnerClass.staticInnerClassMethod_1();}static class StaticInnerClass {public void staticInnerClassMethod_1() {System.out.println("调用静态内部类方法");}} }
-
在其他类中实例化内部类
class InnerClassTest {public static void main(String[] args) {OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();staticInnerClass.staticInnerClassMethod_1();} }
五、局部内部类
在一个代码块声明的类叫局部内部类。此处的代码块指任何{}
内部(如静态代码块、方法、for
循环、if
块)
- 局部内部类可声明在任何代码块中
- 局部内部类内部可以访问其外部的成员变量,但该成员变量的值不允许被修改,因此我们需要使用
final
修饰。 - 不允许定义任何
static
静态成员变量或方法 - 静态方法中的局部内部类,只允许访问外部类中的
static
静态成员变量和方法。 - 不允许被
static
修饰 - 在
{}
代码块中不可以声明接口interface
。因为interface
接口天生就是静态的。 - 局部内部类中仅允许在常量变量声明中使用
static
。从java16
开始允许不被final
修饰 - 编译后生成
外部类.class
和外部类+$+编号+内部类.class
。
创建局部内部类的方式如下:
public class OuterClass {public void localClassMethod() {for (int i=0; i<10; i++) {// for循环中的局部内部类class LocalClassInFor {}}if (true) {// if代码块中的局部内部类class LocalClassInIf {}}// 方法中的局部内部类class LocalClass {}}
}
六、匿名内部类
在一个方法内部声明的类但没有命名该类的名称叫局部内部类。匿名类使您的代码更加简洁。允许我们能够同时声明和实例化一个类。除了没有名字之外,它们类似于局部内部类。如果只需要使用一次局部内部类,就使用它们。
- 本质上是一个表达式。
- 实例化匿名内部类需要借助一个父类 或 接口。
- 可以访问外部类的成员变量和方法。与局部内部类相同。
- 不可以修改外部变量,但该变量的值不允许被修改,因此我们需要使用
final
修饰。与局部内部类相同。 - 与外部变量或方法重名时,默认采用就近原则访问。与非静态内部类相同。
- 不可以声明
static
静态变量或方法。但可以声明final static
常量。 - 编译后生成
外部类.class
和外部类+$+编号.class
。
使用匿名内部类需要借助父类 或 接口实现,其本质相同,都是对方法的重写。如下所示
-
使用父类
public class AnonymousOuterClass {class InnerClass {private String innerField = "field in InnerClass";public void getField() {System.out.println(innerField);}}public void anonymousInnerMethod() {// 声明并实例化匿名内部类InnerClass innerClass = new InnerClass() {private String innerField = "a";@Overridepublic void getField() {// 输出匿名内部类中的成员变量innerFieldSystem.out.println(innerField);// 输出父类类中的成员变量innerFieldsuper.getField();}};innerClass.getField();} }class AnonymousOuterClassTest {public static void main(String[] args) {AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass();anonymousOuterClass.anonymousInnerMethod();} }
-
使用接口
public class AnonymousOuterClass {interface InnerInterface {void getField();}public void anonymousInnerInterfaceMethod() {InnerInterface innerInterface = new InnerInterface() {private String innerField = "field in inner interface";@Overridepublic void getField() {// 输出匿名内部类中的成员变量innerFieldSystem.out.println(innerField);}};innerInterface.getField();}}class AnonymousOuterClassTest {public static void main(String[] args) {AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass();anonymousOuterClass.anonymousInnerInterfaceMethod();} }
七、lambda表达式内部类
使用lambda表达式内部类的理由很简单:当匿名内部类的父类或接口中只有一个方法时,其实现代码最少也得五六行,为了使代码简化,所以使用lambda表达式内部类。
- 编译后生成
外部类.class
。lambda表达式内部类不会生成单独的class
文件。 - 其余特性与匿名内部类相同。
除了实例化方式不同,lambda表达式内部类和匿名内部类其余使用限制完全一致。
public class LambdaOuterClass {interface InnerInterface {void sayHello();}public void sayHello() {// lambda表达式内部类InnerInterface innerInterface = () -> System.out.println("hello world");innerInterface.sayHello();}
}class LambdaOuterClassTest {public static void main(String[] args) {LambdaOuterClass lambdaOuterClass = new LambdaOuterClass();lambdaOuterClass.sayHello();}
}
八、成员重名
无论是静态内部类还是非静态内部类,当内部类中与外部类具有相同名称的成员变量的情况下,当我们在内部类中使用该变量时,默认采用就近原则的方式访问该变量。如果需要访问外部类中的重名变量时,则需要通过OuterClass.this.field
访问,如下所示
public class OuterClass {private String sameNameField = "the same name field in OuterClass";class InnerClass {private String sameNameField = "the same name field in InnerClass";public void testSameNameField() {// 访问内部类的重名变量System.out.println(sameNameField);// 访问外部类的重名变量System.out.println(OuterClass.this.sameNameField);}}
}
九、序列化
java强烈建议不要序列化内部类,包括局部内部类和匿名类。当Java编译器编译某些结构时,比如内部类,它创建合成结构,这些是在源代码中没有相应构造的类、方法、字段和其他构造。合成结构使Java编译器能够在不改变JVM的情况下实现新的Java语言特性。然而,合成构造在不同的Java编译器实现中可能有所不同,这意味着在不同的实现中,类文件也可能不同。因此,如果我们序列化一个内部类,然后用不同的JRE实现反序列化它,可能会有兼容性问题。
十、如何选择内部类
-
非静态内部类
当需要访问外部类实例的非
public
修饰的成员变量和方法时。 -
静态内部类
当不需要访问外部类实例的成员变量和方法时。
-
局部内部类
当我们需要创建一个类的多个实例,请访问其构造函数,或者引入一个新的命名类型(例如,因为您稍后需要调用其他方法)。
-
匿名内部类
当我们需要同时声明和实例化一个类,并且只需要使用一次局部内部类时。
-
lambda表达式内部类
纸上得来终觉浅,绝知此事要躬行。
————————我是万万岁,我们下期再见————————
相关文章:
java内部类详解
文章目录 一、介绍二、为什么要使用内部类三、非静态内部类四、静态内部类五、局部内部类六、匿名内部类七、lambda表达式内部类八、成员重名九、序列化十、如何选择内部类 一、介绍 在java中,我们被允许在编写一个类(外部类OuterClass)时,在其内部再嵌…...
Python 潮流周刊#29:Rust 会比 Python 慢?!
△请给“Python猫”加星标 ,以免错过文章推送 你好,我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容,大部分为英文。本周刊开源,欢迎投稿[1]。另有电报频道[2]作为副刊,补充发布更加丰富的资讯。 ὃ…...
吴恩达《机器学习》11-1-11-2:首先要做什么、误差分析
一、首先要做什么 选择特征向量的关键决策 以垃圾邮件分类器算法为例,首先需要决定如何选择和表达特征向量 𝑥。视频提到的一个示例是构建一个由 100 个最常出现在垃圾邮件中的词构成的列表,根据这些词是否在邮件中出现来创建特征向量&…...
Pandas在Excel同一个sheet里插入多个Dataframe和行
Pandas默认的to_excel是直接把完成的Datafrme写入一个sheet里,这并不能满足我们在一个sheet里插入多个Dataframe或多行的需求。为了实现插入多行或多Dataframe的目的,我们需要新建一个ExcelWriter对象,然后依次插入数据。 这里我们以插入2个Dataframe和三行单元格为例。 新…...
查看mysql 或SQL server 的连接数,mysql超时、最大连接数配置
1、mysql 的连接数 1.1、最大可连接数 show variables like max_connections; 1.2、运行中连接数 show status like Threads_connected; 1.3、配置最大连接数, mysql版本不同可配置的最大连接数不同,mysql8.0的版本默认151个连接数,…...
C++学习之路(七)C++ 实现简单的Qt界面(消息弹框、按钮点击事件监听)- 示例代码拆分讲解
这个示例创建了一个主窗口,其中包含两个按钮。第一个按钮点击时会显示一个简单的消息框,第二个按钮点击时会执行一个特定的操作(在这个例子中,仅打印一条调试信息)。 功能描述: 创建窗口和布局:…...
python实现一个计算器
实现一个计算器首先熟悉一下这个阅读器的属性import subprocess subprocess.run(["espeak", "-v", "enf3", "This is a Calculator"])class Calculator:def speaker(self,word):subprocess.run(["espeak", "-v", …...
C++ 共享内存ShellCode跨进程传输
在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode&am…...
如何快速移植(从STM32F103到STM32F407)
最近用到F4的地方比较多,网上代码还是F1多一些,便需要移植代码,如何快速移植代码呢? 看下面这篇文章 外设 首先就是STM32的外设了。 STM32F407ZGT6的基本外设 STM32F407ZGT6 作为 MCU,该芯片是 STM32F407 里面配置…...
python高级练习题库实验1(B)部分
文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目4代码实验结果题目5代码实验结果题目总结题目1 打包糖果小游戏,用户输入糖果品牌与个数,还有一个盒子里面可以装多少个糖果,输出一些打印信息,如下图所示: 代码 print("Packaging lollies into…...
Qt Rsa 加解密方法使用(pkcs1, pkcs8, 以及文件存储和内存存储密钥)
Qt RSA 加解密 完整使用 密钥格式: pkcs#1pkcs#8 如何区分密钥对是PKCS1还是PKCS8? 通常PKCS1密钥对的开始部分为:-----BEGIN RSA PRIVATE KEY-----或 -----BEGIN RSA PUBLIC KEY-----。而PKCS8密钥对的开始部分为:-----BEGIN…...
区分物理端口与软件端口概念:以交换机端口和Linux系统中的端口为例
文章目录 交换机端口和Linux系统中的端口有什么区别?1. 交换机的端口2. Linux系统中的端口因此,尽管两者都被称为"端口",但它们代表的含义和用途是完全不同的。 交换机端口和Linux系统中的端口有什么区别? 虽然都被称为…...
力扣226:翻转二叉树
力扣226:翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1] 示例 2: 输入:root [2,1,3]…...
亚马逊鲲鹏系统智能自动注册与AI角色养号,探索数字化新境界
在数字化时代,亚马逊鲲鹏系统以其强大的自动化功能,为用户提供了前所未有的购物体验。如果你想利用鲲鹏系统进行自动化注册,那么准备好邮箱、IP、手机号等关键信息后,你将轻松实现自动注册,为购物之旅开启智能化新篇章…...
AOP操作日志记录
AOP操作日志记录 1.创建注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface PassportLog {String operatePage();String operateType();ClassTypEnum classType();}2.创建切面 对于字典,可以通过注解属性去转换,枚举…...
Linux C语言 42-进程间通信IPC之网络通信(套接字)
Linux C语言 42-进程间通信IPC之网络通信(套接字) 本节关键字:C语言 进程间通信 网络通信 套接字 TCP UDP 相关库函数:socket、bind、listen、accept、send、recv、sendto、recvfrom 参考之前的文章 Linux C语言 30-套接字操作…...
微服务知识大杂烩
1.什么是微服务? 微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可…...
记录一次vscode markdown的图片路径相关插件学习配置过程
插件及说明查找过程 csdn搜索markdown图片路径,找到关于这一款插件的回答。打开vscode拓展搜索Paste Image这款插件,看到下载量挺高的,应该不赖。 点击仓库,进入该插件开源的github仓库,查看README文件阅读说明. 淡然在Vscode 插件项目下的细…...
设计原则 | 依赖转置原则
一、依赖转置原则(DIP:Dependence Inversion Principle) 1、原理 高层模块不应该依赖低层模块,二者都应该依赖于抽象抽象不应该依赖于细节,细节应该依赖于抽象 2、层次化 Booch曾经说过:所有结构良好的面…...
前端开发实用技巧与经验分享
导语:在前端开发领域,掌握一些实用的技巧和经验可以帮助你更高效地完成任务。本文将分享一些前端开发的实用技巧和经验,帮助你在工作中更好地应对各种挑战。 一、使用开发者工具进行调试和优化 熟练掌握浏览器开发者工具的使用,…...
推荐一款Excel快速加载SQL的插件,方便又好用
如果告诉你只需要双击一下,SQL数据库中存放在表里面的数据,就能加载到你的Excel中,你想不想要? 今天给大家推荐一款好用的Excel插件,安装简单,使用方便,是经常使用SQL数据库的不二。 这款插件…...
Docker快速入门(docker加速,镜像,容器,数据卷常见命令操作整理)
Docker本质是将代码所需的环境依赖进行打包运行,而在Docker中最重要的是镜像和容器 镜像:可以简单地理解为每启动一个docker镜像就会占用计算机一个进程,这个进程和另外起的docker镜像的进程是相互独立的,以数据库为例,每个镜像都会copy一份数据库,在他所在的进程中.别的镜像在…...
http和https的区别有哪些
目录 HTTP(HyperText Transfer Protocol) HTTPS(HyperText Transfer Protocol Secure) 区别与优势 应用场景 未来趋势 当我们浏览互联网时,我们经常听到两个常用的协议:HTTP(HyperText Tra…...
使用Keil-MDK生成*.bin格式可执行文件
使用Keil-MDK生成*.bin格式可执行文件 文章目录 使用Keil-MDK生成*.bin格式可执行文件前言一、fromelf.exe工具二、使用方法1.配置输出2.输出格式 前言 在使用Keil MDK的集成开发环境中,默认情况下可以生成*.axf格式的调试文件和*.hex格式的可执行文件。虽然文件可…...
基于springboot+vue篮球联盟管理系统源码
🍅 简介:500精品计算机源码学习 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 文末获取源码 目录 一、以下学习内容欢迎交流: 二、文档资料截图: 三、项目技术栈 四、项目运行图 背景: 篮球运…...
分页助手入门以及小bug,报sql语法错误
导入坐标 5版本以上的分页助手 可以不用手动指定数据库语言,它会自动识别 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version> </dependency&g…...
Java中的并发编程:深入理解CountDownLatch
Java中的并发编程:深入理解CountDownLatch 本文将深入探讨Java中的并发编程,重点关注CountDownLatch的使用。通过理解这些概念和技术,我们可以编写出更高效、稳定的Java程序。 一、CountDownLatch简介 CountDownLatch是Java中的一个同步工具…...
Windows 安装 flash-attention 和 bitsandbytes
首先保证cuda版本为12.1,torch版本为2.1.0及以上,python版本3.10以上 从此处下载最新版的whl,https://github.com/jllllll/bitsandbytes-windows-webui/releases/tag/wheels,通过whl来安装bitsandbytes 从此处下载最新版的whl&a…...
AHB 与 DMA
AHB(先进高性能总线) 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP核复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP核复用的SoC(Syst…...
React使用echarts并且修改echarts图大小
React使用echarts 引入 npm install --save echarts-for-react npm install --save echarts使用 <ReactEChartsoption{option}notMerge{true}lazyUpdate{true}style{{"width": "100%","height": "800px"}}theme{"theme_nam…...
网站开发新闻怎么写/做个公司网站多少钱
计算机c语言实训报告范文篇三一、课程设计的目的(1)掌握结构化程序设计的基本方法,基本掌握面向对象程序设计的基本思路和方法。(2)掌握C的基本概念和基础知识。(3)通过训练能够读懂较为复杂的C语言源程序并具备基本C语言程序设计的能力。(4)熟练掌握各种常用类的定…...
网站怎么做免费seo搜索/云南百度公司
哈希表1.定义2.哈希函数的构造方法2.1直接定址法2.2 数字分析法2.3 平方取中法2.4 折叠法2.5 除留余数法2.6随机数法3.处理冲突的方法3.1 开放定址法3.2 再哈希法3.3 链地址法3.4 公共溢出区法4.hash表的查找5.python-hash表以及哈希查找的实现6.小结1.定义 \quad \quad哈希表&…...
温江做网站公司/广州疫情最新情况
概述 Neo4j是一个高性能的,NOSQL图形数据库,本身就支持集群部署,今天要搭建的就是neo4j的因果集群,其中分为: 核心节点:core-server,可以对数据进行读写的中心节点,通过选举确定leader…...
css3 动画网站/公司网站怎么申请怎么注册
一、前言 最近公司项目准备开始重构,框架选定为 Spring Boot ,本篇主要记录了在 IDEA 中搭建 Spring Boot Maven 多模块项目的过程。 这篇文章可以说是完全的一篇实战项目干货,感兴趣的朋友们可以继续看下去 第一个模块:数据库 …...
西安网站的建设/武汉seo推广优化
欢迎关注专栏:Java架构技术进阶。里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦。 微信公众号:慕容千语的架构笔记。欢迎关注一起进步。目前一线城市的薪资水平在13到40k之间,但是找工作的时候并…...
十大看b站直播的推荐理由/网站主页
在 office2007 中安装一个SaveAsPDFandXPS.exe插件程序就可以直接保存成PDF文件了...