方法(method)可以把完成某项功能的代码组织在一起,需要时再调用。例如,我们一直在使用的 main 就是一个方法,它是 Java 程序常见的入口方法。

什么时候适合把代码写成方法呢?一个很直观的情况是:某段代码需要反复使用,或者它本身就负责一项相对独立的功能。 比如游戏中的角色和怪物都可能掉血,我们可以把扣除生命值的操作封装成方法,在需要时调用,而不用在多个地方重复写相同的逻辑。

这样做主要有两个好处:提高代码复用性,以及方便后续维护。以后需要调整这项功能时,通常只需要修改对应的方法。

方法的定义与调用

方法定义,就是把一段实现特定功能的代码组织起来;方法调用,则是在需要使用这项功能时执行它。

普通方法仅仅定义出来并不会自动执行,需要有代码调用它。我们写的 main 稍有不同:它作为程序入口,由 Java 运行环境启动时调用。

无参数、无返回值的方法

先从最简单的格式开始:

纯文本
public static void 方法名() {
    // 方法体
}

这里先沿用我们熟悉的 public static 写法,重点看方法名和方法体。定义好以后,使用 方法名(); 就能调用。

例如定义一个 hello() 方法,让它打印 helloworld。在 main 中调用两次,就会打印两次:

java
public class Test1 {
    public static void main(String[] args) {
        //调用方法
        hello();
        hello();
    }
    public static void hello()
    {
        System.out.println("helloworld");
    }
}

带参数的方法

如果希望方法能够处理不同的数据,就可以给它添加参数。

比如写一个加法方法:每次传入两个数字,方法负责计算它们的和并打印。这样就不必为每一组数字单独写一个加法方法了。

基本格式如下:

纯文本
public static void 方法名(参数类型 参数名) {
    // 方法体
}

多个参数之间使用英文逗号分隔,例如 (int a, int b)。

这里还需要区分两个概念:

  • 形参(形式参数):方法定义时写在括号里的参数,例如 int a、int b。
  • 实参(实际参数):调用方法时传入的具体值或表达式,例如 add(3, 5) 中的 3 和 5。

调用方法时,传入的实参需要与方法声明的参数在数量、顺序和类型上相匹配,Java 也可能按规则进行允许的类型转换。

看下面这个例子。定义 add 时只知道需要两个 int 参数,具体传什么数字由调用时决定;方法收到参数后,再把它们相加并打印:

java
public class Test1 {
    public static void main(String[] args) {
        //这里的3和5是实参
        add(3, 5);// 8
        add(2, 10);// 12
    }
                        //这里的a和b是形参
    public static void add(int a, int b) {
        System.out.println(a + b);
    }
}

带返回值的方法

上面的 add() 会直接打印结果。但如果我们不想马上打印,而是希望先得到两数之和,再用它进行其他计算,应该怎么办?这时候就需要让方法返回一个值。

带返回值的方法基本格式是:

纯文本
public static 返回值类型 方法名(参数列表) {
    // 方法体
    return 返回值;
}

return 用于结束当前方法,并把结果交给调用它的地方。下面把加法方法改成返回 int:

java
public class Test1 {
    public static void main(String[] args) {
        //方法会返回一个整数,也需要一个整型变量来接受
        int a=add(3, 5);
        System.out.println(a);// 8
        //也可以直接打印返回值
        System.out.println(add(2, 3));//5
        //也可以直接执行,什么也不做。
        add(1, 2);
    }
    public static int add(int a, int b) {
        return a + b;
    }
}

这个例子展示了三种调用方式:

  • int a = add(3, 5);:使用变量接收结果,再做后续处理。
  • System.out.println(add(2, 3));:直接把返回结果用于打印。
  • add(1, 2);:只调用方法,不使用返回值。这次加法确实执行了,但因为结果没有被接收或打印,所以控制台不会显示它。

接收返回值的变量不一定要与返回类型完全相同,只要类型兼容即可。比如方法返回 int,也可以赋值给 long 变量。

方法使用的注意事项

  • 普通方法需要被调用才会执行;方法在类中的书写位置不决定调用顺序。
  • 在这里学习的普通 Java 方法定义于类中,不能把一个方法直接定义在另一个方法内部。
  • 返回值类型为 void,表示方法不向调用者返回结果。可以不写 return;也可以写 return;,用于提前结束方法。
  • 有返回值的方法需要按声明的类型返回相应的值,并保证正常结束的执行路径能够返回结果。

再说一下 return。对于返回值类型为 void 的方法,return; 后面不能跟具体数据;对于有返回值的方法,则通常使用 return 表达式; 返回结果。

而且,一旦执行到 return,当前方法就会结束。如果在同一个代码块里,紧接着无条件执行的 return 后面再写普通语句,Java 会认为后面的语句无法到达,产生编译错误。

方法的重载

现在我们写的加法方法能够处理整数。如果还想让它计算小数,是不是就得再起一个新名字?其实不需要,Java 允许在同一个类中定义多个同名但参数列表不同的方法,这就是方法重载(overload)。

简单记住:方法名相同,参数不同,就可以构成重载;只改变返回值类型不行。

这里说的参数不同,主要包括参数个数不同、参数类型不同,或者参数类型的排列顺序不同。比如 (int, double) 和 (double, int) 是两组不同的参数列表,但只是交换两个 int 参数的名字或顺序,并不能产生新的重载方法。

下面把 add 分别定义为整数加法和浮点数加法,调用时 Java 会根据传入的参数选择相匹配的方法:

java
public class Test1 {
    public static void main(String[] args) {
        System.out.println(add(1, 2));
        System.out.println(add(3.14, 4.13));
    }
    public static int add(int a, int b) {
        return a + b;
    }
    public static double add(double a, double b) {
        return a + b;
    }
}

这下就能看出,为什么要强调参数列表必须不同了。假如我们定义两个 add(int a, int b),其中一个返回 void、直接打印结果,另一个返回 int、把结果交出去,那么调用 add(1, 2) 时,应该使用哪一个呢?

Java 不允许只靠返回值类型区分这两个方法,因此这样的定义会直接编译失败。

基本数据类型、引用数据类型与值传递

在理解方法如何传递参数之前,我们先对比一下基本数据类型和引用数据类型。结合之前学过的数组和内存概念,这部分就比较容易理解了。

基本数据类型与引用数据类型

Java 的基本数据类型包括整数类型、浮点数类型、char 和 boolean。数组、字符串以及其他对象类型都属于引用类型。

基本数据类型:赋值时复制的是变量存储的值。

例如:

java
public class Test1 {
    public static void main(String[] args) {
        int a = 10;
        int b = a;
        System.out.println(b);//10
    }
}

定义 int b = a; 时,b 得到的是 a 当前保存的值 10。从此两个变量各自保存自己的数值,修改 b 不会改变 a。

引用数据类型:赋值时复制的是引用值。

我们可以先把引用理解为指向对象的“地址信息”。对于数组,数组变量保存的是引用,而实际的数组元素存储在数组对象中。因此将一个数组变量赋给另一个变量后,两者可能指向同一个数组:

java
public class Test1 {
    public static void main(String[] args) {
        int[] array1 = { 1, 2, 3, 4 };
        int[] array2 = array1;
        System.out.println(array2[0]);//1
        array1[0] = 10;
        System.out.println(array2[0]);//10
    }
}

这里 array2 = array1 复制的是引用,所以 array1 和 array2 指向同一个数组。执行 array1[0] = 10 后,用 array2[0] 访问也会得到 10。

要注意:这不是把整份数组复制了一遍,而是两个变量引用了同一个数组对象。

方法的值传递

先看基本类型参数的例子:

java
public class Test1 {
    public static void main(String[] args) {
        int num = 100;
        change(num);
        System.out.println(num);//100
    }
    public static void change(int num) {
        num = 200;
    }
}

调用 change(num) 以后,打印出来的 num 是 100 还是 200 呢?答案仍然是 100。

因为调用方法时,main 中 num 保存的值 100 被复制给了 change 方法的形参。方法内部把形参改为 200,不会影响外面的那个变量。

那如果传入的是数组呢?我们再看一个例子:

java
public class Test1 {
    public static void main(String[] args) {
        int[] array = { 1, 2, 3, 4, 5 };
        change(array);
        System.out.println(array[0]);//200
    }
    public static void change(int[] array) {
        array[0] = 200;
    }
}

这次输出是 200。数组作为参数传入时,复制的仍然是值,只不过这个值是数组的引用。因此,方法内的形参和外面的变量指向同一个数组,修改数组元素后,外面自然能看到变化。

这里有个容易混淆的地方:Java 的方法参数始终是值传递。传基本类型时复制具体数值;传引用类型时复制引用值。方法内部修改引用指向的数组内容,会影响外部看到的数组;但如果只把形参重新赋值为另一个数组,并不会让外面的变量也指向新数组。

这样再回头看前面的两个例子,就能理解为什么一个变量没有改变,而另一个数组的元素发生了变化。

谢谢你读到这里。 ✧

聊两句

评论审核后显示

有疑问、补充,或者想交流一下,都可以写在这里。

评论区将在滚动到这里时加载。