IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> 来一篇 自动装箱,拆箱 -> 正文阅读

[开发工具]来一篇 自动装箱,拆箱

🌲问题来源

??偶然发现<<深入理解jvm>>出了第三版,于是便又拿起来看了,里面有这样的一个代码段,又遇到了自动拆箱,装箱问题,于是一网打尽吧。

 Integer a = 1;
 Integer b = 2;
 Integer c = 3;
 Integer d = 3;
 Integer e = 321;
 Integer f = 321;
 Long g = 3L;
 System.out.println(c == d);
 System.out.println(e == f);
 System.out.println(c == (a + b));
 System.out.println(c.equals(a + b));
 System.out.println(g == (a + b));
 System.out.println(g.equals(a + b));

----output----
true
false
true
true
true
false

🌲慢慢分析

??什么是自动装箱呢?简而言之,把int转变成Integer 基本类型转变成包装类型,拆箱就是逆操作。

 Integer a = 1;

??这段很简单的代码就是一个装箱过程,怎么装箱的呢?通过IDEA调试的时候,选择force step into,就可以看见来到了这样的代码:

 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转变成为了包装类型。Integer内部提供了[-128, 127]这个区间的缓存,如果说你的这个值是这个区间内的,就从缓存中拿出来给你,这个缓存也就是一个数组Integer cache[],如果超过了就是新的一个Integer对象,我还是贴一下这里的代码:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

??小知识点:需要注意的是,这个数组Cache只有在第一次使用的时候才会被初始化,原因是,外部类会在jvm启动的时候初始化,执行static代码块,但是静态内部类的代码块并不会执行。可以用下面的代码进行测试:

public class Test {

    static {
        System.out.println("外部类执行了static代码");
    }

    private static class Test22 {
        static {
            System.out.println("内部类执行static代码");
        }
    }

    public static void main(String[] args) {

    }
}

----output----
外部类执行了static代码

回到最初的代码:

  • System.out.println(c == d);:==比较的是地址,c和d都属于缓存内的同一个Integer对象,所以是True 很好理解。
  • System.out.println(e == f);:同理因为超过了那个范围不是一个对象,地址自然不同。
  • System.out.println(c == (a + b));:首先a+b,当包装类型遇到加法的时候会先拆箱,因为+用于基本类型计算,所以应该是两个int基本类型进行比较的,我们调试看看。
    public int intValue() {
        return value;
    }
    
    会执行三次这个方法,返回Integer的基本数据类型,所以最后是3==3进行比较的。
  • System.out.println(c.equals(a + b));a+b必然是基本类型3,那么看一下c.equals()方法执行过程。调试:
    // 对a,b执行这个方法
    public int intValue() {
       return value;
    }
    
    // 对a+b int3执行,得到包装类型Integer(3)的缓存。
    public static Integer valueOf(int i) {
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
    }
    
    //调用equals方法
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    
    值得注意的是如果不是两个Integer类型比较,不管你数值上对不对都是直接返回false的。System.out.println(g.equals(a + b));这个就是最好的证明,结果是False。

🌲对于数据计算一劳永逸的方法

不管是浮点数,整数记性计算,都可以用BigDecimal包装一下,既不会出现丢失的问题,也不会出现因为跨类型问题导致的判断错误。demo如下:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");

BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);

if (x.compareTo(y) == 0) {
System.out.println("true");
}

可能内容和别人差不多,但是自己走一遍会印象深刻,下次也好及时拾起来复习。如有错误,还望多多指教。

后记

最近写的博客少了,不是因为不写,而是觉得没有让我感觉perfect的idea我就不会再写了,不做知识搬运工,不做copy,已有的内容只做整理,让我惊艳的东西再做记录。免得自己都是流水账,哈哈哈哈。下一篇博客估计没多久就来了,因为集合的代码着实让人赞不绝口,还有很多网上搜不到正确答案的点。期待吧。

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:26:56  更:2021-07-27 16:28:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 3:18:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码