java并不是纯面向对象的语言,java语言是一个面向对象的语言,但是java中的基本数据类型却不是面向对象的,但是我们在实际使用中经常将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象!
包装类和基本数据类型的关系
| 基础数据类型 | 包装类型 |
|---|---|
| byte | Byte |
| boolean | Boolean |
| short | Short |
| char | Character |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
下面是包装类型的继承结构图
包装类的基本操作

public class TestWrappedClass {public static void main(String[] args) {//基本数据类型转成包装类对象Integer a = new Integer(3);Integer b = Integer.valueOf(30);//把包装类对象转成基本数据类型int c = b.intValue();double d = a.doubleValue();//把字符串转成包装类对象Integer e = Integer.valueOf("31134");Integer f = new Integer("3344");Integer h = Integer.parseInt("34113");//把包装类型对象转成字符串String str = f.toString();//常见的常量System.out.println("int类型最大值 " +Integer.MAX_VALUE);System.out.println("int类型最小值 " +Integer.MIN_VALUE);}}
包装类与基本类型的转换
从源代码的角度来看,基础类型和包装类型都可以通过赋值语法赋值给对立的变量类型,如下面的代码所示。
Integer a = 1;
int a = new Integer(1);
这种语法是可以通过编译的。但是,Java作为一种强类型的语言,对象直接赋值给引用类型变量,而基础数据只能赋值给基本类型变量,这个是毫无异议的。
那么基本类型和包装类型为什么可以直接相互赋值呢?这其实是Java中的一种“语法糖”。“语法糖”是指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会(来自百度百科)。
换句话说,这其实是一种障眼法,那么实际上是怎么样的呢?下面是Integer a = 1;语句编译的字节码。
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
首先,生成一个常量1,然后调用Integer.valueOf(int)方法返回Integer对象,
最后将对象的地址(引用)赋值给变量a。Integer a = 1其实相当于Integer a = Integer.valueOf(1)
“莫名其妙”的NullPointException
经常可能碰到过不少因为请求参数或者接口定义字段设置为int(或者其他基本类型)而导致NullPointException。
代码大致地运行步骤如下所示,当然不会跟这个完全一样
Integer a = null;
...
int b = a; // 抛出NullPointException
上面的代码可以编译通过,但是会抛出空指针异常(NullPointException)。前面已经说过了,int b = a实际上是int b = a.intValue(),由于a的引用值为null,在空对象上调用方法就会抛出NullPointException。
自动装箱、拆箱
Java 5 增加了自动装箱、拆箱机制,提供基本数据类型和包装类型的相互转换操作。
自动装箱
自动装箱即自动将基本数据类型转换成包装类型,在 Java 5 之前,要将基本数据类型转换成包装类型只能这样做,看下面的代码。
Integer i1 = new Integer(8);
Integer i2 = Integer.valueOf(8);
// 自动装箱
Integer i3 = 8;
以上 3 种都可以进行转换,但在 Java 5 之前第 3 种方法是编译失败的,第 3 种方法也正是现在的自动装箱功能。
另外,第一种构造器方法也不推荐使用了,已经标为废弃了。
其实自动装箱的原理就是调用包装类的 valueOf 方法,如第 2 个方法中的 Integer.valueOf 方法。
自动拆箱**
自动拆箱即自动将包装类型转换成基本数据类型,与自动装箱相反,有装就有拆,很好理解。
// 自动拆箱
int i4 = i3;
int i5 = i3.intValue();
继续上面的例子,把 i3 赋值给 i4 就是实现的自动拆箱功能,自动装箱的原理就是调用包装类的 xxValue 方法,
如 i5 中的 Integer 的 intValue 方法。
自动装箱、拆箱不只是体现在以上的例子,在方法接收参数、对象设置参数时都能自动装箱拆箱。
IntegerCache
在Java中,“==”符号判断的内存地址所对应的值得相等性,具体来说,基本类型判断值是否相等,引用类型判断其指向的地址是否相等。看看下面的代码,两种类似的代码逻辑,但是得到截然不用的结果。
Integer a1 = 1;
Integer a2 = 1;
System.out.println(a1 == a2); // true
Integer b1 = 222;
Integer b2 = 222;
System.out.println(b1 == b2); // false
这个必须从源代码中才能找到答案。Integer类中的valueOf()方法的源代码如下:
public static Integer valueOf(int i) {
// 判断实参是否在可缓存范围内,默认为[-128, 127]
if (i >= IntegerCache.low && i <= IntegerCache.high)
// 如果在,则取出初始化的Integer对象
return IntegerCache.cache[i + (-IntegerCache.low)];
// 如果不在,则创建一个新的Integer对象
return new Integer(i);
}
由于1属于[-128, 127]集合范围内,所以valueOf()每次都会取出同一个Integer对象,故第一个“”判断结果为true;而222不属于[-128, 127]集合范围内,所以valueOf()每次都会创建一个新的Integer对象,由于两个新创建的对象的地址不一样,故第一个“”判断结果为false。
XCache
| 类 | 是否有Cache | 最小值 | 最大值 |
|---|---|---|---|
| Boolean | 无 | — | — |
| Byte | ByteCache | -128 | 127(固定) |
| Short | ShortCache | -128 | 127(固定) |
| Character | CharacterCache | 0 | 127(固定) |
| Integer | IntegerCache | -128 | java.lang.Integer.IntegerCache.high |
| Long | LongCache | -128 | 127(固定) |
| Float | 无 | — | — |
| Double | 无 | — | — |
java.lang.Integer.IntegerCache.high

IntegerCache类获取high的方法sun.misc.VM.getSavedProperty
这个值如何如何传递到JVM中?
和系统属性一样在JVM启动时,通过设置-Djava.lang.Integer.IntegerCache.high=xxx传递进来。这个方法和
System.getProperty有什么区别?
为了将JVM系统所需要的参数和用户使用的参数区别开,java.lang.System.initializeSystemClass在启动时,会将启动参数保存在两个地方:
2.1 sun.misc.VM.savedProps中保存全部JVM接收的系统参数。
JVM会在启动时,调用java.lang.System.initializeSystemClass方法,初始化该属性。
同时也会调用sun.misc.VM.saveAndRemoveProperties方法,从java.lang.System.props中删除以下属性:
- sun.nio.MaxDirectMemorySize
- sun.nio.PageAlignDirectMemory
- sun.lang.ClassLoader.allowArraySyntax
- java.lang.Integer.IntegerCache.high
- sun.zip.disableMemoryMapping
- sun.java.launcher.diag
以上罗列的属性都是JVM启动需要设置的系统参数,所以为了安全考虑和隔离角度考虑,将其从用户可访问的System.props分开。
2.2 java.lang.System.props中保存除了以下JVM启动需要的参数外的其他参数。
- sun.nio.MaxDirectMemorySize
- sun.nio.PageAlignDirectMemory
- sun.lang.ClassLoader.allowArraySyntax
- java.lang.Integer.IntegerCache.high
- sun.zip.disableMemoryMapping
- sun.java.launcher.diag
Java 9的IntegerCache
幻想一下,如果以上淘气的玩法出现在第三方的依赖包中,绝对有一批程序员会疯掉(请不要尝试这么恶劣的玩法,后果很严重)。
庆幸的是Java 9对此进行了限制。可以在相应的module中编写module-info.java文件,限制了使用反射来访问成员等,按照需要声明后,代码只能访问字段、方法和其他用反射能访问的信息,只有当类在相同的模块中,或者模块打开了包用于反射方式访问。详细内容可参考一下文章:
在 Java 9 里对 IntegerCache 进行修改?
