Java方法
方法是什么?
Java 方法是语句的集合,它们在一起执行一个功能
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
方法的优点
- 使程序变得更简短而清晰
- 有利于程序维护
- 可以提高程序开发的效率
- 提高了代码的重用性
Java 方法的命名规则
- 必须以字母、下划线(_) 或 美元($) 开头
- 可以包括数字,但不能以它开头
方法的定义
一般情况下,定义一个方法包含以下语法
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
方法包含一个方法头和一个方法体
下面是一个方法的所有部分
- 修饰符: 修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型
- 返回值类型 : 方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字 void
- 方法名: 是方法的实际名称。方法名和参数表共同构成方法签名
- 参数类型: 参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数
- 方法体: 方法体包含具体的语句,定义该方法的功能
return语句在方法中的作用
- 返回方法指定类型的值
- 结束方法的执行,终止后边代码的执行
- ⼀个⽅法最多只能执⾏⼀条return语句,即使你有多个return
- 不可达语句
- 在⼀定会被执⾏的return语句后
- 在循环中⽆条件的break后写语句,会出现不可达语句
- 在死循环(没有break来结束的死循环)后写语句,会出现不可达
语句
重载(overload)
重载 ( overloading ) 是在一个类里面,方法名字相同,而参数个数不同、顺序不同、类型不同,返回类型可以相同也可以不同
每个重载的方法 ( 或者构造函数 ) 都必须有一个独一无二的参数类型列表
//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
重载规则
- 被重载的方法必须改变参数列表 (参数个数或类型或顺序不一样)
- 被重载的方法可以改变返回类型
- 被重载的方法可以改变访问修饰符
- 被重载的方法可以声明新的或更广的检查异常
- 方法能够在同一个类中或者在一个子类中被重载
- 无法以返回值类型作为重载函数的区分标准
注意事项
- 重载方法参数必须不同
- 重载只与方法名与参数类型相关与返回值无关
- 重载与具体的变量标识符无关
参数传递
参数传递,可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数,这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了。这种传递方式,我们称为参数传递。
在这里,定义方法时,参数列表中的变量,我们称为形式参数
调用方法时,传入给方法的数值,我们称为实际参数
递归
递归是⼀种常⻅的解决问题的⽅法,即把问题逐渐简单化。 递
归的基本思想就是 ⾃⼰调⽤⾃⼰ ,⼀个使⽤递归技术的⽅法将
会直接或者间接的调⽤⾃⼰。利⽤递归可以⽤简单的程序来解
决⼀些复杂的问题。
递归的定义
递归是⼀种常⻅的解决问题的⽅法,即把问题逐渐简单化。 递
归的基本思想就是 ⾃⼰调⽤⾃⼰ ,⼀个使⽤递归技术的⽅法将
会直接或者间接的调⽤⾃⼰。利⽤递归可以⽤简单的程序来解
决⼀些复杂的问题。⽐如:⼤多数排序使⽤的就是递归算法
递归的三要素
第一要素:明确你这个函数想要干什么
对于递归,我觉得很重要的一个事就是,这个函数的功能是什么,他要完成什么样的一件事,而这个,是完全由你自己来定义的。也就是说,我们先不管函数里面的代码什么,而是要先明白,你这个函数是要用来干什么。
例如,我定义了一个函数
// 算 n 的阶乘(假设n不为0)
int f(int n){
}
这个函数的功能是算 n 的阶乘。好了,我们已经定义了一个函数,并且定义了它的功能是什么,接下来我们看第二要素。
第二要素:寻找递归结束条件
所谓递归,就是会在函数内部代码中,调用这个函数本身,所以,我们必须要找出递归的结束条件,不然的话,会一直调用自己,进入无底洞。也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。
例如,上面那个例子,当 n = 1 时,那你应该能够直接知道 f(n) 是啥吧?此时,f(1) = 1。完善我们函数内部的代码,把第二要素加进代码里面,如下
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n == 1){
return 1;
}
有人可能会说,当 n = 2 时,那我们可以直接知道 f(n) 等于多少啊,那我可以把 n = 2 作为递归的结束条件吗?
当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。
// 算 n 的阶乘(假设n>=2)
int f(int n){
if(n == 2){
return 2;
}
}
注意我代码里面写的注释,假设 n >= 2,因为如果 n = 1时,会被漏掉,当 n <= 2时,f(n) = n,所以为了更加严谨,我们可以写成这样:
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n <= 2){
return n;
}
}
第三要素:找出函数的等价关系式
第三要素就是,我们要不断缩小参数的范围,缩小之后,我们可以通过一些辅助的变量或者操作,使原函数的结果不变。
例如,f(n) 这个范围比较大,我们可以让 f(n) = n * f(n-1)。这样,范围就由 n 变成了 n-1 了,范围变小了,并且为了原函数f(n) 不变,我们需要让 f(n-1) 乘以 n。
说白了,就是要找到原函数的一个等价关系式,f(n) 的等价关系式为 n * f(n-1),即
f(n) = n * f(n-1)。
找出了这个等价,继续完善我们的代码,我们把这个等价式写进函数里。如下:
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n <= 2){
return n;
}
// 把 f(n) 的等价操作写进去
return f(n-1) * n;
}
递归的缺陷
简单是递归的优点之⼀。但是递归调⽤会占⽤⼤量的系统堆栈,内存耗⽤多,在递归调⽤层次多时速度要⽐循环慢的多。所以在使⽤时要慎重。
**注意:任何可⽤递归解决的问题也能使⽤迭代解决。**但递归易于理解和调试。在不强调效率并且有⾜够的内存空间时,可以采⽤递归;在要求⾼性能的情况下尽量避免使⽤递归,递归调⽤既花时间⼜耗内存。