Java是通过值传递还是通过引用传递或两者兼而有之?

时间:2013-08-15 09:07:52

标签: java pass-by-reference pass-by-value

考虑以下案例。

    List<Integer> listOne = new ArrayList<>();
    List<Integer> listTwo = new ArrayList<>();
    listOne.add(1);I think this happens due to 
    listOne.add(2);
    listOne.add(3);
    Collections.reverse(listOne);
    listTwo = listOne;  //listTwo has same reference 
    Collections.reverse(listOne);
    System.out.println(listOne);  //out put [1, 2, 3] 
    System.out.println(listTwo);  // same out put

Java is pass by value, where values (for non primitive types) happen to be references.我认为这为这种情况提供了java的生存。说实话,java为什么试图避免pass by reference并试图与some of other languages不同?而java仍然受到引用行为的影响?

编辑:另外请一些人解释上面代码中发生的事情

4 个答案:

答案 0 :(得分:3)

Java不会受到引用行为的影响,它使它们成为:)

当你写

  

列表listOne = new ArrayList&lt;&gt;();

您需要考虑三个事项:

1)一个变量,它是一块内存,名为 listOne

2)堆上的一个对象,是一个ArrayList的实例,它是一个更大的内存块,没有名称

3)listOne变量的值,它不是内存块,而是放在变量listOne的内存中的0和1的集合,并且该值也没有名称。

现在,当我们谈论listOne是通过值还是通过引用传递时,我们使用不精确的术语,这会导致误解。 listOne (事物1)根本没有传递,既不是值也不是引用。传递listOne(thing 3)的值,这样就可以访问ArrayList对象(第2项)。因此,如果使用名称“listOne”但是意味着东西3,则通过值传递,如果意味着事情2,则被参照通过。在这两种情况下,名称“listOne”不是东西2或东西3的正确名称,但是因为它简短而方便使用。

答案 1 :(得分:1)

Java是按值传递的。实际参数的副本传递给参数。在原始数据类型的情况下很明显,形式参数的变化没有在实际参数中显示。

 static void incre(int a)
      {
        a++;
      }
public static void main (String a[])
{ 
  int c=3;
  incre(c);
  System.out.println(c);  //still prints 3
}

在引用的情况下会发生确切的事情,但是制作引用副本不会创建新对象,它们现在都指向同一个对象。引用所做的更改将反映在此处。

class Demo { 
  int c =2; 

  Demo(int c)
    {
       this.c=c;
    } 

  void incObject (Demo x)    
    {
       (x.c)++;
    }

  int show()
   {
      return c;
   }
  public static void main (String []args)    
   {
    Demo o = new Demo(1);
    o.incObject(o);
   System.out.print(o.show()); //prints 2

 }

}

答案 2 :(得分:0)

 Collections.reverse();

修改支持数组。从实施中可以清楚地看出:

 public static void reverse(List<?> list) {
        int size = list.size();
        if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
                swap(list, i, j);
        } else {
            ListIterator fwd = list.listIterator();
            ListIterator rev = list.listIterator(size);
            for (int i=0, mid=list.size()>>1; i<mid; i++) {
                Object tmp = fwd.next();
                fwd.set(rev.previous());
                rev.set(tmp);
            }
        }
    }

现在,listOnelistTwo具有相同的引用,指向相同的后备数组。因此,无论哪个句柄(listOne或listTwo)修改后备阵列,另一个将反映相同的更改。在你的情况下:

    Collections.reverse(listOne);  // backing array has [3,2,1]
    listTwo = listOne;  //listTwo has same reference 
    Collections.reverse(listOne);  // backing array has [1,2,3]
    System.out.println(listOne);  //out put [1, 2, 3] 
    System.out.println(listTwo);  // same out put

关于通过value/reference而言。你自己说:

Java是按值传递的,其中值恰好是引用。

  

为什么java试图避免通过引用传递并尝试与之不同   其他一些语言?

其中一个主要原因是Java(JVM)管理自己的内存。

答案 3 :(得分:-2)

Collections.reverse(listOne);按值传递对listOne(对象)的引用,这就是定义“按引用传递”的方式。除了原语之外的所有东西都以这种方式传递:通过引用。它并没有试图避免它,它只是与C ++不同,它不会显式地使用指针。

编辑:好的,我想我知道你来自哪里。

private static void changelist(List<Integer> list) {
    list.add(4);//This modifies the list object
    list = new ArrayList<Integer>();//This modifies the local copy of the reference to the list object
    list.add(5);
}
public static void main(String[] args) {
    List<Integer> list = new ArrayList<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println(list);//output is [1,2,3]
    //This copies the value of the reference. 
    //If you modify the object in the underlying 
    //function, changes will be preserved when you return
    //However if you modify what the reference points to,
    //the function will only modify the local copy
    changelist(list);
    System.out.println(list);//output is [1,2,3,4]

}