概述
主要内容:分析数值型自动拆装箱过程中的常量池问题。
1. 基本数据类型及包装类型
基本数据类型 | 长度(单位:字节) | 取值范围 | 包装类型 |
---|---|---|---|
byte | 1 | -2^7 – 2^7-1 | Byte |
short | 2 | -2^15 – 2^15-1 | Short |
int | 4 | -2^31 – 2^31-1 | Integer |
long | 8 | -2^63 – 2^63-1 | Long |
float | 4 | 3.402823e+38 ~ 1.401298e-45 | Float |
double | 8 | 1.797693e+308~ 4.9000000e-324 | Double |
boolean | 1 | true/false | Boolean |
char | 2 | 0-2^16-1 | Character |
2.自动拆装箱
拆箱(unboxing):将包装类转换成基本数据类型的过程。
装箱(boxing):将基本数据类型装换从包装类型的过程。
//自动装箱
Integer a = 100;
//自动拆箱
int b = a;
2.1 自动拆装箱中常量池的问题
eg1(面试题):
public static void main(String[] args) {
Integer a = 30;
int b = a;
Integer c = Integer.valueOf(30);
Integer d = new Integer(30);
System.out.println(a == b);//true
System.out.println(a == c);//true
System.out.println(a == d);//false
System.out.println(b == c);//true
System.out.println(b == d);//true
}
对代码进行反编译(取赋值部分):
public static void main(java.lang.String[]);
Code:
0: bipush 30
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: istore_2
11: bipush 30
13: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
16: astore_3
17: new #4 // class java/lang/Integer
20: dup
21: bipush 30
23: invokespecial #5 // Method java/lang/Integer."<init>":(I)V
小结:
装箱的过程:使用Integer.valueOf(int i)方法。
拆箱的过程:使用Integer.intValue()方法。
eg2:
Integer a = 128;
Integer b = 128;
Integer c = 127;
Integer d = 127;
System.out.println(a == b);//false
System.out.println(c == d);//true
现象:范围在[-128,127]之间的Integer对象,只要值相等,那么指向的就是同一个对象。
原因:当int类型在某一个范围([-128,127])内,就直接从常量池中取对象。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
那么在ByteShortLong类型中是否同样的有常量池存在?
答案是肯定的,并且常量池的范围都一样[-128,127],详情看源码。
为什么Float和Double没有常量池?
因为即便在[-128,127]之间,取值的个数也是无限个。
3.基本数据类型和包装类之间的比较
原则1:基本数据类型和包装类型使用==比较,先对包装类型进行拆箱,再比较值。所以只要值相同,一定是true。
原则2:包装类使用equals和基本数据类型进行比较,先将基本类型,转换成包装类型,再比较。
-------------------------------------2021-12-29更新----------------------------------------
1. 为什么需要包装类?
因为基本数据类型只能用来存储值,它没有方法。比如String类型和基本数据类型之间的转换,没有包装类型就无法完成。
2. java编译器的常量优化机制
byte b1 = 3;
byte b2 = 4;
byte b3 = 3+4;//编译通过
byte b4 = 120+8;//编译失败
byte b5 = b1+2;//编译失败
byte b6 =(byte) (b1+2);//编译通过
为什么第三行通过?第四行失败?
编译器的常量优化机制:当赋值表达式的等号右边全部都是常量时,编译器会先计算右边的内容,并判断结果是否在左侧数据的取值范围内,如果在,就赋值成功,否则赋值失败,编译报错。
为什么第五行不通过?
当等号右侧包含变量时,常量优化机制不起作用。这个时候,等号右侧的结果是int类型,不能直接赋值给byte类型。加个强转就可以。
3. byte、short、char类型在运算时自动转换为int
byte b1 = 3;
byte b2 = 4;
byte b7 = b1+b2;//编译失败
为什么编译失败?
有一种说法是:因为两个byte类型相加,结果可能超出byte类型的取值范围,所以编译器就报错了。这种说法是不对的,按照这种理论,两个int类型相加就必须要Long类型接收。两个Long类型相加要用什么数据类型接收呢?
按理说,等号右边的数据类型都是byte,应该不需要转成int计算,但结果却是int类型。原因是:JVM不支持直接对byte、short、char类型的算术运算,但是支持对int类型的计算,所以这三种数据在运算之前都会先转换成int类型,再进行运算。具体内容,可以查询JVM指令集,附上JVM中实际类型与运算类型的对应关系如下表:
4. 包装类型和基本数据类型的比较问题
记住下面这些规则就行:
4.1 使用==比较:
左右两边表达式只要有一边是基本数据类型,全部转换成基本数据类型比较值。
如果左右两边都是包装类,比较地址。如果是new出来的,肯定保存在堆中,如果是通过Integer.valueOf()方法赋值的(注意:通过字面量赋值实际上也是使用Integer.valueOf方法,比如:Integer a = 10),就考虑常量池。
4.2 使用equals方法,肯定是比较值了,因为包装类都重写了equals方法。
5. 包装类常量池和String类型常量池的区别
包装类:常量池是固定范围的,使用之前就有内容的。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() {}
}
String类型:常连池一开始是空的,在赋值的时候才会添加到常量池中,或者使用intern方法。
最后
以上就是勤恳麦片为你收集整理的基本数据类型及其包装类的全部内容,希望文章能够帮你解决基本数据类型及其包装类所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复