나는 항상 Java가 pass-by-reference 라고 생각했다 .
그러나 나는 그것이 아니라고 주장하는 두 개의 블로그 게시물 (예 : 이 블로그 )을 보았습니다 .
나는 그들이 만들고있는 차이점을 이해하지 못한다고 생각합니다.
설명은 무엇입니까?
답변
자바는 항상 가치가있다 . 불행히도, 우리는 객체의 값을 전달할 때 그 참조 를 전달 합니다. 초보자에게는 혼동 스럽습니다.
다음과 같이 진행됩니다.
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
기능이 변경되어 있지 foo
에 Dog
"Fifi"
객체 참조는 값으로 전달 될 때. 참조로 전달 된 경우 aDog.getName()
in main
은에 "Fifi"
대한 호출 후 반환 됩니다 foo
.
마찬가지로:
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(...)
. foo
수행하는 모든 작업 d
은 모든 실제적인 목적으로 수행 aDog
되나 , 변수 자체 의 값을 변경할 수 는 없습니다aDog
.
답변
난 당신이 내 기사 를 참조한 것으로 나타났습니다 .
자바 스펙은 자바의 모든 것이 가치에 의한 것이라고 말한다. Java에는 “pass-by-reference”와 같은 것은 없습니다.
이것을 이해하는 열쇠는
Dog myDog;
입니다 하지 개가; 실제로 는 개를 가리키는 포인터 입니다.
그것이 의미하는 것은
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)의 하나) 최대에 자신의 이름을 변경하도록 요청
- “BBB”줄에서
- 새로운
Dog
것이 만들어집니다. 그가 주소 74에 있다고 가정 해 봅시다. - 우리는
someDog
74에 매개 변수 를 할당
- 새로운
- “CCC”라인에서
- someDog가
Dog
가리키는 곳 (Dog
주소 74 의 객체) - 있음
Dog
(주소 (74)의 하나) Rowlf에 자신의 이름을 변경하도록 요청
- someDog가
- 우리는 돌아온다
이제 메소드 외부에서 일어나는 일에 대해 생각해 봅시다.
myDog
변경 되었습니까 ?
열쇠가 있습니다.
그것이 실제 myDog
가 아니라 포인터 라는 것을 명심 Dog
하고 대답은 NO입니다. myDog
여전히 값 42를가집니다. 여전히 원본을 가리키고 있습니다. Dog
그러나 “AAA”줄 때문에 이름이 “Max”입니다. 여전히 같은 Dog; myDog
의 값은 변경되지 않았습니다.
주소 를 따르고 끝에있는 것을 변경하는 것은 완벽하게 유효 합니다. 그러나 변수를 변경하지는 않습니다.
Java는 C와 똑같이 작동합니다. 포인터를 지정하고 포인터를 메소드에 전달하고 메소드의 포인터를 따라 지시 된 데이터를 변경할 수 있습니다. 그러나 해당 포인터가 가리키는 위치는 변경할 수 없습니다.
C ++, Ada, Pascal 및 참조 별 전달을 지원하는 기타 언어에서는 전달 된 변수를 실제로 변경할 수 있습니다.
Java가 참조 별 전달 시맨틱을 가지고 있다면, foo
위에서 정의한 메소드가 BBB 라인에서 지정할 myDog
때 가리키는 위치 가 변경 되었을 것 someDog
입니다.
참조 매개 변수는 전달 된 변수의 별명으로 생각하십시오. 해당 별명이 지정되면 전달 된 변수도 지정됩니다.
답변
Java는 항상 참조가 아닌 value로 인수 를 전달 합니다.
예를 통해 이것을 설명하겠습니다 .
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");
}
}
나는 이것을 단계적으로 설명 할 것이다 :
-
f
type 이라는 이름 의 참조를 선언하고 속성Foo
이있는 새로운 유형의 객체를 할당Foo
합니다"f"
.Foo f = new Foo("f");
-
메소드 측에서
Foo
이름을 가진 유형의 참조a
가 선언되고 처음에 할당됩니다null
.public static void changeReference(Foo a)
-
메소드를 호출하면
changeReference
참조a
에 인수로 전달 된 객체가 지정됩니다.changeReference(f);
-
b
type 이라는 이름 의 참조를 선언하고 속성Foo
이있는 새로운 유형의 객체를 할당Foo
합니다"b"
.Foo b = new Foo("b");
-
a = b
속성이있는 객체 의 참조a
가 아닌 참조에 새로운 할당을합니다 .f
"b"
-
modifyReference(Foo c)
method 를 호출 하면 참조c
가 생성되고 attribute를 가진 객체가 할당됩니다"f"
. -
c.setAttribute("c");
참조하는 객체의 속성을 변경하고이를 참조하는 객체c
와 동일합니다f
.
Java에서 인수로 객체를 전달하는 방법을 이해하기를 바랍니다. 🙂
답변
이것은 자바가 실제로 어떻게 작동하는지에 대한 통찰력을 줄 것입니다. Java가 다음 참조에서 참조로 전달하거나 값으로 전달하는 것에 대해서는 웃을 것입니다 🙂
1 단계 : 특히 다른 프로그래밍 언어에서 온 경우 ‘p’ “_ _ _ _ _ _ _”로 시작하는 단어를 기억하십시오. Java와 ‘p’는 같은 책, 포럼 또는 txt로 작성할 수 없습니다.
2 단계는 Object를 메소드에 전달할 때 Object 자체가 아니라 Object 참조를 전달한다는 것을 기억하십시오.
- 학생 : 스승님, Java가 참조로 전달한다는 의미입니까?
- 마스터 : 메뚜기
이제 객체의 참조 / 변수가 무엇을 / 그 것인지 생각하십시오 :
- 변수는 JVM에서 메모리의 참조 된 오브젝트 (힙)에 도달하는 방법을 알려주는 비트를 보유합니다.
- 메소드에 인수를 전달할 때 참조 변수를 전달하지 않고 참조 변수의 비트 사본을 전달합니다 . 이 같은 것 : 3bad086a. 3bad086a는 전달 된 객체에 도달하는 방법을 나타냅니다.
- 따라서 3bad086a를 전달하여 참조 값입니다.
- 참조 자체가 아니라 참조 자체의 값이 아닌 참조 값을 전달합니다.
- 이 값은 실제로 복사되어 메소드에 제공 됩니다.
다음에서 (컴파일 / 실행하려고하지 마십시오 …) :
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 Object가 2 번 줄에 생성되어 메모리에 저장되고 변수 person 에 Person 객체에 대한 참조가 제공됩니다. 즉, 주소입니다. 3bad086a를 가정 해 봅시다.
- 객체의 주소를 보유한 변수 사람 은 3 번 라인의 함수로 전달됩니다.
- 4 번 라인에서 침묵의 소리를들을 수 있습니다
- 5 번 라인의 코멘트 확인
- 메소드 local 변수 인 anotherReferenceToTheSamePersonObject 가 작성되어 6 번째 줄에 마법이 나타납니다.
- 변수 / 레퍼런스 개인 은 비트 단위로 복사 되어 함수 내의 anotherReferenceToTheSamePersonObject에 전달됩니다 .
- Person의 새 인스턴스가 작성되지 않습니다.
- ” person “과 ” anotherReferenceToTheSamePersonObject “는 모두 같은 값 3bad086a를가집니다.
- 이것을 시도하지 말고 person == anotherReferenceToTheSamePersonObject는 true입니다.
- 두 변수 모두 참조의 동일 사본을 가지며 동일한 개인 오브젝트, 힙의 동일한 오브젝트 및 사본이 아닙니다.
그림은 천 단어의 가치가 있습니다.
anotherReferenceToTheSamePersonObject 화살표는 변수 사람이 아닌 오브젝트를 향합니다!
당신이 그것을 얻지 못하면 저를 믿어 Java가 가치에 의해 전달 된다고 말하는 것이 낫다는 것을 기억하십시오 . 음, 참조 값으로 전달하십시오 . 글쎄, 훨씬 더 나은 값의 복사 값 이 더 좋습니다 ! 😉
이제 나를 미워하지만이 방법을 사용하면 메소드 인수에 대해 이야기 할 때 기본 데이터 유형과 객체를 전달하는 것에는 차이가 없습니다 .
항상 참조 값의 비트 사본을 전달합니다!
- 기본 데이터 유형 인 경우이 비트에는 기본 데이터 유형 자체의 값이 포함됩니다.
- 그것이 Object 인 경우 비트는 JVM에게 Object에 도달하는 방법을 알려주는 주소 값을 포함합니다.
메소드 내에서 참조 된 객체를 원하는만큼 수정할 수 있지만 아무리 노력해도 참조를 계속 전달하는 전달 된 변수를 수정할 수 없기 때문에 Java는 가치에 의해 전달됩니다 (p _ _ _ 아님) _ _ _ _) 무엇이든 상관없이 동일한 객체!
위의 changeName 함수는 전달 된 참조의 실제 내용 (비트 값)을 수정할 수 없습니다. 즉, changeName은 Person 개인이 다른 오브젝트를 참조하도록 만들 수 없습니다.
물론 짧게 자르고 Java가 가치에 의한 것이라고 말할 수 있습니다 !
답변
자바는 항상 예외없이, 값에 의해 전달되는 지금 .
그렇다면 누구나 이것으로 혼란 스러울 수 있고 Java가 참조로 전달된다고 생각하거나 참조로 전달하는 Java의 예가 있다고 생각합니까? 요점은 Java 가 어떤 상황에서도 객체 자체 의 값에 직접 액세스 할 수 없다는 것 입니다. 객체에 대한 유일한 액세스는 해당 객체에 대한 참조 를 통해서 입니다. Java 객체는 직접적이 아니라 항상 참조를 통해 액세스 되므로 필드와 변수 및 메소드 인수 에 대해 일반적 으로 객체 에 대한 참조 일 때만 객체 인 것으로 이야기하는 것이 일반적 입니다.혼동은 명명법의 (엄격하게 말해서, 부정확 한) 변화에서 기인합니다.
따라서 메소드를 호출 할 때
- 기본 인수 (
int
,long
등)의 경우 전달 기준 값은 기본 의 실제 값 입니다 (예 : 3). - 객체의 경우 전달 기준 값은 객체 에 대한 참조 값입니다 .
당신이 doSomething(foo)
있고 public void doSomething(Foo foo) { .. }
두 Foos가 동일한 객체를 가리키는 참조 를 복사했다면 .
당연히, 값을 기준으로 객체에 대한 참조를 전달하는 것은 객체를 참조로 전달하는 것과 매우 유사합니다 (실제로는 구분할 수 없습니다).
답변
Java는 값으로 참조를 전달합니다.
따라서 전달되는 참조를 변경할 수 없습니다.
답변
나는 “기준 별 통과 대 가치 별”에 대해 논쟁하는 것이 도움이되지 않는다고 생각한다.
두 경우 모두 “자바는 통과 (참조 / 값)”라고 말하면 완전한 답을 제공 할 수 없습니다. 다음은 메모리에서 발생하는 상황을 이해하는 데 도움이되는 추가 정보입니다.
Java 구현을하기 전에 스택 / 힙에 대한 충돌 코스 : 식당의 플레이트 스택과 같이 값이 규칙적으로 멋지게 스택으로 올라가거나 내려갑니다. 힙의 메모리 (동적 메모리라고도 함)가 우연히 발생하고 구성이 해제되었습니다. JVM은 가능한 한 공간을 찾고 더 이상 사용하지 않는 변수를 확보합니다.
괜찮아. 먼저, 로컬 프리미티브가 스택으로 이동합니다. 따라서이 코드 :
int x = 3;
float y = 101.1f;
boolean amIAwesome = true;
결과는 다음과 같습니다.
객체를 선언하고 인스턴스화 할 때 실제 객체는 힙에서 진행됩니다. 스택에 무슨 일이? 힙에서 오브젝트의 주소입니다. C ++ 프로그래머는 이것을 포인터라고 부르지 만 일부 Java 개발자는 “포인터”라는 단어에 반대합니다. 도대체 무엇이. 객체의 주소가 스택에 있는지 확인하십시오.
이렇게 :
int problems = 99;
String name = "Jay-Z";
배열은 객체이므로 힙에도 적용됩니다. 그리고 배열의 객체는 어떻습니까? 그들은 자신의 힙 공간을 얻고 각 객체의 주소는 배열 내부로 들어갑니다.
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
가 생성되고 이미 존재하는 문자열의 주소가 할당됩니다.
그래서 가치, 참조? 당신은 “감자”라고 말합니다.
