第五章 函数
文章目录
5.1函数的概念
(函数不能单独成为一个类,不能在函数中再成立一个函数,只能在其他函数之外,当前类之中)
什么是函数?
函数的定义就是指一段具有独立功能的代码,可以减少代码冗余,提高程序的利用率和效率。
我们应该如何定义函数?
- 需要一个封闭的空间,将这段独立性的代码进行封装,用{ }
- 需要对每一个封闭的空间进行命名,函数名
- 函数所需要的一些原始数据
- 函数所产生的一些结果数据
函数的语法格式
修饰符 函数类型 返回值类型 函数名(数据类型 数据1,数据类型 数据2,......){
独立功能的代码片段;
return 函数的计算结果;
}
- 修饰符: 指的是函数的访问权限,public private (默认 protected)
- 函数的类型:函数的分类,本地函数native,静态函数static,同步函数synchronized
- 返回值类型:指的就是函数计算结果的数据类型 如果函数没有返回值 则为void
- 函数名:就是函数的名称
- 参数列表:指的是外界 向函数传入的数据(实际参数),由这些参数变量进行接收(形式参数)
- 函数体:具有独立功能的代码片段
- return:仅仅表示函数结束!如果函数有返回值,则return 后跟返回值;如果没有返回值,则return可以不写,但是是存在的(隐藏的)如果有返回值类型(除void)return后必须有值
根据形参和返回值来看,函数有如下几个分类
- 有参数没返回值
将三个字符串的反转,并拼接打印
public class Sample06{
public static void main(String[] args){
print("123","456","789");
}
public static void print(String s1,String s2,String s3){
System.out.println(reverses(s1) + reverses(s2) + reverses(s3));
}
public static String reverses(String s){
String res = "";
for (int i = s.length() - 1; i >= 0;i--){
char ch = s.charAt(i);
res = res + ch ;
}
return res;
}
}
- 有参数有返回值
求三个数字当中的最大值
public class Sample04{
public static void main(String[] args){
// Scanner input = new
int max = getMax(1,2,3);
System.out.println(max);
}
public static int getMax(int a,int b,int c){
/*
if (a >= b && a >= c){
return a;
} else if (b >= a && b >= c){
return b;
} else {
return c;
}
*/
if (a >= b && a >= c){
return a;
}
if (b >= a && b >= c){
return b;
}
if (c >= b && c >= a) {
return c;
}
return -1
}
}
计算一个数字a的b次幂
public class Sample05{
public static void main(String[] args){
double i = getPow(2,2);
System.out.println(i);
}
public static double getPow(int a,int b){
double pow = 1.0;
if ( a == 0) {
return 0;
}
if (b == 0) {
return 1;
}
for (int i = 0;i < Math.abs(b);i++){
pow *= a;
}
return b > 0 ? pow : 1 / pow;
}
}
- 没参数有返回值
- 没参数没返回值
总结
当函数的返回值类型不为void的时,return后一定要加返回结果。
当函数返回值类型为void时或无返回值,在主函数中不需要定义一个变量来接收,直接调用即可,并且函数中需要加入System.out.println()来打印。
当函数没有参数时,参数的获取来自于其他地方而不是主函数,
定义函数时需要考虑的有哪些?
- 函数的运行有哪些未知的参数?
- 函数的运行结果有是什么?
- 明确参数和结果
- 明确内容和返回
- 函数到底要干什么?
- 尽量将独立功能且重复性较高的代码片段提取出来
5.2函数的运行原理
函数的运行是基于栈运行的
栈:是一种先进后出的容器,我们这里所说的栈是指JVM中的栈内存空间
每一个函数,叫做栈帧,栈帧中所包含的内容有函数的定义,参数列表,函数的执行内容代码
每一个函数要运行,就相当于这个栈帧进入到栈内存中–入栈
如果一个函数即将结束,将这个栈帧从栈移出–出栈
如果栈内存中有多个栈帧,运行的是最上面的栈帧,底下的栈帧暂停运行,直到该栈帧为栈顶元素
比如:主函数先进栈,开始逐行运行,如果执行到第n行,调用另外一个函数A,则主函数在第n行暂停运行,将另一个函数A的栈帧入栈,再继续逐行运行,直到函数A的内容执行完毕,函数A出栈,主函数接着从第n行继续向下执行。以此类推。
5.3函数的重载
同一个类可以出现多个同名函数,这个现象叫做函数的重载(overload)
如何来区分同名函数是否是重载关系?
前提是先必须是同名,并且和返回值类型无关(返回值类型只和函数的计算功能相关),和权限也无关,和形式参数的名称也无关!只和形式参数的数据类型有关(数量,排列组合)
public static void show(int a,float b,char c){}
下列哪些函数是该函数的重载:
- int show(int x,float y,char z):不算重载 数据类型都是int float char
- void show(float b,int a,char c):算重载,顺序不一样 float int char
- void show(int a ,int b,int c):算重载,顺序不一样
- double show():算重载,参数数量不一样
寻找重载函数的流程:
1. 看是否有确切的参数定义匹配,int int 找int int
2. 看是否有可兼容的参数定义匹配,int int 找double double 或 int double或double int
3. 如果可兼容的参数定义匹配较多,会报引用类型错误 引用不明确
总结
double可以向下兼容int
先找最近的,再找相近的,再找可以兼容的
5.4函数的递归
函数的递归就是指函数自身调用自身。
- 但凡迭代能够解决的问题,递归都可以解决;递归可以解决的问题,迭代就不一定
- 相对而言,从内存的角度而言,函数如果过多的自我调用,势必会对内存不友好,占用过多
- 通常来讲,同样的问题用递归写要比用迭代写代码量较小
写递归时,一定要先确定递归结束条件-递归边界
举例
用递归实现前100项求和
1+2 +3+4+…+100
f(1) = 1 //函数边界
f(2) = 2 +f(1)
…
f(99) = 99 + f(98)
f(n) = n + f(n - 1)
1 x=1
f(x)=
f(x-1) +x x>1
public class Sample{
public static void main(String[] args){
int sum = Sum(100);
System.out.println(Sum(10000000));//StackOverflowError
System.out.println(sum);
}
public static int Sum(int a){
int sum = 0;
if (a == 1){
return 1;
} else {
sum = Sum(a - 1) + n;
return sum;
}
}
}
递归实现斐波那契数列
1 1 2 3 5 8 13 21 …
1 x=1,2
f(x)
f(x-1) + f(x-2) x>2
public class Sample{
public static void main(String[] args){
//递归
System.out.println(Sum(10));
//迭代
System.out.println(fibo_it(30));
}
public static int fibo_it(int x){
if (x == 1 || x == 2) {
return 1;
}
int a = 1;
int b = 1;
int c = 0;
for (int i = 3;i <= x;i++){
c = a + b;
a = b;
b = c;
}
return c;
}
public static int Sum(int n){
int sum = 0;
if (n == 1 || n == 2){
return 1;
} else {
sum = Sum(n - 1) + Sum(n - 2);
return sum;
}
}
}
递归解决汉诺塔问题
/*
前4个 x->z
前3个 x->y
前2个 x->z
前1个 x->y
第2个 x->z
前1个 y->z
第3个 x->y
前2个 z->y
前1个 z->x
第2个 z->y
前1个 x->y
第4个 x->z
前3个 y->z
前2个 y->x
前1个 y->z
第2个 y->x
前1个 x->y
第3个 y->z
前2个 x->z
前1个 x->y
第2个 x->z
前1个 y->z
*/
public class Sample{
public static void main(String[] args){
String x = "x";
String y = "y";
String z = "z";
hano(4,x,y,z);
}
public static void hano(int level,String begin,String mid,String end){
if (level == 1){
System.out.println(begin + "->" + end);
} else {
//前level - 1层
hano(level - 1,begin,end,mid);
System.out.println(begin + "->" + end);
//前level - 1层
hano(level - 1,mid,begin,end);
}
}
}
总结
错误种类
String s = "123";
System.out.println(s.charAt(7));
//StringIndexOutOfBoundsException
System.out.println(10 / 0);
//ArithmeticException
Integer.parseInt("123asd");
//NumberFormatException
Scanner input = new Scanner(System.in);
int number = input.nextInt();
//IntputMismatchException