3.4 基本数据类型 3.4.1 整型 3.4.2 字符型 3.4.3 浮点型 3.4.4 数值中使用下画线分隔 3.4.5 布尔型

3.4 基本数据类型

Java的基本数据类型分为两大类:boolean类型和数值类型。而数值类型又可以分为整数类型和浮点类型,整数类型里的字符类型也可被单独对待。因此常把Java里的基本数据类型分为4类,如图3.8所示。
这里有一张图片
Java只包含这8种基本数据类型,值得指出的是,字符串不是基本数据类型,字符串是一个类,也就是一个引用数据类型。

3.4.1 整型

通常所说的整型,实际指的是如下4种类型

类型 描述
byte 一个byte类型整数在内存里占8位,表数范围是:-128(-2^7)~127(2^7-1)。
short 一个short类型整数在内存里占16位,表数范围是:-32768(-2^15)~32767(2^15-1)
int 一个int类型整数在内存里占32位,表数范围是:-2147483648(-2^31)~2147483647(2^31-1)
long 一个long类型整数在内存里占64位,表数范围是:(-2^63)~(2^63-1)。

整数直接量默认为int类型

int是最常用的整数类型,因此在通常情况下,直接给出一个整数值默认就是int类型。除此之外有如下两种情形必须指出。

  1. 如果直接将一个较小的整数值(在byteshort类型的表数范围内)赋给一个byteshrt变量,系统会自动把这个整数值当成byte或者short类型来处理。
  2. 如果使用一个巨大的整数值(超出了int类型的表数范围)时,Java不会自动把这个整数值当成long类型来处理。如果希望系统把一个整数值当成long类型来处理,应在这个整数值后增加l或者L作为后缀。通常推荐使用大写的L,因为英文小写的l很容易跟数字1搞混。

程序示例

下面的代码片段验证了上面的结论:

1
2
3
4
5
6
7
8
9
// 下面代码是正确的,系统会自动把56当成byte类型处理
byte a = 56;
/*
下面代码是错的,系统不会把9999999999999当成long类型处理,
所以超出int的表数范围,从而引起错误
*/
// long bigValue = 9999999999999;
// 下面代码是正确的,在巨大的整数值后使用L后缀,强制使用long类型
long bigValue2 = 9223372036854775807L;

可以把一个较小的整数值(在int类型的表数范围以内)直接赋给一个long类型的变量,这并不是因为Java会把这个较小的整数值当成long类型来处理,Java依然把这个整数值当成int类型来处理,只是因为int类型的值会自动类型转换到long类型

整数的二进制 八进制 十进制 十六进制表示

Java中整数值有4种表示方式:十进制、二进制、八进制和十六进制,其中:

  • 二进制的整数以0b0B开头;
  • 八进制的整数以0开头;
  • 十六进制的整数以0x或者0X开头,其中10-15分别以a-f(此处的a-f不区分大小写)来表示。

程序示例 定义八进制整数 定义十六进制整数

下面的代码片段分别使用八进制和十六进制的数。

1
2
3
4
5
6
7
8
9
// 以0开头的整数值是8进制的整数
int octalValue = 013;
System.out.println(octalValue);

// 以0x或0X开头的整数值是16进制的整数
int hexValue1 = 0x13;
int hexValue2 = 0XaF;
System.out.println(hexValue1);
System.out.println(hexValue2);

在某些时候,程序需要直接使用二进制整数,二进制整数更“真实”,更能表达整数在内存中的存在形式。不仅如此,有些程序(尤其在开发一些游戏时)使用二进制整数会更便捷

程序示例 定义二进制整数

Java7开始新增了对二进制整数的支持,二进制的整数以0b或者0B开头。程序片段如下:

1
2
3
4
5
6
7
8
// 定义二个8位的二进制数
int binVal1 = 0b11010100;
byte binVal2 = 0B01101001;
// 定义一个32位的二进制数,最高位是符号位。
int binVal3 = 0B10000000000000000000000000000011;
System.out.println(binVal1); // 输出212
System.out.println(binVal2); // 输出105
System.out.println(binVal3); // 输出-2147483645

当定义32位的二进制整数时,最高位其实是符号位,当符号位是1时,表明它是一个负数,负数在计算机里是以补码的形式存在的,因此还需要换算成原码

原码

所有数字在计算机底层都是以二进制形式存在的,原码是直接将一个数值换算成二进制数。

反码

反码是对原码按位取反,只是最高位(符号位)保持不变

补码的计算规则

计算机以补码的形式保存所有的整欻。

  • 正数的补码和原码完全相同,
  • 负数的补码是其反码加1;

二进制整数转成十进制整数

  • 补码减一得到原码
  • 原码符号位不变,其他位按位取反得到原码
  • 根据原码使用展开式计算

将上面的二进制整数binVal3转换成十进制数的过程如图3.9所示。
这里有一张图片

二进制整数默认是int类型

正如前面所指出的,整数值默认就是int类型,因此使用二进制形式定义整数时,二进制整数默认占32位,其中第32位是符号位;
如果在二进制整数后添加l或L后缀,那么这个二进制整数默认占64位,其中第64位是符号位
例如如下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
定义一个8位的二进制,该数值默认占32位,因此它是一个正数。
只是强制类型转换成byte时产生了溢出,最终导致binVal4变成了-23
*/
byte binVal4 = (byte)0b11101001;
/*
定义一个32位的二进制数,最高位是1。
但由于数值后添加了L后缀,因此该整数的实际占64位,第32位的1不是符号位。
因此binVal5的值等于2的31次方 + 2 + 1
*/
long binVal5 = 0B10000000000000000000000000000011L;
System.out.println(binVal4); // 输出-23
System.out.println(binVal5); // 输出2147483651

只是在定义binVal5这个二进制整数时添加了L后缀,这就表明把它当成long类型处理,因此该整数实际占64位。此时的第32位不再是符号位,因此它依然是一个正数
至于程序中定义binVal4的代码,其中0b11101001依然是一个32位的正整数,只是程序进行强制类型转换时发生了溢出,导致它变成了负数。

3.4.2 字符型

字符型通常用于表示单个的字符,字符型值必须使用单引号('')括起来。Java语言使用16位的Unicode字符集作为编码方式,而Unicode被设计成支持世界上所有书面语言的字符,包括中文字符,因此Java程序支持各种语言的字符。

字符型直接量的三种形式

字符型值有如下三种表示形式:

  • 直接通过单个字符来指定字符型值,例如'A''9''0'等。
  • 通过转义字符表示特殊字符型值,例如'\n'\t等。
  • 直接使用Unicode值来表示字符型值,格式是\uXXXX,其中XXXX代表一个十六进制的整数

Java语言中常用的转义字符如表3.2所示。

转义字符 说明 Unicode表示方式
\b 退格符 \u0008
\n 换行符 \u000a
\r 回车符 \u000d
\t 制表符 \u0009
\" 双引号 \u0022
\' 单引号 \u0027
\\ 反斜线 \u005c

字符型值也可以采用十六进制编码方式来表示,范围是”\u0000\uFFFF‘,一共可以表示65536个字符,其中前256个(“\u0000\u00FF)字符和ASCI码中的字符完全重合。

char可以当成整型使用

由于计算机底层保存字符时,实际是保存该字符对应的编号,因此char类型的值也可直接作为整型值来使用,它相当于一个16位的无符号整数,表数范围是0~65535

char类型的变量、值完全可以参与加、减、乘、除等数学运算,也可以比较大小实际上都是用该字符对应的编码参与运算。
如果把0~65535范围内的一个int整数赋给char类型变量,系统会自动把这个int整数当成char类型来处理。

程序示例 字符型变量

下面程序简单示范了字符型变量的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CharTest {
public static void main(String[] args) {
// 直接指定单个字符作为字符值
char aChar = 'a';
// 使用转义字符来作为字符值
char enterChar = '\r';
// 使用Unicode编码值来指定字符值
char ch = '\u9999';
// 将输出一个'香'字符
System.out.println(ch);
// 定义一个'疯'字符值
char zhong = '疯';
// 直接将一个char变量当成int类型变量使用
int zhongValue = zhong;
System.out.println(zhongValue);
// 直接把一个0~65535范围内的int整数赋给一个char变量
char c = 97;
System.out.println(c);
}
}

Java没有提供表示字符串的基本数据类型,而是通过String类来表示字符串,由于字符串由多个字符组成,因此字符串要使用双引号括起来。如下代码:

1
2
//下面代码定义了一个s变量,它是一个字符串实例的引用,它是一个引用类型的变量
String s="沧海月明珠有泪,蓝田玉暖日生烟。";

char类型使用单引号 字符串使用双引号

char类型使用单引号括起来,而字符串使用双引号括起来

值得指出的是,Java语言中的单引号双引号反斜线都有特殊的用途,如果一个字符串中包含了这些特殊字符,则应该使用转义字符的表示形式。
例如,在Java程序中表示一个绝对路径:"c:\Codes",但这种写法得不到期望的结果,因为Java会把反斜线当成转义字符,所以应该写成这种形式:"c:\\codes",只有同时写两个反斜线,Java才会把第一个反斜线当成转义字符,和后一个反斜线组成真正的反斜线

3.4.3 浮点型

Java的浮点类型有两种:floatdoubleJava的浮点类型有固定的表数范围和字段长度,字段长度和表数范围与机器无关。
Java的浮点数遵循IEEE 754标准,采用二进制数据的科学计数法来表示浮点数,

  • 对于float型数值,第1位是符号位,接下来8位表示指数,再接下来的23位表示尾数;
  • 对于double类型数值,第1位也是符号位,接下来的11位表示指数,再接下来的52位表示尾数。

float和double不精确

因为Java浮点数使用二进制数据的科学计数法来表示浮点数,因此可能不能精确表示一个浮点数。例如把5.2345556f值赋给一个float类型变量,接着输出这个变量时看到这个变量的值已经发生了改变。
使用double类型的浮点数比float类型的浮点数更精确,但如果浮点数的精度足够高(小数点后的数字很多时),依然可能发生这种情况。如果开发者需要精确保存一个浮点数,则可以考虑使用BigDecimal
double类型代表双精度浮点数,foat类型代表单精度浮点数。一个double类型的数值占8字节、64位,一个foat类型的数值占4字节、32位。

浮点数表示形式

Java语言的浮点数有两种表示形式

  • 十进制数形式:这种形式就是简单的浮点数,例如5.12512.0.512。浮点数必须包含一个小数点,否则会被当成int类型处理。
  • 科学计数法形式:例如5.12e2(即5.12×10^2),5.12E2(也是5.12×10^2)。

浮点数才可以使用科学计数法

必须指出的是,只有浮点类型的数值才可以使用科学计数法形式表示。例如,51200是一个int类型的值,但512E2则是浮点类型的值

java浮点数默认是double

Java语言的浮点类型默认是double类型,如果希望Java把一个浮点类型值当成foat类型处理,应该在这个浮点类型值后加上后缀f或F。例如:5.12代表一个double类型的值,占64位的内存空间;5.12f或者5.12F才表示一个foat类型的值,占32位的内存空间。当然,也可以在一个浮点数后添加d或D后缀,强制指定是double类型,但通常没必要

正无穷大 负无穷大 非数

Java还提供了三个特殊的浮点数值:正无穷大负无穷大非数,用于表示溢出和出错。
例如:

  • 使用一个正数除以0将得到正无穷大,

  • 使用一个负数除以0将得到负无穷大,

  • 0.0除以0.0或对一个负数开方将得到一个非数。

  • 正无穷大通过DoubleFloat类的POSITIVE_INFINITY表示;

  • 负无穷大通过DoubleFoat类的NEGATIVE_INFINITY表示,

  • 非数通过DoubleFloat类的NaN表示。

正无穷等于正无穷 负无穷等于负无穷 非数不等于任何数 非数不等于非数

所有的正无穷大数值都是相等的,所有的负无穷大数值都是相等的;NaN不与任何数值相等,甚至和NaN都不相等

浮点数除0得到无穷大 整数除零抛出算术异常

注意,只有浮点数除以0才可以得到正无穷大或负无穷大,因为Java语言会自动把和浮点数运算的0(整数)当成00(浮点数)处理。
如果一个整数值除以0,则会拋出一个异常:ArithmeticException:/ByZero(除以0异常)

程序示例

下面程序示范了上面介绍的关于浮点数的各个知识点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FloatTest {
public static void main(String[] args) {
float af = 5.2345556f;
// 下面将看到af的值已经发生了改变
System.out.println(af);
double a = 0.0;
double c = Double.NEGATIVE_INFINITY;
float d = Float.NEGATIVE_INFINITY;
// 看到float和double的负无穷大是相等的。
System.out.println(c == d);
// 0.0除以0.0将出现非数
System.out.println(a / a);
// 两个非数之间是不相等的
System.out.println(a / a == Float.NaN);
// 所有正无穷大都是相等的
System.out.println(6.0 / 0 == 555.0 / 0);
// 负数除以0.0得到负无穷大
System.out.println(-8 / a);
// 下面代码将抛出除以0的异常
// System.out.println(0 / 0);
}
}

运行结果:

1
2
3
4
5
6
5.2345557
true
NaN
false
true
-Infinity

3.4.4 数值中使用下画线分隔

正如前面程序中看到的,当程序中用到的数值位数特别多时,程序员眼睛“看花”了都看不清到底有多少位数。为了解决这种问题,Java7引入了一个新功能:程序员可以在数值中使用下画线,不管是整型数值,还是浮点型数值,都可以自由地使用下画线。通过使用下画线分隔,可以更直观地分辨数值中到底包含多少位。如下面程序所示。

1
2
3
4
5
6
7
8
9
10
11
public class UnderscoreTest {
public static void main(String[] args) {
// 定义一个32位的二进制数,最高位是符号位。
int binVal = 0B1000_0000_0000_0000_0000_0000_0000_0011;
double pi = 3.14_15_92_65_36;
System.out.println(binVal);
System.out.println(pi);
double height = 8_8_4_8.23;
System.out.println(height);
}
}

3.4.5 布尔型

布尔型只有一个boolean类型,用于表示逻辑上的“真”或“假”。在Java语言中,boolean类型的数值只能是truefalse,不能用0或者非0来代表。其他基本数据类型的值也不能转换成boolean类型。

Java规范并没有强制指定boolean类型的变量所占用的内存空间。虽然**boolean类型的变量或值只要1位即可保存**,但由于大部分计算机在分配内存时允许分配的最小内存单元是字节(8位),因此**bit大部分时候实际上占用8位**
例如,下面代码定义了两个boolean类型的变量,并指定初始值。

1
2
3
4
//定义b1的值为true
boolean bl=true;
//定义b2的值为 false
boolean b2=false;

字符串"true""false"不会直接转换成boolean类型,但如果使用一个boolean类型的值和字符串进行连接运算,则boolean类型的值将会自动转换成字符串。看下面代码(程序清单同上):

1
2
3
4
5
6
// 下面代码将出现错误:字符串不能直接变成boolean型的值
// boolean b3 = "true";
// 使用boolean和字符串进行连接运算,boolean会自动转换成字符串
String str = true + "";
// 下面将输出true
System.out.println(str);

boolean类型的值或变量主要用做旗标来进行流程控制,Java语言中使用boolean类型的变量或值控制的流程主要有如下几种。

  • if条件控制语句

  • while循环控制语句

  • do while循环控制语句

  • for循环控制语句

  • 除此之外,boolean类型的变量和值还可在三目运算符(?:)中使用