精华内容
下载资源
问答
  • 1、基本数据类型 基本类型,... 前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为 null ,那么自动拆箱时就有可能抛出 NPE。 如果一个 for 循环中有大量拆装箱操作,会浪费很多资源。

    1、基本数据类型

    基本类型,或者叫做内置类型,是 Java 中不同于类(Class)的特殊类型。它们是我们编程中使用最频繁的类型。

    Java 是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为变量的初始化。

    Java 基本类型共有八种,基本类型可以分为三类:

    字符类型 char

    布尔类型 boolean

    数值类型 byteshortintlongfloatdouble

    基本数据类型有什么好处

    我们都知道在 Java 语言中,new 一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。

    对于经常用到的类型,如 int 等,如果我们每次使用这种变量的时候都需要 new 一个 Java 对象的话,就会比较笨重。所以,和 C++ 一样,Java 提供了基本数据类型,这种数据的变量不需要使用 new 创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。

    包装类型

    Java 语言是一个面向对象的语言,但是 Java 中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。

    包装类均位于 java.lang 包,包装类和基本数据类型的对应关系如下表所示:

    为什么需要包装类型?

    这个问题,其实前面已经有了答案,因为 Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型放进去的。因为集合的容器要求元素是 Object 类型。

    为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。

    2、装箱与拆箱

    在 Java SE5 中,为了减少开发人员的工作,Java 提供了自动拆箱与自动装箱功能。

    自动装箱: 就是将基本数据类型自动转换成对应的包装类。

    自动拆箱:就是将包装类自动转换成对应的基本数据类型。

    Integer i=6 可以替代 Integer i = new Integer(10);,这就是因为 Java 帮我们提供了自动装箱的功能,不需要开发者手动去 new 一个 Integer 对象。

    package autoBox;
    
    /**
     * 自动装箱拆箱
     */
    public class AutoBoxOrUnBox {
        public static void main(String[] args) {
            Integer i = 6;
            int j = i;
        }
    }
    

    自动装箱与拆箱的原理:

    我们通过反编译工具的代码:

    package autoBox;
    
    public class AutoBoxOrUnBox {
      public static void main(String[] args) {
        Integer i = Integer.valueOf(10);
        int j = i.intValue();
      }
    }

    我们可以看出,int的 自动装箱是通过 Integer i = Integer.valueOf() 实现的。

    Integer 的自动拆箱都是通过 integer.intValue 来实现的

    自动装箱都是通过包装类的valueOf()方法来实现的.自动拆箱都是通过包装类对象的xxxValue()来实现的(如booleanValue()、longValue()等)。

    3、应用场景

    场景一:将基本数据类型放入集合类

    我们知道我们的结合中只能放入对象,所以我们的基本类型是不能放入到集合中的,所以我们就要使用包装类来实现。

    package autoBox;
    
    import java.util.ArrayList;
    
    /**
     * 自动装箱拆箱
     */
    public class AutoBoxOrUnBox {
        public static void main(String[] args) {
            Integer one = 1;
            Integer two = 2;
            ArrayList<Integer> arrayList = new ArrayList<>();
            arrayList.add(one);
            arrayList.add(two);
            for (Integer integer : arrayList) {
                System.out.println(integer);
            }
        }
    }
    

    把上述的代码进行反编译:

    package autoBox;
    
    import java.util.ArrayList;
    
    public class AutoBoxOrUnBox {
      public static void main(String[] args) {
        Integer one = Integer.valueOf(1);
        Integer two = Integer.valueOf(2);
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(one);
        arrayList.add(two);
        for (Integer integer : arrayList)
          System.out.println(integer); 
      }
    }
    

     可以看出会进行自动装箱。

    场景二:包装类型和基本类型的比较

        Integer a = 1;
        System.out.println(a == 1 ? "等于" : "不等于");
        Boolean bool = false;
        System.out.println(bool ? "真" : "假");
    

     反编译上述的代码:

        Integer a = 1;
        System.out.println(a.intValue() == 1 ? "等于" : "不等于");
        Boolean bool = false;
        System.out.println(bool.booleanValue ? "真" : "假");
    

     可以看到,包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的。

    场景三:包装类型的运算:

    package autoBox;
    
    import java.util.ArrayList;
    
    /**
     * 自动装箱拆箱
     */
    public class AutoBoxOrUnBox {
        public static void main(String[] args) {
            Integer i = 1;
            Integer j = 2;
            int res = i + j;
            System.out.println(res);
        }
    }
    

    反编译看一下:

    package autoBox;
    
    public class AutoBoxOrUnBox {
      public static void main(String[] args) {
        Integer i = Integer.valueOf(1);
        Integer j = Integer.valueOf(2);
        int res = i.intValue() + j.intValue();
        System.out.println(res);
      }
    }
    

     可以看到包装类的运算是先进行拆箱后进行运算的。

    场景四:三目运算符的使用:

    package autoBox;
    
    public class AutoBoxOrUnBox {
        public static void main(String[] args) {
            boolean flag = true;
            Integer i = 0;
            int j = 1;
            int k = flag ? i : j;
            System.out.println(k);
        }
    }
    

     反编译后的代码:

    package autoBox;
    
    public class AutoBoxOrUnBox {
      public static void main(String[] args) {
        boolean flag = true;
        Integer i = Integer.valueOf(0);
        int j = 1;
        int k = flag ? i.intValue() : j;
        System.out.println(k);
      }
    }
    

     可以看到 三目表达式这里发生了自动拆箱。

    这其实是三目运算符的语法规范。当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。

    因为例子中,flag ? i : j; 片段中,第二段的 i 是一个包装类型的对象,而第三段的 j 是一个基本类型,所以会对包装类进行自动拆箱。如果这个时候 i 的值为 null,那么就会发生 NPE。(自动拆箱导致空指针异常

    当第二位和第三位表达式都是包装类型的时候,该表达式的结果才是该包装类型,否则,只要有一个表达式的类型是基本数据类型,则表达式得到的结果都是基本数据类型。如果结果不符合预期,那么编译器就会进行自动拆箱。

    场景五:函数参与返回值

        //自动拆箱
        public int getNum1(Integer num) {
         return num;
        }
        //自动装箱
        public Integer getNum2(int num) {
         return num;
        }
    

    4、自动拆装箱与缓存

    package autoBox;
    
    /**
     * 包装类缓存
     */
    public class AutoBoxOrUnBox {
        public static void main(String... strings) {
    
            Integer integer1 = 6;
            Integer integer2 = 6;
    
            if (integer1 == integer2)
                System.out.println("integer1 == integer2");
            else
                System.out.println("integer1 != integer2");
    
            Integer integer3 = 200;
            Integer integer4 = 200;
    
            if (integer3 == integer4)
                System.out.println("integer3 == integer4");
            else
                System.out.println("integer3 != integer4");
        }
    
    }
    

     结果:

    integer1 == integer2
    integer3 != integer4
    

    == 与 equals       equals方法是Object类的方法。

     public boolean equals(Object obj) {
            return (this == obj);
        }

    从源码上看,equals底层还是调用的==方法,所以如果我们的类没有重equals方法,这两个方法效果是一样的,但是基本上所有的类都重写了这个方法,比较的都是对象的值是否相等。所以基本上==比较的是地址是否一样,equals比较的是值是否相等。

    回到我们的正题,上面的代码,我们的对象的地址肯定是不一样的,但是为什么会是这个结果呢。这里就设计到我们Java的缓存机制。

    在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。

    在包装类中,Integer类型比较特殊,其缓存值为-128~127,其中127为默认上限,也是最低上限。其实,Integer的缓存下限固定为-128,而缓存上限是可以在运行时通过修改系统属性来设置的。

    5、自动拆装箱带来的问题

    当然,自动拆装箱是一个很好的功能,大大节省了开发人员的精力,不再需要关心到底什么时候需要拆装箱。但是,他也会引入一些问题。

    包装对象的数值比较,不能简单的使用 ==,虽然 -128 到 127 之间的数字可以,但是这个范围之外还是需要使用 equals 比较。

    前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为 null ,那么自动拆箱时就有可能抛出 NPE。

    如果一个 for 循环中有大量拆装箱操作,会浪费很多资源。

    展开全文
  • 如何理解Java中的自动拆箱和自动装箱? 自动拆箱?自动装箱?什么鬼,听都没听过啊,这…这…知识盲区… 回到家后小伟赶紧查资料,我透,这不就是问基本类型跟封装类型吗,面试官整啥名词呢… 别问结果,问就是没过...

    小伟刚毕业时面的第一家公司就被面试官给问住了,记忆尤深啊…

    如何理解Java中的自动拆箱和自动装箱?

    自动拆箱?自动装箱?什么鬼,听都没听过啊,这…这…知识盲区…

    回到家后小伟赶紧查资料,我透,这不就是问基本类型跟封装类型吗,面试官整啥名词呢…

    别问结果,问就是没过。

    1、 什么是自动装箱,自动拆箱

    定义:基本数据类型和包装类之间可以自动地相互转换

    理解:装箱就是自动将基本数据类型转换为封装类型,拆箱就是自动将封装类型转换为基本数据类型。

    我们知道,Java中提供了四大类基本数据类型,分别是:整数、浮点数、字符型和布尔型,其中:

    • 整数包含:byte、int、short、long
    • 浮点数包含:float、double
    • 字符类型:char
    • 布尔类型:boolean

    基本数据类型相信大家一定很熟悉了吧,来来来,说说他们的取值范围~

    数据类型取值范围
    byte-128 ~ 127
    short-32786 ~ 32767
    int-4294967296 ~ 4294967295
    long-264 ~ 264 -1
    float3.4e-038 ~ 3.4e+038
    double1.7e-308 ~ 1.7e+308
    char\u0000 ~ \uffff
    booleantrue 、false

    日常开发中,靠这些基本数据类型几乎能够满足我们的需求,但是基本类型终究不是对象,往重了说不满足面向对象的开发思想,往轻了说就是使用不方便。怎么讲?例如做一些数据类型转换,获取int数据类型的取值范围等等。

    我们知道,类的优点在于它可以定义成员变量、成员方法,提供丰富便利的功能,因此Java在JDK1.0的时候就设计了基本数据类型的包装类,而在JDK1.5中引入了新特性:自动装箱和拆箱。

    我们来看一下基本类型跟封装类型之间的对应关系:

    数据类型封装类
    byteByte
    shortShort
    intInteger
    longLong
    floatFloat
    doubleDouble
    charCharacter
    booleanBoolean

    2、 使用包装类型后的便捷

    我们以上边提到的数据类型转换为例,看看使用包装类型后的便捷性。

    小伟在数据库中存放商品库存用的是 varchar 类型来存储的,所以在代码中的实体与之对应的是 String,那么问题来了,既然是库存,那么势必就要用到加减乘除之类的运算,所以就需要先转换成 数值类型(int\long\float等)来运算,我们看一下通过包装类是如何快速转换的「int\long\float」:

    public class Test {
        public static void main(String[] args) {
            // 数据库中的商品数量 number
            String number = "666";
            // 借助封装了 Integer 转换为 int
            int intVal = Integer.valueOf(number);
            // 借助封装了 Float 转换为 float
            float floatVal = Float.valueOf(number);
            // 借助封装了 Long 转换为 long
            long longVal = Long.valueOf(number);
            // 依次输出三个值的内容
            System.out.println("int="+intVal);
            System.out.println("floatVal="+floatVal);
            System.out.println("longVal="+longVal);
        }
    }
    

    3、 落实自动装箱、拆箱

    看完了包装类型的便捷性后,我们再来落实到自动装箱、自动拆箱上…

    怎么就自动装箱,自动拆箱了呢?

    上一段代码,看看哪是自动装箱跟自动拆箱:

    // 自动装箱
    1. Integer a = 100;
    // 自动拆箱
    2. int b = a;
    

    自动装箱,相当于Java编译器替我们执行了 Integer.valueOf(XXX);

    自动拆箱,相当于Java编译器替我们执行了Integer.intValue(XXX);

    我们证实一下,首先通过 javac 编译得到 class 文件,接着反编译看看:

    指令为:javap -c class文件名,得到下图所示:

    看完编译器替我们做的,接下来我们再通过源码看看,首先是自动装箱 valueOf() 方法:

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

    我们可以看到,首先是if方法, 对传入的int 数值进行判断,如果 i >= -128i <= 127 那么就会从IntegerCache缓存中获取指定数字的封装类,如果不存在则 new 出一个新的封装类,关于 IntegerCache ,其内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态块进行初始化-128~127之间的Integer对象,存放到cache数组中,cache属于常量,存放在java的方法区中,对方法区不太了解的小伙伴可以先留空,后面我会单独水一篇的~

    额外补充一下:上边我们只看了Integer封装类的自动装箱方法,从方法中我们了解了在-128~127之间使用了缓存,那么是不是意味着别的封装类也是这样呢?其实不是的,首先Integer使用缓存原因是该区间会被经常使用到,且数量个数比较确定,就256个值,所以为了提高效率,防止每次自动装箱都创建一次对象实例,然后就你懂得~,而double、float浮点型是没有使用缓存的,因为小数点的原因,所以在这个区间范围内个数是比较泛的,即不适合缓存,没有意义。

    我们通过一段代码看看这个缓存的效果吧:

    public class Test2 {
        public static void main(String[] args) {
            Integer a = 100;
            Integer b = 100;
            Integer c = 200;
            Integer d = 200;
            System.out.println(a==b); // 打印true
            System.out.println(a==b); // 打印false
        }
    }
    

    接着再来看自动拆箱 intValue() 方法:

    private final int value;
    
    public int intValue() {
    	return value;
    }
    

    这个方法就比较简单了,调用时直接返回了基本数据类型的 value 值。

    至此我们看完了自动装箱、自动拆箱,以Integer为例我们知道了使用 valueOf() 方法实现装箱,使用 intValue() 方法实现拆箱,接下来我们再结合几行代码重新回顾一下:

    1. Integer a = new Integer(100);
    2. Integer b = 100;
    3. b+=100;
    
    • 第一行代码:new 了一个 Integer 对象实例,将 int 类型的数据传入包装成了 Integer 类型。

    • 第二行代码:首先我们知道 100 是 int 类型的,但是等待复制的 b 是 Integer 类型,此时就用到了自动装箱,b = Integer.valueOf(100),将100包装成包装类了「通过反编译验证」

    • 第三行代码:用到了自动装箱+自动拆箱,b = b + 100 = Integer.intValye(b) + 100 此时计算结果得到的应该是 int 类型的 b,但是 b 又被限定了是 Integer 类型,所以就又要用到 Integet.valueOf() 自动装箱。

    4、 上才艺

    才艺一:如何理解Java中的自动拆箱和自动装箱?

    答:自动装箱就是将基本数据类型自动转换为封装类型,自动拆箱是将封装类型自动转换为基本数据类型。

    才艺二:能说一下是通过哪些方法实现自动拆箱、装箱的吗?

    答:以Integer为例,使用Integer.valueOf()方法实现装箱,使用Integer.intValue()方法实现拆箱。

    推荐阅读:

    跟小伟一起学习类加载机制

    为什么有这么多领优惠劵的群?

    单招计算机类哪个专业比较好

    本文首发于博客园:https://www.cnblogs.com/niceyoo

    展开全文
  • Java自动装箱与自动拆箱(包装类)

    千次阅读 多人点赞 2019-06-15 10:18:39
    达到的效果: 有了自动拆箱之后, 包装类的对象可当成基本类型的值 用——其实是【假相】 。 自动装箱 ----- 基本类型的值 → 包装类的实例 自动拆箱 ----- 基本类型的值 ← 包装类的实例 事实上,...

    一、基本介绍

    包装类的作用: Java 语言中,一切都是对象,但是有例外: 8 个基本数据类型不是对象,因此在很多时候非常不方便。 为此, Java提供为 8 个基本类型提供了对应的包装类:
                           byte ------- Byte 
                           short ------ Short 
                           int ---------- Integer 
                           long -------- Long 
                           char -------- Character 
                           float -------- Float                                                                                                                                                                                 double ----- Double 
                           boolean----- Boolean 

    包装类如何使用? JDK1.5 , 新增了 2 个功能:自动装箱自动拆箱

    • 自动装箱 当我们把一个基本类型的值( 20),赋值给引用变量时候,系统可以 自动将它“包装”为相应的包装类的实例程序需要对象时, 如果给的只是一个基本类型的值, 系统会将它自动装箱为包装类的实例达到的效果: 有了自动装箱之后, 基本类型的值可以当成对象用—— 其实是【假相】 。
    • 自动拆箱: 当我们需要一个基本类型的值时, 但实际上传入的包装类的对象。 系 统会自动把对象“剥”开,得到它的值。 达到的效果: 有了自动拆箱之后, 包装类的对象可当成基本类型的值 用——其实是【假相】 。

    自动装箱 ----- 基本类型的值 → 包装类的实例

    自动拆箱 ----- 基本类型的值 ← 包装类的实例

    事实上,包装类比基本类型更好用——基本类型能做的事情,包装类也能做。 但包装类能做的,基本类型不一定能做,比 如要赋一个 null 值。

    二、装箱和拆箱是如何实现的

    public class Main {
        public static void main(String[] args) {         
            Integer i = 10;
            int n = i;
        }
    }

    反编译为:

    package com.mao.a_box;
    
    public class Test01
    {
    
    	public Test01()
    	{
    	}
    
    	public static void main(String args[])
    	{
    		Integer i = Integer.valueOf(10);
    		int n = i.intValue();
    	}
    }

            从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法

    因此可以用一句话总结装箱和拆箱的实现过程:

      装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)

     三、面试中相关的问题

    1.下面这段代码的输出结果是什么?

    public class Main {
        public static void main(String[] args) {
             
            Integer i1 = 100;
            Integer i2 = 100;
            Integer i3 = 200;
            Integer i4 = 200;
             
            System.out.println(i1==i2);
            System.out.println(i3==i4);
        }
    }
    
    //true
    //false

    输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:

    public static Integer valueOf(int i) {
            if(i >= -128 && i <= IntegerCache.high)
                return IntegerCache.cache[i + 128];
            else
                return new Integer(i);
    }
    private static class IntegerCache {
            static final int high;
            static final Integer cache[];
    
            static {
                final int low = -128;
    
                // high value may be configured by property
                int h = 127;
                if (integerCacheHighPropValue != null) {
                    // Use Long.decode here to avoid invoking methods that
                    // require Integer's autoboxing cache to be initialized
                    int i = Long.decode(integerCacheHighPropValue).intValue();
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - -low);
                }
                high = h;
    
                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
            }
    
            private IntegerCache() {}
        }

    从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用否则创建一个新的Integer对象

      上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

     

    2.下面这段代码的输出结果是什么?

    public class Main {
        public static void main(String[] args) {
             
            Double i1 = 100.0;
            Double i2 = 100.0;
            Double i3 = 200.0;
            Double i4 = 200.0;
             
            System.out.println(i1==i2);
            System.out.println(i3==i4);
        }
    }
    //false
    //false

    在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。

    很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是

      注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。

         Double、Float的valueOf方法的实现是类似的。

     

    3.下面这段代码输出结果是什么:

    public class Main {
        public static void main(String[] args) {
             
            Boolean i1 = false;
            Boolean i2 = false;
            Boolean i3 = true;
            Boolean i4 = true;
             
            System.out.println(i1==i2);
            System.out.println(i3==i4);
        }
    }
    //true
    //true

    至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

    public static Boolean valueOf(boolean b) {
            return (b ? TRUE : FALSE);
        }

    而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

    public static final Boolean TRUE = new Boolean(true);
    
        /** 
         * The <code>Boolean</code> object corresponding to the primitive 
         * value <code>false</code>. 
         */
        public static final Boolean FALSE = new Boolean(false);

     

    4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

      当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:

      1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

      2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

     

    5.下面程序的输出结果是什么?

    public class Main {
        public static void main(String[] args) {
             
            Integer a = 1;
            Integer b = 2;
            Integer c = 3;
            Integer d = 3;
            Integer e = 321;
            Integer f = 321;
            Long g = 3L;
            Long h = 2L;
             
            System.out.println(c==d);
            System.out.println(e==f);
            System.out.println(c==(a+b));
            System.out.println(c.equals(a+b));
            System.out.println(g==(a+b));
            System.out.println(g.equals(a+b));
            System.out.println(g.equals(a+h));
        }
    }
    /*true
    false
    true
    true
    true
    false
    true*/

    其反编译为:

    package com.mao.a_box;
    
    import java.io.PrintStream;
    
    public class Main
    {
    
    	public Main()
    	{
    	}
    
    	public static void main(String args[])
    	{
    		Integer a = Integer.valueOf(1);
    		Integer b = Integer.valueOf(2);
    		Integer c = Integer.valueOf(3);
    		Integer d = Integer.valueOf(3);
    		Integer e = Integer.valueOf(321);
    		Integer f = Integer.valueOf(321);
    		Long g = Long.valueOf(3L);
    		Long h = Long.valueOf(2L);
    		System.out.println(c == d);
    		System.out.println(e == f);
    		System.out.println(c.intValue() == a.intValue() + b.intValue());
    		System.out.println(c.equals(Integer.valueOf(a.intValue() + b.intValue())));
    		System.out.println(g.longValue() == (long)(a.intValue() + b.intValue()));
    		System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));
    		System.out.println(g.equals(Long.valueOf((long)a.intValue() + h.longValue())));
    	}
    }

    第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

     

    作者:Matrix海子  

    出处:http://www.cnblogs.com/dolphin0520/

    转自:http://www.cnblogs.com/dolphin0520/p/3780005.html

    展开全文
  • 自动装箱和自动拆箱实现原理!

    千次阅读 2019-03-06 15:58:41
    什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是...

    什么是自动装箱和拆箱

    自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。原始类型byte, short, char, int, long, float, double 和 boolean 对应的封装类为Byte, Short, Character, Integer, Long, Float, Double, Boolean。

    下面例子是自动装箱和拆箱带来的疑惑

    
        public class Test {  
            public static void main(String[] args) {      
                test();  
            }  
        
            public static void test() {  
                int i = 40;  
                int i0 = 40;  
                Integer i1 = 40;  
                Integer i2 = 40;  
                Integer i3 = 0;  
                Integer i4 = new Integer(40);  
                Integer i5 = new Integer(40);  
                Integer i6 = new Integer(0);  
                Double d1=1.0;  
                Double d2=1.0;  
                  
                System.out.println("i=i0\t" + (i == i0));  
                System.out.println("i1=i2\t" + (i1 == i2));  
                System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));  
                System.out.println("i4=i5\t" + (i4 == i5));  
                System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));      
                System.out.println("d1=d2\t" + (d1==d2));   
                  
                System.out.println();          
            }  
        } 
    

    请看下面的输出结果跟你预期的一样吗?

    输出的结果:
    i=i0        true
    i1=i2       true
    i1=i2+i3    true
    i4=i5       false
    i4=i5+i6    true
    d1=d2     false

    为什么会这样?带着疑问继续往下看。

    自动装箱和拆箱的原理

    自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。

    明白自动装箱和拆箱的原理后,我们带着上面的疑问进行分析下Integer的自动装箱的实现源码。如下:

    
        public static Integer valueOf(int i) {
            //判断i是否在-128和127之间,存在则从IntegerCache中获取包装类的实例,否则new一个新实例
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
    
        //使用亨元模式,来减少对象的创建(亨元设计模式大家有必要了解一下,我认为是最简单的设计模式,也许大家经常在项目中使用,不知道他的名字而已)
        private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            //静态方法,类加载的时候进行初始化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;
            }
    
            private IntegerCache() {}
        }
    

    Integer i1 = 40; 自动装箱,相当于调用了Integer.valueOf(40);方法。
        首先判断i值是否在-128和127之间,如果在-128和127之间则直接从IntegerCache.cache缓存中获取指定数字的包装类;不存在则new出一个新的包装类。
        IntegerCache内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态块进行初始化-128到127之间的Integer对象,存放到cache数组中。cache属于常量,存放在java的方法区中。
        如果你不了解方法区请点击这里查看JVM内存模型

    接着看下面是java8种基本类型的自动装箱代码实现。如下:

        //boolean原生类型自动装箱成Boolean
        public static Boolean valueOf(boolean b) {
            return (b ? TRUE : FALSE);
        }
    
        //byte原生类型自动装箱成Byte
        public static Byte valueOf(byte b) {
            final int offset = 128;
            return ByteCache.cache[(int)b + offset];
        }
    
        //byte原生类型自动装箱成Byte
        public static Short valueOf(short s) {
            final int offset = 128;
            int sAsInt = s;
            if (sAsInt >= -128 && sAsInt <= 127) { // must cache
                return ShortCache.cache[sAsInt + offset];
            }
            return new Short(s);
        }
    
        //char原生类型自动装箱成Character
        public static Character valueOf(char c) {
            if (c <= 127) { // must cache
                return CharacterCache.cache[(int)c];
            }
            return new Character(c);
        }
        
        //int原生类型自动装箱成Integer
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
        //int原生类型自动装箱成Long
        public static Long valueOf(long l) {
            final int offset = 128;
            if (l >= -128 && l <= 127) { // will cache
                return LongCache.cache[(int)l + offset];
            }
            return new Long(l);
        }
    
        //double原生类型自动装箱成Double
        public static Double valueOf(double d) {
            return new Double(d);
        }
    
        //float原生类型自动装箱成Float
        public static Float valueOf(float f) {
            return new Float(f);
        }
    

    通过分析源码发现,只有double和float的自动装箱代码没有使用缓存,每次都是new 新的对象,其它的6种基本类型都使用了缓存策略。
        使用缓存策略是因为,缓存的这些对象都是经常使用到的(如字符、-128至127之间的数字),防止每次自动装箱都创建一次对象的实例。
        而double、float是浮点型的,没有特别的热的(经常使用到的)数据的,缓存效果没有其它几种类型使用效率高。(补充:一个范围内的整数是有明确的个数限制的,而浮点型则不同)

     

    下面在看下装箱和拆箱问题解惑。

        //1、这个没解释的就是true
        System.out.println("i=i0\t" + (i == i0));  //true
        //2、int值只要在-128和127之间的自动装箱对象都从缓存中获取的,所以为true
        System.out.println("i1=i2\t" + (i1 == i2));  //true
        //3、涉及到数字的计算,就必须先拆箱成int再做加法运算,所以不管他们的值是否在-128和127之间,只要数字一样就为true
        System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));//true  
        //比较的是对象内存地址,所以为false
        System.out.println("i4=i5\t" + (i4 == i5));  //false
        //5、同第3条解释,拆箱做加法运算,对比的是数字,所以为true
        System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));//true      
        //double的装箱操作没有使用缓存,每次都是new Double,所以false
        System.out.println("d1=d2\t" + (d1==d2));//false

    转载自:https://www.jianshu.com/p/0ce2279c5691

    展开全文
  •     基本类型可以使用运算符直接进行计算,但是引用类型不可以,而基本类型包装类作为引用类型的一种却可以计算,这就是因为java中自动为我们做好了Integer转换成int类型,这就是自动拆箱。     相应地,...
  • 什么是自动装箱和自动拆箱? 自动装箱就是Java自动将原始值类型转变为对应的对象,如int 自动转变为Integer的过程。 自动拆箱则反之。 Java是怎么实现自动装箱和拆箱的? 测试代码如下: public class AutoPacking {...
  • 什么是自动装箱,自动拆箱 自动装箱就是自动将基本数据类型转换为包装器类型; 自动拆箱就是自动将包装器类型转换为基本数据类型。 基本数据类型 int、short、long、float、double、byte、char、boolean。 包装器...
  • 什么是自动拆箱和自动装箱

    千次阅读 2019-12-25 20:28:02
    什么是自动拆箱和自动装箱 Java中基本类型有八种,可以分为四类: 整形: byte(一个字节来存储,范围为-2^7— 2^7-1), short (两个字节来存储,范围为-2^15— 2^15-1), int(四个字节来存储, -2^31— 2^31-1) ,...
  • 拆箱装箱 装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型) 面试常问 如果面试中问道具体的valueOf()的源码问题,可以通过...
  • 装箱和拆箱,自动装箱和自动拆箱 以Integer的创建为例。 装箱和拆箱 装箱:把基本数据类型转换成包装类对象(int—>Integer) Integer num1=new Integer(17); 拆箱:把一个包装类的对象,转换成基本类型的变量...
  • 自动装箱自动拆箱概念 先看代码: int num = 10;//自动装箱 Integer in = num; int i = in;//自动拆箱 in是个对象,num、i是int型的变量,如果直接赋值按理说会报错,但是由于自动装箱和自动拆箱的存在,上述语句可...
  • Integer自动装箱和自动拆箱 Integer i1 = 3; System.out.println(i1); 当我们运行这一段代码时,会正常输出3;所以我们可以知道,int类型–3是可以自动转换成Integer类型并且赋值给Integer对象的,称之为自动装箱。...
  • 一、什么是自动装箱和自动拆箱? 自动装箱: Java自动将基本数据类型封装为对象类型。 基本数据类型可以直接赋值给对应的包装类变量。如: Integer i = 100; 本质上是,编译器执行了Integer i = Integer.valueOf...
  • //先调用Boolean.valueOf(boolean b)返回false对应的Boolean对象Boolean.FALSE,然后赋值给flag,flag值为Boolean.FALSE /* 先赋值,遇到if条件表达式自动拆箱 * 1. 先调用Boolean.valueOf(boolean b)返回true对应...
  • 自动装箱:将基本数据类型重新转化为对象 public class Test { public static void main(String[] args) { // 声明一个Integer对象,用到了自动的装箱:解析为:Integer num = Integer.valueOf(9); Integer ...
  • 个人觉得自动装箱和自动拆箱其实和基本数据类型隐式转化一样,是自发进行的,但是它和隐式转化又不同,因为隐式转化是单向的,但是封装类和基本数据类型之间不仅可以自动装箱还可以自动拆箱,因此是双向的。...
  • 原来是在计算三元表达式结果时,a变量自动拆箱了。经 javap 反汇编发现,确实调用了Integer.intValue() 方法。 在三元表达式计算中,当第二、第三位操作数中有基本类型和对象时,对象就会拆箱为基本类型进行操作。...
  • 3)对于k == i3,==比较符又将右边的自动拆箱,因此它们比较的是数值是否相等,比较两个基本数据类型,结果当然为true。 4)对于kk == i1,解释和第三个一样。int和integer(无论new否)比,都为true,因为会把...
  • 规避自动拆箱引发的空指针的建议 1. 场景案例 package com.gblfy; /** * 自动拆箱引起的空指针问题场景 */ public class Unboxingnpe { private static int add(int x, int y) { return x + y; } private ...
  • //true a自动拆箱成int类型,再和c比较 实际上,当执行语句②时,系统为我们执行了: Integer i = Integer.valueOf(100);此也就是基本数据类型的自动装箱功能; 执行语句③时,实际执行了int t = i.intValue();自动...
  • 这里的输出是true,通过断点分析发现,在进行**==**判断的时候Integer类型的a拆箱,然后再对像个int类型的数据进行比较 问题2 Integer a = 9; int b = 9; boolean ff = a.equals(b); 这里自己先分析一下,应该是...
  • 所谓自动装箱就是Integer是对象类型,而int是基本数据类型类型。所以“Integer i1=10”这种写法在理论上是不可以的。正确的写法应该是"Integer i1=Integet.valueOf...同理自动拆箱也就是把Integer对象可以直接赋值给i
  • 包装类:java的基本数据类型包括boolean、byte、short、char、int、long、float和double。java同时也提供了与基本数据类型相关的类,实现了对基本数据类型的封装。这些类在java.lang包中,分别是Byte 、Integer、...
  • 什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。  一般我们要创建一个类的对象实例的时候,我们会这样:  Class a = new Class(parameter);  当我们...
  • JDK5新特性:自动装箱和自动拆箱

    千次阅读 2018-02-06 14:39:45
    首先使用Integer的intValue()方法拿到Integer类型i2的int数据(这里用到了自动拆箱)。先做int数据的加减法,将int类型的结果,再靠Integer的valueOf()方法自动装箱成Integer类型,赋给i2 总结一下: Integer与...
  • 如果integer对象直接参加运算是自动拆箱吗? Integer i=new Integer("10"); System.out.println(i+10); 也就是说是不是这里面先将对象i自动拆箱成int类型,然后再去进行运算?</p>
  • 自动拆箱和自动装箱

    2020-10-30 11:29:26
    4 //自动拆箱 5 int totalprim = total; 简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。 下面我们来看看需要装箱拆箱的类型有哪些: 这个过程是自动...
  • 先看以下代码: public class Test{ public static void main(String[] args){ Integer a = 12;//自动装箱 //以上代码等同于下面的代码 ... //自动拆箱 //以上代码等同于一下代码 int b = a.intvalue();
  • (二)自动装箱与自动拆箱 有时, 需要将 int 这样的基本类型转换为对象。 所有的基本类型都有一个与之对应的类。通常, 这些类称为 包装器 ( wrapper )。对象包装器类是不可变的,即一旦构造了包装器,就不允许...
  • Java:自动拆箱和装箱及包装类

    千次阅读 2018-10-26 11:50:48
    自动拆箱和装箱   自动拆箱和装箱是从JDK5.0才开始有的,它方便了基本数据类型和其对应的包装类型之间的转换。   将一个基本数据类型的值赋给其所对应的包装类型称之为自动装箱;将一个基本数据类型包装类类型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 50,534
精华内容 20,213
关键字:

自动拆箱