从面向对象的角度来看,Java中的方法是类或对象行为的抽象。但和C++不同,Java中”剔除“了C++中”残留“的结构化编程。具体地说,Java中的方法必须在类中定义,没有独立存在的函数。
也有人把结构化编程,叫做”面向函数的编程“,从而和”面向对象的编程“对应。
Java中的方法或属于某个对象(普通成员),或属于类(静态成员/类成员)。
Java中方法的执行也必须通过对象或类来调用。
在同一个类中,不同方法的调用其实是:
- 默认使用this调用普通成员;
- 默认通过类调用类成员。
方法的参数传递机制
和C++不同,Java中的参数传递只有一种:值传递
为什么Java没有引用传递呢?因为不需要。引用传递,意味着在函数体外,存在某个变量,将其地址传入函数体,实现节约拷贝开销的作用。但是在Java的方法之外,只有成员变量。成员变量可直接调用修改,并不需要拷贝。或者换个角度,Java的方法的形参里,不存在同类的成员变量,只能是临时变量,所以不存在使用引用传值的空间。
public class PrimitiveTransferTest
{
public static void swap(int a, int b)
{
var temp = a;
a = b;
b = temp;
System.out.println("swap方法里,a的值是"+a+";b的值是"+b);
}
public static void main(String[] args)
{
var a = 6;
var b = 9;
swap(a,b);
System.out.println("交换结束后,变量a的值是"+a+";b的值是"+b);
}
}
上述代码中,swap中的a和b分别是9,6;main中的a和b则是6和9。
为什么会这样?
其实很好理解:和C++中main只代表程序执行入口相比,Java中的main也是一个类方法!既然是类方法,那么就不存在引用传值,只有值传递!
在内存中,这些变量的情况是这样的:
经过swap后,内存中是这样的:
class DataWarp
{
int a;
int b;
}
public class ReferenceTransferTest
{
public static void swap(DataWrap dw)
{
var tmp = dw.a;
dw.a = dw.b;
dw.a = tmp;
System.out.println("swap方法里,a成员变量的值是"+dw.a+";b成员变量的值是"+dw.b);
}
public static void main(String[] args)
{
var dw = new DataWarp();
dw.a = 6;
dw.b = 9;
swap(dw);
System.out.println("main方法里,交换后a成员变量的值是"+dw.a+";b成员变量的值是"+dw.b)
}
}
上述代码执行后,swap和main方法中a和b的值都被交换了。看起来似乎是引用传值啊!
然而:
- main中创建了一个DataWrap对象,并且用tmp引用变量指向它,这时的内存如下图所示:
- 在main中调用swap方法,这时,新开辟了一块swap的栈内存。
此时,dw做为实参传入swap方法:由于dw实际上是引用变量类型(也就是指针),它保存的是指向堆内存中DataWrap对象的地址。因此,这里的值传递其实就是地址的传递,也就是说,swap(dw)
中的dw接受了main中dw传来的地址。这时,swap方法的参数dw也就指向了同一个对象。
为进一步验证main中的dw和swap中的dw是两个变量,在swap方法的最后加一行:
dw = null;
如果这两个dw是同一个变量,那么这句话之后,main中的dw将不能再访问堆内存中的DataWarp对象了。然而main中的dw方法没有受到任何影响。此时内存相当于下图: