UseOfMethods - 方法的使用 - Java

Posted Dark And Grey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UseOfMethods - 方法的使用 - Java相关的知识,希望对你有一定的参考价值。

文章目录

什么是方法?

方法就是一个代码片段. 类似于 C 语言中的 “函数”.

(其实方法就是函数另一个称呼,只不过为了区分 C语言和Java,所以在Java中叫方法,C叫函数,唯一不同的就是两者实现的方式)

方法可以理解为是一个 功能,实现某种我们想要达到的效果,而且这个功能是可以被重复使用的。

 打个比方: 方法就好像是 制作面包的磨具,如果你想得到一个这样形状,或者说达到某种效果,你就去使用该磨具。(该函数能完成某一个 功能/任务/效果)
而且,你可以换材料,但你不能用它做其他的东西,这个磨具是专门正对面包的(函数实现的功能,是专门针对某一种情况)
 也就是说你做面包要使用材料必须属于面粉这一类的东西,无非即使质量的好坏
(函数接收的参数值是可以改变大小的,类型不能变,否则参数类型不匹配,是无法成功调用函数的功能)

方法存在的意义(不要背, 重在体会):

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

方法的语法格式

public static 返回值 方法名称(形式参数列表){
  函数体/方法体;
}

代码实例1(求 1~n的和)

import java.util.Scanner;

public class UseOfMethods {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);// 为输入数据做准备
        int n = scanner.nextInt();// 接收一个整型数据,将其赋给 n
        sumadd(n);// 调用函数(方法)sumadd,并向 sumadd方法 传参(n)。
    }
    public static void sumadd(int n){// 这里接收参数,和C语言一样,可以定义一个相同的变量名,来接收传参数。但必须类型和参数个数要相同
        int sum =0;// 定义一个求和变量,将其初始化
        for(int i =0;i<=n;i++){
            sum += i;// 将1~10的每个数值累加起来
        }
        System.out.println(sum);// 输出累加的结果
    }
}// 图1

图 1


不知道你们有没有发现程序中 我们的 sumadd方法没有写在 main方法上面而是下面,学过C的朋友可能会问 “不需要在前面声明函数吗?”

答案是不用,在Java中 是没有函数声明的概念,它就是要用的时候,直接去调用


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


如果你想让方法(函数)返回一个值,就是说带回一个值回到主函数,再对其打印也行

程序实例2(程序实例1的改版)

import java.util.Scanner;
public class UseOfMethods {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);// 为输入数据做准备
        int n = scanner.nextInt();// 接收一个整型数据,将其赋给 n
        int ret = sumadd(n);// 调用方法(函数)sumadd,并想 sumadd方法 传参(n)。
        System.out.println(ret);
    }
    public static int sumadd(int n){// 这里接收参数,和C语言一样,可以定义一个相同的变量名,来接收传参数。但必须类型和参数个数要相同
        int sum =0;// 定义一个求和变量,将其初始化
        for(int i =0;i<=n;i++){
            sum += i;// 将1~10的每个数值累加起来
        }
        return sum;
    }
}// 图2,注意圈起来的部分
    

图2


有的人可能会说 我可不可以 不创建一个 与方法的返回值相同类型的变量 来接收它的值,直接输出行吗
答案是可以的
写法: System.out.println(sumadd(n));
因为 sumadd 是返回值的输出语句,只是将它返回来的值给打印了而已。
如果方法的返回值为 void(无返回),那么程序会报错(输出语句会说:我都准备好了,你就给我看这? 就这!


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

   每个函数,在被调用的时候,都会开辟栈帧(开辟属于自身的内存空间)

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


注意事项

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

方法调用的执行过程

基本规则

    定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
 比如:
 int b = sumadd();,它在执行该语句时,遇见调用方法的代码,会先去执行调用方法,下面程序它暂时不会去关。
  System.out.println(b); 调用方法的代码执行完了之后,才会轮到该语句执行

 当方法被调用的时候, 会将实参赋值给形参.(int a = 10; sumadd(a); public static void sumadd(int A); 这里的A,其实就是a值的一份拷贝  )
    参数传递完毕后, 就会执行到方法体代码.
    当sumadd方法执行完毕之后(遇到 return 语句), 就执行完毕,,同时将其值带回main方法中,并将其赋给变量b(类型匹配)继续往下执行
    
    并且一个方法可以被多次调用.

代码示例: 计算 1! + 2! + 3! + 4! + 5!

public class UseOfMethods {
    public static void main(String[] args) {
        int n  =5;
        int sum = factorialSum(n);// 调用函数计算 5 的阶乘和是多少,将其赋给sum
        System.out.println(sum);
    }
    public  static  int factorialSum(int n){
        int sum = 0;
        int  ret = 1;
        for(int i = 1;i<=n;i++){
            ret *= i;// 每个数字的阶乘
            sum+=ret;// 将每个数字的阶乘累计相加
        }
        return sum;
    }// 图 4
}

图4


使用方法的优点也就体现出来了,创建一个专门计算阶乘和的函数,只要我的传参没问题,它就能帮我完成想要的效果。
注意 自定义方法名,要让人看得懂是什么意思,见名知其用。看到名字知道它是用来干什么的,接下看代码,往这个思想靠拢,大大提升代码的阅读性


实参和形参的关系

举个例子(交换a与b值)

我们先来用常规形式来写

这里是交换实参

public class UseOfMethods {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println("交换前:a = " + a + " b = " + b);
//        1
//        int tmp =a;
//        a =b;
//        b = tmp;// 图 5

        //2.异或
        a = a^b;
        b = a^b;// b = a^b^b(b^b == 0) == 0 ^ a == a
        a = b^a;// a = b^a == a(b) ^ a^b(a) == 0 ^ b == b
        // 图 6
        // 两种方法均可以交换两者数据
        System.out.println("交换后:a = " + a + " b = " + b);
    }
}

图5

图6


用方法来写

public class UseOfMethods {
    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;
    }

}

图7


图8


由图 7,8.可知这样写不会发生任何改变,因为只是交换形参,也就是说 交换的是 swap方法的参数
不是在交换 main方法的参数,这里的x,y,只是a和b的值的一份拷贝
有人肯定会说像C里面一样,传地址呗
记住 在Java中,是无法取得栈上变量的地址的,也就是说取不到局部变量的地址( 图 9)

图9


如果要去做,只能把 a 和 b 的值,放到堆上(动态空间上)放在堆上的都是对象。
这道题留着 我们讲 对象和类,或者数组的时候,再进行讲解。以我们现在的基础还不足以解决该问题。


没有返回值的方法

方法的返回值是可选的. 有些时候可以没有的

代码示例

public class UseOfMethods {
    public static void main(String[] args) {
        int a = 0;
        int b = 1;
        sum(a,b);
    }
    public static void sum(int x,int y){
        System.out.println(x+y);
        // 我在方法中就已经将我们想打印的结果 图 10,所以不需要返回值
        //但是规定 无返回,就不能 return 返回一个值
//        return x+y; 图 11,都报错了,就别谈运行
        // 但是可以这么写
        return ;// 虚晃一枪,你能奈我何?
        // 图 12
    }
}

图10


图11


图12


方法的重载

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

代码示例

public class UseOfMethods {
    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;
    }
}

图13


由此,我们需要 方法的重载:一个函数同时兼容多种参数的情况

那我们怎么实现呢?

代码实例2

public class UseOfMethods {
    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 b3 = 9.0;
        double ret2 = add(a2, b2,b3);//图 13
        System.out.println("ret2 = " + ret2);
    }
    public  static  int add(int x,int y){
        return  x+y;
    }
    public static double add(double x,double y,double z){
        return (x+y+z);
    }
}// 图 14

图14


记住

 方法重载的重点在于函数名要相同,参数个数或者类型要不同。(两者中必有一者不同)
 而返回值 不重要,只要你接收返回的值的类型是相匹配的就行,要不然无法进行赋值。

另外切记

不要写的一摸一样(参数个数和类型都相同),这样相当于 一个变量被重复定义,是会出错的

例如:


    public  static  int add(int x,int y){
        return  x+y;
    }

    public  static  int add(int x,int y){
        return  x+y;
    }

再来看一个错误示范

public class UseOfMethods {
    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 (double)(x+y);
    }
}// 图 15

图 15

由图我们可以看出这样写也是不行的,正如我前面所说的 返回值,不是重点参数类型和个数才是重点
只要你参数的类型 和 个数 一模一样,无论你返回值是什么,都算方法的重定义, 程序是会报错的(方法的重定义这是我个人理解)


&ensp

图16

由此我们可以看出 方法的重载 有多么强大。
我们可以根据 参数的类型和个数 来为我们的方法,加载 几个新的方法,具有更多的功能
现在你知道了吧 方法的重载,有多爽!


补充一个知识点:

 如果一个类的两个方法(无论是在同一个类中声明,还是都由一个类继承的,或者一个声明和一个继承的【总的来说不一定是同一个类里的】)
 具有相同的名称,但签名不是重写等价的,则称该方法名为重载。 (了解即可)

由上我可以总结出 方法重载的规则

1. 方法名要相同
2. 方法的参数不同(参数个数或者类型,两者选其一,或者都选,反正至少有一个因素是不同的)
3. 方法的返回值类型不影响重载
4. 当两个方法的名字相同, 参数也相同, 但是返回值不同的时候, 不构成重载.


方法递归

递归的概念

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

使用递归之前,需要一个前提

1. 有一个趋近于终止的条件(停止递归的条件)
2. 自己调用自己(让它自己扇自己,扇疼了再停下来,不要问我为什么我举这个例子!)


代码示例

public class UseOfMethods {
    public static void main(String[] args) {
        func();
    }

     这样写,就不满足 使用递归所需的第一个条件 没有一个终止递归的条件
     所以该程序会无限递归死循环,最终导致栈溢出(栈空间不是无限大,是有限,func方法一直调用自己下去,最终肯定是会 爆满/溢出 的)
    因为 每次调用 func方法时,都会为其在栈上空间开辟块自己的空间
    public  static void func(){
        func();
    }
}// 图17

图17


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

代码实例2

public class UseOfMethods {
    public static void main(String[] args) {
        func(3);
    }
    public  static void func(int n){
        if(n==1){// 递归的终止的条件,因为 n == 3.每次自己调用自己时,n都会减1,调用 3 次,就结束了
            System.out.println(n);
            return ;
        }
        func(n-1);
        System.out.println(n);
    }
}// 图18

图 18


其实终止条件,相当于数学里的起始条件

 你这样想 假设我们不知道 n 为多少,那我们怎么办?先从初始情况分析呗
 当 n =0时,怎么办?
  n =1时,怎么办?
 大于1又怎么办?
en~,是不是有点懂了,当我们处理递归时,我们可以有两种思维
当我们去思考它的终止条件是什么? 你可能会被套娃,套成zz。
这时候,我们反过来想想 这个值的初始值是多少,满足怎样的条件?(就比如说要大于,小于或等于某个条件)
是不是打开新思路了?‘’

思考递归的时候,横向思考:根据递推公式(个人理解递推公式跟数学的通项公式有点像)去思考
代码执行:纵向执行的(从上往下,一条条执行)

在坐的某些人,经常会自己代入某个数字,然后,拿着数字和代码一个个展开,很烧脑的
虽然我很喜欢这样,但是我只是在我掌握递归规律(递推公式)之后,对代码执行过程的好奇,去展开很小很小的范围。比如1、2、3之类的
太复杂了,对不起,我不玩了。。。

这点不值得提倡,我们需要的是掌握其递归规律,才是重中之重。

举个例子

求 n的阶乘(n==5)
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)!
以此类推 直到遇到 1! 时,他的返回值里不再带其他阶乘,此时 1!是不是很适合作为我们递归的终止条件
 由此我们发现了 递推公式为 n * (n-1)!
5! == 5 * 4!== 5 * 4 * 3! == 5 * 4 * 3 * 2! == 5 * 3 * 2 * 1(这里只是帮助你们理解,别学,上面的递推公式才是你们该学的)

代码实现

   import java.util.Scanner;

public class UseOfMethods{
    public static void main(String[] args) {
        Scanner sc = new <

以上是关于UseOfMethods - 方法的使用 - Java的主要内容,如果未能解决你的问题,请参考以下文章

如何防止 JA-SIG CAS spring 安全重定向循环?

关于ja3指纹处理

curl_cffi: 支持原生模拟浏览器 TLS/JA3 指纹的 Python 库

第四章:jianja2模板用法

为啥我的 ja.lproj 不显示日语?

Java中最快的子字符串搜索方法是啥