Java 是基于对象的,所以我们都需要以对象的想法来进行思维。
但 Java 又提供了 8 个基本类型,这 8 个基本类型基本上都和数字有关,是直接可以使用的类型。
基本类型 | 大小 | 包装器类型 |
boolean | / | Boolean |
char | 16bit | Character |
byte | 8bit | Byte |
short | 16bit | Short |
int | 32bit | Integer |
long | 64bit | Long |
float | 32bit | Float |
double | 64bit | Double |
void | / | Void |
String 不算基本类型,但是为了加快 String 的处理,Java 就顺手搞了一个 String Pool。
Java 基本类型
什么是基本类型?
直接可以计算的就是基本类型,基本类型不是对象。
所有的基本数据类型的大小(所占用的字节数)都已明确规定,在各种不同的平台上保持不变,这一特性有助于提高 Java 程序的可移植性。
基本类型就是已经定义好的,可以直接拿来计算和比较的那种。
包装类
既然我们有了基本类型,正如前面说的,Java 里面都是对象。
那么我们应该有必要创建一堆对象和前面说的 8 个基本类型进行对应。
这些对象就是针对基本类型创建的包装类( Wrapper Classes)。
为什么要有包装类
举个例子,我们指定 List 中需要存一堆 Integer 的对象。
如果我们定义的是基本类型,那我们怎么存到对象中呢?
我们是不是需要把基本类型转换为对象,然后再存进去,如果我们不提供包装类的话,就没有这个转换过程。
往大了说,基本数据类型没有办法使用 List 这个数据结构,这多无聊呀。
为了解决这个问题 Java 就搞了一个包装类,用来把基本数据类型转换为对象。
装箱和拆箱
既然东西有了,那么对这个转换的过程,自然就需要定义下概念了。
从基本数据类型类型转换为包装对象的过程就叫做装箱。
反过来就叫做拆箱了。
在老的 JDK ,这个过程都没有办法自动进行操作,每次要手动搞了一个类型转换。
其实这个过程每次都手动做的话,是非常繁琐的,所以在 JDK 进化后,现在全是自动化了,基本上都是自动类型转换了。
虽然有时候也会出点类型转换错误,但整体上提高了不少效率。
简单来说就是 JDK 在需要基本类型的时候,你定义却是对象,那么 JDK 自己帮你把这个拆箱过程给完成了,对你来说是透明的了。
缓存
既然有了装箱和拆箱的过程,如何提高这个效率,就是 JDK 需要考虑的东西了。
在这个过程中,缓存就被使用上了。
Integer valueOf(1);
就是用了缓存的典型例子。
JDK 的代码是这样的:
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在这里,JDK 定义了一个最小值和最大值以便于加快类型转换的处理速度。
所以,下面的代码就有点意思了。
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
上面代码的结果是第一个判断为 TRUE,第二个为 False。
因为我们知道,对象使用 == 进行比较,比较的是内存地址,所以对值的比较我们通常会用 equals 来进行比较。
但是因为上面代码中的 Integer 小于 100,所以是从缓存中读的,这样会导致比较的时候返回 TRUE。
总结
在面试的时候,上面缓存中的内容是容易被喜欢扣细节的人问到的。
如果你真没有去看看 JDK 的文档,上面还真有点出问题。
但我们认为这个有点钻牛角尖的意思了,因为 Java 使用缓存的目的就是为加快程序的执行的。至于具体怎么实现,对大部分人来说这部分都是透明的。
通常实际编程的过程中,只需要了解自动拆装,并且如何定义变量,因为针对对象的定义和基本类型的定义是不一样的。
定义一个浮点对象,我们使用的代码是 Float x = 1F;
,在后面需要添加一个 F。
至于缓存部分,有时间就研究下,了解自动拆装是有缓存在里面,多是针对小范围 int 来使用的就可以了。