Java编程基础:新手必备的语法指南

发表时间: 2022-09-27 18:15

第2章 Java语言基础

第2章 Java语言基础

学习任何知识都需要从基础知识开始,同样,学习Java 也需要从Java 的基本语法开始。本章将详细介绍Java 的基本语法,建议初学者不要急于求成,认真学习本章的内容,以便为后面的学习打下坚实的基础。

2.1 标识符和关键字

2.1.1 Unicode 字符集

Java 使用Unicode 标准字符集,该字符集由UNICODE 协会管理并接受其技术上的修改,它最多可以识别65536 个字符。在Unicode 字符集中,前128 个字符刚好是ASCII 码,由于大部分国家的 “字母表” 中的字母都是Unicode 字符集中的一个字母,因此,Java 所使用的字母不仅包括常用的拉丁字母,还包括汉字、俄文、希腊字母等。

2.1.2 关键字

关键字是Java中已经被赋予特定意义的一些单词,这些单词不可以作为标识符来使用。简单来说,凡是在Eclipse 中变成红色粗体的单词,都是关键字。Java 关键字如表2.1 所示。JDK10 新加入了关键字var。

表2.1 Java 关键字

2.1.3 标识符

Java 中的类名、对象名、方法名、常量名和变量名统称为标识符。

为了提高程序的可读性,在定义标识符时,要尽量遵循 “见其名知其意” 的原则。Java 标识符的具体命名规则如下:

(1)一个标识符可以由几个单词连接而成,以表明它的意思。

(2)标识符由一个或多个字母、数字、下画线(_)和美元符号($)组成,没有长度限制。

(3)标识符中的第一个字符不能为数字。

(4)标识符不能是关键字。

(5)标识符不能是true、false 和null。

(6)对于类名,每个单词的首字母都要大写,其他字母则小写,如RecordInfo。

(7)对于方法名和变量名,与类名有些相似,除第一个单词的首字母小写外,其他单词的首字母都要大写,如getRecordName()、recordName。

(8)对于常量名,每个单词的每个字母都要大写,如果由多个单词组成,通常情况下单词之间用下画线(_)分隔,如MAX_VALUE。

(9)对于包名,每个单词的每个字母都要小写,如com.frame。

学习笔记

Java 区分字母的大小写。

2.2 常量与变量

常量和变量在程序代码中随处可见,下面就来学习常量和变量的概念及使用要点,从而达到区别常量和变量的目的。

2.2.1 常量的概念及使用要点

所谓常量,就是值不允许被改变的量。如果要声明一个常量,则必须用关键字final修饰。声明常量的具体方式如下:

例如:

学习笔记

在定义常量标识符时,按照Java 的命名规则,所有的字符都要大写,如果常量标识符由多个单词组成,则在各个单词之间用下画线(_)分隔,如YOUTH_AGE、PIE。

在声明常量时,通常立即为其赋值,即立即对常量进行初始化。声明并初始化常量的具体方式如下:

例如:

学习笔记

在为float 型常量赋值时,需要在数值的后面加上一个字母 “F”(或 “f”),说明数值为float 型。

如果需要声明多个同一类型的常量,也可以采用下面的方式:

例如:

如果在声明常量时并没有对其进行初始化,也可以在需要时对其进行初始化,例如:

但是,如果在声明常量时已经对其进行了初始化,则常量的值不允许再被修改。例如,在尝试执行下面的代码时,将在控制台输出 “常量值不能被修改” 的错误提示:

2.2.2 变量的概念及使用要点

所谓变量,就是值可以被改变的量。如果要声明一个变量,则不需要使用任何关键字进行修饰。声明变量的具体方式如下:

例如:

学习笔记

在定义变量标识符时,按照Java 的命名规则,第一个单词的首字母小写,其他单词的首字母大写,其他字母则一律小写,如name、partyMemberAge。

在声明变量时,可以立即为其赋值,即立即对变量进行初始化。声明并初始化变量的具体方式如下:

例如:

如果需要声明多个同一类型的变量,也可以采用下面的方式:

例如:

变量与常量的区别是,变量的值允许被改变。例如,下面的代码是正确的:

2.3 数据类型

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

图2.1 Java 中的数据类型分类

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

2.3.1 基本数据类型

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

1.整数类型

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

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

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

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

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

2.浮点类型

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

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

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

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

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

学习笔记

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

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

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:

学习笔记

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

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

学习笔记

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

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

4.布尔类型

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

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

图2.2 布尔类型的使用

2.3.2 引用数据类型

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

学习笔记

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

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

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

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

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

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

1.组成

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

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

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

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

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

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

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

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

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

2.Java 虚拟机的处理方式

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1.自动类型转换

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

图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 型)的变量。

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

(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 型)的变量。

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

对于数据类型为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型变量的方式如下:

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

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

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

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

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

学习笔记

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

2.4 数组

数组是一种最为常见的数据结构,可以保存一组相同数据类型的数据。数组一旦创建,它的长度就固定了。数组的类型可以为基本数据类型,也可以为引用数据类型;可以为一维数组、二维数组,也可以为多维数组。

2.4.1 声明数组

声明数组需要指定数组类型和数组标识符。

声明一维数组的方式如下:

上面两种声明数组格式的作用是相同的,但是前一种方式更符合原理,后一种方式更符合原始编程习惯。例如,分别声明一个int 型和boolean 型的一维数组,具体代码如下:

Java 中的二维数组是一种特殊的一维数组,即数组的每个元素是一个一维数组,Java并不直接支接二维数组。

声明二维数组的方式如下:

例如,分别声明一个int 型和boolean 型二维数组,具体代码如下:

2.4.2 创建数组

创建数组实质上就是在内存中为数组分配相应的存储空间。

创建一维数组:

创建二维数组:

可以将二维数组看作一个表格,例如,可以将上面创建的数组days 看作如表2.4 所示的表格。

表2.4 二维数组内部结构表

2.4.3 初始化数组

在声明数组的同时,可以给数组元素一个初始值,一维数组初始化如下:

上述语句等价于:

二维数组初始化如下:

2.4.4 数组长度

数组的元素的个数称为数组的长度。对于一维数组,“数组名.length” 的值就是数组中元素的个数;对于二维数组,“数组名.length” 的值是它含有的一维数组的个数。例如:

如果需要获得一维数组的长度,可以通过下面的方式:

如果是通过下面的方式获得的二维数组的长度,则得到的是二维数组的行数:

如果需要获得二维数组的列数,可以通过下面的方式:

如果是通过 “{}” 创建的数组,则数组中每一行的列数可以不相同,例如:

在这种情况下,通过下面的方式得到的只是第一行拥有的列数:

如果需要获得二维数组中第二行和第三行拥有的列数,可以通过下面的方式:

2.4.5 使用数组元素

一维数组通过索引符来访问自己的元素,如months[0],months[1] 等。需要注意的是,索引是从0 开始的,而不是从1 开始的。如果数组中有4 个元素,那么索引到3 为止。

在访问数组中的元素时,需要同时指定数组标识符和元素在数组中的索引。例如,访问上面代码中创建的数组,输出索引为2 的元素,具体代码如下:

二维数组也是通过索引符访问自己的元素的。在访问数组中的元素时,需要同时指定数组标识符和元素在数组中的索引,例如,访问2.4.2 节代码中创建的二维数组,输出位于第二行、第三列的元素,具体代码如下: