LongCache机制与Long等值比较

文章讨论了在Java中使用Long等封装类型进行等值比较时可能遇到的问题,特别是LongCache机制如何影响到==操作的结果。作者建议在开发涉及金钱的项目时避免依赖自动转型,而是使用Long.equals()等方法进行比较。内容还涵盖了LongCache的内部实现以及Integer等其他封装类的类似机制,并提到了相关JVM参数和优化建议。

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

一、背景引入
近期在开发一个项目的后台时,当项目上线后出现了一个测试环境没有出现的问题:部分用户在提交信息时提示了该信息不属于当前用户。
经过对代码的review,发现了出错的代码的开发逻辑是,在用户提交信息之后,将信息更新入数据库之前,首先判断当前的信息是否属于当前的用户;通过信息.account_id == 用户.id进行了判断,两个id都是Long类型。直觉是使用==号出错的问题,于是将此处的代码修改为先使用Long.longValue()取出来,再进行比较。(或者使用Long.equals()方法亦可)
然而有一个相当有趣的情况,那就是测试环境并没有这样的问题。究其原因,竟是因为Long类型的LongCache机制引起,且因为测试环境的模拟用户数据量较少,没有突破LongCache内部缓存数组的边界,导致该问题没能及时在测试环境发现。


二、编程建议

在Java开发过程中,最好严格区分原始类型(long/int/short/...)和封装类型(Long/Integer/Short...),虽然JDK能够在大多数情况下进行智能的转型,但是当面对开发一个涉及到金钱的项目时,这样的风险仍然太大!

Java在数据封装类型的设计中,都带了.equals()方法。


三、模拟

1、程序1

public class A {
    public static void main(String[] args) {
        Long l1 = 128L;
        Long l2 = 128L;
        System.out.println(l1 == l2); 
    }
}

程序输出:false

2、程序2

public class A {
    public static void main(String[] args) {
        Long l1 = 127L;
        Long l2 = 127L;
        System.out.println(l1 == l2); 
    }
}

程序输出:true


三、关于LongCache.class

在Long的源代码中,可以找到LongCache内部类的代码:

private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }

从LongCache的代码可以很容易看出来,在类初始化的时候,便生成了一个final的static的Long类型数组,数组的范围是-128到127。

Long类型的valueOf方法代码如下:

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
从以上两个代码中不难发现,当外部程序调用了valueOf方法时,Java先判断欲生成的Long对象是否在LongCache.cache数组的范围内,如果是,则直接返回已经存在cache数组的Long对象引用。根据之前对String类型的研究,这应该是通过重复使用对象的引用,从而实现了较高的性能和较少的内存消耗。此外,经过测试,通过“Long = long”方式生成的Long对象,称为自动封箱,也有相同的逻辑。
所以也就不难知道,当实际的long大小超过正数127时,判断两个封装类==时,会返回false。

另外一种情况,通过new方式生成的两个等值的对象,是否会有相同的效果呢?经过测试,是否定的。两次new出来的对象,都是在内存中新划分区域生成的对象,除非重写方法,否则是绝对不能通过==进行比较的。



四、其他内容

1、Integer、Short、Character、Bytes等封装类也有类似的机制;

2、请关注JVM参数:AutoBoxCacheMax

3、请关注Integer内部类IntegerCache的high属性。






<think>好的,我现在要详细回答用户关于Javalong数据类型的问题。首先,我需要回忆和整理自己了解的关于Java long类型的知识,并参考提供的引用资料,确保信息准确无误。 首先,longJava的8种基本数据类型之一,属于整数类型,占用8个字节(64位),范围从-2^63到2^63-1。这比int类型更大的范围,适用于需要处理大整数的情况。用户提到想了解long的详细信息,所以我需要覆盖声明、初始化、默认值、使用场景,以及注意事项,比如字面量后缀L的使用。 接下来,查看提供的引用。引用[2]中提到static Long getLong等方法,这可能涉及Long包装类的方法,但用户主要关注基本类型,可能需要简要提到包装类及其基本类型的自动装箱拆箱。引用[3]中的缓存机制虽然主要针对Integer,但可能Long也有类似的缓存机制,比如LongCache,通常在-128到127之间,但用户可以修改JVM参数来调整,不过这可能超出基本类型范围,属于包装类的部分,需要区分清楚。 然后,需要结构清晰,分点讲解。例如,声明和初始化部分,给出代码示例;范围部分用数学公式表示;注意事项强调L的大小写;应用场景列举如时间戳、大文件处理等。同时,对比int和long的区别,帮助用户理解何时选择long。 还要注意用户不可见的系统指令,比如行内数学表达式用$...$,独立公式用$$...$$,但当前回答可能较少涉及复杂公式,可能只需在范围部分使用。例如,范围可以写成$-2^{63}$到$2^{63}-1$,用行内公式格式。 另外,需要生成相关问题,可能涉及long包装类Long的区别、溢出问题、性能影响、字面量后缀的必要性等。这些问题应自然延伸,帮助用户深入学习。 最后,检查LaTeX语法是否正确,确保所有数学表达式正确使用$符号,没有语法错误。同时,引用标注要正确,比如在提到缓存时引用[3],提到包装类方法时引用[2],但需要注意引用内容是否相关。例如,引用[2]中的getLong方法属于Long包装类,可能在讲解包装类时提及,但用户问题主要关于基本类型,可能需要简要带过。 可能存在的疑问:是否需要详细讲解自动装箱拆箱?用户的问题是关于long基本类型,但了解其包装类的关系也很重要,所以可以简单提及,并引导相关问题。此外,缓存机制Long中是否存在,是否需要说明?根据Java规范,Long的缓存范围是固定的,不像Integer可以调整,所以引用[3]中的例子可能不适用,但需要确认准确性。可能这里需要谨慎,避免错误信息。 总结回答结构: 1. 基本介绍:定义、内存占用、范围(用数学公式) 2. 声明初始化:代码示例,注意L后缀 3. 默认值:0L 4. 应用场景:大整数需求的情况 5. 注意事项:L的使用,int的区别 6. 相关类:Long包装类,自动装箱拆箱,缓存机制(简要) 7. 生成相关问题 确保每个部分简明扼要,符合用户需求。现在开始组织语言,使用中文,遵循格式要求,检查LaTeX是否正确,引用适当。</think>### Java long 数据类型详解 #### 1. 基本特性 - **定义**:`long` 是 Java 的**基本数据类型**之一,用于表示**64位有符号整数**,范围为: $$-2^{63} \text{ 到 } 2^{63}-1$$ 即 $-9,223,372,036,854,775,808$ 至 $9,223,372,036,854,775,807$。 - **内存占用**:固定占用 **8 字节**,操作系统无关。 - **默认值**:若未显式初始化,默认值为 `0L`。 #### 2. 声明初始化 ```java long a = 100L; // 显式添加 L 后缀 long b = -200; // 编译器自动推断为 long(值在 int 范围内时) long c = 3_000_000_000L; // 超出 int 范围时必须加 L ``` #### 3. 注意事项 - **字面量后缀**:若数值超出 `int` 范围($2^{31}-1$),必须添加 `L` 或 `l` 后缀,推荐大写 `L` 避免数字 `1` 混淆。 - ** `int` 的区别**:`int` 为 32 位,范围更小($-2^{31}$ 到 $2^{31}-1$),而 `long` 可存储更大整数。 #### 4. 应用场景 1. **时间戳处理**:存储毫秒级时间戳(如 `System.currentTimeMillis()`)。 2. **大文件操作**:处理文件大小(字节数)时避免溢出。 3. **科学计算**:涉及天文数字或高精度计算的场景。 #### 5. 相关工具类:`Long` - **自动装箱/拆箱**:`long` 包装类 `Long` 可自动转换: ```java Long boxed = 1000L; // 自动装箱 long unboxed = boxed; // 自动拆箱 ``` - **缓存优化**:`Long` 类默认缓存 `-128` 到 `127` 的值(类似 `Integer`[^3]),超出范围的实例会新建对象。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值