基本类型

在Java中,有8种基本类型:int、double、float、boolean、long、short、byte、char,每种类型所占用的字节数如下:

类型 short int long float double boolean byte char
占用字节数 2个字节 4个字节 8个字节 4个字节 8个字节 1个字节 1个字节 1个字节

Java基本类型和包装类型 - 图1

包装类型介绍

在Java中,每种基本类型都有对应的包装类型,比如int类型对应Integer包装类。包装类中除了包含原始类型的数据外,还增加了很多对数据操作的方法,比如parseInt(String s)、valueOf(int i)等,方便了对基本类型的操作。
包装类都是不可变类,一旦创建,就不能更改。

装箱和拆箱

装箱和拆箱操作其实是Java的语法糖,在编译期就会解除糖衣,以下面unBoxing()方法为例,在执行i1+i2操作时,其实编译器在背后先后做了拆箱和装箱操作,首先i1和i2对象分别调用Integer.intValue()进行拆箱,拆箱后的数值相加,得到结果后调用Integer.valueOf(int i)进行装箱,如下面反编译的结果所示。

  1. public void unBoxing() {
  2. Integer i1 = new Integer(1);
  3. Integer i2 = new Integer(2);
  4. //i1和i2会先拆箱,相加后再装箱
  5. Integer i3 = i1 + i2;
  6. }

Java基本类型和包装类型 - 图2
对基本类型的相加不会经过复杂的装箱和拆箱操作,如下面示例所示:

  1. public void primitiveType() {
  2. int i1 = 1;
  3. int i2 = 2;
  4. int i3 = i1 + i2;
  5. }

Java基本类型和包装类型 - 图3

包装类型缓存

在通过装箱操作(Integer.valueOf())创建Integer对象时,JLS规定[-128,127]之间的整数进行缓存,如下面代码注释所描述,如果不需要创建一个新的对象,那么使用装箱操作创建对象可以获得更好的性能,原因是该方法会缓存经常使用的整数值。

  1. /**
  2. * Returns an {@code Integer} instance representing the specified
  3. * {@code int} value. If a new {@code Integer} instance is not
  4. * required, this method should generally be used in preference to
  5. * the constructor {@link #Integer(int)}, as this method is likely
  6. * to yield significantly better space and time performance by
  7. * caching frequently requested values.
  8. *
  9. * This method will always cache values in the range -128 to 127,
  10. * inclusive, and may cache other values outside of this range.
  11. *
  12. * @param i an {@code int} value.
  13. * @return an {@code Integer} instance representing {@code i}.
  14. * @since 1.5
  15. */
  16. public static Integer valueOf(int i) {
  17. if (i >= IntegerCache.low && i <= IntegerCache.high)
  18. return IntegerCache.cache[i + (-IntegerCache.low)];
  19. return new Integer(i);
  20. }

缓存实现的原理比较简单,就是在第一次调用Integer.valueOf()时,会对预设的整数区间进行缓存,默认是[128,127],也可以通过 -XX:AutoBoxCacheMax=设置区间大小,需要注意一点是,在设置区间范围时,左侧的区间已经固定(-128),只能调整右区间。

  1. private static class IntegerCache {
  2. static final int low = -128;
  3. static final int high;
  4. static final Integer cache[];
  5. static {
  6. // high value may be configured by property
  7. int h = 127;
  8. String integerCacheHighPropValue =
  9. sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
  10. if (integerCacheHighPropValue != null) {
  11. try {
  12. int i = parseInt(integerCacheHighPropValue);
  13. i = Math.max(i, 127);
  14. // Maximum array size is Integer.MAX_VALUE
  15. h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
  16. } catch( NumberFormatException nfe) {
  17. // If the property cannot be parsed into an int, ignore it.
  18. }
  19. }
  20. high = h;
  21. cache = new Integer[(high - low) + 1];
  22. int j = low;
  23. for(int k = 0; k < cache.length; k++)
  24. cache[k] = new Integer(j++);
  25. // range [-128, 127] must be interned (JLS7 5.1.7)
  26. assert IntegerCache.high >= 127;
  27. }
  28. private IntegerCache() {}
  29. }

下面是几个示例,通过上面的分析,结果也都显而易见,唯一需要注意一点的是,包装类型的比较都需要用equals()。

  1. public void cache() {
  2. Integer i1 = new Integer(100);
  3. Integer i2 = new Integer(100);
  4. System.out.println("==:" + (i1 == i2) + " equals:" + i1.equals(i2));//false,true
  5. Integer i3 = new Integer(101);
  6. Integer i4 = Integer.valueOf(101);
  7. System.out.println("==:" + (i3 == i4) + " equals:" + i3.equals(i4));//false,true
  8. Integer i5 = 102;
  9. Integer i6 = 102;
  10. System.out.println("==:" + (i5 == i6) + " equals:" + i5.equals(i6));//true,true
  11. Integer i7 = 129;
  12. Integer i8 = 129;
  13. System.out.println("==:" + (i7 == i8) + " equals:" + i7.equals(i8));//false,true
  14. }

常犯的问题

case1:包装类型设值未判空

两个模型中字段的类型分别为包装类型和基本类型,在进行模型转换时,没有对包装类型的字段进行判空处理,直接设值,会出现空指针的问题,如下面示例:

  1. Integer s = null;
  2. int s1 = s.intValue();

case2:对外接口如何选择

对外暴露的接口,如果是数字类型,往往会在int/Integer、long/Long、double/Double之间做选择,以int/Integer为例,来看看它们之间的异同点。
首先它们都表示整形数字,数字的范围是相同的。int类型的默认值是0,Integer类型的默认值是null。如果0是有业务含义的,那么需要使用Integer,因为无法判断是外部系统主动设置的参数为0还是使用的默认值,多少有些模棱两可。