目录
一、对象和引用
java中除了基本类型之外,其余的类型都属于引用类型。对象就是通过引用进行操作的。
大致过程如下:
将硬盘上类的信息读取到数据区的方法区中,这个过程就叫类加载。
jvm的数据区中栈是运行时的单位,管理方法的调用运行;堆是存储空间,用来存储对象,是内存空间中最大的一块区域。方法区是主要用来存储加载类的信息。
首先在栈中运行主方法main,在里面执行创建对象的代码(类名 变量名= new 构造方法;),然后在方法区中加载类的信息,又在堆中创建对象,将对象的地址赋给变量,随后通过变量来访问这个对象。这就是引用。
在即首次使用一个类时,如首次创建对象或首次访问静态成员等,就会发生类加载,并只发生一次。
二、值传递和引用传递
在调用方法进行传递参数的时候,有两种传递方式:值传递和引用传递。
1.值传递
值传递是指在调用方法时将实际参数的值复制一份传递给函数,函数内部对参数的修改不会影响原始参数。实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,但实际上是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。
如:
public class Test {
//被调用的方法
public int pass(int x){
x=4;
return x;
}
//主方法
public static void main(String[] args) {
int a=3;
System.out.println("值传递前:"+a);
//创建对象调用方法
Test test= new Test();
test.pass(a);
System.out.println("值传递后:"+a);
}
}
运行结果:
2.引用传递
引用传递是指在调用函数时,将实际参数的引用或者地址传递给形式参数。这就意味着函数内部对形参的修改,会直接影响到实际参数,因为它们共享同一块内存空间。
注意:java中没有引用传递,只有值传递。
如:
public class Test {
public static void pass(Car car2){
car2.name="宝马";
}
public static void main(String[] args) {
Car car1 = new Car();
car1.name="奔驰";
System.out.println("调用方法前:"+car1.name);
Test.pass(car1);
System.out.println("调用方法后:"+car1.name);
}
}
运行结果:
以上实例,在代码中,参数类型为引用类型,调用方法pass之后,发现创建的对象car1的名字由奔驰变为了宝马,这看似好像满足引用传递中提到的函数内部对形参的更改会直接影响到实参。但实际上这并不属于引用传递。
原因是这个car1存储的实际是对象的地址。它只是对对象的引用,调用方法pass时,形参car2拷贝了car1所指向的对象的地址,这样car2和car1指向了同一个对象。car2修改对象里面的名字后,通过car1再去访问就是修改后的名字。按照严格意义来讲,这仍然属于值传递,只是传递的内容是对象的引用。
可以通过一个例子来验证一下:
public class Test {
//调用的方法
public static void pass(Car car1,Car car2){
Car car = new Car();
car= car1;
car1= car2;
car2= car;
System.out.println("在pass方法中:"+car1.name+" "+car2.name);
}
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
car1.name="奔驰";
car2.name="宝马";
System.out.println("调用方法前:"+car1.name+" "+car2.name);
Test.pass(car1,car2);
System.out.println("调用方法后:"+car1.name+" "+car2.name);
}
}
首先创建两个对象,用方法pass去实现交换两个对象,运行结果如下:
从以上示例可以得出:1.传递的内容确实是对象的地址。2.方法pass中确实实现了交换,但是交换的实际只是对象地址的副本(即形参把实参指向的对象的地址拷贝了一份给自己),在main方法中两个对象并未发生交换。所以实际仍然是值传递。
总结:区分值传递和引用传递的重点就是方法中操作的究竟是副本还是直接对原始变量进行操作。引用传递的例子具体可以通过C++去了解。