2010-07-20 50 views
6

Tôi cảm thấy giống như một người mới để hỏi câu hỏi này - nhưng tại sao khi tôi chuyển Set bên dưới vào phương thức của tôi và trỏ nó đến một HashSet mới, nó vẫn xuất hiện dưới dạng EmptySet ? Có phải vì các biến cục bộ được cấp phát trên ngăn xếp và vì vậy, mới của tôi có bị thổi bay khi tôi thoát khỏi phương thức không? Làm thế nào có thể Tôi đạt được chức năng tương đương?Phân bổ bộ nhớ Java trên stack vs heap

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>(); 

    } 

} 

Trả lời

8

Java chuyển tham chiếu theo giá trị, hãy nghĩ đến mySet làm bản sao của tham chiếu foo. Trong void test(Set<Integer> mySet), biến số mySet chỉ là biến cục bộ trong hàm đó, do đó, đặt biến thành một biến khác không ảnh hưởng đến người gọi trong main.

mySet không tham chiếu (hoặc "trỏ tới" nếu bạn muốn) cùng một Tập hợp với số foo thay đổi trong main.

Nếu bạn muốn thay đổi các tài liệu tham khảo trong chính, bạn có thể làm ví dụ:

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

... Có phải vì các biến địa phương được cấp phát trên stack, và vì vậy mới của tôi đang bị thổi bay khi Tôi thoát khỏi phương pháp?

Không. Đó là vì đối số chuyển ngữ nghĩa của Java.

Đối số Java được chuyển "theo giá trị", nhưng trong trường hợp đối tượng hoặc loại mảng, giá trị bạn đang truyền là tham chiếu đối tượng/mảng. Khi bạn tạo và gán một đối tượng set mới cho mySet, bạn chỉ cần đặt biến cục bộ/tham số. Vì Java sử dụng truyền theo giá trị, điều này không ảnh hưởng đến biến số foo trong phương thức main.

Khi bạn nhập phương thức test, bạn có hai bản sao tham chiếu đến ví dụ HashSet được tạo trong phương thức main; một trong số foo và một ở số mySet. Mã của bạn sau đó thay thế tham chiếu trong mySet với tham chiếu đến một mới được tạo ra HashSet, nhưng tham chiếu mới này không được chuyển lại cho người gọi. (Bạn có thể thay đổi mã của bạn để vượt qua nó lại ... ví dụ như là kết quả của phương pháp test Nhưng bạn phải làm điều này một cách rõ ràng..)

OK - tuy nhiên - nếu tôi được làm thêm hoặc một số hoạt động khác trong cuộc gọi phương thức của tôi, phân bổ đó sẽ được giữ nguyên. Tại sao vậy?

Đó là bởi vì khi bạn gọi một phương pháp dụ bằng cách sử dụng tài liệu tham khảo trong foo hoặc mySet, phương pháp đó được thực hiện trên đối tượng (HashSet) mà tài liệu tham khảo đề cập đến. Giả sử rằng hai tham chiếu trỏ đến cùng một đối tượng, "phân bổ của bạn sẽ được giữ nguyên". Hoặc chính xác hơn, bạn có thể quan sát các tác động của các phép toán trên một tham chiếu đến một đối tượng thông qua các phép toán trên các tham chiếu khác tới cùng một đối tượng.

Chỉ cần nhớ rằng phương thức Java gọi các tham chiếu sao chép vào đối tượng, chứ không phải chính các đối tượng đó.

Bằng cách này, bạn sẽ không thể thêm các phần tử vào tập hợp được trả về bởi Collections.emptySet(). Đối tượng thiết lập đó là bất biến.Gọi điện (ví dụ) add trên đó sẽ ném một ngoại lệ.

+0

Mỹ, tuy nhiên - nếu tôi được làm thêm hoặc một số hoạt động khác trong phạm vi cuộc gọi phương thức của tôi, phân bổ đó sẽ được giữ nguyên. Tại sao vậy? –

+0

Câu trả lời rõ ràng Stephen, cảm ơn bạn. –

0

Không chắc chắn tôi hiểu câu hỏi. Trong phương thức test, bạn đang tạo một bộ mới và gán nó cho biến địa phương mySet. mySet thì sẽ không còn tham chiếu cùng một tập hợp như foo sẽ quay lại trong Main.

Khi bạn trở về từ phương pháp, foo vẫn tham chiếu emptySet() gốc và HashSet được tạo trong phương pháp sẽ được đánh dấu để thu thập rác.

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 
    } 
} 

Làm thế nào tôi có thể đạt được các chức năng tương đương ?

Tôi không chắc chắn nếu tôi hiểu câu hỏi này, bạn đang tìm kiếm một sự trở lại?

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

Bây giờ, nếu bạn chỉ định foo cho thử nghiệm nào, bạn có "chức năng tương đương" không?

1

'Foo' của bạn được gọi là bộ trống đi vào cuộc gọi thử nghiệm(), cuộc gọi thử không sửa đổi đối tượng và do đó nó vẫn là bộ trống khi trả lại từ đó. Trong phương thức test(), 'mySet' chỉ là tham chiếu cục bộ, tham chiếu đến tập hợp gốc (foo) khi nhập, và khi bạn gán một HashSet mới cho tham chiếu đó, bạn đã mất tham chiếu vào tập hợp ban đầu. Nhưng những hiệu ứng này hoàn toàn cục bộ với phương thức test(), bởi vì java chỉ đơn giản là đưa ra test() một bản sao của tham chiếu đến tập gốc.

Bây giờ, trong thử nghiệm(), vì bạn có tham chiếu đến đối tượng ban đầu, bạn có thể sửa đổi đối tượng đó. Ví dụ: bạn có thể thêm các phần tử vào tập hợp đó. Nhưng bạn không thể thay đổi tham chiếu trong hàm gọi, bạn chỉ có thể thay đổi tham chiếu. Vì vậy, bạn không thể thay thế một bộ sưu tập bằng một bộ sưu tập khác, và nếu bạn muốn có một HashSet ở vị trí đầu tiên, bạn phải tạo mới HashSet trong main().

0

bạn nên đọc cuốn sách này:

"Một Programmer của Hướng dẫn Java SCJP Chứng nhận: Một Primer toàn diện (3rd Edition)"

+0

Cảm ơn con trỏ. –

Các vấn đề liên quan