Java基础学习笔记--对象基本类型

1. 对象类型 vs 基本类型 & 不可变性原理

  • 基本类型(如 int:变量中直接存储的是值本身。赋值 a = 20 时,是直接覆盖内存中原来的值,不会创建新对象。它们天生为极致性能而生,不存在于堆内存中。
  • 对象类型(如 String, Integer:变量中存储的是指向堆内存中真实对象的内存地址(引用)
  • 不可变性原理:像 StringInteger 这样的对象,一旦创建,其内部状态就永远不能改变。当你执行 s = "World"a = 20 时,JVM 不会去修改原对象,而是在堆内存中新建一个对象,然后让变量指向新地址(即“换人”而不是“换心”)。这保证了线程安全和作为 HashMap Key 时的稳定性。

2. 缓存池与常量池的运作机制

  • String 常量池:JVM 在内存中开辟的“公共仓库”。相同的字符串字面量在池中只保留一份,所有变量指向同一个对象。
  • Integer 缓存池(IntegerCache):JVM 在启动时,提前在内存中创建好特定范围的 Integer 对象(本质是一个静态数组)。通过 Integer.valueOf() 或自动装箱时,直接复用池中的对象,避免频繁 new 对象带来的内存和 GC 开销。
  • 全局唯一性无论是 String 常量池还是 Integer 缓存池,在整个 JVM 进程中都只会存在一份。 它们属于类级别的静态资源,伴随 JVM 的整个生命周期,全 JVM 共享,绝对不会被垃圾回收(GC)。
  • 核心区别:基本类型没有缓存池;对象类型通过缓存池实现高频对象的复用。

3. -128 到 127 的意义、配置及越界处理

  • 意义:这是 JVM 在“内存占用”和“命中率”之间找到的黄金平衡点。现实业务中(如循环索引、状态码等)这个范围内的数字出现频率极高,缓存它们能拦截绝大部分的内存开销。
  • 配置最大值:可以通过 JVM 启动参数 -XX:AutoBoxCacheMax=2000 手动扩大缓存池范围,以适应特定业务场景。
  • 超出范围的处理:如果数值不在缓存池范围内(如 129),JVM 绝对不会扩容缓存池,而是直接在堆内存中 new 一个全新的对象。程序正常运行,只是享受不到缓存池的性能红利。

4. 变量的存储位置与缓存的关系

  • 局部变量:存储在栈内存(Stack)中,方法执行完毕后,变量(门牌号)立刻销毁。
  • 全局/成员变量:存储在堆内存(Heap)中,随着对象一起存在。
  • 与缓存池的关系:无论是局部还是全局变量,当赋值为 -128~127 之间的数时,它们拿到的都是同一个缓存池对象的地址。局部变量销毁时,仅仅是断开了指向缓存池的线,缓存池里的对象永远不会被垃圾回收(GC)

5. equals 与 == 比较的原理

  • == 比较:比较的是内存地址。对于基本类型,比较的是值;对于对象类型,比较的是指针是否指向堆中的同一个对象。
  • .equals() 比较:比较的是对象的内部内容
  • 铁律:因为 HashMap 依赖这两个方法,所以重写 equals() 必须同时重写 hashCode()。否则会导致内容相同的对象被散列到不同的桶中,引发逻辑 Bug。

6. HashCode 的生成机制

  • 延迟生成(懒加载):对象在 new 的时候,不会立刻生成 HashCode。只有当第一次调用 .hashCode() 方法时,JVM 才会进行计算,并将其固化在对象的“对象头(Mark Word)”中。
  • 重写的特例:像 String 这样重写了 hashCode 的类,每次调用都会根据内容实时计算,不会存入对象头。

7. 哈希冲突(Hash Collision)与 HashMap 底层

  • 必然重复:因为 int 的范围有限(约 42 亿),而现实数据无限,根据“抽屉原理”,不同的对象算出相同的 HashCode 是必然的。
  • 解决机制:HashCode 只是“敲门砖”,负责把数据定位到 HashMap 的同一个“桶(Bucket)”中。真正决定数据是否覆盖的,是桶内通过 .equals() 进行的“验明正身”。
  • 链表与红黑树:如果发生严重冲突,数据会在同一个桶内形成链表。当链表长度超过 8 时,JVM 会将其转化为“红黑树”,将查找时间复杂度从 O(n)O(n) 优化到 O(log⁡n) O(logn) ,防止系统崩溃。
posted @ 2026-08-30 22:50  倾听-静轩水月  阅读(4)  评论(0)    收藏  举报