突然想起自己在刚学Java的时候,成员变量名和方法参数名相同时,我搞不清楚set方法中的this到底是指的哪个值。想想真的挺搞笑的,但是当时确实就是想不明白,哈哈。这种类似傻傻分不清的问题,Java的传递问题算一个,今天来总结一下。
直接先解答问题:
在Java中方法参数传递的方式是按值传递的。
参数是基本类型,传递的是基本类型的字面量值的拷贝。
参数是引用类型,传递的是所引用的对象在堆中地址值的拷贝。
或者有的更官方一点的说法叫:Java函数调用时采用的求值策略为值传递。
如果你理解上面的几句话,那么基本没什么问题了,如果不太懂,那么请往下看,直接上代码和图:
class MainTest {
public static void main(String[] args) {
int count = 3;
changeNumber(count);
System.out.println(count);
String content = "科比";
changeString(content);
System.out.println(content);
SportsMan sportsMan = new SportsMan("科比");
changeObject(sportsMan);
System.out.println(sportsMan.name);
List list = new ArrayList<>();
list.add("一");
list.add("二");
changeObjectValue(list, sportsMan);
for (String str : list) System.out.print(str);
System.out.println("\n" + sportsMan.name);
}
private static void changeNumber(int count) {
count += 10;
}
private static void changeString(String content) {
content += "投进一个三分球";
}
private static void changeObject(SportsMan sportsMan) {
sportsMan = new SportsMan("艾弗森");
}
private static void changeObjectValue(List list, SportsMan sportsMan) {
list.add("三");
sportsMan.name = "哈登";
}
private static class SportsMan {
String name;
public SportsMan(String name) {
this.name = name;
}
}
}
运行结果:
3
科比
科比
一二三
哈登
上图:
理解上面代码的结果,我们要明白两个概念:实参和形参。
形参是作为实参的一个拷贝,我们可以直接看到前三个方法传递进来的值,直接显示为灰色,其实这些值的改变是对原值无影响的。
其实就是一个很简单的问题,说的生动一些吧,我用播音员拿稿子打一个比方:
我们将参数传入方法当中比做——有人往播音室递了一张纸
实参——原稿件
形参——这张纸
参数是基本类型,字面量的拷贝——直接在纸上写着稿子
参数是引用类型,引用地址的拷贝——纸上写着保存这个稿子的文档地址(比如腾讯文档)
纸上面写着稿子,无论你在这张纸怎么改,都不会影响到原来的稿子
纸上面写着文档地址,你把文档里面的稿子改了,原稿件也就跟着改了
把纸上面的文档地址换成了另一个稿件的文档地址,那原稿件内容肯定是不变的
那么就有了这一道面试题:
以下程序输出结果是什么:
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]
主要是看modifyPoint方法:
private static void modifyPoint(Point p1, Point p2) {
// tmpPoint引用着形参p1的地址,也就是main方法中实参p1的地址
Point tmpPoint = p1;
// p1引用着形参p2的地址,也就是main方法中实参p2的地址
p1 = p2;
// p2引用着形参p1的地址,也就是main方法中实参p1的地址,熟悉的味道,就是互相交换了一下
p2 = tmpPoint;
// 此时形参p1引用的是main方法中的实参p2的地址
// 形参p2引用的是main方法中的实参p1的地址
// 给形参p1修改了location,那么修改的就是main方法中实参p2的location
p1.setLocation(5, 5);
// 形参p2的引用变为了一个新的对象,那么原来的对象,main方法中的实参p1的属性不变
p2 = new Point(5, 5);
// 所以最终main方法中p1的location仍是(0,0), p2的location变为了(5,5)
}
总结
其实我觉得值引用的这个问题,是一个很实在的问题,我们在实际的开发中难免会遇到各种基本类型、对象以及方法传参的问题。如果对值引用还很模糊的话,在开发中一旦出现问题我们会感到很莫名其妙,然后再花大量的时间去解决这些原本很基础的问题,真的是得不偿失。