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

java内部类详解

文章目录

  • 一、介绍
  • 二、为什么要使用内部类
  • 三、非静态内部类
  • 四、静态内部类
  • 五、局部内部类
  • 六、匿名内部类
  • 七、lambda表达式内部类
  • 八、成员重名
  • 九、序列化
  • 十、如何选择内部类

一、介绍

在java中,我们被允许在编写一个类(外部类OuterClass)时,在其内部再嵌套一个类(嵌套类NestedClass),java将嵌套类分为两种:非静态内部类(简称内部类)静态内部类,如下所示

public class OuterClass {class InnerClass {}static class StaticInnerClass {}
}

嵌套类作为外部类的一个成员,无论其是否为静态内部类,我们都可以对其添加任何的访问修饰符(publicprotectedprivatedefault),如下所示

public class OuterClass {public class InnerClass {}protected static class StaticInnerClass {}
}

非静态内部类对其外部类中的所有成员变量和方法都具有访问权,即便它被private修饰也可以。但是静态内部类对外部类中的成员变量和方法没有访问权。


二、为什么要使用内部类

内部类为我们提供了以下便利:

  • 它对只在一个地方使用的类进行逻辑分组

    如果一个类A.java只对另一个类B.java有用,那么将它嵌入到那个类中并把两个类放在一起岂不合理?即把A.java作为内部类,而将B.java作为外部类。

  • 它增加了封装性

    考虑两个类A.javaB.java,其中B.java需要访问A.java的被声明为private的成员变量或方法。通过将类B.java隐藏在类A.java中,A.java的成员变量或方法可以被声明为privateB.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中&#xff0c;我们被允许在编写一个类(外部类OuterClass)时&#xff0c;在其内部再嵌…...

Python 潮流周刊#29:Rust 会比 Python 慢?!

△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯。 &#x1f43…...

吴恩达《机器学习》11-1-11-2:首先要做什么、误差分析

一、首先要做什么 选择特征向量的关键决策 以垃圾邮件分类器算法为例&#xff0c;首先需要决定如何选择和表达特征向量 &#x1d465;。视频提到的一个示例是构建一个由 100 个最常出现在垃圾邮件中的词构成的列表&#xff0c;根据这些词是否在邮件中出现来创建特征向量&…...

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、配置最大连接数&#xff0c; mysql版本不同可配置的最大连接数不同&#xff0c;mysql8.0的版本默认151个连接数&#xff0c;…...

C++学习之路(七)C++ 实现简单的Qt界面(消息弹框、按钮点击事件监听)- 示例代码拆分讲解

这个示例创建了一个主窗口&#xff0c;其中包含两个按钮。第一个按钮点击时会显示一个简单的消息框&#xff0c;第二个按钮点击时会执行一个特定的操作&#xff08;在这个例子中&#xff0c;仅打印一条调试信息&#xff09;。 功能描述&#xff1a; 创建窗口和布局&#xff1a;…...

python实现一个计算器

实现一个计算器首先熟悉一下这个阅读器的属性import subprocess subprocess.run(["espeak", "-v", "enf3", "This is a Calculator"])class Calculator:def speaker(self,word):subprocess.run(["espeak", "-v", …...

C++ 共享内存ShellCode跨进程传输

在计算机安全领域&#xff0c;ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度&#xff0c;研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法&#xff0c;通过该方法&#xff0c;两个本地进程可以相互传递ShellCode&am…...

如何快速移植(从STM32F103到STM32F407)

最近用到F4的地方比较多&#xff0c;网上代码还是F1多一些&#xff0c;便需要移植代码&#xff0c;如何快速移植代码呢&#xff1f; 看下面这篇文章 外设 首先就是STM32的外设了。 STM32F407ZGT6的基本外设 STM32F407ZGT6 作为 MCU&#xff0c;该芯片是 STM32F407 里面配置…...

python高级练习题库实验1(B)部分

文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目4代码实验结果题目5代码实验结果题目总结题目1 打包糖果小游戏,用户输入糖果品牌与个数,还有一个盒子里面可以装多少个糖果,输出一些打印信息,如下图所示: 代码 print("Packaging lollies into…...

Qt Rsa 加解密方法使用(pkcs1, pkcs8, 以及文件存储和内存存储密钥)

Qt RSA 加解密 完整使用 密钥格式&#xff1a; pkcs#1pkcs#8 如何区分密钥对是PKCS1还是PKCS8&#xff1f; 通常PKCS1密钥对的开始部分为&#xff1a;-----BEGIN RSA PRIVATE KEY-----或 -----BEGIN RSA PUBLIC KEY-----。而PKCS8密钥对的开始部分为&#xff1a;-----BEGIN…...

区分物理端口与软件端口概念:以交换机端口和Linux系统中的端口为例

文章目录 交换机端口和Linux系统中的端口有什么区别&#xff1f;1. 交换机的端口2. Linux系统中的端口因此&#xff0c;尽管两者都被称为"端口"&#xff0c;但它们代表的含义和用途是完全不同的。 交换机端口和Linux系统中的端口有什么区别&#xff1f; 虽然都被称为…...

力扣226:翻转二叉树

力扣226&#xff1a;翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3]…...

亚马逊鲲鹏系统智能自动注册与AI角色养号,探索数字化新境界

在数字化时代&#xff0c;亚马逊鲲鹏系统以其强大的自动化功能&#xff0c;为用户提供了前所未有的购物体验。如果你想利用鲲鹏系统进行自动化注册&#xff0c;那么准备好邮箱、IP、手机号等关键信息后&#xff0c;你将轻松实现自动注册&#xff0c;为购物之旅开启智能化新篇章…...

AOP操作日志记录

AOP操作日志记录 1.创建注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface PassportLog {String operatePage();String operateType();ClassTypEnum classType();}2.创建切面 对于字典&#xff0c;可以通过注解属性去转换&#xff0c;枚举…...

Linux C语言 42-进程间通信IPC之网络通信(套接字)

Linux C语言 42-进程间通信IPC之网络通信&#xff08;套接字&#xff09; 本节关键字&#xff1a;C语言 进程间通信 网络通信 套接字 TCP UDP 相关库函数&#xff1a;socket、bind、listen、accept、send、recv、sendto、recvfrom 参考之前的文章 Linux C语言 30-套接字操作…...

微服务知识大杂烩

1.什么是微服务? 微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可…...

记录一次vscode markdown的图片路径相关插件学习配置过程

插件及说明查找过程 csdn搜索markdown图片路径&#xff0c;找到关于这一款插件的回答。打开vscode拓展搜索Paste Image这款插件&#xff0c;看到下载量挺高的&#xff0c;应该不赖。 点击仓库,进入该插件开源的github仓库,查看README文件阅读说明. 淡然在Vscode 插件项目下的细…...

设计原则 | 依赖转置原则

一、依赖转置原则&#xff08;DIP&#xff1a;Dependence Inversion Principle&#xff09; 1、原理 高层模块不应该依赖低层模块&#xff0c;二者都应该依赖于抽象抽象不应该依赖于细节&#xff0c;细节应该依赖于抽象 2、层次化 Booch曾经说过&#xff1a;所有结构良好的面…...

前端开发实用技巧与经验分享

导语&#xff1a;在前端开发领域&#xff0c;掌握一些实用的技巧和经验可以帮助你更高效地完成任务。本文将分享一些前端开发的实用技巧和经验&#xff0c;帮助你在工作中更好地应对各种挑战。 一、使用开发者工具进行调试和优化 熟练掌握浏览器开发者工具的使用&#xff0c;…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...