Java面向对象(二)
目录
- Java面向对象(二)
- 五、方法
- 5.1 方法的重载(overload)
- 5.2 可变参数的形参
- 5.3 方法参数的值传递机制
- 5.4 递归方法
五、方法
5.1 方法的重载(overload)
- 定义:在同一个类中,允许定义多个相同名字的方法,只要参数列表(参数类型或者参数个数)是不同的。
- 判断是否为方法重载:
(1)同一个类,同样的方法名,不同的参数列表!
(2)与方法修饰符、方法返回值、形参名、方法体无关!
public class OverloadTest{
public void getSum(int a,int b){}
public void getSum(int a,int b,int c){}
public void getSum(double a,double b){}
public void getSum(int a,double b){}
public void getSum(double a,int b){}
/* 不属于方法重载:与方法返回值类型无关
public int getSum(int a,int b){
return 0;
}
*/
/* 不属于方法重载:与形参名称无关
public void getSum(int m,int n){}
*/
/* 不属于方法重载:与方法修饰符无关
private void getSum(int a,int b){}
*/
}
- 方法重载中一些特殊情况:
情况一:
public class OverloadTest {
public static void main(String[] args){
OverloadTest o = new OverloadTest();
System.out.println(o.getSum(1,2)); // 输出3
System.out.println(o.getSum(1.0,2.0)); // 输出3.0
System.out.println(o.getSum(1.0,2)); // 输出3.0
}
public int getSum(int a,int b){
return a + b;
}
public double getSum(double a,double b){
return a + b;
}
}
/* 可以看到,我们并没有定义 getSum(double,int)类型的方法,但是 getSum(1.0,2)还是正常输出,没有报错。
这里getSum(1.0,2)是调用了getSum(double a,double b)的方法,原因是实参2被自动转换为double类型并传递给这个方法。
那为什么getSum(1,2)调用时不会使用getSum(double a,double b)方法?
原因:
其实 getSum(int a,int b) 和 getSum(double a,double b) 都是 getSum(1.0,2) 可能的匹配。调用方法时,Java编译器会查找最精确匹配的方法,因为方法 getSum(int a,int b) 比 getSum(double a,double b) 更精确,所以调用 getSum(1,2) 时使用的是getSum(int a,int b)。
*/
情况二:
public class OverloadTest {
public static void main(String[] args){
OverloadTest o = new OverloadTest();
System.out.println(o.getSum(1,2));
}
public double getSum(int a,double b){
return 1.0;
}
public double getSum(double a,double b){
return 2.0;
}
}
/* 输出 1.0
根据调用方法时,Java编译器会查找最精确匹配的方法。
所以 getSum(1,2)调用时使用的是 public double getSum(int a,double b)方法
*/
情况三:
有时调用一个方法时会有两个或多个可能的匹配,但编译器无法判断哪个是最精确的匹配。这就称为“歧义调用”(ambiguous invocation),会产生编译错误。
public class OverloadTest {
public static void main(String[] args){
OverloadTest o = new OverloadTest();
System.out.println(o.getSum(1,2));
}
public double getSum(int a,double b){
return 1.0;
}
public double getSum(double a,int b){
return 2.0;
}
}
/* 编译报错:
Error:(9, 29) java: 对getSum的引用不明确
javase.OverloadTest 中的方法 getSum(int,double) 和 javase.OverloadTest 中的方法 getSum(double,int) 都匹配
*/
5.2 可变参数的形参
- JavaSE 5.0 中提供了 Varargs (variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public class Test {
public static void main(String[] args){
Test o = new Test();
o.test(new String[]{"aa","bb","cc"}); //调用时必须new String[]数组
}
public static void test(String[] books){
for (int i = 0; i < books.length; i++) {
System.out.println(books[i]);
}
}
}
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public class Test {
public static void main(String[] args){
Test o = new Test();
o.test(new String[]{"aa","bb","cc"}); //同样可以用
o.test("aa","bb","cc"); //更加简单
}
public static void test(String ... books){
for (int i = 0; i < books.length; i++) {
System.out.println(books[i]);
}
}
}
/* 输出:
aa
bb
cc
*/
- 说明:
- 声明格式:方法名(参数的类型名 ... 参数名) 。
- 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个。
- 可变个数形参的方法与同名的方法之间,彼此构成重载。
- 可变参数方法的使用与方法参数部分使用数组是一致的,故两个不能构成方法重载!
- 方法的参数部分有可变形参,需要放在形参声明的最后。
public static void test(int a ,String ... books);
/* 报错,Java编译器不知道可变参数什么时候结束
public static void test(String ... books,int a);
*/
- 在一个方法的形参位置,最多只能声明一个可变个数形参。
5.3 方法参数的值传递机制
- Java 里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
- 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
- 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
- 例子:
// 针对基本数据类型:
public class Test {
public static void main(String[] args) {
int num1 = 1;
int num2 = 2;
System.out.println("在调用 swap 方法前,变量 num1 的值为:" + num1 + ",变量 num2 的值为:" + num2);
swap(num1, num2);
System.out.println("在调用 swap 方法后,变量 num1 的值为:" + num1 + ",变量 num2 的值为:" + num2);
}
public static void swap(int n1, int n2) {
System.out.println("在进行交换前,变量 n1 的值为:" + n1 + ",变量 n2 的值为:" + n2);
int temp = n1;
n1 = n2;
n2 = temp;
System.out.println("在进行交换后,变量 n1 的值为:" + n1 + ",变量 n2 的值为:" + n2);
}
}
/* 输出结果:
在调用 swap 方法前,变量 num1 的值为:1,变量 num2 的值为:2
在进行交换前,变量 n1 的值为:1,变量 n2 的值为:2
在进行交换后,变量 n1 的值为:2,变量 n2 的值为:1
在调用 swap 方法后,变量 num1 的值为:1,变量 num2 的值为:2
可以看到,num1 和 num2 的值并没有因为调用 swap 方法而进行交换。
*/
Java 内存分析:
// 针对引用数据类型:
public class Test {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ", n = " + data.n);
Test test = new Test();
test.swap(data);
System.out.println("m = " + data.m + ", n = " + data.n);
}
public void swap(Data d){
int temp = d.m;
d.m = d.n;
d.n = temp;
}
}
class Data{
int m;
int n;
}
Java内存分析:
所以,在写交换数组元素位置方法时:
//错误的:交换数组中指定两个位置元素的值
// public void swap(int i,int j){
// int temp = i;
// i = j;
// j = temp;
// }
//正确的:交换数组中指定两个位置元素的值
public void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
- 特殊的,String类型
public class Test {
public static void main(String[] args) {
String s1 = "hello";
Test test = new Test();
test.change(s1);
System.out.println(s1);
}
public void change(String s){
s = "hi";
}
}
// 输出:hello
Java内存分析:
结论:String 属于引用类型变量,形参仍然接收的是实参变量的地址值,只是字符串在常量池是一个 char[] 型的一个字符数组,新赋值 hi 时会在常量池新创建一个字符数组,再将 hi 字符串的地址赋值给 s 变量。
5.4 递归方法
- 递归方法:一个方法体内调用它自身。
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
- 典型使用场景:
- 求 n 的阶乘
// 求 n!方法
public int f(int n) {
if (n == 1) {
return 1;
} else {
return n * f(n - 1);
}
}
- 斐波那契数列
public int f(int n) {
if (n == 1 || n == 2) {
return 1;
}
if (n > 2) {
return f(n-1) + f(n-2);
}
return -1;
}
- 快速排序