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

记得以前好像在一本书上看到说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);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值