java包装类

转自(侵删):https://blog.csdn.net/sinat_30973431/article/details/89332443

目录

一、包装类概述

二、包装类的自动装箱、自动拆箱机制

三、包装类中的缓存机制

四、包装类的四则运算、位运算、比较运算、逻辑运算

五、包装类作为方法的形参、返回值

六、包装类作为集合的元素

七、包装类使用过程中有可能引起的空指针异常

八、为什么需要包装类?有了包装类又为什么要保留基本数据类型?(包装类的优缺点)


一、包装类概述

Java有8种基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、布尔型boolean、字符型char,相对应地,Java提供了8种包装类Byte、Short、Integer、Long、Float、Double、Boolean、Character。包装类创建对象的方式就跟其他类一样。

Integer num = new Integer(0);    //创建一个数值为0的Integer对象

二、包装类的自动装箱、自动拆箱机制

上面的构造对象语句实际上是基本数据类型向包装类的转换。在应用中我们经常需要进行基本类型数据和包装类对象之间的互转。

Integer num1 = new Integer(1);  //基本数据类型转为包装类int num2 = num1.intValue();        //包装类型转为基本数据类型System.out.println(num1 +"    "+ num2);

而Java为了方便我们使用,以及出于其他目的如性能调优,给我们提供了自动装箱、拆箱机制。这种机制简化了基本类型和包装类型的转换。

//1、包装类中的自动装箱拆箱机制Integer  num1 = 1;     //自动装箱int num2 = num1;      //自动拆箱System.out.println(num1 +"    "+ num2);

当使用jad工具对上面的代码进行反编译时,结果如下。

Integer integer = Integer.valueOf(1);int i = integer.intValue();System.out.println((new StringBuilder()).append(integer).append("\t").append(i).toString());

可见,Java编译器帮我们完成了转换操作。另外,我们可以看到,除了使用new关键字,还可以使用Integer类的valueOf()方法创建一个Integer对象。这两个方式是有所区别的,我们下面会说到。

三、包装类中的缓存机制

前面说到创建包装类对象有两种方式:new关键字、valueOf()方法。我们来看一段代码感受一下它们的区别。

//2、包装类中的缓存机制Integer num3 = 10;Integer num4 = 10;Integer num5 = new Integer(20);Integer num6 = new Integer(20);Integer num7 = 128;Integer num8 = 128;System.out.println((num3==num4) +" "+ num3.equals(num4));System.out.println((num5==num6) +"    "+ num5.equals(num6));System.out.println((num7==num8) +"    "+ num7.equals(num8));

运行结果为

image

我们看下它的反编译代码

Integer integer = Integer.valueOf(10);Integer integer1 = Integer.valueOf(10);Integer integer2 = new Integer(20);Integer integer3 = new Integer(20);Integer integer4 = Integer.valueOf(128);Integer integer5 = Integer.valueOf(128);System.out.println((new StringBuilder()).append(integer == integer1).append("\t").append(integer.equals(integer1)).toString());System.out.println((new StringBuilder()).append(integer2 == integer3).append("\t").append(integer2.equals(integer3)).toString());System.out.println((new StringBuilder()).append(integer4 == integer5).append("\t").append(integer4.equals(integer5)).toString());

首先,我们查看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);    }

再查看下Integer的内部类IntegerCache的cache数组成员、low、high成员

        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;        }

可以发现,只要Integer类第一次被使用到,Integer的静态内部类就被加载,加载的时候会创建-128到127的Integer对象,同时创建一个数组cache来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已经缓存的对象,也就是说不会再新建对象;当使用new关键字or使用valueOf()方法创建小于-128大于127的值对象时,就会创建新对象。

//2、包装类中的缓存机制Integer num3 = 10;Integer num4 = 10;Integer num5 = new Integer(20);Integer num6 = new Integer(20);Integer num7 = 128;Integer num8 = 128;

由于num3、num4都小于等于127,它们指向的是同一个缓存的Integer对象,所以用==进行比较的结果是true;num5、num6由于使用new关键字指向的是两个不同的新对象,结果为false;num7、num8虽然是采用自动装箱的方式,但执行valueOf()方法的时候,由于不满足条件i >= IntegerCache.low && i <= IntegerCache.high,而同样新建了两个不同的新对象,结果同样是false。

接着,我们再来看看源码中Integer的equals()方法的实现

    public boolean equals(Object obj) {        if (obj instanceof Integer) {            return value == ((Integer)obj).intValue();        }        return false;    }

可见equals()方法比较的是Integer对象的值,而不是像==一样比较的是对象是否是同一个对象。所以,当需要比较两个Integer对象的值是否相等时,记住要用equals()方法。用==比较的话由于缓存机制的存在,可能产生一些让人困扰的结果。

此外,在8种包装类型中,有缓存区的有Character、Byte、Short、Integer、Long,而且它们的实现方式基本一样,都是-128到127的缓存范围。Boolean虽然没有缓存区,但是因为只有两个值true、false,所以Boolean在成员变量中就创建了两个相应的对象。没有缓存区的只有Float、Double,之所以没有原因很简单,即便是0到1这么小的范围,浮点数也有无数个,使用缓存区缓存它们不具备可能性和实用性。

缓存区的存在使得常用的包装类对象可以得到复用,这有利于提升性能。当我们需要创建新对象的时候再new一个,增加了灵活性。

四、包装类的四则运算、位运算、比较运算、逻辑运算

1、四则运算和位运算

//四则运算、位运算Integer num9 = 1;Integer num10 = 2;Integer num11 = num9 + num10; Short num12 = 5;Integer num13 = num9 + num12;Long num14 = num9 + 10L;System.out.println(num9 << 1);  //位运算System.out.println(num9 +" "+ num10 +" "+ num11 +" "+ num12 +" "+ num13 +" "+ num14);
image

反编译结果如下

        Integer integer = Integer.valueOf(1);        Integer integer1 = Integer.valueOf(2);        Integer integer2 = Integer.valueOf(integer.intValue() + integer1.intValue());        Short short1 = Short.valueOf((short)5);        Integer integer3 = Integer.valueOf(integer.intValue() + short1.shortValue());        Long long1 = Long.valueOf((long)integer.intValue() + 10L);        System.out.println(integer.intValue() << 1);        System.out.println((new StringBuilder()).append(integer).append("\t").append(integer1).append("\t").append(integer2).append("\t").append(short1).append("\t").append(integer3).append("\t").append(long1).toString());

可以看到Integer num11 = num9 + num10; 这一句被划分为3个步骤:将两个Integer对象分别进行拆箱;将拆箱得到的两个int数值相加求其和;将和值进行装箱,从而将num11指向缓存数组中值为3的Integer对象。

而Short num12 = 5; 这一句则先将5强制转换成short类型,再将其装箱把值为5的Short对象的引用赋给num12。

而Integer num13 = num9 + num12; 这一句除了Integer num11 = num9 + num10;的3个步骤,中间还有short+int=int的类型自动提升的过程。

而Long num14 = num9 + 10L; 这一句Integer num11 = num9 + num10;的3个步骤,中间还有强制类型转换的过程。需要注意的是,如果是Long num14 = num9 + num10; 的话就会出现类型不匹配的错误,因为num9、num10拆箱之后相加的和是int类型,而Long.valueOf(long)需要的形参是long类型,自然会出错。我们也可以看到,当包装类型对象和基本类型数据进行四则运算的时候,对象是会被拆箱的,然后再按基本类型数据的运算规则进行运算。

另外,如果仅仅是打印两个包装类型对象求和的结果,是不会有将和值重新转换成该包装类型的步骤的,如下面所示

System.out.println(num9 + num10);
System.out.println(integer.intValue() + integer1.intValue());

这里还需要注意一点,尽管基本类型与自动类型提升/强制类型转换,包装类是没有类似的用法的。下面的做法是错的。

Short num3 = 10;Integer num4 = num3;    //错误: 不兼容的类型: Short无法转换为IntegerLong num5 = (Long)num4;  //错误: 不兼容的类型: Integer无法转换为LongDouble num6 = num5;   //错误: 不兼容的类型: Long无法转换为Double

小结:不同包装类型对象是不能直接转换的,不过有两种途径可以代替:一种是上面讨论的不同包装类对象进行四则运算后赋给某一种类型;另一种就是利用包装类的方法

Integer a = 20;Long b = a.longValue();Short c = b.shortValue();System.out.println(a +"  "+ b +" "+ c);

2、比较运算和逻辑运算

Integer num9 = 100;Integer num10 = 200;Short num11 = 50;Long num12 = 50L;System.out.println((num9<num10) +" "+ (num9<200) +"    "+ (num9<num11) +"  "+ (num9<num12) +"  "+ (num9<10L));

反编译结果为

Integer integer = Integer.valueOf(100);Integer integer1 = Integer.valueOf(200);Short short1 = Short.valueOf((short)50);Long long1 = Long.valueOf(50L);System.out.println((new StringBuilder()).append(integer.intValue() < integer1.intValue()).append("\t").append(integer.intValue() < 200).append("\t").append(integer.intValue() < short1.shortValue()).append("\t").append((long)integer.intValue() < long1.longValue()).append("\t").append((long)integer.intValue() < 10L).toString());

可以看到,两个同类型的包装类对象进行比较时比较的其实是各自的基本类型数值,如num9 < num10;两个不同类型的包装类对象进行比较时则在比较基本类型数值之前,会有类型提升or强制类型转换,如num9 < num11,num9 < num12。

当想比较两个对象是否相等时,注意要使用equals()方法,从前面的讨论也知道,使用==的话比较的其实是引用的对象是否同一个,一般不满足我们的需求。

Integer num13 = new Integer(100);System.out.println(num9.equals(num13) +"   "+ num9.equals(50));

反编译结果为

Integer integer2 = new Integer(100);System.out.println((new StringBuilder()).append(integer.equals(integer2)).append("\t").append(integer.equals(Integer.valueOf(50))).toString());

逻辑运算举例:

System.out.println((num9&1));

反编译结果为

System.out.println(integer.intValue() & 1);

五、包装类作为方法的形参、返回值

//包装类作为方法的形参、返回值    public static Integer intToInteger(int i) {     return i;   }   public static int integerToInt(Integer i) {     return i;   }

反编译结果为

    public static Integer intToInteger(int i)    {        return Integer.valueOf(i);    }     public static int integerToInt(Integer integer)    {        return integer.intValue();    }

六、包装类作为集合的元素

//包装类作为集合元素List list = new ArrayList();list.add(1);list.add(new Object());Iterator it = list.iterator();while (it.hasNext()) {  System.out.println(it.next());}

反编译结果为

ArrayList arraylist = new ArrayList();arraylist.add(Integer.valueOf(1));arraylist.add(new Object());for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));

可以发现,虽然集合元素要求是对象,add()方法的形参也是对象(public boolean add(E e)),但由于自动装箱,基本数据类型也可以直接加入集合中。

                List<Integer> list = new ArrayList<>();     for (int i=0; i<5; i++) {           list.add(i);        }       Iterator it = list.iterator();      while (it.hasNext()) {          System.out.println(it.next());      }

反编译结果为

        ArrayList arraylist = new ArrayList();        for(int i = 0; i < 5; i++)            arraylist.add(Integer.valueOf(i));         for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));

七、包装类使用过程中有可能引起的空指针异常

//注意包装类可能产生的空引用异常       Boolean flag1 = false;      System.out.println(flag1?"命题为真":"命题为假");        Boolean flag2 = null;       System.out.println(flag2?"命题为真":"命题为假");        Boolean flag3 = true;

运行结果为

image

这里只是简单演示空指针异常。平时使用时需要注意这一点,比如当Boolean的对象作为形参时,在方法执行体的头部需要做下null检测。

上述代码的反编译结果为

        Boolean boolean1 = Boolean.valueOf(false);        System.out.println(boolean1.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");        Boolean boolean2 = null;        System.out.println(boolean2.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");        Boolean boolean3 = Boolean.valueOf(true);

可见三目运算符的条件表达式的位置一定是boolean值,如果你传入的是Boolean对象,则会自动拆箱转换为boolean值。

另外,三目运算符的其他两个表达式位置也是如此,会把包装类对象转换为相应的基本类型对象。

八、为什么需要包装类?有了包装类又为什么要保留基本数据类型?(包装类的优缺点)

为什么需要包装类?

首先,Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,将每个基本数据类型设计一个对应的类进行代表,这种方式增强了Java面向对象的性质。

其次,如果仅仅有基本数据类型,那么在实际使用时将存在很多的不便,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的,因为集合的容器要求元素是Object类型。而包装类型的存在使得向集合中传入数值成为可能,包装类的存在弥补了基本数据类型的不足。

此外,包装类还为基本类型添加了属性和方法,丰富了基本类型的操作。如当我们想知道int取值范围的最小值,我们需要通过运算,如下面所示,但是有了包装类,我们可以直接使用Integer.MAX_VALUE即可。

//求int的最大值int max = 0;int flag = 1;for (int i=0; i<31; i++) {   max += flag;    flag = flag << 1;}System.out.println(max +" "+ Integer.MAX_VALUE); //2147483647      2147483647

为什么要保留基本数据类型?

我们都知道在Java语言中,用new关键字创建的对象是存储在堆里的,我们通过栈中的引用来使用这些对象,所以,对象本身来说是比较消耗资源的。对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个对象的话,就会比较笨重了。所以,Java提供了基本数据类型,这种数据的变量不需要使用new在堆上创建,而是直接在栈内存中存储,因此会更加高效。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Block定义 闭包是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量(有时候也称为自由变量)bl...
    Breezes阅读 789评论 1 1
  • 包含的重点内容:JAVA基础JVM 知识开源框架知识操作系统多线程TCP 与 HTTP架构设计与分布式算法数据库知...
    消失er阅读 4,293评论 1 10
  • 4 千变万化的锁[#4-%E5%8D%83%E5%8F%98%E4%B8%87%E5%8C%96%E7%9A%84...
    憩在河岸上的鱼丶阅读 350评论 0 2
  • 尽管 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,即基本类型的数据不具备...
    不会游泳的金鱼_阅读 450评论 0 1
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,482评论 16 22