class Value{
    public int i = 15;

public class Test{
    public static void main(String argv[]){
         Test t = new Test();
         t.first();
    } 
    public void first(){
         int i = 5;
         Value v = new Value();      /// 这里创建了第一个对象[1],由 v 引用
         v.i = 25;
         second(v, i);               /// [1]的引用通过参数传进 second 方法,看 second
         System.out.println(v.i);    /// 这里的 v 仍然引用的 [1]
    } 
    public void second(Value v, int i){ /// 这个 v 由外面传入,引用[1],是外面那个 v 的拷贝(仅是引用的拷贝,引用的都是[1])
         i = 0;
         v.i = 20;
         Value val = new Value();    /// 创建 [2]
         v =  val;                   /// 内部的v引用[2],外部的v不变,仍引用[1]
         System.out.println(v.i + " " + i);      
    }

解决方案 »

  1.   

    在这个问题里,其实可以看作是有两个v。
    第一个是在first方法里的,即Value v = new Value();
    姑且叫做v1。
    第二个是在second方法的参数里声明的v,即second(Value v
    我们把它叫做v2。
    现在来看,在first里先实例化了一个Value,我们把它叫做j1,并且用v1引用它。<<Value v = new Value();>>
    接着给这个j1的属性i赋值为25。<<v.i = 25;>>
    然后调用second,并且把上边的j1传入了这个second。<<second(v, i);>>
    现在来看second的处理。
    现在进入second,因为j1传入了这个second,所以v2引用了j1,也就是说现在v2和v1同时引用了j1。
    接着给j1的i属性赋值为20。也就是说上边赋值好的25现在已经被替换为20了。<<v.i = 20;>>Now,我们又实例化一个Value,并且声明了一个val来引用这个实例。<<Value val = new Value();>>
    我们把这个新的实例叫做j2,把这个val叫做v3。接着又把v2的引用指向v3,也就是说v2指向了j2这个实例。<<v =  val;>>
    现在打印v2引用的实例的i属性,即j2的i属性。<<System.out.println(v.i + " " + i); >>
    因为j2实例化以后并没有对它进行赋值,所以它的i保留了初始的15。同时打印的那个i,你也可以看到它在second方法开始的地方被设为0了。你的第一行结果就出来了。现在second方法执行完毕,其中的v2和v3引用以及实例j2,它们的生命期到此结束。
    现在看first的最后一行,它打印v1的i属性,由于v1一直指向j1,从未改变,j1的i属性前边已被改变为20。自然现在打出来也就是20了。