Hey, Nice to meet You. 

必有过人之节.人情有所不能忍者,匹夫见辱,拔剑而起,挺身而斗,此不足为勇也,天下有大勇者,猝然临之而不惊,无故加之而不怒.此其所挟持者甚大,而其志甚远也.          ☆☆☆所谓豪杰之士,

夯实Java基础(六)----包装类

1、包装类简介

我们都知道Java是面向对象编程语言,包含了8种基本数据类型,但是这8种基本数据类型并不支持面向对象的特征,它们既不是类,也不能调用方法,它们仅是关键字。这在实际使用时存在很多的不便,比如int类型需要转换成字符串,是非常麻烦的,为了解决这个不足,然后就出现了包装类。包装类顾名思义,就是将这些基本数据类型封装在类中,并且提供丰富的方法来对数据进行操作。这样八个类和基本数据类型对应的类统称为包装类(Wrapper Class)。

Java中的包装类提供了将基本数据类型转换为对象,以及将对象转换为基本数据类型的机制。

img

其中Byte,Short,Integer,Float,Double,Long都属于Number子类。Character ,Boolean属于Object子类。它们的默认值都是NULL。

包装类的特点:

  • 所有包装类都是类,是可以实例化的
  • 所有包装类都是final类型,因此不能创建他们的子类。
  • 包装类是不可变类,一个包装类的对象自创建后,他所包含的基本类型数据就不能被改变。

包装类有以下用途:

  • 集合不允许存放基本数据类型,所以可以用包装类来代替
  • 包含了每种基本类型的相关属性,如最大值,最小值,所占位数等
  • 作为基本数据类型对应的类类型,提供了一系列实用的对象操作,如类型转换,进制转换等

2、装箱和拆箱

什么是装箱和拆箱?

  • 装箱:将基本数据类型变为包装类对象。
  • 拆箱:将包装类中包装的基本数据类型取出转为基本数据类型。

在 Java 1.5 之前,开发人员如果想要将基本数据类型和包装类进行转换,则需要手动进行装拆箱,比如说:

Integer a = new Integer(10); // 手动装箱
int b = a.intValue();  // 手动拆箱

[1]、装箱可以通过调用包装类的构造器来实现(以Integer为例,其它同理):

构造方法 描述
Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值
Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值

注:如果传递的字符串,必须是基本类型的字符串,否则会抛出异常,例如:"100" 正确、"a" 抛异常。

[2]、装箱也可通过包装类的静态方法来实现(其它同理):

静态方法 描述
static Integer valueOf(int i) 返回一个表示指定的 int 值的 Integer 实例
static Integer valueOf(String s) 返回保存指定的 String 的值的 Integer 对象

[3]、拆箱(包装类>>>基本类型的数据)通过调用包装类的xxxValue()方法实现:

成员方法 描述
int intValue() 以 int 类型返回该 Integer 的值

3、自动装箱和自动拆箱

理解了上面的装箱和拆箱后,那么自动装箱和拆箱也就好理解了,就是全自动完成咯,不需要开发人员手动来完成!!!

在JDK1.5之后,为了减少开发人员的工作,Java提供了自动装箱和自动拆箱功能。

Integer a = 21; // 自动装箱
int b = a;     // 自动拆箱

/*
  ArrayList集合无法直接存储整数,可以存储Integer包装类
*/
ArrayList<Integer> list = new ArrayList<>();
list.add(1);  //-->自动装箱 list.add(new Integer(1));
int a = list.get(0);  //-->自动拆箱 list.get(0).intValue();

由于出现了自动装箱和自动拆箱功能,所以基本数据类型和包装类之间的相互转换变得非常简单,再也不用去调用繁琐的方法了。


自动装箱和自动拆箱的原理:

Integer a = 21; // 自动装箱
int b = a;     // 自动拆箱

将上面自动装拆箱的代码反编译class文件之后得到如下内容:

image

将上面反编译后的代码转为我们可以看得懂的代码就是:

当执行到Integer a = 21;这句代码时,系统为我们执行了:Integer a = Integer.valueOf(21); 自动进行装箱

当执行到int b = a;这句代码时,系统为我们执行了:int b = a.intValue(); 自动进行拆箱

通过上面可以发现:自动装箱和自动拆箱的原理也需要同调用指定的方法来实现,只不过这个步骤不再用开发人员手动完成,而是由编译器自动帮我们完成,这就是所谓的自动装箱和拆箱。

4、基本数据类型、包装类、字符串之间的转换

虽然上面已经介绍了自动装箱和自动拆箱,但是下面还是简单举例:

[1]、基本数据类型>>>>>包装类(装箱):调用包装类的构造器

@Test
public void test1(){
    Integer i1=new Integer(10);
    System.out.println("i1结果:"+i1);
    Integer i2=new Integer("10");
    System.out.println("i2结果:"+i2);
    //Integer i3=new Integer("10abc"); 必须是纯数字,java.lang.NumberFormatException: For input string: "10abc"
    //System.out.println(i3);
    
    Double d1=new Double(12.3);
    Double d2=new Double("12.3");
    System.out.println("d1结果:"+d1);
    System.out.println("d1结果:"+d2);
    
    Boolean b1=new Boolean(true);
    Boolean b2=new Boolean("TrUe");
    Boolean b3=new Boolean("TrUe123");
    System.out.println("b1结果:"+b1);
    System.out.println("b2结果:"+b2);
    System.out.println("b3结果:"+b3);
}

运行结果:

img

从运行的结果来看,通过调用包装类构造器可以将基本数据类型转为包装类,但是传入的参数必须合法,否则就像Integer那样会报错。可以看见Boolean包装类传入的字符串是忽略大写的,这是因为构造器中调用了parseBoolean(String s)方法,然后这个方法内部又调用了String方法的equalsIgnoreCase(String anotherString)方法,所以传入字符串会忽略大小写。而且你传入的是非法字符串它并不会报错,只是返回了false,这里需要注意一下。


[2]、包装类>>>>>基本数据类型(拆箱):调用包装类的xxxValue()方法

@Test
public void test2(){
    Integer integer = new Integer(10);
    int i=integer.intValue();
    System.out.println(i+1);
    
    Double aDouble = new Double(10.11);
    double d=aDouble.doubleValue();
    System.out.println(d+1);
    
    Boolean aBoolean = new Boolean(false);
    boolean b=aBoolean.booleanValue();
    System.out.println(b);
}

运行结果:

img

包装类转换为基本数据类型是非常简单的,只需要调用正确的方法即可,其他的包装类也是同样的道理。


[3]、由于出现了自动拆箱和自动装箱,基本数据类型和包装类就可以看成一个整体。

基本数据类型、包装类>>>>>字符串:调用String重载的valueOf(Xxx xxx)

@Test
public void test3(){
    int num=10;
    //方式1:连接运算
    String str1=num+"";
    System.out.println("str1结果"+str1);
    
    //方式2:调用String的valueOf(Xxx xx)方法
    String str2=String.valueOf(15);
    System.out.println("str2结果"+str2);
    
    Integer integer=new Integer(21);
    String str3=String.valueOf(integer);
    System.out.println("str3结果"+str3);
}

运行结果:

img


[4]、字符串>>>>>基本数据类型、包装类:调用包装类中的parseXxx()方法

@Test
public void test5(){
    String str1="211";
    int num=Integer.parseInt(str1);
    System.out.println(num);
    //运行报错,ava.lang.NumberFormatException: For input string: "211abc"
    //String str2="211abc";
    //int num1=Integer.parseInt(str2);
    
    String str3="truetrue";
    boolean b=Boolean.parseBoolean(str3);
    System.out.println(b);
}

运行结果:211、false。

无论怎样转换,数据的格式必须是正确的,否则就会报错,boolean特殊点,返回的是false。


最后它们三者的转换可以详细归结为:

1、基本数据类型与包装类之间转换:直接使用自动装箱和自动拆箱。

2、基本数据类型、包装类转换为字符串:调用String的valueOf(Xxx xxx)方法。

3、字符串转换为基本数据类型、包装类:调用包装类的parseXxx()方法。

另外包装类中还有丰富的方法可供大家调用,如需学习可以自行查看包装类的API。

5、Integer经典面试题

以下是一道关于Integer包装类非常经典的面试题,如下:

@Test
public void test6(){
    Integer a=127;
    Integer b=127;
    System.out.println(a == b);

    Integer c=128;
    Integer d=128;
    System.out.println(c == d);
}

运行结果:

img

运行的结果肯定会有很大的疑问?同样是两个int型自动装箱为包装类进行比较,为什么127比较返回是true,而128比较则返回了false。这是为什么呢?答:这与Java的Integer类的设计有关,查看Integer类的源代码,如下所示。

上面已经知道了在自动装箱的过程中,编译器自动调用了valueOf()方法,所以先进入这个方法一探究竟:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

通过上面的字段代码可知,进来先先判断 i 的范围,如满足条件,则直接从IntegerCache缓存中返回值,否则通过调用包装类的构造方法创建一个对象然后返回值。所以下面重点进入IntegerCache这个类。

    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变量
            high = h;
            //定义长度为256的Integer数组
            cache = new Integer[(high - low) + 1];
            int j = low;
            //执行初始化,创建-128~127的Integer实例,并放入cache[]数组中
            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() {}
    }

从上面代码可以看出,Integer类中定义了一个IntegerCache内部类,内部类中定义了一个静态常量的Integer cache [ ]数组,而这个数组存储了从-128--127之间的整数自动装箱成Integer实例,如果我们使用自动装箱的方式,给Integer包装类赋值时,范围在-128~127之间,就可以直接使用数组中的元素,因为-128--127之间的同一个整数自动装箱成Integer实例时,永远是引用cache数组的同一个数组元素。而如果超出了这个范围,自动装箱需要再重新new对象,所以就出现了程序中的运行结果。

6、包装类小结

1、包装类的默认值都是NULL。

2、基本数据类型、包装类、字符串之间的转换可以归结为:

  • 基本数据类型与包装类之间转换:直接使用自动装箱和自动拆箱。
  • 基本数据类型、包装类转换为字符串:调用String的valueOf(Xxx xxx)方法。
  • 字符串转换为基本数据类型、包装类:调用包装类的parseXxx()方法。

3、Integer类中默认存放值的范围是-128~127之间。

posted @ 2019-07-20 21:47  唐浩荣  阅读(469)  评论(0编辑  收藏  举报