一道关于Java引用传递和值传递的题目

package Test;

public class Point {

	private int x;

	private int y;

	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
	}

	private static void modifyPoint(Point p1, Point p2) {
		Point tmpPoint = p1;
		p1 = p2;
		p2 = tmpPoint;
		p1.setLocation(5, 5);
		p2 = new Point(5, 5);
		
	}
	
	public static void main(String[] args) {
		Point p1 = new Point(0, 0);
		Point p2 = new Point(0, 0);
		modifyPoint(p1, p2);
		System.out.println("[" + p1.x + "," + p1.y + "]" + "[" + p2.x + "," + p2.y + "]");
	}

}
输出结果:
[0,0][5,5]

上面题目main函数的执行步骤为:
1.在堆内存中创建两个Point对象,分别指向了栈内存中的p1,p2.
2.执行modifyPoint方法.
3.在modifyPoint方法中,Point tmpPoint = p1; p1 = p2; p2 = tmpPoint;是对传进来的p1,p2两个引用的交换.
4.再执行p1.setLocation(5, 5);此时已经是p2这个引用在调用setLocation方法,所以main函数中的对象p2会将设定值 (0,0) 换为 (5,5).
5.继续执行p2 = new Point(5, 5);要知道一个很重要的点:

 当对象的引用被执行赋值操作(=), 将不再指向原来的对象(新的对象不是原来的对象)

所以这一步执行完之后,main中的p1对象并没有被改变,此时modifyPoint方法中的p2main方法中的p1并不是指向同一片堆内存的Point对象.
6.最后执行System.err.println("[" + p1.x + "," + p1.y + "]" + "[" + p2.x + "," + p2.y + "]");会输出[0,0][5,5].

那么当对象的引用被执行赋值操作(=),是不是不再指向原来的对象,我们可以来观察hashcode值

代码如下

package Test;

public class Point {

	public static void main(String[] args) {
		Point p1 = new Point(0, 0);
		Point p2 = new Point(0, 0);
		System.out.println("main中的p1"+p1);
		System.out.println("main中的p2"+p2);
		modifyPoint(p1, p2);
		System.out.println("[" + p1.x + "," + p1.y + "]" + "[" + p2.x + "," + p2.y + "]");
	}

	private int x;

	private int y;

	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
	}

	private static void modifyPoint(Point p1, Point p2) {
		Point tmpPoint = p1;
		p1 = p2;
		p2 = tmpPoint;
		p1.setLocation(5, 5);
		p2 = new Point(5, 5);
		System.out.println("modifyPoint中的p1"+p1);
		System.out.println("modifyPoint中的p2"+p2);
	}
	
	
}
输出结果:
main中的p1Test.Point@15db9742
main中的p2Test.Point@6d06d69c
modifyPoint中的p1Test.Point@6d06d69c
modifyPoint中的p2Test.Point@7852e922
[0,0][5,5]
/**
    * 可以使用断点调试观察modify方法中p2的指向变化
    * Eclipse中引用被赋予新的值时, 调试界面中[变量列表]的变化为 黄色高亮显示
    */

注意不要重写这个类的hashcode方法!!!那么这个类就会继承父类object类的hashcode方法!而Objecthashcode方法默认返回的是内存地址.
从上面可以看出main中的p1Test.Point@15db9742modifyPoint中的p2Test.Point@7852e922hashcode值不相等,那么这俩对象的内存地址一定不相等,也就是说他俩并不指向同一块地址值.
所以当对象的引用被执行赋值操作(=)后, 将不再指向原来的对象(新的对象不是原来的对象)!!!

总结

Java引用传递和值传递其实主要是看参数列表声明中参数名的属性
引用的意义是起个别名, 其本身指的就是实际传入的那个参数地址, 这和 c++ 中指针功能是相近的.
而按照值传递,是传递一个参数副本进去,也就说这个副本必是非地址型的变量.
如果参数以 8大基本数据类型( byte short int long float double boolean char) 定义的, 则为按值传递,如果参数以类名或某个引用类型的变量定义的则为按引用传递.
基本数据类型, 变量的存储方式是比较精简的,而对于复杂数据类型, 诸如 class 定义的类的对象, 那么对象名所指的虽然是该对象存储的首地址, 而其存储方式上则相对复杂庞大. 而函数本身的参数列表基于存储空间的限制, 是不支持复杂的数据结构这种存储方式(因为他太浪费空间了), 那么作为复杂数据类型的传递, 只能以地址也就是引用的方式存在.

注意:
基本数据类型的值传递,不改变其值;而引用数据类型的值传递,改变其值
所以p1.setLocation(5, 5);改变了原值.

还有一点要注意

将程序改为下面这样

package Test;

public class Point {

	public static void main(String[] args) {
		Point p1 = new Point(1, 0);
		Point p2 = new Point(2, 0);
		modifyPoint(p1, p2);
		System.out.println("[" + p1.x + "," + p1.y + "]" + "[" + p2.x + "," + p2.y + "]");
	}

	private int x;

	private int y;

	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void setLocation(int x, int y) {
		this.x = x;
		this.y = y;
	}

	private static void modifyPoint(Point p1, Point p2) {
		Point tmpPoint = p1;
		p1 = p2;
		p2 = tmpPoint;
	}
	
}
输出结果:
[1,0][2,0]

输出结果却为[1,0][2,0]而不是[2,0][1,0]!
我们发现前后p1,p2的值并没有发生变化,也就是方法并没有改变存储在变量p1和p2中的对象引用。modifyPoint方法的参数p1和p2被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝的值而已,最终,所做的事都是白费力气罢了。在方法结束后p1和p2将被丢弃,而原来的变量p1和p2仍然引用这个方法调用之前所引用的对象.
这个过程也充分说明了java程序设计语言对对象采用的不是引用调用,实际上是对象引用进行的是值传递,当然在这里我们可以简单理解为这就是按值调用和引用调用的区别,而且必须明白即使java函数在传递引用数据类型时,也只是拷贝了引用的值罢了,之所以能修改引用数据是因为它们同时指向了一个对象,但这仍然是按值调用而不是引用调用.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值