题目如下:
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 150;
Integer i4 = 150;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
你想好答案了吗?
答案是:
true
false
为什么不都是true呢?
这就需要我们看看Integer i1 = 100;执行这条语句的时候发生了什么?
当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,查看jdk源码,valueOf方法内容如下:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
从代码和注释上可以看到,这个方法是从jdk1.5之后开始添加的。
如果整型字面量的值在-128到127之间,直接引用常量池中的Integer对象,否则new新的Integer对象。所以上面的面试题中i1 == i2的结果是true,而i3 == i4的结果是false。
从这道面试题中我们可以看到基本数据int 转换成了 Integer类型,Java中为什么存在这两种类型呢?
我们知道在Java中,new一个对象会存储在堆里,通过栈中的引用来使用这些对象;但是对于经常用到的一系列类型如int,如果我们用new将其存储在堆里就不是很高效,特别是简单的小的变量。所以就出现了基本类型,对于这些类型不是用new关键字来创建,而是直接将变量的值存储在栈中,因此更加高效。
既然基本类型会将值直接存储到栈中实现高效率,为什么还要有包装类型呢?
我们知道Java是一个面相对象的编程语言,基本类型并不具有对象的性质,为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
另外,当需要往ArrayList,HashMap中放东西时,像int,double这种基本类型是放不进去的,因为容器都是装Object,这时就需要这些基本类型的包装器类了。
到此我们知道了Java中为什么会同时存在基本类型和包装类型了,那两者有什么区别呢?
- 声明方式不同:
基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间; - 存储方式及位置不同:
基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用; - 初始值不同:
基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null; - 使用方式不同:
基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
说到这里,我们说下Java的基本数据类型和其对应的包装类型有哪些?
序号 | 数据类型 | 大小/位 | 封装类 | 默认值 | 可表示的数据范围 |
---|---|---|---|---|---|
1 | byte(位) | 8 | Byte | 0 | -128 ~ 127 |
2 | short(短整数) | 16 | Short | 0 | -32768 ~ 32767 |
3 | int(整数) | 32 | Integer | 0 | -2的31次方到2的31次方-1 |
4 | long(长整数) | 64 | Long | 0 | -2的63次方到2的63次方-1 |
5 | float(单精度) | 32 | Float | 0.0 | 大约±3.402 823 47E+38F(6~7位有效数字) |
6 | double(双精度) | 64 | Double | 0.0 | 大约±1.797 693 134 862 315 70E+308(15位有效数字) |
7 | char(字符) | 16 | Character | ‘\u0000’ | ‘\u0000’ ~ ‘\uffff’ |
8 | boolean(布尔) | ? | Boolean | false | true 或 false |
可参看:Java官网描述
boolean类型的大小我写了个问号,大小不确定。我们看下Java官网中关于boolean类型的描述:
boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its “size” isn’t something that’s precisely defined.
布尔类型:布尔数据类型只有两个可能的值:真和假。使用此数据类型为跟踪真/假条件的简单标记。这种数据类型就表示这一点信息,但是它的“大小”并不是精确定义的。也就是说,Java规范中,没有明确指出boolean的大小。
那么boolean类型到底占几个字节呢?
有以下三个说法:
- 1个bit
boolean类型的值只有true和false两种逻辑值,在编译后会使用1和0来表示,这两个数在内存中按位算,仅需1位(bit)即可存储,位是计算机最小的存储单位。 - 1个字节
虽然编译后1和0只需占用1位空间,但计算机处理数据的最小单位是1个字节,1个字节等于8位,实际存储的空间是:用1个字节的最低位存储,其他7位用0填补,如果值是true的话则存储的二进制为:0000 0001,如果是false的话则存储的二进制为:0000 0000。 - 4个字节
在《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。也就是说JVM规范指出boolean当做int处理,也就是4字节,boolean数组当做byte数组处理,这样我们可以得出boolean类型占了单独使用是4个字节,在数组中是确定的1个字节。
如果认同第三条,那么存在一个问题:虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗?
经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),32 位 CPU 使用 4 个字节是最为节省的,哪怕你是1个bit它也是占用 4 个字节。因为 CPU 寻址系统只能 32 位 32 位地寻址,具有高效存取的特点。
It depends on Java virtual machine.这取决于java虚拟机 。
总结:Java规范中,没有明确指出boolean的大小。在《Java虚拟机规范》给出了4个字节,和boolean数组1个字节的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。
欢迎朋友们关注转发点赞,谢谢~~
浏览更多文章可关注微信公众号:diggkr