更好的阅读体验:点这里 ( www.doubibiji.com
)
更好的阅读体验:点这里 ( www.doubibiji.com
)
更好的阅读体验:点这里 ( www.doubibiji.com
)
6 方法
方法也可以称之为功能,我们可以将一段实现特定功能的代码封装为一个方法。
为什么需要封装为方法呢?
举个栗子:
给定两个整数,我现在想获取其中的最大值。
于是乎,哐哐敲代码:
public class HelloJava {
public static void main(String[] args) {
int num1 = 24;
int num2 = 12;
int maxNum;
if (num1 > num2) {
maxNum = num1;
} else {
maxNum = num2;
}
System.out.println("较大的数为:" + maxNum); // 较大的数为:24
}
}
一个地方需要这个功能,我在一个地方写了这个功能的代码,100个、1000个地方需要这个功能,那就需要写1000遍。万一这个功能需要修改,修改1000个地方不得崩溃。
所以我们可以将这个功能封装为一个方法,想要使用到这个功能的时候,直接调用这个方法即可。
所以通过使用方法,提高代码的复用性,减少重复代码,提高开发效率。
我们之前已经使用了很多 Java API 的方法,例如 System.out.println()
、Arrays.toString()
等,现在我们主要学习如何自定义方法。
因为还没有讲解类和对象,所以这里学习的是静态方法,后面学习类和对象的时候,再学习成员方法。
方法在有的语言中又叫函数,两个基本是一个意思。
6.1 方法的定义
下面定义一个将两个数相加的方法
/**
* 定义两个数相加的方法
*/
public static int add() { // 定义方法
return 3 + 5;
}
public
是权限修饰符,表示在哪里可以访问到这个方法,static 表示是静态方法,这里 public static 先不用管,固定写法就好。int
表示返回值类型,int 表示这个方法返回一个整形的数据,方法的返回值可以是任何类型或者是void;add
表示方法名,方法名按照标识符命名规则即可,推荐使用首字母小写的驼峰规则(xxxYyyZzz);()
中可以传递参数,参数可以缺省;return
用来返回结果,返回的结果要和返回值类型匹配,所以这里需要return
一个整形数据;如果返回值类型为void
,那么return
可以省略,不返回结果;- 方法必须先定义后调用。
- 注意:return后面不能有语句。
下面来调用方法:
public class HelloJava {
public static void main(String[] args) {
int result = add(); // 调用两个数相加的方法
System.out.println(result); // 8
}
/**
* 定义两个数相加的方法
*/
public static int add() { // 定义方法
return 3 + 5;
}
}
调用方法使用 方法名();
这样的方式来调用,如果方法有返回值,也可以定义变量接收返回结果,当然不接收也可以。
上面定义的方法只能计算 3 + 5 的值,复用性太差,我们可以让方法接收两个参数,然后根据这两个参数来计算和。
6.2 方法的参数
方法的参数可以接收外面传递的值。
1 方法参数
通过传递参数,我们可以改写上面计算两个数的和的方法:
public class HelloJava {
public static void main(String[] args) {
int result = add(1, 2); // 调用方法
System.out.println(result); // 输出3
}
/**
* 定义两个数相加的方法
*/
public static int add(int a, int b) { // 定义方法
return a + b;
}
}
在调用的时候,传递了两个参数,1
和 2
,根据传入的顺序,1
传递给了 a
,2
传递给了 b
,调用方法的时候,需要根据方法定义时候的参数顺序来传递参数。
通过传递不同的参数,可以计算不同值的和,复用性更强。
方法名后面的参数为形式参数(形参),方法的参数个数不限,使用逗号分隔;
调用方法的时候,传递的是实际参数(实参),表示方法执行的时候的参数值。
加深印象,再举个栗子:
public class HelloJava {
public static void main(String[] args) {
String name = "doubibiji";
int year = 13;
sayHello(name, year); // 调用方法
}
/**
* 定义方法
*/
public static void sayHello(String name, int years) {
System.out.println(String.format("Hello, I am %s, I am %d years old.", name, years));
}
}
上面定义了一个 sayHello()
方法,接收两个参数,分别是字符串和整形类型。方法没有返回值,所以返回值类型就是 void
。
2 可变参数
可变参数是一种特殊的参数类型,它允许在方法定义中传入可变数量的参数。这通过在类型名称后面添加三个点(…)来实现。可变参数使得方法的调用更加灵活,因为你可以传入任意数量的参数,而无需创建方法重载。
举个栗子:
public class HelloJava {
public static void main(String[] args) {
// 调用printNumbers方法,传入不同数量的参数
printNumbers(1, 2, 3); // 输出:1 2 3
printNumbers(6); // 输出:6
printNumbers(); // 输出:(无输出)
printNumbers(new int[]{1, 2, 3, 4, 5}); // 1 2 3 4 5
}
/**
* 定义可变参数的方法
*/
public static void printNumbers(int... numbers) {
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println();
}
}
上面定义了一个方法 printNumbers()
,方法的参数是一个可变参数,表示可以接收任意个参数。在 main()
方法中调用方法 printNumbers()
可以传递任意参数,也也可以不传递参数。
其实可变参数 int... numbers
就是一个数组,所以可以直接使用数组 new int[]{1, 2, 3, 4, 5}
来传递参数。当不传递参数的时候, int... numbers
为空数组,不是 null
。
需要注意:一个方法只能有一个可变参数,且它必须是方法参数列表中的最后一个参数。
3 参数值传递
在 Java 中调用方法传递参数是值传递,不是地址传递!
什么意思呢?举个栗子:
我们编写代码,交换 a 和 b 两个变量的值,代码如下:
public class HelloJava {
public static void main(String[] args) {
int a = 3;
int b = 5;
// 交换a和b的值
int temp = a;
a = b;
b = temp;
System.out.println(a); // 5
System.out.println(b); // 3
}
}
交换两个变量这个功能,可能其他地方也会用到,于是写一个方法来实现:
public class HelloJava {
public static void main(String[] args) {
int a = 3;
int b = 5;
// 调用交换方法
swap(a, b);
System.out.println(a); // 3
System.out.println(b); // 5
}
/**
* 交换两个变量
*/
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
}
运行代码,发现 main() 方法中 a
和 b
的值并没有被交换,不科学啊,为什么呢。
首先 main 方法中执行:int a = 3; int b = 5;
,方法中的变量会在栈中开辟内存空间:
继续执行,调用 swap 方法,swap 方法的形参 int a, int b
和 main 方法中的变量 a
和 b
根本就不是同一个变量,只是这里恰巧名称相同而已,只是将 main 方法中的变量的值赋值给了 swap 方法的形参。
然后执行 swap 方法中的代码,创建了 temp 变量,调换了方法中 a
和 b
的值:
再看一段代码:
import java.util.Arrays;
public class HelloJava {
public static void main(String[] args) {
int[] array = new int[]{3, 5};
// 调用交换方法
swap(array);
System.out.println(Arrays.toString(array)); // [5, 3]
}
/**
* 交换两个变量
*/
public static void swap(int[] array) {
int temp = array[0];
array[0] = array[1];
array[1] = temp;
}
}
咋蒙圈了呢,为什么 main()
方法中的 array 数组中元素值被交换了呢?
这是因为数组变量是引用变量,存储的是数组的地址。
首先 main 方法中执行: int[] array = new int[]{3, 5};
,会分别在栈中和堆中开辟内存空间:
然后调用 swap 方法,将 main() 方法中的变量 array 的地址值传递给了 swap 方法的形参 array :
这里也是值传递,只是传递的是引用的地址,所以指向的是堆中同一块内存地址,此时在 swap() 方法中,修改 array 变量,修改的就是同一块内存地址了,所以 main 方法中的变量 array 数组就被修改了。
所以在 Java 中方法的传参是值传递。
6.3 方法的返回值
如果方法没有返回值,返回值类型为 void
:
public static void add(int a, int b) {
System.out.println(a / b);
return; // 可以省略
}
return
语句如果在最后,那么可以省略,也可以提前返回:
public static void add(int a, int b) {
if (b == 0) {
return; // 提前返回,方法终止
}
System.out.println(a / b);
}
方法 return 后,方法将终止执行。
如果方法有返回值,则在定义方法的时候,需要指定方法的返回值类型。
举个栗子:
public class MethodTest {
/**
* 返回值类型为int
*/
public static int add(int a, int b) {
// 定义方法
return a + b;
}
public static void main(String[] args) {
int sum = add(1, 2); // 调用方法
System.out.println(sum); // 3
}
}
上面定义了一个方法,将两个数相加,然后将结果返回,并打印出来。
方法的返回值需要定义变量来接收,不过,如果你只是调用,不接收结果,也是没有问题的:
public static int add(int a, int b) {
// 定义方法
return a + b;
}
public static void main(String[] args) {
add(1, 2); // 调用方法
}
6.4 方法的嵌套调用
方法的嵌套调用就是一个方法可以调用另外一个方法,另外一个方法还可以继续调用其他的方法,依此类推。
举个栗子:
下面定义了2个方法,funA() 和 funB(),并在main()方法中调用了funA() ,然后在funA()中调用了 funB()。
public class MethodTest {
public static void funB() {
System.out.println("----b");
}
public static void funA() {
System.out.println("----a1");
funB();
System.out.println("----a2");
}
public static void main(String[] args) {
funA();
}
}
执行结果:
----a1
----b
----a2
我们会发现在 funA() 中调用 funB() 后,funB() 执行完成,重新回到了 funA() 继续执行。
6.6 方法的重载
什么是方法的重载?
方法的重载就是 Java 中允许存在多个方法,它们的方法名一样,但是参数不一样,注意不包括返回值。
举个栗子:
public class HelloJava {
public static void main(String[] args) {
String name = "doubibiji";
int year = 13;
sayHello(name, year); // 调用方法
}
// 定义方法
public static void sayHello() {
}
// 重载方法1
public static void sayHello(String name) {
}
// 重载方法2
public static void sayHello(String name, int age) {
}
// 重载方法3
public static void sayHello(int age, String name) {
}
}
上面定义了4个 sayHello
的方法,但是是被允许的,因为它们的参数不一样(包括参数类型和顺序、参数个数),在调用的时候根据,根据传递的参数来判断调用的那个方法。
注意返回值类型不同,不构成重载。
再看一下下面的代码:
public class HelloJava {
public static void main(String[] args) {
add(1, 2); // 会调用哪一个
}
public static void add(int x, int y) {
System.out.println("int相加:" + (x + y));
}
public static void add(float x, float y) {
System.out.println("float相加:" + (x + y));
}
public static void add(long x, long y) {
System.out.println("long相加:" + (x + y));
}
public static void add(double x, double y) {
System.out.println("double相加:" + (x + y));
}
}
在上面的代码中,add(1, 2)
会调用哪一个重载的方法呢?首先会调用 add(int x, int y)
。
如果将 add(int x, int y)
方法删掉,代码是不会报错的,运行后,会调用 add(long x, long y)
,自动进行类型的提升。
如果再将 add(long x, long y)
方法删掉,运行后,会调用 add(float x, float y)
,再删掉 add(float x, float y)
的话,最终会调用 (double x, double y)
。
Java API 中有很多重载的方法,例如用于数学计算的 Math 类。
6.7 方法的递归
什么是方法的递归?
方法的递归,就是在一个方法内部,又调用了这个方法自己,这个就是方法的递归。
举个例子:
public class HelloJava {
public static void funA() {
System.out.println("----");
funA();
}
public static void main(String[] args) {
funA();
}
}
上面的方法,执行 funA()
,然后在 funA()
内部又调用了自己,那么会重新又调用了 funA()
方法,然后又调用了自己,这样就会一直无限调用,变成了无限循环调用,执行的时候很快就报Stack Overflow的错误,栈溢出。
所以方法的递归有时候是很危险的,很容易无限调用,造成栈溢出,程序崩溃。所以方法的递归调用一定要注意 结束或跳出递归 的条件。
例如我们写一个用递归求阶乘的方法:
public class HelloJava {
/**
* 求阶乘
*/
public static int factorial(int num) {
if (num <= 1) {
return 1;
}
return num * factorial(num - 1);
}
public static void main(String[] args) {
int num = 5;
int result = factorial(num);
System.out.println(num + "的阶乘为:" + result); // 120
}
}
以 5 举例,求 5 的阶乘,调用了 factorial方法
,则计算 5 乘以 4 的阶乘,然后求 4 的阶乘,重新调用了 factorial方法
,然后计算 4 乘以 3 的阶乘,一次类推,一直得到1的阶乘,然后向上返回。
递归方法一定得有结束的条件,否则就会无限递归导致栈溢出错误。