函数的出现
在目前为止的开发实现中,我们的代码都在 main 方法中,从上到下进行编写。在此模式下,程序结构会随着功能复杂度的增加而越来越复杂。
在代码中会有不连续的出现重复性代码,无法用循环解决。为了简化代码以及程序的结构,把这些不连续的重复性代码提取出来,封装为一个方法(函数),并且为之取名。在需要用到该方法(函数)代码的位置去调用该方法(函数)。
方法(函数)初步出现,函数出现之后,有一些显而易见的好处(意义):
- 代码量少了很多,减少了无谓的重复劳动;
- 代码便于维护,如果重复的代码需要修改时,只需要在单独的方法(函数)中修改一次即可;
- 如果能够在函数命名时,做到 “见名知意” ,对代码的可读性也是一种提高
在后期由于模块化编程思想的出现:我们除了把不连续的重复性的代码提取封装为函数(方法)外,也会把独立的功能模块封装为函数(方法)。
当然,函数(方法)的出现还有一些其他的益处:
- 设计人员不用考虑实现的细节,只需要理清楚在整个程序中有哪些模块、模块与模块的关系,这样更有利于他们对于程序宏观的控制
- 开发人员可以只考虑自己负责的模块的实现,达到多人同时开发的效果,在效率上大大提升
函数的定义
语法格式如下:
// 函数(方法)的声明部分
public static 返回类型 函数名(形参列表){
// 函数(方法)的实现部分 功能的实现语句块
}
// public static:修饰符 -- 定义本函数(方法)的访问范围和访问方式
// 返回类型 -- 定义本函数在执行结束后,是否有返回的结果,该结果是什么数据类型(能且只能书写一个)
// 函数名(方法名) -- 定义函数(方法)是给方法取的名字,属于自定义标识符,按规范书写即可
// 形参列表 -- 定义本函数在执行前需要从使用者身上得到的初始数据,如果初始数据没有,那么可以不写
// 函数的实现部分 --- 方法体:书写任意的语句,实现该方法所要完成的功能即可
定义函数(方法)时注意:
- 函数(方法)本身是一个单独的模块,所以它的定义是在其他方法之外的
- 所有的函数(方法)都是平行关系,千万注意不要在一个函数(方法)里面定义另一个方法
- 函数(方法)的定义只能写在 Java 类的花括号中
函数的调用
调用函数的语法: 类名.方法名(参数列表); ,当调用的是本类的方法时,类名.可以省略
调用的方法,可以把这个方法看做是一个数据,可以有种调用的方式:
方式1:直接输出 方式2:赋值调用 方式3:输出调用
函数的调用设计到参数,参数分为两类:形参和实参
形参(形式上的参数) :一般用于方法定义 ,只是一个参数形式,并不是真正的数据,具体的参数数据,需要在方法调用时由使用者给出,所以定义方法的变量声明语句,先确定参数的类型、个数和顺序,如果有一个写 参数类型 参数名 即可,如果参数有多个,中间用逗号分隔:参数类型 参数名1,参数2 ,如果没有形参可不写。
实参(实际的参数,方法调用时由使用者传入的数据):方法调用时,由方法使用者,根据方法形参的类型和个数,传入相对于的数据,利用 实参 获取数据 数据来源于方法的提供者。
下面将用一个小练习来演示函数的调用,示例代码如下:
// 功能 计算 1+2+3+……+100 的和 并将之输入,如果,使用者提供 20 则计算 10 + 11+ …… +100的和
// 2、调用方法时:
public static void main(String[] args) {
// 不能在方法内部定义方法
// 调用 computeSum 方法,就可以执行该代码的功能
// 计算从20开始 20 - 100的和,实参
Method.computeSum(20); // 类名.方法名
// 计算从 45 开始,45 - 100的和
computeSum(45); // 省略了 类名.
}
// 1、定义函数时:
public static void computeSum(int startNum){//computeSum为函数名,形参定义在小括号里面
//方法体 实现方法功能
int sum = 0;
for (int i = startNum;i <= 100;i++){// i 表示 1-100 范围的数
sum = sum + i;
}
System.out.println(sum);
}
返回数据类型函数
语法格式如下:
public static 返回值类型 方法名(参数){
// 方法体;
return 返回值;
}
定义方法在执行结束后是否有返回结果,描述它的数据类型(能且只有一个),
方法在执行结束后,有需要返回的数据直接书写数据类型名称(基本或引用)即可。
返回数据类型时需要注意:
- 如果方法定义了返回类型(不是 void),就必须给方法返回数据,如果不给会报错;
- 如果方法定义了返回类型(不是 void),那么调用方法的语句可以将其视作为一个数据进行操作(声明变量保存、输出、通过运算符对其进一步运算都可);
- 如果方法定义的返回类型为 void ,那么不能使用 return 返回值; 这一句语法,且不能将调用该方法的语句视作为数据进行操作;
- 如果方法定义的返回类型为 void ,虽然不能使用 return 返回值; 这一语法,但能使用 return 关键字,因为 return 关键字,除了可以用于返回数据之外,还可以用于结束函数。这时 return 表示结束整个函数。
函数的调用栈
方法调用栈(方法执行顺序/流程),当多个方法出现嵌套调用时,最先执行的方法最后结束,此时,呈现出一种“先进后出”的栈结构,就称之为 方法调用栈。
在使用方法调用方法时,注意不要产生方法本身调用自己情况,否则就会出现StackOverFlowError(堆栈溢出错误)。
函数调用栈,是一个先入后出的结构,栈,类似于一个水桶,只有一个入口和出口,把 方法不断调用的过程,叫做 入栈或者 压栈,当我们栈顶部的方法执行完毕后,该方法的空间会自动在栈内销毁出栈。
为了帮助大家更好理解,示例代码如下:
// 方法调用栈 方法执行顺序/流程
public static void main(String[] args) {
// A方法 调用B 在B方法中调用C 在B方法中调用C 在C方法中调用D
testA();
}
public static void testA(){
System.out.println("进入A方法");
testB();
System.out.println("结束A方法");
}
public static void testB(){
System.out.println("进入B方法");
testC();
System.out.println("结束B方法");
}
public static void testC(){
System.out.println("进入C方法");
testD();
System.out.println("结束C方法");
}
public static void testD(){
System.out.println("进入D方法");
System.out.println("结束D方法");
}
方法重载
当我们一个类中需要定义多个同名方法时,需要使用到方法重载,
在同一个类中,声明多个同名方法,这些方法参数列表不同,将其称为方法的重载。
参数列表不同:参数类型,或参数个数,或参数顺序不同,参数列表和参数名无关,编写运行代码时,Java 编译器可以通过这些同名方法的参数去进行区分。
方法的递归调用
当方法自己调用自己时,根据方法的执行流程,会形成一种类似于循环执行该方法的情况,就叫“递归”。
递归一定要有结束的条件,没有结束条件的话,会进入无止境的递归,最终导致内存被完全的消耗,会造成堆栈溢出的错误。
递归的三个要素:
- 一个结束递归的代码
- 一个继续递归的调用
- 一个停止或继续递归的测试
可采用递归算法解决的问题有这样的特点:
- 原始的问题转化为解决方法相同的新问题;
- 而新问题的规模要比原始的问题小;
- 新问题又转化为规模更小的问题;
- 直到最终归结到最基本的情况---递归的终结的条件。