java 아규먼트 pass - 자바는 "참조에 의한 전달"인가 "가치에 의한 전달"인가?



15 Answers

나는 당신이 나의 기사 를 참조하는 것을 보았습니다.

Java Spec은 Java의 모든 것이 가치에 의해 전달된다고 말합니다. 자바에서는 "참조에 의한 통과"와 같은 것이 없다.

이것을 이해하는 열쇠는

Dog myDog;

개가 아닙니다 . 실제로는 Dog에 대한 포인터 입니다.

그것이 의미하는 바는 당신이 가질 때입니다.

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

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

(필자는 자바 포인터가 직접 주소가 아니기 때문에 본질적으로 말하지만, 그렇게 생각하는 것이 가장 쉽다)

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 (주소 42의 Dog 객체)
    • Dog (42 번지에있는 개)는 그의 이름을 맥스로 바꿀 것을 요청받습니다.
  • 라인 "BBB"
    • 새로운 Dog 만들어집니다. 그가 주소 74에 있다고 가정 해 봅시다.
    • 파라미터 someDog 를 74에 대입합니다.
  • 라인 "CCC"
    • someDog가 가리키는 Dog (주소 74의 Dog 객체)
    • Dog (주소 74에있는 Dog )는 그의 이름을 Rowlf로 바꿀 것을 요청받습니다.
  • 그 다음, 우리는 돌아 간다.

이제 메소드 밖에서 일어나는 일에 대해 생각해 봅시다.

myDog 바뀌 었습니까?

열쇠가 있습니다.

myDog 는 실제 Dog 가 아니라 포인터 임을 명심하십시오. 대답은 NO입니다. myDog 의 값은 여전히 ​​42입니다. 여전히 원래 Dog 가리키고 있습니다 (단, 줄 "AAA"로 인해 그 이름은 "Max"임 - 여전히 동일한 Dog, myDog 의 값은 변경되지 않았습니다).

그것은 주소를 따르고 그것의 끝에 무엇이 바뀌는 것이 완벽하게 유효합니다; 그러나 변수를 변경하지는 않습니다.

Java는 C와 똑같이 작동합니다. 포인터를 지정하고 메소드에 포인터를 전달하고 메소드의 포인터를 따라 가며 가리킨 데이터를 변경할 수 있습니다. 그러나 포인터가 가리키는 위치는 변경할 수 없습니다.

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

Java가 pass-by-reference 의미론을 가지고 있다면 위에서 정의한 foo 메소드는 myDog 이 BBB 라인에서 someDog 를 할당 할 때 가리키고있는 곳에서 변경되었을 것입니다.

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

by reference call

나는 항상 자바가 참조에 의한 것이라고 생각했다.

그러나 나는 그것이 아니라고 주장하는 몇 가지 블로그 게시물 (예 : 이 블로그 )을 보았습니다.

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

설명은 무엇입니까?




이렇게하면 Java가 실제로 어떻게 동작하는지에 대한 통찰력을 얻을 수 있습니다. 자바에 대한 다음 논의에서 참조로 전달하거나 값으로 전달하면 웃을 것입니다 .-)

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

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

  • Student : Master, Java가 참조로 전달된다는 의미입니까?
  • 마스터 : 메뚜기, 아니.

이제 개체의 참조 / 변수가하는 일을 생각해보십시오.

  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라고 가정 해 봅시다.
  • Object의 주소를 포함하는 변수 person 은 라인 # 3의 함수에 전달됩니다.
  • 4 번 줄에서는 침묵의 소리를들을 수 있습니다.
  • 5 번 줄에 주석 달기
  • 메서드 로컬 변수 인 anotherReferenceToTheSamePersonObject 가 만들어지고 그 다음에는 # 6 행의 마법 이옵니다 .
    • 변수 / 참조 은 비트 단위로 복사되고 함수 내에서 anotherReferenceToTheSamePersonObject에 전달됩니다.
    • Person의 새 인스턴스는 작성되지 않습니다.
    • " person "과 " anotherReferenceToTheSamePersonObject "는 모두 3bad086a 와 동일한 값을 유지합니다.
    • 이 것을 시도하지 말고 person == anotherReferenceToTheSamePersonObject가 true가됩니다.
    • 두 변수 모두 참조에 대해 동일한 사본을 가지고 있으며 둘 다 동일한 개인 오브젝트, 힙의 SAME 오브젝트 및 사본이 아 U니다.

그림은 천 단어의 가치가있다 :

다른 ReferencesToTheSamePersonObject 화살표는 오브젝트쪽으로 향하고 가변 사람쪽으로 향하지 않습니다!

당신이 그것을 얻지 못한다면 나는 단지 나를 신뢰하고 자바가 가치에 의한 것임을 말하는 것이 낫다는 것을 기억하십시오. 음, 기준값으로 전달하십시오 . 오, 더 나은 것은 패스 바이 바이 변이 가치입니다! ;)

이제 나를 미워하되,이 점을 고려할 때 메소드 인자에 대해 말할 때 원시 데이터 유형과 객체를 전달하는 것 사이에는 차이가 없다는 점에 유의하십시오.

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

  • 기본 데이터 형식 인 경우이 비트는 기본 데이터 형식 자체의 값을 포함합니다.
  • 오브젝트의 경우, 비트는 JVM에 오브젝트에 도달하는 방법을 알려주는 주소 값을 포함합니다.

Java는 메서드 내에서 참조 된 Object를 원하는만큼 수정할 수 있기 때문에 값으로 전달됩니다. 그러나 아무리 노력해도 참조 된 변수를 수정할 수 없으므로 (p _ _ _가 아닌) _ _ _ _) 같은 개체 상관없이!

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

당연히 당신은 그것을 짧게자를 수 있고 자바가 가치에 의한 것이라고 말할 수 있습니다 !




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

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




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

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는 내장 유형에 대한 값과 오브젝트 유형에 대한 포인터 값의 두 가지 유형 만 전달합니다.




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

private void foo(Object bar) {
    bar = null;
}

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

"Hah!" 가 인쇄됩니다 "Hah!" null 대신. 그 이유는 barbaz 의 값의 복사본이기 때문입니다.이 값은 "Hah!" 대한 참조 일뿐 "Hah!" . 이것이 실제 참조 자체라면 foobaznull 재정의했을 것입니다.




문제의 핵심은 단어이다 참조 표현 "참조에 의해 전달"에서이 단어의 일반적인 의미는 전혀 다른 것을 의미 참조 자바를.

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




참조는 표현할 때 항상 사용하는 언어와 상관없이 값입니다.

박스 뷰 외부에서 어셈블리 또는 저수준 메모리 관리를 살펴 보겠습니다. CPU 레벨 에서 메모리 또는 CPU 레지스터 중 하나에 쓰여 지면 모든 것에 대한 참조 가 즉시 값이 됩니다. (그 이유는 포인터 가 좋은 정의입니다. 그것은 동시에 목적을 가진 값입니다).

메모리의 데이터에는 위치가 있으며 그 위치에는 값 (바이트, 단어 등)이 있습니다. 어셈블리에서는 특정 위치 (일명 변수)에 이름지정 하는 편리한 솔루션이 있지만 코드를 컴파일 할 때 어셈블러는 브라우저가 도메인 이름을 IP 주소로 바꾼 것처럼 Name 을 지정된 위치로 간단히 대체 합니다.

핵심에 이르기까지 모든 언어의 표현을 표현하지 않고 참조를 전달하는 것은 기술적으로 불가능합니다 (즉시 값이 될 때).

변수 Foo가 있고, 위치 가 메모리에서 47 번째 바이트이고 이 5 인 경우를 가정 해 봅시다 . 메모리에있는 223 번째 바이트에 있는 또 다른 변수 Ref2Foo있으며이 값은 47입니다.이 Ref2Foo는 기술 변수 일 수 있습니다 프로그램에 의해 명시 적으로 생성되지는 않습니다. 다른 정보없이 5와 47을 보면 두 개의 값만 보입니다 . 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 스택에 밀어 넣습니다.
  5. 223은 CPU 레지스터 중 하나로 복사됩니다.
  6. 223 스택에 PUSHd를 가져옵니다.

기존 값 의 사본 인 값 이상인 모든 경우 에 처리 할 수있는 수신 메소드가 추가되었습니다. 메서드 내에서 "Foo"를 작성하면 EAX에서 읽거나 자동으로 역 참조 하거나 이중 역 참조하므로 프로세스는 언어 작동 방식 및 / 또는 Foo 유형에 따라 달라집니다. 이는 참조 취소 프로세스를 우회 할 때까지 개발자에게 표시되지 않습니다. 따라서 참조 A는 가치 기준은 (언어 레벨에서) 처리 할 수있는 값이므로 표현.

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

  • 경우 1. 및 2. Foo ( Foo = 9) 를 변경 하면 값의 복사본이 있으므로 로컬 범위에만 영향을줍니다. 메소드 내부에서 우리는 원래 Foo가 위치한 곳을 확인할 수 없다.
  • 경우 3. 및 4. 기본 언어 구문을 사용하여 Foo ( Foo = 11)를 변경하면 Foo가 전역 적으로 변경 될 수 있습니다 (Java 또는 Pascal의 procedure findMin(x, y, z: integer; var m 과 같은 언어에 따라 다름 : integer);). 언어 당신이 역 참조 프로세스를 우회 할 수 있습니다 그러나, 당신은 변경할 수 47에 말한다 49. 그 시점에서 Foo는 로컬 포인터 를 변경 했으므로 읽으면 변경된 것 같습니다 . 그리고 만일 당신이 Foo = 12예상했던 것보다 다른 메모리에 쓸 것이기 때문에 당신은 프로그램의 실행을 푸시 (아마도 segfault) 할 것이고, 당신은 실행 가능을 유지할 운명의 영역을 수정할 수도있다. 프로그램을 작성하고 실행하면 실행중인 코드가 수정됩니다 (Foo는 현재 실행되지 않습니다 47). 하지만 Foo의 가치47전역 적으로 변경되지 않았으며, 메소드 내부의 47사본 이기 때문에 메소드 내부의 메소드 만 변경 되었습니다.
  • case 5.와 6. 만약 당신 223이 메소드 내에서 수정하면 3. 나 4.와 같은 문제를 일으킨다. (포인터가 다시 나쁜 값을 가리키며 다시 포인터로 사용된다.)하지만 여전히 로컬이다. 문제는 223이 복사 된 것 입니다. 당신이 역 참조 할 수있다 그러나 경우 Ref2Foo(즉 223)에 도달하고 뾰족한 값을 수정 47, 말,하는 49, 그것은 푸에 영향을 미칠 것입니다 세계적으로 이 경우에는 방법의 사본을 가지고 있기 때문에, 223하지만, 참조는 47한 번만 존재하고 있음을 변경 to 49가 모든 Ref2Foo이중 참조 해제를 잘못된 값으로 유도 합니다.

중요하지 않은 세부 사항, 심지어 참조로 전달되는 언어조차도 값을 함수로 전달하지만 함수는 역 참조를 위해 값을 사용해야한다는 것을 알고 있습니다. 이 pass-the-reference-as-value는 실제적으로 쓸모가 없으므로 프로그래머가 숨길 뿐이며 전문 용어는 참조별로 전달됩니다 .

엄격한 pass-by-value 도 쓸모가 없습니다. 배열을 인수로 사용하여 메소드를 호출 할 때마다 100MB의 배열을 복사해야하므로 엄격하게 Java가 값으로 전달 될 수 없습니다. 모든 언어는이 거대한 배열에 대한 참조를 (값으로) 전달할 것이고 그 배열이 메소드 내에서 로컬로 변경 될 수 있거나 (자바처럼) 메소드가 전역으로 배열을 수정할 수있게한다면 copy-on-write 메커니즘을 사용한다. 호출자의 견해) 및 몇 가지 언어로 참조 자체의 값을 수정할 수 있습니다.

짧은 자바 자신의 용어 그래서, 자바는 패스에 의해 가치값이 중 하나가 될 수있는 진정한 가치 또는 (A)의 표현이다 참조 .




내가 아는 한, 자바는 가치에 의한 호출만을 알고있다. 즉, 원시 데이터 유형의 경우 사본으로 작업하고 객체에 대해서는 객체에 대한 참조 복사본으로 작업합니다. 그러나 나는 몇몇 함정이 있다고 생각한다; 예를 들어,이 작동하지 않습니다 :

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

스왑 함수에서는 메인의 참조에 영향을주지 않는 copys를 사용하기 때문에 Hello World가 아닌 World Hello가 채워집니다. 그러나 객체가 변경되지 않는 경우 다음과 같이 변경할 수 있습니다.

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

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




네 가지 예를 통해 나의 이해를 설명하려고 노력하겠습니다. 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 '느낌을 가지고있다.

어떤 사람들은 원시 타입과 'String'은 '값으로 전달'이고 객체는 '참조로 전달'이라고 말합니다.

그러나이 예에서 우리는 값으로 전달하는 것이 사실임을 이해할 수 있습니다. 여기에서 참조를 값으로 전달한다는 것을 명심하십시오. ie : 참조는 값으로 전달됩니다. 그것이 변화 할 수있는 이유이며, 여전히 지역 범위 이후에도 적용됩니다. 그러나 원래의 범위를 벗어난 실제 참조는 변경할 수 없습니다. 이것이 의미하는 바는 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]



Java는 가치에 의한 호출입니다.

작동 원리

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

  • 원시 데이터 형식 인 경우 이러한 비트는 원시 데이터 형식 자체의 값을 포함하므로 메서드 내부의 header 값을 변경하면 변경 내용이 외부에 반영되지 않습니다.

  • Foo foo = new Foo () 와 같은 객체 데이터 유형 이면 객체의 주소 사본이 파일 바로 가기처럼 전달 됩니다. C : \ desktop에 텍스트 파일 abc.txt 가 있다고 가정하고 다음과 같이 단축키를 만든다고 가정합니다. C : \ desktop \ abc.txt 에서 파일에 액세스하고 ''라고 쓰고 파일을 닫은 다음 바로 가기에서 파일을 다시 열 때이 파일을 C : \ desktop \ abc- 바로 가기 안에 넣으십시오 . 쓰기 '는 프로그래머가 배울 수있는 가장 큰 온라인 커뮤니티' 총 파일 변경이 될 것입니다 '스택 오버플로는 프로그래머가 배우기에 가장 큰 온라인 커뮤니티입니다.어떤이, 우리가 추측 할 수는 파일, 우리가 같은 파일에 액세스하고 때마다 열 곳에서 중요하지 않습니다 의미 에 저장된 파일 및 가정하자 foo는 같은 123hd7h (같은 원래 주소 C : 바탕 화면 \의 abc.txt \ ) 주소 및 234jdid ( C : \ desktop \ abc-shortcut 같은 복사 된 주소) 실제로 .. 파일의 원래 주소가 들어 있습니다.) 그래서 더 나은 이해를 위해 바로 가기 파일을 만들고 느낌 ...




내가 원래 포스터와 같은 인상을 받았던 것처럼 구별 또는 기억하는 방식은 다음과 같습니다. Java는 항상 가치에 의해 전달됩니다. Java의 모든 객체 (Java에서는 기본 요소를 제외한 모든 객체)는 참조입니다. 이러한 참조는 값으로 전달됩니다.




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



저는 항상 이것을 "사본으로 전달"이라고 생각합니다. 프리미티브 또는 참조 값의 사본입니다. 원시적 인 경우는 값인 비트의 복사본이며 Object 인 경우 참조의 복사본입니다.

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

java PassByCopy의 출력 :

name = Maxx
name = Fido

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




일부 게시물에 약간의 수정.

C는 참조에 의한 전달을 지원하지 않습니다. 그것은 항상 가치에 의해 전달됩니다. C ++은 참조로 전달을 지원하지만 기본값은 아니며 매우 위험합니다.

Java에서 값이 무엇인지는 중요하지 않습니다. 객체의 원시 또는 주소 (대략), 항상 값으로 전달됩니다.

Java 객체가 참조로 전달되는 것처럼 "동작"하는 경우, 이는 변경 가능성의 속성이며 메커니즘 전달과는 전혀 관련이 없습니다.

왜 이렇게 많은 혼란 스러울 지 모르겠다. 아마도 많은 자바 프로그래머들이 공식적으로 훈련받지 못했고 실제로 메모리에서 진행되고있는 것을 이해하지 못했을 것이다.




Java는 매개 변수를 VALUE 및 값 전용으로 전달 합니다.

짧은 이야기를 짧게 자르기 :

C #에서 오는 경우 : "out"매개 변수가 없습니다.

파스칼에서 오는 사람들을 위해 : "var"매개 변수가 없습니다 .

즉, 객체 자체에서 참조를 변경할 수는 없지만 객체의 속성은 언제든지 변경할 수 있습니다.

해결 방법은 StringBuilder매개 변수를 대신 사용하는 것 String입니다. 그리고 항상 배열을 사용할 수 있습니다!




Related