函数Functions

代码新格式

以前
1 2 3 4 5
public class Main { public static void main(String[] args) { // 执行代码 }}
现在
1 2 3-4+5 6 7+8+9+10
public class Main { public static void main(String[] args) { // 执行代码 new Main().run(); } public void run(){ // 执行代码 }}

注意:代码文件名、class 后面的名字 和 new 后面的名字,必须完全一致!

其它地方照抄,不要动

何时使用

如果你之后写代码,要用到函数,且还没学会 JAVA 112 - 高级内存与面向对象,

就需要这个格式,会减少很多问题

函数功效预览

使用前

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
public class Question { public static void main(String[] args) { new Question().run(); } public void run(){ Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); int command = Console.readInt(); while (command != 0){ double value1 = Console.readDouble(); double value2 = Console.readDouble(); double result = 0.0; if (command == 1) { result = value1 + value2; } else if (command == 2) { result = value1 - value2; } else if (command == 3) { result = value1 * value2; } else if (command == 4) { result = value1 / value2; } Console.println(result); Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); command = Console.readInt(); } }}

功能抽离 与 转接调用

1 2 3 4 5 6+7+8+9+10+11+12+13+14+15 16 17 18*19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
public class Question { public static void main(String[] args) { new Question().run(); } // 功能抽离 public void printMenu(){ Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); } public void run(){ // 转接调用 printMenu(); int command = Console.readInt(); while (command != 0){ double value1 = Console.readDouble(); double value2 = Console.readDouble(); double result = 0.0; if (command == 1) { result = value1 + value2; } else if (command == 2) { result = value1 - value2; } else if (command == 3) { result = value1 * value2; } else if (command == 4) { result = value1 / value2; } Console.println(result); Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); command = Console.readInt(); } }}
收益

化繁为简,段落归纳

将一大部分逻辑紧凑的代码进行打包

打包后 需要起个名字,可以用来总结段落大意

代码重用

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43*44 45 46 47
public class Question { public static void main(String[] args) { new Question().run(); } // 功能抽离 public void printMenu(){ Console.println("请选择操作"); Console.println("1. 加法"); Console.println("2. 减法"); Console.println("3. 乘法"); Console.println("4. 除法"); Console.println("0. 退出"); } public void run(){ // 转接调用 printMenu(); int command = Console.readInt(); while (command != 0){ double value1 = Console.readDouble(); double value2 = Console.readDouble(); double result = 0.0; if (command == 1) { result = value1 + value2; } else if (command == 2) { result = value1 - value2; } else if (command == 3) { result = value1 * value2; } else if (command == 4) { result = value1 / value2; } Console.println(result); // 代码重用 printMenu(); command = Console.readInt(); } }}
收益

代码重用,重复代码消除

函数基础语法

基本元素

变量基本元素
1 2 3 4 5 6 7 8 9 10
public class Main{ public static void main(String[] args) { new Main().run(); } public void run(){ int a = 1; Console.println(a); }}

变量有 三个基本元素

声明

赋值

使用 / 取值

定义 = 声明 + 赋值

函数基本元素
1 2 3 4 5 6 7 8 9 10 11 12 13
public class Func{ public static void main(String[] args) { new Func().run(); } public void run(){ f(); } public void f(){ Console.println("Hello"); }}

函数也有 三个基本元素

声明

实现

使用 / 调用

定义 = 声明 + 实现

函数的声明与实现

1 2 3
public void 函数名() { <statements>}

函数声明后,上下均可使用

注意:函数的声明 应该出现在 有一个缩进的位置

调用语法

1
函数名();

执行规则

遇到函数,原函数暂停

执行被调用函数

结束后,再恢复执行原函数

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public class Func{ public static void main(String[] args) { new Func().run(); } public void run(){ 1 f(); 2 } public void f(){ 3 }}

执行顺序:1 3 2

挑战
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
public class Func{ public static void main(String[] args) { new Func().run(); } public void run(){ Console.println(11); h(); Console.println(12); f(); Console.println(13); } public void f(){ Console.println(21); g(); Console.println(22); h(); Console.println(23); } public void g(){ Console.println(31); h(); Console.println(32); } public void h(){ Console.println(41); }}
答案
run {
    11
    h {
        41
    }
    12
    f {
        21
        g {
            31
            h {
                41
            }
            32
        }
        22
        h {
            41
        }
        23
    }
    13
}

函数生命周期Function Lifecycle

函数内的变量

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public class Func{ public static void main(String[] args) { new Func().run(); } public void run(){ int a = 1; f(); Console.println(a); } public void f(){ int a = 1; a++; Console.println(a); }}

你会看见什么

A

2 1

B

2 2

规则

通过大括号规则,可以看到 变量活在自己所在的函数里

每个函数有自己的数据空间,用于存储变量,且相互独立

函数间的数据传输

希望把数据 从一个函数里 传到 另外一个函数里

函数级别的 IO专业名词数据传输方向
输入参数从 外面 进入到 函数里
输出返回值从 函数里 流出到 外面

函数调用的完整生命周期

1.

调用前

2.

开始调用 f,准备变量空间

调用 f 时,为 被调用函数 f,开启单独变量空间

run 可使用 参数 将一些值 带到 函数 f的 新的变量空间

3.

运行 被调用函数 f

被调用的函数 f 运行时,如果需要开变量,会开在自己的空间里

4.

被调用函数 f 执行结束,准备返回

有些函数有返回值,此时会带着返回值 回到 刚才的函数 run

5.

调用结束,销毁函数 f

结束后,会将 被调用函数 f 和对应的变量空间,彻底销毁,就当没发生过

参数Parameters

是什么

变量,只不过可以接受传值

函数的输入

参数声明

变量定义,用 , 分割

public void f(int a, int b) {

}

函数调用时传参

public void run() {
    f(3, 4);
}

函数调用前表达式缩合

1 2 3 4 5 6 7 8 9 10
public void run() { int c = 3; int d = 4; int e = c + d; = 3 + 4; = 7; f(c, d); f(3, 4);}public void f(int a, int b) { int a = 3; int b = 4; Console.println(a); Console.println(b);}

传输的是值,不是变量

是数据格里的数据,而不是 数据格子

1 2 3 4 5 6 7 8 9
public void run() { int a = 1; f(a); Console.println(a); }public void f(int a) { a = 9;}
上面代码运行完了,你会看到什么
A

1

B

9

实参 与 形参

中文英文意义
实参arguments传到参数里的值
形参parameters接收参数值的那个参数变量

传参数量匹配

传过去的和接收的,必须一致,多了也不行,少了也不行

1 2!3 4!5 6 7 8
public void run() { f(); f(1); f(1, 2);}public void f(int a) { Console.println(a);}

顺序传输

按顺序传参,一个个传输

1 2 3 4 5 6 7 8
public void run() { int a = 1; int b = 2; f(b, a);}public void f(int a, int b) { Console.println(a + ", " + b);}
上面代码运行完了,你会看到什么
A

1, 2

B

2, 1

传参类型匹配

按着顺序,每一个的类型必须匹配

也可以不匹配,那么就遵循变量赋值的 类型转换规则

1 2 3 4!5 6 7 8 < <
public void run() { f(1); f(1.3); f("1");}public void f(double a) { Console.println(a);}1.01.3

返回值Return Value

是什么

用来给 调用方 返回 结果使用

函数的输出

返回值声明

函数可以有一个返回值

1 2 3
public int f() { }

void

如果不希望返回东西,则需要写 void

1 2 3
public void f() { }

返回值

1 2 3
public int f() { return 1; }

变量接住函数返回值

函数表达式 会缩合成 函数的返回值

1 2 3 <4
public void run() { int a = f(); = 1; Console.println(a); 1}

函数返回前的表达式缩合

1 2 3 <4 5 6 7 8 9
public void run() { int a = f(); Console.println(a); 1} public int f() { int c = 1; return c; return 1;}

函数返回的是值,而不是变量

不是数据格,是数据格里的数据

1 2 3 4 5 6 7 8 9 10
public void run() { int a = 1; a = f(); Console.println(a);} public int f() { int a = 9; return a;}
上面代码运行完了,你会看到什么
A

1

B

9

return

return 有两个效果,

1.

立刻终止当前函数

2.

后面可以跟值,达到返回值效果

也就是说,可以在无返回值函数里,干写一个 return,达到提前结束的效果

1 2 3 4 5 6 7 8 9 10
public void run() { f(3);} public void f(int v) { if (v < 0) { return; } Console.println(v);}

三体匹配

return,返回值类型声明,变量接收 三者要匹配

return 与 返回值类型声明匹配
函数声明 无返回值函数声明 有返回值
return;可以写,写了算提前终止不可以
return x;不可以必须要写,不能不写,且需要类型匹配
变量接收 与 返回值类型声明匹配
函数声明 无返回值函数声明 有返回值
使用 变量接收 a = f();不可以可以接收,但需要类型匹配
不使用 变量接收 f();可以也可以不接,此时返回值就丢了

函数综合应用

完整语法

public <返回值类型> <函数名>(<函数参数列表>) {
    <语句>
}
<函数参数列表> = <参数1类型> <参数1名>, <参数2类型> <参数2名> ...

带参数,带返回值的函数

1 2 3 4 5 6 7 8 9 10
public void run() { int a = 1; a = f(a); Console.println(a);} public int f(int a) { a = 9; return a;}
上面代码运行完了,你会看到什么
A

1

B

9

挑战

add

写一个函数,算出两个整数的和

max

写一个函数,算出两个整数的最大值

greating

写一个函数,接收一个名字后,控制台打印欢迎 ta 的信息

next

写一个函数,接收一个整数,返回比它大的下一个整数

roll

写一个函数,返回一个 1 - 6 的随机值

randomSelect

写一个函数,从数组中随机抽选一个值 返回回去

函数对接

一个函数的返回值,可以直接对接另外一个函数的参数

1 2 3 4 5 6 7 8 9 10 11
public void run() { echo(generate());} public int generate() { return 1;} public void echo(int value) { Console.println(value);}

函数重载Function Overloading

能干什么

同一个文件下,可以有同名函数

函数签名

函数签名 = 函数名 + 参数类型列表

案例
1 2 3
public int max(int a, int b) { return 0}

签名为

1
max(int, int)

函数重名规则

在一个结构下,不能出现两个函数签名一样的函数

挑战
1
int f(int a){}

以下这些函数,是否可以跟上面的函数同时出现在一个结构里

1.
1
int f(int a, int b){}
A

可以同时出现

B

不可以同时出现

2.
1
int f(int b){}
A

可以同时出现

B

不可以同时出现

3.
1
int f(double a){}
A

可以同时出现

B

不可以同时出现

4.
1
int g(int a){}
A

可以同时出现

B

不可以同时出现

5.
1
double f(int a){}
A

可以同时出现

B

不可以同时出现

函数调试

样例代码

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public int[] filterOutZeros(int[] values) { int count = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { count++; } } int[] filteredValues = new int[count]; int index = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { filteredValues[index] = values[i]; index++; } } return filteredValues;}

单元测试

针对一个函数进行测试

1 2 3 4 5
public void run() { int[] values = {6, 7, 3, 8, 4}; int[] result = filterOutZeros(values); Console.debugPrintln(values);}

使用代码块 隔离变量

1 2 3 4 5 6 7 8 9 10 11 12
public void run() { { int[] values = {6, 7, 3, 8, 4}; int[] result = filterOutZeros(values); Console.debugPrintln(values); } { int[] values = {6, 7, 0, 8, 4}; int[] result = filterOutZeros(values); Console.debugPrintln(values); }}

函数下的人工内存

每个函数会 开出自己的新空间

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
public void run() { int[] values = {6, 7, 3, 8, 4}; int[] result = filterOutZeros(values); Console.debugPrintln(values);}public int[] filterOutZeros(int[] values) { int count = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { count++; } } int[] filteredValues = new int[count]; int index = 0; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { filteredValues[index] = values[i]; index++; } } return filteredValues;}
run():
line                        io 
2       [6, 7, 3, 8, 4]
3                           call filterOutZeros( [6, 7, 3, 8, 4] )
        values

filterOutZeros():
line                            
6       [6, 7, 3, 8, 4]
7       |                   0
8       |                   |       0
9       |                   |       |       true 
10      |                   1       |       
        values              count   i
...

debug 工具

跳过 函数

进入 函数

跳出 函数

递归Recursions

什么是

自己调自己

案例

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public void run() { int[] values = {6, 7, 3}; recursivePrint(values, values.length);} public void recursivePrint(int[] arr, int length) { if (length == 0) { } else if (length == 1) { Console.print(arr[length - 1]); } else { recursivePrint(arr, length - 1); Console.print(", " + arr[length - 1]); }}

递归下的变量管理

当自己调用自己的时候,等同于开了一个新的同名函数

各自保持自己的变量空间

数据传输

三大传输

1.

赋值

2.

传参

3.

返回值

数据传输的规则

数据传输,需要负责类型匹配规则

1.

变量 要比 值 大 或者一样

2.

函数参数 要比 接收的值 大 或者 一样

3.

接收返回值的变量 要比 返回值 大 或者 一样

数据传输下的问题

修改变量
1 2 3 4 5 6 7 8 9
public void run(){ int a = 1; f(a); Console.println(a);} public void f(int a) { a = 2;}
修改数组变量
1 2 3 4 5 6 7 8 9
public void run(){ int[] a = {1, 2}; f(a); Console.debugPrintln(a);} public void f(int[] a) { a = new int[]{3, 4};}
修改数组变量 里的内容!
1 2 3 4 5 6 7 8 9 10
public void run(){ int[] a = {1, 2}; f(a); Console.debugPrintln(a);} public void f(int[] a) { a[0] = 3; a[1] = 4;}

注意:使用 中括号语法,会让 a 变量产生迷幻的同步效果

ZZAX 微信公众

文档一更新,立刻告诉你