- Java语言GUI程序设计
- 赵满来
- 5044字
- 2025-02-28 01:27:57
4.1 类的方法
如2.3节所述,方法是类定义的主要组成部分,是对类中域变量进行操作的程序单元。Java语言的方法必须在类中,不能有位于类之外的方法。
4.1.1 方法的定义
Java语言中方法定义的一般格式是:
[修饰符] 返回值类型 方法名(类型 参数名1, 类型 参数名2, …) { // 这对大括号内是方法体 一些变量声明语句 一些执行语句 return 表达式; }
其中,修饰符是可选的,可以限定该方法的可见范围或其他特性,随后介绍。
(1)返回值类型:方法完成对数据的计算和处理等操作之后获得了处理结果,可能需要将结果返回给请求这个方法的对象,返回值类型告诉该方法会返回什么类型的数据。方法的具体返回值就是return语句后表达式的值。如果没有返回数据,则用void关键字说明,此时return语句后没有表达式部分,或者干脆不要return语句。例如,前面例题中自动生成的事件处理方法(如addNumber)就没有返回值,其返回值类型为void,方法体中没有return语句。
(2)方法名:为该方法取的在该类中的唯一名字,符合Java标识符规则。对象等通过方法名请求该方法。一般方法名的第一个字母小写。
(3)参数表:在方法名后的一对小括号内定义,声明了每个参数的类型和参数名。如果有多个参数,使用逗号“,”分隔。
从Java 5开始还可以在方法参数表中定义一个可变元参数,即参数个数不确定而参数类型确定的参数表。可变元参数必须在整个参数表的最后,形式是:类型…参数名。例如:
int calcAvg(float avg, int...values) { // … return avg; }
(4)变量声明:方法体中声明的变量一般是计算过程中使用的一些中间变量,它的使用范围是在该方法体内,出了这个方法的最后一个大括号“}”,这些变量就不能使用了,因此叫作局部变量。
事实上,在任何一个语句块内定义的变量都是局部变量,出了这个语句块范围就不能使用。局部变量必须在使用之前初始化,如果没有赋初值则不能获得一个默认值,而是保存有一个随机值。
Java 10中可以采用“var变量名=初值;”的方式定义局部变量,Java 10会根据初值的类型推断此变量的类型,此后在其作用域范围内会保持其类型。例如,语句“var x=10;”则使变量x为int类型,此后若执行语句“x=10.1;”会发生类型不兼容错误。应注意,在定义字段变量,方法的参数及方法返回值的类型时,不能使用保留字var。
(5)执行语句:方法体中的执行语句对类的域变量、方法体中的局部变量及参数变量进行操作,可以是赋值语句、流程控制语句以及调用其他方法的语句等。方法体内可以直接使用它本身定义的局部变量、该方法的参数变量以及方法所在类中的域变量。
例如,例3.4四则运算计算器程序中“=”按钮的鼠标单击事件处理方法(见图4.1),方法名是calcOpr;void表示该方法无返回值;参数表中只有一个参数——参数类型是java.awt.event.ActionEvent类,参数名是evt;118行称为方法头。该方法体中定义了6个局部变量,strNum1、strNum2和opr是String类型,num1、num2和result是int类型;其中,strNum1和strNum2的作用范围是整个calcOpr方法,其余4个局部变量作用范围是122~143行的if语句块;使用if、if-else-if语句、赋值语句、jTextFieldResult对象的setText和getText等方法调用以及jComboBoxOpr对象的getSelectedItem方法调用等执行语句根据用户选择的运算种类获得运算结果,并将结果使用文本字段组件显示出来。

图4.1 例3.4程序“=”按钮事件处理方法
4.1.2 方法的调用及参数传递
对类的对象中方法的执行请求就是对该方法的调用。请求执行方法的对象是调用者,而被请求执行的方法是被调用方法。例如,在book.ifdemos.ArithmeticClac类的calcOpr方法中调用了jTextFieldNum1对象的getText方法(见图4.1第120行语句),方法调用者就是calcOpr方法所属的对象;又如,chap03项目的ArithmeticClac.java文件中的以下程序段:

在第53行调用了calcOpr方法,方法调用者和被调用方法属于同一个对象。
方法调用的一般形式是:
对象名.方法名(参数表);
(1)对象名:该对象名是被调用方法所属的对象。例如,图4.1第120行语句的对象名jTextFieldNum1指明调用的getText方法是第一个文本字段组件(对象)的方法,而不是第二个文本字段组件或显示运算结果文本字段组件的getText方法。如果被调用方法与调用者是同一个对象,则可以省略方法名前面的对象名,例如上段代码的第53行。
(2)参数表:此参数表是一些表达式、常量或变量组成的列表,每个参数都有一个确定值,与方法定义时的参数表所列的参数的类型、次序和个数一一对应,多个参数之间用逗号“,”分隔。方法定义时的参数称为形式参数,简称形参,参数表也叫形参表;方法调用时给出的参数称为实际参数,简称实参,参数表也叫实参表。例如,如图4.1所示calcOpr方法定义的形参表只有一个形参evt,形参类型是java.awt.event.ActionEvent类,上段代码第53行调用calcOpr方法时给出的实参是变量evt,evt是第52行声明的java.awt.event.ActionEvent类的对象。
在调用具有可变元参数的方法时,实参也要与形参对应。例如,前面定义的方法calcAvg,调用时第一个实参必须是float类型的,后面是多个int类型的实参,如“calcAvg(3.2f, 3, 5, 10, 40);”是正确的,但是“calcAvg(3.2f, 3, 5, 10.0, 40);”则是错误的,因为第4个实参类型并不是int。
Java程序中方法的调用是按值调用,即调用者将实参的值传递给被调方法对应的形参。如果方法的参数是基本数据类型,则实参向形参传值的过程十分直观,就是形参复制了实参的值,在方法中对形参的改变不会影响实参。
例4.1 编写方法swap交换两个int型变量的值,并显示交换前后形参和实参的值。
解:按照以下步骤操作。
(1)在NetBeans IDE中创建一个Java应用程序项目,项目名称为chap04。
(2)创建包book.methoddemos,并在该包中新建JFrame窗体,类名为VarSwap。
(3)设计如图4.2所示的界面,其中,文本字段组件jTextFieldVar1和jTextFieldVar2分别用于输入两个整数,jTextFieldVar1A和jTextFieldVar2A分别显示调用交换方法swap后实参的值,jTextFieldArg1和jTextFieldArg2分别显示调用交换方法swap前形参的值,jTextFieldArg1A和jTextFieldArg2A分别显示调用交换方法swap后形参的值。这8个文本字段组件的columns属性值设置为8,除jTextFieldVar1和jTextFieldVar2之外的其他6个editable属性值都设置为false。其余组件按照图4.2导航器窗口中内容命名。

图4.2 例4.1程序界面设计视图
(4)在VarSwap类中设计方法swap,源代码如下。

(5)为“交换”按钮jButtonSwap组件添加actionPerformed事件处理方法swapVars,该方法源代码如下。

(6)为“清除”按钮jButtonClear组件添加actionPerformed事件处理方法clearAll,该方法源代码如下。
private void clearAll(java.awt.event.ActionEvent evt) { // TODO add your handling code here: jTextFieldVar1.setText(""); jTextFieldVar2.setText(""); jTextFieldVar1A.setText(""); jTextFieldVar2A.setText(""); jTextFieldArg1.setText(""); jTextFieldArg2.setText(""); jTextFieldArg1A.setText(""); jTextFieldArg2A.setText(""); }
完成上述步骤后运行程序,在var1和var2右边的文本框中输入两个整数后单击“交换”按钮,发现两个实参变量在调用交换方法swap后仍然保持原值不变,而两个形参的值则在执行交换操作后确实进行了交换(见图4.3)。

图4.3 整数类型实参不受调用方法的影响
如果方法调用所传递的参数是引用类型的变量,则将实参的引用值复制给形参的引用变量。此时,相对应的实参和形参链接到同一个对象,在方法中改变了形参引用对象时,其实就是改变了实参引用的对象。
例4.2 TwoInt类中有两个int类型的属性,编写方法swap交换该类对象的两个属性的值,并显示交换前后形参引用对象和实参引用对象的属性值。
解:按照以下步骤操作。
(1)右击book.methoddemos包中VarSwap.java文件名,在快捷菜单中单击“复制”菜单项。右击book.methoddemos包名,在快捷菜单中单击“粘贴”|“重构复制”菜单项,新名称输入“ObjSwap”,单击“重构”按钮。
(2)将“var 1:”“var2:”“arg 1:”“arg2:”标签上的文字(text属性)修改为“var.var1:”“var.var2:”“var.arg1:”“var.arg2:”。
(3)右击book.methoddemos包名,在快捷菜单中单击“新建”|“Java类”菜单项,类名输入“TwoInt”,单击“完成”按钮。
(4)在TwoInt类中添加两个整型域,完成后该类的源代码如下(空行和注释部分省略)。
package book.methoddemos; public class TwoInt { int var1; int var2; }
(5)修改ObjSwap类中的swap方法。完成修改后该方法的源代码如下。
void swap(TwoInt arg) { // 显示交换前形参的值 jTextFieldArg1.setText(arg.var1+""); jTextFieldArg2.setText(arg.var2+""); // 执行交换 int t = arg.var1; arg.var1 = arg.var2; arg.var2 = t ; // 显示交换后形参的值 jTextFieldArg1A.setText(arg.var1+""); jTextFieldArg2A.setText(arg.var2+""); }
(6)修改ObjSwap类中的swapVars方法。完成修改后该方法的源代码如下。

完成上述步骤后运行ObjSwap.java文件,在var.var1和var.var2右边的文本框中输入两个整数后单击“交换”按钮,发现实参变量所引用的对象在调用交换方法swap后发生改变——对象的两个属性值发生了交换,形参所引用的对象在执行交换操作后两个属性值也进行了交换(见图4.4)。

图4.4 例4.2程序运行结果——参数引用的对象发生改变
如果例4.1中的swap方法修改为swap(javax.swing.JTextField arg1, javax.swing.JTextField arg2),在事件处理方法swapVars中调用swap(jTextFieldVar1, jTextFieldVar2),即传递接收用户输入的两个文本字段组件的引用,则arg1也链接到第一个文本字段组件,arg2链接到第二个文本字段组件。在swap方法体内交换arg1和arg2的值,那么只是arg1链接到第二个文本字段组件,arg2链接到第一个文本字段组件,实参变量jTextFieldVar1仍然链接到第一个文本字段组件,jTextFieldVar2仍然链接到第二个文本字段组件。显然,在被调方法中无法改变调用者实参引用。要在被调方法中改变实参引用对象的状态,可以将该对象引用传递给形参,使形参也链接到这个对象,之后就可以通过形参引用修改该对象的状态。
4.1.3 方法的重载
在例4.1和例4.2中看到了两个同名方法swap,其作用都是交换两个变量的值,但是由于是在两个不同的类中,有不同的作用范围,因此方法名字相同并没有什么不妥。有时候会遇到在同一个类中作用相同的两个或多个方法的情形,如果它们也可以使用相同的方法名,则可以减轻该类的使用者的记忆负担。例如,需要设计方法实施两个数相加,但是这两个数可能是整数,也可能是浮点数,甚至是两个byte类型数;又如,还需要设计方法实施两个数、三个数、四个数相加。为此,Java语言提供了方法重载机制,可以定义多个具有相同方法名但参数表不同的方法——可以是参数个数相同但类型不同,也可以是参数个数不同。重载的方法应该有相同的返回值类型。
1. 参数个数相同类型不同的重载方法
例如,String类中以下valueOf方法分别使用不同类型的一个参数,而形成方法重载。
public static String valueOf(boolean b):返回boolean参数的字符串表示形式。
public static String valueOf(char c):返回char参数的字符串表示形式。
public static String valueOf(int i):返回int参数的字符串表示形式。
public static String valueOf(long l):返回long参数的字符串表示形式。
public static String valueOf(float f):返回float参数的字符串表示形式。
public static String valueOf(double d):返回double参数的字符串表示形式。
又如,String类中以下两个indexOf方法参数个数相同,但第一个参数类型不同,而第二个参数类型相同,从而形成方法重载。
indexOf(int ch, int fromIndex):返回在该字符串中指定字符ch第一次出现处的索引,从指定的索引fromIndex开始搜索。
indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
再如,以下OverloadDemo1类中的三个addNums方法都有两个参数,但参数类型不同从而形成重载。

2. 参数个数不同的重载方法
例如,String类中以下substring方法是参数个数不同的两个重载方法。
public String substring(int beginIndex)返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引beginIndex处的字符开始,直到此字符串末尾。
public String substring(int beginIndex, int endIndex)返回一个新字符串,它是此字符串的一个子字符串。该子字符串是从指定的beginIndex处开始,直到索引endIndex − 1处的字符序列。因此,该子字符串的长度为endIndex−beginIndex。
再如,在OverloadDemo1类中设计以下两个方法可以查找计算2~n和m~n自然数中的素数。其中,第一个方法只需给出结束的那个自然数,而第二个方法则需要给出起始和结束的两个自然数,由于方法参数个数不同而方法名相同构成重载。

4.1.4 构造方法
类的对象中包含域变量,创建了对象就需要对它的域变量进行初始化,尽管没有明确初始化的域变量会有默认初值,但是大部分情况下默认初值是不能满足需求的。Java语言允许且鼓励为类定义构造方法以实现对类对象的初始化。
构造方法与一般方法的结构一样,但构造方法名与类名相同,不能有返回值,也无返回值类型声明,参数表可以为空或定义多个参数。根据需要可以为一个类定义多个重载的构造方法。在创建类的对象时自动调用它的构造方法,跟在new操作符之后的就是相应构造方法的调用形式——给出对应的实参表。
例4.3 为例2.7完成的用户登录程序界面(见图2.58)编写用户信息类,其中,用户信息包括用户名、密码和用户身份三个方面的内容。
解:按照以下步骤操作。
(1)右击book.methoddemos包名,在快捷菜单中单击“新建”|“Java类”菜单项,类名输入“User”,单击“完成”按钮。
(2)在生成的User类体中添加三个域变量,输入以下代码。
String name; String password; int job;
(3)单击主菜单中的“源”|“插入代码”|“构造函数”菜单项,在“生成构造函数”对话框中单击“全选”按钮,然后单击“生成”按钮(见图4.5)。
(4)在“源”代码编辑窗口右击,在快捷菜单中单击“插入代码”|“getter和setter”菜单项,在“生成getter和setter”对话框中单击“全选”按钮,单击“封装字段”前面的复选框选取该项,然后单击“生成”按钮(见图4.6)。

图4.5 “生成构造函数”对话框

图4.6 “生成getter和setter”对话框
(5)在“源”代码编辑窗口右击,在快捷菜单中单击“插入代码”|“equals ()和hashCode()”菜单项,在“生成equals ()和hashCode()”对话框中单击两个“全选”按钮,然后单击“生成”按钮(见图4.7)。

图4.7 “生成equals ()和hashCode()”对话框
(6)单击主菜单中的“源”|“插入代码”|“构造函数”菜单项,在“生成构造函数”对话框中单击name : String前面的复选框选择该项,然后单击“生成”按钮。
完成上述操作步骤后即生成了User类的代码。该类的源代码如程序清单4.1所示(省略空行和注释),可以看到有两个重载的构造方法。
程序清单4.1:

