java核心内容——int和Integer有什么区别?

2023-05-16

java核心内容——int和Integer有什么区别?

    • 1、典型回答
    • 2、知识扩展
        • 1. 理解自动装箱、拆箱
        • 2. 源码分析
        • 3. 原始类型线程安全
        • 4.Java 原始数据类型和引用类型局限性

1、典型回答

int 是我们常说的整形数字,是 Java 的 8 个原始数据类型(Primitive Types,boolean、byte 、short、char、int、float、double、long)之一。Java 语言虽然号称一切都是对象,但原始数据类型是例外。

Integer 是 int 对应的包装类,它有一个 int 类型的字段存储数据,并且提供了基本操作,比如数学运算、int 和字符串之间转换等。在 Java 5 中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java 可以根据上下文,自动进行转换,极大地简化了相关编程。

关于 Integer 的值缓存,这涉及 Java 5 中另一个改进。构建 Integer 对象的传统方式是直接调用构造器,直接 new 一个对象。但是根据实践,我们发现大部分数据操作都是集中在有限的、较小的数值范围,因而,在 Java 5 中新增了静态工厂方法 valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照 Javadoc,这个值默认缓存是 -128 到 127 之间。

2、知识扩展

1. 理解自动装箱、拆箱

自动装箱实际上算是一种语法糖。什么是语法糖?可以简单理解为 Java 平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的。

像前面提到的整数,javac 替我们自动把装箱转换为 Integer.valueOf(),把拆箱替换为 Integer.intValue(),这似乎这也顺道回答了另一个问题,既然调用的是 Integer.valueOf,自然能够得到缓存的好处啊。

如何程序化的验证上面的结论呢?

你可以写一段简单的程序包含下面两句代码,然后反编译一下。当然,这是一种从表现倒推的方法,大多数情况下,我们还是直接参考规范文档会更加可靠,毕竟软件承诺的是遵循规范,而不是保持当前行为。

Integer integer = 1;
int unboxing = integer ++;

反编译输出:

1: invokestatic  #2                  // Method
java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
8: invokevirtual #3                  // Method
java/lang/Integer.intValue:()I

这种缓存机制并不是只有 Integer 才有,同样存在于其他的一些包装类,比如:

Boolean,缓存了 true/false 对应实例,确切说,只会返回两个常量实例 Boolean.TRUE/FALSE。

Short,同样是缓存了 -128 到 127 之间的数值。

Byte,数值有限,所以全部都被缓存。

Character,缓存范围’\u0000’ 到 ‘\u007F’。

自动装箱 / 自动拆箱似乎很酷,在编程实践中,有什么需要注意的吗?

原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。

我们其实可以把这个观点扩展开,使用原始数据类型、数组甚至本地代码实现等,在性能极度敏感的场景往往具有比较大的优势,用其替换掉包装类、动态数组(如 ArrayList)等可以作为性能优化的备选项。一些追求极致性能的产品或者类库,会极力避免创建过多对象。当然,在大多数产品代码里,并没有必要这么做,还是以开发效率优先。以我们经常会使用到的计数器实现为例,下面是一个常见的线程安全计数器实现。

class Counter {
    private final AtomicLong counter = new AtomicLong();  
    public void increase() {
        counter.incrementAndGet();
    }
}

如果利用原始数据类型,可以将其修改为

 class CompactCounter {
    private volatile long counter;
    private static final AtomicLongFieldUpdater<CompactCounter> updater = AtomicLongFieldUpdater.newUpdater(CompactCounter.class, "counter");
    public void increase() {
        updater.incrementAndGet(this);
    }
}

2. 源码分析

考察是否阅读过、是否理解 JDK 源代码可能是部分面试官的关注点,这并不完全是一种苛刻要求,阅读并实践高质量代码也是程序员成长的必经之路,下面我来分析下 Integer 的源码。

整体看一下 Integer 的职责,它主要包括各种基础的常量,比如最大值、最小值、位数等;前面提到的各种静态工厂方法 valueOf();获取环境变量数值的方法;各种转换方法,比如转换为不同进制的字符串,如 8 进制,或者反过来的解析方法等。我们进一步来看一些有意思的地方。

  • 首先,继续深挖缓存,Integer 的缓存范围虽然默认是 -128 到
    127,但是在特别的应用场景,比如我们明确知道应用会频繁使用更大的数值,这时候应该怎么办呢?

缓存上限值实际是可以根据需要调整的,JVM 提供了参数设置:

-XX:AutoBoxCacheMax=N

这些实现,都体现在java.lang.Integer源码之中,并实现在 IntegerCache 的静态初始化块里。

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 =VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            ...
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
        ...
  }
  • 第二,我们在分析字符串的设计实现时,提到过字符串是不可变的,保证了基本的信息安全和并发编程中的线程安全。如果你去看包装类里存储数值的成员变量“value”,你会发现,不管是
    Integer 还 Boolean 等,都被声明为“private final”,所以,它们同样是不可变类型!

这种设计是可以理解的,或者说是必须的选择。想象一下这个应用场景,比如 Integer 提供了 getInteger() 方法,用于方便地读取系统属性,我们可以用属性来设置服务器某个服务的端口,如果我可以轻易地把获取到的 Integer 对象改变为其他数值,这会带来产品可靠性方面的严重问题。

  • 第三,Integer 等包装类,定义了类似 SIZE 或者 BYTES 这样的常量,这反映了什么样的设计考虑呢?如果你使用过其他语言,比如
    C、C++,类似整数的位数,其实是不确定的,可能在不同的平台,比如 32 位或者 64 位平台,存在非常大的不同。那么,在 32 位
    JDK 或者 64 位 JDK 里,数据位数会有不同吗?或者说,这个问题可以扩展为,我使用 32 位 JDK 开发编译的程序,运行在 64
    位 JDK 上,需要做什么特别的移植工作吗?

其实,这种移植对于 Java 来说相对要简单些,因为原始数据类型是不存在差异的,这些明确定义在Java 语言规范里面,不管是 32 位还是 64 位环境,开发者无需担心数据的位数差异。

对于应用移植,虽然存在一些底层实现的差异,比如 64 位 HotSpot JVM 里的对象要比 32 位 HotSpot JVM 大(具体区别取决于不同 JVM 实现的选择),但是总体来说,并没有行为差异,应用移植还是可以做到宣称的“一次书写,到处执行”,应用开发者更多需要考虑的是容量、能力等方面的差异。

3. 原始类型线程安全

前面提到了线程安全设计,你有没有想过,原始数据类型操作是不是线程安全的呢?

这里可能存在着不同层面的问题:

原始数据类型的变量,显然要使用并发相关手段,才能保证线程安全,这些我会在专栏后面的并发主题详细介绍。如果有线程安全的计算需要,建议考虑使用类似 AtomicInteger、AtomicLong 这样的线程安全类。

特别的是,部分比较宽的数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!

4.Java 原始数据类型和引用类型局限性

前面我谈了非常多的技术细节,最后再从 Java 平台发展的角度来看看,原始数据类型、对象的局限性和演进。

对于 Java 应用开发者,设计复杂而灵活的类型系统似乎已经习以为常了。但是坦白说,毕竟这种类型系统的设计是源于很多年前的技术决定,现在已经逐渐暴露出了一些副作用,例如:

原始数据类型和 Java 泛型并不能配合使用
这是因为 Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为 Object。

无法高效地表达数据,也不便于表达复杂的数据结构,比如 vector 和 tuple
我们知道 Java 的对象都是引用类型,如果是一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组则不然,数据存储的是引用,对象往往是分散地存储在堆的不同位置。这种设计虽然带来了极大灵活性,但是也导致了数据操作的低效,尤其是无法充分利用现代 CPU 缓存机制。

Java 为对象内建了各种多态、线程安全等方面的支持,但这不是所有场合的需求,尤其是数据处理重要性日益提高,更加高密度的值类型是非常现实的需求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java核心内容——int和Integer有什么区别? 的相关文章

随机推荐

  • Python实现观测值o文件和精密星历sp3文件读取

    博主之前准备利用Python编写精密单点定位程序 xff0c 奈何写了一半的读取文件代码 xff0c 觉得太浪费时间 xff0c 就此作罢 xff0c 这些时间不如多用来研究现有代码 xff0c 把这部分放弃的代码拿出来 xff0c 希望给
  • webpack4+react+antd从零搭建React脚手架(四)-redux搭建

    redux 文档地址 redux是对数据的状态管理 xff0c 是react不可缺少的一部分 xff0c 具体的概念这里就不进行详细的介绍 本文主要是介绍怎么引入redux和使用redux 单向数据流 xff1a 从父组件流向子组件 xff
  • Python:入门到实践-安装

    Python 入门到实践 安装 安装python环境安装启动终端会话HelloWorld 安装python环境 写在前面 xff0c 本文是基于Windows10系统下学时 xff0c 其他环境下 请自行研究 python版本是3 6 1
  • 阿里云部署web项目

    开始 xff08 额 xff0c 主要是自己忘了 xff0c 别人可以跳过 xff09 修改终端登录密码 xff1a 在实例里面 xff0c 有一个更多 xff0c 来管理密码安装yum xff08 其实也可以用pipe进行安装 xff0c
  • 前端项目部署到阿里云

    由于本人是个前端这里只介绍前端项目的部署 xff08 后台的部署见下一篇 xff09 准备工作 下载两个软件Xshell和Xftp xff08 也可以使用WinSCP 我使用的是windows系统 xff09 购买阿里云 xff0c 看需要
  • 如何重启MySQL,正确启动MySQL

    RedHat Linux Fedora Core Cent OS 1 启动 xff1a etc init d mysqld start 2 停止 xff1a etc init d mysqld stop 3 重启 xff1a etc ini
  • 阿里云Ubuntu16.04 python升级

    Ubuntu16 04 python2 7升级python3 5 正常情况下 xff0c 你安装好ubuntu16 04版本之后 xff0c 系统会自带 python2 7版本 xff0c 如果需要下载新版本的python3 5 xff0c
  • react 脚手架 run eject 之后 打包生成map文件 体积过大

    react 脚手架 打包生成map文件 体积过大 写在前面 xff0c map文件是帮助我们查看报错的位置的 map文件由devtool属性控制 xff0c 然后全文搜索devtool 发现在webpack config js文件 150
  • 机器学习实战分享:用 Python 进行信用卡欺诈检测

    本文旨在使用 XGBoost 随机森林 KNN 逻辑回归 SVM 和决策树解决分类问题 xff0c 内容较长 xff0c 建议收藏 关注 点赞 案例简介 假设你受雇于帮助一家信用卡公司检测潜在的欺诈案件 xff0c 你的工作是确保客户不会因
  • 麻将算法之 ------ 胡牌算法

    麻将数据牌集合 span class hljs keyword private span span class hljs keyword int span cardDataArray 61 span class hljs number 0x
  • ROS+Bebop2无人机+YOLO算法实现无人机视角的实时目标检测

    前言 xff1a 很久之前 xff0c 用TK1玩过一段时间的ROS xff0c 再加上各种硬件 Arduino 激光雷达 编码电机等 xff0c 模仿着做过Turtlebot小车 xff0c 实现了部分Turtlebot部分的功能 xff
  • 享年94岁,图灵奖得主、计算复杂性理论先驱Juris Hartmanis逝世

    7月29日 xff0c 1993年图灵奖得主 计算复杂性理论创始人之一Juris Hartmanis去世 xff0c 享年94岁 从物理学到数学 xff0c 最终深耕计算机科学领域 Hartmanis于1928年7月5日出生于拉脱维亚 xf
  • DSST目标跟踪算法

    DSST算法也是基于KCF算法改的较好的一种 DSST xff08 Accurate Scale Estimation for RobustVisual Tracking xff09 是2015年BMVC xff08 InProceedin
  • 解决workman部署到Linux环境无法启动和连接的问题(结合TP6框架)

    0 检查Linux是否满足workman的环境要求 span class token function curl span span class token parameter variable Ss span www workerman
  • 按照 STAR 法则介绍自己做过的项目

    大家好啊 xff0c 我是大田 介绍项目注意两点 xff1a 1 自己真的做过 2 逻辑表达能力 为什么推荐你用 STAR 法则说呢 xff1f STAR 法则是结构化面试中非常重要的理论 面试官通过这样的描述全面了解你的测试知识 经验 技
  • 汇总最近遇到的 Linux 面试题

    大家好啊 xff0c 我是大田 今天汇总最近小伙伴遇到的 Linux 面试题 1 你之前在公司使 linux 命令做什么 xff1f 搭建测试环境 查看后台 志 2 在之前公司 xff0c 测试环境使 的是哪个 linux 版本 xff1f
  • vio

    VIO概述 0 IMU与视觉进行比较 IMU视觉惯性测量单元利用图像的VIO六自由度IMU xff0c 陀螺仪测量角加速度 加速度计测量加速度利用图像通过特征 像素 xff08 直接法 xff09 进行位姿估计高频 gt 61 100hz
  • ZUPT的相关初步理解

    参考至https zhuanlan zhihu com p 115529319 零速修正 Zero Velocity Update ZUPT 即 xff0c 当载体处于静止状态时 xff0c 载体此时的速度为零 xff0c 利用载体中的惯性
  • 误差状态方程与雅可比矩阵

    误差状态方程 在惯性的优化中包括p v q ba bg 外参等等优化变量 预积分量由陀螺 加计的示数得到 xff0c 以及陀螺 加计的白噪声 偏置 可以先将白噪声从预积分量中剥离出来 xff0c 作为预积分量测的协方差阵 xff1b 计算预
  • java核心内容——int和Integer有什么区别?

    java核心内容 int和Integer有什么区别 xff1f 1 典型回答2 知识扩展1 理解自动装箱 拆箱2 源码分析3 原始类型线程安全4 Java 原始数据类型和引用类型局限性 1 典型回答 int 是我们常说的整形数字 xff0c