当前位置 博文首页 > moon_blade的博客:基础类型和包装类型

    moon_blade的博客:基础类型和包装类型

    作者:[db:作者] 时间:2021-07-15 19:02

    一、基础类型和包装类型对照

    对照表

    基础类型包装类型
    bytejava.lang.Byte
    shortjava.lang.Short
    intjava.lang.Integer
    longjava.lang.Long
    floatjava.lang.Float
    doublejava.lang.Double
    booleanjava.lang.Boolean
    charjava.lang.Character

    继承树

    包装类型继承树

    二、基础类型和包装类型的区别

    • 包装类型是对象,基础类型不是
      • 包装类型的变量存储在堆中,基础类型的变量存储在虚拟机栈的栈帧中
      • 包装类型可以用于容器和泛型,基础类型不可以
      • 包装类型的对象是引用类型,默认值为null,而基础类型的默认值都不是null
        • 如果一个包装类型的引用为null,则将其转换为基础类型会报空指针异常
      • 包装类型的内存占用比基础类型大
    • 包装类型不能直接进行算数运算(必须通过自动拆箱),基础类型可以直接进行算数运算
      • 包装类型的运算消耗比基础类型大,因为伴随着自动装箱和拆箱

    三、自动装箱(Autoboxing)和拆箱(Unboxing)

    自动装箱(Autoboxing)

    自动装箱是指java编译器将基础类型自动转换为对应的包装类型。

    • 什么时候发生自动装箱:
      • 传参:作为参数传递给需要相应包装类对象的方法
          Integer a = 10;//Integer a = Integer.valueOf(10);
        
      • 赋值:赋值给相应包装器类的变量
        public static Integer show(Integer iParam){
        	System.out.println("autoboxing example - method invocation i: " + 			iParam);
        	return iParam;
        }
        show(3); //autoboxing
        
    • 自动装箱的本质:调用包装类的静态方法valueof()将基础类型转换为包装类型。下面从字节码层面来观察:
      public static void main(String[] args) {
          Integer a = 3;
      }
      
      对应的字节码为
      public static void main(java.lang.String[]);
      Code:
         0: iconst_3
         1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: astore_1
         5: return
      

    自动拆箱(Unboxing)

    与自动装箱相对,自动编译器是指java编译器将包装类型自动转换为对应的基础类型。

    • 什么时候发生自动拆箱:
      • 传参:作为参数传递给需要相应原始类型值的方法。
        Integer oldCount = new Integer(10);
        int newCount = increment(oldCount);
        
        private static int increment(int count) {
        	return ++count;
        }
        
      • 赋值:赋值给相应原始类型的变量。
        Integer a = new Integer(3);
        int b = a;
        
      • 包装类型参与算数运算:
        Integer a = new Integer(3);
        int b = a + 1;
        
    • 自动拆箱的本质:调用包装类的静态方法intValue()、longValue()等等,来转换为基础类型。下面从字节码层面进行观察:
      public static void main(String[] args) {
          Integer a = new Integer(3);
          int b = a + 1;
      }
      
      对应的字节码为
      public static void main(java.lang.String[]);
      Code:
         0: new           #2                  // class java/lang/Integer
         3: dup
         4: iconst_3
         5: invokespecial #3                  // Method java/lang/Integer."<init>":(I)V
         8: astore_1
         9: aload_1
        10: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
        13: iconst_1
        14: iadd
        15: istore_2
        16: return
      

    包装类型的缓存

    为了减少自动装箱产生的无用中间对象对内存和垃圾回收的压力,Byte、Short、Integer、Long、Boolean、Character都带有缓存用来存储和复用常用的数据对象(float和double没有)。除了Integer、Boolean以外,其他包装类型的缓存策略均一样。

    • Integer:默认缓存-128到127之间的对象,可以通过jvm参数-XX:AutoBoxCacheMax来设置缓存的上届,缓存下届为-128固定不变
    • Boolean:true和false分别只有一个缓存
    • 其他:缓存对应数值在-128到127之间对象

    以上所有缓存只有使用包装类型的静态方法valueOf()生成对象时生效,包括主动调用、间接调用、自动装箱。

    基础类型和包装类型的相等判定

    • 基础类型之间只能用"=="来判定数值是否相等
    • 包装类型之间可以用“equals”方法来判定数值是否相等,用“==”来判定是否是同一个对象
    • 基础类型和包装类型之间可以用“==”来判定数值是否相等,此时会发生包装类型的自动拆箱;也可以用“equals”来判定数值是否相等,equals前面必须是对象,此时会发生自动装箱
    • 如果是valueof产生的在缓存区间的对象,则数值相等的必定是同一个对象

    示例如下:

    public class AutoboxingTest {
    
        public static void main(String args[]) {
    
            // Example 1: == comparison pure primitive – no autoboxing
            int i1 = 1;
            int i2 = 1;
            System.out.println("i1==i2 : " + (i1 == i2)); // true
    
            // Example 2: equality operator mixing object and primitive
            Integer num1 = 1; // autoboxing
            int num2 = 1;
            System.out.println("num1 == num2 : " + (num1 == num2)); // true
    
            // Example 3: special case - arises due to autoboxing in Java
            Integer obj1 = 1; // autoboxing will call Integer.valueOf()
            Integer obj2 = 1; // same call to Integer.valueOf() will return same
                                // cached Object
    
            System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true
    
            // Example 4: equality operator - pure object comparison
            Integer one = new Integer(1); // no autoboxing
            Integer anotherOne = new Integer(1);
            System.out.println("one == anotherOne : " + (one == anotherOne)); // false
    
        }
    
    }
    
    Output:
    i1==i2 : true
    num1 == num2 : true
    obj1 == obj2 : true
    one == anotherOne : false
    
    cs