方法的概念在之前已经提到过,不在赘述。
一、为什么要有方法
假如我们想要获得一到一百所有数相加的和,和一到五十所有数相加的和,如果不用方法。
//1+2+3+....+100
int sum = 0;
for(int i = 1; i <= 100; i++) {
sum = sum + i;
}
//1+2+3+....+50
int sum = 0;
for(int i = 1; i <= 50; i++) {
sum = sum + i;
}
很明显,这两段代码其实是基本一样的,就是换一下数字而已。这样的代码重复度就很高,用行话说,就是代码冗余度高。为了解决这一问题,我们就引入了方法的概念。
二、方法定义
[修饰符] 返回值类型 方法名(参数列表) {
//方法体
return 返回值;
}
比如,写一个将将两个int类型数相加的方法:
/*
* 定义一个方法,实现两个整数相加---有返回值
* */
public static int add(int a, int b){
b += a;
return b;
}
- 修饰符在某些场合下可以没有,当前修饰符为public static;
- 返回值类型要和返回值的类型一样;
- 方法中可以没有返回值,"return 返回值;"可以写成"return;"或者不写,返回值类型为void;
- 方法名要符合标识符的命名规范;
- 参数列表: 数据类型 参数名1, 数据类型 参数名2, 数据类型 参数名3,多个参数时以逗号隔开,方法当中可以没有参数列表;
- 大括号成对存在;
- 方法要定义在类中。
三、方法调用----使用方法
格式(参数与方法的参数列表的顺序、数量对应):
方法名(参数)
在main中调用我们刚写好的方法:
package com.daw.test;
public class Test1 {
public static void main(String[] args) {
int a = 5, b = 10;
System.out.println(add(a, b));
}
/*
* 定义一个方法,实现两个整数相加---有返回值
* */
public static int add(int a, int b){
b += a;
return b;
}
}
四、方法重载
满足以下条件,称为方法重载:
1. 在同一个类中;
2. 方法名相同,参数列表不同
3. 和返回值类型没有关系
比如,我们刚完成了一个int形相加的方法,此时我们如果也有double型需要相加该怎么办呢,又或者我们有三个数需要相加该怎么办呢。我们可以重载我们的add方法:
package com.daw.test;
public class Test01 {
public static void main(String[] args) {
int a = 5, b = 10;
double c = 5.0, d =10.0;
int e = 1, f = 2, g = 3;
System.out.println(add(a, b));
System.out.println(add(c, d));
System.out.println(add(e, f, g));
}
/*
* 定义一个方法,实现两个整数相加---有返回值
* */
public static int add(int a, int b){
b += a;
return b;
}
/*
* <重载>
* 定义一个方法,实现两个浮点数相加---有返回值
* */
public static double add(double a, double b) {
return a+b;
}
/*
* <重载>
* 定义一个方法,实现三个整数相加---有返回值
* */
public static int add(int a, int b, int c) {
return a+b+c;
}
}
在double型重载时,我们的返回值类型变成了double,但这并不影响我们的重载,重载只和参数列表有关,和返回值类型无关。
五、方法递归
当方法调用方法本身时,称为方法递归
如斐波那契数列,即可使用递归来打印。(斐波那契数列:第一项与第二项都为1,此后的每一项都为其前面两项的和。如食堂的粥,就是斐波那契粥,今天的粥等于昨天的粥加前天的粥。):
package com.daw.test;
public class Test01 {
public static void main(String[] args) {
System.out.println(f(6));
}
/*
* 方法递归
* 递归函数必须要有明确的结束标志,否则会发生栈溢出错误!
* 斐波那契数列 --- 求第n项的值
* */
public static int f(int n) {
if(n == 1 || n == 2){
return 1;
}
return f(n-1) + f(n-2);
}
}
尝试编写运行下面的代码:
package com.daw.test1;
public class Test3 {
public static void main(String[] args) {
Test();
}
public static void Test() {
Test();
}
}
一瞬间,整个控制台被红色字体填满。
当你把右侧的侧边滚动条拉倒最上面,你会看到这个错误的解释。
StackOverflowError:栈溢出,方法递归时,每个方法都会被压入系统的一个栈中。
当你的方法没有退出条件,或者递归次数过多时,都会导致我们系统中的这个栈被塞满,从而导致程序无法再继续运行。
StackOverflow和死循环有点相似,要避免栈溢出,方法就有明确的退出条件。
六、可变参数
1.可变参数必须是参数列表的最后一个参数
2.参数列表中可变参数只能有一个
3.可变参数本质上是一个数组
4.在可变参数中传参,既可以传递多个参数,也可以直接传递一个数组
5.可变参数在方法内部当成数组使用
package com.daw.test;
public class Test02 {
public static void main(String[] args) {
System.out.println(add(1, 2, 3, 4, 5));
int[] arr = {1, 2, 3, 4, 5};
System.out.println(add(arr));
}
public static int add(int... args) { //args即为一个可变参数
int sum = 0;
for(int i : args){
sum += i;
}
return sum;
}
}
七、形式参数与实际参数
形式参数:方法定义时,参数列表中的参数
实际参数:方法调用时,实际传递给方法的参数
package com.daw.test;
public class Test03 {
public static void main(String[] args) {
int m = 5, n = 6;
int sum = add(m, n); //此处的m,n即为实际参数
System.out.println(sum);
}
public static int add(int a, int b) { //a,b为形式参数
return a+b;
}
}
其实很多人都容易把形参和实参的概念搞不清楚。其实很简单,你不要把它看成是计算机里的东西。
有一个西红柿炒鸡蛋的菜谱,说需要西红柿,鸡蛋等等这些原材料,但是在菜谱中写的 “西红柿,鸡蛋” 这些字,其实只是一堆名字,他们并不是真的在菜谱上放一些西红柿、鸡蛋,他们只用来给你解释怎么做这道菜的,这就是形式参数。
但是你如果真想做一道西红柿炒鸡蛋,那你就得拿来真正的西红柿,鸡蛋,然后依据菜谱上说的,把那些名字与这些实物对应,然后去做菜。这些实物,就是实际参数。
简而言之,形式参数是假的,是个代号,我可以说是西红柿,也可以说是番茄,还可以说是特麻头。它叫什么无关,你知道它对应的实际的东西是什么那就行,只要能对应上,你说是 “红色的具有防水外壳内含大量液状物质的神秘圆状物”都行。
八、值传递和引用传递
参数列表中是基本数据类型时,其为值传递
值传递
:方法体内的操作,对实际参数无任何影响。
package com.daw.test;
public class Test03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int m = 5, n = 6;
swap(m, n);
System.out.println("m = " + m);
System.out.println("n = " + n);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
输出内容可看出。
swap内部,m对应a,n对应b,互换成功;swap外部,m,n并没有互换。
参数列表中不是基本数据类型时,其为引用传递
引用传递
:方法体内的操作,会对实际参数的“值”产生影响
package com.daw.test;
public class Test03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] x = {3, 2, 1};
int[] y = {5, 6};
swap(x, y);
System.out.println("x[0] = " + x[0]);
System.out.println("y[0] = " + y[0]);
}
public static void swap(int[] a, int[] b) {
int temp = a[0];
a[0] = b[0];
b[0] = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
输出内容可看出。
swap内部,x[0]对应a[0],y[0]对应b[0],互换成功;swap外部,x[0],y[0]也互换了。
这一区别的主要原因是,方法不真的这我们给它提供的这个实际参数直接去用了,而是把我们提供这个参数的值给复制了,又另整了一个新的变量以供方法使用。
但我们前面学习了,引用数据类型的值其实是个地址,你整个新的去用了,但也是改的这个地址上的数据,但地址本身本来就不会变化。所以实际参数也就跟着一起改变了。