Java方法
- ✔️前言
- 一,方法的基本用法
- 方法的定义规则
- 方法的执行过程
- Java理解方法(函数)栈帧
- 实参和形参的关系
- 二,方法的重载
- 使用重载的目的
- 重载的规则
- 三,递归
- 递归公式解决问题
- 递归分析过程
- 代码递归路线图
- 形象分析图
- 递归练习
- ✨总结
一,方法的基本用法
对于初学者来说,方法的概念是第一次听说,但其实他就类似于C语言中的函数,是一个可以多次使用,并且完成单一功能的代码块。
那么,为什么要引入方法的概念呢?
举一个例子演示使用方法进行编程的好处:
例如,编程实现1!~ 5!求和。
如果用普通方法实现,需要使用到循环嵌套技术:
public class Method { /** * 求1! ~ 5!的求和 * @param args */ public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 5; i++) { int ret = 1; for (int j = 1; j <= i; j++) { ret *= j; } sum += ret; } System.out.println(sum); }}
使用嵌套循环比较容易写出bug,而利用方法就可以有效避免,并且能将功能单一化:
public class Method { /** * 求n的阶乘 * @param args */ public static int fac(int n) { int ret = 1; for (int i = 1; i <= n; i++) { ret *= i; } return ret; } /** * 求1~5的阶乘之和 * @param args */ public static int sumFac(int n) { int sum = 0; for (int i = 1; i <= n; i++) { sum += fac(i); } return sum; } public static void main(String[] args) { int n = 5; int ret = sumFac(n); System.out.println(ret); }}
使用两个不同的方法完成两个独立的功能,这种写法更加体现代码的可读性和可维护性~
方法的定义规则
在Java中的使用规则需要用到类和对象的概念,但那不是本文主要讲述的内容,所以这里不多赘述,只需要知道初学者大部分使用的方法都有固定的写法:
例如:
public class Method { public static void func() { System.out.println("hehe"); } public static void main(String[] args) { func(); }}
这里的func方法采用的写法就是
public + static + 返回类型 + 方法名(参数列表)
我们只需要知道他固定的写法格式即可,具体含义在学习了类和对象即可理解,有兴趣的小伙伴也可以看博主的专栏JavaSE
里面记录了JavaSE需要掌握的全部内容~
注意:
- 参数列表中必须给出参数的数据类型。
(可以没有参数) - 方法名必须采用小驼峰写法。
总结:
public和static在这里有特殊的含义,但不在本文作详细介绍,有兴趣可以看博主专栏JavaSE。
在方法定义中,可以没有参数,但如果有参数,一定要指明参数类型。
方法定义中,可以没有返回值,但方法定义的返回类型应该是void类型。
方法调用时的参数称为实参,方法定义时的参数称为形参。
方法的定义必须在类当中,但在类当中的具体位置不受约束,可处于代码上下文任意位置。
Java中没有函数声明的概念。
————————————————
方法的执行过程
这里我们需要注意一点,与C语言不同的是,Java中没有函数声明之类的东西,也就是说,我们在写Java代码时,不需要对方法进行声明,这也就意味着,方法的所处的位置是任意的。
我们回忆一下在学习C语言时,函数定义必须在main函数定义之前,否则就需要在使用该函数之前进行函数声明,而Java中因为没有方法声明这种概念,所以也就不需要将方法定义在main函数之前。
下面用一段代码的执行过程分析方法的执行流程:
public class Method { /** * 求n的阶乘 * @param n * @return */ public static int fac(int n) { int ret = 1; for (int i = 1; i <= n; i++) { ret *= i; } return ret; } public static void main(String[] args) { int ret = fac(5); System.out.println(ret); }}
运行结果:
我们在这里分析一下代码运行流程:
注意: main函数中fac方法是方法调用,括号里的是实参,方法定义中参数列表里的是形参,实参和形参的个数必须一致,数据类型必须一一对应。
方法调用基本规则:
- 方法代码定义时,不会被执行,只有被调用后才会被执行,也就意味着同一个方法可能被执行多次。
- 方法在调用时,会将实参临时拷贝给形参。(具体只是可以看图解函数栈帧 - 函数的创建与销毁)。
- 参数传递(拷贝)后,就会开始执行方法的代码。
- 当方法代码被执行完之后(遇到return)就会返回被调用方法中,继续执行下面的代码。
- 一个方法可以被多次调用。
Java理解方法(函数)栈帧
Java是面向对象的语言,其反汇编的封装做得比较完善,所以不容易分析其方法栈帧的原理,所以这里博主通过画图的方式简单介绍即可。
- 我们知道,任何程序的开端都是main方法,而所有的方法都是在栈上以压栈的形式开辟的。
- 在main方法里调用func方法,则会在栈顶为func方法开辟栈帧空间。
- 当func方法return之后,则销毁func方法的栈帧,并带回返回值交给main方法中调用方法的执行语句。
实参和形参的关系
举个简单的例子:交换两个整数
public class Method { public static void swap(int x, int y) { int tmp = x; x = y; y = tmp; } public static void main(String[] args) { int a = 3; int b = 5; System.out.println(a); System.out.println(b); swap(a, b); System.out.println(a); System.out.println(b); }}
运行结果:
可以发现,这里无法通过形参的交换而改变实参。
画图解释:
- 这里有两个实参。
- 将实参做一份临时拷贝给形参
- 通过第三个变量交换形参。
- 交换后
交换后形参发生了改变,但实参并没有进行交换,当该swap方法执行完之后,其方法栈帧将会被销毁,也就意味着这个方法什么功能都没能实现。
到这,可能有码友会问,那能不能类似于C语言,进行传址调用呢?
答案是否定的,在Java中没有取地址(&)这种操作,也没有指针的概念,只有类似的引用,所以想要完成这样的交换功能,只能通过引用的方式,但如果要介绍这道题的正确解法,需要用到类和对象以及引用的知识,就不多介绍,有需要的可以去JavaSE专栏找到相应文章。
这里只把相应代码展示,以供参考:
public class Method { public static void swap(int[] arr) { int tmp = arr[0]; arr[0] = arr[1]; arr[1] = tmp; } public static void main(String[] args) { int[] arr = {10, 20}; swap(arr); System.out.println("a = " + arr[0] + " b = " + arr[1]); } }
二,方法的重载
在Java中方法是可以重载的(overload)。
使用重载的目的
用一个简单的例子说明为什么需要重载。
比如:我要写一个加法方法。
public class Method { public static int add(int x, int y) { return x + y; } public static void main(String[] args) { int a = 3; int b = 5; int ret = add(a, b); System.out.println(ret); }}
这个方法只适用于两个整型的相加,如果我想让三个整型相加或者两个浮点数相加则做不到,必须重新写一个新的方法,其方法名不能和该方法相同。
而方法重载的概念就是改变参数列表,方法名不变,看以下代码:
public class Method { public static int add(int x, int y, int z) { return x + y + z; } public static int add(int x, int y) { return x + y; } public static void main(String[] args) { int a = 3; int b = 5; int c = 1; int ret1 = add(a, b); int ret2 = add(a, b, c); System.out.println(ret2); }}
此时的public static int add(int x, int y, int z)和public static int add(int x, int y)就构成了重载的关系。
无独有偶。
public class Method { public static double add(double x, double y) { return x + y; } public static int add(int x, int y, int z) { return x + y + z; } public static int add(int x, int y) { return x + y; } public static void main(String[] args) { int a = 3; int b = 5; int c = 1; int ret1 = add(a, b); int ret2 = add(a, b, c);// System.out.println(ret2); double d1 = 3.4; double d2 = 2.4; double ret3 = add(d1, d2); System.out.println(ret3); }}
此时的public static double add(double x, double y)和public static int add(int x, int y, int z)以及public static int add(int x, int y)都构成了重载的关系。
这样写代码就可以共用一个函数名,通过不同的参数列表对不同的数据类型实现相同的功能。
重载的规则
- 方法名必须相同。
- 参数列表必须不同(个数,类型)。
- 与返回类型无关。
首先,方法名必须相同,因为实现的是相同的功能,我们可以理解为是同一个方法对于不同数据的衍生处理。
例如:
public class Method { public static int add1(int x, int y) { return x + y; } public static int add2(int x, int y) { return x + y; } public static void main(String[] args) { int a = 3; int b = 5; int ret1 = add1(a, b); int ret2 = add2(a, b); System.out.println(ret1); System.out.println(ret2); }}
此时的add1和add2是完全不同的两个方法,他们不构成重载关系。
满足重载关系的条件还有参数列表的不同,也就是说参数个数,类型,有一个满足不同即可。
最后一点,也是初学者最容易产生误区的一点,方法的返回类型并不能影响重载。
也就是说如果方法名相同,参数列表不同的两个方法,不管返回类型是否一样,都构成重载关系。
而如果方法名相同,参数列表也相同的两个方法,不管返回类型是否一样,都不构成重载关系。
三,递归
以上概念来自百度百科,虽然话术比较官方,但还算言简意赅。
其实简单来说,程序运行时,某一个方法自己调用自己,就被称作递归。
其实递归很好理解,他就类似于我们高中学的"数学归纳法",或者又类似于"通项",我们就是要找出其起始条件,推出递归公式即可完成任务。
递归公式解决问题
public class Method { public static int fac(int n) { if (n > 1) { return n * fac(n - 1); }else { return 1; } } public static void main(String[] args) { int ret = fac(5); System.out.println(ret); }}
运行结果:
又比如要求斐波那契数列,我们也可以直接使用递归公式。
public class Method { public static int fib(int n) { if (n == 1 || n == 2) { return 1; }else { return fib(n - 1) + fib(n - 2); } } public static void main(String[] args) { int ret = fib(10); System.out.println(ret); }}
运行结果:
递归分析过程
代码递归路线图
递归的过程其实是横向的,接下来画图分析。
就拿阶乘举例:
假设给定n为3。
- 下面展示全过程
- 接下来就是归过程
此时就完成了递归的全过程,最终将3!答案6返回给调用方法。
为了方便理解,再用另一种方法描述。
形象分析图
同样还是用阶乘的代码来描述。
如图:
假设这是一个阶乘方法的流程,从上至下运行。
遇到递归调用,则重新开辟栈帧然后进入新的栈帧。
最后一个fac方法走完之后(遇到return)往回归。
这样就完成了递归的内容,并将返回值带回。
说了那么多概念和解释,用几道简单的小例题加以巩固。
递归练习
- 示例代码1: 按顺序打印出一个数字的每一位(例如 1234 打印出 1 2 3 4)
public class Method { /** * 按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4) * @param args */ public static void print(int n) { if (n < 10) { System.out.print(n + " "); }else { print(n / 10); System.out.print(n % 10 + " "); } } public static void main(String[] args) { print(1234); }}
代码示例2: 递归求 1 + 2 + 3 + … + 10
public class Method { /** * 递归求 1 + 2 + 3 + ... + 10 * @param args */ public static int sum(int n) { if (n > 1) { return n + sum(n - 1); }else { return 1; } } public static void main(String[] args) { int ret = sum(10); System.out.println(ret); }}
代码示例3: 写一个递归方法,输入一个非负整数,返回组成它的数字之和。 例如,输入1729,则应该返回1+7+2+9,它的和是19。
public class Method { /** * 写一个递归方法,输入一个非负整数,返回组成它的数字之和。 例如,输入 1729, 则应该返回1+7+2+9, * 它的和是19 * @param args */ public static int sumEveryOne(int n) { if (n < 10) { return n; }else { return n % 10 + sumEveryOne(n / 10); } } public static void main(String[] args) { int ret = sumEveryOne(1729); System.out.println(ret); }}
✨总结
本文主要介绍了Java方法的概念和用法,并拓展了Java方法引出的方法重载以及方法递归,希望本文能对大家的学习有帮助,最后求一波三连支持~
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/70217.html