文档

Java™ 教程-Java Tutorials 中文版
将信息传递给方法或构造函数
Trail: Learning the Java Language
Lesson: Classes and Objects
Section: Classes

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

方法或构造函数的声明声明了该方法或构造函数的参数的数量和类型。例如,以下是根据贷款金额,利率,贷款期限(期数)和贷款的未来价值计算住房贷款的每月支付的方法:

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;
}

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


注意: Parameters (形参) 是指方法声明中的变量列表。Arguments (实参) 是调用方法时传入的实际值。当你调用一个方法时,使用的实参必须与形参的类型和顺序相匹配。

参数类型

你可以将任何数据类型用于方法或构造函数的参数。这包括像 computePayment 方法中所看到的双精度浮点数和整数等基本数据类型以及对象和数组等引用数据类型。

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

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

注意: 如果要将方法传递给方法,请使用 lambda expressionmethod reference

任意数量的参数

你可以使用名为 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);

或者具有不同数量的参数。

参数名称

向方法或构造函数声明参数时,要为该参数提供一个名称。该名称在方法体内用于引用传入的参数。

参数的名称在其范围内必须是唯一的。它不能与同一方法或构造函数的另一个参数的名称相同,也不能是方法或构造函数内的局部变量的名称。

一个参数可以和一个类的字段名称相同。如果是这种情况,则该参数被称为 shadow 该字段。阴影字段可能会使你的代码难以阅读,并且通常仅用于设置特定字段的构造函数和方法中。例如,请考虑以下 Circle 类及其 setOrigin 方法:

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

Circle 类有三个字段:xyradiussetOrigin 方法有两个参数,每个参数都与其中一个字段具有相同的名称。每个方法参数都会隐藏共享其名称的字段。因此,在方法体内使用简单名称 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
        System.out.println("After invoking passMethod, x = " + x);
           
    }
        
    // change parameter in passMethod()
    public static void passMethod(int p) {
        p = 10;
    }
}

当你运行这个程序时,输出是:

After invoking passMethod, x = 3

传递引用数据类型实参

引用数据类型参数(如对象)也 通过值 传递到方法中(译者注:结合上下文,此处应为实参 arguments,但原文为形参 parameters,姑且译为参数)。这意味着当方法返回时,传入的引用仍然引用与之前相同的对象。然而,如果对象的字段的值具有适当的访问级别,则 可以 在该方法中更改该字段。

例如,考虑任意类中的移动 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)的 x 和 y 坐标分别改为 23 和 56。方法返回时,这些更改将持续。然后 circle 被赋予一个 x = y = 0 的新 Circle 对象的引用。然而,这种重新分配没有永久性,因为引用是通过值传递的,并且不能更改。在该方法中,由 circle 指向的对象已经发生了变化,但是当方法返回时,myCircle 仍然像该方法被调用前一样引用相同的 Circle 对象。


Previous page: Providing Constructors for Your Classes
Next page: Objects