文章目录
本文编写灵感来源我之前整理的面经的题目之一:
2020年11月最新互联网大厂面试经验分享【网易、阿里、腾讯、京东、百度、爱奇艺、字节、小米、美团、搜狐、58】
定义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大厂面试官】,回复:架构、资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。
公众号也会持续输出高质量文章,和大家共同进步。