精华内容
下载资源
问答
  • 基于S7 200PLC饮料瓶自动装箱系统
  • 以计数器74LS160芯片为主,结合LED和光电二极管组成的信号产生电路、NE555 组合整形电路等设计的产品自动装箱生产线电路。通过信号产生电路将光信号转换为电信号,再经过放大电路对输出波形电压进行放大,接着经过...
  • 根据自动装箱控制系统的机械结构和工作原理,利用STM32F103系列单片机和伺服电机,设计了控制系统硬件电路原理图,完成了对自动装箱系统码垛机械手的控制,实现了自动装箱系统对小型电器产品包装盒的抓取、运送、...
  • 随着我国的经济的不断发展,啤酒被广大的消费者...本文设计的课题就是基于PLC的啤酒自动装箱控制系统设计,对本设计的各个内容将进行具体的描述,本文设计的内容包括各个方案的选择,以及所涉及到元器件的性能和措施等。 
  • 基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装成为装箱,解包装称为拆箱)
  • 主要介绍了Java基础之自动装箱,注解操作,结合实例形式分析了java拆箱、装箱、静态导入、注释等相关使用技巧,需要的朋友可以参考下
  • CAD2014版自动封箱机方案案例,适合小家电产品或电脑周边配件的自动装箱,利用真空吸盘将产品自动放入打包箱里,节省人力,快速高校。
  • 如何理解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

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

    千次阅读 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自动装箱与自动拆箱(包装类)

    千次阅读 多人点赞 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

    展开全文
  • Java自动装箱性能

    2021-01-20 03:39:18
    有了自动装箱,你可以写如下的代码  Character boxed = 'a';  char unboxed = boxed;  编译器自动将它转换为  Character boxed = Character.valueOf('a');  char unboxed = boxed.charValue();  然而,...
  • 自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象。自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接。  如果你在Java...
  • 自动拆箱与自动装箱六. 自动装箱/自动拆箱原理七. 分析自动拆装箱使用场景八. 自动拆装箱与缓存九.使用包装类弊端 一. 八大基本数据类型 基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型。它们是...

    一. 八大基本数据类型

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

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

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

    分类类型
    字符类型char(8位)
    整数类型byte(8位)、short(16位)、int(32位)、long(64位)
    布尔类型boolean(1位)
    浮点数类型float(32位)、double(64位)
    • boolean 只有两个值:true、false,可以使用 1 bit来存储,但是具体大小没有明确规定。
    • JVM 会在编译时期将 boolean 类型的数据转换为int,使用1 来表示 true,0 表示 false
    • JVM 支持 boolean 数组,但是是通过读写 byte 数组来实现的。
    • 实际上,Java中还存在另外一种基本类型void,它也有对应的包装类java.lang.Void,不过我们无法直接操作

    二. 使用基本数据类型有什么好处

    1. 在java中, new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象,创建对象本身来说是比较消耗资源的。

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

    三. 整数类型的取值范围

    Java中的整型主要包含byte、short、int和long这四种,表示的数字范围也是从小到大的,之所以表示范围不同主要和他们存储数据时所占的字节数有关。

    1字节=8位(bit) java中的整型属于有符号数(可以带正负符号)

    8bit可以表示的数字:(以byte类型为例)
    最小值:10000000 (-128)(-2^7)
    最大值:01111111(127)(2^7-1)

    整型的这几个类型中

    类型字节占用范围默认值
    byte1个字节范围为 -128 (-2^7)127 (2^7-1)在变量初始化的时候,byte类型的默认值为0
    short2个字节范围为 -32,768 (-2^15)32,767 (2^15-1)在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0
    int4个字节范围为 -2,147,483,648 (-2^31)2,147,483,647 (2^31-1)在变量初始化的时候,int类型的默认值为0
    long8个字节范围为 -9,223,372,036,854,775,808 (-2^63)9,223,372,036, 854,775,807 (2^63-1)在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0

    超出取值范围怎么办
    每个整型类型都有一定取值范围,当超出取值范围,即溢出。如以下代码:

        @Test
        public void testOverFlow() {
            int i = Integer.MAX_VALUE;
            int j = Integer.MAX_VALUE;
    
            int k = i + j;
            System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");
            //i (2147483647) + j (2147483647) = k (-2)
        }
    

    结果为: i (2147483647) + j (2147483647) = k (-2) 这就是发生了溢出,溢出的时候并不会抛异常也没有任何提示。在程序中,使用同种类型的数据进行运算的时候,一定要注意数据溢出的问题。

    四. 八大包装类型

    • 包装类(Wrapper Class),均位于java.lang包, 它将基本类型进行一层封装,并类中定义了许多方法操作该数据。如:转换字符串, 转换成基本类型
    基本类型包装类型所属分类
    byteByte整数类型
    shortShort整数类型
    intInteger整数类型
    longLong整数类型
    doubleDouble浮点数类型
    floatFloat浮点数类型
    booleanBoolean布尔类型
    charCharacter字符类型

    为什么需要包装类型

    1. 因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如集合类List,Map中,我们无法将int 、double等类型保存进去的。因为集合的容器要求元素是Object类型
    2. 为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作

    五. 自动拆箱与自动装箱

    那么,有了基本数据类型和包装类,肯定有些时候要在他们之间进行转换。比如把基本数据类型的int转换成一个包装类型的Integer对象。把包装类型Integer对象转换成基本类型int

    因此

    • 基本类型转换成包装类型的过程就是装箱,英文对应于boxing
    • 包装类型转换成基本类型的过程就是拆箱,英文对应于unboxing

    Java 1.5以前我们需要手动进行转换才行

    /Java 1.5以前
    Integer iObject = Integer.valueOf(3);// 装箱
    int iPrimitive = iObject.intValue()// 拆箱
    

    Java 1.5以后提供了自动装箱的功能,直接使用 Integer integer = 10;语法就可以完成装箱,所有的转换都是由编译器来完成,编译器会判断是否进行自动装箱动作。自动装箱只适用于八大基本类型

    • 自动装箱: 就是将基本类型自动转换成对应的包装类型。
    • 自动拆箱:就是将包装类型自动转换成对应的基本类型。
    //Java 1.5之后
    Integer iObject = 3; //自动装箱 - primitive to wrapper conversion
    int iPrimitive = iObject; //拆箱 - object to primitive conversion
    

    六. 自动装箱/自动拆箱原理

        public static void main(String[] args) {
            Integer integerNum = 1; //自动装箱
            int intNum = integerNum; //自动拆箱
        }
    

    反编译上面代码

      public static void main(String[] args) {
        Integer integerNum = Integer.valueOf(1);
        int intNum = integerNum.intValue();
      }
    

    从上面反编译后的代码可以看出,
    int自动装箱都是通过Integer.valueOf()方法来实现的,
    Integer自动拆箱都是通过integer.intValue来实现的。

    可以试着将八种类型都反编译一遍 ,你会发现以下规律:

    自动装箱都是通过包装类的valueOf()方法来实现的,自动拆箱都是通过包装类对象xxxValue()来实现的。

    七. 分析自动拆装箱使用场景

    • 场景一:将基本数据类型放入集合类
    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i ++){
        li.add(i);
    }
    

    反编译结果

    
    List<Integer> li = new ArrayList<>();
    for (int i = 1; i < 50; i += 2){
        li.add(Integer.valueOf(i));
    }
    

    结论: Java中的集合类只能保存对象类型,基本数据类型放入集合类中的时候,会进行自动装箱成包装类对象

    • 场景二:包装类型和基本类型的大小比较
            Integer a = 1;
            System.out.println(a == 1 ? "等于" : "不等于");
            Boolean bool = false;
            System.out.println(bool == true ? "真" : "假");
    

    反编译结果

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

    结论: 包装类与基本类型比较时,是先将包装类进行拆箱成基本数据类型,然后进行比较的。

    • 场景三:包装类型的运算
            Integer i = 10;
            Integer j = 20;
            System.out.println(i + j);
    

    反编译结果

        Integer i = Integer.valueOf(10);
        Integer j = Integer.valueOf(20);
        System.out.println(i.intValue() + j.intValue());
    

    结论: 两个包装类型之间的运算,会被自动拆箱成基本类型进行。

    • 场景四:三目运算符的使用
            Integer i = 0;
            int j = 1;
            boolean flag = true;
            int k = flag ? i : j;
    

    反编译结果

        Integer i = Integer.valueOf(0);
        int j = 1;
        boolean flag = true;
        int k = flag ? i.intValue() : j;
    

    结论: 这是三目运算符的语法规范:当第二,第三位操作数分别为基本类型包装类型时,其中的包装类型就会自动拆箱为基本类型进行操作。
    例子中,flag ? i : j;片段中,第二段的i是一个包装类型的对象,而第三段的j 是一个基本类型,所以会对包装类进行自动拆箱。如果这个时候i 的值为null,那么久会发生NPE。(自动拆箱导致空指针异常

    • 场景五:方法形参与返回值
    //自动拆箱
    public int getInt(Integer num) {
    	return num;
    }
    //自动装箱
    public Integer getInteger(int num) {
    	return num;
    }
    

    反编译结果

    public int getInt(Integer num) {
    return num.intValue();
    }
    
    public Integer getInteger(int num) {
    	return Integer.valueOf(num);
    }
    

    结论:

    八. 自动拆装箱与缓存

    Java的编译器把基本类型 自动 转换成 包装类对象的过程叫做自动装箱,相当于使用valueOf()方法:

    代码

    		Integer integer1 = 3;
            Integer integer2 = 3;
            if (integer1 == integer2) {
                System.out.println("integer1 == integer2");
            } else {
                System.out.println("integer1 != integer2");
            }
    
            Integer integer3 = 300;
            Integer integer4 = 300;
            if (integer3 == integer4) {
                System.out.println("integer3 == integer4");
            } else {
                System.out.println("integer3 != integer4");
            }
    

    执行结果:
    在这里插入图片描述

    • 上面的两个都是用==判断包装类对象是否相等。而 == 如果左右两边比较的是对象,那比较的是内存地址是否相同, 所以在这个例子中,不同的对象有不同内存,那个么应该返回的都是false才对,但实际上却是一个true,一个false

    那为什么返回的结果不一致呢?

    • 原因和Integer中的缓存机制有关。在Java1.5中,在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用
      • integer整数值取值范围 在-128 至 +127`直接从缓存中获取,不创建新的对象
      • 只适用于自动装箱。使用构造函数new Integer()创建对象不适用。

    下面是JDK 1.8IntegervalueOf()方法的实现:

        /**
         * Returns an {@code Integer} instance representing the specified
         * {@code int} value.  If a new {@code Integer} instance is not
         * required, this method should generally be used in preference to
         * the constructor {@link #Integer(int)}, as this method is likely
         * to yield significantly better space and time performance by
         * caching frequently requested values.
         *
         * This method will always cache values in the range -128 to 127,
         * inclusive, and may cache other values outside of this range.
         *
         * @param  i an {@code int} value.
         * @return an {@code Integer} instance representing {@code i}.
         * @since  1.5
         */
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    

    在创建对象之前首先 数值 i取值范围是否在 IntegerCacheIntegerCache.high 之间, 如果在就 从IntegerCache.cache中寻找。如果找到就使用缓存里的Integer对象, 如果没找到就new 一个新的Integer对象

    IntegerCache 类
    IntegerCache 是 Integer类中的一个私有的静态内部类,使用了享元模式

     /**
         * Cache to support the object identity semantics of autoboxing for values between
         * -128 and 127 (inclusive) as required by JLS.
         *
         * The cache is initialized on first usage.  The size of the cache
         * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
         * During VM initialization, java.lang.Integer.IntegerCache.high property
         * may be set and saved in the private system properties in the
         * sun.misc.VM class.
         */
    
        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 = 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() {}
        }
    

    其中的源码注释详细的说明了缓存支持-128到127之间的自动装箱过程。

    1. 最大值127可以通过-XX:AutoBoxCacheMax=size修改。这个功能在Java1.5中引入的时候,范围是固定的-128 至 +127。后来在Java1.6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。

    2. 缓存通过一个for循环实现。从低到高并创建尽可能多的整数并存储在一个整数数组中。

    3. 这个缓存会在Integer类第一次使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

    实例

            Integer a = 12;  
            Integer b = 12;  
            System.out.println(a == b);// true  
            Integer c = 1234;  
            Integer d = 1234;  
            System.out.println(c == d);// false  
              
            Integer e = new Integer(13);  
            Integer f = new Integer(13);  
            System.out.println(e == f); //false  
              
            Integer g = new Integer(1200);  
            Integer h = new Integer(1200);  
            System.out.println(g == h); //false 
    
    1. 通过Java源代码可以看到Integer.valueOf()中有个内部类IntegerCache(类似于一个常量数组,也叫对象池),它维护了一个Integer数组cache,长度为(128+127+1)=256 。
    2. Integer类中还有一个Static Block(静态块)。从这个静态块可以看出,Integer已经默认创建了数值【-128-127】的Integer缓存数据。所以使用Integer a=10时,JVM会直接在该在对象池找到该值的引用。也就是说这种方式声明一个Integer对象时,JVM首先会在Integer对象的缓存池中查找有没有10的对象,如果有直接返回该对象的引用;如果没有,则使用new一个对象,并返回该对象的引用地址
    3. 因为Java中“==”比较的是两个对象的引用(即内存地址),a、b为同一个对象,所以结果为true,因为300超过了Integer缓存数据范围,使用new方式创建:c = new Integer(1234); d=new Integer(40);虽然他们值相等,但是属于不同的对象,不会被放到对象池中,所以他们不是同一个引用,返回false。

    用一句简短通俗的话话概括享元模式:

    • 如果有很多个小的对象,它们有很多属性相同,那可以把它们变成一个对象,那些不同的属性把它们变成方法的参数,称之为外部状态,相同的属性称之为内部状态,这种机制即为享元模式。

    到底是什么原因选择这个-128到127范围呢?

    • 因为这个范围的数字是最被广泛使用的。 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存。

    在Boxing Conversion部分的Java语言规范(JLS)规定如下:

    • 如果一个变量p的值是:-128至127之间的整数 或者 true 和 false的布尔值 或者 ‘\u0000’至 ‘\u007f’之间的字符范围内的时,将p包装成a和b两个对象时,可以直接使用a==b判断a和b的值是否相等。否则就要使用equals来判断

    其他整型对象有没有缓存机制
    这种缓存行为不仅适用于Integer对象。所有的整数类型的类都有类似的缓存机制

    包装类对应的缓存内部类
    ByteByteCache用于缓存Byte对象
    ShortShortCache用于缓存Short对象
    LongLongCache用于缓存Long对象
    CharacterCharacterCache用于缓存Character对象
    • Byte, Short, Long固定范围: -128 到 127。对于Character, 范围是0 到 127, 并且他们的范围都不能改变

    九.使用包装类弊端

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

    2. 有些场景由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE(空指针异常)

    3. 容易生成无用对象,因为自动装箱会隐式地创建对象,在一个循环体中,会创建无用的中间对象,这样会增加GC压力,拉低程序的性能。 所以在写循环时一定要注意代码,避免引入不必要的自动装箱操作.

    展开全文
  • 什么是自动装箱和自动拆箱? 自动装箱就是Java自动将原始值类型转变为对应的对象,如int 自动转变为Integer的过程。 自动拆箱则反之。 Java是怎么实现自动装箱和拆箱的? 测试代码如下: public class AutoPacking {...
  • 1. 装箱拆箱的基本介绍 Java中本身具有八种基本数据类型,他们分别是: 基本数据类型 包装类 boolean Boolean char Character byte Byte short Short int Integer long Long float Float ...
  • 什么是自动装箱,自动拆箱 自动装箱就是自动将基本数据类型转换为包装器类型; 自动拆箱就是自动将包装器类型转换为基本数据类型。 基本数据类型 int、short、long、float、double、byte、char、boolean。 包装器...
  • JAVA中最常用的语法糖--自动装箱和拆箱 一,究竟什么是自动装箱和拆箱 1.1 我们直接来看一段非常简单的实例代码: public static void main(String[] args) { int intValue = 100; Integer boxValue = int...
  • Java自动装箱和拆箱的实现原理

    千次阅读 2019-10-02 10:02:31
    一、装箱和拆箱 原始类型转换为对象类型就是装箱,反之就是拆箱。 原始类型byte,short,char,int,long,float,double,...自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intV...
  • 主要剖析了Kotlin基本类型自动装箱的一点问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 装箱和拆箱,自动装箱和自动拆箱 以Integer的创建为例。 装箱和拆箱 装箱:把基本数据类型转换成包装类对象(int—>Integer) Integer num1=new Integer(17); 拆箱:把一个包装类的对象,转换成基本类型的变量...
  • 1 什么是自动装箱?什么是自动拆箱? 自动装箱和自动拆箱的示例: public class Test { public static void main(String[] args) { ArrayList<Integer> intList = new ArrayList<Integer>(); int...
  • 什么是自动拆箱和自动装箱

    千次阅读 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) ,...
  • 主要介绍了Kotlin基本类型自动装箱出现问题解决办法的相关资料,希望通过本文能帮助到大家,让大家遇到这样的问题顺利解决,需要的朋友可以参考下
  • 记得以前学习Java的时候有自动装箱与拆箱的概念。比如: // 自动装箱 Integer num = 100; // 自动拆箱 int primitiveNum = num; 所谓装箱拆箱就是指基本数据类型与包装类型的自动相互转换。他的目的就是可以简化...
  • 自动装箱:将基本数据类型重新转化为对象 public class Test { public static void main(String[] args) { // 声明一个Integer对象,用到了自动的装箱:解析为:Integer num = Integer.valueOf(9); Integer ...
  • 主要给大家介绍了关于Java中自动装箱、拆箱引起的耗时的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • 自动装箱自动拆箱概念 先看代码: int num = 10;//自动装箱 Integer in = num; int i = in;//自动拆箱 in是个对象,num、i是int型的变量,如果直接赋值按理说会报错,但是由于自动装箱和自动拆箱的存在,上述语句可...
  • 主要介绍了java编程中自动拆箱与自动装箱详解,具有一定参考价值,需要的朋友可以了解下。
  • 自动装箱

    2018-05-15 10:17:34
    自动装箱只能用equals== 比较地址Equals 比较对象

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 75,990
精华内容 30,396
关键字:

自动装箱