一、包装类概述
1、为什么需要包装类
Java并不是纯面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。基本数据类型有它的优势:性能(效率高,节省空间)。
在实际使用中经常需要将基本数据类型转成对象,这时就需要将基本数据类型数据转化为对象。
2、包装类
Java 提供了两个类型系统:基本类型与引用类型。
使用基本类型在于效率,然后很多情况,需要创建对象使用,因为对象可以做更多的功能。
基本类型对应的包装类:
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
有了类的特点,就可以调用类中的方法, Java才是真正的面向对象
二、装箱与拆箱
基本类型与对应包装类对象之间,来回转换的过程称为“装箱”与“拆箱”。
装箱:从基本类型转换到对应的包装类对象;
拆箱:从包装类对象转换到对应的基本类型。
1、装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)
构造方法:
Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。
Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。传递的字符串,必须是基本类型的字符串,否则会抛出异常 如:"100" 正确; "a" 抛出NumberFormatException数字格式化异常
静态方法:
static Integer valueof(int i):返回一个表示指定的 int 值的 Integer 实例。
static Integer valueof(String a):返回保存指定的 String 的值的 Integer 对象。
2、拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)
成员方法:
int intValue() 以 int 类型返回该 Integer 的值。
调用包装类的.xxxValue() 方法
Demo:
1 // 装箱
2 //构造方法
3 Integer in1 = new Integer(1);
4 Integer in2 = new Integer("1");
5
6 //静态方法
7 Integer in3 = Integer.valueOf(1);
8 Integer in4 = Integer.valueOf("1");
9
10 // 拆箱
11 int i = in1.intValue();
三、自动装箱(auto_boxing)与自动拆箱(unboxing)
由于经常要做基本类型与包装类之间的转换,从 Java5(JDK1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。
自动装箱:当把基本数据类型的值,赋值给包装类的变量时,就会自动装箱。
自动拆箱:把包装类的对象赋值给对应的基本数据类型的变量时,就会自动拆箱。
注意:自动装箱与拆箱都是只发生在对应类型上。
Demo:
1 Integer i = 3; // 自动装箱。相当于Integer i = Integer.valueOf(3);
2 i = i + 5; // 等号右边:将 i 对象转成基本数值(自动拆箱) i.intValue() + 5;
3 // 加法运算完成后,再次装箱,把基本数值转成对象。
四、基本数据类型、包装类与 String之间的转换
1、基本数据类型 -》 包装类
方式一:调用包装类的对应的构造器
1 以 int 转 Integer 为例:
2 public Integer(int value) {
3 this.value = value;
4 }
5
6 public Integer(String s) throws NumberFormatException { //当参数为 String时,可能会抛异常
7 this.value = parseInt(s, 10);
8 }
方式二:调用对应包装类静态方法 valueOf()
1 public static Integer valueOf(int i) {
2 if (i >= IntegerCache.low && i <= IntegerCache.high)
3 return IntegerCache.cache[i + (-IntegerCache.low)];
4 return new Integer(i);
5 }
6
7 public static Integer valueOf(String s) throws NumberFormatException {
8 return Integer.valueOf(parseInt(s, 10));
9 }
2、包装类 -》 基本数据类型
方式一:调用包装类Xxx的 xxxValue() 方法
1 public int intValue() {
2 return value;
3 }
扩展:可以发展 Integer中不仅仅有 intValue()方法
调用其对应的 xxxValue() 方法,其实内部帮助我们做了强转。
方式二:自动拆箱
3、基本数据类型、包装类 -》String
方式一:使用“+” 进行连接运算
int num1 = 10;
String str1 = num1 + "";
方式二:调用 String的 valueOf(Xxx xxx)
1 public static String valueOf(double d) {
2 return Double.toString(d);
3 }
4 public static String valueOf(float f) {
5 return Float.toString(f);
6 }
7 public static String valueOf(long l) {
8 return Long.toString(l);
9 }
10 public static String valueOf(int i) {
11 return Integer.toString(i);
12 }
13 public static String valueOf(char c) {
14 char data[] = {c};
15 return new String(data, true);
16 }
17 public static String valueOf(boolean b) {
18 return b ? "true" : "false";
19 }
Demo:
1 float f1 = 12.3f;
2 String str2 = String.valueOf(f1);//"12.3"
3
4 Double d1 = new Double(12.4);
5 String str3 = String.valueOf(d1);
6 System.out.println(str2);
7 System.out.println(str3);//"12.4"
4、String -》基本数据类型、包装类
除了 Character 类之外,其他所有包装类都具有 parseXxx 静态方法可以将字符串参数转换为对应的基本类型。
1 public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
2 public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
3 public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
4 public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
5 public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
6 public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
7 public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException
异常。
5、总结
五、包装类的缓存(面试题)
在实际编程时可能需要各种各样的包装类对象,如果只能通过 new 来创建,需要在堆中开辟大量值一样的包装类对象。
这是非常不划算的,所以 Java 对于大部分包装类都设置了缓存。
1、Boolean 类
Boolean 的包装类型,缓存最简单,直接定义为静态常量就可以。
部分源码:
1 public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
2 public static final Boolean TRUE = new Boolean(true);
3 public static final Boolean FALSE = new Boolean(false);
4 public static Boolean valueOf(boolean b) {
5 return (b ? TRUE : FALSE);
6 }
7 }
2、Character 类
ASCII 码范围为 0-127,这里只缓存 ASCII 码范围的 char
Character 静态内部类:CharacterCache
部分源码:
1 public final
2 class Character implements java.io.Serializable, Comparable<Character> {
3 private static class CharacterCache {
4 private CharacterCache(){}
5
6 static final Character cache[] = new Character[127 + 1];
7
8 static {
9 for (int i = 0; i < cache.length; i++)
10 cache[i] = new Character((char)i);
11 }
12 }
13
14 public static Character valueOf(char c) {
15 if (c <= 127) { // must cache
16 return CharacterCache.cache[(int)c];
17 }
18 return new Character(c);
19 }
20
21 }
通过源码可以看出来, Character 的缓存范围是 [0, 127]
3、Byte 类
byte 的范围是:[-128, 127],所以 Byte 一定是从缓存里面获取。
部分源码:
1 public final class Byte extends Number implements Comparable<Byte> {
2 private static class ByteCache {
3 private ByteCache(){}
4
5 static final Byte cache[] = new Byte[-(-128) + 127 + 1];
6
7 static {
8 for(int i = 0; i < cache.length; i++)
9 cache[i] = new Byte((byte)(i - 128));
10 }
11 }
12
13 public static Byte valueOf(byte b) {
14 final int offset = 128;
15 return ByteCache.cache[(int)b + offset];
16 }
17
18 }
可以看出,Byte 一共缓存了 256个数,把整个Byte范围都缓存了。
4、Short 类(缓存范围为 256)
和byte 范围一样, 但是如果超过了 -128<=x<=127 这个范围,就不会从缓存中获取了。
部分源码:
1 public final class Short extends Number implements Comparable<Short> {
2 private static class ShortCache {
3 private ShortCache(){}
4
5 static final Short cache[] = new Short[-(-128) + 127 + 1];
6
7 static {
8 for(int i = 0; i < cache.length; i++)
9 cache[i] = new Short((short)(i - 128));
10 }
11 }
12
13 public static Short valueOf(short s) {
14 final int offset = 128;
15 int sAsInt = s;
16 if (sAsInt >= -128 && sAsInt <= 127) { // must cache
17 return ShortCache.cache[sAsInt + offset];
18 }
19 return new Short(s);
20 }
21 }
可以看出,如果 Short 在范围内,会从缓存中获取,否则就 new 一共新的对象。
5、Integer 类(可以通过参数配置上限)
部分源码:
1 public final class Integer extends Number implements Comparable<Integer> {
2 private static class IntegerCache {
3 static final int low = -128;
4 static final int high;
5 static final Integer cache[];
6
7 static {
8 // high value may be configured by property
9 int h = 127;
10 String integerCacheHighPropValue =
11 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
12 if (integerCacheHighPropValue != null) {
13 try {
14 int i = parseInt(integerCacheHighPropValue);
15 i = Math.max(i, 127);
16 // Maximum array size is Integer.MAX_VALUE
17 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
18 } catch( NumberFormatException nfe) {
19 // If the property cannot be parsed into an int, ignore it.
20 }
21 }
22 high = h;
23
24 cache = new Integer[(high - low) + 1];
25 int j = low;
26 for(int k = 0; k < cache.length; k++)
27 cache[k] = new Integer(j++);
28
29 // range [-128, 127] must be interned (JLS7 5.1.7)
30 assert IntegerCache.high >= 127;
31 }
32
33 private IntegerCache() {}
34 }
35
36 public static Integer valueOf(int i) {
37 if (i >= IntegerCache.low && i <= IntegerCache.high)
38 return IntegerCache.cache[i + (-IntegerCache.low)];
39 return new Integer(i);
40 }
41 }
这里的从缓存中获取的代码是一样的,重点是 IntegerCache 的范围是可以配置的。
从源码中可以看出来 最低值一定是-128 是不可以修改的,但是上限值high 是可以修改的。
通过jvm参数: -Djava.lang.Integer.IntegerCache.high=1024 修改为1024
1. 判断是否有 jvm参数 java.lang.Integer.IntegerCache.high
2. 解析为 int 类型(解析失败就用默认值 127)
3. 修改最大值和最小值
4. 将 low<=x<= high 的值加入到缓存
5. 验证 IntegerCache.high > 127
如果 小于 127,那么 high=127;
6、Long 类(缓存范围 -128~127)
部分源码:
1 public final class Long extends Number implements Comparable<Long> {
2 private static class LongCache {
3 private LongCache(){}
4
5 static final Long cache[] = new Long[-(-128) + 127 + 1];
6
7 static {
8 for(int i = 0; i < cache.length; i++)
9 cache[i] = new Long(i - 128);
10 }
11 }
12
13 public static Long valueOf(long l) {
14 final int offset = 128;
15 if (l >= -128 && l <= 127) { // will cache
16 return LongCache.cache[(int)l + offset];
17 }
18 return new Long(l);
19 }
20 }
7、Float 与 Double 不被缓存
Float 与 Double 调用 valueOf() 时,直接 new 包装类型的对象,然后返回,并没有返回。
部分源码:
1 public final class Float extends Number implements Comparable<Float> {
2 public static Float valueOf(float f) {
3 return new Float(f);
4 }
5 }
6
7 public final class Double extends Number implements Comparable<Double> {
8 public static Double valueOf(double d) {
9 return new Double(d);
10 }
11 }
总结:
Boolean 的缓存对象:True 与 False;
Character 缓存对象范围 0~127;
Byte,Short,Integer,Long:都有缓存对象,范围 -128~127;
Float,Double没有缓存对象。
Demo1:
1 (1)
2 int a = 1;
3 int b = 1;
4 System.out.println(a == b);//true
5
6 (2)
7 int c = 130;
8 int d = 130;
9 System.out.println(c == d);//true
10
11 (3)
12 //自动装箱
13 Integer a = 1;
14 Integer b = 1;
15 System.out.println(a == b);//true a == b比较的也是地址值 a和b指向的是同一个缓存的常量对象
16
17 (4)
18 Integer c = 130;
19 Integer d = 130;
20 System.out.println(c == d);//false c == d比较的也是地址值 c和d都是在堆中新建的Integer对象
21
22 (5)
23 Integer a = 1;
24 Double b = 1.0;
25 Long c = 1L;
26 long d = 1L;
27
28 //System.out.println(a == b);//无法比较,因为对象比较地址,必须是同一种类型或有父子类关系
29 //System.out.println(a == c);//无法比较,因为对象比较地址,必须是同一种类型或有父子类关系
30
31 System.out.println(a == d);// true 因为d是基本数据类型,a才会自动拆箱, int 与 long 比较,可以比较
32
33 (6)
34 Double d1 = 1.0;
35 Double d2 = 1.0;
36 System.out.println(d1 == d2);//false Float与 Double没有缓存对象
37
38 (7)
39 Character c1 = '0';//ASCII码,Unicode码:48
40 Character c2 = '0';
41 System.out.println(c1 == c2);//true
42
43 Character c3 = '中';
44 Character c4 = '中';
45 System.out.println(c3 == c4);//false
Demo2:
1 Integer i = 1;
2 Integer j = 1;
3 System.out.println(i == j);//true
4
5 Integer i = 128;
6 Integer j = 128;
7 System.out.println(i == j);//false
8
9 Integer i = new Integer(1);//新new的在堆中
10 Integer j = 1;//这个用的是缓冲的常量对象,在方法区
11 System.out.println(i == j);//false
12
13 Integer i = new Integer(1);//新new的在堆中
14 Integer j = new Integer(1);//另一个新new的在堆中
15 System.out.println(i == j);//false
16
17 Integer i = new Integer(1);
18 int j = 1;
19 System.out.println(i == j);//true,凡是和基本数据类型比较,都会先拆箱,按照基本数据类型的规则比较
Demo3:
1 public void test1() {
2 Object o1 = true ? new Integer(1) : new Double(2.0);
3 System.out.println(o1);// 1.0
4
5 }
这里由于应用了三元运算符,所以在编译时就会统一两边的类型。
这里会先进行拆箱,然后把 int 类型提升为 double 类型,然后再进行计算。