您的位置:首页 > 编程语言 > Java开发

Java对对象采用的是值调用还是引用调用?

2016-05-16 13:21 369 查看
记得以前好像在一本书上看到说Java中基本类型是值调用,类对象实例是引用调用,写程序时候也没感觉有什么不对,后来看到Java核心卷中赫然有一句话:对象引用进行的是值传递。作者为此还专门给出一个例子作为佐证,后面会提到。先来用C++的经典小程序说一下值调用,地址调用,引用调用:

#include <iostream>
#include <cstdio>
using namespace std;

void exchange(int a, int b)
{
printf("参数传进来以后局部变量a, b原始值: a = %d, b = %d\n", a, b);
printf("参数传进来以后局部变量a, b原始地址: a = %d, b = %d\n", &a, &b);
// 参数传进来以后局部变量a, b原始值: a = 1, b = 2
// 参数传进来以后局部变量a, b原始地址: a = 2686736, b = 2686740

int tmp = a;
a = b;
b = tmp;
printf("函数调用以后局部变量a, b的值: a = %d, b = %d\n", a, b);
printf("函数调用以后局部变量a, b的地址: a = %d, b = %d\n", &a, &b);
// 函数调用以后局部变量a, b的值: a = 2, b = 1
// 函数调用以后局部变量a, b的地址: a = 2686736, b = 2686740
}

int main(int argc, char const *argv[])
{
int a = 1, b = 2;
printf("a, b原始值: a = %d, b = %d\n", a, b);
printf("a, b原始地址: a = %d, b = %d\n", &a, &b);
// a, b原始值: a = 1, b = 2
// a, b原始地址: a = 2686764, b = 2686760
exchange(a, b);
printf("交换以后a, b的值: a = %d, b = %d\n", a, b);
printf("交换以后a, b的地址: a = %d, b = %d\n", &a, &b);
// 交换以后a, b的值: a = 1, b = 2
// 交换以后a, b的地址: a = 2686764, b = 2686760

return 0;
}

我们发现值传递的时候实参的值拷贝给了形参(只是把值拷贝了过去,地址明显不同),你在调用的函数不管做什么都没有卵用。就好比说,孩儿,我给你建一座跟我的房子一模一样的房子,你随便在那里折腾,但休想动我家的东西。接着再用指针进行地址传递:

#include <iostream>
#include <cstdio>
using namespace std;

void exchange(int *a, int *b)
{
printf("参数传进来以后局部变量指针a, b指向的值: a = %d, b = %d\n", *a, *b);
printf("参数传进来以后局部变量指针a, b的值: a = %d, b = %d\n", a, b);
// 参数传进来以后局部变量指针a, b指向的值: a = 1, b = 2
// 参数传进来以后局部变量指针a, b的值: a = 2686764, b = 2686760

int tmp = *a;
*a = *b;
*b = tmp;
printf("函数调用以后局部变量指针a, b指向的值: a = %d, b = %d\n", *a, *b);
printf("函数调用以后局部变量指针a, b的值: a = %d, b = %d\n", a, b);
// 函数调用以后局部变量指针a, b指向的值: a = 2, b = 1
// 函数调用以后局部变量指针a, b的值: a = 2686764, b = 2686760
}

int main(int argc, char const *argv[])
{
int a = 1, b = 2;
printf("a, b原始值: a = %d, b = %d\n", a, b);
printf("a, b原始地址: a = %d, b = %d\n", &a, &b);
// a, b原始值: a = 1, b = 2
// a, b原始地址: a = 2686764, b = 2686760
exchange(&a, &b);
printf("交换以后a, b的值: a = %d, b = %d\n", a, b);
printf("交换以后a, b的地址: a = %d, b = %d\n", &a, &b);
// 交换以后a, b的值: a = 2, b = 1
// 交换以后a, b的地址: a = 2686764, b = 2686760

return 0;
}

我们发现地址传递是把实参的地址拷贝给形参的值。形参拿着这个地址通过“解引用”就得到了实参的值,然后就可以胡作非为了。就好比,我给了你我家的钥匙,你就可以随便动我家的东西。然后我们看大师怎么用一个例子证明Java中对象引用进行的是值传递:

import java.io.Console;
import java.io.PrintWriter;
import java.text.DateFormatSymbols;
import java.util.*;

public class InputTest {
private String name;
private int id;
public InputTest(String n, int i) {
name = n;
id = i;
}
public static void exchange(InputTest n1, InputTest n2) {
System.out.println(n1.name + " " + n1.id + " " + n2.name + " " + n2.id);
System.out.println(n1 + " " + n2);
// 张三 1 李四 2
// InputTest@15db9742 InputTest@6d06d69c
InputTest tmp = n1;
n1 = n2;
n2 = tmp;
System.out.println(n1.name + " " + n1.id + " " + n2.name + " " + n2.id);
System.out.println(n1 + " " + n2);
// 李四 2 张三 1
// InputTest@6d06d69c InputTest@15db9742
}

public static void main(String[] args) {
InputTest n1 = new InputTest("张三", 1);
InputTest n2 = new InputTest("李四", 2);
exchange(n1, n2);
System.out.println(n1.name + " " + n1.id + " " + n2.name + " " + n2.id);
System.out.println(n1 + " " + n2);
// 张三 1 李四 2
// InputTest@15db9742 InputTest@6d06d69c
}
}

看过以后你可能更迷惑了?你可能会说等等!实参明明是把自己的地址拷贝给了形参的值,这不就是C++中的地址传递吗?不对,既然是地址传递,可是又没有交换两个对象,我勒个去,这是嘛玩意啊!哎,不对,函数里面应该“解引用”一下,通过钥匙去实参的别墅里胡搞一下,可惜Java中没有“解引用”,不过我们照样可以交换,比如我们想交换两个对象的名字,可以这样写;
String tmp = n1.name;
n1.name = n2.name;
n2.name = tmmp;

至此是不是有些想法了呢?最后我们再来看一下引用传递:

#include <iostream>
#include <cstdio>
using namespace std;

void exchange(int &a, int &b)
{
printf("参数传进来以后局部变量引用a, b的值: a = %d, b = %d\n", a, b);
printf("参数传进来以后局部变量引用a, b的地址: a = %d, b = %d\n", &a, &b);
// 参数传进来以后局部变量引用a, b的值: a = 1, b = 2
// 参数传进来以后局部变量引用a, b的地址: a = 2686764, b = 2686760

int tmp = a;
a = b;
b = tmp;
printf("函数调用以后局部变量引用a, b的值: a = %d, b = %d\n", a, b);
printf("函数调用以后局部变量引用a, b的地址: a = %d, b = %d\n", &a, &b);
// 函数调用以后局部变量引用a, b的值: a = 2, b = 1
// 函数调用以后局部变量引用a, b的地址: a = 2686764, b = 2686760
}

int main(int argc, char const *argv[])
{
int a = 1, b = 2;
printf("a, b原始值: a = %d, b = %d\n", a, b);
printf("a, b原始地址: a = %d, b = %d\n", &a, &b);
// a, b原始值: a = 1, b = 2
// a, b原始地址: a = 2686764, b = 2686760
exchange(a, b);
printf("交换以后a, b的值: a = %d, b = %d\n", a, b);
printf("交换以后a, b的地址: a = %d, b = %d\n", &a, &b);
// 交换以后a, b的值: a = 2, b = 1
// 交换以后a, b的地址: a = 2686764, b = 2686760

return 0;
}

你的第一感觉是和地址传递很像,因为他们都到了交换的目的。可是有点不同,引用传递时实参和形参的地址完全相同,值相同,简直没有区别,而地址传递时实参的值是形参解引用以后指向的内容。至此三种传递解说完毕。最后看看Java核心卷中大师给我们的忠告:

1、一个方法不能改变一个基本数据类型的参数(即数值型和布尔型) 

基本类型在内存中应该是以值储存的,调用方法传递的时候又重新拷贝了一份值,所以方法中改变不了原来的值

2、一个方法可以改变一个对象参数的状态

类实例对象把引用传了进来,我就可以改变原来的值,比如我可以在方法中这样写n1.name = othername;

3、一个方法不能让对象参数引用一个新的对象

类实例对象传进来以后,实参与形参同时指向同一个内存中的对象,自然不能让形参在引用另一个新对象,不过我感觉你可以new一下,像这样写n1 = new InputTest("aa", 1);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: