在进入正题之前,我们首先来了解一下JAVA对象的引用和实体。在JAVA中对象是在堆中分配内存,对象的引用是在栈中分配内存。也就是说,当用类创建一个对象时,类中的成员变量在堆中分配内存空间,这些内存空间称为该对象的实体和对象的变量。而对象中存放着引用,该引用在栈中分配内存,以确保实体由该对象操作使用。
下面给出一个小例子。
我们定义了一个类的构造方法Point(int x,int y)创建了两个对象p1,p2。Point p1 = new Point(12,16);
Point p2 = new Point(6,18);那么内存模型是:
假如在程序中使用如下的赋值语句:
p1 = p2;
即把p2的引用(p2在内存中的名字)赋值给了p1,因此p1和p2在本质上是一样的.虽然在源程序中p1和p2是两个名字,,但他们在系统中只有一个--0XDD。系统将取消原来分配给p1的内存。这时候p1和p2将具有相同内存实体。则内存模型是:
一个类创建的两个对象,如果具有相同的引用,那么就具有完全相同内存实体。
接下来我们进入正题,在JAVA中当方法被调用,如果方法有参数,那么参数必须实例化,即参数变量必须有具体的值。在JAVA中所有的参数都是“传值”的(请注意这里是引号),也就是说,方法中参数变量的值就是调用者指定值的一个副本。
1.简单类型是按值传递的:
public class Test {
public static void test(boolean test) {
test = ! test;
System.out.println("In test(boolean): test = " + test);
}
public static void main(String[] args) {
boolean test = true;
System.out.println("Beforetest(boolean) : test = " + test);
test(test);
System.out.println("Aftertest(boolean) : test = " + test);
}
}
运行结果:
Beforetest(boolean) : test = true
In test(boolean) : test = false
After test(boolean) : test = true
2.所谓的“传引用”调用:
那么JAVA是传引用还是传值调用,问题还是看你是怎样传递对象。因为JAVA的简单类型没有引用。
那么引用是什么?引用其实是一个对象的名字或者别名。一个对象会在内存中申请一块空间来保存数据。我们访问对象的数据时,不会直接去访问,而是通过引用来访问。引用是一种数据类型,相当于c++中的指针。指示了对象中数据在内存中的地址。
如果我们定义了不只一个引用。这些引用是互不相同的,因为引用也是一种数据类型,但他们的值是相同的,因为都指向了同一块内存地址。
下面来介绍一个例子:
/*例 2 */
/**
* @(#) Test.java
* @author fancy
*/
public class Test {
public static void test(StringBuffer str) {
str.append(", World!");
}
public static void main(String[] args) {
StringBuffer string = newStringBuffer("Hello");
test(string);
System.out.println(string);
}
}
运行结果:
Hello,World
这里需要纠正一下,看了上面例子不要认为传对象都是引用调用,这道例题中是由于a和b指向了同一块内存地址,所以修改其中一个就可以更改数据。你可以看看下面例子:
/*例 3 */
/**
* @(#) Test.java
* @author fancy
*/
public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}
结果是:
Hello
看完上面的例子,你可能有点糊涂,为什么这道题就不行了,首先我们来分析一下。当我们把值传进函数还未执行函数体时,这时候string,str都指向了"hello",可是执行完函数体时,str就指向了"world",所以你这时在输出string时就只能看到"hello"。
我们现在可以做出总结了,JAVA中基本数据不存在引用,JAVA的引用类型包括前面学到的对象,数组,以及接口。
具体是否传进函数时是否修改了值,还是要看你是否直接对值进行了修改,还是把引用改变了指向的对象。
最后,之所以存在这个问题,主要是由于c语言中有个东西叫“指针”。JAVA中没有指针
我们给出两个例子以示区分:
/**
* @(#) test.c
* @author fancy
*/
void SwapValue(int a, int b) {
int t = a;
a = b;
b = t;
}
void SwapPointer(int * a, int * b) {
int t = * a;
* a = * b;
* b = t;
}
void main() {
int a = 0, b = 1;
printf("1 : a = %d, b = %d/n", a, b);
SwapValue(a, b);
printf("2 : a = %d, b = %d/n", a, b);
SwapPointer(&a, &b);
printf("3 : a = %d, b = %d/n", a, b);
}
结果是:
1: a = 0, b = 1
2 : a = 0, b = 1
3 : a = 1, b = 0
另外一个:
/**
* @(#) Test.java
* @author fancy
*/
public class Test {
public static void swap(int[] data, int a, int b) {
int t = data[a];
data[a] = data[b];
data[b] = t;
}
public static void main(String[] args) {
int[] data = new int[10];
for (int i = 0; i < 10; i++) {
data[i] =(int) (Math.random() * 100);
System.out.print(" " + data[i]);
}
System.out.println();
for (int i = 0; i < 9; i++) {
for (int j =i; j < 10; j++) {
if (data[i] > data[j]) {
swap(data, i, j);
}
}
}
for (int i = 0; i < 10; i++) {
System.out.print(" " + data[i]);
}
System.out.println();
}
}
结果是:
7869 94 38 95 31 50 97 84 1
1 31 38 50 69 78 84 94 95 97
最后感谢houpuhope的博客的例子。