方法的使用
- BIT-5-方法的使用
- 绪论
- 1. 方法概念及使用
- 1.1什么是方法
- 1.2 方法定义
- 1.3 实参和形参的关系(重要)
- 1.4 没有返回值的方法
- 2. 方法重载
- 2.1 为什么需要方法重载
- 2.2 方法重载概念
- 3. 递归
- 3.1 生活中的故事
- 3.2 递归的概念
- 3.2 递归执行过程分析
- 3.3 递归练习
BIT-5-方法的使用
【本节目标】
-
掌握方法的定义以及使用
-
掌握方法传参
-
掌握方法重载
-
掌握递归
绪论
在编程中也是一样,某段功能的代码可能频繁使用到,如果在每个位置都重新实现一遍,会:
-
使程序变得繁琐
-
开发效率低下,做了大量重复性的工作
-
不利于维护,需要改动时,所有用到该段代码的位置都需要修改
-
不利于复用
因此,在编程中,我们也可以将频繁使用的代码封装成"函数"(方法),需要时直接拿来链接(即方法名–方法的入口地址)使用即可,避免了一遍一遍的累赘。
1. 方法概念及使用
1.1什么是方法
方法就是一个代码片段. 类似于 C 语言中的 “函数”。方法存在的意义(不要背, 重在体会):
-
是能够模块化的组织代码(当代码规模比较复杂的时候).
-
做到代码被重复使用, 一份代码可以在多个位置使用.
-
让代码更好理解更简单.
-
直接调用现有方法开发, 不必重复造轮子.
比如:在日历程序中经常要判断一个年份是否为闰年,则有如下重复性代码:
int year = 1900;
if((0 == year % 4 && 0 != year % 100) || 0 == year % 400){
System.out.println(year+"年是闰年");
}else{
System.out.println(year+"年不是闰年");
}
那么如何把重复性的代码封装成方法呢?
1.2 方法定义
方法语法格式
// 方法定义
修饰符 返回值类型 方法名称([参数类型 形参 ...]){
方法体代码;
[return 返回值];
}
public static 返回值 方法名(形式参数列表) {
方法体;
}
public static void main(String[] args) {
}
示例一:实现一个函数,检测一个年份是否为闰年
public static boolean isLeapYear(int year) {
if((0 == year % 4 && 0 != year % 100) || 0 == year % 400){
return true;
}else{
return false;
}
}
public static void main(String[] args) {
boolean ret = isLeapYear(2020);
System.out.println(ret);
}
方法名采取小驼峰的方式
实参和形参要一一匹配
返回值要和接收类型匹配
示例二: 实现一个两个整数相加的方法
public static int add(int a,int b) {
return a+b;
}
public static void main(String[] args) {
int sum = add(2,3);
System.out.println(sum);
}
在Java当中函数的定义位置没有要求,C语言当中一般从上到下。
代码示例: 计算 1! + 2! + 3! + 4! + 5!
public static int fac(int i) {
int fac = 1;
for (int j = 1; j <= i ; j++) {
fac *= j;
}
return fac;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
int sum = 0;
for (int i = 1; i <= num ; i++) {
//计算i的阶乘
sum += fac(i);
}
System.out.println(sum);
}
【注意事项】
-
修饰符:现阶段直接使用public static 固定搭配
-
返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成void
-
方法名字:采用小驼峰命名
-
参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
-
方法体:方法内部要执行的语句
-
在java当中,方法必须写在类当中
-
在java当中,方法不能嵌套定义
-
在java当中,没有方法声明一说
补充
在多个.c文件当中只能有一个main函数,在多个.java文件当中可以有多个main函数。
1.3 实参和形参的关系(重要)
代码示例: 交换两个整型变量
public class TestMethod {
public static void main(String[] args) {
int a = 10;
int b = 20;
swap(a, b);
System.out.println("main: a = " + a + " b = " + b);
}
public static void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
System.out.println("swap: x = " + x + " y = " + y);
}
}
// 运行结果
swap: x = 20 y = 10
main: a = 10 b = 20
可以看到,在swap函数交换之后,形参x和y的值发生了改变,但是main方法中a和b还是交换之前的值,即没有交换成功。
原因;形参是实参的一份临时拷贝,对形参的改变,不影响实参。
而Java当中并没有指针,所以关于交换数字的解决办法,我们后期学习了类和对象之后,才能解决这个问题。
1.4 没有返回值的方法
方法的返回值是可选的. 有些时候可以没有的,没有时返回值类型必须写成void
代码示例
class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
print(a, b);
}
public static void print(int x, int y) {
System.out.println("x = " + x + " y = " + y);
}
}
另外, 如刚才的交换两个整数的方法, 就是没有返回值的
2. 方法重载
2.1 为什么需要方法重载
public static int sumInt(int a,int b) {
return (a+b);
}
public static void main(String[] args) {
int sum1 = sumInt(1,2);
System.out.println(sum1);
double sum2 = sumInt(1.5,2.5);
System.out.println(sum2);
}
//Java编译器会报错
//不兼容的类型: 从double转换到int可能会有损失
由于参数类型不匹配, 所以不能直接使用现有的 add 方法.
一种比较简单粗暴的解决方法如下:
public static int sumInt(int a,int b) {
return (a+b);
}
public static double sumDou(double a,double b) {
return (a+b);
}
public static void main(String[] args) {
int sum1 = sumInt(1,2);
System.out.println(sum1);
double sum2 = sumDou(1.5,2.5);
System.out.println(sum2);
}
上述代码确实可以解决问题,但不友好的地方是:需要提供许多不同的方法名,而取名字本来就是让人头疼的事
情。那能否将所有的名字都给成add
呢?
答案是可以的,Java当中允许方法名相同,在调用方法的时候会根据传递的参数类型匹配不同的方法。
java当中可以取相同的名字会根据传递的参数,调用对应的方法。
但是不能方法名和形式列表内容完全相同
代码如下
public static int sum(int a,int b) {
return (a+b);
}
public static int sum(int a,int b,int c) {
return (a+b+c);
}
public static double sum(double a,double b) {
return (a+b);
}
public static void main(String[] args) {
int sum1 = sum(1,2);
System.out.println(sum1);
double sum2 = sum(1.5,2.5);
System.out.println(sum2);
}
2.2 方法重载概念
在自然语言中,一个词语如果有多重含义,那么就说该词语被重载了,具体代表什么含义需要结合具体的场景。
在Java中方法也是可以重载的。
在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。
注意:
-
方法名必须相同
-
参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同)
-
与返回值类型是否相同无关
-
和参数的类型名无关
- 编译器在编译代码时,会对实参类型进行推演,根据推演的结果来确定调用哪个方法
补充:
public class Test {
public static int add(int a,int b){
return a+b;
}
public static int add(int a,int b,int c){
return a+b+c;
}
public static int add(int... arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
public static void main(String[] args) {
System.out.println(add(1, 2, 3, 4));
}
}
//打印
10
这里的int... arr
与int[] arr类似
我们把int... arr
这样的参数称为可变参数,但是这样代码的可读性降低,我们一般不这样写。
6.作业+递归+数组
3. 递归
3.1 生活中的故事
从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:
"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
“从前有座山,山上有座庙…”
“从前有座山……”
上面的故事有个特征:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解决之后,原问题就迎刃而解了。
3.2 递归的概念
一个方法在执行过程中调用自身, 就称为 “递归”.
递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.
例如, 我们求 N!
起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! == N * (N-1)!
递归的必要条件:
-
自己调用自己
-
递归出口
代码示例:递归求N的阶乘
public static int fac(int n) {
if(n > 1) {
return n * fac(n-1);
}else {
return 1;
}
}
public static void main(String[] args) {
System.out.println(fac(5));
}
//------------------
//编译器运行结果为
//120
3.2 递归执行过程分析
执行过程图
程序按照序号中标识的 (1) -> (8) 的顺序执行.
3.3 递归练习
代码示例1 按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
public static void print(int n) {
if (n < 10) {
System.out.println(n%10);
return;
}
print(n / 10);
System.out.println(n % 10);
}
代码示例2 递归求 1 + 2 + 3 + … + 10
public static int sum(int n) {
if(n ==1) {
return 1;
}
return n + sum(n - 1);
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
System.out.println(sum(num));
}
代码示例3 写一个递归方法,输入一个非负整数,返回组成它的数字之和. 例如,输入 1729, 则应该返回1+7+2+9,它的和是19
public static int func(int n) {
if(n < 10) {
return n;
}
return (n%10) + func(n / 10);
}
代码示例4 求斐波那契数列的第 N 项
public static int fibonacci(int n) {
if(n >2) {
return fibonacci(n - 1) + fibonacci(n - 2);
}
return 1;
}
补充
循环和递归有什么区别呢?
递归的好处是:代码少,不好的地方:不好书写
循环的好处是:容易理解
递归浪费的空间比较多。经常会出现栈溢出的情况。
汉诺塔问题
如果有一个盘子:A->C 1(2^1-1)
两个盘子:A->B A->C B->C 3(2^2-1)
三个盘子:A->C A->B C->B A->C B->A B->C A->C 7(2^3-1)
核心思想:n个盘子,
n-1个盘子从pos1借助pos3移动到pos2.
第n个盘子从pos1->pos3
再将n-1个盘子借助pos1移动到pos3
核心思想:n个盘子先把n-1个放好,n-2个放好……
public static void hanoiTower(int n,char pos1,char pos2,char pos3) {
if(n == 1) {
move(pos1, pos3);
return;
}
hanoiTower(n-1,pos1,pos3,pos2);
move(pos1, pos3);
hanoiTower(n-1,pos2,pos1,pos3);
}
public static void move(char pos1,char pos2) {
System.out.println(pos1 + "->" + pos2);
}
public static void main(String[] args) {
hanoiTower(1,'A','B','C');
System.out.println();
hanoiTower(2,'A','B','C');
System.out.println();
hanoiTower(3,'A','B','C');
}