本教程适用于JDK 8。本页中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言变化以获取Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发布说明了解所有JDK版本中的新功能、增强功能和已删除或弃用选项的信息。
方法或构造函数的声明声明了该方法或构造函数的参数的数量和类型。例如,下面是一个方法,根据贷款金额、利率、贷款期限(期数)和贷款的未来价值计算每月还款额:
public double computePayment( double 贷款金额, double 利率, double 未来价值, int 期数) { double 利息 = 利率 / 100.0; double partial1 = Math.pow((1 + 利息), - 期数); double 分母 = (1 - partial1) / 利息; double 答案 = (-贷款金额 / 分母) - ((未来价值 * partial1) / 分母); return 答案; }
这个方法有四个参数:贷款金额、利率、未来价值和期数。前三个是双精度浮点数,第四个是整数。参数在方法体中被使用,并且在运行时将接受传递的参数的值。
方法或构造函数的参数可以使用任何数据类型。这包括基本数据类型,例如双精度浮点数、浮点数和整数,就像在computePayment
方法中看到的那样,还有引用数据类型,例如对象和数组。
下面是一个接受数组作为参数的方法的示例。在这个示例中,该方法创建一个新的Polygon
对象,并从Point
对象的数组中进行初始化(假设Point
是表示x、y坐标的类):
public Polygon polygonFrom(Point[] corners) { // 方法体在这里 }
你可以使用一种称为可变参数的结构来传递任意数量的值给一个方法。当你不知道会传递给方法的某种类型的参数的数量时,你可以使用可变参数。这是一个手动创建数组的捷径(前面的方法可以使用可变参数而不是数组)。
要使用可变参数,你需要在最后一个参数的类型后面加上省略号(三个点,...),然后是一个空格和参数名称。然后可以使用任意数量的该参数来调用方法,包括没有参数。
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); // 更多方法体代码遵循,用于创建并返回连接点的多边形 }
你可以看到,在方法内部,corners
被当作一个数组处理。无论是使用数组还是使用参数序列来调用该方法,方法体中的代码都将将参数视为数组。
在打印方法中,你最常见到的是 varargs,例如这个 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
、y
和 radius
。 setOrigin
方法有两个参数,每个参数与一个字段具有相同的名称。每个方法参数都遮蔽了与其名称相同的字段。因此,在方法体中使用简单名称 x
或 y
是指参数,而不是字段。要访问字段,必须使用限定名称。这将在本课程的后面的“使用 this
关键字”一节中进行讨论。
原始类型的参数,例如 int
或 double
,通过“按值”传递给方法。这意味着对参数值的任何更改仅存在于方法的作用域内。方法返回时,参数消失,并且对它们的任何更改都会丢失。下面是一个例子:
public class PassPrimitiveByValue { public static void main(String[] args) { int x = 3; // 以 x 作为参数调用 passMethod() passMethod(x); // 打印 x,以查看其值是否改变 System.out.println("调用 passMethod 后,x = " + x); } // 在 passMethod() 中修改参数 public static void passMethod(int p) { p = 10; } }
运行此程序的输出结果为:
调用 passMethod 后,x = 3
引用数据类型参数,例如对象,也是按值传递到方法中的。这意味着当方法返回时,传入的引用仍然引用与之前相同的对象。然而,如果这些对象的字段具有适当的访问级别,它们的值可以在方法中更改。
例如,考虑一个在任意类中移动 Circle 对象的方法:
public void moveCircle(Circle circle, int deltaX, int deltaY) { // 将圆的原点移动到 x+deltaX, y+deltaY circle.setX(circle.getX() + deltaX); circle.setY(circle.getY() + deltaY); // 将 circle 分配一个新引用的代码 circle = new Circle(0, 0); }
让该方法以这些参数调用:
moveCircle(myCircle, 23, 56)
在方法内部,circle 最初引用 myCircle。该方法将 circle 引用的对象(即 myCircle)的 x 和 y 坐标分别增加了 23 和 56。这些更改在方法返回时将保持有效。然后,circle 被分配一个新的 Circle 对象的引用,其 x 和 y 均为 0。然而,此重新分配是没有持久性的,因为引用是按值传递的,无法更改。在方法内部,circle 指向的对象已经改变,但是当方法返回时,myCircle 仍然引用调用该方法之前的同一个 Circle 对象。