Java-自动拆箱/装箱/缓存/效率

为什么基本类型需要包装类?

  • **泛型与集合支持问题:**基本数据类型在使用上虽然方便、简单且高效,但像泛型以及集合元素的存储等场景并不支持基本数据类型,而包装类可以解决这个问题,使其能更好地融入到一些需要对象类型的机制中。
  • **面向对象思维:**基本数据类型不符合面向对象编程的思维方式,包装类把基本数据类型 "包装" 成了对象形式,更契合面向对象的编程模式。
  • **提供实用方法:**包装类提供了很多实用的方法,例如 Integer 类中的 toHexString (int i) 可以将整数转换为十六进制字符串,parseInt (String s) 方法能把字符串解析为整数,方便了开发过程中的各种操作。

Long 或 Integer 如何比较大小?

  • 错误方法

使用 == 。因为 Long 与 Integer 都是包装类型,是对象。"==" 比较的是对象的引用地址,而不是对象所包装的基本类型的值。

使用 equals() 方法。因为 equals() 方法只能比较同类型的类,例如两个都是 Integer 类型。

  • 正确方法

先使用 LongValue() 或 intValue() 方法来得到它们的基本类型的值然后再使用 == 比较。

拆箱与装箱原理

装箱就是将基本数据类型转化为包装类型,那么拆箱就是将包装类型转化为基本数据类型。

示例:

java 复制代码
package org.example.a;
public class Demo{
    public static void main(String[] args) {

        //自动装箱,底层其实执行了Integer a=Integer.valueOf(10);
        Integer a = 10;

        //自动拆箱,底层其实执行了int b=a.intValue();
        int b = a;
    }
}

实例:自动拆装箱

实例1:基本类型与其包装类

java 复制代码
package org.example.a;
public class Demo {
    public static void main(String[] args) {
        int int1 = 12;
        int int2 = 12;
        Integer integer1 = new Integer(12);
        Integer integer2 = new Integer(12);
        System.out.println("int1 == int2         : " + (int1 == int2));
        System.out.println("int1 == integer1     : " + (int1 == integer1));
        System.out.println("integer1 == integer2 : " + (integer1 == integer2));
    }
}

运行结果:

int1 == int2 : true

int1 == integer1 : true

integer1 == integer2 : false

解释:

当进行"int1 == int2" 比较时,就是普通的基本类型值的比较,结果为 true。

对于"int1 == integer1" 比较,由于 Integer 与 int 进行 "==" 比较时,Integer 会自动拆箱成 int 类型,相当于两个 int 类型比较,所以结果也是 true。

而 "integer1 == integer2" 比较,因为它们是不同的对象实例(通过 new 创建的两个独立对象,即使包装的值相同,对象引用地址不同),所以结果为 false。

实例2:自动拆箱引起的异常

java 复制代码
package com.example.a;
public class Demo {
    public static void main(String[] args) {
        Integer a = null;
        System.out.println(2 == a);
    }
}

运行结果:

Exception in thread "main" java.lang.NullPointerException

at com.example.a.Demo.main(Demo.java:6)

解释:

因为基本类型与包装类型进行比较时会自动拆箱,也就是会调用 a.intValue () 方法来获取基本类型的值,但此时 a 为 null,调用 null 的方法就会报空指针异常(NullPointerException)。

实例3:基本类型与Object

java 复制代码
package org.example.a;
public class Demo {
    public static void main(String[] args) {
        Object object = true;
        System.out.println(object);
        boolean b = (boolean)object;
        System.out.println(b);
        System.out.println(b == true);
    }
}

运行结果:

true

true

true

解释:

在代码 Object object = true; 这一行中,发生了自动装箱(autoboxing)操作。对于boolean基本类型,其对应的包装类型是Boolean。当把true(基本类型boolean的值)赋给Object类型的变量object时,Java 会自动将true这个基本类型值包装成Boolean类型的对象,然后再赋值给object变量。从本质上来说,这一行代码等同于 Object object = new Boolean(true);,只不过编译器帮我们自动完成了从基本类型到包装类型的转换这一过程,这个机制使得代码编写更加简洁和方便,符合 Java 中面向对象操作的风格,因为在很多面向对象的场景里(比如将值存储在集合中,集合元素要求是对象类型),基本类型需要先转换为包装类型才能参与操作。

代码 boolean b = (boolean)object; 进行了强制类型转换操作。虽然object的实际类型是Boolean(前面经过自动装箱转换而来),但它声明的类型是Object,在 Java 中如果要获取其包装的基本类型的值,需要进行显式的类型转换。这里通过 (boolean) 这种强制类型转换的语法,告诉编译器要把object所代表的对象转换回基本类型boolean。编译器会检查object实际指向的对象是否能转换为boolean类型(在这里因为它本身就是由boolean值自动装箱而来的Boolean对象,所以是可以转换的),然后提取出其包装的true这个基本类型值,并赋值给boolean类型的变量b

执行 System.out.println(b == true);,这里就是简单的基本类型boolean值之间的比较,因为b的值就是true,所以比较结果为true,控制台输出 true

实例:缓存问题

java 复制代码
package org.example.a;
public class Demo{
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        Integer c = 128;
        Integer d = 128;
        System.out.println(a == b);
        System.out.println(c == d);
    }
}

执行结果:

true

false

解释:

valueOf() 源码:

java 复制代码
//private static class IntegerCache {
    //    static final int low = -128;
    //    static final int high = 127;
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

Integer 有一个缓存机制,在其 valueOf 方法的实现中,会判断传入的 int 值是否在缓存范围([-128, 127])内,如果在这个范围内,就会直接返回 Integer 缓存数组中相应对象的引用,如果超出这个范围(大于 127 或小于 -128),则会重新创建一个 Integer 实例并返回。

实例代码中,定义了 Integer 类型变量 a、b 赋值为 127,c、d 赋值为 128,"a == b" 比较结果为 true,是因为 127 在缓存范围内,a 和 b 指向的是缓存中同一个 Integer 对象;而 "c == d" 比较结果为 false,因为 128 超出了缓存范围,c 和 d 是通过 new 创建出来的不同对象。

其他包装类的缓存:

Byte、Short、Integer、Long、Character的valueOf()实现机制类似。

|--------------------|-------------------------------------------------------------------|
| 包装类 | 说明 |
| Byte | 相同值的Byte比较永远返回true。因为byte取值范围就是[-128,127]。 |
| Short、Integer、Long | 相同值在[-128,127]则返回true,不在则返回false |
| Character | 要返回true,只需保证i <= 127。因为char最小值为0,本来就大于等于-128。 |
| Float、Double | 永远返回false。因为其永远返回新创建的对象,因为一个范围内的整数是有限的,但是小数却是无限的,无法保存在缓存中。 |
| Boolean | 只有两个对象,要么是true的Boolean,要么是false的Boolean,只要boolean的值相同,Boolean就相等。 |

实例:效率问题

java 复制代码
package org.example.a;
public class Demo {
    public static void main(String[] args) {
        long t1 = System.currentTimeMillis();
        Long sum = 0L;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
    }
}

运行结果:

6218

如果将 sum 改为 long (基本类型),运行结果:

624

解释:

在涉及大量数值运算的场景中,使用基本类型通常比使用对应的包装类型效率更高,就是因为包装类型存在自动拆箱、装箱以及复杂的对象内存管理等额外开销,而基本类型能更直接快速地进行数值处理。

相关推荐
带刺的坐椅6 分钟前
qwen3 惊喜发布,用 ollama + solon ai (java) 尝个鲜
java·spring·solon·solon-ai·qwen3
香蕉可乐荷包蛋29 分钟前
Python面试问题
开发语言·python·面试
ErizJ39 分钟前
Golang|分布式索引架构
开发语言·分布式·后端·架构·golang
.生产的驴40 分钟前
SpringBoot 接口国际化i18n 多语言返回 中英文切换 全球化 语言切换
java·开发语言·spring boot·后端·前端框架
Howard_Stark44 分钟前
Spring的BeanFactory和FactoryBean的区别
java·后端·spring
饮长安千年月44 分钟前
学生管理系统审计
java·网络安全·代码审计
八股文领域大手子1 小时前
深入浅出限流算法(三):追求极致精确的滑动日志
开发语言·数据结构·算法·leetcode·mybatis·哈希算法
-曾牛1 小时前
Spring Boot中@RequestParam、@RequestBody、@PathVariable的区别与使用
java·spring boot·后端·intellij-idea·注解·spring boot 注解·混淆用法
新时代苦力工1 小时前
处理对象集合,输出Map<String, Map<String, List<MyObject>>>格式数据,无序组合键处理方法
java·数据结构·list
niesiyuan0001 小时前
MAC如何安装多版本jdk(以8,11,17为例)
java