Java基础-内部类
内部类
- 引言
- 内部类的共性
- 成员内部类
- 静态内部类
- 非静态内部类
- 局部内部类
- 匿名内部类
- 内部类的使用场景和好处
引言
Java不仅可以定义变量和方法,还可以定义类.
内部类允许你把一些逻辑相关的类组织在一起,并可以控制内部中类的可见性.
这么看来,内部类就像是代码一种隐藏机制:将类放在其他类的内部,从而隐藏名字和组织代码的模式.
根据定义方式的不同,分为四种类型: 静态内部类, 成员内部类,局部内部类,匿名内部类
内部类的共性
- 依然是一个独立的类,在编辑之后内部类会被编辑成独立的.class文件,但是前面会添加外部类的类名和$符号
- 声明为静态的,就不能随便访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量或方法
- 外部类不能直接访问内部类的成员,但可以通过内部类对象来访问
内部类是外部类的一个成员,因此内部类可以自由访问外部类的成员变量,无论是否为private.
因为某个外围类对象创建内部类对象时,此内部类会捕获一个隐式引用,它引用了实例化内部对象的外围类对象,通过这个指针,可以访问外围类对象的全部状态.
实现原理
反编译内部类字节码,分析主要通过以下几步做到的:
- 编译器为内部类添加一个成员变量,它的类型和外部类的类型相同,这个成员变量就是指向外部类对象的引用
- 编译器为内部类的构造方法添加一个参数,参数类型是外部类类型,在构造方法内部使用这个参数为1中添加的成员变量赋值
- 在调用内部类的构造函数初始化内部类对象时,会默认传入外部类的引用
下面我们举个例子来看一下:
public class Outter {private Inner inner = null;public Outter() {}public Inner getInnerInstance() {if(inner == null)inner = new Inner();return inner;}protected class Inner {public Inner() {}}
}

反编译Outter$Inner.class文件得到下面信息:
E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner
Compiled from "Outter.java"
public class com.cxh.test2.Outter$Inner extends java.lang.ObjectSourceFile: "Outter.java"InnerClass:#24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes
t2/Outterminor version: 0major version: 50Constant pool:
const #1 = class #2; // com/cxh/test2/Outter$Inner
const #2 = Asciz com/cxh/test2/Outter$Inner;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz this$0;
const #6 = Asciz Lcom/cxh/test2/Outter;;
const #7 = Asciz <init>;
const #8 = Asciz (Lcom/cxh/test2/Outter;)V;
const #9 = Asciz Code;
const #10 = Field #1.#11; // com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t
est2/Outter;
const #11 = NameAndType #5:#6;// this$0:Lcom/cxh/test2/Outter;
const #12 = Method #3.#13; // java/lang/Object."<init>":()V
const #13 = NameAndType #7:#14;// "<init>":()V
const #14 = Asciz ()V;
const #15 = Asciz LineNumberTable;
const #16 = Asciz LocalVariableTable;
const #17 = Asciz this;
const #18 = Asciz Lcom/cxh/test2/Outter$Inner;;
const #19 = Asciz SourceFile;
const #20 = Asciz Outter.java;
const #21 = Asciz InnerClasses;
const #22 = class #23; // com/cxh/test2/Outter
const #23 = Asciz com/cxh/test2/Outter;
const #24 = Asciz Inner;{
/看中间的代码
final com.cxh.test2.Outter this$0; //注意这一行代码!!!
/看中间的代码
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);Code:Stack=2, Locals=2, Args_size=20: aload_01: aload_12: putfield #10; //Field this$0:Lcom/cxh/test2/Outter;5: aload_06: invokespecial #12; //Method java/lang/Object."<init>":()V9: returnLineNumberTable:line 16: 0line 18: 9LocalVariableTable:Start Length Slot Name Signature0 10 0 this Lcom/cxh/test2/Outter$Inner;
}
final com.cxh.test2.Outter this$0;
这是一个指向外部类对象的指针.
也就是说,编译器会默认成员内部类添加了一个指向外部类对象的引用
那这个引用如何赋初值呢?
下面接着看一下内部类的构造器
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
虽然我们定义的内部类的构造器说无参构造器,但是编译器还是默认添加一个参数,该参数类型为指向外部类对象的一个引用.
所以成员内部类中的Outter this&0指针便指向类外部类对象,因此可以在成员内部类中随意访问外部类的成员.
从这里也间接说明了成员内部类是依赖外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了.
成员内部类
成员内部类像是外部类的一个成员,它定义在另一个类的内部
成员内部类分为两种:
- 静态成员内部类: 使用static修饰类
- 非静态成员内部类: 不实用static修饰类,在没说明是静态成员内部类时,默认成员内部类指的是非静态成员内部类
静态内部类
定义在类内部的静态类,就是静态内部类.
静态内部类不需要依赖外部类,这点和静态成员属性类似,并且它不能使用外部类的非Static成员变量和方法.
因为没有外部类对象的情况下,我们可以创建出静态内部类对象,这时候如果允许访问外部类的非Static成员就会产生矛盾,因为外部类的非Static成员必须依附于具体的对象.
public class Out {private static int a;private int b;public static class Inner {public void print() {System.out.println(a);}}
}
访问作用域: 可以访问外部类所有的静态变量和方法,即使是private也一样可以访问
与类不同点: 和一般类一致,可以定义静态变量,方法,构造方法等
使用的方法: 外部类.静态内部类。如:Out.Inner inner = new Out.Inner();inner.print();
非静态内部类
最普通的内部类,定义位于另一类的内部,如下面形式
class Circle {double radius = 0;public Circle(double radius) {this.radius = radius;}class Draw { //内部类public void drawSahpe() {System.out.println("drawshape");}}
}
类Draw像是Circle的一个成员,Circle称为外部类.
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
但是外部类访问内部类成员,首先要创建一个成员内部类的对象,再通过指向这个对象的引用来访问
- 内部类访问外部类
class Circle {private double radius = 0;public static int count =1;public Circle(double radius) {this.radius = radius;}class Draw { //内部类public void drawSahpe() {System.out.println(radius); //外部类的private成员System.out.println(count); //外部类的静态成员}}
}
- 外部类访问内部类
class Circle {private double radius = 0;public Circle(double radius) {this.radius = radius;getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问}private Draw getDrawInstance() {return new Draw();}class Draw { //内部类public void drawSahpe() {System.out.println(radius); //外部类的private成员}}
}
注意:
成员内部类和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员.
如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
局部内部类
定义在一个方法或者一个作用域里面的内,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内.
class People{public People() {}
}class Man{public Man(){}public People getWoman(){class Woman extends People{ //局部内部类int age =0;}return new Woman();}
}
注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有public,protected,private以及static修饰符的.
匿名内部类
这个应该是我们编写代码时用的最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更容易维护.
public class AnonymousInnerClassExample { public static void main(String[] args) { // 创建一个订单处理系统实例 OrderProcessingSystem system = new OrderProcessingSystem(); // 创建一个订单实例 Order order = new Order("123", 100.0); // 使用匿名内部类实现一个打印订单详情的处理器 system.process(order, new OrderProcessor() { @Override public void processOrder(Order order) { System.out.println("Processing order: " + order); } }); // 使用另一个匿名内部类实现一个打折处理订单的逻辑 system.process(order, new OrderProcessor() { @Override public void processOrder(Order order) { double discount = 0.1; // 假设打10%的折扣 double discountedAmount = order.getAmount() * (1 - discount); System.out.println("Processing discounted order: " + order + " with discounted amount: " + discountedAmount); } }); }
}
使用匿名内部类的好处显而易见
- 比较简洁,只需要实现一个接口的简单任务,使用它可以避免创建额外命名类
- 可以访问其外部类所有成员(包括私有成员),在某些场景下非常灵活
内部类的使用场景和好处
为什么Java需要内部类呢?总结有下面四点:
- 每个内部类都能独立的继承一个接口的实现,无论外部类是否已经继承了某个(接口)实现,对于内部类都没有影响.
解释一下:
Java本身不支持类的多继承,但可以通过内部类来实现接口的多继承效果.我们可以在一个外部类中定义多个内部类,每个内部类都可以实现不同的接口,从而达到类类似多继承的效果.
举个简单的代码例子来说明这个概念:
// 定义一个接口A
interface InterfaceA { void methodA();
} // 定义另一个接口B
interface InterfaceB { void methodB();
} // 外部类
class OuterClass { // 外部类可以继承自一个类,这里我们假设它继承自Object(实际上所有类都隐式继承自Object) // 内部类1,实现接口A private class InnerClassA implements InterfaceA { @Override public void methodA() { System.out.println("Implementing methodA from InterfaceA in InnerClassA"); } } // 内部类2,实现接口B private class InnerClassB implements InterfaceB { @Override public void methodB() { System.out.println("Implementing methodB from InterfaceB in InnerClassB"); } } // 可以提供获取内部类实例的方法 public InterfaceA getInnerClassA() { return new InnerClassA(); } public InterfaceB getInnerClassB() { return new InnerClassB(); }
} // 使用示例
public class Main { public static void main(String[] args) { OuterClass outer = new OuterClass(); // 获取并调用内部类A的方法 InterfaceA innerA = outer.getInnerClassA(); innerA.methodA(); // 获取并调用内部类B的方法 InterfaceB innerB = outer.getInnerClassB(); innerB.methodB(); }
}
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏
- 方便编写事件驱动程序
- 方便编写线程代码
相关文章:
Java基础-内部类
内部类 引言内部类的共性成员内部类静态内部类非静态内部类 局部内部类匿名内部类内部类的使用场景和好处 引言 Java不仅可以定义变量和方法,还可以定义类. 内部类允许你把一些逻辑相关的类组织在一起,并可以控制内部中类的可见性. 这么看来,内部类就像是代码一种隐藏机制:将类…...
设计模式-行为型模式-职责链模式
在软件系统运行时,对象并不是孤立存在的,它们可以通过相互通信协作完成某些功能,一个对象在运行时也将影响到其他对象的运行。行为型模式(Behavioral Pattern)关注系统中对象之间的交互,研究系统在运行时对…...
代码随想录算法训练营第四十天|LeetCode343 整数拆分、LeetCode96 不同的二叉搜索树
343.整数拆分 思路:确定dp数组以及下标的含义 dp[i]代表 i可以被拆分后的最大乘积。确定递推公式,假如拆成连个数,dp[i] j*(i-j),拆成两个数以上,dp[i]j*dp[i-j],j的范围为1到i-1.dp[i]找到所有情况的最大值。初始化…...
接口自动化测试用例如何设计
说到自动化测试,或者说接口自动化测试,多数人的第一反应是该用什么工具,比如:Python Requests、Java HttpClient、Apifox、MeterSphere、自研的自动化平台等。大家似乎更关注的是哪个工具更优秀,甚至出现“ 做平台的 &…...
弱电综合布线:连接现代生活的纽带
在当今信息化快速发展的时代,弱电网络布线作为信息传输的重要基础设施,其作用日益凸显。它不仅保障了数据的高效流通,还确保了通信的稳定性。从商业大厦到教育机构,从政府机关到医院急救中心,再到我们居住的社区&#…...
Java零基础 - 数组的定义和声明
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...
CSS补充(下),弹性布局(上)
高级选择器 1.兄弟选择器 2.同时满足 div.bg{background-color: red;}p.bg{background-color: green;}spam.bg{background-color: blue;}注:选择器中间没有空格,有明确标识的选择器写在后面 3.各种伪类的应用 3.1作为第几个子元素 选择器:nth-child…...
图数据库 之 Neo4j - 应用场景4 - 反洗钱(9)
原理 Neo4j图数据库可以用于构建和分析数据之间的关系。它使用节点和关系来表示数据,并提供实时查询能力。通过使用Neo4j,可以将大量的交易数据导入图数据库,并通过查询和分析图结构来发现洗钱行为中的模式和关联。 案例分析 假设有一家转账服务公司,有以下交易数据,每个…...
uboot分区介绍
RK平台的U-Boot支持两种分区表 RK paramter格式(旧)和 标准GPT格式(新),当机器上同时存在 两种分区表时,优先使用GPT分区表。无论是 GPT 还是 RK parameter,烧写用的分区表文件都叫parameter.t…...
快速收集诊断信息,敏捷诊断工具obdiag应用实践——《OceanBase诊断系列》之三
1. 前言 作为OceanBase的敏捷诊断工具,obdiag具有以下特点: 部署便捷:提供rpm包和OBD上部署的模式,都能够一键部署安装。用户可以选择将其部署到集群中任意一台能连接到各个节点的设备上,而不仅限于OBServer节点。即…...
C++错误总结(1)
1.定义函数类型时,如果没有返回值,用void void swap(int &x, int &y){ int tem x; x y; y tem; } 2.输入时,不加换行符 cin >> a >> b >> c >> endl ;(红色标记的是错误的部分) 3.【逆序出入…...
std::shared_from_this注意事项:exception bad_weak_ptr
1.不可以在构造函数中调用shared_from_this() 因为它的实现是: _LIBCPP_INLINE_VISIBILITYshared_ptr<_Tp> shared_from_this(){return shared_ptr<_Tp>(__weak_this_);}也就是它依赖的__weak_this_此时还未创建完成。 2.一定要public继承 class MyTy…...
【工具】Raycast – Mac提效工具
引入 以前看到同事们锁屏的时候,不知按了什么键,直接调出这个框,然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还是蛮炫酷的~ 调研 但是由于之前比较繁忙,这件事其实都忘的差不多了࿰…...
蓝桥杯集训·每日一题2024 (二分,双指针)
前言: 开学了,平时学习的压力也逐渐大起来了,不过还算可以接受,等到后面阶段考的时候就不一样了,我目前为了转专业退选了很多课,这些课我都需要花时间来刷绩点,不然保研就没有竞争力了。我自己会…...
在Linux(Ubuntu)中使用终端编译 vscode安装
文章目录 📚在Linux(Ubuntu)中使用终端编译🐇.cpp程序编译🐇.py程序编译🐇查看Python、C编程环境 📚vscode安装 📚在Linux(Ubuntu)中使用终端编译 虚拟机安装…...
官网正在被哪些产品蚕食,定制网站又被哪些建站产品挤占。
2023-12-09 16:22贝格前端工场 官网建设是一个被大多数人看衰的市场,本文来理性分析下,谁在蚕食这个市场,谁又在挤占这个产品生存空间,欢迎大家评论,探讨。 网站正在被以下产品形式取代: 1. 移动应用&…...
BUUCTF---[MRCTF2020]你传你呢1
1.题目描述 2.打开题目链接 3.上传shell.jpg文件,显示连接成功,但是用蚁剑连接却连接不上。shell文件内容为 <script languagephp>eval($_REQUEST[cmd]);</script>4.用bp抓包,修改属性 5.需要上传一个.htaccess的文件来把jpg后缀…...
vite+vue3门户网站菜单栏动态路由控制
门户网站用户端需要分板块展示,板块内容由管理端配置,包括板块名称,访问路径,路由组件,展示顺序,是否展示。如下图所示: 用户访问门户网站时,展示菜单跳转通过板块配置,动…...
【C语言】linux内核packet_setsockopt
一、中文注释 // 发送数据包函数。它尝试通过特定的网络设备队列直接传输一个skb(socket缓冲区)。 static int packet_direct_xmit(struct sk_buff *skb) {return dev_direct_xmit(skb, packet_pick_tx_queue(skb)); // 调用dev_direct_xmit函数&#x…...
LeetCode的使用方法
LeetCode的使用方法 一、LeetCode是什么?1.LeetCode简介2.LeetCode官网 二、LeetCode的使用方法1.注册账号2.力扣社区力扣编辑器 2.1 讨论发起讨论参与讨论关注讨论 2.2 文章撰写文章关注文章 3.力扣面试官版测评面试招聘竞赛 4.力扣学习LeetBook 书架我的阅读猜您喜…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
