• javaSE- 方法的使用


    一、方法的基本用法

    方法就是一个代码片段. 类似于 C 语言中的 “函数”.
    方法存在的意义(不要背, 重在体会):

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

    回忆一个之前写过的代码: 计算 1! + 2! + 3! + 4! + 5!

    public static void main(String[] args) {
            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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个代码中使用双重循环, 比较容易写错.
    接下来我们可以使用方法来优化这个代码

    	/**
         * 计算 n 阶乘
         * @param n
         * @return
         */
        public static int tmp(int n){
            int ret = 1;
            for(int i = 1; i <= n; i++){
                ret = ret * i;
            }
            return ret;
        }
        
        public static void main(String[] args) {
            int sum = 0;
            for (int i = 1; i <= 5; i++) {
                //产生 1 - 5的数
                int ret = tmp(i);//tmp 这个函数就是 计算 n 阶乘
                sum = sum + ret;
            }
            System.out.println("sum = " + sum);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    像上面tmp函数的功能就是计算一个数的阶乘,这就是函数的基本用法,并且这个函数的功能是唯一的

    1.1、方法定义语法

    // 方法定义
    public static 方法返回值 方法名称([参数类型 形参 ...]){
    	方法体代码;
    	[return 返回值];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    1.1.1、代码示例: 实现一个方法实现两个整数相加

    	public static int sumAdd(int x, int y){
            return x + y;
        }
    
        public static void main(String[] args) {
            int a = 10;
            int b = 20;
            int sum = sumAdd(a, b);
            System.out.println("sum = " + sum);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    方法的参数叫做形参,main方法的参数叫实参,形参相当于实参的一份拷贝,这种传参方式被称为 按值传递(传值)
    由此引申出一个知识点, 在Java中 是没有 传址 的概念,只有传值,所以我们在调用方法的时候,只需要注重 形参 和 实参 的类型 和 个数 是否匹配(相同)
    在这里插入图片描述

    1.1.2、既然讲到函数(方法),也就会涉及函数栈帧问题

    想了解 c方面的或者想对比一下的,可以看这篇文章函数栈帧销毁与创建(vs2013)- 修改版
    在这里插入图片描述

    1.1.3、注意事项

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

    1.1.4、方法调用的执行过程

    基本规则

    定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
    当方法被调用的时候, 会将实参赋值给形参.
    参数传递完毕后,就会执行到方法体代码.
    当方法执行完毕之后(遇到 return 语句), 就执行完毕, 回到方法调用位置继续往下执行
    一个方法可以被多次调用.

    1.1.4、实参和形参的关系(重要)

    public static void main(String[] args) {
            int a = 10;
            int b = 20;
            System.out.println("交换前:" + "a = " + a + " b = " + b);
            swap(a, b);
            System.out.println("交换前:" + "a = " + a + " b = " + b);
        }
    
        public static void swap(int x, int y) {
            int tmp = x;
            x = y;
            y = tmp;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述
    原因分析:
    刚才的代码, 没有完成数据的交换.
    对于基础类型来说, 形参相当于实参的拷贝. 即 传值调用
    可以看到, 对 x 和 y 的修改, 不影响 a 和 b
    解决办法: 传引用类型参数 (例如数组来解决这个问题),这个数组 的 时候在讲。

    1.2、没有返回值的方法

    就像上面的swap函数,是没有返回值的

    //有两种形式:
    	public static void swap(int x, int y) {
            int tmp = x;
            x = y;
            y = tmp;
        }
        
        public static void fun(int c){
            return;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    1.3、方法的重载overlord(重点)

    有些时候我们需要用一个函数同时兼容多种参数的情况, 我们就可以使用到方法重载

    错误的例子

    	public static void main(String[] args) {
            int a = 10;
            int b = 20;
            int ret = add(a, b);
            System.out.println("ret = " + ret);
            double a2 = 12.5;
            double b2 = 18.5;
            double ret2 = add(a2, b2);//图 13
            System.out.println("ret2 = " + ret2);
        }
        
        public  static  int add(int x,int y){
            return  x+y;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    由于参数类型不匹配, 所以不能直接使用现有的 add 方法

    那么是不是应该创建这样的代码呢?

    public static void main(String[] args) {
            int a = 10;
            int b = 20;
            int ret = addInt(a, b);
            System.out.println("ret = " + ret);
            double a2 = 10.5;
            double b2 = 20.5;
            double ret2 = addDouble(a2, b2);
            System.out.println("ret2 = " + ret2);
        }
        public static int addInt(int x, int y) {
            return x + y;
        }
        public static double addDouble(double x, double y) {
            return x + y;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如果要计算float类型,那是不是要写一个addFloat函数,那这样不就很麻烦吗
    所以就要使用方法的重载

    1.3.1、使用重载

    	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) {
            return x + y;
        }
        public static double add(double x, double y) {
            return x + y;
        }
        public static double add(double x, double y, double z) {
            return x + y + z;
        }
    
    • 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

    在这里插入图片描述

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

    1.3.2、重载的规则

    参考JavaSE的标准文档
    可以不是用一个类,继承关系也是可以的

    • 方法名相同
    • 方法的参数不同(参数个数或者参数类型)
    • 方法的返回值类型不影响重载
      在这里插入图片描述

    1.4、方法递归

    1.4.1、递归的概念

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

    使用递归之前,需要一个前提
    1. 有一个趋近于终止的条件(停止递归的条件)
    2. 自己调用自己
    
        public static void main(String[] args) {
            func();
        }
        
         这样写,就不满足 使用递归所需的第一个条件 没有一个终止递归的条件
         所以该程序会无限递归死循环,最终导致栈溢出(栈空间不是无限大,是有限,func方法一直调用自己下去,最终肯定是会 爆满/溢出 的)
        因为 每次调用 func方法时,都会为其在栈上空间开辟块自己的空间
        public  static void func(){
            func();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    那我该怎么去写一个可以使用的递归呢?

    1. 有一个趋近于终止的条件(停止递归的条件)
    2. 自己调用自己

    代码示例: 递归求 N 的阶乘
    	public static void main(String[] args) {
            //递归求 N 的阶乘
            int n = 5;
            int ret = fun(n);
            System.out.println(ret);
        }
    
        public static int fun(int n){
            if(n == 1){
                return 1;
            }
            return n * fun(n-1);
        }
    
      	 1! == 1 //这就是我们的起始条件,也是我们的终止条件
    	 2! == 2*1 == 2 * 1!
    	 3! == 3*2*1 == 3 * 2!
    	 4! == 4*3*2*1 == 4 * 3!
    	 5! == 5*4*3*2*1 == 5 * 4!
    	 你发现了 求 5!的值,它是 5 *5-1)!
    	 而  4== 4 * (4-1)3!  == 3 * (3-1)!
    	    2!  == 2 *2-1)!
    	
    	 5! == 5 * 4!== 5 * 4 * 3! == 5 * 4 * 3 * 2! == 5 * 3 * 2 * 1
    	 程序运行 跟我们刚才藏宝殿是一样的,一层一层的抢。从最里面的开始抢
    	 也就从我们终止条件开始
    	   n == 1;  return 1;// 这里的返回值 是返回到 调用它的上一层级(藏宝殿核心是第一层吗,抢完了,肯定去抢第二程啊)
    	   n == 2   return 2* factorial(2-1) == 2 * 1 == 2
    	   n == 3;  return 3 * 2 == 6
    	   n == 4;  return 4 * 6 == 24
    	   n == 5;  return 5 *   ==
    	 考验你们的时候,在下面评论,看你们到底有没有看懂学会
    	
    	递归的字面意思
    	    递归 n * factorial(n-1)l  n-1 就是它传过去的值,称为递, factorial(n-1)返回的值,称为归
    	    结合称为: 递归。
    
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    在这里插入图片描述

    执行过程
    ![public static void main(String[] args) {
            //递归求 N 的阶乘
            int n = 5;
            int ret = fun(n);
            System.out.println(ret);
        }
    
        public static int fun(int n){
            if(n == 1){
                return 1;
            }
            return n * fun(n-1);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    关于 “调用栈”
    方法调用的时候, 会有一个 “栈” 这样的内存空间描述当前的调用关系. 称为调用栈.
    每一次的方法调用就称为一个 “栈帧”, 每个栈帧中包含了这次调用的参数是哪些, 返回到哪里继续执行等信息.
    后面我们借助 IDEA 很容易看到调用栈的内容

    1.4.2、 递归练习

    代码示例1 按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
    public static void main(String[] args) {
            //按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
            int n = 4569;
            print(n);
        }
    
        public static void print(int n){
            if(n > 9){
                print(n / 10);
            }
            System.out.println(n % 10);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    代码示例2 递归求 1 + 2 + 3 + … + 10
    public static void main(String[] args) {
            //递归求 1 + 2 + 3 + ... + 10
            int a = 10;
            int ret = sumAdd(a);
            System.out.println(ret);
        }
    
        public static int sumAdd(int n){
            if(n == 1){
                return 1;
            }
            return n + sumAdd(n - 1);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    代码示例3 写一个递归方法,输入一个非负整数,返回组成它的数字之和. 例如,输入 1729, 则应该返回1+7+2+9,它的和是19
    public static void main(String[] args) {
            //代码示例3 写一个递归方法,输入一个非负整数,返回组成它的数字之和.
            // 例如,输入 1729, 则应该返回1+7+2+9,它的和是19
            int a = 1729;
            int ret = printAdd(a);
            System.out.println(ret);
        }
        public static int printAdd(int num){
            if(num <= 9){
                return num;
            }
            return num % 10 + printAdd(num / 10);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    代码示例4 求斐波那契数列的第 N 项

    斐波那契数列介绍
    斐波那契数列:1,1,2,3,5,8,13,21,34,55,89…

    public static void main(String[] args) {
            ///代码示例4 求斐波那契数列的第 N 项
            //斐波那契数列:1,1,2,3,5,8,13,21,34,55,89...
            int n = 10;
            int ret = fibonacci(n);
            System.out.println(ret);
        }
    
        public static int fibonacci(int n){
            if(n == 1 || n == 2){
                return 1;
            }
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    用 递归的方法来求 斐波那契数,效率很低

    当我们求 fibonacci(40) 的时候发现, 程序执行速度极慢. 原因是进行了大量的重复运算

    public static int cont = 0;
    
        public static void main(String[] args) {
            ///代码示例4 求斐波那契数列的第 N 项
            //斐波那契数列:1,1,2,3,5,8,13,21,34,55,89...
            int n = 40;
            int ret = fibonacci(n);
            System.out.println(ret);
            System.out.println("函数执行了:" + cont + "次");
        }
    
        public static int fibonacci(int n){
            if(n == 1 || n == 2){
                return 1;
            }
            if(n == 3){
                cont++;
            }
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    在这里插入图片描述
    可以使用迭代/循环的方式来求斐波那契数列问题, 避免出现冗余运算

        public static int cont = 0;
    
        public static int fibonacci(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;
                cont++;
            }
            System.out.println("函数执行了:" + cont + "次");
            return cur;
        }
    
        public static void main(String[] args) {
            ///代码示例4 求斐波那契数列的第 N 项
            //斐波那契数列:1,1,2,3,5,8,13,21,34,55,89...
            int n = 40;
            int ret = fibonacci(n);
            System.out.println(ret);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    递归小结

    递归是一种重要的编程解决问题的方式.
    有些问题天然就是使用递归方式定义的(例如斐波那契数列, 二叉树等), 此时使用递归来解就很容易.
    有些问题使用递归和使用非递归(循环)都可以解决. 那么此时更推荐使用循环, 相比于递归, 非递归程序更加高效

  • 相关阅读:
    xmind思维导图的绘制技巧
    Java main方法的形参 和 方法的可变参数
    ERP对接淘宝/天猫/京东/拼多多商品详情数据API接口
    阿里大于发送短信(用户微服务--消息微服务)
    【力客热题HOT100】-【040】101 对称二叉树
    交换机与路由器技术-10-交换机密码恢复
    2022年的暑假
    云图说丨初识华为云微服务引擎CSE
    后羿采集器的使用出现了问题
    个人博客测试报告
  • 原文地址:https://blog.csdn.net/ryy1999/article/details/128068127