所有的计算机编程语言都有一套属于自己的语法规则,Java语言自然也不例外。要使用Java语言进行程序设计,就需要充分了解其语法规则。本章介绍Java语言的标识符、数据类型、变量、常量、运算符、控制语句和数组等基础知识。语法是枯燥的,但是实例是生动的。对所有事物的认识都是一个渐进的过程,让我们一步一步进入Java的世界吧。
· 了解标识符定义
· 熟悉Java基本数据类型
· 掌握Java各种运算符的运算规则
· 熟练掌握各种流程控制语句
· 理解Java数组和C语言的不同
· 熟练应用Java语法规则编写简单应用程序
标识符和关键字是Java语言的基本组成部分,本节对二者进行介绍。
标识符(identifier)可以简单地理解为一个名字,是用来标识类名、变量名、方法名、数组名的有效字符序列。
Java语言规定标识符由任意顺序的字母、下画线(_)、美元符号($)和数字组成,并且第一个字符不能是数字。
下面是合法的标识符。
birthday User_name _system_varl $max
下面是非法的标识符。
3max (变量名不能以数字开头)
room# (不允许包含字符“#”)
class (“class”为保留字)
(1)标识符不能是关键字。(2)Java语言严格区分大小写,例如标识符republican和Republican是两个不相同的标识符。(3)Java语言使用unicode标准字符集,最多可以使用其中的65535个字符。因此,Java语言中的字母不仅包括英文字母,还包括汉字以及其他语言中的文字。
关键字是Java语言中已经被赋予特定意义的一些单词。关键字对Java编译器有着特殊的含义。Java的关键字可以划分为5种类型:类类型(Class Type)、数据类型(Data Type)、控制类型(Control Type)、存储类型(Storage Type)和其他类型(Other Type)。
每种类型所包含的关键字如下所示。
(1)类类型(Class Type)
package, class, abstract, interface, implements, native, this, super, extends, new, import, instanceof, public, private, protected
(2)数据类型(Data Type)
char, double, enum, float, int, long, short, boolean, void, byte
(3)控制类型(Control Type)
break, case, continue, default, do, else, for, goto, if, return, switch, while, throw, throws, try, catch, synchronized, final, finally, transient, strictfp
(4)存储类型(Storage Type)
register, static
(5)其他类型(Other Type)
const, volatile
关键字值得注意的地方包括以下几点。
(1)所有Java关键字都是由小写字母组成的。
(2)Java语言无sizeof关键字,因为Java语言的数据类型长度和表示是固定的,与程序运行环境没有关系,在这一点上Java语言和C语言是有区别的。
(3)goto和const在Java语言中并没有具体含义,之所以把他们列为关键字,只是因为它们在某些计算机语言中是关键字。
计算机编程是为了解决实际问题,而实际问题中会存在不同类别的数据,因此,每种编程语言都会提供不同的数据类型。数据类型规定某类数据在计算机中的存储和运算规则。Java有两种数据类型,即基本数据类型和引用数据类型。其中基本数据类型又分为八种,六种数值类型,一种字符类型和一种布尔类型;引用数据类型分为数组、类和接口,如图2-1所示。
图2-1 Java数据类型
整数类型用来存储整数数值。可以是正数,也可以是负数。整型数据在Java程序中有3种表示形式,分别为十进制、八进制和十六进制。
十进制:十进制的表现形式大家都很熟悉,例如15、309、27。
八进制:八进制必须以0开头,如0123(转换成十进制数为83)。
十六进制:十六进制必须以0x开头,如0x25(转换成十进制数为37)。
整型数据根据所占内存大小的不同,可分为byte、short、int和long四种类型。它们具有不同的取值范围,如表2-1所示。
表2-1 整数类型
下面演示以上几种数据类型的使用方法。
【例2-1】定义不同的整数类型变量并赋值,实例代码如下。
在定义上述变量时,要注意变量的取值范围,超出取值范围就会出错。特别地,对于long型值需要在数字后加L或l。理论上不分大小写,但是若写成l容易与数字1混淆,不容易分辩,所以最好大写。
浮点类型表示有小数部分的数字。Java语言中浮点类型分为单精度浮点类型(float)和双精度浮点类型(double)。它们具有不同的取值范围,如表2-2所示。
表2-2 浮点型数据类型
在默认情况下,小数都被看作double型,若使用float型小数,则需要在小数后面添加F或f。可以使用后缀d或D来明确表明这是一个double类型数据,不加d不会出错,但声明float型变量时如果不加f,系统会认为变量是double类型而出错。下面举例讲解浮点型变量的定义。
【例2-2】定义浮点型变量,实例代码如下。
float x = 100.23f; double y1 = 32.12d; double y2 = 123.45;
在定义上述变量时,要注意变量的取值范围,超出取值范围就会出错。
字符类型(char)用于存储单个Unicode字符,占用16位(两个字节)的内存空间。在定义字符型变量时,要以单引号表示,如's'表示一个字符。而"s"则表示一个字符串。虽然只有一个字符,但由于使用双引号,它仍然表示字符串,而不是字符。
下面举例说明使用char关键字可定义字符变量。
【例2-3】声明字符型变量,实例代码如下。
char c1 = 'a';
同C和C++语言一样,Java语言也可以把字符作为整数对待,由于字符'a'在Unicode表中的排序位置是97,因此允许将上面的语句写成如下代码。
char c1 = 97;
由于Unicode编码采用无符号编码,可以存储65536个字符(0x0000~0xffff),所以Java中的字符几乎可以处理所有国家的语言文字。若想得到一个0~65536之间的数所代表的Unicode表中相应位置上的字符,也必须使用char型显式转换。
有些字符(如回车符)不能通过键盘录入字符串中。针对这种情况,Java提供了转义字符,以反斜杠(\)开头,将其后的字符转变为另外的含义,例如'\n'(换行)、'\b'(退格)、'\' '(单引号)、'\t'(水平制表符)。
用双引号引用的文字,是字符串而不是原始类型。它是一个类(class)String,被用来表示字符序列。字符本身符合Unicode标准,且上述char类型的转义字符适用于String。
布尔类型又称逻辑类型。通过关键字boolean定义布尔类型变量,只有true和false两个值,分别代表布尔逻辑中的“真”和“假”。布尔类型通常被用在流程控制中作为判断条件。布尔类型变量的默认值是false。
【例2-4】声明boolean型变量,实例代码如下。
和C语言不同,在Java语言中,布尔值不能与整数类型进行转换。
在程序执行过程中,其值不能被改变的量称为常量,其值能被改变的量称为变量。变量与常量的命名都必须使用合法的标识符。
在程序运行过程中不能被改变的量称为常量(constant),通常也被称为“final变量”。常量在整个程序中只能被赋值一次。
在Java语言中声明一个常量,除了要指定数据类型外,还需要通过final关键字进行限定。声明常量的标准语法如下。
final datatype CONSTNAME=VALUE;
其中,final是Java的关键字,表示定义的是常量,datatype为数据类型,CONSTNAME为常量的名称,VALUE是常量的值。
【例2-5】声明常量,实例代码如下。
常量名通常使用大写字母,但这并不是必需的。只不过很多Java程序员已经习惯使用大写字母来表示常量。通过这种命名方法实现与变量的区别。
在程序中除了可以定义符号常量,还可以把字面量赋给任何内置类型的变量。例如如下代码。
byte、int、long和short都可以用十进制、十六进制以及八进制的方式来表示。当使用字面量时,前缀0表示八进制,而前缀0x代表十六进制,例如如下代码。
int decimal = 100; int octal = 0144; int hexa = 0x64;
和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:“Hello world”“two\nlines”“\" this is a quotes\" ”。
Java语言支持一些特殊的转义字符序列。常见的转移字符如表2-3所示。
表2-3 常用的转义字符
变量是Java程序的一个基本存储单元,由一个或多个连续的字节组成。变量都有名字,程序中通过变量名引用对应内存单元中的数据。
在Java中,使用变量之前需要先声明变量。变量声明通常包括三部分,变量类型、变量名和初始值,其中变量的初始值是可选的。声明变量的语法格式如下。
type identifier [= value][,identifier[=value]...];
其中,type可以是Java语言的基本数据类型,或者类、接口等复杂类型的名称(类和接口在本书后面章节中进行介绍)。identifier是变量名,必须是合法的标识符,可以使用逗号隔开声明多个同类型的变量。=value表示用具体的值对变量进行初始化,即把某个值赋给变量。例如如下代码。
由于变量被定义后暂存在内存中,等到程序执行到某一个点,该变量会被释放掉,也就是说变量有它的生命周期。在变量的生命周期内其可以被访问的范围称为作用域,若超出该区域对变量进行访问则在编译时会出现错误。
根据作用域的不同,可将变量分为不同的类型:类成员变量、局部变量、方法参数变量和异常处理参数变量。下面对这几种变量进行详细说明。
类成员变量声明在类中,但不属于任何一个方法,其作用域为整个类。
【例2-6】声明类成员变量,实例代码如下。
class ClassVar{ int x = 45; int y ; }
在上述代码中,定义的两个变量x、y均为类成员变量,其中第一个进行了初始化,而第二个没有进行初始化。
在类的成员方法中定义的变量(在方法内部定义的变量)称为局部变量。局部变量只在当前代码块中有效。
【例2-7】声明两个局部变量,实例代码如下。
在上述代码中,定义的两个变量x、y均为局部变量,其中x的作用域是整个main()方法,而y的作用域仅仅局限于if语句代码块。
声明为方法参数的变量的作用域是整个方法。
【例2-8】声明一个方法参数变量,实例代码如下。
在上述代码中,定义了一个成员方法getSum(),方法中包含一个int类型的方法参数变量x,其作用域是整个getSum ()方法。
有关变量的声明、作用域和使用方法等更多内容将在后续章节中通过大量实例进行深入讲解。
对数据进行加工的过程称为运算,表示各种不同运算的符号称为运算符。Java提供丰富的运算符,如赋值运算符、算术运算符、关系运算符等。本节向读者介绍这些运算符。
赋值运算符以符号“=”表示,它是一个二元运算符(对两个操作数作处理),其功能是将右方操作数所含的值赋给左方的操作数。例如如下代码。
int a = 100;
该表达式是将100赋值给变量a。左方的操作数必须是一个变量,而右边的操作数则可以是任何表达式,包括变量。
Java算术运算符主要有+(加)、-(减)、*(乘)、/(除)、%(求余)。它们都是二元运算符。另外,还有一些单目运算符,如++(自增)和--(自减)运算符。Java运算符的功能及使用方式如表2-4所示。需要说明的是,表中的变量a为整型变量。
表2-4 算术运算符
Java算术运算符的优先级如表2-5所示。
表2-5 算术运算符的优先级
在算术运算符中比较难于理解的是“++”和“--”运算符,下面对这两个运算符较为详细地介绍。
自增和自减运算是两个快捷运算符(常称作“自动递增”和“自动递减”运算)。其中,自减操作符是“--”,意为“减少一个单位”;自增操作符是“++”,意为“增加一个单位”。例如,a是一个int变量,则表达式++a等价于a=a+1。递增和递减操作符不仅改变变量,并且以变量的值作为生成的结果。
这两个操作符各有两种使用方式,通常称为“前缀式”和“后缀式”。“前缀递增”指++操作符位于变量的前面;而“后缀递增”指++操作符位于变量的后面。“前缀递减”指--操作符位于变量的前面;而“后缀递减”指--操作符位于变量的后面。
对于前缀递增和前缀递减(如++a或--a),会先执行运算,再生成值。而对于后缀递增和后缀递减(如a++或a--),是先生成值,再执行运算。下面是一个有关“++”运算符的例子。
【例2-9】++运算符在程序中的使用。
程序执行结果如图2-2所示。
图2-2 例2-9的运行结果
从运行结果中可以看到,放在变量前面的自增运算符,先将变量的值加1,然后再使该变量参与其他运算。放在变量后面的自增运算符,先使变量先参与其他运算,然后再将该变量加1。
关系运算实际上就是“比较运算”。将两个值进行比较,判断比较的结果是否符合给定的条件。如果符合则表达式的结果为true,否则为false。
Java关系运算符都是二元运算符。由Java关系运算符组成的关系表达式的计算结果为逻辑值。具体的关系运算符及其说明见表2-6所示。
表2-6 比较运算符
【例2-10】使用比较运算符对变量进行比较,并将运算后的结果输出。
程序执行结果如图2-3所示。
图2-3 例2-10的运行结果
Java语言逻辑运算符有三个,分别是&&(逻辑与)、||(逻辑或)、!(逻辑非),其中前两个是双目运算符,第三个为单目运算符。具体的运算规则如表2-7所示。
表2-7 逻辑运算符
【例2-11】逻辑运算符在程序中的应用。
程序执行结果如图2-4所示。
图2-4 例2-11的运行结果
特别地,当使用逻辑与运算符时,在两个操作数都为true时,结果才为true。但是当得到第一个操作数为false时,其结果就必定是false,这时候就不会再判断第二个操作数。这称作短路逻辑运算符,如例2-12。
【例2-12】短路逻辑运算符的应用。
执行结果如图2-5所示。
图2-5 例2-12的运行结果
该程序使用到了短路逻辑运算符(&&),首先判断a<4的结果为false,则b的结果必定是false,不再执行第二个操作a++<10的判断,所以a的值为5。
位运算符用来对二进制的位进行操作,其操作数的类型是整数类型以及字符类型,运算结果是整型数据。
整型数据在内存中以二进制的形式表示,如int型变量7的二进制表示是00000000 00000000 00000000 00000111。其中,左边最高位是符号位,最高位是0表示正数,若为1则表示负数。负数采用补码表示,如-8的二进制表示为111111111 111111111 1111111 11111000。
了解了整型数据在内存中的表示形式后,开始学习位运算符。
“按位与”运算符“&”为双目运算符,其运算法则是将参与运算的数转换成二进制数,然后低位对齐,高位不足补零,如果对应的二进制位都是1,则结果为1,否则结果为0。
使用按位与运算符的示例如下。
按照按位与运算的计算规则,3&5的结果是1。
“按位或”运算符“|”为双目运算符。“按位或”运算的运算法则是将参与运算的数转换成二进制数,然后低位对齐,高位不足补零,如果对应的二进制位只要有一个为1,则结果为1,否则结果为0。
使用按位或运算符的示例如下。
按照按位或运算的计算规则,3|5的结果是7。
“按位异或”运算符“^”为双目运算符。“按位异或”运算的运算法则是将参与运算的数转换成二进制数,然后低位对齐,高位不足补零,如果对应的二进制位相同,则结果为0,否则结果为1。
使用按位异或运算符的示例如下。
按照按位异或运算的计算规则,3^5的结果是6。
“按位取反”运算符“~”为单目运算符。“按位取反”运算的运算法则:先将参与运算的数转换成二进制数,然后把各位的1改为0,0改为1。
使用按位取反运算符的示例如下。
按照按位取反运算的计算规则,~3的结果是-4。
“右移位”运算符“>>”为双目运算符。“右移位”运算的运算法则:先将参与运算的数转换成二进制数,然后所有位置的数统一向右移动对应的位数,低位移出(舍弃),高位补符号位(正数补0,负数补1)。
使用右移位运算符的示例如下。
按照右移位运算的计算规则,3 >>1的结果是1。
“左移位”运算符“<<”为双目运算符。“左移位”运算的运算法则:先将参与运算的数转换成二进制数,然后所有位置的数统一向左移动对应的位数,高位移出(舍弃),低位的空位补0。
使用左移位运算符的示例如下。
按照左移位运算的计算规则,3 <<1的结果是6。
“无符号右移位”运算符“>>>”为双目运算符。“无符号右移位”运算的运算法则:先将参与运算的数转换成二进制数,然后所有位置的数统一向右移动对应的位数,低位移出(舍弃),高位补0。
使用无符号右移位运算符的示例如下。
按照无符号右移位运算的计算规则,3 >>>1的结果是1。
【例2-13】位运算符的使用。
程序执行结果如图2-6所示。
图2-6 例2-13的运行结果
条件运算符“?:”需要三个操作数,所以又被称为三元运算符。条件运算符的语法规则如下。
<布尔表达式> ? value1:value2
如果“布尔表达式”的结果为true,返回value1的值。如果“布尔表达式”的结果为false,则返回value2的值。
使用条件运算符的示例如下。
int a = 3; int b = 5; int c = (a > b)? 1:2;
按照条件运算符的计算规则,执行后c的值为2。
当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及运算符的优先级的问题。
Java语言规定了运算符的优先级与结合性。在表达式求值时,先按照运算符的优先级由高到低的次序执行。例如,算术运算符中的乘、除运算优先于加、减运算。
对于同优先级的运算符要按照它们的结合性来决定。运算符的结合性决定它们是从左到右计算(左结合性)还是从右到左计算(右结合性)。左结合性很好理解,因为大部分的运算符都是从左到右来计算的。需要注意的是右结合性的运算符,主要有3类:赋值运算符(如“=”、“+=”等)、一元运算符(如“++”、“!”等)和三元运算符(即条件运算符)。表2-8列出各运算符优先级的排列与结合性,请读者参考。
表2-8 运算符的优先级与结合性
因为括号优先级最高,所以不论任何时候,当无法确定某种计算的执行次序时,可以使用加括号的方法来明确指定运算的顺序。这样不容易出错,同时也是提高程序可读性的一个重要方法。
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算,或者当一种数据类型变量的值赋给另外一种数据类型的变量时,也会涉及数据类型的转换。数据类型的转换有两种方式:隐式类型转换(自动转换)和显式类型转换(强制转换)。
从低级类型向高级类型的转换,系统自动执行,程序员无须进行任何操作。这种类型的转换称为隐式转换。除了布尔型不参与转换外,其他数据类型转换的顺序如下。
byte,short,char -->int -->long -->float -->double
当进行赋值运算时,目标类型大于源类型,会自动进行类型转换。例如,在计算表达式的值时,参与运算的数据只要有一个是double型,则其他数据都会自动转换为double,然后再进行运算,最后表达式的结果也是double型。
【例2-14】自动类型转换。实例代码如下。
程序运行结果如图2-7所示。
图2-7 例2-14的运行结果
c1的值为字符a,查ASCII码表可知对应int类型值为97,A对应值为65,所以i2=65+1=66。
当把高精度的变量的值赋给低精度的变量时,必须使用显式类型转换运算,又称强制类型转换。
强制类型转换的语法规则如下。
(type)variableName;
其中,type为variableName要转换的数据类型,而variableName是要进行类型转换的变量名称,示例如下。
int a = 3; double b = 5.0; a = (int)b; //将double类型的变量b的值转换为int类型,然后赋值给变量a
如果此时输出a的值,结果是5。
需要注意的是,强制类型转换可能会导致数据精度的损失。例如如下代码。
int i =128; byte b = (byte)i;
因为byte类型是8位,最大值为127,所以当int强制转换为byte类型时,值达到128时候就会导致溢出。
另外,浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如如下代码。
(int)23.7 == 23; (int)-45.89f == -45
程序通过流程控制语句决定运行时的走向,并完成特定的任务。在默认情况下,系统按照语句的先后顺序依次执行,这就是所谓的顺序结构。顺序结构学习起来虽然简单,但在处理复杂问题时往往捉襟见肘。为此,在计算机编程语言中又出现了分支结构、循环结构和跳转结构。
本节主要对分支结构、循环结构和跳转结构中涉及的流程控制语句进行介绍。
分支语句提供一种机制,使得程序可以根据表达式结果或者变量状态选择不同的执行路径。它解决了顺序结构不能判断的缺点。
Java提供两种选择语句:if语句和switch语句。它们也被称为条件语句或选择语句。
if语句的语法格式如下。
if (条件表达式){ 语句块; }
上述语法格式表达的意思是,如果if关键字后面的表达式成立,那么程序就执行语句块,其执行流程如图2-8所示。
图2-8 if语句执行流程图
当if后面的条件表达式为true时,则执行紧跟其后的语句块;如果条件表达式为false,则执行程序中if语句后面的其他语句。语句块中如果只有一个语句,可以不用{}括起来,但为了增强程序的可读性最好不要省略。
【例2-15】通过键盘输入两个整数,输出其中较大的一个。
程序执行结果如图2-9所示。
图2-9 例2-15的运行结果
if语句后面可以跟else语句,if-else语句的语法格式如下。
if (条件表达式){ 语句块1; }else{ 语句块2; }
上述语法表达的意思是,如果if关键字后面的表达式成立,那么程序就执行语句块1,否则执行语句块2。其执行流程如图2-10所示。
图2-10 if-else语句执行流程图
【例2-16】if-else应用举例。
功能实现: 通过键盘输入一个整数,判断该整数是否大于或等于18,如果大于或等于18输出“成年人”,否则输出“未成年人”。
程序执行结果如图2-11所示。
图2-11 例2-16的运行结果
if-else嵌套语句是功能强大的分支语句,可以解决几乎所有的分支问题。if-else嵌套语句的语法格式如下。
其执行流程如图2-12所示。
图2-12 if-else嵌套语句执行流程图
【例2-17】通过键盘输入两个整数,比较它们的大小。
功能实现: 通过嵌套的if-else语句判断两个整数的大小,并根据情况输出不同内容。
程序执行结果如图2-13所示。
图2-13 例2-17的运行结果
Java语言中除了嵌套的if语句可以实现多分支结构外,switch语句也可以实现多分支结构,也称为开关语句。
switch语句是多分支的开关语句。它的一般格式定义如下(其中break语句是可选的)。
其中,switch、case、break是Java的关键字。switch语句的功能是判断表达式的值与一系列值中某个值是否相等,每个值称为一个分支。
switch语句有如下几个规则。
· switch后面括号中表达式的值可以是byte、short、int或者char。从Java 7开始,表达式的值也可以是字符串String类型。
· switch语句可以拥有多个case语句。每个case后面跟一个要比较的值和冒号。
· case语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量。
· 当变量的值与case语句的值相等时,case语句之后的语句开始执行,直到break语句出现才会跳出switch语句。
· 当遇到break语句时,switch语句终止。程序跳转到switch语句后面的语句执行。case语句不一定要包含break语句。如果没有break语句出现,程序会继续执行下一条case语句,直到出现break语句。
· switch语句可以包含一个default分支,该分支一般是switch语句的最后一个分支(可以在任何位置,但建议在最后一个)。default在没有case语句的值和变量值相等的时候执行。default分支不需要break语句。
【例2-18】利用switch语句处理表达式中的运算符,并输出运算结果。
程序执行结果如图2-14所示。
图2-14 例2-18的运行结果
如果case语句块中没有break语句,匹配成功后,从当前case开始,后续所有case的值都会输出。
【例2-19】case后面不带break的例子。
程序运行结果如图2-15所示。
图2-15 例2-19的运行结果
循环语句的作用是反复执行一段代码,直到满足特定条件为止。Java语言中提供的循环语句主要有三种,分别是while语句、do-while语句、for语句。在Java 5中引入了一种主要用于数组的增强型for循环,具体用法在介绍数组时讲解。
while语句的语法格式如下。
执行while循环时,首先判断“条件表达式”的值,如果为true,则执行语句块。每执行一次语句块,都会重新计算条件表达式的值。如果为true,则继续执行语句块,直到条件表达式的值为false时结束循环。
while语句执行流程如图2-16所示。
图2-16 while语句执行流程图
【例2-20】利用while语句求10个整数的和,并输出运算结果。
程序执行结果如图2-17所示。
图2-17 例2-20的运行结果
do-while语句的格式如下。
do-while循环与while循环的不同在于:它先执行语句块,然后再判断条件表达式的值。如果为true则继续执行语句块,直到条件表达式的值为false为止。因此,do-while语句至少要执行一次语句块。
do-while语句的执行流程如图2-18所示。
图2-18 do-while语句执行流程图
【例2-21】利用do-while语句输出整数n的位数。
程序执行结果如图2-19所示。
图2-19 例2-21的运行结果
for语句是一种功能最强、使用最广泛的循环语句。for语句的循环次数是在执行前就确定的。for语句的语法格式如下:
for语句中3个表达式之间用“;”分开,他们的具体含义如下。
表达式1:初始化表达式,通常用于给循环变量赋初值。
表达式2:条件表达式。它是一个布尔表达式,只有值为true时才会继续执行for语句中的语句块。
表达式3:更新表达式,用于改变循环变量的值,避免死循环。
for语句的执行流程如图2-20所示。
图2-20 for语句执行流程图
①循环开始时,首先计算表达式1,完成循环变量的初始化工作。
②计算表达式2的值,如表达式2的值为true,则执行语句块,否则不执行语句块,跳出循环语句。
③执行完一次循环后,计算表达式3,改变循环变量的状态。
④转入②继续执行。
【例2-22】利用for语句求n个整数的最大值。
程序执行结果如图2-21所示。
图2-21 例2-22的运行结果
所谓循环语句嵌套就是循环语句的循环体中包含另外一个循环语句。Java语言支持循环语句嵌套,如for循环语句嵌套,while循环语句嵌套,也支持二者的混合嵌套。
【例2-23】利用for循环语句嵌套打印九九乘法表。
程序执行结果如图2-22所示。
图2-22 例2-23的运行结果
跳转语句用来实现循环语句中的执行流程转移。在前面学习switch语句时,用到的break语句就是一种跳转语句。在Java语言中,经常使用的跳转语句主要包括break语句和continue语句。
在Java语言中,break用于强行跳出循环体,不再执行循环体中break后面的语句。如果break语句出现在嵌套循环中的内层循环,则break的作用是跳出内层循环,即break每次只能跳出一层循环。
【例2-24】break语句应用举例。
功能实现: 利用for循环语句计算1到100之间的整数之和,当和大于500时,使用break跳出循环,并打印此时的求和结果。
程序执行结果如图2-23所示。
图2-23 例2-24的运行结果
从程序执行结果可以发现,当sum大于500时,程序执行break语句跳出循环体,不再继续执行求和运算。此时sum的值为528,而不是1~100的所有数之和5050。
continue语句只能用在循环语句中,否则将会出现编译错误。当程序在循环语句中执行到continue语句时,自动结束本轮次循环体的执行,回到循环的开始处重新判断循环条件,决定是否继续执行循环体。
【例2-25】输出1~10的所有不能被3整除的自然数。
程序执行结果如图2-24所示。
从程序执行结果可以发现,1~10能被3整除的自然数在结果中均没有出现。这是因为当程序遇到能被3整除的自然数时,满足了if语句的判断条件,因执行continue语句,不再执行continue语句后面的输出语句,开始了新一轮次的循环,所以能被3整除的数没有出现在结果中。
图2-24 例2-25运行结果
使用注释可以提高程序的可读性,帮助程序员更好地阅读和理解程序。在Java源程序文件的任意位置都可添加注释语句。注释中的文字Java编译器不进行编译,所有代码中的注释对程序不产生任何影响。Java语言提供三种添加注释的方法,分别为单行注释、多行注释和文档注释。
“//”为单行注释标记。从符号“//”开始直到换行为止的所有内容均作为注释而被编译器忽略。
单行注释语法如下。
//注释内容
例如,以下代码为声明的int型变量添加注释:
“/* */”为多行注释标记,符号“/*”与“*/”之间的所有内容均为注释内容。注释中的内容可以换行。
多行注释语法如下。
/* 注释内容1 注释内容2 … */
有时为了多行注释的美观,编程人员习惯在每行的注释内容前面加入一个“*”号,构成如下的注释格式。
/* *注释内容1 *注释内容2 *… */
“/** */”为文档注释标记。符号“/**”与“*/”之间的内容均为文档注释内容。当文档注释出现在声明(如类的声明、类的成员变量的声明、类的成员方法声明等)之前时,会被Javadoc文档工具读取作为Javadoc文档内容。文档注释的格式与多行注释的格式相同。对于初学者而言,文档注释并不是很重要,了解即可。
文档注释语法如下。
其注释方法与多行注释很相似,但它是以“/**”符号作为注释的开始标记。与单行、多行注释一样,被“/**”和“*/”符号注释的所有内容均会被编译器忽略。
在解决实际问题的过程中,往往需要处理大量相同类型的数据,而且这些数据被反复使用。这种情况下,可以考虑使用数组来存储数据。数组就是相同类型的数据按顺序组成的一种复合型数据类型。数据类型可以是基本数据类型,也可以是引用数据类型。当数组元素的类型仍然是数组时,就构成了多维数组。
数组名可以是任意合法的Java标识符。通过数组名和下标来使用数组中的数据,下标从0开始。使用数组的最大好处是可以让一批相同性质的数据共用一个变量名,而不必为每个数据命名一个名字。使用数组不仅使程序书写大为简便清晰,可读性大大提高,而且便于用循环语句处理这类数据。
一维数组是指维度为1的数组。它是数组最简单的形式,也是最常用的数组。
与变量一样,使用数组之前,必须先声明数组。声明一维数组的语法格式有以下两种形式。
数据类型 数组名[ ]; 数据类型 [ ] 数组名;
其中,数据类型可以是基本数据类型,也可以是引用数据类型。数组名可以是任意合法的Java标识符,例如如下代码。
在声明数组时,不能指定数组的长度,否则编译无法通过。
声明数组仅为数组指定数组名和数组元素的类型,并没有为元素分配实际的存储空间。需要为数组分配空间才能使用。
分配空间就是告诉计算机在内存中为它分配几个连续的位置存储数据。在Java中使用new关键字为数组分配空间。其语法格式如下。
数组名 = new 数据类型[数组长度];
其中,数组长度就是数组中能存放的元素个数,是大于0的整数,例如如下代码。
也可以在声明数组时就为它分配空间,语法格式如下。
数据类型 数组名[ ] = new 数据类型[数组长度];
例如:
int a2[] = new int[10]; //声明数组的同时并分配空间
数组的大小一旦确定,就不能再修改。
初始化一维数组是指分别为数组中的每个元素赋值。可以通过以下两种方法进行数组的初始化。
(1)直接指定初值的方式
在声明一个数组的同时将数组元素的初值依次写入赋值号后的一对花括号内,给这个数组的所有元素赋初始值。这样,Java编译器可通过初值的个数确定数组元素的个数,为它分配足够的存储空间并将这些值写入相应的存储单元。
语法格式如下。
数据类型 数组名[ ] = {元素值1, 元素值2, 元素值3, ... , 元素值n};
例如:
int [ ] a1 = {23,-9,38,8,65}; double b1[] = {1.23, -90.1, 3.82, 8.0 ,65.2};
(2)通过下标赋值的方式
数组元素在数组中按照一定的顺序排列编号,首元素的编号规定为0,其他元素顺序编号。元素编号也称为下标或索引。因此,数组下标依次为0,1,2,3,…。数组中的每个元素可以通过下标进行访问,例如a1[0]表示数组的第一个元素。
通过下标赋值的语法格式如下。
数组名[下标] = 元素值;
例如:
a1[0] = 13; a1[1] = 14; a1[2] = 15; a1[3] = 16; ...
下面通过一个实例,让读者对数组的应用有进一步的了解。
【例2-26】一维数组应用举例。
功能实现: 在数组中存放4位同学的成绩,计算这4位同学的总成绩和平均成绩。
程序执行结果如图2-25所示。
图2-25 例2-26的执行结果
在Java语言中,数组是一种引用类型,拥有方法和属性。例如在例子中出现的length就是它的一个属性,利用该属性可以获得数组的长度。
JDK 1.5引进了一种新的循环类型,被称为For-Each循环或者增强型循环。它能在不使用下标的情况下遍历数组。语法格式如下:
其中type是数组元素类型,element是一个局部变量,array是要遍历的数组。下面通过例子具体演示增强for循环的用法。
【例2-27】使用增强for循环遍历数组中的元素。
程序运行结果如图2-26所示。
图2-26 例2-27的执行结果
在介绍数组基本概念时,已经给出这样的结论:数组元素可以是Java语言允许的任何数据类型。当数组元素的类型是数组时,就构成多维数组。例如,二维数组实际上就是每个数组元素是一个一维数组的一维数组。
这里以二维数组为例。声明多维数组的语法格式有以下两种方式:
例如:
在声明数组时,不能指定数组的长度,否则编译无法通过。
声明数组仅为数组指定数组名和数组元素的类型,并没有为元素分配实际的存储空间。需要为数组分配空间才能使用。
分配空间就是告诉计算机在内存中为它分配几个连续的位置存储数据。在Java中使用new关键字为数组分配空间。为多维数组(这里以三维数组为例)分配空间的语法格式如下:
数组名 = new 数据类型[数组长度1] [数组长度2] [数组长度3];
其中,数组长度1是第一维数组元素个数,数组长度2是第二维数组元素个数,数组长度3是第三维数组元素个数。
例如:
也可以在声明数组时,就为它分配空间,语法格式如下:
数据类型 数组名[ ] [ ] [ ] = new 数据类型[数组长度1] [数组长度2] [数组长度3];
例如:
int array3[][][] = new int[2] [2] [3];
该数组有2*2*3个元素,各元素在内存中的存储情况如表2-9所示。
表2-9 三维数组array3的元素存储情况
初始化多维数组是指分别为多维数组中的每个元素赋值。可以通过以下两种方法进行数组的初始化。
(1)直接指定初值的方式
在声明一个多维数组的同时将数组元素的初值依次写入赋值号后的一对花括号内,给这个数组的所有元素赋初始值。这样,Java编译器可通过初值的个数确定数组元素的个数,为它分配足够的存储空间并将这些值写入相应的存储单元。
这里以二维数组为例,其语法格式如下:
数据类型 数组名[ ] [ ] = {数组1, 数组2 };
例如:
int matrix2[][] = {{1, 2, 3}, {4,5,6}};
(2)通过下标赋值的方式
例如:
int matrix3[][] = new int[2][3]; matrix3 [0] [0] = 0; matrix3 [0] [1] = 1; matrix3 [0] [2] = 2; matrix3 [1] [0] = 3; matrix3 [1] [1] = 4; matrix3 [1] [2] = 5;
以二维数组为例,可用length()方法测定二维数组的长度,即元素的个数。只不过使用“数组名.length”得到的是二维数组的行数,而使用“数组名[i].length”得到的是该行的列数。
下面通过一个实例对上述内容进行进一步的解释,首先声明一个二维数组
int[ ][ ] arr1={{3, -9},{8,0},{11,9} };
则arr1.length的返回值是3,表示数组arr1有3行。而arr1[1].length的返回值是2,表示arr1[1]对应的行(第二行)有2个元素。
【例2-28】声明并初始化一个二维数组,然后输出该数组中各元素的值。
程序执行结果如图2-27所示。
图2-27 例2-28的执行结果
本章首先对Java语言的标识符、关键字和数据类型进行了介绍,在此基础上讲解了变量、常量、运算符、表达式等基础知识。然后,重点介绍了结构化程序设计的三种结构(顺序结构、分支结构和循环结构)和流程控制语句。其中,分支语句重点介绍了if语句、if-else语句和switch语句,循环语句重点介绍了while语句、do-while语句和for语句。最后,介绍了一维数组和多维数组的声明、分配空间、初始化的方法,并通过一些简单的实例介绍了数组的应用方法和技巧。通过对本章内容的学习,读者可以对Java语言的语法规则以及程序流程控制有比较深入的理解,并能够和数组结合在一起开发一些Java应用程序。
练习1: 编写一个程序,打印100~200的素数,要求每行按10个数(数与数之间有一个空格隔开)的形式对其输出。
练习2: 打印出所有的“水仙花数”。“水仙花数”是指一个三位数,其各位数字的立方和等于该数本身。例如153是一个“水仙花数”,因为153 = 1 3 +5 3 +3 3 。
练习3: 通过键盘输入年份和月份,根据输入的年份和月份判断该月份的天数,并输出结果。
练习4: 计算企业应发放奖金总数,奖金发放标准如下。
企业发放的奖金根据利润进行提成,利润低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;20万到40万之间时,高于20万元的部分可提成5%;40万到60万之间时高于40万元的部分可提成3%;60万到100万之间时,高于60万元的部分可提成1.5%,高于100万元时,超过100万元的部分按1%提成。从键盘输入当月利润,求应发放奖金总数,并输出结果。
练习5: 编写程序,找出4×5矩阵中值最大的元素,显示其值以及其所在的行号和列号。