美文网首页
[Java基础]包装类型

[Java基础]包装类型

作者: LCN233 | 来源:发表于2019-10-02 21:19 被阅读0次

    [Java基础]包装类

    Alt '包装类'

    了解一下

    • 什么是包装类型
      在 Java 中, 数据类型总共可以分为2大类 : 基础数据类型引用数据类型。基础数据类型并不支持面向对象编程, 因为基础数据类型不具备"对象"的特性 -- 携带属性和方法。 所以 Java 为8种基础数据类型提供了对应的类,他们就是包装类,侧面的将基础数据类型变为类,符合面向对象编程。 之所以没有一开始就是提供包装类,不使用基础数据类型, 个人认为只是为了迎合人类根深蒂固的习惯,并的确能简单、有效地进行常规数据处理。总的来说:包装类就是基础数据类型在面向对象中的体现。

    • 种类

    基本数据类型 对应的包装类
    byte(1字节) Byte
    short (2字节) Short
    int (4字节) Integer
    long (8字节) Long
    char (2字节) Character
    float (4字节) Float
    double(8字节) Double
    boolean (未定) Boolean

    在 Java boolean 的大小, 有三种说法 1/8字节, 1字节, 4个字节

    • Java 代码中的关系
    Alt '包装类的继承关系'

    装箱/拆箱

    在 Java 中,我们可以无缝的在包装类型和基础数据类型之间切换,之间是有一个装换过程的, 这个过程就是 装箱/拆箱

    // jdk5 之前, 我们如果需要将一个基础数据和包装类型切换,需要手动处理
    
    int m = 500;
    // 手动装箱
    Integer obj = new Integer(m);  
    // 手动拆箱
    int n = obj.intValue();
    
    // jdk5 后支持自动装箱和拆箱
    
    // 自动装箱, 实质是调用 对应包装类.valueOf() 方法实现的
    Integer obj = m;
    // 自动拆箱, 实质是调用 对应包装类.xxxValue()方法实现的, 其中的xxx 就是对应的数据基础数据类型
    int n = obj;
    

    包装类型的缓存问题

    从装箱和拆箱, 我们可以知道, 把一个基础数据类型转为对应的包装类型, 有2种方式

    // 主动装箱
    Integer num = new Integer(1);
    // 自动装箱
    Integer num = 1;
    

    这2种方式,虽然都能达到 将一个基础数据类型转为包装类型,但是他们内部的实现, 还是有很大的区别, 这个区别很多时候,能被用到很多面试题上(亲测,被问过好几次)。

    • 手动装箱
    public class Integer {
    
      // 内部还是通过一个基础数据类型存储我们的数据值的
      private final int value;
    
      public Integer(int value) {
        this.value = value;
      }
    }
    

    从 Integer 的源码我们可以知道,我们每次手动装箱,都会创建一个新的 Integer 对象。

    • 自动装箱
    public class Integer {
    
      /** 自动装箱 会调用到这个方法 */
      public static Integer valueOf(int i) {
          // 从这3行的 逻辑和 变量名 我们可以推测出
          // 如果我们 装箱的值  Integer缓存的下限 <= i <= Integer缓存的上限, 那么他会从 Interger的缓存里面取出对应的包装类,
          // 否则就重新创建一个新的
          // 那么 Integer的上下限分别是多少呢,这个就需要看到 IntegerCache的代码了
          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[];
    
          private IntegerCache() {}
    
          static {
            int h = 127;
            // 读取启动命令行 里面的 java.lang.Integer.IntegerCache.high 配置的值
            String integerCacheHighPropValue =  sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    
            if (integerCacheHighPropValue != null) {
              try {
                  int i = parseInt(integerCacheHighPropValue);
                  // 从配置的值和127中取大的那一个
                  i = Math.max(i, 127);
    
                  // 控制 h 的值 小于等于 Integer.MAX_VALUE - 129
                  // 这里说一下, h 和 high 都是 int 声明的, 所以最大值为 Integer.MAX_VALUE
                  // 同时为了避免下面的(high - low) + 1 = high + 128 + 1 经过运算后超过了 int 的最大值,变为负数, 所以此处才需要控制 h 的最大上限为  Integer.MAX_VALUE - 129
                  h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
              } catch( NumberFormatException nfe) {
              }
            }
    
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            // 创建 一个 缓存数组 存放 -128 到 h
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            assert IntegerCache.high >= 127;
          }
      }
    }
    
    1. 从 Integer 的源码 可以知道, Integer 内部维护了一个Integer的缓存,这个缓存默认为 -128 到 127 之间, 所以我们通过自动装箱创建的 Integer 并且值在这个区间内,都是从缓存里面获取到的,
    2. 同时我们可以在我们程序启动时, 指定参数 java.lang.Integer.IntegerCache.high = 你想要设置的缓存上限, 修改我们的Integer缓存池的上限
    3. 几个数值包装类型,除了有精度的 Float 和 Double 外都有对应的缓存, 都是 [-128, 127] 之间,但是只要 Integer 支持 配置

    题目

    // 第一, 比较的是否为同一个对象
    Integer num01 = new Integer(1);
    Integer num02 = 1;
    int num03 = 1;
    // false  2个对象 == 比较的是2个对象的内存地址是否一样
    System.out.println(num01 == num02);  
    // ture  基础数据类型 和 包装类型 比较时,之间比较数值大小
    System.out.println(num02 == num03);
     // true
    System.out.println(num01 == num03);
    
    
    // 第二, 比较的是 缓存, 自动装箱 数值默认在 [-128, 127]之间都是从缓存取对象, Integer num04 = 2;
    Integer num05 = 2;
    // true
    System.out.println(num04 == num05);
    
    Integer num06 = 128;
    Integer num07 = 128;
    // false
    System.out.println(num06 == num07);
    
    
    // 第三, 包装类型进行运算后,会被转为 基础数据类型
    Integer  num08 = 1;
    Integer  num09 = 2;
    Long num10 = 3L;
    // true, num08 + num09 变成了数值 3, 此处比较的是数值大小
    System.out.println(num10 == (num08 + num09));
    // false, equals 比较的是2个对象是否一样, 基础数据类型是无法比较的,所以此处自动装箱了
    System.out.println(num10.equals(num08 + num09));
    
    // 第四:低精度和高精度运算,低精度换转为高精度,再进行运算
    Integer  num11 = 1;
    Long  num12 = 2L;
    Long num13 = 3L;
    // 这里运算后 结果为 3L, 自动装箱为 3, 同时有缓存,所以2个一样
    System.out.println(num13.equals(num11 + num12));
    
    // 第五: Double 和 Float 的自动装箱没有缓存
    Double num14 = 1D;
    Double num15 = 1D;
    System.out.println(num14 == num15);
    

    总结

    包装类型的知识就这些吧, 在判断2个对象是否一样时,基本可以从下面入手

    1. 比较的是数值, 还是对象, 存在自动装箱和自动拆箱
    2. 比较的是包装类型,是否自动装箱了,值超过了缓存
    3. 包装类型进行了运算,默认运算后的结果是基础数据类型
    4. 低精度和高精度运算,低精度换转为高精度
    5. Double 和 Float 的自动装箱没有缓存

    相关文章

      网友评论

          本文标题:[Java基础]包装类型

          本文链接:https://www.haomeiwen.com/subject/vbprpctx.html