
4.3 方法设计
在Java程序中,方法是类为用户提供的接口,用户使用方法操作对象。前面介绍了方法声明的格式,本节将学习如何设计方法、方法的重载、构造方法、方法的参数传递等。
4.3.1 如何设计方法
在类的设计中,方法的设计尤为重要。设计方法包括方法的返回值、参数以及方法的实现等。
1.方法的返回值
方法的返回值是方法调用结束后返回给调用者的数据。很多方法需要返回一个数据,这时要指定方法返回值,具体是在声明方法时要指定返回值的类型。有返回值的方法需要使用return语句将值返回给调用者,它的一般格式如下:

这里,expression是返回值的表达式,当调用该方法时,该表达式的值返回给调用者。例如,Employee类的computeSalary()方法需返回工资值,因此需要指定返回值类型,如下所示:

如果方法调用结束后不要求给调用者返回数据,则方法没有返回值,此时返回类型用void表示,在方法体中可以使用return语句表示返回void,格式如下:

注意:这里没有返回值,它仅表示将控制转回到调用处。当然,也可以省略return,这时当方法中的最后一个语句执行结束以后,程序自动返回到调用处。例如,Employee类的sayHello()方法没有return语句。
2.方法的参数
方法可以没有参数,也可以有参数。没有参数的方法在定义时只需一对括号。例如,Employee类的sayHello()方法没有参数。
有参数的方法在定义时要指定参数的类型和名称,指定的参数称为形式参数。例如,Employee类computeSalary()方法带两个参数,一个是int型;另一个是double型。对带参数的方法,在调用方法时要为其传递实际参数。方法的参数类型可以是基本类型,也可以是引用类型。
3.方法的实现
方法声明的后面是一对大括号,大括号内部是方法体。方法体是对方法的实现,包括局部变量的声明和所有合法的Java语句。
方法的实现是,在方法体中通过编写有关的代码,实现方法所需要的功能。例如,在Employee类的computeSalary()方法是要计算员工的工资,因此通过有关公式计算得到结果,然后将其返回。方法体中可以包含多条语句。

4.访问方法和修改方法
一般地,把能够返回成员变量值的方法称为访问方法(accessor method),把能够修改成员变量值的方法称为修改方法(mutator method)。访问方法名一般为getXxx(),因此访问方法也称getter方法。修改方法名一般为setXxx(),因此修改方法也称setter方法。访问方法的返回值一般与原来的变量值类型相同,而修改方法的返回值为void。例如,在Employee类中可以定义下面两个方法:

分别是修改方法和访问方法。这种设计也是Java Beans规范所要求的。
5.方法签名
在一个类中可定义多个方法,可以通过方法签名来区分这些方法。方法签名(signature)是指方法名、参数个数、参数类型和参数顺序的组合。
注意:方法签名的定义不包括方法的返回值。方法签名将用在方法重载、方法覆盖和构造方法中。
4.3.2 方法的调用
一般来说,要调用类的实例方法应先创建一个对象,然后通过对象引用调用。例如:

如果要调用类的静态方法,通常使用类名调用。例如:

在调用没有参数的方法时,只使用括号即可,对于有参数的方法需要提供实际参数。关于方法参数的传递将在4.3.6节讨论。
方法调用主要使用在如下三种场合:
(1)用对象引用调用类的实例方法。
(2)类中的方法调用本类中的其他方法。
(3)用类名直接调用static方法。
4.3.3 方法重载
Java语言提供了方法重载的机制,允许在一个类中定义多个同名的方法,这称为方法重载(method overloading)。实现方法重载,要求同名的方法要么参数个数不同,要么参数类型不同,仅返回值不同不能区分重载的方法。方法重载就是在类中允许定义签名不同的方法。
在类中定义了重载方法后,对重载方法的调用与一般方法的调用相同,下面的程序在OverloadDemo类中定义了4个重载的display()方法。
程序4.3 OverloadDemo.java

程序运行结果为:

在调用重载的方法时还可能发生自动类型转换。假设没有定义带一个int参数的display()方法,“od.display(10);”语句将调用带double参数的display()方法。
通过方法重载可实现编译时多态(静态多态),编译器根据参数的不同调用相应的方法,具体调用哪个方法是由编译器在编译阶段决定的。前面输出语句中经常使用的println()就是重载方法的典型例子,它可以接受各种类型的参数。
4.3.4 构造方法
构造方法也有名称、参数和方法体。构造方法与普通方法的区别是:
- 构造方法的名称必须与类名相同;
- 构造方法不能有返回值,也不能返回void;
- 构造方法必须在创建对象时用new运算符调用。
构造方法定义的格式为:

这里,public|protected|private为构造方法的访问修饰符,用来决定哪些类可以使用该构造方法创建对象。这些访问修饰符与一般方法的访问修饰符的含义相同。ClassName为构造方法名,它必须与类名相同。paramList为参数列表,构造方法可以带有参数。
1.无参数构造方法
无参数构造方法(no-args constructor)是不带参数的构造方法。例如,在Employee类中,定义了一个无参数构造方法:

使用无参数构造方法创建对象,只需在类名后使用一对括号即可,如下所示:

构造方法主要作用是创建对象并初始化类的成员变量。对类的成员变量,若声明时和在构造方法中都没有初始化,新建对象的成员变量值都被赋予默认值。对于不同类型的成员变量,其默认值不同。整型数据的默认值是0,浮点型数据默认值是0.0,字符型数据默认值是'\u0000',布尔型数据默认值是false,引用类型数据默认值是null。
2.带参数构造方法
如果希望在创建一个对象时就将其成员变量设置为某个值而不是采用默认值,可以定义带参数构造方法。例如,在创建一个Employee对象时就指定员工的姓名、年龄,则可以定义如下带两个参数的构造方法。

然后,在创建Employee对象时可以指定员工的姓名和年龄,如下代码创建一个姓名为“李明”,年龄为30的员工对象:

3.默认构造方法
如果在定义类时没有为类定义任何构造方法,则编译器自动为类添加一个默认构造方法(default constructor)。默认构造方法是无参数构造方法,方法体为空。假设没有为Employee类定义构造方法,编译器提供的默认构造方法如下:

一旦为类定义了带参数的构造方法,编译器就不再提供默认构造方法。再使用默认构造方法创建对象,编译器将给出编译错误提示。假设为Employee类只定义了带参数构造方法,再使用下面语句创建对象:

编译器就会给出如下错误提示:

其含义是没有定义无参构造方法。如果还希望使用无参构造方法创建对象,则必须自己明确定义一个,如下所示:

4.构造方法的重载
构造方法也可以重载,下面代码为Employee类定义了4个重载的构造方法,其中包含一个无参数构造方法和三个带参数的构造方法。

通过重载构造方法,就可以有多种方式创建对象。由于有了这些重载的构造方法,在创建Employee对象时就可以根据需要选择不同的构造方法。
4.3.5 this关键字的使用
this关键字表示对象本身。在一个方法的方法体或参数中,也可能声明与成员变量同名的局部变量,此时的局部变量会隐藏成员变量。要使用成员变量就需要在前面加上this关键字。例如:

同样,在定义方法时,方法参数名也可以与成员变量同名。这时,在方法体中要引用成员变量也必须加上this。例如,在Employee类中,定义修改姓名的方法如下:

这里,参数名与成员变量同名,因此,在方法体中通过this.name使用成员变量name,而没有带this的变量name是方法的参数。
this关键字的另一个用途是在一个构造方法中调用该类的另一个构造方法。例如,假设在Employee类定义了一个构造方法Employee(String name,int age,double salary),现在又要定义一个无参数的构造方法,这时可以在下面的构造方法中调用该构造方法:

注意:如果在构造方法中调用另一个构造方法,则this语句必须是第一条语句。
综上所述,this关键字主要使用在下面三种情况。
- 解决局部变量与成员变量同名的问题;
- 解决方法参数与成员变量同名的问题;
- 用来调用该类的另一个构造方法。
Java语言规定,this只能用在非static方法(实例方法和构造方法)中,不能用在static方法中。实际上,在对象调用一个非static方法时,向方法传递了一个引用,这个引用就是对象本身,在方法体中用this表示。
4.3.6 方法参数的传递
对带参数的方法,调用方法时需要向它传递参数。那么参数是如何传递的呢?在Java语言中,方法的参数传递是按值传递(pass by value),即在调用方法时将实际参数值的一个副本传递给方法中的形式参数,方法调用结束后实际参数的值并不改变。形式参数是局部变量,其作用域只在方法内部,离开方法后自动释放。
尽管参数传递是按值传递的,但对于基本数据类型的参数和引用数据类型的参数的传递还是不同的。对于基本数据类型的参数,是将实际参数值的一个副本传递给方法,方法调用结束后,对原来的值没有影响。当参数是引用类型时,实际传递的是引用值,因此在方法的内部有可能改变原来的对象。
下面程序说明了这两种类型的参数传递。
程序4.4 PassByValue.java

程序的运行结果为:

从程序运行结果可以看到,当参数为基本数据类型时,若在方法内修改了参数的值,在方法返回时,原来的值不变。当参数为引用类型时,传递的是引用,方法返回时引用没有改变,但对象的状态可能被改变。
注意:如果为方法传递的是不可变的引用类型对象(如String对象),对象在方法内部不可能被改变。