JAVA应用】自动装箱自动拆箱的代码实现原理

2021-03-01 09:34发布

6条回答

一、装箱和拆箱

原始类型转换为对象类型就是装箱,反之就是拆箱。

原始类型byte,short,char,int,long,float,double,boolean对应的封装类为Byte,Shor,Character,Integer,Long,Float,Double,Boolean.


二、源码解读

自动装箱时编译器调用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>

                cache[k] = new Integer(j++);


            // range [-128, 127] must be interned (JLS7 5.1.7)

            assert IntegerCache.high >= 127;

        }


        private IntegerCache() {}

    }


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

Integer i = 40;相当于调用了Integer.valueOf(40)方法:

首先判断i是否在-128和127之间,如果在,直接从IntegerCache缓存中获取指定数字的包装类;不存在则new出一个新的包装类。

IntegerCache内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态代码块进行初始化-128到127之间的Integer对象,存到cache数组中。cache属于常量,存放在Java的方法区中。


这就解释了以下的结果:


Integer i = 40;

Integer j = 40;

Integer k = 200;

i == j    为true;

i == k   为false;

Integer i4 = new Integer(40);  

Integer i5 = new Integer(40);  

Integer i6 = new Integer(0); 

i4 == i5   为false,因为直接new,地址不同

i4 == i5+i6  为true,因为涉及到缓存。

1

2

3

4

5

6

7

8

9

10

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

    }


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

通过源码分析,只有double和float没有使用缓存,每次都是new一个新对象。

使用缓存的策略是因为缓存的这些对象都是经常使用到的,防止每次自动装箱都创建一次对象的实例。




十七
3楼 · 2021-03-01 17:12

先说说定义吧:

自动装箱就是Java自动将基本数据类型换成对应的对象类型,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱


实现原理:

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

希希
4楼 · 2021-03-02 08:41

一、装箱和拆箱

原始类型转换为对象类型就是装箱,反之就是拆箱。

原始类型byte,short,char,int,long,float,double,boolean对应的封装类为Byte,Shor,Character,Integer,Long,Float,Double,Boolean.


二、源码解读

自动装箱时编译器调用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>

                cache[k] = new Integer(j++);


            // range [-128, 127] must be interned (JLS7 5.1.7)

            assert IntegerCache.high >= 127;

        }


        private IntegerCache() {}

    }


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

Integer i = 40;相当于调用了Integer.valueOf(40)方法:

首先判断i是否在-128和127之间,如果在,直接从IntegerCache缓存中获取指定数字的包装类;不存在则new出一个新的包装类。

IntegerCache内部实现了一个Integer的静态常量数组,在类加载的时候,执行static静态代码块进行初始化-128到127之间的Integer对象,存到cache数组中。cache属于常量,存放在Java的方法区中。


这就解释了以下的结果:


Integer i = 40;

Integer j = 40;

Integer k = 200;

i == j    为true;

i == k   为false;

Integer i4 = new Integer(40);  

Integer i5 = new Integer(40);  

Integer i6 = new Integer(0); 

i4 == i5   为false,因为直接new,地址不同

i4 == i5+i6  为true,因为涉及到缓存。

1

2

3

4

5

6

7

8

9

10

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

    }


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

通过源码分析,只有double和float没有使用缓存,每次都是new一个新对象。

使用缓存的策略是因为缓存的这些对象都是经常使用到的,防止每次自动装箱都创建一次对象的实例。


py大白
5楼 · 2021-03-04 16:11

先说说定义吧:

自动装箱就是Java自动将基本数据类型换成对应的对象类型,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱


实现原理:

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


xiaoxiao_123
6楼 · 2021-03-18 14:43

装箱:指的是将基本类型数据封装成包装类对象形式。

自动装箱:指的是可以直接将基本类型数据赋值给包装类对象。

拆箱:指的是将包装类对象变成基本类型数据形式。

自动拆箱:指的是可以直接将包装类对象直接赋值给基本类型。

以Integer为例:

   自动装箱实现原理是省略了:new Integer(int value),构造方法的调用,可以直接Integer in = 123;

   自动拆箱实现原理是省略了: intValue()方法的调用, 可以直接 int num = in;

路小雨xiaoyu
7楼 · 2021-04-21 16:02

自动装箱和拆箱的原理


自动装箱时编译器调用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>


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


相信你看到这就应该能明白上面的程序输出的结果为什么是true,false了,只要掌握原理,类似的问题就迎刃而解了。

相关问题推荐

  • 回答 20

    100-199 用于指定客户端应相应的某些动作。 200-299 用于表示请求成功。 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。 400-499 用于指出客户端的错误。 400 语义有误,当前请求无法被服务器理解。 401 当前请求需要用户验证...

  • 回答 5
    已采纳

    1、相同点(1)都是表现层框架,都是基于MVC设计模型(2)底层都离不开 Servlet API(3)处理请求的机制都是一个核心控制器2、不同点(1)SpringMVC的入口是Servlet,而Struts2的入口是Filter(2)SpringMVC是基于方法设计的,而Struts2是基于类(3)SpringMV...

  • 回答 22
    已采纳

    类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结...

  • 回答 23
    已采纳

    (1)idea启动时会有两个快捷方式,安装完后默认生成在桌面的是32位的idea的快捷方式,如果我们使用这个快捷方式运行大项目,一般都会很卡。解决方法是找到idea的安装目录,然后进入bin文件夹,找到名称为idea64的应用程序,右键他生成桌面快捷方式。以后每次...

  • 回答 12
    已采纳

    获取Map集合中所有的key可以通过map集合的keySet()方法获取例如:    Map map = new HashMap();    map.put(xx,xx); //存放数据    //.... 省略    Set set = map.keySet();    //可以通过迭代器进行测试    Iterator iter = set.iter...

  • 回答 4
    已采纳

    Java中有八种数据类型,基础数据类型分别是:byte,short,int,long,float,double,char,boolean,引用数据类型分别是:数组,类和接口。方法传参的时候我们有两种,一种是形式参数(定义方法时写的参数),一种是实际参数(调用方法时给的具体值)。首先...

  • 回答 15
    已采纳

    现在的架构很多,各种各样的,如高并发架构、异地多活架构、容器化架构、微服务架构、高可用架构、弹性化架构等,还有和这些架构相关的管理型的技术方法,如 DevOps、应用监控、自动化运维、SOA 服务治理、去 IOE 等等,还有很多。分布式架构其实就是分布式系...

  • 回答 10

    1、监控GC的状态使用各种JVM工具,查看当前日志,分析JVM参数的设置,分析堆内存快照和GC日志,根据实际的各区域的内存划分和GC的执行时间,判断是否需要进行优化2、分析结果、判断是否需要优化如果各项参数设置合理,系统没有超时的日志出现,GC频率也不高,...

  • 回答 9
    已采纳

    从两个方面对ElasticSearch和Solr进行对比,从关系型数据库中的导入速度和模糊查询的速度。单机对比1. Solr 发布了4.0-alpha,试了一下,发现需要自己修改schema,好处是它自带一个data importer。在自己的计算机上测试了一下,导入的性能大概是:14分钟导入 ...

  • 回答 10
    已采纳

    操作系统中有若干进程并发执行,它们不断申请、使用、释放系统资源,虽然系统的进 程协调、通信机构会对它们进行控制,但也可能出现若干进程都相互等待对方释放资源才能 继续运行,否则就阻塞的情况。此时,若不借助外界因素,谁也不能释放资源,谁也不能解 ...

  • 回答 6

    MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以...

  • 回答 6

    学vue应该要先学习javascript 的基础知识和用法。

  • 回答 7

    synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 2. 修饰一个方法,被修饰的方法称为同步方法,其作用...

  • 回答 9

    是一个文件服务器,用来上传下载文件.它是一个分布式集群的.分2个角色调度和存储.上传时配置调度后上传.用的时候需要搭建.如果不会搭建想省事还可以选择阿里的oss .项目中应用还是比较多的因为一般项目都有文件上传和下载.选择目前就这2种方案.根据公司情况选...

  • 回答 8

    使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。...

没有解决我的问题,去提问