一、包装类概述

  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、基本数据类型 -》 包装类

    方式一:调用包装类的对应的构造器

1int 转 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 (12 int a = 1;
 3 int b = 1;
 4 System.out.println(a == b);//true
 5 
 6 (27 int c = 130;
 8 int d = 130;
 9 System.out.println(c == d);//true
10 
11 (312 //自动装箱
13 Integer a = 1;
14 Integer b = 1;
15 System.out.println(a == b);//true    a == b比较的也是地址值     a和b指向的是同一个缓存的常量对象
16 
17 (418 Integer c = 130;
19 Integer d = 130;
20 System.out.println(c == d);//false  c == d比较的也是地址值    c和d都是在堆中新建的Integer对象
21 
22 (523 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 (634 Double d1 = 1.0;
35 Double d2 = 1.0;
36 System.out.println(d1 == d2);//false  Float与 Double没有缓存对象
37 
38 (739 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 类型,然后再进行计算。  

 

posted on 2020-11-09 17:51  格物致知_Tony  阅读(1162)  评论(0编辑  收藏  举报