方法的使用
1.什么是方法
举个例子 :
有一天,老师布置 作业 ,他将 班长 找来了 办公室,跟 班长 讲 了 今天的作业,让 班长 传递, 班长 得到 指
令,到班级上 跟 同学们 说, 今天的作业是什么 ,
他 告诉 了 同学 A 然后 告诉 了 同学 B ,这时 班长就会 重复 做 很多 次 (将作业内容 一个 一个 告诉 同学
们), 班长 心想 这 不要累死 我, 此时 班长 就 将
作业 写 到了 黑板上 , 这时 就减少了 重复 将 作业 告诉 大家 只要 同学 看 黑板 即可。
这里就是 方法的好处, 将 作业 内容 写到 黑板上,减少了 重复性的工作(一个一个 告诉 同学) ,这时 班长
就可以 干自己的事情, 同学们 看 黑板 就能够 知道 今天的 作业是什么,而不是 然 班长 一遍一遍的重复。
这里 对于编程 也是 一样 :
在编程中也是一样,某段功能的代码可能频繁使用到,如果在每个位置都重新实现一遍,会:
- 使程序变得繁琐
- 开发效率低下,做了大量重复性的工作
- 不利于维护,需要改动时,所有用到该段代码的位置都需要修改
- 不利于复用
因此,在编程中,我们也可以将频繁使用的代码封装成(方法),需要时直接拿来(即方法名–方法的入口
地址)使用即可,避免了一遍一遍的累赘。
那么 我们大致 了解了 方法 , 下面就来看一下 概念。
方法就是一个代码片段. 类似于 C 语言中的 “函数” 。方法存在的意义:
- 是能够模块化的组织代码(当代码规模比较复杂的时候).
- 做到代码被重复使用, 一份代码可以在多个位置使用.
- 让代码更好理解更简单.
- 直接调用现有方法开发, 不必重复造轮子.
了解了 方法 , 直接 来几个 例题我们就能 很好的 了解方法。
2.方法定义
语法格式:
// 方法定义
修饰符 返回值类型 方法名称([参数类型 形参 ...]){
方法体代码;
[return 返回值];
}
1.实现一个方法,检测一个年份是否为闰年
// 使用 方法 判断 一个年份是否 为 闰年
public static void main(String[] args) {
int x = 2000;
System.out.println(Examine(x));
}
public static boolean Examine(int x){
if(x % 4 == 0 && x % 100 != 0 || x % 400 == 0){
return true;
}
return false;
}
可以 我们 就看到 这里我们就完成了 一个 方法,判断 x 是否 为 闰年 , 这里 相比 与 c 语言, 我们 可以看 到
Java 中 的 方法是 没有声明 的 ,这里就 与 c 语言 有 区别 在 java中 是没有 函数声明的概念, 要用的时候 直
接 使用即可。
另外 ,我们 传递的 参数 是 形参 , main 方法内 的 是 实参, 形参 相当于 实参 的 一份 拷贝, 我们 在 c 中
肯定 ,学过 两种 传参 方式 , 方式 一 : 按值传参(传值) 方式二:按地址传参(传地址) 而 java 中 没有传
址 的概念, 只有 传值,这里 就只需要 注意 形参 和 实参 的 类型 和 个数 是否匹配 。
相比 大家 肯定 有 疑问 如果 没有 传址 那么我们 想要 交换 变量 如 a = 10 , b = 20 变为 b = 10, a = 20, 要如何 改变呢?
下面我们就来看看 :
这 里 我们 就 创建了 一个 swap 方法 来 交换 a 和 b , 那么 这里我们 能 不能 交换完成呢?
可以看到 我们编译 后 , 发现没有交换, 其实 这里 就是 按值 传参 , 这时 方法 swap 中 的 变量 a 和 b 是 形参 ,形参 的 改变 ,不会 影响 实参 ,那么
这里到底 如何 改变 呢 ?
方法一: 通过 数组:
方法二 : 通过 对象 (这里 会在 类和 对象中 讲到)。
这里两种 方法 其实 都是 传递 对象,数组 是 引用类型,存放 的 是 堆区 的 地址 (一块空间),这里 也是 对象。
按照我们现在的知识储备 还不能完成 这道例题,这里我们就 在 留下 一个 坑 ,等 到 数组 和 类和对象 在 来 填 这个坑。
下面 就来看 一下 方法的注意事项
【注意事项】
- 修饰符:现阶段直接使用public static 固定搭配
- 返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成
void
- 方法名字:采用小驼峰命名
- 参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
- 方法体:方法内部要执行的语句
- 在java当中,方法必须写在类当中
- 在java当中,方法不能嵌套定义
- 在java当中,没有方法声明一说
知道了 方法的 定义 ,那么 我们来看一下方法 的 执行 过程。
方法调用的执行过程
【方法调用过程】
调用方法—>传递参数—>找到方法地址—>执行被调方法的方法体—>被调方法结束返回—>回到主调方法继续往下
执行
如:
【注意事项】
定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
一个方法可以被多次调用.
方法 只有 调用 时 才能 被 执行 :
public static int sum(int a, int b) {
System.out.println("这里 执行 了 sum 方法");
return a + b;
}
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("hhhhhh");
}
可以看到 没有 调用 sum 方法 ,sum 就没有执行,
方法 可以 多次使用
public static int sum(int a, int b) {
// System.out.println("这里 执行 了 sum 方法");
return a + b;
}
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("第一次使用 sum 方法");
int ret = sum(a,b);
System.out.println(ret);
System.out.println("第二次使用 sum 方法");
int ret2 = sum(ret,a);
System.out.println(ret2);
System.out.println("第三次使用 sum 方法");
int ret3 = sum(ret,b);
System.out.println(ret3);
}
方法 其实 也没 多难,就是 将 可能 多次 使用 的 代码 封装 起来 , 需要使用 的 时候 调用 即可, 下面我们 来 看一下 方法 的 重载。
注意: 重载 这里我们 需要留意一下, 并不是 多难, 因为 在 类和 对象中 ,会有 一个 重写, 这里 就 容易 被 问道 重载 和 重写 的 区别
方法重载
有些时候我们需要用一个函数同时兼容多种参数的情况, 我们就可以使用到方法重载
我们 这里 来 看 两张图片:
这里 就 很形象 的 说明了 重载 , “好人” 的 两种 状态。
这里我们 假设 一个 场景 ,我们 要 相加 两个整数, 两个浮点类型 , 相加 三个整数
public static int sum(int a, int b) {
return a + b;
}
public static double SomDouble(double a, double b) {
return a + b;
}
public static int sum2(int a, int b, int c) {
return a + b + c;
}
这里我们 可以定义 三个 方法 ,创建了 三个 方法名 sum, SumDouble , sum2, 可以发现 本来就 是 一个简单 的 a + b 或 a + b + c , 却 写了 3 个 方法名,如果 在 继续 ,是不是 要写 更多 ,这里我们 就可以使用 重载。
重载概念:在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。
public static int sum(int a, int b) {
return a + b;
}
public static double sum(double a, double b) {
return a + b;
}
public static int sum(int a, int b, int c) {
return a + b + c;
}
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
double f = 10.5;
double e = 20.5;
System.out.println(sum(a,b));
System.out.println(sum(a,b,c));
System.out.println(sum(f,e));
}
这里就 将 3 个方法名 变 为了 一个。这就是 方法的 重载。
这里 就 需要 记住:
方法重载的重点在于函数名要相同,参数个数或者类型要不同。(两者中必有一者不同)
而返回值 不重要,只要你接收返回的值的类型是相匹配的就行,要不然无法进行赋值。
注意:
这我们 看 这两个 sum 除了 返回值 不同 其他 相同,这里 就 报错 了 , 也 更加 验证 了 ,返回值 不重要,只要你接收返回的值的类型是相匹配的就行,要不然无法进行赋值。
重载 总结 :
方法名要相同
方法的参数不同(参数个数或者类型,两者选其一,或者都选,反正至少有一个因素是不同的)
方法的返回值类型不影响重载
当两个方法的名字相同, 参数也相同, 但是返回值不同的时候, 不构成重载
重载学完 , 那么我们 了解 一下 递归 , 相比 学过 c 的同学 就 听过 递归 吧, 那么 java 同样 可以 ,这里 我们就来 了解 一下。
方法递归
递归的 概念: 一个方法在执行过程中调用自身, 就称为 “递归”.
递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式 递推公式是递归的重点,推出它,递归就很好写。
没推出。就。。。嗯~~~
在使用递归 是 我们 要考虑 递归的 几个条件
- 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同 (大事 化小 , 小事化了)
- 递归出口 (递归的 终止 条件)
比如 :我们求 N!
起始条件: N = 1 的时候, N! 为 1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!
这里 如果 没有 终止条件 ,我们的 栈 就会 溢出 。
如:
这我们 就 通过 func 一直 调用 自己 , 每次 调用 func 都会在 栈 区开辟 一块 空间,这样 永无止境 的 调用 func 就会 一直开辟,直到 栈 被 堆满,然后溢出 报错。
下面 就 通过 几个例题 来 了解 一下 递归 。
递归求 N 的阶乘
求 N 的 阶乘 先来看一下 迭代 如何 求解。
public static void main(String[] args) {
int n = 5;
int ret = 1;
for (int i = 1; i <= n; i++) {
ret *= i;
}
System.out.println(ret);
}
这里我们能 的 ret 是不是 通过 1 * 2 * 3 * 4 * 5 得出了 5 的 阶乘 ,
那么 递归 差不多 是 反 过 来 , 传入 参数 5 , 第一次 5 * 4 * 3 * 2 * 1 , 当 n == 1 是 就是 我们 结束 递归 的 终止 条件,
递推 公式 也 很好得到 n * func(n -1)
public static int func(int n){
if(n == 1){
return n;
}
return n * func(n - 1);
}
下面继续
题目二 :按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
这里我们 想要 打印 1 2 3 4 , 我们就可以 通过 :
这里 要打印 4 只需要 1234 % 10 —》 4 , 4 搞定 了 ,我们 要 搞定 3 只需要 1234 / 10 == 123 就 去掉 了 最后 一位
同理 123 % 10 == 3 , 123/10 = 12
12 % 10 = 2 , 12 / 10 = 1
这里 我们 就惊奇 的 发现 ,n 为 个位数 了 这里 终止条件 ,就是 n < 10 这时 直接 打印 n 即可 ,然后 开始 归 。
public static void printf(int n){
if(n < 10){
System.out.print(n + " ");
return;
}
printf(n / 10);
System.out.printf(n % 10 + " ");
}
题目 三: 递归求 1 + 2 + 3 + … + 10
这里 递归 求 1 + 到 10 , 也 非常 简单 , 我们 只需要 直到 当 n = 1 是 就为 终止条件
public static int sum(int n) {
if (n == 1) {
return n;
}
return n + sum(n - 1);
}
题目四 :写一个递归方法,输入一个非负整数,返回组成它的数字之和
. 例如,输入 1729, 则应该返回 1+7+2+9,它的和是19
public static int sum(int n){
if(n < 10){
return n;
}
return sum(n / 10) + n % 10;
}
题目 五 : 求斐波那契数列的第 N 项
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
因为 递归 回 重复 计算 多次 n - 1 和 n -2 ,这里 建议 使用迭代 写 斐波那契数列
public static void main(String[] args) {
int a = 1;
int b = 1;
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for (int i = 3; i <= n; i++) {
int c = a + b;
a = b;
b = c;
}
System.out.println(b);
}
如果 不懂 这里 我们 还可以 画图 了解 , 来 一步一步走 ,了解 他 是 如何 递 的 然后 走到 终止条件, 我们 也能 一步一步 的 归 ,这样 就能 更好的 理解 递归,
虽然 递归 有点抽象,但是我们 只要 宏观的 去看 其实 也还 好, 如果 想要提升 这方面的 能力,可以 在学完链表的 时候,去使用递归来 完成 链表的 一些 题目,
会后 更深的 理解。