一文干翻Integer、int等基础数据类型和包装类型相关问题
int和Integer有什么区别?
https://www.nowcoder.com/questionTerminal/17f69e2a0f4e44728c66e304f3d431e7
💨 作者:
laker
,因为喜欢LOL滴神faker
,又是NBA湖人队🏀(laker
)粉丝儿(主要是老詹的粉丝儿),本人又姓李,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案,以及一些读书笔记和心得分享。
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道
。
面试题
定义Integer x=20 Integer y=200 在内存里是个什么过程?
首先这题,会牵扯出来好几个知识点,我自己能力范围内看到的如下:
- 自动拆装箱
- 静态工厂使用的缓存机制
下面我们来带着问题分析解读。
开始分析
1、什么是自动拆装箱?
其实就是基本数据类型和其包装类由 javac
相互转换。
Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
举例 Integer:
装箱:调用 Integer.valueOf()
,int转Integer
拆箱:调用 Integer.intValue()
,Integer转int
自动装箱拆箱是在编译期实现的,可以通过反编译字节码文件求证。
源码:
Integer a = 3; // java/lang/Integer.valueOf (I) int b = new Integer(100); // java/lang/Integer.intValue ()I
字节码:
L0 LINENUMBER 5 L0 ICONST_3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; ASTORE 1 L1 LINENUMBER 6 L1 NEW java/lang/Integer DUP BIPUSH 100 INVOKESPECIAL java/lang/Integer.<init> (I)V INVOKEVIRTUAL java/lang/Integer.intValue ()I ISTORE 2 L2
2、为什么会存在原始类型、包装类型以及自动拆装箱机制呢?
原始数据类型和 Java 泛型并不能配合使用,之前没有自动拆装箱机制的时候,开发者就必须每次手动显示转换,才有了Java5中引入自动拆装箱机制。
List<int> // 这个是编译不过的 List<Integer>
原始类型在内存中存的是值,所以找到内存位置,就可以获得值;
包装类型存的是引用地址,找到对象的引用内存位置,还要根据引用内存位置找下一个内存空间,要产生更多的IO,所以计算性能比原始类型差,但是包装类型具备泛化的能力,更抽象,解决业务问题编程效率高。
如果开发者要做计算,就应该使用原始类型,如果开发者要处理业务问题,就应该使用object,采用Generic机制;反正JAVA有auto-boxing/unboxing机制,对开发者来讲也不需要注意什么。然后为了弥补object性能的不足,还设计了static valueOf()
方法提供缓存机制,算是一个弥补。
3、静态工厂使用的缓存机制到底是个啥?
在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。
这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。
在自动装箱(autoboxing)的时候,调用
valueOf()
方法。这里是使用了享元模式实现的范围缓存支持
public static Integer valueOf(int i) { // low -128 , high 127 if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
- 这个类是用来实现缓存支持,并支持
-128
到127
之间的自动装箱过程。最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size
修改.
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; }
相关题目
基础知识
对于对象引用类型:== 比较的是对象的内存地址。
对于基本数据类型:== 比较的是值。
== 比较题目
如果整型字面量的值在 -128 到 127 之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 return new Integer(i);
Integer a = new Integer(3); Integer b = 3; // 将3自动装箱成Integer类型 int c = 3; System.out.println(a == b); // false 两个引用没有引用同一对象 System.out.println(a == c); // true a自动拆箱成int类型再和c比较 System.out.println(b == c); // true Integer a1 = 128; Integer b1 = 128; System.out.println(a1 == b1); // false Integer a2 = 127; Integer b2 = 127; System.out.println(a2 == b2); // true
int和Integer有什么区别?
Integer相关设计模式?
总结
其他整型类型的缓存机制
- 这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。
Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。
- 特别注意 Double Float 没有使用到缓存机制
Double i = 100.0; Double j = 100.0; System.out.print(i == j);// false
装箱和拆箱在编程实际中注意点
建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。
参考:
- 杨晓峰的极客时间专栏
- https://www.jianshu.com/p/734ee940f431
这是面试题解读的第一篇,后面还会继续,希望大家批评指正。
QQ群【837324215】
关注我的公众号【Java大厂面试官】,回复:架构、资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。
公众号也会持续输出高质量文章,和大家共同进步。