2009-09-24 5 views
1

자바의 매우 수수께끼 인 기능 (?)을 발견했습니다.Java 메소드 인수 퍼즐

import java.util.ArrayList; 

public class Puzzle { 
    public static void main(String[] args) { 
     ArrayList<Integer> outer = new ArrayList<Integer>(); 
     outer.add(17); 
     Puzzle.change(outer); 
     outer.add(6); 
     System.out.println(outer); 

     // excpected output: 
     // [23] 
     // [23, 6] 
     // 
     // actual output: 
     // [23] 
     // [17, 7, 6] 
    } 

    public static void change(ArrayList<Integer> inner) { 
     inner.add(7); 
     inner = new ArrayList<Integer>(); 
     inner.add(23); 
     System.out.println(inner); 
    } 
} 

는 사람이 이상한 설명 할 수 :

그것은 다른 범위로 객체를 이동하는 방법을 인수 종류를 대체 할 "새로운"키워드를 사용하는 것 같다? 나는 배정으로 같은 종류의 행동을 알아 차렸다.

+0

그건 퍼즐이 아니라 기본 개념입니다. "가치에 의한 호출 대 참조로 호출"에 대한 Google. –

답변

8

이 자바 초보자를위한 클래식 여행을 업입니다. 문제의 일부는 우리가 이것을 식별 할 수있는 공통된 언어가 없다는 것입니다. 매개 변수가 값으로 전달됩니까? 객체는 참조로 전달됩니까? 당신이 누구에게 이야기하고 있는지에 따라, 모든 사람들이 똑같은 것을 말하고 싶지만 가치에 의해 전달되고 참조로 전달되는 단어에 대한 다른 반응이있을 것입니다.

이를 시각화하는 방법은 Java 변수가 두 가지 중 하나 일 수 있음을 이해하는 것입니다. 프리미티브 또는 객체에 대한 참조를 포함 할 수 있습니다. 그 참조를 메모리에있는 위치를 가리키는 숫자라고 생각하십시오. 특정 메모리 위치를 가리 키지 않는다는 점에서 C++ 의미의 포인터가 아니며 JVM이 객체를 찾을 수있는 특정 메모리 위치를 찾을 수 있도록 핸들의 역할을합니다. 하지만 당신은 이런 식으로 시각화 할 수 있습니다 :

ArrayList<Integer> outer = @1234; //The first reference where the ArrayList was created. 

당신은 다음의 매개 변수와 내부 전화 : 당신이 외부 변수를 전달하지 않는

Puzzle.change(@1234); 

주, 당신은 1234 @의 값을 전달합니다. 바깥 쪽은 메소드의 매개 변수가되어 어떤 식 으로든 변경 될 수 없습니다. 이것이 가치 전달이라고 말할 때 우리가 의미하는 바입니다. 값은 전달되지만 외부 변수와의 연결이 끊어집니다.

내부 퍼즐 :

public static void change(ArrayList<Integer> inner) { // a new reference inner is created. 
    //inner starts out as @1234 
    inner.add(7); 
    //now inner becomes @5678 
    inner = new ArrayList<Integer>(); 
    //The object @5678 is changed. 
    inner.add(23); 
    //And printed. 
    System.out.println(inner); 
} 

그러나 외부 여전히 방법이 변경되지 수로 1234 @ 가리키는, 그것은 단지 그 내용을 가지고, 외부 변수 없었어요. 그러나 변경 메소드가 @ 1234에 대한 참조로 시작 되었기 때문에 해당 위치의 객체는 메소드에 의해 실제로 변경 될 수 있으며 결과는 @ 1234에 대한 다른 참조에서 볼 수 있습니다. 변경 방법이 완료되면

는 아무것도 5678 @ 객체를 참조하지 않으므로 가비지 컬렉션의 대상이된다.

6

이것은 초보자를위한 고전적인 자바 질문 중 하나입니다.

내부 매개 변수는 값으로 전달됩니다 (비 프리미티브 개체의 경우 참조). 작업을 수행하면 외부 코드와 동일한 개체에 영향을 미치므로 외부 메서드에 미치는 영향을 확인할 수 있습니다.

개체를 새 개체로 바꾸면 개체가 동일하지 않습니다. 새 작업을 할 때 이전 작업을 변경하지 않고 외부 메서드에 영향을주지 않습니다.


업데이트 : 많은 의견을 말한 것은 어 떤 실수입니다. 지금 내 대답을 바로 잡았습니다. 나는 그 요지가 만들어 졌다고 믿지만, 지금은 훨씬 더 분명하다.

+5

사실, 값으로 호출되기 때문에 바깥 참조의 값을 변경할 수 없습니다. –

+0

흠 .. 좋아. 따라서 JRE는 단일 객체에 대한 두 개의 별도 참조를 작성하지만 참조 재 지정은 메모리의 원래 객체를 대체하지 않습니다. 참조 사본은 메시지 전달에만 적합합니다. – user178476

+0

"내부 매개 변수는 참조로 전달됩니다." - 아니....!! Java는 참조 전달을 지원하지 않습니다. 'inner'매개 변수는 BY VALUE로 전달되지만 자체적으로 참조입니다. VALUE로 전달 된 REFERENCE입니다. 그것은 참조에 의한 전달과 동일하지 않습니다. – Jesper

2

outer 참조는 여전히 기본 메소드에서 작성된 원래 ArrayList를 가리 킵니다. inner 참조는 change 메서드 내에서 생성 된 새로운 ArrayList를 가리키며이 ArrayList는 change 외부에서 결코 참조되지 않습니다.

이것은 예상되는 동작입니다. 호출 범위에서 액세스하려면 내부 ArrayList에 대한 참조를 반환해야합니다.

1

"실제 출력물"은 완벽한 의미입니다. change() 메서드 내에서 내부 값을 새 값으로 다시 지정 했으므로 outer는 더 이상 영향을받지 않습니다. 디버거를 사용하여 실행을 추적하면 어떤 일이 일어나는지 알 수 있습니다.

1

범위를 변경하지 않았으므로 방금 새 ArrayList를 inner 매개 변수로 할당했습니다. 이를 방지하려면 innerfinal 매개 변수로 설정하십시오.

+1

'최종'키워드는 바보 같은 짓을하지 않을 것이지만 "예상되는"결과를주는 'ref'키워드와 동일한 것이 없다. –

0

'inner'는 처음에는 '외부'참조와 동일한 객체에 대한 참조입니다 (Java에서 메소드의 객체 매개 변수에 대한 참조로 전달하므로 내부 및 외부 모두 동일한 객체를 가리 킵니다). 그런 다음 메소드 '내부'참조를 새로운 객체로 만듭니다.

Java의 중요한 점은 기본 메소드에서 '외부'가 참조하는 내용을 변경하지 않는다는 것입니다. 일단 메서드 호출이 끝나면 'inner'는 범위를 벗어나 결국에는 가비지 수집됩니다. '바깥 쪽'이 가리키는 대상은 여전히 ​​범위 내에 있습니다 (바깥 쪽은 여전히 ​​활성 상태 임).

1

필자는 이것이 퍼즐이 아니라는 것을 이해하고, Java는 값으로 전달 만 지원합니다. 항상 인수를 복사합니다. 더 here.

6

java에서는 모든 변수를 실제 객체에 대한 포인터로 생각할 수 있습니다.

은 무슨 일이 있습니다 :

  1. 당신이 목록을 만들 수 있습니다.
  2. 17을 추가하십시오.
  3. 목록에 대한 자체 포인터가있는 다른 함수로 목록을 보냅니다.
  4. 첫 번째 목록에 7을 더하십시오.
  5. 새 목록을 만들고 을 "내부"포인터로 가리 키십시오.
  6. 23을 새 목록에 추가하십시오.
  7. 새 목록을 인쇄하고 반환하십시오.
  8. 원래 함수 인 경우 은 이전과 동일한 객체 을 가리키는 포인터를 가지고 있습니다.
  9. 첫 번째 목록에 6을 추가합니다.
  10. 첫 번째 목록을 인쇄하십시오.
1

당신은 다음 해당 코드를 얻는 방법 인라인 :

import java.util.ArrayList; 

public class Puzzle { 
    public static void main(String[] args) { 
     ArrayList<Integer> outer = new ArrayList<Integer>(); 
     outer.add(17); 

     ArrayList<Integer> inner = outer; 
     inner.add(7); // inner refers to same array list as outer 

     inner = new ArrayList<Integer>(); // inner refers to new array list 
     inner.add(23); 
     System.out.println(inner); // new list is printed 

     outer.add(6); 
     System.out.println(outer); // outer list is printed 
} 

}