Java程序设计教程
上QQ阅读APP看书,第一时间看更新

2.2 类的方法

方法在有些程序设计语言中也被称为函数或过程,它是类的一个重要成员,是实现某种数据处理操作的一段程序。使用方法处理数据实现程序功能,最大的好处就是便于代码的重复使用。选择结构和循环结构程序设计方法是类方法设计的主要手段。方法可以根据调用语句传递过来的参数经过选择结构或循环结构程序的分析、加工和处理,最终将结果返回给调用语句或直接输出。

2.2.1 数据的输入和输出

数据的输入和输出是方法设计的两个端点,熟练掌握相关概念和编程技巧是程序设计中至关重要的一环。

任何一个应用程序都需要从外界接收数据,这些数据被应用程序加工、处理后必然需要进行保存、返回或直接显示出来。应用程序从外界接收数据的途径有许多,常用的有用户键盘输入、从文件或数据库中读取、从网络中读取等。数据的输出方式常用的有输出到控制台窗格、输出到文件或数据库、输出到打印机等。这里重点介绍用户键盘输入和输出到控制台的相关知识和编程技巧,其他方式将在后续章节中逐步介绍。

1.键盘输入的接收

Java使用预定义的Scanner类对象来实现用户键盘输入数据的接收,它提供的若干个方法可以实现单个或多个数据的接收。接收到的数据可以是字符串,也可以是int、float、double等类型。Scanner类提供的常用方法见表2-1。

表2-1 Scanner类的常用方法

Scanner类隶属于java.util包,所以若要在程序中使用该类,就需要通过import语句将其导入到程序中,而且import语句一定要写在主类的上方。例如:

又如:

使用nextInt()、nextDouble()或nextFloat()方法接收用户输入时,需要注意对应的数据类型不能出错。例如,不能用nextInt()方法接收用户输入的12.34,这将因无法将12.34隐式地转换成int类型而导致错误。为避免此类错误,Scanner类专门提供了用于检测用户输入数据类型的hasNextInt()、hasNextDouble()和hasNextFloat()方法,当输入数据不符合相应类型时方法的返回值为false。一般在调用nextInt()、nextDouble()或nextFloat()方法前,可先调用hasNextInt()、hasDouble()或hasFloat()方法进行检测,若返回false则提示用户输入错误,确认数据类型无误后再调用Scanner类对象的nextInt()、nextDouble()或nextFloat()方法接收用户输入的数据。

2.将数据输出到控制台

使用Java预定义的System.out.print()或System.out.println()方法可将字符串数据输出到输出窗口。这两个方法的区别在于输出内容的结尾是否包含一个回车符。例如,下列4条输出语句执行后,如图2-2所示,“abcd”和“1234”会出现在同一行,而“Welcome”和“5678”会各自独占一行。

图2-2 代码执行结果

思考:若第2行语句中没有“\r”,会得到怎样的输出结果?

2.2.2 选择结构程序设计

所谓“选择结构”是指程序可以根据一定的条件有选择地执行某一程序段,对不同的问题采用不同的处理方法。最简单的选择结构可以概括成“如果A,则B,否则C”,显然A是一个条件,而B和C是处理问题的方法,也就是说如果条件A成立,则按方案B执行,否则按方案C执行。Java提供了多种形式的语法格式来实现选择结构。

1.if…else语句

if语句是程序设计中基本的选择语句,if语句的语法格式如下:

说明:

1)条件表达式可以是关系表达式、布尔表达式或布尔常量值真(true)与假(false),当条件表达式的值为真时,程序执行语句序列1,否则执行语句序列2。

2)语句序列1和语句序列2可以是单行语句,也可以是语句块。如果语句序列中为单行语句,大括号可以省略。

3)else子句为可选部分,可根据实际情况决定是否需要该部分。

if…else语句执行时,首先会计算条件表达式的值,若该值为真(true)则执行if后的语句序列,为假(false)则执行else后的语句序列。

【演练2-1】设计一个应用程序,程序启动后提示用户输入一个不为零的整数。若用户输入的数据符合要求,则显示该数的平方值,否则显示“只能输入一个整数”或“数据不能为零”。程序运行结果如图2-3所示。

图2-3 程序运行结果

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_1,并创建主类和主方法。在主类的上方添加用于导入Scanner类的import语句。

2)编写如下所示的主方法代码。

2.if…else if语句

使用if…else if语句可以进行多条件判断,故也称为“多条件if语句”。它适合用于对3种或3种以上的情况进行判断的选择结构。if…else if语句的语法格式如下:

例如,挑选男子篮球队员。要求身高要不低于1.75m,体重应为75~90kg,判断过程如下:

3.if语句的嵌套

所谓“if语句的嵌套”是指在一个if选择结构程序段中包含另一个if选择结构。例如,下列代码描述了使用if语句的嵌套实现通过匹配用户名和密码管理用户登录的情形。

4.条件赋值表达式

条件赋值表达式可以看作是关系表达式或布尔表达式和赋值表达式的组合,它可根据关系表达式或布尔表达式的值(true或false)返回不同的结果。其语法格式如下:

在条件赋值表达式运算时,首先计算关系表达式或布尔表达式的值,如果为true,则将表达式1的值赋值给变量,否则将表达式2的值赋值给变量。例如:

由于x的值小于5,故语句执行时将返回第二个表达式(x*9)的值给变量y。

条件赋值表达式也可以嵌套使用,例如:

上例中“x>y”为关系表达式,表达式1为“大于”,表达式2本身又是一个完整的条件赋值表达式“(x==y ? "等于" : "小于")”,含义为:若x等于y返回“等于”,否则返回“小于”。显然,通过条件赋值表达式的嵌套,也可以实现多分支的选择判断。

5.switch语句

如果在多重分支的情况下,虽然可以使用if…else if语句或if语句的嵌套实现,但层次较多会使程序的结构变得较为复杂,降低了代码的可读性。使用专门用于多重分支选择的switch语句,则可以使多重分支选择结构的设计更加方便,层次更加清晰。

(1)switch语句的语法格式

switch语句的语法格式如下:

需要注意以下几点:

1)控制表达式所允许的数据类型为整数类型(byte、short、int等)、字符类型(char)、字符串类型(String)。

2)各个case语句后的常量表达式的数据类型与控制表达式的类型相同,或能够隐式转换为控制表达式的类型。

3)如果case标签后含有语句序列,则语句序列最后必须使用break语句,以便跳出switch结构。缺少break语句将会导致多个case语句的连续执行。

(2)switch语句的执行顺序

switch语句根据控制表达式的值选择要执行的语句分支,其执行顺序如下。

1)控制表达式求值。

2)如果case标签后的常量表达式的值等于控制表达式的值,则执行其后的内嵌语句。

3)如果没有常量表达式等于控制语句的值,则执行default标签后的内嵌语句。

4)如果控制表达式的值不满足case标签,并且没有default标签,则跳出switch语句而执行后续代码。

【演练2-2】使用switch语句设计一个程序,程序启动后要求用户输入自己的身份(“教师”“职工”或“学生”,不支持其他),按〈Enter〉键后,根据用户身份进一步提示输入“职称”(教师)、“工龄”(职工)或“班级”(学生)数据。输入完毕后程序能在控制台窗格中显示类似“身份:教师,职称:副教授”的信息。程序运行结果如图2-4所示。

图2-4 程序运行结果

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_2,并创建主类和主方法。在主类的上方添加用于导入Scanner类的import语句。

2)编写如下所示的主方法代码。

6.在switch中共享处理语句

在switch语句中,多个case标记可以共享同一处理语句序列。例如,描述10分制学生成绩时,设9分及以上等级值为“优”,7分及以上为“良”,6分为“及格”,6分以下为“不及格”。分数转等级的实现代码如下:

2.2.3 循环结构程序设计

循环是在指定的条件下重复执行某些语句的运行方式。例如,计算学生总分时没有必要对每个学生都编写一个计算公式,可以在循环结构中使用相同的计算方法不同的数据来简化程序的设计。

Java中常用的循环结构语句有for、while、do…while和foreach。本节重点介绍前3个语句,foreach语句将在后续章节中介绍。

1.for循环

for循环常常用于已知循环次数的情况,故也称为“定次循环”。使用该循环时,先测试是否满足执行循环的条件,若满足条件,则进入循环执行循环体语句,否则退出该循环。for循环语句的语法格式如下:

for循环的执行顺序如下。

1)首次进入循环执行到for语句时对循环变量赋予初始值。

2)执行循环的条件为一个条件表达式,即每次执行到for语句时都会判断该表达式是否成立:如果成立(表达式的值为true),则执行循环语句序列(进入循环体);否则循环结束,执行循环语句的后续语句。

3)满足循环条件开始执行循环体语句前,for语句会根据步长增量表达式改变循环变量值,一般通过递增或递减来实现。

【演练2-3】下列代码表示当用户输入一个整数范围后,按〈Enter〉键程序将在控制台窗格中输出所有偶数和奇数的和。程序运行结果如图2-5所示。

图2-5 程序运行结果

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_3,并创建主类和主方法。在主类的上方添加用于导入Scanner类的import语句。

2)编写如下所示的主方法代码。

说明:for循环的循环变量i的值从用户输入的范围起始值(输入的第1个值)开始,到范围结束值(输入的第2个值)结束,每次循环i值加1。这使得程序能对范围内的所有数据实现一个遍历。

思考:如果用户输入的第1个数大于第2个数时会出现什么情况?怎样修改代码才能使程序在这种情况下仍能正常运行?

2.while循环

在实际应用中常会遇到一些不定次循环的情况。例如,统计全班学生的成绩时,不同班级的学生人数可能是不同的,这就意味着循环的次数在设计程序时无法确定,能确定的只是某条件被满足(如后面不再有任何学生了)。此时,使用while循环最为合适。

while循环执行时首先会判断某个条件是否满足,若满足条件则进入循环,执行循环体语句,否则退出循环。while循环语句的语法格式如下:

条件表达式是每次进入循环前进行判断的条件,当条件表达式的值为true时,执行循环,否则退出循环。

while循环在使用时应注意以下几个问题。

1)条件表达式是一个关系表达式或逻辑表达式,其运算结果为true或false。

2)作为循环体的语句序列可以是多条语句,也可以是一条语句。如果是一条语句,大括号可以省略。

3)循环语句序列中一般应包含能改变条件表达式或强制退出的语句(如break、return等),以避免程序陷入永远无法结束的“死循环”。

例如,下列代码将产生一系列的1~9的随机整数,当产生的随机整数正好为9时结束循环。

3.do…while循环

do…while循环非常类似于while循环。在一般情况下,二者可以相互转换使用。它们之间的差别在于while循环的测试条件在每一次循环开始时执行,而do…while循环的测试条件在每一次循环体结束时进行判断。do…while语法的一般格式如下:

当程序执行到do语句后,无条件开始执行循环体中的语句序列,执行到while语句时再对条件表达式进行测试。若条件表达式的值为true,则返回do语句重复循环,否则退出循环执行while语句后面的语句。例如,前面的例子要使用do…while结构实现时,代码可按如下所示修改。

4.循环的嵌套

若一个循环结构中包含有另一个循环,则称为“循环的嵌套”,也称为多重循环结构。循环嵌套的层数理论上无限制。在多重循环结构中,3种循环语句(for循环、while循环和do…while循环)可以互相嵌套。需要注意的是,使用循环嵌套结构时,内循环必须完全包含在外循环内部,不能出现交叉。

【演练2-4】使用循环嵌套,实现在控制台窗格中输出一个九九乘法表。程序运行结果如图2-6所示。

图2-6 for循环的嵌套示例

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_4,并创建主类和主方法。

2)编写如下所示的主方法代码。

5.在循环中的跳转语句

循环中的跳转语句可以实现循环执行过程中的流程转移。Java中可用于循环跳转的语句有break、continue和return。

break语句执行后无论是否满足循环终止条件都可以无条件地结束当前正在执行的循环。

continue语句执行后并不终止当前正在执行的循环,而是忽略continue后面的语句返回循环的开始部分,执行下一次循环。

return语句执行后会忽略return后面的语句,从当前方法中返回。若当前方法为主方法,则会导致程序运行结束。

【演练2-5】设计一个应用程序,程序启动后要求用户输入密码。若输入“123456”,则在控制台窗格中显示“Welcome”并结束运行。若输入不正确,则在控制台窗格中显示“密码错,你还有x次机会”。若3次输入不正确,则在控制台窗格中显示“bye!”并结束运行。程序运行结果如图2-7所示。

图2-7 程序运行结果

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_5,并创建主类和主方法。在主类的上方添加用于导入Scanner类的import语句。

2)编写如下所示的主方法代码。

2.2.4 方法的声明和调用

声明方法指的是在类中定义方法的名称、规定可被谁调用、从调用语句接收怎样的参数以及是否需要将加工好的数据返回给调用语句等。方法可以被同一个类或不同类中的语句调用。调用方法时可以根据需要,将某些数据作为参数传递给方法,并从方法接收数据处理的结果。也可以仅调用方法去执行某个操作,而无须返回操作结果。

1.方法的声明

在类中声明方法的语法格式如下:

(1)访问修饰符

访问修饰符是可选项,由一个或多个Java关键字组成,用来说明方法的一些特征。修饰符包括访问控制符、静态修饰符、抽象修饰符、最终修饰符、本地修饰符等。修饰符及其说明见表2-2。

表2-2 方法的修饰符及说明

(2)返回值类型

方法的返回值类型可以是Java支持的任何预定义(如int、double、String、数组等)或自定义数据类型。方法的返回值类型使用void标记时,表示该方法没有返回值。

(3)方法名和参数列表

方法名和变量名的命名习惯相同。要求使用能表明方法功能的、全部字符都小写的英文单词或众所周知的缩写命名方法,如area、count等。若名称中包含多个单词,要求第一个单词全部小写,第二个及以后的单词首字母大写,如checkUser、isPass等。

方法声明语句中的参数列表是一个可选项,表示从调用语句接收的数据存储在怎样的变量中。例如,Java应用程序的主方法声明语句public static void main(String[] args)中的args,表示从调用语句接收到的数据保存在字符串数组类型的变量args中。也就是说参数由数据类型和参数名组成。若列表中存在多个参数,则各参数之间要使用逗号分隔。

(4)方法体语句

方法体语句由实现方法具体功能的一系列代码组成。这些代码被封装在方法体内,使方法对用户而言变成了一个“黑匣子”,用户只需要知道方法可以干什么,而不必知道具体是怎样实现的。若方法的声明语句中的返回值类型不是void,则方法体内至少要有一个return语句按声明语句中返回值类型将处理后的数据返回给调用语句。

2.方法的调用

在程序中调用方法的语法格式如下:

方法一般需要通过类的对象来调用。例如,Java预定义的Scanner类中的nextInt()方法用于读取用户通过键盘输入的一个整数。调用时首先需要创建一个Scanner类的对象,而后才能通过该对象调用nextInt()方法。

需要说明如下内容:

1)如果方法位于本类中则可直接通过方法名调用方法,而不必通过类对象调用(调用语句中可省略“类对象名”)。

2)使用参数传递数据时需要注意,若存在多个参数则各参数之间要使用逗号“,”分隔。调用语句中使用的参数称为实际参数,简称为实参;方法声明语句中的参数用于接收实参值,称为形式参数,简称为形参。

3)在未调用方法时,形参并不占用存储单元。只有在发生方法调用时,系统才会给方法中的形参分配内存单元。在调用结束后,形参所占的内存单元也自动释放。

4)实参可以是常量、变量或表达式;形参必须是声明的变量,且必须指定类型。

5)实参列表中参数的数量、类型和顺序必须与形参列表中的参数完全对应,否则将发生异常。

【演练2-6】创建一个Java项目,要求在主类中创建一个能计算任意三角函数的方法getValue()。要求调用语句向方法传递表0示三角函数名(sin、cos或tan)和角度值的两个参数,方法根据这两个参数返回一个double类型的三角函数值。程序运行结果如图2-8所示。

图2-8 程序运行结果

程序设计步骤如下。

1)首先在Eclipse中新建一个Java应用程序项目YL2_6,并创建主类和主方法。

2)编写如下所示的主类代码。

思考:使用上述方法计算tan 90°或cot 0°时会出现怎样的结果?为什么?怎样修正?

需要说明的是,上例中声明getValue()方法时使用了private访问控制符,说明该方法只能在本类中被调用。使用static静态修饰符说明该方法是一个静态方法。这是因为主方法main()是一个静态方法,在静态方法中只能调用其他静态方法,而不能调用非静态方法。

2.2.5 方法的重载

Java允许在同一个类中声明多个具有不同参数列表(不同参数数量、不同参数数据类型、不同参数顺序)的同名方法,调用方法时程序能根据调用语句传递参数的情况自动选择相应的方法,这种处理方式称为“方法重载”。

使用方法重载时需要注意以下两点要求。

1)重载的方法名称必须相同。

2)重载的方法,其形参个数或类型必须有所不同。也就是说,类中不能出现完全相同的两个方法,否则将出现编译错误。

声明了重载方法后,当调用具有重载的方法时,系统会根据参数的个数、类型或顺序的不同寻找最匹配的方法予以调用。

下列代码是一个通过参数数量不同来实现方法重载的示例。在Rectangle(矩形)类中定义了同名的两个方法calculate(计算),若从调用语句接收两个float类型参数时,返回矩形面积值,若从调用语句接收3个float类型参数时返回柱体的体积值。

2.2.6 方法调用中的参数传递

调用带参数的方法时,程序能自动实现实参为形参赋值的操作。由于调用方法时参数的传递通常是通过实参变量为形参变量赋值来实现的,而Java中数据又分为基本类型和引用类型两种,所以方法的参数传递形式也对应有传值和传址两种方式。

1.传值方式

使用基本类型(int、double、char等)的实参变量传递数据时,形参接收到的是实参变量值的副本,两个数据存储在不同的内存地址中,所以在方法中修改了形参变量的值对实参的值不会有任何影响。

2.传址方式

使用引用类型(String、数组、类的对象等)的实参变量传递数据时,形参将接收到实参变量传递来的实参值所在的内存地址值,所以修改形参会导致实参值的同步改变。

传值和传址两种方式的示意如图2-9所示。

图2-9 传值和传址方式的比较