📚博客主页:爱敲代码的小杨.
?专栏:《Java SE语法》
??感谢大家点赞👍🏻收藏?评论?🏻,您的三连就是我持续更新的动力??
在编程的过程中,经常会出现一部分代码多次使用的情况,比如计算多边形面积,输出固定格式的文字等。
今天我们就来学习方法。
方法就是一个代码片段,类似于C语言的“函数”。
方法存在的意义:
方法的语法格式:
修饰符 返回值类型 方法名称(形参列表){
// 方法体
return 返回值;
}
代码示例:实现一个两个整数相加的方法
public static int add(int a, int b) {
int c = a + b;
return c;
}
【注意事项】:
public static
固定搭配void
方法调用过程:
调用方法—>传递参数—>找到方法地址—>执行被调方法的方法体—>被调方法结束返回—>回到主调方法继续往下执行
【注意事项】
代码示例:计算两个整数相加
public class Main {
public static void main(String[] args) {
int x = 10;
int y = 20;
int ret = add(x, y);
System.out.println(ret);
}
public static int add(int a, int b) {
int c = a + b;
return c;
}
}
方法调用过程:
DeBug
内存图
方法是放在方法区中的,被调用的时候,需要进入到栈内存中运行
一旦程序遇到return
或者方法执行结束,就会把当前方法栈帧就从栈上进行销毁(回收)
方法的形参相当于数学函数中的自变量,比如:1 + 2 + 3 + … + n的公式为sum(n) =(1 + n) * n / 2
Java中方法的形参就相当于sum函数中的自变量n,用来接收sum函数在调用时传递的值的。形参的名字可以随意取,对方法都没有任何影响,形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。
public class Main {
public static void main(String[] args) {
getSum(10); // 10是实参,在方法调用时,形参n用来保存10
getSum(100); // 100是实参,在方法调用时,形参n用来保存100
}
private static int getSum(int n) { // n 是形参
return (1 + n) * n / 2;
}
}
再比如:
public class Main {
public static void main(String[] args) {
add(2,3); // 2 和 3 是实参,在调用时传给形参a 和 b
}
public static int add(int a, int b) {
return a + b;
}
}
方法的返回值是可选的. 有些时候可以没有的,没有时返回值类型必须写成void
代码示例:
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 20;
print(a,b);
}
private static void print(int x, int y) {
System.out.println("x = " + x + ",y = " + y);
}
}
return
问题
如果没有返回值的方法要写return
,则return
后面不能加任何返回值。
return
后面的语句不会被执行,return
表示方法的结束
由于参数类型不匹配, 所以不能直接使用现有的 add
方法.
一种比较简单粗暴的解决方法如下:
public class Main {
public static void main(String[] args) {
int x = 10;
int y = 20;
System.out.println(addInt(x,y));
double a = 1.1;
double b = 1.2;
System.out.println(addDouble(a,b));
}
public static int addInt(int a, int b) {
return a + b;
}
public static double addDouble(double a, double b) {
return a + b;
}
}
上述代码确实可以解决问题,但不友好的地方是:需要提供许多不同的方法名,而取名字本来就是让人头疼的事情。那能否将所有的名字都给成 add
呢?
在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。
public class Main {
public static void main(String[] args) {
int x = 10;
int y = 20;
System.out.println(add(x,y));// 调用add(int, int)
double a = 1.1;
double b = 1.2;
System.out.println(add(a,b));// 调用add(double, double)
double c = 1.3;
System.out.println(add(a,b,c));// 调用add(double, double,double)
}
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
public static double add(double a, double b, double c) {
return a + c;
}
}
注意:
方法名必须相同
参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
与返回值类型是否相同无关
在同一个作用域中不能定义两个相同名称的标识符。比如:方法中不能定义两个名字一样的变量,那为什么类中就可以定义方法名相同的方法呢?
方法签名即:经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方法完整的名字。
从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:
"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
“从前有座山,山上有座庙…”
“从前…”
上面的两个例子有个共同的特征:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了。
一个方法在执行过程中调用自身, 就称为 “递归”.
递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.
例如, 我们求 N!
起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!
递归的必要条件:
代码示例
public class Main {
public static void main(String[] args) {
fun();
}
public static void fun() {
fun();
}
}
上述代码就是一个最简单的递归。
但是存在错误:出来栈溢出错误的时候,就说明结束条件不对或者没有结束条件
代码示例:递归求 N 的阶乘
public class Main {
public static void main(String[] args) {
System.out.println(factor(5)); // 120
}
public static int factor(int n) {
if (n == 1) {
return 1;
}
return factor(n - 1) * n; // factor()方法调用自己
}
}
递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 "方法执行结束之后, 回到调用位置继续往下执行.
代码示例:递归求 N 的阶乘
public class Main {
public static void main(String[] args) {
System.out.println(factor(5));
}
public static int factor(int n) {
System.out.println("函数开始, n = " + n);
if (n == 1) {
System.out.println("函数结束, n = 1 ret = 1");
return 1;
}
int ret = n * factor(n - 1);
System.out.println("函数结束, n = " + n + " ret = " + ret);
return ret;
}
}
/*
函数开始, n = 5
函数开始, n = 4
函数开始, n = 3
函数开始, n = 2
函数开始, n = 1
函数结束, n = 1 ret = 1
函数结束, n = 2 ret = 2
函数结束, n = 3 ret = 6
函数结束, n = 4 ret = 24
函数结束, n = 5 ret = 120
120
*/
执行图: