调用方法和构造函数

将信息传递给方法或构造函数

方法或构造函数的声明用于指定该方法或构造函数的参数数量和类型。例如,以下方法根据贷款金额、利率、贷款期限(期数)以及贷款的未来价值来计算住房贷款的月供:

public double computePayment(
                  double loanAmt,
                  double rate,
                  double futureValue,
                  int numPeriods) {
    double interest = rate / 100.0;
    double partial1 = Math.pow((1 + interest), 
                    - numPeriods);
    double denominator = (1 - partial1) / interest;
    double answer = (-loanAmt / denominator)
                    - ((futureValue * partial1) / denominator);
    return answer;
}

该方法有四个参数:贷款金额、利率、未来价值和期数。前三者为双精度浮点数,第四个为整数。这些参数在方法主体中使用,运行时将采用传入的参数值。

注意:参数指方法声明中的变量列表。参数值则是方法被调用时实际传递的值。调用方法时,使用的参数值必须在类型和顺序上与声明的参数匹配。

参数类型

方法或构造函数的参数可以使用任何数据类型。这包括基本数据类型(如双精度浮点数、浮点数和整数,正如你在 <font color=”#ea8b43”computePayment() 方法中所见),以及引用数据类型(如对象和数组)。

以下是一个接受数组作为参数的方法示例。该方法创建新的 Polygon 对象,并从 Point 对象数组初始化(假设 Point 是表示 x、y 坐标的类):

public Polygon polygonFrom(Point[] corners) {
    // method body goes here
}

任意数量的参数

你可以使用一种称为可变参数(varargs)的构造,向方法传递任意数量的值。当你无法确定将向方法传递多少个特定类型的参数时,就需要使用可变参数。它相当于手动创建数组的快捷方式(前文的方法本可使用可变参数替代数组实现)。
要使用可变参数,需在最后一个参数类型后跟上省略号(三个点,…),接着加空格,再写参数名。这样该方法就能接受任意数量的该参数,包括零个参数。

public Polygon polygonFrom(Point... corners) {
    int numberOfSides = corners.length;
    double squareOfSide1, lengthOfSide1;
    squareOfSide1 = (corners[1].x - corners[0].x)
                     * (corners[1].x - corners[0].x) 
                     + (corners[1].y - corners[0].y)
                     * (corners[1].y - corners[0].y);
    lengthOfSide1 = Math.sqrt(squareOfSide1);

    // more method body code follows that creates and returns a 
    // polygon connecting the Points
}

你可以看到,在方法内部,corners 被当作数组处理。该方法既可以接受数组参数,也可以接受序列参数。无论哪种情况,方法体中的代码都会将参数视为数组。
你最常在打印方法中看到可变参数;例如这个 printf()方法:

public PrintStream printf(String format, Object... args)

允许您打印任意数量的对象。调用方式如下:

System.out.printf("%s: %d, %s%n", name, idnum, address);

或者这样

System.out.printf("%s: %d, %s, %s, %s%n", name, idnum, address, phone, email);

或带有更多参数的版本。

参数名称

当你为方法或构造函数声明参数时,需要为该参数指定一个名称。该名称将在方法主体中用于引用传入的参数。
参数名称在其作用域内必须唯一。它不能与同一方法或构造函数中的其他参数名称相同,也不能与方法或构造函数内部的局部变量名称相同。
参数可以与类的字段同名。若出现这种情况,则称该参数遮蔽了字段。遮蔽字段会降低代码可读性,通常仅在构造函数和设置特定字段的方法中使用。例如,请看以下Circle 类及其 setOrigin() 方法:

public class Circle {
    private int x, y, radius;
    public void setOrigin(int x, int y) {
        ...
    }
}

Circle类包含三个字段:x、yradiussetOrigin() 方法有两个参数,每个参数的名称都与其中一个字段相同。每个方法参数都会遮蔽与其同名的字段。因此在方法体内使用简单的 xy 时,会引用参数而非字段。要访问字段,必须使用限定名。本课后续章节“使用 this 关键字”将对此进行详细说明。

传递基本数据类型参数

基础数据类型的参数(如 intdouble)以值传递方式传入方法。这意味着对参数值的任何修改仅存在于方法作用域内。当方法返回时,这些参数便消失,对它们所做的任何修改都会丢失。以下是一个示例:

public class PassPrimitiveByValue {
    public static void main(String[] args) {
        int x = 3;

        // invoke passMethod() with
        // x as argument
        passMethod(x);

        // print x to see if its
        // value has changed
        IO.println("After invoking passMethod, x = " + x);
    }

    public static void passMethod(int p) {
        p = 10;
    }
}

程序输出如下

After invoking passMethod, x = 3

传递引用数据类型参数

引用数据类型参数(如对象)同样以值传递方式传入方法。这意味着当方法返回时,传入的引用仍指向原对象。但若具备相应访问权限,可在方法内部修改对象字段的值。
例如,考虑某个任意类中移动 Circle 对象的方法:

public void moveCircle(Circle circle, int deltaX, int deltaY) {
    // code to move origin of circle to x+deltaX, y+deltaY
    circle.setX(circle.getX() + deltaX);
    circle.setY(circle.getY() + deltaY);
        
    // code to assign a new reference to circle
    circle = new Circle(0, 0);
}

让该方法使用以下参数被调用:

moveCircle(myCircle, 23, 56)

在方法内部, circle 最初指向 myCircle 。该方法将 circle 所引用的对象(即 myCircle )的 xy 坐标分别改变为23和56。这些修改在方法返回后仍会持续生效。随后 circle 被赋予对新 Circle 对象的引用,其 x = y = 0 。然而这种重新赋值并不会持久化,因为该引用是以值传递方式传入的,无法改变。方法执行期间, circle 所指向的对象已发生变更,但方法返回时, myCircle 仍指向方法调用前的原始 Circle 对象。