基本类型及包装类源码解析

基本与包装类

  • 基本类型有默认值,而包装类型初始为null
  • 所有的POJO类必须使用包装类型,在rpc方法中定义参数和返回值的类型用包装类,因为默认值不代表空值,比如0不代表该属性为空
  • 泛型调用函数必须用包装类,因为其参数类型都是Object。但是反射调用方法,其方法参数可以为基本类型,在invoke的java doc中说明了原始类型会和引用类型自动转换。
  • 在本地变量推荐使用基本类型。
  • 什么时候该用包装类,什么时候用基本类型,看基本的业务来定:这个字段允不允许null值或者参数为Object,则必然要用封装类,否则值类型就可以了。

数据类型

八种基本类型

  • 整数:byte(8-bit)、short(16-bit)、int(32-bit)、long(64bit)初始值为0。
  • 浮点型:float(32-bit)、double(64-bit)初始值为0.0
  • 字符:char(16-bit)初始值为空格,即’ ',如果输出,在Console上是看不到效果的。
    • Java采用unicode,2个字节来表示一个字符,1个字节等于16位
  • 布尔:boolean初始值为false

包装类

  • Integer 、Long、Short、Byte、Character、Double、Float、Boolean

  • BigInteger、BigDecmail

    • 没有相对应的基本类型,主要应用于高精度的运算

    • BigInteger 支持任意精度的整数, BigDecimal支持任意精度带小数点的运算。

    • 原理

      • 精度丢失

        不是所有的十进制数都能转化为有限位二进制数的。

        1、任意十进制整数可以转化为有限位数的二进制整数。

        ​ 如123=64+32+16+8+2+1,转化为二进制整数是1111011。

        2、能分解为以(1/2)^n为单位的十进制小数,可以转化为有限位数的二进制小数。

        ​ 如十进制数:13/16=0.8125,它可以是拆成:13/16=1/2+1/4+1/16,或者直接可以看作是13个 1/16所组成。而1/2,1/4,1/16这些数都是符合(1/2)^n形式的数。所以13/16转化为4位二进制 小数:0.1101。

        3、上述情形以外的十进制数都不能转化为有限位数的二进制数。

        ​ 如十进制小数0.7,转化为二进制小数是:0.1011001100110…,循环节是0110。

        当第三种情况的小数出现的时候,就会出现计算的精度误差,BigDecimal的原理很简单,就是将小数扩大N倍,转成整数后再进行计算,同时结合指数,得出没有精度损失的结果。

      • 源码(BigDecimal)

      public class BigDecimal {
          //值的绝对long型表示(数值整体扩大(10^scale)倍到不含小数)
          private final transient long intCompact;
          //值的小数点后的位数
          private final int scale;
          //先double转成String,再String转成BigDecimal
          private transient String stringCache;
       
          private final BigInteger intVal;
          //值的有效位数,不包含正负符号
          private transient int precision;
           
          //加、减、乘、除、绝对值
          public BigDecimal add(BigDecimal augend) {}
          public BigDecimal subtract(BigDecimal subtrahend) {}
          public BigDecimal multiply(BigDecimal multiplicand) {}
          public BigDecimal divide(BigDecimal divisor) {}
          public BigDecimal abs() {}
      }
      

      在运算完后调用静态工厂方法:

      // sum:小数扩大rscale倍long型数
      // rsacle:小数点后的位数
      BigDecimal.valueOf(sum,rscale);
      
      • 源码(BigInteger)
      /**
       * BigInteger是按位存储的数据,一个10进制数占3.3bit,mag存储的数据全是正数,符号单独用signum保存,如果数据的总位数超过Integer.MAX_VAULE 则会溢出。
       */
      public class BigInteger extends Number implements Comparable<BigInteger> {
          
          // 标志为正负或0
          final int signum;
      
          // 存储数据每一位的二进制数,一个int占32bit,最大可以用Integer.MAX_VAULE个bit
          final int[] mag;
         
         	....
      }
      
      /**
       * add先比较符号是否同,不同的话先比较两个数mag的大小,然后相减取较大值的符号
       */
      public BigInteger add(BigInteger val) {
              if (val.signum == 0)
                  return this;
              if (signum == 0)
                  return val;
              if (val.signum == signum)
                  return new BigInteger(add(mag, val.mag), signum);
      
              int cmp = compareMagnitude(val);
              if (cmp == 0)
                  return ZERO;
              int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
                                 : subtract(val.mag, mag));
              resultMag = trustedStripLeadingZeroInts(resultMag);
      
              return new BigInteger(resultMag, cmp == signum ? 1 : -1);
       }
      

异同

  • 八大基本类型不是对象。
  • 声明方式的不同,基本类型无需通过new关键字来创建,而封装类型需new关键字。
  • 原生类型在作为参数传递到方法中时使用的是“值传递”的方式,而对象作为参数传递到方法中时使用的是“传引用”的方式。
  • 存储方式及位置的不同,
    • 基本类型是直接存储变量的值保存在堆栈中能高效的存取
      • 在方法中声明的变量,即该变量是局部变量,存在虚拟机栈
      • 在类中声明的变量是成员变量,也叫全局变量,放在堆中
    • 封装类型需要通过引用指向实例,具体的实例保存在堆中
  • 初始值的不同,封装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;
  • 使用方式的不同,比如与集合类合作使用时只能使用包装类型。

长度

基本类型:Byte二进制位数:8
包装类:java.lang.Byte
最小值:Short.MIN=-128 (-2的7此方)
最大值:Short.MAX=127 (2的7次方-1)

基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768 (-2的15此方)
最大值:Short.MAX_VALUE=32767 (2的15次方-1)

基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE= -2147483648 (-2的31次方)
最大值:Integer.MAX_VALUE= 2147483647 (2的31次方-1)

基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808 (-2的63次方)
最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)

基本类型:float 二进制位数:32包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45 (2的-149次方)
最大值:Float.MAX_VALUE=3.4028235E38 (2的128次方-1)

基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324 (2的-1074次方)
最大值:Double.MAX_VALUE=1.7976931348623157E308 (2的1024次方-1)

float和double说明(IEEE754)

float:符号位(1bit),指数位(8bit),尾数位(23bit)。

  • 取值范围:-21 ~~ -22 , 0 , 23 ~~ 24
    • 指数位:指数位范围为(255-127 , 0 -127)
  • 精度范围(看小数(尾数)部分):2^(-23) = 1.1920929E-7,所以float的精度为6~7位,能保证6位为绝对精确,7位一般也是正确的,8位就不一定了(但不是说8位就绝对不对了)

double:符号位(1bit),指数位(11bit),尾数位(52bit)

  • 取值范围:-25 ~~ -26 , 0 , 27 ~~ 28
    • 指数位:指数位范围为(2047-1023 , 0 -1023)
  • 精度范围(看小数(尾数)部分):2^(-52) = 2.220446049250313E-16,所以double精度是15~16,能保证15,一般16位。

缓存池

--------------------------------------缓存自身所有值--------------------------------------

Boolean

boolean 的包装类型,缓存最简单,直接定义为静态常量就可以

public final class Boolean implements java.io.Serializable,Comparable<Boolean>{
    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
}

Character

Character 静态内部类 CharacterCache(ASCII 码范围为 0-127, 这里只缓存ASCII 码范围的char)

public final class Character implements java.io.Serializable, Comparable<Character> {

    public static Character valueOf(char c) {
        if (c <= 127) { // must cache
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }

    private static class CharacterCache {
        private CharacterCache(){}

        static final Character cache[] = new Character[127 + 1];

        static {
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Character((char)i);
        }
    }
}

Byte

Byte静态内部类ByteCache(byte 的范围是: -128<=x<=127,所以byte 一定是从缓存里面获取)

public final class Byte extends Number implements Comparable<Byte> {
  public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
  }

  private static class ByteCache {
      private ByteCache(){}
      
      static final Byte cache[] = new Byte[-(-128) + 127 + 1];

      static {
          for(int i = 0; i < cache.length; i++)
              cache[i] = new Byte((byte)(i - 128));
      }
  }
}

--------------------------------------缓存一定范围(256)--------------------------------------

Short

Short静态内部类ShortCache(缓存 -128<=x<=127 )

public final class Short extends Number implements Comparable<Short> {
    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);
    }

    private static class ShortCache {
        private ShortCache(){}

        static final Short cache[] = new Short[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Short((short)(i - 128));
        }
    }
}

Integer

IntegerCache 的范围是可以配置的,从源码中可以看出来 最低值一定是-128 是不可以修改的,但是上限值high 是可以修改的(通过jvm参数: -Djava.lang.Integer.IntegerCache.high=1024 修改为1024)。

public final class Integer extends Number implements Comparable<Integer> {
    public static Integer valueOf(int i) {
        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[];

            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() {}
      }
}

Long

Long静态内部类LongCache(缓存 -128<=x<=127 )

public final class Long extends Number implements Comparable<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);
    }

    private static class LongCache {
            private LongCache(){}

            static final Long cache[] = new Long[-(-128) + 127 + 1];

            static {
                for(int i = 0; i < cache.length; i++)
                    cache[i] = new Long(i - 128);
            }
     }
}

--------------------------------------不被缓存--------------------------------------
Float

  public static Float valueOf(float f) {
        return new Float(f); 
  }
}

Double

public final class Double extends Number implements Comparable<Double> {
    public static Double valueOf(double d) {
        return new Double(d);
    }
}

自动拆箱装箱

为啥有包包装类

  • 第一:如果你想在方法体内更新primitive类型的值,必须要使用primitive对应的object,因为前者使用的值传递,后者使用的是引用传递。
  • 第二:java.util内操作的都是对象,如果没有PWC,会让程序员在使用这些工具类操作primitive类型时编写额外的代码。集合框架中的数据结构,比如ArrayList和Vector,也是只能操作对象。
  • 第三:如果对象的属性有值类,在反序列化时无法把null值赋给基本类型
  • 第四:多线程中也必须使用对象来完成各种同步操作。
  • 第五:从设计理念上,在Java中,万物皆对象,为primitive类型设计出与之匹配的对象类型,更能让编程体验与设计理念融为一体!

装箱拆箱

  • 装箱

    // 手动装箱
    Integer i = Integer.valueOf(1);
    
    // 自动装箱
    Integer i = 1;
    
    List<Integer> list = new ArrayList();
    list.add(1);
    
  • 拆箱

    //手动拆箱
    int i = integer.intValue()
    
    int i = integer;
    

补充

位移符号

  • >>:带符号右移。正数右移高位补0,负数右移高位补1。

    比如:4 >> 1,结果是2;

    ​ -4 >> 1,结果是-2。

  • >>>:无符号右移。无论是正数还是负数,高位通通补0。

    对于正数而言,>>和>>>没区别。

    对于负数而言,-2 >>> 1,结果是2147483647(Integer.MAX_VALUE)

总结

  • 对于位移前,全部都是转化为int进行的,所以正数高位补0(补够32位),负数补1(补够32位)
  • 对于进行位移导致缺位
    • 补高位:>>正数高位补0,负数高位补1,符号位不移动;对于>>> 全都补0,符号位移动
    • 补低位:<<低位全都补0,符号位不移动、<<<低位全部补0,符号位移动
  • 对于计算机来说存储的都是补码,所以位移操作作用的都是补码
  • 对于通过二进制数读取的数值是补码,要转为原码,比如底层二进制数为 1110 0000 ,读取的值为-32

  1. 128 ↩︎

  2. -126 - 23 ↩︎

  3. -126 - 23 ↩︎

  4. 128 ↩︎

  5. 1024 ↩︎

  6. -1022 - 52 ↩︎

  7. -1022 - 52 ↩︎

  8. 1024 ↩︎

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页