【java】——自动拆箱和装箱的坑

探讨Java中空指针异常的根源,特别是由自动拆箱引发的问题,及Integer对象在不同情况下的比较行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

坑介绍,具体请先看代码

public void test1(){
        Integer a = null;
        int b = 6;
        if(b > a){
            System.out.println("a>b");
        }
    }

上面的代码编译没有什么问题,但是运行就报错空指针,具体原因就是自动拆箱引起的,

代码分析

定义a和b,在if中做判断的时候,因为b是int类型,因为会做值比较,a为包装类型,如果需要做值比较就需要拆箱,对a做拆箱

需要执行a.intValue()方法,但是a对null,因此会报空指针错误

 

额外知识点

1、如果“==”两边都是引用对象类型,则比较的是对象的内存地址是否一样,如果其中一个是基本类型,则需要拆箱做值比较

除了"==",+、-、*、/ 运算都会做拆箱操作

代码

public void test3(){
        Integer a = 200;
        Integer b = 200;
        int c = 200;
        System.out.println(a==b);//false
        System.out.println(a==c);//true
    }

2、当Interger 类型的变量赋值为(-128,128]即装箱操作(valueOf()),用"=="作比较,返回为true,原因是jdk为我们默认生成好了,这256个对应的Integer数组,而不需要重新new新的对象,jdk中对可以枚举的都做了这样的初始化,包括,Integer、Long、Short、Charactor;但是对Double、Float则不会这样初始化操作

代码

public void test4(){
        Integer a = 100;
        Integer b = 100;
        int c = 100;
        System.out.println(a==b);//true
        System.out.println(a==c);//true
    }

3、八种基本类的包装对象的equals(Object o ),不仅要对应类型,还要对比具体的值 ;而对于其他对象类型比较的是内存的地址,但是String类的equals方式是重写了的,是比较的对象的值而不是内存地址

代码

public void test5(){
        String[] str1 = {"1"};
        String[] str2 = {"1"};
        String s1 = "aa";
        String s2 = "aa";
        String s3 = new String("aa");
        System.out.println(str1.equals(str2));//false
        System.out.println(s1==s2);//true
        System.out.println(s1==s3);//false
        System.out.println(s1.equals(s2));//true
        System.out.println(s1.equals(s3));//true

    }

其中为什么"s1==s2" 返回的是true呢,需要注意,

String s1 = "aa";

实际上是两个步骤,首先创建了常量"aa",然后在创建String s1 ,然后在把s1 指向常量池中"aa" 的内存地址,在执行

String s2 = "aa";

的时候,现在常量池中查找是否有"aa"这个常量,如果有就直接把s2指向"aa"这个常量的内存地址,因此上述代码中s1==s1返回为true

### 装箱拆箱的概念及原理 #### 定义与基本概念 装箱是指将值类型(如整数、浮点数等)转换为引用类型的对象的过程。此过程涉及创建一个新的对象实例并将该值复制到新对象中[^1]。 拆箱则是相反的操作,即将引用类型的数据重新转换回原始的值类型数据。这通常涉及到从对象中提取出存储的值并将其赋给相应的值类型变量。 ```csharp // C# 中的装箱操作示例 static void SimpleBoxUnbox() { int myInt = 25; object boxedInt = myInt; // 将 int 类型的数值放入 object 对象中 } // C# 中的拆箱操作示例 static void UnboxingExample() { object boxedInt = 25; int unboxedInt = (int)boxedInt; // 把 object 对象里的值取出来转成 int 类型 } ``` #### 性能考量 尽管装箱拆箱提供了灵活性,允许不同种类的数据可以被统一处理,然而这些操作会带来额外开销。每当发生一次装箱时,会在托管堆上分配新的内存用于存放这个临时的对象;频繁执行这样的操作可能会显著降低应用程序性能[^3]。 为了提高效率,在设计代码逻辑时应尽可能减少不必要的装箱行为。例如,使用泛型集合代替传统的非泛型集合能够有效规避这一问题,因为前者可以在编译期间保持原生的值类型而不必经历装箱/拆箱流程[^4]。 #### 不同编程语言间的差异 值得注意的是,在不同的面向对象的语言里实现上述机制的方式有所区别: - **C#:** 使用隐式的转型语法完成装箱(`object obj = valType`)以及显式强制转换来进行拆箱(`valType = (ValType)obj`)[^1]。 - **Java:** 同样支持自动化的装箱拆箱功能,不过具体语法规则略有差别——比如通过 `Integer.valueOf(int)` 方法手动触发装箱动作,而在某些情况下也会自动生成对应的指令来简化开发者的编码工作[^2]。 总之,理解这两种技术背后的运作机理对于编写高效且易于维护的应用至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值