• Java自动装箱与自动拆箱


    1、Java基本数据类型及其对应的包装器类类型

    Java中共用8种基本数据类型,并为这8种基本数据类型中的每一种都提供了一个包装器类,例如int类型对应的包装器类是Integer。具体类型如下表:
    在这里插入图片描述

    2、自动装箱和自动拆箱

    • 自动装箱:就是指自动将基本数据类型转换为包装器类型
    • 自动拆箱:就是指自动将包装器类型转换为基本数据类型
    Integer i = 19;  // 自动装箱
    int j = i;  // 自动拆箱
    
    • 1
    • 2

    如上第一行代码,数组19是基本数据类型(int),当赋值给包装器类型(Integer)变量时,触发自动装箱操作,创建一个Integer类型对象,并且赋值给了 i 。其底层实际执行了以下代码:

    Integer i = Integer.valueOf(19);
    
    • 1

    同理,第二行代码,将 i 赋值给 j,触发了自动拆箱操作,将 i 中的数据取出赋值给 j 。其底层实际执行了以下代码:

    int j = i.intValue();
    
    • 1

    JDK5前是手动装箱和拆箱,JDK5之后就可以自动装箱和自动拆箱了。

    Object o1 = true ? new Integer(1) : new Double(2.0);   // 1.0
    
    object o2;
    if(true) {
    	o2 = new Integer(1);  // 1
    } else {
    	o2 = new Double(2.0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上第一个三元运算符输出的结果是1.0,因为三元运算符是一个整体,所以前面的Integer受后面的Double影响。而下面的if-else是无影响的,则输出1。

    Integer i = 100;  // 自动装箱
    String str1 = i +  "";
    
    • 1
    • 2

    以上只是以i的基本数值转成了字符串,并不会影响i的数据类型。

    3、双等于“=="

    通过” == “ 来比较对比的是栈中的值,基本数据类型比较的值,引用数据类型比较的是堆中内存对象的地址。即判断两个对象是否相等,实际上是在判断两个局部变量存储的地址是否相同,即是否指向相同的对象。
    但如下代码中比较的a1和a2却是相等的。b1和b2又是不相等的。

    Integer a1 = 58;
    Integer a2 = 58;
    Integer b1 = 129;
    Integer b2 = 129;
    Integer a3 = new Integer(58);
    System.out.println(a1 == a2);  // true
    System.out.println(b1 == b2);  // false
    System.out.println(a1 == a3);  // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实际是,Integer运用了 享元设计模式 来复用对象,当通过自动装箱,即调用 valueOf() 来创建Integer对象的时候,如果要创建的Integer对象的值在 -128~127之间,就会从IntegerCache类中直接返回,否则才会调用new方法创建新的对象。即a1和a2都是指向同一个对象(享元对象),b1和b2不是同一个对象。 而Integer a3 = new Integer(58);并不会调用valueOf(),即不会使用到IntegerCache。

    为什么IntegerCache只缓存 -128~127之间的整型值?

    当IntegerCache类被加载的时候,缓存的享元对象会被集中一次性创建好,而整型数值太多,不能一次性被创建,否则会占用太多的内存,类加载的时间会过长,即只能缓存大部分应用来说最常用的整型值,即一个字节的大小,byte数据类型的范围。
    实际,jdk提供了自定义缓存的最大值,设置方法有

    方法一: -Djava.lang.Integer.IntegerCache.high = 255
    方法二: -XX:AutoBoxChaheMax = 255
    
    • 1
    • 2

    4、字符串类型String

     String s1 = "小姐姐";
     String s2 = "小姐姐";
     String s3 = new String("小姐姐");
     System.out.println(s1 == s2);  // true
     System.out.println(s1 == s3);  // false
    
     System.out.println(s1 == s3.intern());  // true
     System.out.println(s3 == s3.intern());  // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    String类型利用了享元设计模式来复用相同的字符串常量,JVM会专门开辟一块存储区来存储字符串常量,这块存储区叫做”字符串常量池“

    Integer类中要共享的对象是在类加载的时候就会集中一次性创建好,而字符串是在第一次被使用时被存储到常量池中,当之后再使用的时候,就直接引用常量池中已经存在的即可。

    当调用intern()方法时,如果池已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将此 String 对象 添加到池中,并返回此String 对象的引用,即 intern()方法 最终返回的是常量池的地址(对象)。s3是重新new了一个对象,所以s3是指向堆的,而s3.intern()是指向常量池的,所以 s3不会等于s3.intern() 。

    String s1 = "hello";
    s1 = "haha";
    
    • 1
    • 2

    String是一个final类,代表不可变的字符序列。字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。即以上是先创建hello对象,之后在常量池中找是否有haha对象,没有就创建haha对象,再将s1指向haha对象,共创建了两个对象

    String a = "hello" + "abc";
    
    • 1

    以上编译器会自动优化等价为:(即总共创建了一个对象)

    String a = "helloabc";
    
    • 1
    String a = "hello";  // 创建a对象
    String b = "abc";  // 创建b对象
    
    // 先创建一个StringBuilder sb = new StringBuilder()
    // 执行 sb.append("hello"); 再执行 sb.append("abc"); 最后转成string :String c = sb.toString()
    String c = a + b;  // 最终c指向堆中的对象
    
    Strin d = "helloabc";
    System.out.print(c == d);  // false  d指向常量池,c指向堆
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    SpringCloud Alibaba(六) - Seata 分布式事务锁
    MATLAB|不给糖果就捣蛋
    L4W2KT作业 Keras教程
    技术与业务同等重要,偏向任何一方都是错误
    Django内置函数详解Httprequest详解(模拟搜索/模拟用户登陆/模拟上传文件功能)
    C 语言的整数类型
    PHP代码审计13—弱类型
    【无标题】 python 数据写入excel
    python3-网站路径扫描
    互联网技术岗笔试基础题目练习①
  • 原文地址:https://blog.csdn.net/weixin_43763430/article/details/127944433