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

Java 内部类要点总览

内部类类别典型定义位置.class 文件名模式是否依赖外部实例 (this$0)是否可捕获局部变量 (val$…)访问限定
成员内部类(非 static)外部类体中Outer$Inner.class不涉及(不能定义在方法里)与外部类成员同级,可用修饰符控制
静态嵌套类static class Nest {}Outer$Nest.class类似顶层类,被外部包围于命名空间
局部内部类方法 / 代码块中命名定义Outer$1Local.class取决于上下文:
• 实例方法 → 有
• 静态方法 → 无
只对引用的 final/effectively‑final 变量生成仅在声明块内部可见
匿名内部类new Super() { … }Outer$2.class(数字递增)同上同上仅通过引用持有


1. 命名解析 & 字段隐藏顺序

  1. 局部变量 / 形参

  2. 当前类自己声明的字段

  3. 从父类 / 接口继承的字段(最近父类优先)

  4. 封闭类(OuterOuter 的父类 …)层层向外

  5. 仍找不到 → 编译错误

一旦命中,外层同名字段被隐藏,必须用限定名 Outer.name 访问。


2. thisthis$0 与生命周期

场景隐式 thisthis$0 字段担当职责
实例成员方法有 (this 本类)访问本类字段
静态成员方法
非 static 内部类 / 局部类 / 匿名类(实例上下文)为内部类在方法返回后继续持有外部对象引用
相同但出现在静态上下文无外部实例可捕获


3. 捕获局部变量

步骤说明
① 判断变量是否被内部/匿名类引用若未引用 → 不做任何事
② 校验变量是否 finaleffectively‑final只赋值一次即可视为 effectively‑final(Java 8+)
③ 为每个被引用变量生成 val$… 字段final T val$var;
④ 在构造器首位插入同类型形参初始化快照:this.val$var = var;
⚠ 若变量后来再次赋值直接编译错误:必须保持 effectively‑final

可变需求 → 用可变对象(AtomicInteger、数组、StringBuilder…)或提升为外部类字段。


4. 本地类 & 匿名类的加载时机

  • 编译阶段始终产生独立的 .class 文件。

  • JVM 仅在首次执行到 new 该类的位置时才加载,因此调试器/类浏览器里看不到 = 还未加载。


5. 变量作用域与安全性动机

  • 方法栈帧消失 → 捕获值必须搬到堆上(内部类字段)。

  • 禁止二次赋值可避免“快照值”与原局部变量脱离同步。

  • this$0val$… 统一采用 final 字段 + 构造器注入 实现,简单、高效、易被 JIT 优化。


6. 其他易被忽略的细节

细节说明
枚举、注解 实际都是静态嵌套类编译器自动加 static
内部类访问权限修饰符成员内部类支持 public / protected / private / package;局部、匿名类没有显式修饰符,只在块内可见。
反射取到的字段名 this$0 / val$1标记为 synthetic,不计入 getDeclaredFields() 默认返回(可见性受 setAccessible 控制)。
Outer.this 语法仅在非静态内部/局部/匿名类中合法;静态上下文写它直接编译错误。


7. 速查结论

问题答案一行说明
局部/匿名类必须捕获外部实例吗?仅当定义在实例上下文才捕获;静态上下文无 this$0
捕获局部变量一定要 final 吗?Java 8+ 只需 “赋值一次” 即视为 effectively‑final。
能在内部类里给局部变量重新赋值吗?不行;重赋值破坏 effectively‑final,编译失败。
静态方法里的局部内部类能访问外部类字段吗?只能访问外部类的 static 字段;无外部实例引用。


参考思维导图
内部类
├─ 成员内部类 (非static)
│   ├─ 拥有 this$0
│   └─ 与外部字段/方法互访
├─ 静态嵌套类
│   └─ 无 this$0,似顶层类
├─ 局部类
│   ├─ 实例环境 → 有 this$0
│   └─ 静态环境 → 无 this$0
└─ 匿名类
    ├─ 文件名 Outer$N.class
    ├─ 规则同局部类
    └─ 依赖实例上下文与否决定 this$0


总结
Java 为了让内部类安全且高效地访问外部作用域,采用“构造期捕获 + final 快照”策略:

  • this$0 捕获外部实例(仅在实例上下文)

  • val$… 捕获局部变量(必须 final/effectively‑final)
    通过禁止后续重新赋值,避免生命周期错位导致的数据不一致,同时保持 JVM 对象模型简单,利于 JIT 优化。

补充:

匿名内部类的构造方法数量永远只有 1 个——编译器自动合成,源码里不能再写其它构造器。

public class Test2 {public static void main(String[] args) {//  匿名内部类1 (静态方法里面)Animal dog = new Animal(){@Overridepublic void eat() {System.out.println("啃骨头");}};dog.eat();}//  匿名内部类2 (成员变量位置)Animal cat = new Animal("小花"){@Overridepublic void eat() {System.out.println("吃老鼠");}};public void show(){//  匿名内部类3 (成员方法里面)int num = 10;Animal tiger = new Animal("小虎",10){@Overridepublic void eat() {System.out.println("吃牛排");}public void getNum(){System.out.println(num);}};}
}

javap反编译后的匿名内部类结构图: 

相关文章:

  • 开源夜莺支持MySQL数据源,更方便做业务指标监控了
  • Vision Prompt Tune(视觉提示微调)
  • 在ARM 架构的 Mac 上 更新Navicat到17后连接Oracle时报错:未加载 Oracle 库。
  • Windows 系统中修改文件默认打开方式
  • Java多线程实现之线程池详解
  • 机器人教学和实践的可编程智能仿生机器人平台——智能六足机器人
  • 临时抱佛脚v2
  • Vue Electron 使用来给若依系统打包成exe程序,出现登录成功但是不跳转页面(已解决)
  • OpenSSL 无法验证 DevSidecar 的自签名证书
  • 目标检测yolo算法
  • Windows 上安装 devsidecar 后,使用 WSL ubuntu ssl 报错
  • 机器视觉开发-边缘提取
  • Java-43 深入浅出 Nginx - 基本配置方式 nginx.conf Events块 HTTP块 反向代理 负载均衡
  • 永磁同步电机无速度算法--基于稳态卡尔曼滤波器SSEKF的滑模观测器
  • 实战使用docker compose 搭建 Redis 主从复制集群
  • 【docker】docker registry搭建私有镜像仓库
  • Linux 杀进程指令详解:`kill -9 PID` 和 `kill -15 PID` 有什么区别?
  • 云计算迁移策略:分步框架与优势
  • 开源生态新势能: 驱动国产 DevSecOps 与 AI 工程新进展
  • Vim鼠标右键复制问题解决方法
  • 网站后台编辑器编辑内容无法显示/免费b站推广网站2023
  • 广东网站设计流程/百度推广公司怎么代理到的
  • 汾阳网架公司/seo这个行业怎么样
  • 把网站做静态化/抖音推广合作方式
  • 西宁网站建设制作公司/搜索引擎营销优化的方法
  • 学校网站建设的wbs/大数据查询平台