堆栈与堆上的Java内存分配

时间:2010-07-20 01:28:28

标签: java stack heap allocation

我觉得自己像是一个问这个问题的新手 - 但为什么当我将下面的Set传递给我的方法并将其指向一个新的HashSet时,它仍然作为EmptySet出现?是因为局部变量是在堆栈上分配的,所以当我退出方法时,我的 new 会被吹走? 我如何实现功能等同?

import java.util.HashSet;
import java.util.Set;

public class TestMethods {

    public static void main(final String[] args) {

        final Set<Integer> foo = java.util.Collections.emptySet();
        test(foo);

    }

    public static void test(Set<Integer> mySet) {

        mySet = new HashSet<Integer>();

    }

}

6 个答案:

答案 0 :(得分:8)

Java按值传递引用,将mySet视为foo引用的副本。在void test(Set<Integer> mySet)中,mySet变量只是该函数中的局部变量,因此将其设置为其他变量不会影响main中的调用者。

mySet确实引用(或“指向”,如果您愿意)与foo变量在main中所做的相同的设置。

如果你想改变main中的引用,你可以这样做:

foo = test(); //foo can't be final now though
 public static Set<Integer>  test() {
   return new HashSet<Integer>();
}

答案 1 :(得分:8)

  

...是因为局部变量是在堆栈上分配的,所以当我退出方法时,我的新东西会被吹走吗?

没有。这是因为参数传递了Java的语义。

Java参数是“按值”传递的,但在对象或数组类型的情况下,传递的值是对象/数组引用。创建新的设置对象并将其分配给mySet时,只需设置局部变量/参数即可。由于Java使用pass by value,因此这对foo方法中的main变量没有影响。

当您输入test方法时,您有两个对HashSet方法中创建的main实例的引用副本; foo中的一个和mySet中的一个。然后,您的代码将mySet中的引用替换为对新创建的HashSet的引用,但此新引用不会传递回调用方。 (您可以更改您的代码以将其传回...例如test方法的结果。但您必须明确地执行此操作。)

  

好的 - 但是 - 如果我要在方法调用中添加或执行其他操作,则会保留该分配。那是为什么?

这是因为当您使用foomySet中的引用调用实例方法时,该方法将在引用所引用的对象(HashSet)上执行。假设两个引用指向同一个对象,您的“分配将被保留”。或者更准确地说,您可以通过对同一对象的其他引用上的操作来观察操作对对象的一个​​引用的影响。

请记住,Java方法调用对象的复制引用,而不是对象本身。

顺便说一句,您将无法将元素添加到Collections.emptySet()返回的集合中。该set对象是不可变的。在其上调用(例如)add将引发异常。

答案 2 :(得分:1)

你的'foo'引用了一个进入test()调用的空集,测试调用没有修改那个对象,所以它从那里返回时仍然是一个空集。

在test()方法中,'mySet'只是一个本地引用,在引用时引用原始set(foo),当你为该引用分配新的HashSet时,你丢失了对原来的一套。但是这些效果都完全是test()方法的局部效果,因为java只是给了test()一个对原始集的引用的副本。

现在,在test()中,由于您有对原始对象的引用,因此可以修改该对象。例如,您可以向该集添加元素。但是你无法在调用函数中更改引用,只能更改它所引用的内容。所以你不能用一个不同的集合替换一个集合,如果你想要一个HashSet,你必须在main()中新建一个HashSet。

答案 3 :(得分:0)

不确定我理解这个问题。在test方法中,您将实例化一个新集并将其分配给本地mySet变量。 mySet然后不再引用fooMain所引用的相同集。

当您从方法返回时,foo仍引用原始emptySet(),并且方法中创建的HashSet将被标记为垃圾回收。

答案 4 :(得分:0)

import java.util.HashSet;
import java.util.Set;

public class TestMethods {

    public static void main(final String[] args) {

        final Set<Integer> foo = java.util.Collections.emptySet();
        test(foo);

    }

    public static void test(Set<Integer> mySet) {
        // here mySet points to the same object as foo in main
        mySet = new HashSet<Integer>();
        // mySet now points to a new object created by your HashSet constructor call,
        // any subsequent operations on mySet are no longer associated with foo, because
        // they are no longer referencing the same object
    }
}
  

我怎样才能实现功能   等效?

我不确定我是否理解这个问题,你在寻找回报吗?

    public static Set<Integer> test(Set<Integer> mySet) {
        for(Integer i : mySet){
            // do something??
        }
        mySet = new HashSet<Integer>();
        return mySet;
    }

现在,如果你将foo分配给哪个测试返回,你有“功能等价物”?

答案 5 :(得分:0)

你应该读这本书:

“Java SCJP认证程序员指南:综合入门(第3版)”