Java 基本数据类型
8种基本数据类型
Java 共提供了八种基本数据类型:
- byte : 8 bit 有符号整数。最小值 -128(-2^7) ,最大值 127(2^7-1),默认值0。
- short:16 bit 有符号整数。最小值 -32768(-2^15),最大值 32767(2^15-1),默认值 0。
- int:32 bit 有符号整数。最小值 -2,147,483,648(-2^31),最大值 2,147,483,647 (2^31-1),默认值 0 L 。
- long:64 bit 有符号整数。 最小值 -2^63,最大值 2^63-1,默认值 0。
- float:32 bit 单精度浮点数。 最小值:1.4E-45,最大值:3.4028235E38,默认值 0.0F。
- double:64 bit 双精度浮点数。最小值:4.9E-324,最大值:1.7976931348623157E308,默认值 0.0D。
- boolean:1 bit,只有两个值 false 和 true。 默认值 false。
- char:一个单一的 16 bit Unicode 字符。
byte 的取值范围
在计算机中,bit 是存储数据的最小单元。一个 bit 也叫做一个二进制位。
byte 在Java中占有 8 bit 。且 byte 是有符号的整数位。取值范围为 -128 ~ 127 。那么这个取值范围是怎么来的呢?
计算机存储数值不是按照表面看到的数值来存储的。而是存在着 原码,反码和补码。在Java中使用补码的形式存储二进制数。
首先我们了解几个概念:机器数、真值、原码、反码、补码。
- 机器数:在计算机中的二进制表示形式。机器数是带符号的。最高位存放符号,正数为0,负数为1。
- 真值:因为机器数的最高位为符号位,那么我们看到的机器数跟在计算机中看到的数值是不一样的。例如:
有符号数 1000 0011 ,其最高位为1,表示其是一个负数。而不是我们转换成十进制看到的 131 。所以为了区别起见,将带符号的机器数对应的真正数值称为机器数的真值。
例如: 0000 0001的真值 = +000 0001 = +1
1000 0001的真值 = - 000 0001 = -1 - 原码:原码就是在符号位加上真值的绝对值,即用第一个为表示符号,其余位表示值。
例如: +1 的原码 = 0000 0001
- 1 的原码 = 1000 0001 - 反码:正数的反码是其本身,负数的反码是在原码的基础上符号位不变,其余各个位逐位取反。
例如:+1的原码 = 0000 0001 ,那么它的反码也是 0000 0001
-1的原码 = 1000 0001 ,那么它的反码是 1111 1110 - 补码:正数的补码是其本身,负数的补码是在反码的基础上 + 1。
例如:+1的原码 = 0000 0001 ,那么它的反码也是 0000 0001,补码是 0000 0001
-1的原码 = 1000 0001 ,那么它的反码是 1111 1110,补码是1111 1111
接下来我们看 byte 取值范围,在计算机中
- byte 占有 8 bit ,最高位符号位;
- 最大值应该是 0111 1111 ,其中最高位 0 代表 正数;转换为十进制为 127 ;
- 最小值为 1000 0000,其中最高位1 代表 负数。
- 由于负数是以补码的形式存在;将 最小值 1000 0000 先 -1转换成反码为 0111 1111 ,
- 然后在取反转换成原码为 1000 0000
- 将原码装换成十进制为 128 ,该数为负数,则最后为 -128。
- 即 byte的取值范围为 127 ~ -128 。
其实像short , int和long这样的整型计算取值范围也可以这样计算。
浮点值的二进制表示
在Java中浮点数的存储遵循了 IEEE754 标准。
单精度浮点数占用4个byte,32bit;双精度占用 8个byte,64bit。
根据 IEEE754 标准,任意一个二进制浮点数V可以表示成下面的形式:
![](https://cdn.nlark.com/yuque/__latex/a424988e5aa40f2b9674415ccae9f1e4.svg#card=math&code=V%20%3D%20%28-1%29%5Es%20%5Ctimes%20M%20%5Ctimes%202%5EE&height=29&width=193)
- 表示符号位,当s = 0,V为正数;当s = 1,V 为负数。
- M 表示有效数字,大于等于1,小于2。
- 表示指数位。其中叫做指数,或者 阶码。
- M 的取值范围一般为 (二进制浮点数一般的都能写成 1.xxxx 的形式)
举例来说:
十进制的5.0 ,写成二进制是 101.0;相当于 那么按上面的V的格式,可以得出 s = 0,M = 1.01,E = 2 ;
十进制的 - 5.0 ,写成二进制是 - 101.0;相当于 那么按上面的V的格式,可以得出 s = 1,M = 1.01,E = 2 ;
根据 IEEE754 标准,浮点数存储分为三个部分: 符号(sign),指数(exponet),有效数字(fraction),分别对应上面的
,E , M。图示如下:
根据 IEEE754 标准,对于32位的浮点数,最高位是符号位 s,接着8位是指数E,剩下的 23 位为有效数字M。
如上图,从右向左,给每个bit 编号,从 0-31。
第31位为符号位,第30-23位为指数位,第22-0位为有效数位。
单精度在内存的存储:
- 第1位表示正负
- 中间8位表示指数,采用补码的形式存储。
- 后23位存储有效数位。
对于64位的浮点数,最高的1位是符号位 S ,接着的11位是指数E,剩下的52位为有效数字M。
如上图,从右向左,给每个bit 编号,从 0-63。第63位为符号位,第62-52位为指数位,第51-0位为有效数位。
双精度在内存的存储:
- 第1位表示正负
- 中间11位表示指数,采用补码的形式存储。
- 后52位存储有效数位。
根据 IEEE754 标准规定,对于有效数字M和指数E,还有一些特殊规定。
由于 ,M可以写成 1.xxxx 的形式,其中 xxxx 表示小数部分,IEEE754 标准规定,在计算机内保存M时,默认这个数的第一个为1,因此可以被舍去,只保存后面的小数部分。比如保存1.01的时候,只保存 01,等到读取时,再把1加上去。这样做的目的,是节省1位有效数字。32位的浮点数,留给M只有23位,将第一位舍去后,等于可以保存 23 + 1= 24 位有效数字。那么64位的浮点数,留给M的只有52位,将第一位舍去后,等于可以保存 52 + 1= 53 位有效数字。
指数偏移值
关于指数 E ,它占有 8个bit,它是无符号的,可以表示 个数即 0-255。但是科学计数法中的E是可以出现负数的。所以这里就引出了一个指数偏移值的概念。
指数偏移值(exponent bias),即浮点数表示法中指数域的编码值(即二进制的值),等于指数的实际值加上某个固定的值,IEEE 754 标准规定该固定值为,其中的 存储指数的 bit 的长度。
对于8位的E,这个指数偏移值是 ;对于11位的E,这个指数偏移值是 。
采用指数的实际值加上固定的偏移值的办法表示浮点数的指数,好处是可以用长度为个bit的无符号整数来表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易,实际上可以按照字典序比较两个浮点表示的大小。
以 32位浮点数来说, 0 ~ 126 代表 -127 ~ -1; 127 代表 0 ; 128 ~ 255 代表 1 ~ 128 。 E的取值范围为 -126~127。
指数E还可以分为三种情况,看这个公式:
![](https://cdn.nlark.com/yuque/__latex/a424988e5aa40f2b9674415ccae9f1e4.svg#card=math&code=V%20%3D%20%28-1%29%5Es%20%5Ctimes%20M%20%5Ctimes%202%5EE&height=29&width=193)
- E不全为0或不全为1。这时,浮点数的指数E- 127得到真实值。再将有效数字M前面加上第一位的1。
- E全为0,这时,浮点数的指数E等于1-127,有效数字M不在机上第一位的1,而是还原为 0.xxxx 的小数。这样做是为了表示 ,以及接近于0的很小的数字
- E全为1。这时,如果有效数字M全为0,表示 无穷大(取决于符号位);如果有效数字M不全为0,表示这个不是一个数(NaN)
比如, 的 E 是 10 ,要考虑偏移值 127 ,在计算机存储时会进行偏移计算 ,即 1000 1001。
这样单精度浮点数的指数部分实际取值是从-126到127。
char 类型
在设计 char
类型时,是基于Unicode原始规范。占有16个bit,2个字节。取值范围为 U+0000 到 U+FFFF
。char 可以表示一个单独的字符,这个字符可以是一个字母,一个汉字,一个数字,一个标点符号,一个简单的空格,甚至一个表情符号,以及其它类似的内容。
什么是Unicode? 计算机内部都是 0 和 1,那么怎么显示字符呢?人们就设计一个字符编码表,给每个字符编一个编码,需要显示的时候就去编码表里去找对应的字符。Unicode就是其中一中字符编码的方式。 代码点 (code point): 简称码点,指在Unicode编码表中一个字符所对应的编号,该编码为唯一的。使用16进制表示 代码单元( code unit): 简称码元,编码的基本组成单位。对于UTF-8来说,码元是8bit;对于UTF-16来说,码元是16bit。换一种说法就是UTF-8的是以一个字节为最小单位的,UTF-16是以两个字节为最小单位的。
Java中 char
是固定的 16 bit 的长度,只要代码点在 U+0000 到 U+FFFF
之间,都可以使用一个char完整的表示出一个字符。但是随着时代的发展,Unicode的长度早已今非昔比,Unicode的范围早已超出了 16 bit,现在的范围达到了 U+0000 到 U+10FFFF
,在Unicode中超出 U+0000 到 U+FFFF
范围的字符叫做补充码点。那么如果需要表示的字符超出了char的范围怎么办呢?
举一个例子:码点 0x1F470 ,超出了 U+0000 到 U+FFFF
的范围,它在码表上是一个表情字符 👰。在 char 的包装类 Character 类,提供了一个方法 Character.charCount(codePoint)
,来判断当前的码点代表几个 char
int codePoint = 0x1F470;
System.out.println(Character.charCount(codePoint));
结果是 2 。
这个码点代表了两个 char 。两个char就可以组成一个char数组。在 Java 中字符串 String
就是使用char 数组表示,那么我们就可以很容易的猜到,在 Java 使用了 String 来表示这些补充码点。
现在我们知道了这个码点不能正常的使用char来表示,那么怎么将其转换成字符串呢?Character
提供了一个方法 Character.toChars(codePoint)
,来将这个补充码点来转换成char数组,继而我们就可以得到一个字符串了,通过下面的代码,我们就可以在控制台看到一个 👰 表情了。
int codePoint = 0x1F470;
System.out.println(Character.toChars(codePoint));
参考主要来自于阮一峰大神的博客和维基百科:
http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
https://zh.wikipedia.org/wiki/IEEE_754
https://zh.wikipedia.org/wiki/單精度浮點數