etc./StackOverFlow

Java가 "참조별 전달"입니까 아니면 "값별 전달"입니까?

청렴결백한 만능 재주꾼 2021. 9. 22. 13:27
반응형

질문자 :Community Wiki


나는 항상 Java가 pass-by-reference를 사용한다고 생각했습니다.

그러나 Java가 pass-by-value 를 사용한다고 주장 하는 블로그 게시물 을 보았습니다.

나는 그들이 만드는 구별을 이해하지 못한다고 생각합니다.

설명은 무엇입니까?



답변자 : Community Wiki


Java는 항상 값으로 전달됩니다 . 불행히도, 우리가 객체를 다룰 때 우리는 값에 의해 전달되는 참조라는 객체 핸들도 실제로 다루고 있습니다. 이 용어와 의미는 많은 초보자를 쉽게 혼동시킵니다.

다음과 같이 진행됩니다.

 public static void main(String[] args) { Dog aDog = new Dog("Max"); Dog oldDog = aDog; // we pass the object to foo foo(aDog); // aDog variable is still pointing to the "Max" dog when foo(...) returns aDog.getName().equals("Max"); // true aDog.getName().equals("Fifi"); // false aDog == oldDog; // true } public static void foo(Dog d) { d.getName().equals("Max"); // true // change d inside of foo() to point to a new Dog instance "Fifi" d = new Dog("Fifi"); d.getName().equals("Fifi"); // true }

위의 예에서 aDog.getName() 은 여전히 "Max" 반환합니다. 값 aDog 내의 main 기능으로 변경되지 fooDog "Fifi" 오브젝트 레퍼런스 값에 의해 전달한다. 참조로 전달된 경우 main aDog.getName()foo 호출 후 "Fifi" 반환합니다.

비슷하게:

 public static void main(String[] args) { Dog aDog = new Dog("Max"); Dog oldDog = aDog; foo(aDog); // when foo(...) returns, the name of the dog has been changed to "Fifi" aDog.getName().equals("Fifi"); // true // but it is still the same dog: aDog == oldDog; // true } public static void foo(Dog d) { d.getName().equals("Max"); // true // this changes the name of d to be "Fifi" d.setName("Fifi"); }

위의 예에서 Fifi foo(aDog) 호출 후 개의 이름입니다. 왜냐하면 객체의 이름이 foo(...) 내부에 설정되었기 때문입니다. food 대해 수행하는 모든 작업은 모든 실제적인 목적을 위해 aDog 대해 수행되지만 aDog 자체의 값을 변경하는 것은 불가능합니다.



답변자 : Community Wiki


방금 당신이 내 기사 를 참조했다는 것을 알았습니다.

Java Spec은 Java의 모든 것이 값으로 전달된다고 말합니다. Java에는 "참조에 의한 전달"과 같은 것이 없습니다.

이것을 이해하는 열쇠는 다음과 같은 것입니다.

 Dog myDog;

개가 아닙니다. 이것은 실제로 Dog에 대한 포인터입니다. Java에서 "참조"라는 용어를 사용하는 것은 매우 오해의 소지가 있으며 여기서 대부분의 혼란을 야기합니다. 그들이 "참조"라고 부르는 것은 우리가 대부분의 다른 언어에서 "포인터"라고 부르는 것과 더 유사하게 작용/느끼게 합니다.

그것이 의미하는 바는 당신이 가지고있을 때입니다

 Dog myDog = new Dog("Rover"); foo(myDog);

본질적으로 생성된 Dog 개체 의 주소 foo 메서드에 전달합니다.

(기본적으로 Java 포인터/참조가 직접 주소가 아니기 때문에 말하지만 그렇게 생각하는 것이 가장 쉽습니다.)

Dog 개체가 메모리 주소 42에 있다고 가정합니다. 이것은 메서드에 42를 전달한다는 의미입니다.

방법이 다음과 같이 정의된 경우

 public void foo(Dog someDog) { someDog.setName("Max"); // AAA someDog = new Dog("Fifi"); // BBB someDog.setName("Rowlf"); // CCC }

무슨 일이 일어나고 있는지 봅시다.

  • 매개변수 someDog 는 값 42로 설정됩니다.
  • 라인 "AAA"에서
    • someDog 받는 뒤에 Dog 는 (받는 포인트 Dog 어드레스 42 개체)
    • Dog (주소 42에 있는 개)에게 이름을 Max로 변경하라는 요청을 받았습니다.
  • 라인 "BBB"에서
    • 새로운 Dog 생성됩니다. 그가 주소 74에 있다고 가정해 봅시다.
    • 우리는 매개변수 someDog 를 74에 할당합니다.
  • 라인 "CCC"에서
    • someDog는에 따른다 Dog 는 (받는 포인트 Dog 어드레스 74 개체)
    • Dog (주소 74에 있는 개)는 자신의 이름을 Rowlf로 변경하라는 요청을 받습니다.
  • 그럼, 우리는 반환

이제 메서드 외부에서 어떤 일이 발생하는지 생각해 보겠습니다.

myDog 변경되었습니까?

열쇠가 있습니다.

myDog 는 실제 Dog 가 아니라 포인터 라는 사실을 염두에 두고 대답은 NO입니다. myDog 의 값은 여전히 42입니다. 여전히 원래 Dog 가리키고 있습니다(그러나 "AAA" 줄 때문에 이름은 이제 "Max"입니다. 여전히 동일한 Dog입니다. myDog 의 값은 변경되지 않았습니다.)

주소를 따르고 주소 끝에 있는 내용을 변경하는 것은 완벽하게 유효합니다. 그러나 변수는 변경되지 않습니다.

Java는 C와 정확히 동일하게 작동합니다. 포인터를 할당하고, 포인터를 메서드에 전달하고, 메서드의 포인터를 따르고, 가리킨 데이터를 변경할 수 있습니다. 그러나 호출자는 포인터가 가리키는 위치에 대한 변경 사항을 볼 수 없습니다. (참조에 의한 전달 의미 체계가 있는 언어에서 메서드 함수 포인터를 변경할 수 있으며 호출자는 해당 변경 사항을 볼 수 있습니다.)

참조에 의한 전달을 지원하는 C++, Ada, Pascal 및 기타 언어에서는 전달된 변수를 실제로 변경할 수 있습니다.

Java에 참조에 의한 전달 의미 체계가 있는 경우 위에서 정의한 foo 메서드는 라인 BBB에서 someDog 를 할당할 때 myDog

참조 매개변수를 전달된 변수의 별칭으로 생각하십시오. 해당 별칭이 할당되면 전달된 변수도 할당됩니다.



답변자 : Community Wiki


Java는 항상 참조가 아닌 값으로 인수를 전달합니다.


예를 들어 설명하겠습니다.

 public class Main { public static void main(String[] args) { Foo f = new Foo("f"); changeReference(f); // It won't change the reference! modifyReference(f); // It will modify the object that the reference variable "f" refers to! } public static void changeReference(Foo a) { Foo b = new Foo("b"); a = b; } public static void modifyReference(Foo c) { c.setAttribute("c"); } }

단계별로 설명하겠습니다.

  1. Foo 유형의 f 라는 참조를 선언하고 "f" 속성을 가진 Foo 유형의 새 객체를 할당합니다.

     Foo f = new Foo("f"); 

    여기에 이미지 설명 입력

  2. 메서드 측면에서 이름이 a Foo 형식의 참조가 선언되고 처음에는 null 할당됩니다.

     public static void changeReference(Foo a) 

    여기에 이미지 설명 입력

  3. changeReference 메서드를 호출하면 참조 a 에 인수로 전달되는 객체가 할당됩니다.

     changeReference(f); 

    여기에 이미지 설명 입력

  4. Foo 유형의 b 라는 참조를 선언하고 "b" 속성을 가진 Foo 유형의 새 객체를 할당합니다.

     Foo b = new Foo("b"); 

    여기에 이미지 설명 입력

  5. a = b 는 속성이 "b" a아닌 f 대해 새로 할당합니다.

    여기에 이미지 설명 입력

  6. modifyReference(Foo c) 메서드를 호출하면 참조 c 가 생성되고 속성이 "f" 객체가 할당됩니다.

    여기에 이미지 설명 입력

  7. c.setAttribute("c"); c 를 참조하는 객체의 속성을 변경하고 f 를 참조하는 객체와 동일한 객체를 가리킵니다.

    여기에 이미지 설명 입력

이제 Java에서 객체를 인수로 전달하는 방법을 이해하셨기를 바랍니다. :)



답변자 : Community Wiki


자바는 항상 적, 예외없이, 값에 의해 전달된다.

그렇다면 어떻게 누군가가 이것으로 혼란스러워 할 수 있고 Java가 참조로 전달된다고 믿거나 참조로 전달하는 Java의 예가 있다고 생각합니까? 요점은 Java가 어떤 상황에서도 개체 자체 의 값에 대한 직접 액세스를 제공 하지 않는다는 것입니다. 개체에 대한 유일한 액세스는 해당 개체에 대한 참조를 통해서입니다. Java 객체는 항상 참조를 통해 액세스하기 보다는 직접적으로 접근 하기 때문에 현학적으로 객체에 대한 참조 일 때 필드, 변수 및 메소드 인수객체 로 말하는 것이 일반적입니다. 혼란은 명명법의 (엄밀히 말하면 잘못된) 변경에서 비롯됩니다.

따라서 메서드를 호출할 때

  • 기본 인수( int , long 등)의 경우 값에 의한 전달 은 기본 요소의 실제 값 (예: 3)입니다.
  • 개체의 경우 값에 의한 전달 은 개체 에 대한 참조 값입니다.

따라서 doSomething(foo)public void doSomething(Foo foo) { .. } 두 개의 Foo가 동일한 객체를 가리키는 참조를 복사했습니다.

당연히 객체에 대한 참조 값을 전달하는 것은 객체를 참조로 전달하는 것과 매우 유사합니다(실제로는 구별할 수 없습니다).



답변자 : Community Wiki


이것은 Java가 참조로 전달하거나 값으로 전달하는 것에 대한 다음 토론에서 웃을 수 있을 정도로 Java가 실제로 어떻게 작동하는지에 대한 통찰력을 제공합니다 :-)

1단계, 특히 다른 프로그래밍 언어에서 온 경우 'p' "_ _ _ _ _ _"로 시작하는 단어를 머릿속에서 지우십시오. Java와 'p'는 같은 책, 포럼 또는 txt에 쓸 수 없습니다.

2단계에서는 Object를 메서드에 전달할 때 Object 자체가 아니라 Object 참조를 전달한다는 것을 기억하십시오.

  • 학생 : 선생님, Java가 참조에 의한 전달이라는 뜻인가요?
  • 마스터 : 메뚜기, No.

이제 객체의 참조/변수가 무엇을 하는지 생각해 보십시오.

  1. 변수는 메모리(힙)에서 참조된 개체에 도달하는 방법을 JVM에 알려주는 비트를 보유합니다.
  2. 메소드에 인수를 전달할 때 참조 변수를 전달하는 것이 아니라 참조 변수에 있는 비트의 복사본을 전달하는 것 입니다. 이와 같은 것: 3bad086a. 3bad086a는 전달된 개체에 도달하는 방법을 나타냅니다.
  3. 따라서 3bad086a를 참조 값이라고 전달합니다.
  4. 참조 자체가 아니라 참조 값을 전달하고 있습니다(객체 아님).
  5. 이 값은 실제로 COPIED이며 메서드에 제공 됩니다.

다음에서(이것을 컴파일/실행하지 마십시오...):

 1. Person person; 2. person = new Person("Tom"); 3. changeName(person); 4. 5. //I didn't use Person person below as an argument to be nice 6. static void changeName(Person anotherReferenceToTheSamePersonObject) { 7. anotherReferenceToTheSamePersonObject.setName("Jerry"); 8. }

무슨 일이야?

  • 변수 person 은 라인 #1에서 생성되며 처음에는 null입니다.
  • 새로운 Person 객체가 라인 #2에 생성되어 메모리에 저장되고, 변수 person 에 Person 객체에 대한 참조가 제공됩니다. 즉, 주소입니다. 3bad086a라고 합시다.
  • 개체의 주소를 보유하고 있는 변수 person 은 3행의 함수에 전달됩니다.
  • 라인 #4에서 당신은 침묵의 소리를들을 수 있습니다
  • 5행의 주석을 확인하십시오.
  • 메서드 지역 변수( otherReferenceToTheSamePersonObject) 가 생성되고 라인 #6에 마법이 나타납니다.
    • 변수/참조 사람 은 비트별로 복사되어 함수 내부의 anotherReferenceToTheSamePersonObject에 전달됩니다.
    • Person의 새 인스턴스는 생성되지 않습니다.
    • " person "과 " anotherReferenceToTheSamePersonObject "는 모두 3bad086a의 동일한 값을 보유합니다.
    • 이것을 시도하지 마십시오. 하지만 person==anotherReferenceToTheSamePersonObject가 true일 것입니다.
    • 두 변수 모두 참조의 IDENTICAL COPIES를 가지며 둘 다 동일한 Person 개체, 힙의 동일한 개체 및 NOT A COPY를 참조합니다.

그림은 천 마디 말의 가치가 있습니다.

값으로 전달

anotherReferenceToTheSamePersonObject 화살표는 개체를 향하고 변수 사람을 향하지 않는다는 점에 유의하십시오!

이해하지 못했다면 저를 믿고 Java가 값으로 전달 된다고 말하는 것이 더 낫다는 것을 기억하십시오. 음, 참조 값으로 전달하십시오 . 글쎄요, 변수 값을 복사하여 전달하는 것이 더 좋습니다! ;)

이제 저를 미워해도 되지만 메서드 인수에 대해 이야기할 때 기본 데이터 유형과 개체를 전달하는 것 사이에는 차이가 없습니다.

당신은 항상 참조 값의 비트 사본을 전달합니다!

  • 기본 데이터 유형인 경우 이러한 비트에는 기본 데이터 유형 자체의 값이 포함됩니다.
  • 객체인 경우 비트에는 객체에 도달하는 방법을 JVM에 알려주는 주소 값이 포함됩니다.

Java는 메소드 내에서 참조된 Object를 원하는 만큼 수정할 수 있지만 아무리 노력해도 참조를 계속할 전달된 변수를 수정할 수 없기 때문에 값에 의한 전달(p _ _ _ _ _ _) 무슨 일이 있어도 같은 물건!


위의 changeName 함수는 전달된 참조의 실제 내용(비트 값)을 수정할 수 없습니다. 즉, changeName은 Person person이 다른 Object를 참조하도록 할 수 없습니다.


물론 간단히 말해서 Java가 가치에 따라 전달 된다고 말할 수 있습니다!



답변자 : Community Wiki


Java는 값으로 참조를 전달합니다.

따라서 전달되는 참조를 변경할 수 없습니다.



답변자 : Community Wiki


"pass-by-reference 대 pass-by-value"에 대해 논쟁하는 것은 별로 도움이 되지 않는 것 같습니다.

두 경우 모두 "Java는 무엇이든(참조/값) 통과"라고 말하면 완전한 답변을 제공하지 못합니다. 다음은 메모리에서 일어나는 일을 이해하는 데 도움이 되는 몇 가지 추가 정보입니다.

Java 구현에 도달하기 전에 스택/힙에 대한 단기집중 과정: 값은 카페테리아의 접시 더미처럼 질서정연한 방식으로 스택을 왔다갔다 합니다. 힙의 메모리(동적 메모리라고도 함)는 무질서하고 무질서합니다. JVM은 가능한 모든 곳에서 공간을 찾고 그것을 사용하는 변수가 더 이상 필요하지 않을 때 공간을 확보합니다.

괜찮아. 먼저 로컬 프리미티브가 스택으로 이동합니다. 따라서 이 코드는 다음과 같습니다.

 int x = 3; float y = 101.1f; boolean amIAwesome = true;

결과:

스택의 기본 요소

객체를 선언하고 인스턴스화할 때. 실제 개체는 힙으로 이동합니다. 스택에 무엇이 들어가나요? 힙에 있는 개체의 주소입니다. C++ 프로그래머는 이것을 포인터라고 부르지만 일부 Java 개발자는 "포인터"라는 단어에 반대합니다. 무엇이든. 객체의 주소가 스택에 간다는 것만 알아두세요.

이렇게:

 int problems = 99; String name = "Jay-Z";

b*7ch는 하나가 아닙니다!

배열은 객체이므로 힙에도 적용됩니다. 배열의 객체는 어떻습니까? 그들은 자신의 힙 공간을 얻고 각 객체의 주소는 배열 내부로 들어갑니다.

 JButton[] marxBros = new JButton[3]; marxBros[0] = new JButton("Groucho"); marxBros[1] = new JButton("Zeppo"); marxBros[2] = new JButton("Harpo");

마르크스 형제

그렇다면 메소드를 호출할 때 전달되는 것은 무엇입니까? 객체를 전달하면 실제로 전달하는 것은 객체의 주소입니다. 어떤 사람들은 주소의 "값"이라고 말할 수도 있고 어떤 사람들은 그것이 단지 객체에 대한 참조라고 말할 수도 있습니다. 이것이 "기준"과 "가치" 지지자들 사이의 전쟁의 기원입니다. 당신이 그것을 부르는 것은 전달되는 것이 객체에 대한 주소라는 것을 이해하는 것만큼 중요하지 않습니다.

 private static void shout(String name){ System.out.println("There goes " + name + "!"); } public static void main(String[] args){ String hisName = "John J. Jingleheimerschmitz"; String myName = hisName; shout(myName); }

하나의 문자열이 생성되고 힙에 공간이 할당되고 문자열에 대한 주소가 스택에 저장되고 식별자 hisName 이 지정됩니다. 두 번째 문자열의 주소가 첫 번째 문자열과 같기 때문에 새 문자열이 생성되지 않습니다. 새 힙 공간이 할당되지 않지만 스택에 새 식별자가 생성됩니다. 그 다음 우리는 shout() 을 호출합니다: 새로운 스택 프레임이 생성되고 새로운 식별자, name 이 생성되고 이미 존재하는 String의 주소가 할당됩니다.

라다디다다다다다

그래서, 가치, 참조? 당신은 "감자"라고 말합니다.



답변자 : Community Wiki


대조를 보여주기 위해 다음 C++Java 스니펫을 비교하십시오.

C++: 참고: 잘못된 코드 - 메모리 누수! 그러나 그것은 요점을 보여줍니다.

 void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef) { val = 7; // Modifies the copy ref = 7; // Modifies the original variable obj.SetName("obj"); // Modifies the copy of Dog passed objRef.SetName("objRef"); // Modifies the original Dog passed objPtr->SetName("objPtr"); // Modifies the original Dog pointed to // by the copy of the pointer passed. objPtr = new Dog("newObjPtr"); // Modifies the copy of the pointer, // leaving the original object alone. objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to // by the original pointer passed. objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed } int main() { int a = 0; int b = 0; Dog d0 = Dog("d0"); Dog d1 = Dog("d1"); Dog *d2 = new Dog("d2"); Dog *d3 = new Dog("d3"); cppMethod(a, b, d0, d1, d2, d3); // a is still set to 0 // b is now set to 7 // d0 still have name "d0" // d1 now has name "objRef" // d2 now has name "objPtr" // d3 now has name "newObjPtrRef" }

자바에서는

 public static void javaMethod(int val, Dog objPtr) { val = 7; // Modifies the copy objPtr.SetName("objPtr") // Modifies the original Dog pointed to // by the copy of the pointer passed. objPtr = new Dog("newObjPtr"); // Modifies the copy of the pointer, // leaving the original object alone. } public static void main() { int a = 0; Dog d0 = new Dog("d0"); javaMethod(a, d0); // a is still set to 0 // d0 now has name "objPtr" }

Java에는 내장 유형의 경우 값에 의한 전달과 객체 유형에 대한 포인터 값의 두 가지 전달 유형만 있습니다.



답변자 : Community Wiki


Java는 값으로 개체에 대한 참조를 전달합니다.



답변자 : Community Wiki


기본적으로 Object 매개변수를 재할당해도 인수에 영향을 주지 않습니다. 예를 들어,

 private void foo(Object bar) { bar = null; } public static void main(String[] args) { String baz = "Hah!"; foo(baz); System.out.println(baz); }

"Hah!" 가 출력됩니다. 대신 null . 이것이 작동하는 이유는 bar "Hah!" 대한 참조 baz 값의 복사본이기 때문입니다. . 그것이 실제 참조 자체라면 foo baznull 재정의했을 것입니다.



답변자 : Community Wiki


아무도 Barbara Liskov에 대해 아직 언급하지 않았다는 것이 믿기지 않습니다. 그녀는 1974 년 CLU를 설계 할 때, 그녀는이 같은 용어 문제로 실행하고, 값이 어디 그녀는 값으로 호출 "이 특정 사건에 대한 공유 (또한 객체에 의해 객체 공유전화로 전화라고도 함)에 의해 용어 전화를 발명 참조".



답변자 : Community Wiki


문제의 핵심은 표현의 단어 참조 자바 단어 기준의 일반적인 의미에서 완전히 다른 의미 뭔가 "참조 통과"는 것이다.

일반적으로 Java에서 참조 는 개체에 대한 참조를 의미합니다. 그러나 프로그래밍 언어 이론의 참조/값으로 전달되는 기술 용어는 완전히 다른 변수를 보유하는 메모리 셀에 대한 참조에 대해 이야기하고 있습니다.



답변자 : Community Wiki


자바에서는 모든 것이 참조이므로 다음과 같은 경우: Point pnt1 = new Point(0,0); 자바는 다음을 수행합니다.

  1. 새 Point 개체를 만듭니다.
  2. 새로운 Point 참조를 생성하고 이전에 생성된 Point 객체의 point(참조)에 대한 참조를 초기화합니다.
  3. 여기에서 Point 개체 수명을 통해 pnt1 참조를 통해 해당 개체에 액세스합니다. 따라서 Java에서는 참조를 통해 객체를 조작한다고 말할 수 있습니다.

여기에 이미지 설명 입력

Java는 참조로 메서드 인수를 전달하지 않습니다. 값으로 전달합니다. 이 사이트의 예를 사용하겠습니다.

 public static void tricky(Point arg1, Point arg2) { arg1.x = 100; arg1.y = 100; Point temp = arg1; arg1 = arg2; arg2 = temp; } public static void main(String [] args) { Point pnt1 = new Point(0,0); Point pnt2 = new Point(0,0); System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y); System.out.println(" "); tricky(pnt1,pnt2); System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y); }

프로그램의 흐름:

 Point pnt1 = new Point(0,0); Point pnt2 = new Point(0,0);

두 개의 서로 다른 참조가 연결된 두 개의 서로 다른 Point 개체를 만듭니다.여기에 이미지 설명 입력

 System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y); System.out.println(" ");

예상대로 출력은 다음과 같습니다.

 X1: 0 Y1: 0 X2: 0 Y2: 0

이 라인에서 '값에 의한 전달'이 연극에 들어갑니다...

 tricky(pnt1,pnt2); public void tricky(Point arg1, Point arg2);

참조 pnt1pnt2값으로 까다로운 방법으로 전달됩니다. 즉, 이제 참조 pnt1pnt2 arg1arg2 라는 copies 이 있습니다. 따라서 pnt1arg1 은 동일한 개체를 가리킵니다. ( pnt2arg2 동일)여기에 이미지 설명 입력

tricky 방법:

 arg1.x = 100; arg1.y = 100; 

여기에 이미지 설명 입력

다음으로 tricky 방법

 Point temp = arg1; arg1 = arg2; arg2 = temp;

여기에서 먼저 arg1 참조와 같은 동일한 위치를 가리킬 temp 포인트 참조를 만듭니다. 그런 다음 참조 arg1arg2 참조와 같은 동일한 위치 를 가리킵니다. 마지막으로 arg2temp 와 같은 위치를 가리킬 것입니다.

여기에 이미지 설명 입력

여기에서 tricky 방법의 범위가 사라지고 참조에 더 이상 액세스할 수 없습니다: arg1 , arg2 , temp . 그러나 중요한 점은 이러한 참조가 '살아 있을 때' 이러한 참조로 수행하는 모든 작업은 참조 대상이 가리키는 대상에 영구적으로 영향을 미친다는 것입니다.

tricky 메서드를 실행한 후 main 돌아가면 다음과 같은 상황이 발생합니다.여기에 이미지 설명 입력

이제 프로그램의 완전한 실행은 다음과 같습니다.

 X1: 0 Y1: 0 X2: 0 Y2: 0 X1: 100 Y1: 100 X2: 0 Y2: 0


답변자 : Community Wiki


Java는 항상 참조로 전달되지 않고 값으로 전달됩니다.

먼저 값에 의한 전달과 참조에 의한 전달이 무엇인지 이해해야 합니다.

값으로 전달은 전달된 실제 매개변수 값의 메모리에 복사본을 만드는 것을 의미합니다. 이것은 실제 매개변수 내용의 복사본입니다 .

참조에 의한 전달(주소에 의한 전달이라고도 함)은 실제 매개변수의 주소 사본이 저장된다는 것을 의미합니다 .

때때로 Java는 참조에 의한 전달의 환상을 줄 수 있습니다. 아래 예를 사용하여 작동 방식을 살펴보겠습니다.

 public class PassByValue { public static void main(String[] args) { Test t = new Test(); t.name = "initialvalue"; new PassByValue().changeValue(t); System.out.println(t.name); } public void changeValue(Test f) { f.name = "changevalue"; } } class Test { String name; }

이 프로그램의 출력은 다음과 같습니다.

 changevalue

단계별로 이해합시다.

 Test t = new Test();

우리 모두 알고 있듯이 힙에 개체를 만들고 참조 값을 다시 t로 반환합니다. 예를 들어, t의 값이 0x100234 라고 가정합니다(실제 JVM 내부 값을 알지 못하며 이것은 단지 예일 뿐입니다).

첫 번째 삽화

 new PassByValue().changeValue(t);

참조 t를 함수에 전달할 때 개체 테스트의 실제 참조 값을 직접 전달하지 않지만 t의 복사본을 만든 다음 함수에 전달합니다. 으로 전달되기 때문에 실제 참조가 아닌 변수의 복사본을 전달합니다. t의 값이 0x100234 라고 말했기 때문에 t와 f는 모두 동일한 값을 가지므로 동일한 객체를 가리킬 것입니다.

두 번째 삽화

참조 f를 사용하여 함수에서 무엇이든 변경하면 객체의 기존 내용이 수정됩니다. 이것이 함수에서 업데이트되는 changevalue 얻은 이유입니다.

이것을 더 명확하게 이해하려면 다음 예를 고려하십시오.

 public class PassByValue { public static void main(String[] args) { Test t = new Test(); t.name = "initialvalue"; new PassByValue().changeRefence(t); System.out.println(t.name); } public void changeRefence(Test f) { f = null; } } class Test { String name; }

NullPointerException 합니까? 아니요, 참조 사본만 전달하기 때문입니다. 참조로 전달하는 경우 NullPointerException 이 발생했을 수 있습니다.

세 번째 삽화

이것이 도움이 되기를 바랍니다.



답변자 : Community Wiki


이것을 다루는 훌륭한 답변이 이미 있습니다. 나는 C++의 Pass-by-reference와 Java의 Pass-by-value 간의 동작을 대조 하는 매우 간단한 예제 (컴파일될)를 공유하여 작은 기여를 하고 싶었습니다.

몇 가지 사항:

  1. "참조"라는 용어는 두 가지 별도의 의미로 오버로드됩니다. Java에서는 단순히 포인터를 의미하지만 "참조에 의한 전달"의 맥락에서 전달된 원래 변수에 대한 핸들을 의미합니다.
  2. Java는 값으로 전달 됩니다. Java는 (다른 언어 중에서) C의 후손입니다. C 이전에는 FORTRAN 및 COBOL과 같은 여러 이전 언어(전부는 아님)가 PBR을 지원했지만 C는 지원하지 않았습니다. PBR은 이러한 다른 언어가 서브루틴 내에서 전달된 변수를 변경할 수 있도록 했습니다. 같은 일을 하기 위해(즉, 함수 내부의 변수 값 변경), C 프로그래머는 변수에 대한 포인터를 함수에 전달했습니다. Java와 같은 C에서 영감을 받은 언어는 이 아이디어를 차용하여 Java가 포인터 참조를 호출한다는 점을 제외하고 C와 마찬가지로 메서드에 포인터를 계속 전달합니다. 다시 말하지만, 이것은 "Pass-By-Reference"에서와 "Reference"라는 단어의 다른 사용입니다.
  3. C++에서는 "&" 문자(C와 C++에서 "변수 주소"를 나타내는 데 사용되는 동일한 문자임)를 사용하여 참조 매개변수를 선언함으로써 참조에 의한 전달을 허용합니다. 예를 들어 참조로 포인터를 전달하면 매개변수와 인수가 동일한 객체를 가리키는 것이 아닙니다. 오히려 같은 변수입니다. 하나가 다른 주소나 null로 설정되면 다른 것도 마찬가지입니다.
  4. 아래의 C++ 예제에서는 null로 끝나는 문자열 에 대한 포인터 를 참조로 전달하고 있습니다. 그리고 아래 Java 예제에서 값으로 String에 대한 Java 참조를 전달합니다(역시, String에 대한 포인터와 동일). 주석의 출력을 확인하십시오.

C++ 참조로 전달 예:

 using namespace std; #include <iostream> void change (char *&str){ // the '&' makes this a reference parameter str = NULL; } int main() { char *str = "not Null"; change(str); cout<<"str is " << str; // ==>str is <null> }

Java는 값 예제로 "Java 참조"를 전달합니다.

 public class ValueDemo{ public void change (String str){ str = null; } public static void main(String []args){ ValueDemo vd = new ValueDemo(); String str = "not null"; vd.change(str); System.out.println("str is " + str); // ==> str is not null!! // Note that if "str" was // passed-by-reference, it // WOULD BE NULL after the // call to change(). } }

편집하다

여러 사람들이 내 예제를 보고 있지 않거나 C++ 예제를 얻지 못한다는 의견을 작성했습니다. 연결이 끊어진 위치가 확실하지 않지만 C++ 예제를 추측하는 것은 명확하지 않습니다. 참조에 의한 전달이 파스칼에서 더 깨끗해 보인다고 생각하기 때문에 동일한 예를 파스칼에 게시하고 있지만 내가 틀릴 수 있습니다. 나는 사람들을 더 혼란스럽게 할 수 있습니다. 내가하지 희망.

파스칼에서 참조로 전달된 매개변수를 "var 매개변수"라고 합니다. 아래의 setToNil 절차에서 'ptr' 매개변수 앞에 오는 키워드 'var'에 유의하십시오. 포인터가 이 프로시저에 전달 되면 참조 로 전달됩니다. 동작에 주의하십시오. 이 프로시저가 ptr을 nil로 설정하면(파스칼은 NULL을 의미합니다) 인수를 nil로 설정합니다. Java에서는 그렇게 할 수 없습니다.

 program passByRefDemo; type iptr = ^integer; var ptr: iptr; procedure setToNil(var ptr : iptr); begin ptr := nil; end; begin new(ptr); ptr^ := 10; setToNil(ptr); if (ptr = nil) then writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. } end.

편집 2

Ken Arnold, James Gosling(Java를 발명한 사람) 및 David Holmes의 "THE Java Programming Language" 에서 일부 발췌, 2장, 2.6.5절

메소드에 대한 모든 매개변수는 "값으로" 전달됩니다 . 즉, 메서드의 매개변수 변수 값은 인수로 지정된 호출자의 복사본입니다.

그는 계속해서 사물에 관해 같은 점을 지적합니다. . .

매개변수가 객체 참조일 때 "값으로" 전달 되는 것은 객체 자체가 아니라 객체 참조라는 점에 유의해야 합니다.

그리고 같은 섹션의 끝에서 그는 Java가 값으로만 전달되고 참조로 전달되지 않는다는 더 광범위한 진술을 합니다.

Java 프로그래밍 언어 는 참조로 개체를 전달하지 않습니다. 그 값에 의해 객체 참조를 전달한다. 동일한 참조의 두 복사본이 동일한 실제 개체를 참조하기 때문에 한 참조 변수를 통해 수행된 변경 사항은 다른 참조 변수를 통해 볼 수 있습니다. 값으로 전달하는 모드를 전달하는 매개변수가 정확히 하나 있으며 이는 작업을 단순하게 유지하는 데 도움이 됩니다.

이 책의 이 섹션에는 Java의 매개변수 전달과 참조에 의한 전달과 값에 의한 전달의 구분에 대한 훌륭한 설명이 있으며 이는 Java 작성자가 작성한 것입니다. 특히 아직 확신이 서지 않는다면 누구에게나 이 책을 읽으라고 권하고 싶습니다.

두 모델의 차이는 매우 미묘하며 실제로 pass-by-reference를 사용한 프로그래밍을 하지 않는 한 두 모델이 다른 부분을 놓치기 쉽습니다.

이것이 논쟁을 해결하기를 희망하지만 아마도 그렇지 않을 것입니다.

편집 3

이 글에 조금 집착할 수도 있습니다. 아마 자바 제작자들이 무심코 잘못된 정보를 퍼뜨렸다고 생각하기 때문일 것입니다. 포인터에 "참조"라는 단어를 사용하는 대신 dingleberry와 같은 다른 것을 사용했다면 문제가 없었을 것입니다. "Java는 참조가 아닌 값으로 dingleberry를 전달합니다"라고 말할 수 있으며 아무도 혼동하지 않을 것입니다.

이것이 Java 개발자만 이 문제를 가지고 있는 이유입니다. 그들은 "참조"라는 단어를보고 그것이 무엇을 의미하는지 정확히 알고 있다고 생각하므로 반대 주장을 고려하는 것을 귀찮게하지 않습니다.

어쨌든, 나는 내가 정말 좋아하는 풍선 비유를 만든 이전 게시물에서 댓글을 보았습니다. 너무 많아서 요점을 설명하기 위해 만화 세트를 만들기 위해 일부 클립 아트를 함께 붙이기로 결정했습니다.

값으로 참조 전달 --참조에 대한 변경 사항은 호출자의 범위에 반영되지 않지만 개체에 대한 변경 사항은 반영됩니다. 이는 참조가 복사되지만 원본과 복사본이 모두 동일한 개체를 참조하기 때문입니다. 값으로 개체 참조 전달

참조로 전달 -- 참조 사본이 없습니다. 단일 참조는 호출자와 호출되는 함수 모두에서 공유됩니다. 참조 또는 개체의 데이터에 대한 모든 변경 사항은 호출자의 범위에 반영됩니다. 참조로 전달

편집 4

나는 이 주제에 대해 Java에서 매개변수 전달의 낮은 수준 구현을 설명하는 게시물을 본 적이 있습니다. 이는 추상적인 아이디어를 구체화하기 때문에 훌륭하고 매우 도움이 된다고 생각합니다. 그러나 나에게 문제는 동작의 기술적 구현보다 언어 사양에 설명된 동작에 대한 것입니다. 이것은 Java 언어 사양, 섹션 8.4.1 에서 발췌한 내용입니다.

메서드 또는 생성자가 호출되면(§15.12), 실제 인수 표현식의 값은 메서드 또는 생성자의 본문을 실행하기 전에 선언된 각 유형의 새로 생성된 매개변수 변수를 초기화합니다. DeclaratorId에 나타나는 Identifier는 형식 매개 변수를 참조하기 위해 메서드 또는 생성자의 본문에서 간단한 이름으로 사용할 수 있습니다.

즉, Java는 메서드를 실행하기 전에 전달된 매개변수의 복사본을 만듭니다. 대학에서 컴파일러를 공부 대부분의 사람들처럼, 내가 사용하는 "드래곤 도서" 컴파일러의 책이다. 1장의 "Call-by-value" 및 "Call-by-Reference"에 대한 좋은 설명이 있습니다. Call-by-value 설명은 Java 사양과 정확히 일치합니다.

90년대에 컴파일러를 공부할 때 Java보다 9~10년 앞선 1986년 책의 초판을 사용했습니다. 그러나 실제로 Java를 언급하는 2007년 2nd Edition 사본을 우연히 발견했습니다! "매개변수 전달 메커니즘"이라는 레이블이 붙은 섹션 1.6.6은 매개변수 전달을 꽤 잘 설명합니다. 다음은 Java를 언급하는 "값별 호출"이라는 제목 아래의 발췌문입니다.

값에 의한 호출에서 실제 매개변수는 평가되거나(표현식인 경우) 복사됩니다(변수인 경우). 값은 호출된 프로시저의 해당 형식 매개변수에 속하는 위치에 배치됩니다. 이 방법은 C 및 Java에서 사용되며 C++ 및 대부분의 다른 언어에서 일반적인 옵션입니다.



답변자 : Community Wiki


Java는 값에 의한 전달(스택 메모리)

작동 방식

  • Java가 원시 데이터 유형과 객체 데이터 유형을 저장하는 위치를 먼저 이해합시다.

  • 원시 데이터 유형 자체와 개체 참조는 스택에 저장됩니다. 객체 자체는 힙에 저장됩니다.

  • 즉, 스택 메모리는 원시 데이터 유형과 객체의 주소도 저장합니다.

  • 그리고 항상 참조 값의 비트 복사본을 전달합니다.

  • 원시 데이터 유형인 경우 이러한 복사된 비트에는 원시 데이터 유형 자체의 값이 포함됩니다. 그렇기 때문에 메서드 내부에서 인수 값을 변경할 때 외부 변경 사항을 반영하지 않습니다.

  • Foo foo=new Foo() 와 같은 객체 데이터 유형인 경우 이 경우 객체 주소의 복사본이 file 바로 가기와 같이 전달됩니다. C:\desktop에 abc.txt 텍스트 파일이 있다고 가정하고 동일한 파일을 만들고 이것을 C:\desktop\abc-shortcut 안에 넣어 C:\desktop\abc.txt 에서 파일에 접근하고 'Stack Overflow'를 작성하고 파일을 닫고 바로 가기에서 파일을 다시 열면 write '는 프로그래머가 배울 수 있는 가장 큰 온라인 커뮤니티'인 경우 총 파일 변경은 'Stack Overflow는 프로그래머가 배울 수 있는 가장 큰 온라인 커뮤니티입니다' 가 됩니다. 동일한 파일, 여기서 Foo 를 파일로 가정하고 foo가 123hd7h (C:\desktop\abc.txt 와 같은 원래 주소) 주소와 234jdid (C:\desktop\abc-shortcut 과 같은 복사된 주소에 실제로 포함되어 있다고 가정합니다. 내부 파일의 원래 주소) .. 이해를 돕기 위해 바로가기 파일을 만들어 느낌.



답변자 : Community Wiki


참조는 사용하는 언어에 관계없이 표시될 때 항상 값입니다.

상자 보기에서 벗어나 어셈블리 또는 일부 낮은 수준의 메모리 관리를 살펴보겠습니다. CPU 수준에서 참조 는 메모리나 CPU 레지스터 중 하나에 기록되면 즉시 값이 됩니다. (그래서 포인터 는 좋은 정의입니다. 동시에 목적이 있는 값입니다.)

메모리의 데이터에는 위치가 있고 해당 위치에는 값(바이트, 워드 등)이 있습니다. 어셈블리에는 특정 위치 (변수라고도 함)에 이름 을 부여하는 편리한 솔루션이 있지만, 코드를 컴파일할 때 브라우저가 도메인 이름을 IP 주소로 바꾸는 것처럼 어셈블러는 단순히 이름을 지정된 위치로 바꿉니다.

핵심까지 표현하지 않고 어떤 언어로든 참조를 전달하는 것은 기술적으로 불가능합니다(즉시 값이 되는 경우).

변수 Foo가 있고 위치 가 메모리의 47번째 바이트에 있고 이 5라고 가정해 보겠습니다 . 메모리의 223번째 바이트에 있는 또 다른 변수 Ref2Foo가 있고 그 값은 47이 됩니다. 이 Ref2Foo는 기술 변수일 수 있습니다. , 프로그램에 의해 명시적으로 생성되지 않았습니다. 다른 정보 없이 5와 47만 보면 Values 2개만 보입니다. 그것들을 참조로 사용한다면 5 에 도달하기 위해 우리는 여행해야 합니다:

 (Name)[Location] -> [Value at the Location] --------------------- (Ref2Foo)[223] -> 47 (Foo)[47] -> 5

이것이 점프 테이블이 작동하는 방식입니다.

Foo 값으로 메서드/함수/프로시저를 호출하려는 경우 언어 와 여러 메서드 호출 모드에 따라 메서드에 변수를 전달할 수 있는 몇 가지 방법이 있습니다.

  1. 5는 CPU 레지스터 중 하나(예: EAX)에 복사됩니다.
  2. 5는 스택에 PUSHd를 가져옵니다.
  3. 47은 CPU 레지스터 중 하나에 복사됩니다.
  4. 47 스택에 PUSHd.
  5. 223은 CPU 레지스터 중 하나에 복사됩니다.
  6. 223은 PUSHd를 스택으로 가져옵니다.

값 위의 모든 경우( 기존 값의 복사본) 가 생성되었으며 이제 이를 처리하는 것은 수신 방법에 달려 있습니다. 메소드 내부에 "Foo"를 작성하면 EAX에서 읽거나 자동으로 역참조 되거나 이중 역참조됩니다. 프로세스는 언어 작동 방식 및/또는 Foo 유형이 지시하는 내용에 따라 다릅니다. 이것은 개발자가 역참조 프로세스를 우회할 때까지 숨겨져 있습니다. 기준은 (언어 레벨에서) 처리 할 수있는 값이므로 기준을 나타내는 값이다.

이제 Foo를 메서드에 전달했습니다.

  • 경우 1. 및 2. Foo ( Foo = 9 )를 변경하면 Value의 복사본이 있으므로 로컬 범위에만 영향을 줍니다. 메소드 내부에서 우리는 메모리에서 원래 Foo가 어디에 위치했는지조차 결정할 수 없습니다.
  • 3과 4의 경우 기본 언어 구성을 사용하고 Foo( Foo = 11 )를 변경하면 Foo가 전역적으로 변경될 수 있습니다(예: Java 또는 Pascal의 procedure findMin(x, y, z: integer; var m : integer); ). 그러나 언어에서 역참조 프로세스를 우회할 수 있는 경우 47 을 변경할 수 있습니다(예: 49 . 그 시점에서 Foo에 대한 로컬 포인터 를 변경했기 때문에 읽은 경우 Foo가 변경된 것으로 보입니다. Foo = 12 ) 내에서 이 Foo를 수정하는 경우 예상과 다른 메모리에 쓸 것이기 때문에 프로그램 실행(일명 segfault)을 FUBAR할 것입니다. 대상 영역을 수정할 수도 있습니다. 실행 가능한 프로그램을 보유하고 그것에 쓰는 것은 실행 중인 코드를 수정합니다(Foo는 이제 47 아닙니다). 그러나 Foo의 값 47 은 전역적으로 변경되지 않고 메서드 내부의 값만 변경되었습니다. 47 도 메서드에 대한 복사본이기 때문입니다.
  • 5. 및 6.의 경우 223 을 수정하면 3. 또는 4.(이제 잘못된 값을 가리키는 포인터, 다시 포인터로 사용됨)에서와 동일한 혼란을 야기하지만 이것은 여전히 223이 복사 되었기 때문에 로컬 문제입니다. Ref2Foo (즉 223 )를 역참조할 수 있고 가리키는 값 47 49 수정할 수 있다면 이는 Foo에 전역적으로 영향을 미칠 것입니다. 왜냐하면 이 경우 메서드는 223 의 복사본을 얻었지만 참조된 47 이 존재하기 때문입니다 단 한 번만 49 변경하면 모든 Ref2Foo 에서 잘못된 값으로 이중 참조가 발생합니다.

중요하지 않은 세부 사항, 심지어 참조에 의한 전달을 수행하는 언어도 함수에 값을 전달하지만 해당 함수는 역참조 목적으로 사용해야 한다는 것을 알고 있습니다. 이 값으로 참조를 전달하는 것은 실제로 쓸모가 없고 용어가 참조로 전달 하기 때문에 프로그래머에게 숨겨져 있습니다.

엄격한 값으로 전달하는 것도 쓸모가 없습니다. 즉, 배열을 인수로 사용하여 메서드를 호출할 때마다 100Mbyte 배열을 복사해야 하므로 Java는 엄격하게 값으로 전달될 수 없습니다. 모든 언어는 이 거대한 배열에 대한 참조(값으로)를 전달하고 해당 배열이 메서드 내에서 로컬로 변경될 수 있는 경우 쓰기 시 복사 메커니즘을 사용하거나 메서드(Java가 하는 것처럼)가 전역적으로 배열을 수정할 수 있도록 허용합니다. 호출자 보기) 및 일부 언어에서는 참조 자체의 값을 수정할 수 있습니다.

실제 값 또는 참조의 표현 인 중 하나를 : 그래서 짧은 자바 자신의 용어로, 자바는 패스에 의해 값 값이 될 수있는 곳입니다.



답변자 : Community Wiki


내가 아는 한 Java는 값에 의한 호출만 알고 있습니다. 이것은 원시 데이터 유형의 경우 복사본으로 작업하고 개체의 경우 개체에 대한 참조 복사본으로 작업한다는 것을 의미합니다. 그러나 몇 가지 함정이 있다고 생각합니다. 예를 들어 다음은 작동하지 않습니다.

 public static void swap(StringBuffer s1, StringBuffer s2) { StringBuffer temp = s1; s1 = s2; s2 = temp; } public static void main(String[] args) { StringBuffer s1 = new StringBuffer("Hello"); StringBuffer s2 = new StringBuffer("World"); swap(s1, s2); System.out.println(s1); System.out.println(s2); }

스왑 기능에서 기본 참조에 영향을 미치지 않는 복사본을 사용하기 때문에 이것은 World Hello가 아닌 Hello World를 채울 것입니다. 그러나 개체가 변경되지 않는 경우 예를 들어 다음과 같이 변경할 수 있습니다.

 public static void appendWorld(StringBuffer s1) { s1.append(" World"); } public static void main(String[] args) { StringBuffer s = new StringBuffer("Hello"); appendWorld(s); System.out.println(s); }

명령줄에 Hello World가 채워집니다. StringBuffer를 String으로 변경하면 String은 변경할 수 없기 때문에 Hello만 생성됩니다. 예를 들어:

 public static void appendWorld(String s){ s = s+" World"; } public static void main(String[] args) { String s = new String("Hello"); appendWorld(s); System.out.println(s); }

그러나 다음과 같이 String에 대한 래퍼를 만들어 String과 함께 사용할 수 있습니다.

 class StringWrapper { public String value; public StringWrapper(String value) { this.value = value; } } public static void appendWorld(StringWrapper s){ s.value = s.value +" World"; } public static void main(String[] args) { StringWrapper s = new StringWrapper("Hello"); appendWorld(s); System.out.println(s.value); }

편집: 나는 이것이 String과 같은 불변 객체로는 할 수 없는 원래 객체를 수정할 수 있기 때문에 두 개의 String을 "추가"할 때 StringBuffer를 사용하는 이유라고 생각합니다.



답변자 : Community Wiki


아니요, 참조로 전달되지 않습니다.

Java는 Java 언어 사양에 따라 값으로 전달됩니다.

메서드 또는 생성자가 호출되면(§15.12) 실제 인수 식의 값은 메서드 또는 생성자의 본문을 실행하기 전에 선언된 각 유형의 새로 생성된 매개 변수 변수를 초기화합니다. DeclaratorId에 나타나는 Identifier는 형식 매개 변수 를 참조하기 위해 메서드 또는 생성자의 본문에서 간단한 이름으로 사용할 수 있습니다.



답변자 : Community Wiki


네 가지 예의 도움으로 내 이해를 설명하려고 노력하겠습니다. Java는 참조에 의한 전달이 아니라 값에 의한 전달입니다.

/**

값으로 전달

Java에서 모든 매개변수는 값으로 전달됩니다. 즉, 메서드 인수를 할당하는 것은 호출자에게 표시되지 않습니다.

*/

예 1:

 public class PassByValueString { public static void main(String[] args) { new PassByValueString().caller(); } public void caller() { String value = "Nikhil"; boolean valueflag = false; String output = method(value, valueflag); /* * 'output' is insignificant in this example. we are more interested in * 'value' and 'valueflag' */ System.out.println("output : " + output); System.out.println("value : " + value); System.out.println("valueflag : " + valueflag); } public String method(String value, boolean valueflag) { value = "Anand"; valueflag = true; return "output"; } }

결과

 output : output value : Nikhil valueflag : false

예 2:

/** * * 값으로 전달 * */

 public class PassByValueNewString { public static void main(String[] args) { new PassByValueNewString().caller(); } public void caller() { String value = new String("Nikhil"); boolean valueflag = false; String output = method(value, valueflag); /* * 'output' is insignificant in this example. we are more interested in * 'value' and 'valueflag' */ System.out.println("output : " + output); System.out.println("value : " + value); System.out.println("valueflag : " + valueflag); } public String method(String value, boolean valueflag) { value = "Anand"; valueflag = true; return "output"; } }

결과

 output : output value : Nikhil valueflag : false

예 3:

/** 이 'Pass By Value'는 'Pass By Reference'라는 느낌을 가지고 있습니다.

어떤 사람들은 원시 유형과 '문자열'이 '값에 의한 전달'이고 객체가 '참조에 의한 전달'이라고 말합니다.

그러나 이 예에서 우리는 참조를 값으로 전달한다는 점을 염두에 두고 실제로 값으로만 전달한다는 것을 이해할 수 있습니다. 즉: 참조는 값으로 전달됩니다. 이것이 변경될 수 있는 이유이며 여전히 로컬 범위 이후에도 마찬가지입니다. 그러나 원래 범위를 벗어나 실제 참조를 변경할 수는 없습니다. 이것이 의미하는 바는 PassByValueObjectCase2의 다음 예에서 보여줍니다.

*/

 public class PassByValueObjectCase1 { private class Student { int id; String name; public Student() { } public Student(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } } public static void main(String[] args) { new PassByValueObjectCase1().caller(); } public void caller() { Student student = new Student(10, "Nikhil"); String output = method(student); /* * 'output' is insignificant in this example. we are more interested in * 'student' */ System.out.println("output : " + output); System.out.println("student : " + student); } public String method(Student student) { student.setName("Anand"); return "output"; } }

결과

 output : output student : Student [id=10, name=Anand]

예 4:

/**

Example3(PassByValueObjectCase1.java)에서 언급한 것 외에도 원래 범위 외부에서 실제 참조를 변경할 수 없습니다."

private class Student 대한 코드를 붙여넣지 않습니다. Student 에 대한 클래스 정의는 Example3과 동일합니다.

*/

 public class PassByValueObjectCase2 { public static void main(String[] args) { new PassByValueObjectCase2().caller(); } public void caller() { // student has the actual reference to a Student object created // can we change this actual reference outside the local scope? Let's see Student student = new Student(10, "Nikhil"); String output = method(student); /* * 'output' is insignificant in this example. we are more interested in * 'student' */ System.out.println("output : " + output); System.out.println("student : " + student); // Will it print Nikhil or Anand? } public String method(Student student) { student = new Student(20, "Anand"); return "output"; } }

결과

 output : output student : Student [id=10, name=Nikhil]


답변자 : Community Wiki


Java에서는 참조로 전달할 수 없으며 메서드 호출에서 둘 이상의 값을 반환하려는 경우가 분명한 방법 중 하나입니다. C++에서 다음 코드 비트를 고려하십시오.

 void getValues(int& arg1, int& arg2) { arg1 = 1; arg2 = 2; } void caller() { int x; int y; getValues(x, y); cout << "Result: " << x << " " << y << endl; }

때로는 Java에서 동일한 패턴을 사용하고 싶지만 그렇게 할 수 없습니다. 적어도 직접적으로는 아닙니다. 대신 다음과 같이 할 수 있습니다.

 void getValues(int[] arg1, int[] arg2) { arg1[0] = 1; arg2[0] = 2; } void caller() { int[] x = new int[1]; int[] y = new int[1]; getValues(x, y); System.out.println("Result: " + x[0] + " " + y[0]); }

이전 답변에서 설명했듯이 Java에서는 배열에 대한 포인터를 값으로 getValues 합니다. 메서드가 배열 요소를 수정하고 규칙에 따라 요소 0에 반환 값이 포함될 것으로 예상하기 때문에 그것으로 충분합니다. 분명히 필요하지 않도록 코드를 구조화하거나 반환 값을 포함하거나 설정할 수 있는 클래스를 구성하는 것과 같은 다른 방법으로 이 작업을 수행할 수 있습니다. 그러나 위의 C++에서 사용할 수 있는 간단한 패턴은 Java에서 사용할 수 없습니다.



답변자 : Community Wiki


사양에서 자세한 내용을 추가하기 위해 이 답변에 기여할 것이라고 생각했습니다.

첫째, 참조로 전달하는 것과 값으로 전달하는 것의 차이점은 무엇입니까?

참조로 전달한다는 것은 호출된 함수의 매개변수가 호출자가 전달한 인수(값이 아니라 ID - 변수 자체)와 동일하다는 것을 의미합니다.

값으로 전달은 호출된 함수의 매개변수가 호출자가 전달한 인수의 복사본이 됨을 의미합니다.

또는 wikipedia에서 참조에 의한 전달 주제에 대해

참조에 의한 호출 평가(참조에 의한 전달이라고도 함)에서 함수는 값의 복사본이 아니라 인수로 사용되는 변수에 대한 암시적 참조를 받습니다. 이것은 일반적으로 함수가 인수로 사용되는 변수를 수정(즉, 할당)할 수 있음을 의미합니다. 이는 호출자가 볼 수 있는 것입니다.

그리고 값에 의한 전달이라는 주제에 대해

값에 의한 호출에서 인수 표현식이 평가되고 결과 값은 [...] 함수의 해당 변수에 바인딩됩니다. 함수 또는 프로시저가 매개변수에 값을 할당할 수 있는 경우 해당 로컬 복사본만 할당됩니다[...].

둘째, Java가 메소드 호출에서 무엇을 사용하는지 알아야 합니다. Java 언어 사양 상태

메서드 또는 생성자가 호출되면(§15.12) 실제 인수 식의 값은 메서드 또는 생성자의 본문을 실행하기 전에 선언된 각 유형의 새로 생성된 매개 변수 변수를 초기화합니다.

따라서 인수의 값을 해당 매개변수 변수에 할당(또는 바인딩)합니다.

주장의 가치는 무엇입니까?

참조 유형, Java Virtual Machine 사양 상태를 살펴보겠습니다.

참조 유형 에는 클래스 유형, 배열 유형 및 인터페이스 유형의 세 가지 유형이 있습니다. 해당 값은 동적으로 생성된 클래스 인스턴스, 배열 또는 인터페이스를 구현하는 클래스 인스턴스 또는 배열에 대한 참조입니다.

Java 언어 사양 은 또한 다음과 같이 명시합니다.

참조 값(종종 단순히 참조)은 이러한 개체에 대한 포인터 이며 개체를 참조하지 않는 특수한 null 참조입니다.

(일부 참조 유형의) 인수 값은 개체에 대한 포인터입니다. 변수, 참조 유형 반환 유형이 있는 메소드 호출 및 인스턴스 생성 표현식( new ... )은 모두 참조 유형 값으로 해석됩니다.

그래서

 public void method (String param) {} ... String var = new String("ref"); method(var); method(var.toString()); method(new String("ref"));

String 인스턴스에 대한 참조 값을 메서드의 새로 생성된 매개변수 param 바인딩합니다. 이것이 바로 pass-by-value의 정의가 설명하는 것입니다. 따라서 Java는 값으로 전달됩니다 .

참조를 따라 메서드를 호출하거나 참조된 개체의 필드에 액세스할 수 있다는 사실은 대화와 완전히 관련이 없습니다. 참조에 의한 전달의 정의는 다음과 같습니다.

이것은 일반적으로 함수가 인수로 사용되는 변수를 수정(즉, 할당)할 수 있음을 의미합니다. 이는 호출자가 볼 수 있는 것입니다.

Java에서 변수를 수정한다는 것은 다시 할당하는 것을 의미합니다. Java에서 메소드 내에서 변수를 재할당하면 호출자가 알아차리지 못할 것입니다. 변수가 참조하는 객체를 수정하는 것은 완전히 다른 개념입니다.


기본 값은 여기 에 있는 Java Virtual Machine 사양에도 정의되어 있습니다. 유형의 값은 적절하게 인코딩된 해당 정수 또는 부동 소수점 값입니다(8, 16, 32, 64 등의 비트).



답변자 : Community Wiki


Java에서는 참조만 전달되고 값으로 전달됩니다.

Java 인수는 모두 값으로 전달됩니다 (참조는 메서드에서 사용할 때 복사됨).

기본 유형의 경우 Java 동작은 간단합니다. 값이 기본 유형의 다른 인스턴스에 복사됩니다.

Object의 경우에도 마찬가지입니다. Object 변수는 "new" 키워드를 사용하여 생성된 Object의 주소만을 담고 있는 포인터(버킷)이며, 원시 유형처럼 복사됩니다.

동작은 기본 유형과 다르게 나타날 수 있습니다. 복사된 객체 변수에는 동일한 주소(동일한 객체에 대한)가 포함되어 있기 때문입니다. 개체의 콘텐츠/구성원 은 여전히 메서드 내에서 수정되고 나중에 외부에 액세스하여 (포함하는) 개체 자체가 참조로 전달된 것처럼 보일 수 있습니다.

"문자열" 개체는 "참조에 의해 개체가 전달됨"이라는 도시 전설에 대한 좋은 반례로 보입니다.

실제로 메서드를 사용하면 인수로 전달된 String 값을 업데이트할 수 없습니다.

문자열 객체는 수정할 수 없는 final로 선언된 배열로 문자를 보유합니다. Object의 주소만 "new"를 사용하여 다른 주소로 대체될 수 있습니다. "new"를 사용하여 변수를 업데이트하면 변수가 처음에 값으로 전달되고 복사되었기 때문에 외부에서 개체에 액세스할 수 없습니다.



답변자 : Community Wiki


차이점 또는 아마도 내가 원래 포스터와 같은 인상을 받았을 때 기억하는 방식은 다음과 같습니다. Java는 항상 가치에 의해 전달됩니다. Java의 모든 객체(Java의 경우 프리미티브를 제외한 모든 것)는 참조입니다. 이러한 참조는 값으로 전달됩니다.



답변자 : Community Wiki


이전에 많은 사람들이 언급했듯이 Java는 항상 값에 의해 전달됩니다.

다음은 차이점을 이해하는 데 도움이 되는 또 다른 예입니다( 클래식 스왑 예 ).

 public class Test { public static void main(String[] args) { Integer a = new Integer(2); Integer b = new Integer(3); System.out.println("Before: a = " + a + ", b = " + b); swap(a,b); System.out.println("After: a = " + a + ", b = " + b); } public static swap(Integer iA, Integer iB) { Integer tmp = iA; iA = iB; iB = tmp; } }

인쇄물:

이전: a = 2, b = 3
이후: a = 2, b = 3

이것은 iA 및 iB가 전달된 참조의 동일한 값을 갖는 새로운 로컬 참조 변수이기 때문에 발생합니다(각각 및 b를 가리킴). 따라서 iA 또는 iB의 참조를 변경하려는 시도는 이 메서드 외부가 아닌 로컬 범위에서만 변경됩니다.



답변자 : Community Wiki


나는 항상 그것을 "복사본 통과"라고 생각합니다. 그것은 원시 또는 참조 값의 복사본입니다. 프리미티브인 경우 값인 비트의 복사본이고 객체인 경우 참조의 복사본입니다.

 public class PassByCopy{ public static void changeName(Dog d){ d.name = "Fido"; } public static void main(String[] args){ Dog d = new Dog("Maxx"); System.out.println("name= "+ d.name); changeName(d); System.out.println("name= "+ d.name); } } class Dog{ public String name; public Dog(String s){ this.name = s; } }

자바 PassByCopy의 출력:

이름= 맥스
이름=피도

기본 래퍼 클래스와 문자열은 변경할 수 없으므로 이러한 유형을 사용하는 예제는 다른 유형/객체와 동일하게 작동하지 않습니다.



답변자 : Community Wiki


일부 다른 언어와 달리 Java에서는 값에 의한 전달과 참조에 의한 전달 중에서 선택할 수 없습니다. 모든 인수는 값으로 전달됩니다. 메서드 호출은 기본 값의 복사본(예: int 및 double 값)과 개체에 대한 참조 복사본의 두 가지 유형의 값을 메서드에 전달할 수 있습니다.

메소드가 기본 유형 매개변수를 수정할 때 매개변수에 대한 변경사항은 호출 메소드의 원래 인수 값에 영향을 미치지 않습니다.

객체의 경우 객체 자체는 메서드에 전달할 수 없습니다. 그래서 우리는 객체의 참조(주소)를 전달합니다. 이 참조를 사용하여 원본 개체를 조작할 수 있습니다.

Java가 객체를 생성하고 저장하는 방법: 객체 를 생성할 때 객체의 주소를 참조 변수에 저장합니다. 다음 문장을 분석해 보자.

 Account account1 = new Account();

"Account account1"은 참조 변수의 유형 및 이름이고, "="는 할당 연산자이고, "new"는 시스템에서 필요한 공간을 요청합니다. 객체를 생성하는 키워드 new의 오른쪽에 있는 생성자는 키워드 new에 의해 암시적으로 호출됩니다. 할당 연산자를 사용하여 생성된 객체의 주소(오른쪽 값의 결과, "클래스 인스턴스 생성 표현식"이라는 표현)를 왼쪽 값(이름 및 유형이 지정된 참조 변수)에 할당합니다.

개체의 참조가 값으로 전달되더라도 메서드는 개체의 참조 복사본을 사용하여 공용 메서드를 호출하여 참조된 개체와 계속 상호 작용할 수 있습니다. 매개변수에 저장된 참조는 인수로 전달된 참조의 복사본이므로 호출된 메서드의 매개변수와 호출하는 메서드의 인수는 메모리에서 동일한 개체를 참조합니다.

배열 객체 자체 대신 배열에 대한 참조를 전달하는 것은 성능상의 이유로 합리적입니다. Java의 모든 것은 값으로 전달되기 때문에 배열 객체가 전달되면 각 요소의 복사본이 전달됩니다. 대형 어레이의 경우 이는 시간을 낭비하고 요소 사본을 위한 상당한 스토리지를 소모합니다.

아래 이미지에서 두 개의 참조 변수(C/C++에서는 포인터라고 하며 이 용어를 사용하면 이 기능을 더 쉽게 이해할 수 있다고 생각합니다)가 기본 메서드에 있는 것을 볼 수 있습니다. 기본 및 참조 변수는 스택 메모리에 보관됩니다(아래 이미지의 왼쪽). array1 및 array2 참조 변수 "point"(C/C++ 프로그래머가 부름) 또는 힙 메모리(아래 이미지의 오른쪽)에서 개체(이 참조 변수가 보유하는 값은 개체의 주소임)인 각각 및 b 배열에 대한 참조 .

값으로 전달 예 1

array1 참조 변수의 값을 reverseArray 메서드에 인수로 전달하면 메서드에 참조 변수가 생성되고 해당 참조 변수는 동일한 배열(a)을 가리키기 시작합니다.

 public class Test { public static void reverseArray(int[] array1) { // ... } public static void main(String[] args) { int[] array1 = { 1, 10, -7 }; int[] array2 = { 5, -190, 0 }; reverseArray(array1); } }

값으로 전달 예 2

그래서 우리가 말하면

 array1[0] = 5;

reverseArray 메서드에서는 배열 a를 변경합니다.

reverseArray 메소드(array2)에는 배열 c를 가리키는 또 다른 참조 변수가 있습니다. 우리가 말한다면

 array1 = array2;

reverseArray 메서드에서 reverseArray 메서드의 참조 변수 array1은 배열 가리키는 것을 중지하고 배열 c(두 번째 이미지의 점선)를 가리키기 시작합니다.

참조 변수 array2의 값을 reverseArray 메서드의 반환 값으로 반환하고 이 값을 main 메서드의 참조 변수 array1에 할당하면 main의 array1이 배열 c를 가리키기 시작합니다.

자, 이제 한 번에 한 모든 것을 작성해 봅시다.

 public class Test { public static int[] reverseArray(int[] array1) { int[] array2 = { -7, 0, -1 }; array1[0] = 5; // array a becomes 5, 10, -7 array1 = array2; /* array1 of reverseArray starts pointing to c instead of a (not shown in image below) */ return array2; } public static void main(String[] args) { int[] array1 = { 1, 10, -7 }; int[] array2 = { 5, -190, 0 }; array1 = reverseArray(array1); /* array1 of main starts pointing to c instead of a */ } }

여기에 이미지 설명 입력

그리고 reverseArray 메소드가 끝났으니 참조변수(array1, array2)가 없어졌습니다. 이는 이제 각각 c 및 b 배열을 가리키는 주 메서드 array1 및 array2에 두 개의 참조 변수만 있음을 의미합니다. 객체(배열)를 가리키는 참조 변수가 없습니다. 따라서 가비지 컬렉션에 적합합니다.

main의 array2 값을 array1에 할당할 수도 있습니다. array1은 b를 가리키기 시작할 것입니다.



답변자 : Community Wiki


Java에는 값에 의한 전달만 있습니다. 이를 검증하는 매우 간단한 예입니다.

 public void test() { MyClass obj = null; init(obj); //After calling init method, obj still points to null //this is because obj is passed as value and not as reference. } private void init(MyClass objVar) { objVar = new MyClass(); }


답변자 : Community Wiki


간단히 말해서 Java 객체에는 몇 가지 매우 독특한 속성이 있습니다.

일반적으로 Java에는 값으로 직접 전달되는 int , bool , char , double 그런 다음 Java에는 객체( java.lang.Object 에서 파생된 모든 것)가 있습니다. 객체는 실제로 항상 참조를 통해 처리됩니다(참조는 만질 수 없는 포인터임). 이는 참조가 일반적으로 흥미롭지 않기 때문에 실제로 개체가 참조로 전달됨을 의미합니다. 그러나 참조 자체가 값으로 전달되므로 가리키는 개체를 변경할 수 없음을 의미합니다.

이상하고 혼란스럽게 들립니까? C에서 참조로 전달과 값으로 전달을 구현하는 방법을 살펴보겠습니다. C에서 기본 규칙은 값에 의한 전달입니다. void foo(int x) 는 int를 값으로 전달합니다. void foo(int *x) int a 가 아니라 int: foo(&a) 대한 포인터를 원하는 함수입니다. 변수 주소를 전달하기 & 연산자와 함께 이것을 사용할 것입니다.

이것을 C++로 가져가면 참조가 있습니다. 참조는 기본적으로 (이 컨텍스트에서) 방정식의 포인터 부분을 숨기는 구문 설탕입니다. void foo(int &x) foo(a) 의해 호출되며, 여기서 컴파일러 자체는 그것이 참조이고 비의 주소임을 알고 있습니다. 기준 a 전달한다. Java에서 객체를 참조하는 모든 변수는 실제로 참조 유형이며, 예를 들어 C++에서 제공하는 세밀한 제어(및 복잡성) 없이 대부분의 의도와 목적에 대해 참조에 의한 호출을 강제 실행합니다.



답변자 : Community Wiki


나는 여기 에서 모든 프로그래밍 언어에 대한 이러한 종류의 질문에 전념하는 스레드를 만들었습니다.

자바도 언급된다 . 다음은 간략한 요약입니다.

  • Java는 값으로 매개 변수를 전달합니다.
  • "값으로"는 Java에서 매개 변수를 메서드에 전달하는 유일한 방법입니다.
  • 매개변수로 주어진 객체의 메소드를 사용하면 참조가 원본 객체를 가리킬 때 객체가 변경됩니다. (해당 메서드 자체가 일부 값을 변경하는 경우)


출처 : Here


출처 : http:www.stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value">

반응형