我熟悉java中的引用调用概念,但看到这段代码后,我对感到困惑
public class App
{
public static void main( String[] args )
{
Test t1 = new Test(1);
Test t2 = new Test(8);
App.doSomething(t1, t2);
System.out.print(t1.a);
System.out.print(t2.a);
}
public static void doSomething(Test t1, Test t2){
System.out.print(t1.a++);
System.out.print(t2.a++);
t1 = new Test(999);
t2 = new Test(888);
}
}
打印:
1
8
2
9
为什么主函数中"t1.a"one_answers"t1.b"的值没有变为888和999?
Java没有引用调用。所有参数都是通过值传递的,但如果参数是对象,则传递的"值"是对对象的引用。
因此,如果在方法中使用对该对象的本地引用来修改该对象,它将修改同一对象。但是,如果将某个内容指定给该局部变量,则它将不再具有对原始对象的引用,随后的更改也不会影响原始对象。
这就是为什么你通常将方法参数设置为final-以避免这种混乱:)findbugs实际上会产生关于这种影响的样式警告
这是因为在java中,参数总是pass-by-value
。
在您的示例中,您传递了对对象的引用,但是该引用是按值传递的。换句话说,将创建新的局部变量t1
,并使用参数中传递的对对象的引用对其进行初始化。因此,当您将new Test()
分配给它时,只有局部变量引用新对象。
在Java中,一切都是按值传递的。两个t1和t2的范围不同。一个在main()的作用域中,另一个在doSomething()的范围中。因此,它没有更改为888或999的原因是,一旦您离开doSomething(),这些值就不存在了。
新手对此很难理解,我将尝试以故事的形式解释:
如果我让你借我的便签(上面写着我的名字),上面有一个指向红色沙发的箭头,你拿着我的便票,擦掉我的箭头,换上一个指向绿色沙发的新箭头。当你完成你的恶作剧时,我会有一张写有我名字的便签,指向绿色沙发。我的粘手被换了。
然而,如果我让你借用我的写有我名字的便签,上面有一个指向红色沙发的箭头,你把我的便签放在一边,你写了一个写有你名字的新便签,然后你在上面放了一个指向绿色沙发的箭头。然后你完成了你的恶作剧,那么我的便票就没有改变,我的名字指向红色沙发。你制作了我够不着的便签。
当你把一个新对象塞进t1时,你没有销毁旧对象,因为你没有这样做的授权。你只是把它推到一边,然后创建了一个新的对象,除了你,没有人可以访问它。
此行:
t1 = new Test(999);
不会擦除旧的t1。您只需创建一个新的t1,它只在方法的本地,在方法结束后进行垃圾收集。在这一行中,您没有更改传入的参数,而是创建了一个同名的新变量。