今天分享一份来自金蝶面试的Java基础面试真题:
- 你了解Java中的几种基本数据类型吗?
 - 对于包装类型的常量池技术,你有什么了解?
 - 你知道自动装箱与拆箱的原理吗?
 
了解Java中的基本数据类型
Java语言拥有8种基本数据类型,具体如下:
- 数字类型:
- 4种整数型:
byte、short、int、long - 2种浮点型:
float、double 
 - 4种整数型:
 - 1种字符类型:
char - 1种布尔类型:
boolean 
这8种基本数据类型的默认值和所占空间如下:
| 基本类型 | 位数 | 字节 | 默认值 | 取值范围 | 
|---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 | 
short | 16 | 2 | 0 | -32768 ~ 32767 | 
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 | 
long | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 | 
char | 16 | 2 | 'u0000' | 0 ~ 65535 | 
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 | 
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 | 
boolean | 1 | false | true、false | 
关于boolean类型,官方文档并未明确规定其占用位数,通常理解为1位,但实际上会根据JVM的实现来决定。
重要的是,Java的每种基本数据类型的存储空间大小是固定的,不会因计算机硬件架构不同而变化,这使得用Java写的程序比用其他多种语言编写的程序更具可移植性(详见《Java编程思想》2.2节)。
注意事项:
- 使用
long类型数据时,数值后必须加上L,否则会被视为整型。 char a = 'h'使用单引号,String a = "hello"使用双引号。
这八种基本类型都有对应的包装类:Byte、Short、Integer、Long、Float、Double、Character和Boolean。
包装类型在未赋值时是Null,而基本类型有默认值且非Null。
从JVM的层面来看,基本数据类型直接存储在虚拟机栈中的局部变量表,而包装类型则是对象类型,实例存储在堆中。相较于对象类型,基本数据类型占用的空间较小。
你了解包装类型的常量池技术吗?
Java的基本类型包装类大多实现了常量池技术。
其中,Byte、Short、Integer和Long这四种包装类会默认缓存-128到127范围内的数值,Character类则缓存0到127之间的字符,而Boolean类仅返回True或False。
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 {  
        int h = 127;  
    }  
}
Character缓存源码示例:
public static Character valueOf(char c) {  
    if (c <= 127) {  
      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);  
    }  
}
Boolean缓存源码示例:
public static Boolean valueOf(boolean b) {  
    return (b ? TRUE : FALSE);  
}
超出上述缓存范围的数值仍会创建新的对象,缓存范围的设计是为了在性能和资源之间取得平衡。两个浮点类型的包装类Float和Double则没有实现常量池技术。
Integer i1 = 33;  
Integer i2 = 33;  
System.out.println(i1 == i2); // 输出 true  
Float i11 = 333f;  
Float i22 = 333f;  
System.out.println(i11 == i22); // 输出 false  
Double i3 = 1.2;  
Double i4 = 1.2;  
System.out.println(i3 == i4); // 输出 false  
接下来,考虑一下下面代码的输出结果是true还是false?
Integer i1 = 40;  
Integer i2 = new Integer(40);  
System.out.println(i1 == i2);  
在这段代码中,Integer i1=40 实际上会进行装箱,也就是说等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接访问的是常量池中的对象。而Integer i2 = new Integer(40)则是创建了新的对象。
因此,结果是false。你答对了吗?
请记住:所有整型包装类对象之间的值比较,应该使用equals方法。

自动装箱与拆箱的原理是什么?
什么是自动装箱和拆箱?
- 装箱:将基本数据类型用其对应的引用类型包装起来;
 - 拆箱:将包装类型转换为基本数据类型;
 
例如:
Integer i = 10;  // 装箱  
int n = i;      // 拆箱  
上述代码对应的字节码如下:
L1  
    LINENUMBER 8 L1  
    ALOAD 0  
    BIPUSH 10  
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;  
    PUTFIELD AutoBoxTest.i : Ljava/lang/Integer;  
L2  
    LINENUMBER 9 L2  
    ALOAD 0  
    ALOAD 0  
    GETFIELD AutoBoxTest.i : Ljava/lang/Integer;  
    INVOKEVIRTUAL java/lang/Integer.intValue ()I  
    PUTFIELD AutoBoxTest.n : I  
    RETURN  
通过字节码可以看出,装箱实际上是调用了包装类的valueOf()方法,而拆箱则是调用了xxxValue()方法。
因此,
Integer i = 10实质上等价于Integer i = Integer.valueOf(10)int n = i实质上等价于int n = i.intValue()
需要注意的是:频繁的拆装箱操作会严重影响系统性能,应尽量避免不必要的拆装箱。
private static long sum() {  
    // 应该使用 long 而不是 Long  
    Long sum = 0L;  
    for (long i = 0; i <= Integer.MAX_VALUE; i++)  
        sum += i;  
    return sum;  
}