Skip to content

Java 方法

在 Java 中,**方法(method)**是一个执行特定任务的代码块。方法允许你将相关的语句分组,给它们一个名称,并在需要时通过调用该名称来执行。这有助于代码组织、重用,并使程序更易于理解和维护。

可以把方法想象成食谱:它们有一个名称(例如,“bakeCake”),可能需要配料(参数),执行一系列步骤(方法体),并且可能产生一个结果(返回值,就像烤好的蛋糕)。

你已经使用过 System.out.println() 等方法。现在,让我们学习如何创建自己的方法。

方法定义的基本结构(语法)是:

modifier(s) returnType methodName(parameterList) throws ExceptionType(s) {
// 方法体:执行任务的语句
// ...
return value; // 如果 returnType 不是 void,则必需
}
  • modifier(s): (可选)关键字,如 public、private、protected、static、final、abstract。它们控制访问和行为。我们稍后会详细介绍这些。
  • public:可从任何其他类访问。
  • static:属于类本身,而不是特定的对象实例。可以使用 ClassName.methodName() 调用。
  • returnType: 方法返回值的 数据类型。如果方法不返回任何值,使用关键字 void。
  • methodName: 你给方法起的名称。遵循命名约定(camelCase,以小写字母开头,例如,calculateSum,printUserDetails)。
  • parameterList: (可选)方法接受的输入变量的逗号分隔列表。每个参数都有一个类型和一个名称(例如,int number1, String message)。如果没有参数,则括号 () 留空。
  • throws ExceptionType(s): (可选)指定方法可能抛出的已检查异常的类型。将在异常章节中介绍。
  • 方法体: 包含实现方法逻辑的语句的代码块(在 {} 中)。
  • return value;: 如果 returnType 不是 void,方法必须使用 return 关键字后跟指定 returnType 的值,将结果返回给调用者。
/**
* 计算两个整数的和。
* @param num1 第一个整数。
* @param num2 第二个整数。
* @return num1 和 num2 的和。
*/
public static int addNumbers(int num1, int num2) {
int sum = num1 + num2;
return sum; // 返回一个整数值
}
/**
* 向控制台打印问候消息。
* 此方法不返回任何值。
* @param name 要问候的名称。
*/
public static void printGreeting(String name) {
System.out.println("Hello, " + name + "!");
// void 方法不需要 return 语句(或者使用 'return;' 提前退出)
}

要执行方法,你需要调用它,使用它的名称,后面跟着括号 ()。如果方法需要参数(实参),你在括号内提供值,需要匹配参数列表的类型和顺序。

  1. 程序控制转移到被调用方法的开头。
  2. 方法体执行。
  3. 在以下情况下,控制返回到方法调用之后的点:
  4. 执行了 return 语句(如果适用,返回一个值)。
  5. 到达方法体 (}) 的末尾(对于 void 方法)。
public class MethodDemo {
public static void main(String[] args) {
// 调用 void 方法
printGreeting("Alice"); // 将 "Alice" 作为 'name' 参数的实参传递
// 调用返回值的方法
int numberA = 10;
int numberB = 25;
int result = addNumbers(numberA, numberB); // 调用并存储返回值
System.out.println("The sum is: " + result);
// 你也可以直接使用返回值
System.out.println("Sum of 5 and 7: " + addNumbers(5, 7));
}
// --- 来自前面示例的方法定义 ---
public static int addNumbers(int num1, int num2) {
int sum = num1 + num2;
return sum;
}
public static void printGreeting(String name) {
System.out.println("Hello, " + name + "!");
}
}

输出:

Hello, Alice!
The sum is: 35
Sum of 5 and 7: 12

按值传递 vs. 按引用传递(澄清)

Section titled “按值传递 vs. 按引用传递(澄清)”

Java 严格按值传递。 这意味着当你将变量传递给方法时:

  • 对于基本类型(int, float, boolean 等): 变量值的副本被传递给方法参数。在方法内部对参数所做的更改不会影响方法外部的原始变量。
  • 对于引用类型(对象,数组): 变量的引用(内存地址)的副本被传递。原始变量和方法参数现在都指向内存中的同一个对象。因此,如果方法修改了对象本身(例如,改变对象的字段,向数组列表中添加元素),这些更改将反映在方法外部,因为它们作用于同一个对象。然而,如果方法尝试将参数重新赋值给一个新对象(parameter = new Object();),这只会改变参数的引用副本,而不会影响方法外部的原始变量。
public class PassByValuePrimitive {
public static void main(String[] args) {
int originalValue = 10;
System.out.println("Before calling method: " + originalValue);
tryToModify(originalValue);
System.out.println("After calling method: " + originalValue); // 仍然是 10
}
public static void tryToModify(int number) {
number = number + 5; // 修改副本
System.out.println("Inside method: " + number);
}
}

输出:

Before calling method: 10
Inside method: 15
After calling method: 10

示例(引用类型按值传递 - 修改对象状态):

Section titled “示例(引用类型按值传递 - 修改对象状态):”
import java.util.Arrays;
public class PassByValueReference {
public static void main(String[] args) {
int[] originalArray = {1, 2, 3};
System.out.println("Before calling method: " + Arrays.toString(originalArray));
modifyArray(originalArray);
System.out.println("After calling method: " + Arrays.toString(originalArray)); // 数组被修改了
}
// 参数 'arr' 接收到原始数组引用的副本
public static void modifyArray(int[] arr) {
if (arr != null && arr.length > 0) {
arr[0] = 99; // 修改引用指向的对象
System.out.println("Inside method: " + Arrays.toString(arr));
}
// arr = new int[]{4, 5, 6}; // 这只会改变局部变量 'arr' 的引用,不会改变 originalArray
}
}

输出:

Before calling method: [1, 2, 3]
Inside method: [99, 2, 3]
After calling method: [99, 2, 3]

方法重载允许你在同一个类中定义多个同名的方法,只要它们的参数列表不同。不同之处可以体现在:

  • 参数的数量。
  • 参数的数据类型。
  • 参数的数据类型的顺序。

仅凭返回值类型不足以重载方法。

重载允许你根据输入实参提供执行类似操作的不同方式。编译器根据方法调用中提供的实参确定调用哪个重载方法。

public class OverloadDemo {
public static void main(String[] args) {
System.out.println("Sum of 2 ints: " + add(5, 10));
System.out.println("Sum of 3 ints: " + add(5, 10, 15));
System.out.println("Sum of 2 doubles: " + add(3.5, 2.7));
printInfo("Alice");
printInfo("Bob", 30);
}
// 重载的 add 方法
public static int add(int a, int b) {
System.out.println("-> Called add(int, int)");
return a + b;
}
public static int add(int a, int b, int c) {
System.out.println("-> Called add(int, int, int)");
return a + b + c;
}
public static double add(double a, double b) {
System.out.println("-> Called add(double, double)");
return a + b;
}
// 重载的 printInfo 方法
public static void printInfo(String name) {
System.out.println("-> Called printInfo(String)");
System.out.println("Name: " + name);
}
public static void printInfo(String name, int age) {
System.out.println("-> Called printInfo(String, int)");
System.out.println("Name: " + name + ", Age: " + age);
}
}

输出:

-> Called add(int, int)
Sum of 2 ints: 15
-> Called add(int, int, int)
Sum of 3 ints: 30
-> Called add(double, double)
Sum of 2 doubles: 6.2
-> Called printInfo(String)
Name: Alice
-> Called printInfo(String, int)
Name: Bob, Age: 30

**构造方法(constructor)**是一种特殊类型的方法,用于在使用 new 关键字创建对象时初始化对象。它看起来类似于普通方法,但有特定的规则:

  • 它必须与类同名。
  • 它没有显式返回值类型,甚至没有 void。
  • 当类的对象被实例化(new ClassName(...))时,它会自动被调用。

构造方法主要用于为对象的实例变量(字段)设置初始值。

如果你在类中没有定义任何构造方法,Java 会自动提供一个默认构造方法。这个默认构造方法不接受任何参数,并将实例变量初始化为它们的默认值(数字为 0,布尔值为 false,对象为 null)。

然而,如果你确实定义了任何构造方法(无论带参数还是不带参数),默认构造方法将不会自动提供。

与普通方法一样,构造方法也可以重载。

class Dog {
String name;
int age;
// 构造方法 1:无参数(用默认名称初始化)
public Dog() {
this.name = "Unknown"; // 'this' 指代当前对象
this.age = 0;
System.out.println("Dog() constructor called.");
}
// 构造方法 2:接受 name 作为参数
public Dog(String name) {
this.name = name;
this.age = 0;
System.out.println("Dog(String) constructor called.");
}
// 构造方法 3:接受 name 和 age
public Dog(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Dog(String, int) constructor called.");
}
public void displayInfo() {
System.out.println("Dog [Name=" + name + ", Age=" + age + "]");
}
}
public class ConstructorDemo {
public static void main(String[] args) {
Dog dog1 = new Dog(); // 调用 Dog()
Dog dog2 = new Dog("Buddy"); // 调用 Dog(String)
Dog dog3 = new Dog("Lucy", 3); // 调用 Dog(String, int)
dog1.displayInfo();
dog2.displayInfo();
dog3.displayInfo();
}
}

输出:

Dog() constructor called.
Dog(String) constructor called.
Dog(String, int) constructor called.
Dog [Name=Unknown, Age=0]
Dog [Name=Buddy, Age=0]
Dog [Name=Lucy, Age=3]

Varargs 在 Java 5 中引入,允许方法接受可变数量的同类型实参。

语法:在方法签名中,指定类型后跟省略号 (...)。

`public static void myMethod(String fixedArg, int... numbers)`
  • Varargs 参数(示例中的 int... numbers)在方法内部被视为该类型的数组(int[])。
  • 一个方法只能有一个 varargs 参数。
  • varargs 参数必须是方法签名中的最后一个参数。
  • 你可以为 varargs 参数传递零个或多个实参来调用方法,或者传递指定类型的数组。
public class VarargsDemo {
public static void main(String[] args) {
printNumbers("Set 1:", 1, 2, 3);
printNumbers("Set 2:", 10, 20, 30, 40, 50);
printNumbers("Set 3:"); // 用零个 varargs 调用
int[] data = {100, 200};
printNumbers("Set 4:", data); // 用一个数组调用
}
// 方法接受一个标题和可变数量的整数
public static void printNumbers(String title, int... numbers) {
System.out.print(title + " ");
if (numbers.length == 0) {
System.out.println(" (No numbers)");
return;
}
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i]);
if (i < numbers.length - 1) {
System.out.print(", ");
}
}
System.out.println();
}
}

输出:

Set 1: 1, 2, 3
Set 2: 10, 20, 30, 40, 50
Set 3: (No numbers)
Set 4: 100, 200

main 方法作为 Java 应用程序的入口点,有一个参数 String[] args(或 String... args)。当程序在命令行执行时,传递给程序的任何参数都会被这个数组接收。

public class CommandLineArgs {
public static void main(String[] args) {
System.out.println("Number of arguments received: " + args.length);
if (args.length > 0) {
System.out.println("Arguments are:");
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "]: " + args[i]);
}
}
}
}

编译并运行:

Terminal window
javac CommandLineArgs.java
java CommandLineArgs firstArg "Second Argument" 123

输出:

Number of arguments received: 3
Arguments are:
args[0]: firstArg
args[1]: Second Argument
args[2]: 123

注意:实参是以字符串的形式传递的。如果你需要数字,你需要解析它们(例如,使用 Integer.parseInt())。

finalize() 方法(已废弃 - 避免使用)

Section titled “finalize() 方法(已废弃 - 避免使用)”

较旧的 Java 版本有一个 finalize() 方法(定义在 Object 类中)。其意图是重写它,以便在对象被垃圾回收之前执行清理操作。然而,finalize() 有明显的缺点:

  • 不可预测的时机: 无法保证 finalize() 何时 或甚至 是否会被调用。
  • 性能问题: 终结化可能会延迟垃圾回收并影响性能。
  • 容易出错: 在 finalize() 中抛出的异常会被忽略。
  • 安全风险: 可能导致对象“复活”。

从 Java 9 起,finalize() 已被废弃,应避免使用。 现代资源清理的替代方案包括:

  • try-with-resources: 管理需要显式关闭的资源(如 I/O 流、网络套接字、数据库连接)的标准方法。
  • java.lang.ref.Cleaner (Java 9+): 一种更可靠、更灵活的机制,用于在对象变得不可达时注册清理操作。

方法是 Java 程序的“主力”。理解如何定义、调用和组织它们至关重要。接下来的章节将探讨基于方法和类的更高级概念,首先是如何使用 Java 异常处理错误。