Java方法传参方式记住了两点。基本上不会犯错误。
- 方法内修改基础数据类型(数字类型或布尔类型)不会改变方法外的参数;
- 方法内修改对象(包含数组)状态会同步修改方法方法外的参数。
程序片段1来验证第一点:
public static void main(String[] args) {
Integer a = 5;
System.out.println("入栈前 a = " + a);
calc(a);
System.out.println("出栈后 a = " + a);
}
private static void calc(Integer a) {
a = a * 3;
System.out.println("栈内计算 a = " + a);
}
程序的运行结果为:
入栈前 a = 5
栈内计算 a = 15
出栈后 a = 5
程序片段2来验证第二点
public static void main(String[] args) {
Stu tom = new Stu(30, "tom");
System.out.println("入栈前 tom = " + tom);
doubleScore(tom);
System.out.println("出栈后 tom = " + tom);
}
private static void doubleScore(Stu tom) {
tom.score = tom.score * 2;
System.out.println("栈内计算 tom = " + tom);
}
private static class Stu {
String name;
int score;
public Stu(int score, String name) {
this.score = score;
this.name = name;
}
@Override
public String toString() {
return "[ name='" + name + '\'' + ", score=" + score + ']';
}
}
片段2的运行结果为:
入栈前 tom = [ name='tom', score=30]
栈内计算 tom = [ name='tom', score=60]
出栈后 tom = [ name='tom', score=60]
从两段程序的结果看,片段1的基础数据参数用值传递来解释的通;片段2的对象参数用引用传递也行的通。
究竟参数传递采用哪种方式呢,特地翻了 Java编程思想第四版 第12章 传递和返回对象 中给出了解释,Java中所有参数或自变量都是通过句柄的方式传递的,向方法中传递一个参数时,实际上传递的只是指向外部对象的一个句柄。
看清楚了吗,人家根本不关心什么值传递、引用传递,实际上问题的关键是怎么理解 句柄。
- Java按值传递任何东西。句柄当做指针,传递对象就是拷贝对象的地址值,参数也指向该对象,方法内对象状态改外部对象也会改变;基础数据类型就更明显,基础数据传入方法会得到一个数据的副本,所以一切传参都是按值传递。
- Java主要按值传递,但对象却是引用传递。句柄看做对象的一个“别名”,由于对象传入方法没有获得一个本地副本,所以对象传递显然不是值传递,而是引用传递。
句柄 的理解不同带来了不同的结果,原书的作者直接回避了这个问题,也许java的设计者压根没有考虑这个问题,才会后面的争议。
还有一点一定要记住,记为第三点:
- 方法不能改变对象参数的引用
程序片段3来验证第3点:
public static void main(String[] args) {
Stu tom = new Stu(30, "tom");
System.out.println("入栈前 tom = " + tom);
convert(tom);
System.out.println("出栈后 tom = " + tom);
}
private static void convert(Stu tom) {
tom = new Stu(50, "jack");
System.out.println("栈内计算 tom = " + tom);
}
程序片段3来运行结果:
入栈前 tom = [ name='tom', score=30]
栈内计算 tom = [ name='jack', score=50]
出栈后 tom = [ name='tom', score=30]
总结
不管一切按值传递,还是主要按值传递,从这三个特点来讲都解释的通,没必要为概念纠结,明白特性才是关键!