零基础Java学习笔记
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3 数据类型

img

Java 是强类型的编程语言,Java 中的数据类型分类如图2.1 所示。

img

图2.1 Java 中的数据类型分类

Java 中的数据类型分为两大类,分别是基本数据类型和引用数据类型。其中,基本数据类型由Java 定义,其数据占用内存的大小固定,在内存中存入的是数值本身;而引用数据类型在内存中存入的是引用数据的存放地址,并不是数据本身。

2.3.1 基本数据类型

基本数据类型分为数值类型、字符类型和布尔类型,数值类型又分为整数类型和浮点类型,下面将依次讲解这4 种基本数据类型的特征及使用方法。

1.整数类型

声明为整数类型的常量或变量用来存储整数,整数类型包括字节型(byte)、短整型(short)、整型(int)和长整型(long)4 种数据类型,这4 种数据类型的区别是它们在内存中所占用的字节数不同,因此,它们能够存储的整数的取值范围也不同,如表2.2 所示。

表2.2 整数类型数据占用内存的字节数及取值范围

img

在为这4 种数据类型的常量或变量赋值时,所赋的值不能超出对应数据类型允许的取值范围。例如,在下面的代码中依次将byte、short 和int 型的变量赋值为9412、794 125和9 876 543 210 是不允许的,即下面的代码均是错误的:

img

在为long 型常量或变量赋值时,需要在所赋值的后面加上一个字母 “L”(或 “l”),说明所赋的值为long 型。如果所赋的值未超出int 型的取值范围,也可以省略字母“L”(或“l”)。例如,下面的代码均是正确的:

img

但是下面的代码就是错误的:

img

2.浮点类型

声明为浮点类型的常量或变量用于存储小数(也可以存储整数)。浮点类型包括单精度型(float)和双精度型(double)两种数据类型,这两种数据类型的区别是它们在内存中所占用的字节数不同,因此,它们能够存储的浮点数的取值范围也不同,如表2.3 所示。

表2.3 浮点类型数据占用内存的字节数及取值范围

img

在为float 型常量或变量赋值时,需要在所赋值的后面加上一个字母 “F”(或 “f”),说明所赋的值为float 型。如果所赋的值为整数,并且未超出int 型的取值范围,也可以省略字母 “F”(或 “f”)。例如,下面的代码均是正确的:

img

但是下面的代码就是错误的:

img

在为double 型常量或变量赋值时,需要在所赋值的后面加上一个字母 “D”(或 “d”),说明所赋的值为double 型。如果所赋的值为小数,或者所赋的值为整数,并且未超出int型的取值范围,也可以省略字母 “D”(或 “d”)。例如,下面的代码均是正确的:

img

学习笔记

Java 默认小数为double 型,所以在将小数赋值给double 型常量或变量时,可以不加字母 “D”(或 “d”)。

但是下面的代码就是错误的:

img

3.字符类型

声明为字符类型的常量或变量用来存储单个字符,它占用内存的2 字节来存储,字符类型使用关键字char 进行声明。

因为计算机只能存储二进制数据,所以需要将字符通过一串二进制数据来表示,也就是通常所说的字符编码。Java 对字符采用Unicode 编码,Unicode 使用2 字节表示1 个字符,并且Unicode 字符集中的前128 个字符与ASCII 字符集兼容。例如,字符 “a” 的ASCII 编码的二进制数据形式为01100001,Unicode 编码的二进制数据形式为00000000 01100001,它们都表示十进制数97,因此Java 与C、C++ 一样,都把字符当作整数对待。

学习笔记

ASCII 编码是用来表示英文字符的一种编码,每个字符占用1 字节,最多可以表示256 个字符。但英文字符并没有那么多,ASCII 编码使用前128 个字符(字节中最高位为0)来存放包括控制符、数字、大小写英文字母和一些其他符号的字符。而字节的最高位为1 的另外128 个字符被称为 “扩展ASCII”,通常用来存放英文的制表符、部分音标字符等字符。使用ASCII 编码无法表示多国语言文字。

Java 中的字符通过Unicode 编码,以二进制的形式存储到计算机中,计算机可通过数据类型判断要输出的是一个字符还是一个整数。Unicode 编码采用无符号编码,一共可以存储65536 个字符(0x0000 ~0xffff),所以Java 中的字符几乎可以处理所有国家的语言文字。

在为char 型常量或变量赋值时,如果所赋的值为一个英文字母、一个符号或一个汉字,则必须将所赋的值放在英文状态下的一对单引号中。例如,下面的代码分别将字母 “M”、符号 “*” 和汉字 “男”赋值给char 型变量ca、cb 和cc:

img

学习笔记

在为char 型常量或变量赋值时,无论所赋的值为字母、符号还是汉字,都只能为一个字符。

因为Java 把字符当作整数对待,并且可以存储65536 个字符,所以也可以将0 ~65535 的整数赋值给char 型常量或变量,但是在输出时得到的并不是所赋的整数。例如,下面的代码将整数88 赋值给char 型变量c,在输出变量c 时得到的是大写字母“X”:

img

学习笔记

代码 “System.out.println();” 用来将指定的内容输出到控制台,并且在输出后换行;代码 “System.out.print();” 用来将指定的内容输出到控制台,但是在输出后不换行。

也可以将数字0 ~9 以字符的形式赋值给char 型常量或变量,赋值方式为将数字0 ~9 放在英文状态下的一对单引号中。例如,下面的代码将数字 “6” 赋值给char 型变量c:

img

4.布尔类型

声明为布尔类型的常量或变量用于存储布尔值,布尔值只有true 和false,分别用来代表逻辑判断中的 “真” 和 “假”,布尔类型使用关键字boolean 进行声明。

img

程序运行结果如图2.2 所示。

img

图2.2 布尔类型的使用

2.3.2 引用数据类型

引用数据类型包括类引用、接口引用和数组引用。下面的代码分别声明一个java.lang.Object 类的引用、一个java.util.List 接口的引用和一个int 型数组的引用:

img

学习笔记

当将引用数据类型的常量或变量初始化为null 时,表示引用数据类型的常量或变量不引用任何对象。

执行上面的代码,在控制台将会输出以下内容:

img

在具体初始化引用数据类型时需要注意的是,对接口引用的初始化需要通过接口的相应实现类实现。例如,下面的代码在具体初始化接口引用list 时,是通过接口java.util.List的实现类java.util.ArrayList 实现的:

img

执行上面的代码,在控制台将会输出以下内容:

img

2.3.3 基本类型与引用类型的区别

基本数据类型与引用数据类型的主要区别在于以下两个方面。

1.组成

基本数据类型是一个单纯的数据类型,它表示的是一个具体的数字、字符或逻辑值,如68、'M' 或true。对于引用数据类型,若一个变量引用的是一个复杂的数据结构的实例,则该变量的类型属于引用数据类型,在引用数据类型变量所引用的实例中,不仅可以包含基本数据类型的变量,还可以包含对这些变量的具体操作行为,甚至包含其他引用数据类型的变量。

【例2.1】 使用基本数据类型与引用数据类型的场景。

创建一个档案类Record,在该类中使用String 型变量name 存储姓名,使用char 型变量sex 存储性别,使用int 型变量age 存储年龄,使用boolean 型变量married 存储婚姻状况,并提供了一些操作这些变量的方法,Record 类的具体代码如下:

img
img

下面创建两个Record 类的实例,并分别通过变量you 和me 进行引用,具体代码如下:

img

上面的变量you 和me 属于引用数据类型,并且引用的是类的实例,更具体的是属于类引用类型。

下面继续在Example 类的main() 方法中编写如下代码,通过Record 类中的相应方法,依次初始化代表读者和作者的变量you 和me 中的姓名、性别、年龄和婚姻状况:

img

下面继续在Example 类的main() 方法中编写如下代码,通过Record 类中的相应方法,依次获得读者和作者的姓名、性别、年龄和婚姻状况,并将得到的信息输出到控制台:

img

执行上面的代码,在控制台将会输出两种人物角色的信息,如图2.3 所示。

img

图2.3 输出两种人物角色的信息

2.Java 虚拟机的处理方式

对于基本数据类型的变量,Java虚拟机会根据变量的实际类型为其分配实际的内存空间,例如,为int型变量分配一个4字节的内存空间来存储变量的值。而对于引用数据类型的变量,Java 虚拟机同样需要为其分配内存空间,但引用数据类型的变量在内存空间中存放的并不是变量所引用的对象,而是对象在堆区存放的地址。也就是说,引用变量最终只是指向被引用的对象,而不是存储了被引用的对象,因此两个引用变量之间的赋值,实际上就是将一个引用变量存储的地址复制给另一个引用变量,从而使两个变量指向同一个对象。

例如,创建一个图书类Book,具体代码如下:

img

下面声明两个Book 类的实例,分别通过变量book1 和book2 进行引用,对变量book1 进行具体的初始化,而将变量book2 初始化为null,具体代码如下:

img

Java 虚拟机为变量book1、book2 及book1 所引用对象的成员变量分配的内存空间如图2.4 所示。

img

图2.4 内存空间的分配情况1

从图2.4 可以看出,变量book1 引用了Book 类的实例,变量book2 没有引用任何实例。下面对变量book2 进行具体的初始化,将变量book1 引用实例的地址复制给变量book2,即令变量book2 与book1 引用同一个Book 类的实例,具体代码如下:

img

此时,Java 虚拟机的内存空间分配情况如图2.5 所示。

img

图2.5 内存空间的分配情况2

2.3.4 数据类型之间的相互转换

所谓数据类型之间的相互转换,就是将变量从当前数据类型转换为其他数据类型。在Java 中,数据类型之间的相互转换可以分为以下3 种情况:

(1)基本数据类型之间的相互转换。

(2)字符串与其他数据类型之间的相互转换。

(3)引用数据类型之间的相互转换。

在这里只介绍基本数据类型之间的相互转换,其他两种情况将在相关的章节中介绍。

在对多个基本数据类型的数据进行混合运算时,如果这几个数据并不属于同一基本数据类型,例如,在一个表达式中同时包含整数类型、浮点类型和字符类型的数据,则需要先将它们转换为统一的数据类型,然后才能进行计算。

基本数据类型之间的相互转换又分为两种情况,分别是自动类型转换和强制类型转换。

1.自动类型转换

当需要从低级类型向高级类型转换时,编程人员无须进行任何操作,Java 会自动完成从低级类型向高级类型的转换。低级类型是指取值范围相对较小的数据类型,高级类型是指取值范围相对较大的数据类型,如long 型相对于float 型是低级数据类型,但是相对于int 型则是高级数据类型。在基本数据类型中,除boolean 型外,其他数据类型均可参与算术运算,这些数据类型从低到高的排序如图2.6 所示。

img

图2.6 数据类型从低到高的排序

在不同数据类型之间的算术运算中,可以分为两种情况进行考虑:一种情况是在算术表达式中含有int、long、float或double型的数据;另一种情况是不含有上述4种类型的数据,即只含有byte、short 或char 型的数据。

(1)在算术表达式中含有int、long、float 或double 型的数据。

如果在算术表达式中含有int、long、float 或double 型的数据,则Java 会先将表达式中所有数据类型相对较低的变量自动转换为表达式中数据类型最高的数据类型,再进行计算,并且计算结果的数据类型也为表达式中数据类型最高的数据类型。

例如,在下面的代码中,Java 会先自动将表达式 “b * c - i + l” 中的变量b、c 和i 的数据类型转换为long 型,再进行计算,并且计算结果的数据类型为long 型。也就是说,将表达式 “b * c - i + l” 直接赋值给数据类型低于long 型(如int 型)的变量是不被允许的,但是可以直接赋值给数据类型高于long 型(如float 型)的变量。

img

而在下面的代码中,Java 会先自动将表达式 “b * c - i + d” 中的变量b、c 和i 的数据类型转换为double 型,再进行计算,并且计算结果的数据类型为double 型。也就是说,将表达式 “b * c - i + d” 直接赋值给数据类型低于double 型(如long 型)的变量是不被允许的。

img

(2)在算术表达式中只含有byte、short 或char 型的数据。

如果在算术表达式中只含有byte、short 或char 型的数据,Java 会先将所有变量的类型自动转换为int 型,再进行计算,并且计算结果的数据类型为int 型。

例如,在下面的代码中,Java 会先自动将表达式 “b + s * c” 中的变量b、s 和c 的数据类型转换为int 型,再进行计算,并且计算结果的数据类型为int 型。也就是说,将表达式 “b + s * c” 直接赋值给数据类型低于int 型(如char 型)的变量是不被允许的,但是可以直接赋值给数据类型高于int 型(例如long 型)的变量。

img

在下面的代码中,Java 会先自动将表达式 “s1 * s2” 中的变量s1 和s2 的数据类型转换为int 型,再进行计算,并且计算结果的数据类型也为int 型。

img

对于数据类型为byte、short、int、long、float 和double 的变量,可以将数据类型相对较低的数据或变量直接赋值给数据类型相对较高的变量,例如,可以将数据类型为short的变量直接赋值给数据类型为float 的变量,但是不可以将数据类型相对较高的数据或变量直接赋值给数据类型相对较低的变量,例如,不可以将数据类型为float 的变量直接赋值给数据类型为short 的变量。

对于数据类型为char 的变量,不可以将数据类型为byte 或short 的变量直接赋值给char 型变量;但是可以将char 型变量直接赋值给int、long、float 或double 型的变量。

2.强制类型转换

如果需要把数据类型相对较高的数据或变量赋值给数据类型相对较低的变量,就必须进行强制类型转换。例如,将Java 默认为double 型的数据 “7.5” 赋值给数据类型为int型变量的方式如下:

img

上面代码中在数据 “7.5” 的前方添加了代码 “(int)”,意思是将数据 “7.5” 的类型强制转换为int 型。

在进行强制类型转换时,可能会导致数据溢出或精度降低。例如,上面代码中最终变量i 的值为7,导致数据精度降低。

将Java 默认为int 型的数据 “774” 赋值给数据类型为byte 型变量的方式如下:

img

最终变量b 的值为6,导致数据溢出。导致数据溢出的原因是整数774 超出了byte 型数据的取值范围,在进行强制类型转换时,表示整数774 的二进制数据流的前24 位会被舍弃,最终赋值给变量b 的数值是后8 位的二进制数据流的数据,如图2.7 所示。

img

图2.7 将十进制数774 强制类型转换为byte 型

学习笔记

在编程的过程中,建议读者谨慎使用可能导致数据溢出或精度降低的强制类型转换。