前言
李刚老师《JAVA疯狂讲义》第5版,第5章学习笔记。
1.JAVA方法的所属性
JAVA中,方法是对类或对象行为特征的抽象,它是类或对象的最重要的组成部分。面向对象编程中的方法,结构化编程中的函数,二者到底有什么本质区别呢?
二者所属性不同,结构化编程中,函数可被单独执行,函数不属于任何事物,软件由一个个函数构成,函数是程序的老大。面向对象的编程中,方法不可被单独执行,方法属于类或对象,软件由一个个类构成,类是程序的老大,方法是类的跟班。
在JAVA中,如果要定义一个方法,只能在类中定义,如果方法被static修饰,则该方法属于这个类,如果方法没有static修饰,则方法属于类的对象。JAVA语言是静态的, 一旦类的定义完成后,只要不重新编译,则该类和类的对象所拥有的方法都是固定的,永远不会改变。
2.JAVA方法的参数传递机制
声明方法时的参数叫做形参,调用方法时传递给形参的参数值叫做实参,那么在内存中,实参的值是如何传递给形参的呢?
JAVA中的参数传递方式只有值传递一种,也就是说,将实参的值复制一份,传给形参,但是,实参本身不会受到影响,例如:
public static class PrimitiveTransferTest{
public static void swap(int a , int b) {
//定义一个交换变量值的方法
int tem = a;
a = b;
b = tem;
System.out.println("swap方法中,a的值为:" + a + ",b的值为:" + b);
}
}
public static void main(String args[]) {
PrimitiveTransferTest p = new PrimitiveTransferTest();
int a = 3;
int b = 5;
p.swap(a, b);
System.out.println("调用swap方法后,a的值为:" + a + ",b的值为:" + b);
}
代码运行结果为:
swap方法中,a的值为:5,b的值为:3
调用swap方法后,a的值为:3,b的值为:5
说明swap()方法中,a,b值的改变,没有影响main()方法中,a,b值的改变,具体原因如下:
JAVA程序执行入口为main()方法,当开始执行main()方法,在main()方法中定义a、b两个int型变量时,内存形式如下:
随后,借助类的对象调用swap()方法后,JAVA内部会开辟一个swap()方法的内存空间,其中的变量值从main()方法栈内存中拷贝而来,具体如下:
可见,虽然变量名相同,但是main()方法中的两个变量和swap中的两个变量是不同的,保存在内存中的不同区域。在swap()方法执行完毕后,内存中结果如下:
可见,改变的只是swap()方法栈内存中的变量,而main()方法中的变量并未改变,这就是JAVA方法的参数传递机制,不会改变main()方法中本来的变量!
无论是基本类型的参数传递,还是引用类型的参数传递,例如:
public static class PrimitiveTransferTest{
public static void swap(DataWrap dw) {
//定义一个交换变量值的方法,方法的形参为引用变量
int tem = dw.a;
dw.a = dw.b;
dw.b = tem;
System.out.println("swap方法中,a的值为:" + dw.a + ",b的值为:" + dw.b);
}
}
public static void main(String args[]) {
PrimitiveTransferTest p = new PrimitiveTransferTest();
DataWrap dw = new DataWrap();
dw.a = 3;
dw.b = 5;
p.swap(dw);
System.out.println("调用swap方法后,a的值为:" + dw.a + ",b的值为:" + dw.b);
}
这段程序的运行结果是:
swap方法中,a的值为:5,b的值为:3
调用swap方法后,a的值为:5,b的值为:3
看起来,似乎形参和实参的值都发生了变化,和形参为基础类型时的机制不同,其实并非这样。具体如下:
当main()方法中创建了DataWrap这个类的对象dw,并对dw的两个成员变量a,b赋值后,内存中为:
调用swap方法后,main()方法中的dw变量的值会赋值到swap()栈内存中,由于main()栈内存中dw储存的为对象的地址,因此swap()栈内存中,也是对象的地址,在传递参数后,内存中为:
swap()方法中,将实参的值赋值过来,同样也指向了堆内存中的同一个对象,在swap()方法执行完毕后,内存中结果如下:
所以,方法的参数传递机制并没有变化,main()栈内存中的dw变量并未发生任何变化,指向的还是堆内存中的对象,但是swap()方法执行后,对象本身发生了变化,导致看起来,实参dw也发生了变化。
3.JAVA方法的递归
JAVA的方法中,可以调用其自身,这就是方法的递归,这是一种隐形的循环,而无需循环控制语句。例如:
已知一个数列:
f(0) = 1
f(1) = 4
f(n+2) = 2*f(n+1) + f(n),n >= 0
求f(10),递归的写法为:
public static class Recursive{
public static int fn(int n) {
if(n == 0) {
return 1;
}
else if (n == 1) {
return 4;
}
else {
return 2*fn(n-2)+fn(n-1);
}
}
}
public static void main(String args[]) {
Recursive r = new Recursive();
System.out.println(r.fn(10));
}
注意,一定要保证递归最终在某个时刻的返回值的确定的,比如上述例子中,可以保证递归最终回到n=1或者n=0,不然就会导致无限递归,死循环。也就是说,递归一定要向已知的方向递归。
4.JAVA方法的重载
JAVA允许在一个类中定义多个同名的方法,只要形参列表不同就OK,这就是方法的重载,可见,JAVA中唯一确定一个方法需要三个因素:
- 所属的类或对象
- 方法名
- 形参列表
举例如下:
public static class overLoad{
public void test() {
System.out.println("方法重载测试1");
}
public void test(String msg) {
System.out.println("方法重载测试2"+msg);
}
}
上述代码中实现了test()方法的重载,虽然方法名相同,但是二者形参列表不同,因此系统可以正常的区分这两个方法。