栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java】带你从零到一系列4(方法的使用详解)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java】带你从零到一系列4(方法的使用详解)

前言:
对于代码来说,如果一直都是在main函数内写,那么像大型工程中几千几万行的代码,难道每次想找需要修改的bug都要一点一点审吗,所以我们有了方法这个概念。

每文一图:

让我们走进方法的学习ba!

方法的使用详解:
  • 一.方法的基本用法
    • 1.什么是方法(method)
    • 2.方法定义语法
    • 3.方法调用的执行过程
    • 4.实参和形参的关系(重要)
  • 二.方法的重载
    • 1.重载要解决的问题
    • 2. 使用重载
  • 三.方法递归
    • 1.递归的概念
    • 2.递归执行过程分析
    • 3.递归经典题目
  • 4.总结:

一.方法的基本用法
1.什么是方法(method)

首先我们来了解一下,什么是方法。方法就是一个代码片段,类似于 C 语言中的 “函数”。

对于方法的意义,我们可以总结,但重在体会:

  1. 是能够模块化的组织代码(当代码规模比较复杂的时候)。
  2. 做到代码被重复使用, 一份代码可以在多个位置使用。
  3. 让代码更好理解更简单。
  4. 直接调用现有方法开发, 不必重复造轮子。

在上一集中,我们说过一个代码,计算1-5的阶乘的和,如果我们按照以前的写法是这样子的:

 public static void main(String[] args) {
 //在main函数中写
        int sum = 0;
        for (int i = 1; i <= 5; i++) {
            int tmp = 1;
            for (int j = 1; j <= i; j++) {
                tmp *= j;
            }
            sum += tmp;
        }
        System.out.println("sum = " + sum);
    }

这里有两个循环,如果有时候不留神就会写错,而且阅读起来还比较不好理解,如果使用方法,每一个方法对应一部分运算,就可以更清晰明了了。我们来写一遍:

public static int fac(int n) {
//计算每一个数的阶乘
        int ret = 1;
        for (int i = 1; i <= n; i++) {
            ret = ret *i;
        }
        return ret;
    }

  public static int facSum(int n) {
  //计算阶乘合起来的和  
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum = sum + fac(i);
        }
        return sum;
    }

    public static void main(String[] args) {
    //输入n 输出阶乘和的结果
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        System.out.println(facSum(n));
    }

是不是,我们将其分为三部分,当阶乘出错我们就去找每个数阶乘的那一部分,如果是和出错了就找和的那部分,然后更改就可以了。

这就是方法的优势。


2.方法定义语法

看见方法的优势了,那我们就开始学习方法到底是怎么样的语法吧。

基本语法:

// 方法定义
public static 方法返回值 方法名称([参数类型 形参 ...]){
 方法体代码;
 [return 返回值];
}
// 方法调用
返回值变量 = 方法名称(实参...);

这样看可能很枯燥看不懂,我们在代码中演示:

//用简单的加法来演示

//方法的定义
public static int sum(int a,int b){
//这里 int 就是方法返回值的类型,然后sum就是方法名称,后面括号的就是类型和形参
        return a+b;
        //如果有返回值的就要写return 和 返回值
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        int ret = sum(a, b);
        //方法调用 调用sum方法 传参a和b 接收返回值 
        System.out.println("ret = " + ret);
        //方法调用,方法名sum
    }

这就是方法!多写几次就熟练起来了,至于前面为什么是public static我们会在后面的博客中写道,这里就先统一在创建方法的时候就是默认前面。

当然对于方法的使用也有注意事项:

  1. public 和 static 两个关键字在此处具有特定含义,我们暂时不讨论, 后面会详细介绍。
  2. 方法定义时,参数可以没有,每个参数要指定类型。
  3. 方法定义时,返回值也可以没有,如果没有返回值,则返回值类型应写成 void。
  4. 方法定义时的参数称为 “形参”,方法调用时的参数称为 “实参”。
  5. 方法的定义必须在类之中,代码书写在调用位置的上方或者下方均可。
  6. Java 中没有 “函数声明” 这样的概念。

3.方法调用的执行过程

对于方法我们已经知道怎么创建和使用了,但是它的性质,或者说它用起来对于执行起来是怎么样的,我们也需要来理解一下:

基本规则:

1.定义方法的时候,不会执行方法的代码,只有调用的时候才会执行。

比如:

 public static int sum(int a,int b){
        return a+b;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        System.out.println("hello");
        //无调用 不执行 打印结果 hello
    }

这就展现了方法其中的一个优点,不使用的时候不会调用,我们需要的时候就调用,很方便,我们可以写好一个功能比如计算两数之和,想要就调用,不用就放那,用的时候只需要再调用一下。

2.当方法被调用的时候, 会将实参赋值给形参。

方法之所以可以调用起来的时候和main方法或者其他方法建立起有机关联,就是因为方法被调用的时候,可以将实参赋值给形参,就相当于拿到了你的试卷帮你写。

如:

  public static int sum(int A,int B){
        return A+B;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        System.out.println(sum(a,b));
       //a b 实参传给sum方法的A B 
    }

3.参数传递完毕后,就会执行到方法体代码。一个方法可以被多次调用。

在调用了该方法,又进行完传参之后,就会执行到方法体代码。

如:

    public static int sum(int A,int B){
        System.out.println("hello");
        //会打印hello 因为执行了方法体内代码
        return A+B;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        System.out.println(sum(a,b));
        System.out.println(sum(a,b));//调用俩次输出俩次
       //a b 实参传给sum方法的A B
    }

4.当方法执行完毕之后(遇到 return 语句),就执行完毕,回到方法调用位置继续往下执行。

如果是有返回值的方法,当遇到return 语句就是执行完毕了,如果是void类型的方法,就是把方法体的代码执行下去就结束。然后再回到方法调用的地方去继续执行。

这就是方法调用的执行过程。

最后我们再来举个例子:

public static void main(String[] args) {
            int a = 10;
            int b = 20;
            System.out.println("第一次调用方法之前");
            int ret = add(a, b);
            System.out.println("第一次调用方法之后");
            System.out.println("ret = " + ret);
            System.out.println("第二次调用方法之前");
            ret = add(30, 50);
            System.out.println("第二次调用方法之后");
            System.out.println("ret = " + ret);
        }
        public static int add(int x, int y) {
            System.out.println("调用方法中 x = " + x + " y = " + y);
            return x + y;
        }
    //    执行结果
    //    一次调用方法之前
    //    调用方法中 x = 10 y = 20
    //    第一次调用方法之后
    //            ret = 30
    //    第二次调用方法之前
    //    调用方法中 x = 30 y = 50
    //    第二次调用方法之后
    //            ret = 80

4.实参和形参的关系(重要)

我们再来看一次这个代码:

//代码示例: 交换两个整型变量

public static void swap(int x,int y) {
        int tmp = x;
        x = y;
        y = tmp;
    }

    public static void main(String[] args) {
            int a = 10;
            int b = 20;
            swap(a, b);
            System.out.println("a = " + a + " b = " + b);
            //打印结果 a=10 b=20
    }

这里我们不是调用了方法吗,我们不是将值传过去调用了吗,实际上,这可能并没有你想象的那么简单:

刚才的代码,没有完成数据的交换。对于基础类型来说,形参相当于实参的拷贝。即传值调用。

int a = 10;
int b = 20;

int x = a;
int y = b;

int tmp = x;
x = y;
y = tmp;

实际上,他x和y交换,关我a和b什么事情?这就是传值调用,实际上新建了两个量在操作。

对于它在传值调用,我们的解决办法也是用的,你传你的值过去没用,那我就传存放你的地方过去,直接把你改了(传引用类型参数):

//对于数组就是传的是他的地址(这里只做解释 具体数组可到数组篇去看)
public static void main(String[] args) {
            int[] arr = {10, 20};
            swap(arr);
            System.out.println("a = " + arr[0] + " b = " + arr[1]);
        }
        public static void swap(int[] arr) {
            int tmp = arr[0];
            arr[0] = arr[1];
            arr[1] = tmp;
        }
        // 运行结果
        //    a = 20 b = 10

二.方法的重载

重载,第一次听这个词想到的是什么,重新下载?重新加载?这里可不是这些意思。对于方法的重载,是有些时候我们需要用一个函数同时兼容多种参数的情况,我们就可以使用到方法重载。

1.重载要解决的问题

对于重载,是帮我们解决用一个函数同时兼容多种参数的情况,也就是比如有时候我们使用的方法传参过去的是两个int,有时候是两个double,那么需要定义几个不同类型但作用相同的方法吗,这就用到我们重载了。

//这就是问题所在
public static int add(int x, int y) {
        return x + y;
    }

    public static void main(String[] args) {
            int a = 10;
            int b = 20;
            int ret = add(a, b);
            System.out.println("ret = " + ret);
            
            double a2 = 10.5;
            double b2 = 20.5;
            double ret2 = add(a, b);//不同类型 err
            System.out.println("ret2 = " + ret2);
            //由于参数类型不匹配, 所以不能直接使用现有的 add 方法
    }

所以我们就到重载的part了,重载就是同一个方法,但参数传的是不一样的,系统会自动选择调用到哪个参数的方法处。

就像这样:

 public static void main(String[] args) {
            int a = 10;
            int b = 20;
            int ret = add(a, b);
            System.out.println("ret = " + ret);
            double a2 = 10.5;
            double b2 = 20.5;
            double ret2 = add(a2, b2);
            System.out.println("ret2 = " + ret2);
        }
        public static int add(int x, int y) {
            return x + y;
        }
        public static double add(double x, double y) {
            return x + y;
        }

2. 使用重载

既然我们知道了这个知识,那么我们的方法就可以适用于多个类型了。

比如:

public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int ret = add(a, b);
        System.out.println("ret = " + ret);
        double a2 = 10.5;
        double b2 = 20.5;
        double ret2 = add(a2, b2);
        System.out.println("ret2 = " + ret2);
        double a3 = 10.5;
        double b3 = 10.5;
        double c3 = 20.5;
        double ret3 = add(a3, b3, c3);
        System.out.println("ret3 = " + ret3);
    }
    public static int add(int x, int y) {
    //int类型变量
        return x + y;
    }
    public static double add(double x, double y) {
    //double类型变量
        return x + y;
    }
    public static double add(double x, double y, double z) {
    //不仅是变量,传参的数的数量也可以变
        return x + y + z;
    }

在这里,方法的名字都叫 add。但是有的 add 是计算 int 相加,有的是 double 相加;有的计算两个数字相加,有的是计算三个数字相加。同一个方法名字, 提供不同版本的实现, 称为方法重载。

那么用作用就有规则,我们来看看:重载的规则。

针对同一个类:
1.方法名相同 。
2.方法的参数不同(参数个数或者参数类型)。
2.方法的返回值类型不影响重载。

也就是说,在同一个类上,方法的重载是看方法名+其传参的,方法名系统传参不同就构成重载,无论返回值的类型是什么。

我们来看一下例子:

//错误示范 
//同方法名同传参类型 不同返回类型不构成重载
public static void main(String[] args) {
 int a = 10;
 int b = 20;
 int ret = add(a, b);
 System.out.println("ret = " + ret);
 }
 public static int add(int x, int y) {
 return x + y;
 }
 public static double add(int x, int y) {
 return x + y;
 }
}

结论:

当两个方法的名字相同,参数也相同,但是返回值不同的时候,不构成重载。当两个方法的名字相同,参数不相同,不管返回值,都是重载。


三.方法递归
1.递归的概念

一个方法在执行过程中调用自身, 就称为 “递归”。递归相当于数学上的 “数学归纳法”,有一个起始条件,然后有一个递推公式。

例如, 我们求 N!
起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!

实际上递归就是实现一件事情的过程有许多相同的地方,所以我们可以写一个归纳好的逻辑,然后使用,并在其中自己调用自己。

多说无益,我们直接上代码:

题目:求 N 的阶乘。

按往常,我们可能会写一个for循环,但是我们不是说过有时候可能需要重复使用,所以可以把他写成一个方法。而这个方法,还是递归的。

 public static void main(String[] args) {
        int n = 5;
        int ret = fac(n);
        System.out.println("ret = " + ret);
    }

 public static int fac(int n) {
        if (n == 1) {
            return 1;
        }
        return n * fac(n - 1); // fac调用函数自身
    }
// 执行结果
//    ret = 120

如上面的代码,我们想实现的是求n的阶乘,也就是求n*(n-1)* ...,所以我们可以写出一个递归,每一次递归回去就乘上n-1。

2.递归执行过程分析

什么?对于上面的东西没看懂,其实递归确实是一种抽象的东西,所以我们可以对其进行一定的分析,让我们更好的吸收。

首先,要想理解清楚递归,必须先理解清楚 “方法的执行过程”,尤其是 "方法执行结束之后,回到调用位置继续往下执行。

我们还是以n的阶乘为例,但是我们配上图解,不能理解就画图解!

 public static void main(String[] args) {
        int n = 5;
        int ret = fac(n);
        System.out.println("ret = " + ret);
    }

 public static int fac(int n) {
        if (n == 1) {
            return 1;
        }
        return n * fac(n - 1); // fac调用函数自身
    }
// 执行结果
//    ret = 120

那么这样调用的时候,内存是什么情况呢?

关于 “调用栈”:

方法调用的时候,会有一个 “栈” 这样的内存空间描述当前的调用关系。称为调用栈。每一次的方法调用就称为一个 “栈帧”,每个栈帧中包含了这次调用的参数是哪些,返回到哪里继续执行等信息。

这就是递归。


3.递归经典题目

1.按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)

参考代码:

 public static void print(int num) {
        if (num > 9) {
            print(num / 10);
        }
        System.out.println(num % 10);
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        print(n);
    }

2.递归求 1 + 2 + 3 + … + 10

参考代码:

public static int sum(int num) {
        if (num == 1) {
            return 1;
        }
        return num + sum(num - 1);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        sum(n);
    }

3.求斐波那契数列的第 N 项

斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34。这个数列从第3项开始,每一项都等于前两项之和。

参考代码:

public static int fib(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        return fib(n - 1) + fib(n - 2);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        fib(n);
    }

这里需要注意,当我们求的是40左右的斐波那契数的时候,其实编译器需要运行的时间已经开始变得慢起来了,这是因为递归需要进行了大量的重复运算,所以导致运行过慢。

这里更好的选择是循环(迭代)去解决这个斐波那契数列的问题。

//迭代版斐波那契
public static int fib(int n) {
        int last2 = 1;
        int last1 = 1;
        int cur = 0;
        for (int i = 3; i <= n; i++) {
            cur = last1 + last2;
            last2 = last1;
            last1 = cur;
        }
        return cur;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        fib(n);
    }

4.总结:

递归是一种重要的编程解决问题的方式。

有些问题天然就是使用递归方式定义的(例如斐波那契数列, 二叉树等),此时使用递归来解就很容易.

有些问题使用递归和使用非递归(循环)都可以解决。那么此时更推荐使用循环,相比于递归,非递归程序更加高效。

具体要看要求。


这就是本篇方法的使用详解的全部内容啦,如果觉得还不错或者感觉对你有帮助,不妨点赞关注一键三连,一起学习,共同努力!也可以期待这个系列接下来的博客噢。

链接:都在这里! Java SE 带你从零到一系列

还有一件事:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/396893.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号