UseOfMethods - 方法的使用 - Java

答案是可以的

写法: 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 = abb(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! == 321 == 3 * 2!

4! == 432*1 == 4 * 3!

5! == 54321 == 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 Scanner(System.in);

int n = sc.nextInt();

System.out.println(factorial(n));

sc.close();//关闭输入

}

public static int factorial(int n){

if(1==n){

return 1;

}

return n*factorial(n-1);// n * (n-1)!

// (n-1)! == factorial(n-1); 即 factorial == !

}

}// 图19,不懂没关系,我帮你搬下来,对着看

1! == 1 //这就是我们的起始条件,也是我们的终止条件

2! == 2*1 == 2 * 1!

3! == 321 == 3 * 2!

4! == 432*1 == 4 * 3!

5! == 54321 == 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)返回的值,称为归

结合称为: 递归。

你赚的钱迟早是有一天会花完的。。。

赶快跟着我一起卷。

图19

在这里插入图片描述


再来看几道例题


求 1~n之间的和

import java.util.Scanner;

public class UseOfMethods {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt();

System.out.println(sumadd(n));// 图 20

sc.colse()// 关闭输入

}

public static int sumadd(int n){

if(n==1){

return 1;

}

return n + sumadd(n-1);

}

}// 自己琢磨,不懂在下方评论,一起探讨。(n 比 n-1 多了1,看似废话,其实是真理)

图 20

图20

在这里插入图片描述


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

import java.util.Scanner;

public class UseOfMethods {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt();

print(n);

sc.close();//关闭输入

}

public static void print(int n){

if(n<10){

System.out.println(n);

return;

}

print(n/10);// 在调用自身是,将 n/10 的值 作为下个自身函数的形参

// 最终肯定有 剩一位数的时候,一位数(0~9)肯定是小于10的,别问为什么,问就不会!

// 这个也就是我们终止条件

// 假设我们输入的是 1234 , 在调用自身是,将 n/10 的值 作为下个自身函数的形参,那么 剩余的最后一位就是 1.所以从1开始打印

// 其次是2 (12%10)、 3(123%10)、4(1234%10)

System.out.println(n%10);

}// 图 21

}

图21

在这里插入图片描述


递归执行过程分析


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


&ensp;

继续实践

写一个递归方法,输入一个非负整数,返回组成它的数字之和. 例如,输入 1729, 则应该返回1+7+2+9,它的和是19

import java.util.Scanner;

public class UseOfMethods {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt();

System.out.println(sum(n));

sc.close();

}

public static int sum(int n){

if(n<10){

return n;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

image

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
组成它的数字之和. 例如,输入 1729, 则应该返回1+7+2+9,它的和是19

import java.util.Scanner;

public class UseOfMethods {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt();

System.out.println(sum(n));

sc.close();

}

public static int sum(int n){

if(n<10){

return n;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-x2ZlFpjr-1713435510328)]

[外链图片转存中…(img-vygNt7IU-1713435510329)]

[外链图片转存中…(img-cOD8lALT-1713435510329)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

[外链图片转存中…(img-floGbf4n-1713435510329)]

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值