方法(函数) | 我的日常分享

方法(函数)

方法(函数)

一、方法的概念

案例:控制台打印《静夜思》

1
2
3
4
5
6
7
床前明月光,
---------
疑似地上霜,
---------
举头望明月,
---------
低头思故乡。

以现有的知识,使用两种方法打印以上效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestFunction{
public static void main(String[] args){
System.out.println("窗前明月光,");
System.out.println("----------");
System.out.println("疑似地上霜,");
System.out.println("----------");
System.out.println("举头望明月,");
System.out.println("----------");
System.out.println("低头思故乡。");
for(int i=0;i<10;i++){
System.out.print("-");
}
System.out.println();
}
}

运行结果:

image-20220121154500993

以上代码无论是直接打印,还是使用循环打印,都无法避免冗余的代码。

二、方法的定义与调用

  • 概念:实现特定功能的一段代码,可反复使用。

  • 定义语法:

    1
    2
    3
    public static void 方法名称(){
    //方法主体
    }
  • 定义的位置:方法定义在类的内部,不能嵌套定义,与main方法并列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //位置1
    public class TestDefinitionFunction{
    //位置2
    public static void main(String[] args){
    //位置3
    }
    //位置4
    }
    //位置5

    正确的位置为:位置2位置4

  • 定义第一个方法printSign

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class TestFunction{
    public static void main(String[] args){
    System.out.println("窗前明月光,");
    System.out.println("疑似地上霜,");
    System.out.println("举头望明月,");
    System.out.println("低头思故乡。");
    }
    public static void printSign(){
    for(int i=0;i<10;i++){
    System.out.print("-");
    }
    System.out.println();
    }
    }
  • 方法的调用

    在需要执行方法代码的位置,通过方法名称进行调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class TestFunction{
    public static void main(String[] args){
    System.out.println("窗前明月光,");
    printSign();
    System.out.println("疑似地上霜,");
    printSign();
    System.out.println("举头望明月,");
    printSign();
    System.out.println("低头思故乡。");
    printSign();
    }
    public static void printSign(){
    for(int i=0;i<10;i++){
    System.out.print("-");
    }
    System.out.println();
    }
    }

    运行结果:

    image-20220121160350362

调用方法时,会优先执行方法内部代码,结束后,返回到方法的调用处,继续向下执行。

三、方法的参数

  1. 多数情况下,方法与调用者之间需要数据的交互;调用者必须提供必要的数据,才能使方法完成对应的功能。
  2. 调用方法时,所传入的数据被称为“参数”。
  3. 定义时的方法的参数被称为形式参数,调用时方法的参数被称为实际参数。
  4. 方法的参数可以让代码功能更加灵活、普适性更高,易于修改及维护。
  • 定义语法:

    “形参”等价于“局部变量的声明”

    1
    2
    3
    public static void 方法名称(形式参数){
    //方法主体
    }
  • 调用语法:

    “实参”等价于“为形式参数局部变量赋值”

    1
    方法名称(实际参数);

1.单个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestFunction{
public static void main(String[] args){
System.out.println("窗前明月光,");
printSign(10);
System.out.println("疑似地上霜,");
printSign(10);
System.out.println("举头望明月,");
printSign(10);
System.out.println("低头思故乡。");
printSign(10);
}
//定义:打印count个分隔符的函数
public static void printSign(int count){
for(int i=0;i<count;i++){
System.out.print("-");
}
System.out.println();
}
}

运行结果:

image-20220121162056768

2.多个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestFunction{
public static void main(String[] args){
System.out.println("窗前明月光,");
printSign(10,'-');
System.out.println("疑似地上霜,");
printSign(10,'*');
System.out.println("举头望明月,");
printSign(10,'#');
System.out.println("低头思故乡。");
printSign(10,'@');
}
//定义:打印count个sign的函数
public static void printSign(int count,char sign){
for(int i=0;i<count;i++){
System.out.print(sign);
}
System.out.println();
}
}

运行结果:

image-20220121162335845

四、返回值与返回值类型

  • 概念:方法执行后的返回结果。

  • 方法执行后,一些情况下无需放回结果;另一些情况下则必须返回结果。

  • 返回值类型:基本、引用、void,同时实际返回值的类型要与定义的返回值类型一致。

  • 定义语法:

    1
    2
    3
    4
    public static 返回值类型 方法名称(形式参数列表){
    //方法主体
    return 返回值;
    }
  • 调用语法:

    变量类型要与返回值类型保持一致。

    1
    变量 = 方法名称();

案例1:

1
2
3
4
5
6
7
8
9
public class TestResultValue{
public static void main(String[] args){
int result = add(5,6);
System.out.println("结果为:"+result);
}
public static int add(int a, int b){
return a+b;
}
}

运行结果:

image-20220121163158486

return关键字

  • 一个方法只能有一个返回值

    1
    2
    3
    4
    public static int calc(int a,int b){
    return a+b;
    return a-b;//错误
    }
  • 当有返回值的方法存在分支结构时,必须保证每一条分支都具有正确的返回值。

    1
    2
    3
    4
    5
    6
    7
    public static String isEven(int num){
    if(num%2==0){
    return "偶数";
    }else{
    return "奇数";
    }
    }

    否则将报错:java: 缺少返回语句

    image-20220121163615429

  • return 的两种用法

    1. 应用在具有返回值类型的方法中

      1
      return value;//表示结束当前方法,并伴有返回值,返回到方法的调用处。
    2. 应用在没有返回值类型(void)的方法中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      return;//表示结束当前方法

      public static void show(){
      for(int i=1;i<=100;i++){
      if(i==50){
      return;
      }
      }
      }

    注意:return与break的区别,break是跳出循环,如果方法中循环语句下方还有代码,则会继续执行,而return则是结束当前方法,后续代码都不会执行到。

五、方法的多级调用与无穷递归

  • 多级调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class TestNestInvoke{
    public static void main(String[] args){
    m1();
    }

    public static void m2(){
    System.out.println("m2() start");
    System.out.println("m2() end");
    }
    }

    运行结果:

    image-20220121164940194

  • 无穷递归

    当方法自己调用自己时,如果没有正确的出口条件,则产生无穷递归。会造成堆栈内存溢出异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class TestRecursionInvoke{
    public static void main(String[] args){
    m1();
    }
    public static void m1(){
    System.out.println("m1() start");
    System.out.println("m1() end");
    }
    }

    运行结果:

    image-20220121170627748

六、递归

  • 什么是递归?

    解决具有既定规律的问题时,在方法内部再次调用自身方法的一种编程方式。

  • 何时使用递归?

    当需要解决的问题可以拆分成若干个小问题,大小问题的解决方式相同,方法中自己调用自己;使用循环解决的常规问题,都可以替换为递归解决。

  • 如何正确使用递归?

    设置有效的出口条件,可以让调用链上的每个方法都可以正确返回,避免无穷递归。

案例1:

  • 循环阶乘:计算5的阶乘。

    循环计算阶乘较为简单,依次与每个值相乘即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class TestFactorial{
    public static void main(String[] args){
    System.out.println(factorial(5));
    }
    public static int factorial(int n){
    int sum = 1;
    for(int i=2;i<=n;i++){
    sum = sum*i;
    }
    return sum;
    }
    }
  • 递归阶乘

    案例1:求5的阶乘

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class TestFactorial{
    public static void main(String[] args){
    System.out.println(getFive(5));
    }
    public static int getFive(int n){
    //return n * 4的阶乘;
    return n * getFour(4);
    }
    public static int getFour(int n){
    //return n * 3的阶乘;
    return n * getThree(3);
    }
    public static int getThree(int n){
    //return n * 2的阶乘;
    return n * getTwo(2);
    }
    public static int getTwo(int n){
    //return n * 1的阶乘;
    return n * getOne(1);
    }
    public static int getOne(int n){
    //return 1的阶乘;1的阶乘就是1
    return 1;
    }
    }

    运行结果:

    image-20220122132845075

案例2:案例1中除了1的阶乘,其他的函数都是相同的部分,于是我们可以抽离出来。

1
2
3
4
5
6
7
8
9
10
11
public class TestFactorial{
public static void main(String[] args){
System.out.println(getFactorial(5));
}
public static int getFactorial(int n){
if(n==1){
return 1;
}
return n * getFactorial(n-1);
}
}

运行结果:

image-20220122133241165

分析图:

递进,每一次推进,计算都比上一次变得简单,直至简单到无需继续推进,就能获得结果。也叫到达出口。

回归,基于出口的结果,逐层向上回归,依次计算每一层的结果,直至回归到最顶层。

image-20220122134229079