在Java中,总共有8种基本类型,其中4种整型,2种浮点型,char字符类型和boolean类型
整型
类型 | 存储字节 | 取值范围 |
---|---|---|
int | 4字节 | -2 147 483 648 ~ 2 147 483 647,-2^31 ~ 2^31-1 |
short | 2字节 | -32 768 ~ 32 767,-2^15 ~ 2^15-1 |
long | 8字节 | -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807,-2^63 |
byte | 1字节 | -128 ~ 127,-2^7 ~ 2^7-1 |
- 在Java中,整型都有固定的取值范围和字节长度,且不受操作系统的影响,这就解决了软件跨平台的诸多问题
- 在Java中,整数有多种表现形式,分别是:
a. 十进制整数,如110,1111,233,0
b. 八进制整数,要求以0开头(注意这是数字0,不是字母o),如010对应八进制中的8,显然八进制表示法比较容易混淆,所以建议最好不要使用八进制常数。
c. 十六进制整数,要求以0x或0X开头(注意这是数字0,不是字母o),如0x12就是18,0xCAFE就是51966
d. 从Java 7开始,加上前缀0b或0B(注意这是数字0,不是字母o),如0b1001就是9 - 通常情况下,int类型最常用,但是需求很大的数值时,如表示星球上的居住人数时就绪使用long类型,long类型需要有一个后缀L或l,如4000000000L
- byte和short类型主要用于特殊场合,其中byte是8位的,有符号得,最大值就是2^7-1=127,二进制为:0111 1111,而最小值为-2^7=-128,二进制形式为:1000 0000,默认值为0,综合起来就是总长度为2^8=256即:
0~127:128个
-0~127:128个 - byte对应的包装类型为Byte,其使用Flyweight模式(享元模式)预先构造了一个能表示所有byte类型数据的对象缓存,当使用
valueOf(byte)
进行装箱时,会使用缓存的对象,但其构造方法Byte(byte)/Byte(String)
不会使用缓存对象,即存在:
Byte.valueOf((byte) 1) == Byte.valueOf("1"); // true
new Byte((byte) 1) == new Byte("1"); // false
Byte.decode("1") == Byte.decode("0x1"); // true,内部调用 valueOf(byte)
- 整数运算在JVM中只有两套,一套用于int类型数据,另一套用于long类型数据,所以,所有的byte/char/short/int类型数据在运算时,均使用操作int类型数据的指令。如果操作数中没有long类型,则需要将所有的操作数提升为int类型进行运算(JVM中体现为所有指令均为int类型指令,运算之前不会有提升的指令,直接采用int指令操作),运算结果也是int类型的,如果需要窄化,需要强制类型转换:
byte a = 1;
byte b = 1;
int c = a + b; // output:2
byte d = a + b; // error:Incompatible types.Required:byte Found:int
byte e = (byte)(a + b); // output:2
如果操作数中有long型,则需要将所有的操作数提升为long类型进行运算(JVM中体现为所有指令均为long类型指令,但运算之前需要使用i2l
指令将int类型数据提升为long型数据),运算结果为long型。
浮点类型
Java余元支持两种基本的浮点类型:float和double,以及他们对应的包装类型 Float 和 Double。它们都依据IEEE 754标准,该标准为32位浮点和64位双精度浮点二进制小数定义了二进制标准。
IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。IEEE 浮点数用 1 位表示数字的符号,用 8 位来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2)小数来表示,这意味着最高位对应着值 ?(2 -1),第二位对应着 ?(2 -2),依此类推。对于双精度浮点数,用 11 位表示指数,52 位表示尾数。如图:
类型 | 存储字节 | 取值范围 |
---|---|---|
float | 4字节 | 大约 ±3.402 823 47E+38F(有效位数为6 ~ 7位) |
double | 8字节 | 大约 ±1.797 693 134 862 315 70E+308(有效位为15位) |
- double表示这种类型的数值精度是float类型的两倍,所以double类型有双精度数值的说法。在通常情况下,应该采用double类型,因为多数情况下,float类型的精度很难满足需求,除非需要单精度数据的库,或者需要存储大量的数据
- float类型的数值有个后缀F或者f(例如,3.14F),没有后缀的浮点数值(如3.14)默认为double类型。
- 所有的浮点数值计算都遵循IEEE754规范。用于表示溢出和出错情况的三个特殊的浮点数值:
- 正无穷大,对应常量 Double.POSITIVE_INFINITY
- 负无穷大,对应常量 Double.NEGATIVE_INFINITY
- NaN(不是一个数字),对应常量 Double.NaN
- 浮点型的运算指令也有两套,当操作符两段的数据均为float型时,使用float对应的运算指令,若操作数两段的数据有double型,则需要插入
f2d
指令将float型数据提升为double型:
float a = 0.1f, b = 0.2f
double c = 1.2;
double d = a * b * c; // 产生的指令相当于 ((double)(a * b)) * c
double e = c * a * b; // 产生的指令相当于 c * ((double)a) * ((double)b)
- 对于整型和浮点型数据的混合运算,如果运算符两端均为整型,则使用整型指令运算,否则使用浮点数运算指令:
int a = 1, b = 2;
double c = 2.3;
double d = a * b * c; // 产生的指令相当于 ((double)(a * b)) * c
double e = c * a * b; // 产生的指令相当于 c * ((double)a) * ((double)b)
综上所述,整型和浮点数运算过程中数据类型都是以运算符两端最高操作数类型为标准,将次等级的操作数类型提升为最高类型后进行运算,具体的提升方向为:
- INTEGRAL - > long, INTEGRAL -> float, INTEGRAL -> double
- long -> float, long -> double
- float -> double
char类型
char类型原本用于表示单个字符。但是现在Unicode字符可以用一个char值描述,而另一些Unicode字符则需要两个char值,char类型的字面量要用单引号括起来,如:'A'是编码值为65所对应的字符常量。它与"A"不同,"A"是包含一个字符A的字符串。char类型的值可以表示为十六进制值,其范围为\u0000到\Uffff。
在Java中,char类型描述了UTF-16编码中的一个代码单元(所以要了解char需要先弄明白Unicode和UTF-16相关知识,请看这里),所以在 Java 中 char 数据类型是定长的,其长度永远只有 16 位,char 数据类型永远只能表示代码点在 U+0000 ~ U+FFFF 之间的字符,也就是在 BMP 内的字符。如果代码点超过了这个范围,即使用了增补字符,那么 char 数据类型将无法支持,因为增补字符需要 32 位的长度来存储,我们只能转而使用 String 来存储这个字符。
char c1 = '𝌆';
char c2 = '\u64321';
如上编写的代码,使用 char 数据类型来保存辅助平面的字符,编译器将会报错 Invalid character constant。
boolean类型
boolean类型有两个值:false和true,但是在Java虚拟机中没有任何供boolean值专用的字节码指令。根据《Java虚拟机规范》的描述:
虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位
所以在谈及boolean类型占用的存储空间,可能最合理的是4字节,在数组中又是1个字节
参考
- 《Java核心技术 卷I 基础知识》
- Java 7之基础类型第1篇 - Java数据类型
- 深入理解Java基本数据类型
- 您的小数点到哪里去了?
- Java 中 char 和 String 的细节和使用注意