基本数据类型与包装数据类型

299 阅读4分钟

基础知识:位、字节

  • 位(bit):计算机中最小的数据单位。每一位的状态只能是0或1
  • 字节(byte):8个二进制位构成1个"字节(Byte)",存储空间的基本计量单位。如:英文字母占一个字节,汉字占两个字节

如 byte占一个字节,即占用存储空间为 2^8 换算到正负范围 -2^7 ~ 2^7-1

1. 基本数据类型分类

Java中基本数据类型的分类:四类8种

  • 整型:byte、short、int、long
  • 浮点型:float、double
  • 字符型:char
  • 布尔型:boolean v2-a4de2da2942089375382858919e3ae63_720w.png

2. boolean占几个字节

《Java虚拟机规范》中描述:虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节

3. 类型转换

image-20210713002853210.png

  • 实心箭头,表示无信息丢失的转换;
  • 虚箭头, 表示可能有精度损失的转换;
  • 如果要反过来需要使用强制类型转换关键字 cast ;

不同类型间运算导致的自动装换:

  • 如果两个操作数中有一个是 double 类型, 另一个操作数就会转换为 double 类型。
  • 否则,如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型。
  • 否则, 如果其中一个操作数是 long 类型, 另一个操作数将会转换为 long 类型。
  • 否则, 两个操作数都将被转换为 int 类型。
  • 在JAVA中,对char类型字符运行时,直接当做ASCII表对应的整数来对待。

4. 包装类

Java会为每一个基础数据类型都提供一个相应包装类的目的,在于将Java的所有东西都抽象成对象,可以更方便的控制和使用。

区别:

  • 包装类型可以为 null,而基本类型不可以

    《阿里巴巴 Java 开发手册》上规定POJO属性必须使用包装类

    数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把
    Integer 对象转换成 int 值),就会抛出 `NullPointerException` 的异常。
    
  • 包装类型可用于泛型,而基本类型不可以

    泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个特例

  • 基本类型比包装类型更高效

    基本数据类型存放在栈空间,而包装类存放在堆空间

  • 两个包装类型的值可以相同,但却不相等

   Integer a = new Integer(10);
   Integer b = new Integer(10);
   System.out.println(a == b); // false == 判断的是地址,因为对象不同所以不相等
   System.out.println(a.equals(b)); // true

自动装箱和自动拆箱:

装箱就是自动将基本数据类型转换为包装器类型;如:使用Integer.valueOf方法;

拆箱就是自动将包装器类型转换为基本数据类型;如:使用Integer.intValue方法。

当基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱.

  • 进行 = 赋值操作(装箱或拆箱)
  • 进行+,-,*,/混合运算 (拆箱)
  • 进行>,<,==比较运算(拆箱)
  • 调用equals进行比较(装箱)
  • ArrayList,HashMap等集合类 添加基础类型数据时(装箱)

相关面试题:

  1. 拆装箱导致的对象比较问题

    public void testAutoBox2() {
    	 //1
         int a = 100;
         Integer b = 100;
         System.out.println(a == b); // true  比较时进行拆箱,直接比较值
         
         //2
         Integer c = 100;
         Integer d = 100;
         System.out.println(c == d); // true  比较时是比较的对象地址,但是却为true
         
         //3   
         c = 200;
         d = 200;
         System.out.println(c == d); // false  比较时是比较的对象地址,false
    }
    

    针对2中的现象,我们可以查看JDK中Integer的源码

    // 此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。
    // 当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象。
    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;
        }
    }
    
  2. 拆装箱导致的性能问题

    long t1 = System.currentTimeMillis();
    Long sum = 0L;
    for (int i = 0; i < Integer.MAX_VALUE;i++) {
        sum += i;
    }
    long t2 = System.currentTimeMillis();        
    System.out.println(t2-t1);
    

    由于sum被声明成了包装类Long,所以在sum += i 计算的时候进行了大量的拆装箱操作,导致这段代码运行比sum声明成long时时间花费更大