Java数据类型——基本类型/包装类型

Java数据类型(基本类型/引用类型)

基本类型

Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型;Java是一种面向对象语言,为了让基本类型具备对象特性(比如方法调用),Java为每种基本类型提供了一个包装类。

类型 默认值 占用储存空间 存储范围 包装类
byte 0 1*byte -128(-2^7)~ 127(2^7-1) Byte
short 0 2*byte -32768(-2^15)~ 32767(2^15 - 1) Short
int 0 4*byte -2,147,483,648(-2^31)~ 2,147,483,647(2^31 - 1) Integer
long 0L 8*byte -9,223,372,036,854,775,808(-2^63)~ 9,223,372,036,854,775,807(2^63 -1) Long
float 0.0f 4*byte 1.4E-45 ~ 3.4028235E38 Float
double 0.0d 8*byte 4.9E-324 ~ 1.7976931348623157E308 Double
boolean false 1*bit false、true Boolean
char false 2*byte \u0000 ~ \uffff Character

因为Java为每种基本类型提供了包装类型,实际工作中,更多的是使用包装类型;那对于包装类型的比较应该是大家都会遇到的一些坑;

  • 装箱与拆箱
package com.sjw.box;

public class DateType {
    public static void main(String[] args) {
        int i = 100;
        // 自动装箱(autoboxing) 等价于 Integer a=Integer.valueOf(100);
        Integer a = 100;
        Integer b = 100;
        // 包装类型与基本类型通过==比较,比较值大小
        // 拆箱(unboxing) 等价于 a.intValue()==i
        System.out.println(a == i);
        // 包装类型通过==比较,比较地址
        System.out.println(a == b);
        // 包装类型通过equals比较,比较值大小
        System.out.println(a.equals(b));
    }
}

为了增强说服力,下面我们通过JDK自带的javap反解析工具,分析上述Java代码编译生成的Class文件。

// javap -c -l DateType.class
Compiled from "DateType.java"
public class com.sjw.box.DateType {
  public com.sjw.box.DateType();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lcom/sjw/box/DateType;

  public static void main(java.lang.String[]);
    Code:
       // 将int 100 push压入操作数栈
       0: bipush        100
       // pop出栈(int 100),赋值给局部变量表索引为1的变量 int i
       2: istore_1
       // 将int 100 push压入操作数栈
       3: bipush        100
       // pop出栈(int 100),调用Integer.valueOf,并将方法返回值(引用)push压入操作数栈;装箱(autoboxing)
       5: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       // pop出栈(引用地址),赋值给局部变量表索引为2的变量 Integer a
       8: astore_2
       // 将int 100 push压入操作数栈
       9: bipush        100
      // pop出栈(int 100),调用Integer.valueOf,并将方法返回值(引用)push压入操作数栈;装箱(autoboxing)
      11: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      // pop出栈(引用地址),赋值给局部变量表索引为3的变量 Integer b
      14: astore_3
      // 将静态属性PrintStream System.out push压入操作数栈
      15: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      // 将局部变量表索引为2的引用类型变量a push压入操作数栈
      18: aload_2
      // pop出栈a(引用)调用其intValue方法,并将返回值push压入操作数栈;拆箱(unboxing)
      19: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
      // 将局部变量表索引为1的int类型变量i push压入操作数栈
      22: iload_1
      // pop操作数栈栈顶2个元素,进行int比较,不相等跳转到Code 30,相等继续
      23: if_icmpne     30
      // 将int常量1压入操作数栈
      26: iconst_1
      // 跳转Code 31
      27: goto          31
      // 将int常量0压入操作数栈
      30: iconst_0
      // pop操作数栈栈顶2个元素(out,1或0),以栈顶元素(1或0)作为参数调用out引用的方法println
      31: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V
      34: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      // 将局部变量表索引为2的引用类型变量a push压入操作数栈
      37: aload_2
      // 将局部变量表索引为3的引用类型变量b push压入操作数栈
      38: aload_3
      // pop操作数栈栈顶2个元素,进行引用对象比较(比较地址),不相等跳转到Code 46,相等继续
      39: if_acmpne     46
      42: iconst_1
      43: goto          47
      46: iconst_0
      47: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V
      50: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      53: aload_2
      54: aload_3
      55: invokevirtual #6                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
      58: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V
      // 将局部变量表索引为2的引用类型变量a push压入操作数栈
      61: aload_2
      // pop出栈a(引用)调用其intValue方法,并将返回值push压入操作数栈;拆箱(unboxing)
      62: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
      // pop出栈(int 100),赋值给局部变量表索引为1的变量 int i
      65: istore_1
      66: return
    // Java代码行与Code汇编指令行偏移量映射表
    LineNumberTable:
      // Java代码第5行代码对用Code汇编指令0-2
      line 5: 0
      // Java代码第7行代码对用Code汇编指令3-8
      line 7: 3
      line 8: 9
      line 11: 15
      line 13: 34
      line 15: 50
      line 17: 61
      line 18: 66
    // 局部变量表
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          // 函数参数args
          0      62     0  args   [Ljava/lang/String;
          // int变量 i
          3      59     1     i   I
          // Integer变量 a  
          9      53     2     a   Ljava/lang/Integer;
          // Integer变量 b  
         15      47     3     b   Ljava/lang/Integer;
}

注本文重点不是分析javap反解析class字节码文件的具体信息,具体如何分析、汇编指令等参考官方文档

经过上面简单分析,了解int包装类型Integer的装箱(Integer.valueOf)、拆箱(Integer.intValue)的过程;对于常见的面试题

    public void compare(Integer arg) {
        int a = 0;
        Integer b = 1;
        // 拆箱(unboxing),arg.intValue();如果arg为null,会出现NPE(NullPointerException)
        if (arg == a) ;
        // 不需要拆箱(unboxing),比较引用对象,即地址的比较
        if (arg == b) ;
        // 如果arg为null,会出现NPE(NullPointerException)
        if (arg.equals(b)) ;
        // 如果arg为null,不会出现NPE(NullPointerException),
        if (b.equals(arg)) ;
    }
    // Integer.java源码
    public boolean equals(Object obj) {
        // 如果obj为null,直接返回false
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
  • 包装对象的缓存
    正如前面介绍,包装对象的值大小比较需要使用equals,在一定范围内包装类型==比较是可以的,注意不是比较值,依然比较地址
    public void compare() {
        // 装箱(autoboxinxg)  Integer a = Integer.valueOf(100);
        Integer a = 100;
        Integer b = 100;
        Integer c = new Integer(100);
        Integer e = 1000;
        Integer f = 1000;
        // ture
        if (a == b) ;
        // false
        if (a == c) ;
        // ?
        if (e == f) ;
    }

上述代码 e == f 是ture还是false?不要急向下看,Java如何缓存对象的?如何分析,我们知道new出来的对象用==比较,只要没用引用同一个对象就为false;对于通过装箱来的包装对象可能用的是缓存对象,我们知道装箱的过程是通过valueOf()方法的;

    // valueOf源码
    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;
            // 可通过jvm参数-XX:AutoBoxCacheMax进行配置
            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);
                    // 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() {}
    }

通过int的包装类型Integer分析了一下装箱(autoboxing)、拆箱(unboxing),以及缓存IntegerCache是如何实现的,并且IntegerCache缓存范围默认是-128~127,max value可以通过jvm参数-XX:AutoBoxCacheMax进行配置;假设jvm参数加上-XX:AutoBoxCacheMax=2000;会缓存value值为-127~2000的封装类型,以供通过装箱生成的对象实例;到这里可以回答e==f是true还是false。

对于其他基本类型long、float、double对应的包装类型Long、Float、Double装箱、拆箱以及是否缓存,感兴趣的可以通过Java源码学习了解一下;本文代码样例采用JDK1.8。

知其然,更要知其所以然。。。

posted @ 2020-02-10 21:54  ClassicalRain  阅读(307)  评论(0编辑  收藏  举报