包装类
Java中有八种基本数据类型,它们只能表示一些最简单的数字,这些数字最小的在内存中占1个字节8位,最大占8个字节64位。这些都是简单的数字,不是对象,所以不能用来调用方法或者属性,功能不够强大。
1 概述
针对这八种基本类型,JavaAPI又专门提供了对应的类类型,目的就是为了分别把这八种基本类型的数据,包装成对应的类类型,这时候就变成对象了,就可以调用方法了或者访问属性了。
基本类型 | 包装类型 |
---|---|
boolean | java.lang.Boolean |
byte | java.lang.Byte |
short | java.lang.Short |
int | java.lang.Integer |
long | java.lang.Long |
float | java.lang.Float |
double | java.lang.Double |
char | java.lang.Character |
2 基础案例
在这些包装类型中,都定义相关的属性和方法,例如Integer中:
package com.briup.chap07.test;
public class Test042_Integer {
public static void main(String[] args) {
int i = 1;
//编译报错,因为i不是对象
//i.toString();
//1.int --> Integer
//Integer o = new Integer(i);
Integer o = Integer.valueOf(1);
//2.Integer --> int
int j = o.intValue();
System.out.println(o.toString());
//3.包装类静态成员访问
System.out.println(Integer.MIN_VALUE);
System.out.println(Integer.MAX_VALUE);
//4.包装类静态方法调用
//把100分别转为十进制、二进制、八进制、十六进制的字符串形式
System.out.println(Integer.toString(100));
System.out.println(Integer.toString(100,2));
System.out.println(Integer.toString(100,8));
System.out.println(Integer.toString(100,16));
//把100转为二进制形式
System.out.println(Integer.toBinaryString(100));
//把字符串"100"转为int类型的100
int num = Integer.parseInt("100");
System.out.println(num);
}
}
注意:一定要掌握 public static int parseInt(String s)
方法。
3 装箱拆箱
JDK1.5或以上,可以支持基本类型和包装类型之间的自动装箱、自动拆箱,这简化了基本类型和包装类型之间的转换。
-
**自动装箱:**基本数据类型值 自动转化为 包装类对象
-
**自动拆箱:**包装类对象 自动转化为 基本数据类型值
案例展示:
int、Integer自动装箱拆箱展示。
package com.briup.chap07.test;
public class Test043_Integer {
public static void main(String[] args) {
//JKD1.5 之前
//Integer o = new Integer(1);
//Integer o = Integer.valueOf(1);
//JDK1.5 之后
//自动装箱,这里会自动把数字1包装成Integer类型的对象
Integer o = 1;
//JKD1.5 之前
//Integer o = new Integer(1);
//int i = o.intValue();
//JDK1.5 之后
//Integer o = new Integer(1);
//自动拆箱,这里会自动把对象o拆箱为一个int类型的数字,并把数字赋值给int类型的变量i
int i = o;
}
}
其他的基本类型和包装类型之间的转换,与此类似。
注意事项:
不同类型的基本数据类型和包装类,是不可以自动装箱拆箱的,例如int和Long;
给Byte Short Character赋值时,需要考虑 常量优化机制。
具体案例如下:
package com.briup.chap07.test;
public class Test043_Test {
public void test1(int i) {
}
public void test2(Integer i) {
}
public void test3(long i) {
}
public void test4(Long i) {
}
//不同类型的基本数据类型和包装类,不可以自动装箱拆箱
public static void main01(String[] args) {
Test043_Test t=new Test043_Test();
t.test1(1);// 编译通过 int i = 1; 正常赋值
t.test2(1);// 编译通过 Integer i = 1; 自动装箱
t.test3(1);// 编译通过 long i = 1; 隐式类型转换
// 编译报错
// 错误的代码:Long i = 1;
// int和Long 之间没有任何关系
//t.test4(1);
t.test4(1L);// 编译通过 Long i = 1L; 自动装箱
}
public static void main(String[] args) {
//注意: 自动装箱时候 要保证类型一致
//Long l = 2; ERROR
Long l2 = 23L;
//int t = l2; ERROR
long t = l2;
//给Byte Short Character赋值时,需要考虑 常量优化机制
Byte b1 = 2;
Byte b2 = 127;
//Byte b3 = 128; ERROR
Character c1 = 3;
Character c2 = '\u0989';
Character c3 = 65535;
//Character c4 = 65536; ERROR
}
}
4 Integer缓冲区
常见面试题:分析下面程序的输出结果。
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println("i1 == i2: "+ (i1 == i2)); //?
Integer i3 = 127;
Integer i4 = 127;
System.out.println("i3 == i4: "+ (i3 == i4)); //?
Integer i5 = 128;
Integer i6 = 128;
System.out.println("i5 == i6: "+ (i5 == i6)); //?
}
//程序的输出结果为:
i1 == i2: false
i3 == i4: true
i5 == i6: false
在Java中方法区有一个叫做**运行时常量池(Runtime Constant Pool)**的区域,用于存储编译期间生成的各种字面量和符号引用,Integer常量池就存储在该区域。
Integer常量池是一个特殊的缓存机制,用于存储在范围[-128, 127]
之间的Integer常量对象。这个范围是Java中的一个固定范围,超出这个范围的整型常量不会被缓存。
当使用**自动装箱(Autoboxing)**将一个整数赋值给一个Integer对象时,如果该整数在[-128, 127]
范围内,那么会从Integer常量池中获取一个已经存在的对象,而不是创建一个新的对象。这是因为在这个范围内的整数常见且频繁使用,通过复用对象可以节省内存空间和提高性能。
Integer对象内存构成:
结果分析:
i1 == i2
结果为false,图上很清楚,无需多解释。
i3 == i4
结果为true,因为127在[-128,127]这个范围中,i3和i4直接复用了常量池中Integer(127)对象,故而两者哈希码值相同,== 比较也相同。
i5 == i6
结果为false,因为128不在[-128,127]这个范围中,i5和i6自动装箱过程中,系统底层执行了Integer i5 = new Integer(128); Integer i6 = new Integer(128);
,故而i5和i6指向2个不同的对象。
Integer源码分析:
package java.lang;
public final class Integer extends Number implements Comparable<Integer> {
//省略...
/**
* Cache to support the object identity semantics of autoboxing for values between 128 and 127 (inclusive) as required by JLS.
*/
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;
// 省略...
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() {}
}
//省略...
}
补充内容:
System
类方法:
public static native int identityHashCode(Object x);
该方法会返回对象的哈希码,即Java根据对象在内存中的地址计算出来一个整数值,结论:相同地址的对象调用该方法结果相同,不同地址对象结果不同!
注意,哈希码并不是对象的内存地址。
验证案例:
package com.briup.chap07.test;
public class Test044_Cache {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println("i1: " + System.identityHashCode(i1));
System.out.println("i2: " + System.identityHashCode(i2));
System.out.println("i1 == i2: "+ (i1 == i2)); //false
System.out.println("--------------");
Integer i3 = 127;
Integer i4 = 127;
System.out.println("i3: " + System.identityHashCode(i3));
System.out.println("i4: " + System.identityHashCode(i4));
System.out.println("i1 == i3: "+ (i1 == i3)); //false
System.out.println("i3 == i4: "+ (i3 == i4)); //true
System.out.println("--------------");
Integer i5 = 128;
Integer i6 = 128;
System.out.println("i5: " + System.identityHashCode(i5));
System.out.println("i6: " + System.identityHashCode(i6));
System.out.println("i5 == i6: "+ (i5 == i6)); //false
}
}
运行效果: