一、前置知识点

1. 基本类型与包装类整体介绍

Java 有 8 种基本数据类型,为了让基本类型具备对象特性、可以赋值为 null、支持泛型、参与集合存储,每种基本类型都对应一个包装类

基本类型 对应包装类 占用字节 取值范围
byte Byte 1 字节 -128 ~ 127
short Short 2 字节 -32768 ~ 32767
int Integer 4 字节 -2147483648 ~ 2147483647
long Long 8 字节 很大
float Float 4 字节 小数
double Double 8 字节 小数
char Character 2 字节 0 ~ 65535
boolean Boolean 未定 true / false

核心区别总览

  1. 基本类型:不是对象,直接存数值,在栈内存,默认有初始值。
  2. 包装类:引用类型、对象,存堆内存,变量存对象地址,可以为 null,具备方法和属性。

2. 内存存储本质差异

基本类型 int

int a = 10;
  • 直接在栈内存开辟空间,直接存放数值 10
  • 没有对象、没有地址,只存值

包装类 Integer

Integer a1 = 10;
Integer a2 = new Integer(10);
  • 栈中存对象地址
  • 真实数值存在堆内存的对象里
  • 可以赋值为 Integer x = null;,基本类型不能为 null

3. 自动装箱与自动拆箱(编译期自动优化)

自动装箱

定义:基本类型 自动转为 包装类对象写法:

Integer num = 10;

编译器底层自动补全:

Integer num = Integer.valueOf(10);

自动拆箱

定义:包装类对象 自动转为 基本类型数值场景:包装类和基本类型运算、比较、赋值

int n = num;
boolean flag = num == 10;

编译器底层自动补全:

int n = num.intValue();
boolean flag = num.intValue() == 10;

总结:装箱用 valueOf(),拆箱用 intValue(),都是编译器自动完成


4. IntegerCache 整数缓存池(必考核心)

底层原理

Integer 内部有私有静态内部类 IntegerCache,提前预创建、缓存一批 Integer 对象。

  • 默认缓存范围:-128 ~ 127
  • 只要在这个范围,用 Integer 变量 = 数值 方式创建,复用缓存中同一个对象,地址完全一样。

两种创建方式区别

  1. Integer a = 10;底层调用 Integer.valueOf(10)走缓存,复用对象。
  2. new Integer(10)直接在堆中新建对象永远不走缓存,哪怕数值在 -128~127 也是全新对象。

缓存可修改(拓展知识点)

可以通过 JVM 启动参数调整缓存上限:

-XX:AutoBoxCacheMax=200

可以把缓存上限改成 200,-128~200 之间都复用缓存。


5. == 与 equals () 完整区分规则

比较场景 == 规则 equals () 规则
int 与 int 直接比较数值 无,基本类型没有方法
Integer 与 Integer 比较内存地址 Integer 重写了方法,只比较内部数值
Integer 与 int 包装类自动拆箱,比数值 基本类型自动装箱,比数值

核心口诀:

  • 基本类型 == 只比值
  • 包装对象 == 比地址
  • 包装对象等值比较只用 equals

6. 包装类特有特性(补充拓展)

  1. 包装类可以为 null
Integer x = null;  // 合法
int y = null;      // 编译报错

开发中接收前端参数、数据库字段,必须用包装类,允许为空。

  1. 支持泛型、集合存储集合只能存引用类型,不能存基本类型:
List<Integer> list = new ArrayList<>(); // 合法
List<int> list = new ArrayList<>();      // 编译报错
  1. 提供大量工具常量 / 方法
Integer.MAX_VALUE;   // int最大值
Integer.MIN_VALUE;   // int最小值
Integer.parseInt("123"); // 字符串转int
Integer.toString(100);   // 数值转字符串

二、经典例题插入 + 逐行精讲

原题代码

public class Main {
    public static void main(String[] args) {
        int   a  = 10;
        int   b  = 10;
        Integer a1 = 10;
        Integer b1 = 10;
        Integer a2 = new Integer(10);
        Integer b2 = new Integer(10);
        
        System.out.println(a == b);       // true
        System.out.println(a1 == b1);     // true
        System.out.println(a2 == b2);     // false
        System.out.println(a1 == a);      // true
        System.out.println(a1.equals(a)); // true
        System.out.println(a1 == a2);     // false
        System.out.println(a == a2);      // true
    }
}

内存结构文字图解

栈内存:
  a、b:直接存数值 10
  a1、b1:同时指向 IntegerCache 缓存中同一个对象
  a2、b2:分别指向堆中两个不同的 new Integer 对象

堆内存:
  缓存池:唯一一个数值为10的Integer对象
  new 对象1:a2 指向
  new 对象2:b2 指向

逐行结果 + 详细原理

代码 结果 详细解释
a == b true 两个 int 基本类型,== 直接比较数值 10=10
a1 == b1 true 自动装箱,数值 10 在 - 128~127,复用缓存同一个对象,地址相同
a2 == b2 false 两个 new Integer,堆中两个独立对象,地址不一样
a1 == a true Integer 和 int 比较,自动拆箱,转成 int 比数值
a1.equals(a) true equals 重写只比数值,a 自动装箱为 Integer 再比较
a1 == a2 false a1 是缓存对象,a2 是 new 新对象,地址不同
a == a2 true a2 自动拆箱为 int,和 a 比数值

三、高频易错陷阱

陷阱 1:超出缓存范围直接为 false

Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false

原因:128 超出默认缓存 -128~127,不再复用缓存,创建两个不同对象。

陷阱 2:new 对象永远不走缓存

Integer x = new Integer(10);
Integer y = new Integer(10);
System.out.println(x == y); // false

无论数值是否在缓存区间,只要 new,一定是新对象。

陷阱 3:包装类为 null 触发空指针

Integer x = null;
int y = x; // 运行空指针异常:拆箱时调用intValue()

开发中包装类转基本类型一定要判空

陷阱 4:不要用 == 判断包装类数值相等

业务开发、数据库比较、接口参数:一律使用 equals (),杜绝用 ==。


四、完整版终极背诵总结

  1. 基本类型存栈、存数值;包装类是对象,栈存地址、堆存数值。
  2. 自动装箱底层 valueOf(),自动拆箱底层 intValue()
  3. Integer 默认缓存范围 -128~127,范围内常量赋值复用同一对象。
  4. new Integer() 永远新建对象,不使用缓存。
  5. 基本类型 == 比数值;包装类 == 比地址,equals() 比数值。
  6. 包装类和基本类型比较,自动拆箱只比数值。
  7. 包装类可以为 null、支持泛型集合、拥有工具方法;基本类型不行。
  8. 开发中 Integer 数值是否相等,强制用 equals ()

 

更多推荐