《Java 核心技术面试》课程笔记(七)

int 和 Integer 有什么区别?

典型回答

  • Java 虽然号称是面向对象的语言,但是原始数据类型仍然是重要的组成元素,所以在面试中,经常考察原始数据类型和包装类等 Java 语言特性。
  • 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,在调用它的时候会利用一个缓存机制,带来了明显的性能改进这个值默认缓存是 -128 到 127 之间。

考点分析

  • 这个问题涵盖了 Java 里的两个基础要素:原始数据类型、包装类。
  • 谈到这里,就可以非常自然地扩展到自动装箱、自动拆箱机制,进而考察封装类的一些设计和实践。
  • 面试官可以结合其他方面,来考察面试者的掌握程度和思考逻辑,比如:
    • Java 使用的不同阶段:编译阶段、运行时,自动装箱 / 自动拆箱是发生在什么阶段?
    • 使用静态工厂方法 valueOf 会使用到缓存机制,那么自动装箱的时候,缓存机制起作用吗?
    • 为什么我们需要原始数据类型,Java 的对象似乎也很高效,应用中具体会产生哪些差异?
    • 阅读过 Integer 源码吗?分析下类或某些方法的设计要点。

知识扩展

  • 理解自动装箱、拆箱
    • 自动装箱实际上算是一种语法糖。
    • 可以简单理解为 Java 平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的
    • 比如整数,javac 自动把装箱转换为 Integer.valueOf(),把拆箱替换为 Integer.intValue(),既然调用的是 Integer.valueOf,自然能够得到缓存的好处。
    • 这种缓存机制并不是只有 Integer 才有,同样存在于其他的一些包装类,比如:
      • Boolean,缓存了 true/false 对应实例,确切说,只会返回两个常量实例 Boolean.TRUE/FALSE。
      • Short,同样是缓存了 -128 到 127 之间的数值。
      • Byte,数值有限,所以全部都被缓存。
      • Character,缓存范围’\u0000’ 到 ‘\u007F’。
    • 原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合。
      • 创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。
      • 使用原始数据类型、数组甚至本地代码实现等,在性能极度敏感的场景往往具有比较大的优势,用其替换掉包装类、动态数组(如 ArrayList)等可以作为性能优化的备选项。
      • 一些追求极致性能的产品或者类库,会极力避免创建过多对象。
      • 当然,在大多数产品代码里,并没有必要这么做,还是以开发效率优先。
  • 源码分析
    • 整体看一下 Integer 的职责,它主要包括各种基础的常量,比如最大值、最小值、位数等;
    • 各种静态工厂方法 valueOf();
    • 获取环境变量数值的方法;
    • 各种转换方法,比如转换为不同进制的字符串,如 8 进制,或者反过来的解析方法等。
    • Integer 的缓存范围虽然默认是 -128 到 127,但是在特别的应用场景,比如我们明确知道应用会频繁使用更大的数值,这时候应该怎么办呢?缓存上限值实际是可以根据需要调整的,JVM 提供了参数设置-XX:AutoBoxCacheMax=N
    • 包装类里存储数值的成员变量“value”,不管是 Integer 还 Boolean 等,都被声明为“private final”,所以,它们同样是不可变类型。
  • 原始类型线程安全
    • 原始数据类型的变量,显然要使用并发相关手段,才能保证线程安全。
    • 如果有线程安全的计算需要,建议考虑使用类似 AtomicInteger、AtomicLong 这样的线程安全类。
    • 特别的是,部分比较宽的数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值。
  • Java 原始数据类型和引用类型局限性
    • 原始数据类型和 Java 泛型并不能配合使用。
      • 因为 Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧。
      • Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为 Object。
    • 无法高效地表达数据,也不便于表达复杂的数据结构,比如 vector 和 tuple。
      • Java 的对象都是引用类型,如果是一个原始数据类型数组,它在内存里是一段连续的内存。
      • 而对象数组则不然,数据存储的是引用,对象往往是分散地存储在堆的不同位置。
      • 这种设计虽然带来了极大灵活性,但是也导致了数据操作的低效,尤其是无法充分利用现代 CPU 缓存机制。
    • Java 为对象内建了各种多态、线程安全等方面的支持,但这不是所有场合的需求,尤其是数据处理重要性日益提高,更加高密度的值类型是非常现实的需求。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值