首先要明确的是,Java中只有值传递,而没有引用传递!
只有在方法调用的时候才涉及到值传递的概念!
Java中进行方法调用的时候传递参数时,遵循值传递的原则:
1)基本数据类型,传递的是数据的拷贝
2)引用数据类型,传递的是传递的引用地址的拷贝,而不是该对象本身
形参:方法声明时的参数变量,用于接收调用方法时传过来的实参
如:void f(int i){} 或 void f(String s){}
实参:方法调用时实际传过去的内容,用于给形参赋值
如:f(1) 或 f(“abc”)
变量按不同的数据类型分为2类:
1)基本数据类型变量:4类8种基本类型,byte,short,char,int,long,float,double,boolean
如:int i = 0;
2)引用数据类型变量: 类,接口,数组
如:String s = "abc"; String 属于一个类,因此属于引用数据类型变量
注意:Java中除了4类8种基本数据类型以外全部是引用数据类型
变量按声明的位置不同分为2类:
1)成员变量:方法外部,类内部声明的变量
2)局部变量:方法内部声明的变量,形参属于局部变量
变量的初始化:
1)成员变量:如果不显示对其初始化,那么Java采用默认值对其进行初始化
2)局部变量:不赋值,就不能使用
执行中的内存管理:
不同的操作系统不太一样,但一般内存都分为4个区域:
1)heap(堆):存放new出来的对象
2)stack(栈):存放局部变量
3)data segment(数据区):静态变量 和 字符串常量
4)code segment(代码区):存放代码
例子:
public class Test {
void f(int j) {
System.out.println(j);
}
void ff(String ss) {
System.out.println(ss);
}
public static void main(Stirng[] args) {
int i = 100;
String s = "hello";
Test t = new Test();
t.f(i);
t.ff(s);
}
}
当mian方法开始执行的时候
int i = 100; 栈中分配一块空间,存放变量i,值为100,基本数据类型只占一块空间
String s = “hello”; "hello"是字符串常量,分配在data区,字符串常量为String类的一个对象,
s指向了这个"hello"对象,这就是引用数据类型,在内存中占两块空间,
有点形象思维:一提引用类型,就是一块内存指向另一块内存
s可以被叫做:引用、引用变量、引用地址,其实s就是一个指针,在这里不用钻牛角尖,你就知道
s是一个引用,s的值是一个地址,根据这个地址就可以找到一个对象就ok了,
Test t = new Test(); 同理,栈中的引用t指向了堆中new出来的这个Test对象
Java中进行方法调用的时候传递参数时,遵循值传递的原则:
1)基本数据类型,传递的是数据的拷贝
2)引用数据类型,传递的是传递的引用地址的拷贝,而不是该对象本身
t.f(i);
方法的形参属于局部变量,所以在调用f方法的时候,栈内存分配一个int型的变量j,将i的值当做实参传递过去,i的值是100,现在将100拷贝给了j,那么j的值就是100,接着打印,最后方法结束,为该方法分配的局部变量立刻全部清空,那么Stack中这个j消失了。
t.ff(s)调用ff方法的时候,栈内存分配一个String型的变量ss,将s的值当做实参传递过去,s指向
了"hello"对象,s的值是一个地址,现在将这个地址拷贝给了ss,那么ss也就指向这个"hello"了。现在s 和 ss 两个引用 同时指向了"hello"对象,然后打印ss,最后方法结束,栈中这个ss被清除
现在main方法执行结束,为main方法分类的局部变量清除,i,s,t全部消失,data区的符串常量"hello"和堆内存的Test对象,由于没有任何引用指向他们了,就会被垃圾收集器回收。
那么下面来看:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname); // 传入fullname数组
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
System.out.println(p.getName()); // "Homer Simpson"还是"Bart Simpson"?
}
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
注意到setName()的参数现在是一个数组。一开始,把fullname数组传进去,然后,修改fullname数组的内容,结果发现,实例p的字段p.name也被修改了!
结论:引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)。
内存表示图如下:
`
数组值改变是一片连续内存的指向改变,因此指向这内存的变量值当然也改变。
`
下面再看:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // 传入bob变量
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bob改名为Alice
System.out.println(p.getName()); // "Bob"还是"Alice"?
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
不要怀疑引用参数绑定的机制,试解释为什么上面的代码两次输出都是"Bob"
上面两次结果的不同的原因就是一个是field指向数组,数组再指向常量池,一个是field直接指向常亮池