Java "pass-by-reference" və ya "pass-by-value" mı?

Mən həmişə java bir keçid keçid olduğuna inanıram.

Buna baxmayaraq, bir neçə blog ismarıcını (məsələn, bu blog ) gördüyünü iddia edirəm.

Mən etdikləri fərqini başa düşmürəm.

Təsvir nədir?

5703
02 сент. user4315 müəyyən edilib 02 sept. 2008-09-02 23:14 '08 at 11:14 pm 2008-09-02 23:14
@ 92 cavab

Java həmişə dəyərlə qəbul olunur . Təəssüf ki, bir obyektin dəyərini keçdikdən sonra ona bir keçid verəcəyik. Bu yeni başlayanlar üçün qarışıqdır.

Belə görünür:

 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 } 

Yuxarıdakı nümunədə, aDog.getName() hələ də "Max" qaytarır. aDog dəyəri, aDog "Fifi" ilə foo funksiyasında dəyişilmir, çünki obyektə istinad dəyərlə ötürülür. Referansla qəbul edildikdə, main aDog.getName() foo sonra "Fifi" qayıdır.

Eynilə:

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

Yuxarıda göstərilən misalda, foo(aDog) zəng etdikdən sonra it adının adı foo(...) daxilində təyin edilmişdir. d də yerinə yetirən hər hansı əməliyyatlar, praktiki məqsədlər üçün, aDog yerinə aDog , lakin aDog dəyişəninin dəyərini dəyişdirmək mümkün deyildir .

5068
02 сент. cavab verildi erlando 02 Sentyabr 2008-09-02 23:25 '08 at 23:25 2008-09-02 23:25

Mən yalnız məqaləmə istinad etdiyini fərq etdim.

Java Spec deyir ki, Java-dakı hər şey dəyərdən keçir. Java'da "pass-by-reference" kimi bir şey yoxdur.

Bunu anlamaq üçün əsas bir şeydir

 Dog myDog; 

bir it deyil; bu əslində itə bir göstərici.

Bu zaman nə deməkdir

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

əslində yaradılan Dog obyektinin ünvanını foo üsuluna keçir.

(Mən, həqiqətən, demişəm ki, Java göstəriciləri birbaşa ünvanlar deyil, belə düşünmək üçün ən asan yol)

Dog obyektinin yaddaş ünvanında 42 olduğunu düşünün. Bu, 42 üsulu keçməyimiz deməkdir.

üsul kimi təyin olundu

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

nə olacağını gör.

  • someDog parametri 42 olaraq təyin olunur
  • "AAA"
    • someDog Dog ( Dog obyekti 42 ünvanında)
    • Dog (42 ünvanında olan biri) adını Maxə dəyişmək istədi
  • "BBB"
    • Yeni Dog yaradılıb. Deyək, bu 74 yaşındadır
    • 74 parametresine someDog parametr someDog
  • "CCC" xəttində
    • Bəzi şeylər Dog (Şeytanın obyekti 74)
    • Dog (74 nömrəli ünvana) adını Rowlf-ə dəyişmək istədi
  • sonra qayıdırıq

İndi üsuldan kənarda nə baş verdiyini düşünün:

myDog dəyişdi?

Burada əsasdır.

myDog bir göstəricidir, faktiki Dog deyil, unutmayın ki, cavab NO. myDog hələ 42; hələ də orijinal Dog (amma qeyd etmək lazımdır ki, "AAA" simvolundan onun adı indi "Max" - eyni Dog xarakteri, myDog dəyişməyib.)

Doğrudan ünvanı izləmək və onun sonunda nə olduğunu dəyişmək; lakin dəyişən dəyişməz.

Java, C kimi eyni şəkildə işləyir. Göstərici təyin edə, bir üsula bir göstəricidən keçə bilər, bir metodda göstəriciyə əməl edin və göstərilən məlumatları dəyişdirin. Lakin, bu göstəricinin göstərdiyi yer dəyişə bilməz.

C ++, Ada, Paskal və pass-by-istinad ilə dəstəkləyən digər dillərdə, həqiqətən keçmiş dəyişənləri dəyişə bilərsiniz.

Java-da keçid someDog semantikası varsa, yuxarıda müəyyən etdiyimiz foo metodu, myDog BBB xəttində someDog təyin someDog göstərildiyi yerdə dəyişəcək.

Mətn parametrlərini keçən dəyişən üçün alias kimi düşünün. Bu alias təyin edildikdə, keçmiş dəyişən kimi.

2768
16 сент. Sep 16-da Scott Stanchfield tərəfindən verilmiş cavab 2008-09-16 17:37 '08 at 5:37 pm 2008-09-16 17:37

Java həmişə argumentləri istinad edərək deyil, dəyəri ilə keçir.


Bir nümunə ilə mənə izah edək:

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

Bunu addımlarla izah edəcəyəm:

  1. Foo tipi f tipi ilə əlaqəni bəyan edin və ona "f" atributu ilə Foo növündən yeni bir obyekt verin.

     Foo f = new Foo("f"); 

    2019

1472
14 сент. Cavab verilir Eng.Fouad 14 sep . 2012-09-14 21:22 '12 saat 09:22 'da 2012-09-14 21:22

Bu, həqiqətən, Java həqiqətən necə işlədiyini bir fikir verəcəkdir ki, istinad edərək və ya dəyərdən keçərək Java-dan keçməklə bağlı aşağıdakı müzakirələrdə sadəcə gülümsəyəcəksiniz: -)

Birinci addım, xüsusən də digər proqramlaşdırma dillərindən gəldiyinizdə "p" "_ _ _ _ _ _" ilə başlayan bu sözü nəzərə alın. Java və "p" eyni kitabda, forumda və hətta txt yazıla bilməz.

Addım 2: bir metodu bir obyektə keçdiyiniz zaman obyektin özü deyil, obyekt istinadını keçdiyinizi unutmayın.

  • Tələbə: Usta, bu Java referansla keçdiyi anlamına mı gəlir?
  • Usta: Çəyirtkə, Yox.

Artıq hansı istinad / obyektin dəyişənliyi haqqında düşünün:

  • Dəyişən JVM-yə yaddaşda (yığınlarda) göstərilən obyektə necə gedəcəyini söyləyən bitləri ehtiva edir.
  • Dəyişiklikləri üsula keçərkən referans dəyişənə keçmirsiniz, ancaq referans dəyişən bitlərin bir nüsxəsi . Bu kimi bir şey: 3bad086a. 3bad086a köçürülmüş bir obyektə daxil olmaq üçün bir yoldur.
  • Beləliklə, yalnız 3bad086a keçmək, yəni linkin dəyəri.
  • Linkin (obyektin) deyil, linkin dəyərini verirsiniz.
  • Bu dəyər əslində COPED və üsula verilir .

Aşağıdakıda (xahiş edirik bunu tərtib et və ya icra etməyin):

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

Whats davam edir

  • Dəyişən istifadəçi # 1 satırında yaradılıb və əvvəlində null.
  • Yeni bir şəxs obyekti yaddaşı saxlanan # 2 satırında yaradılıb və dəyişənə bir şəxs obyektinə bir dəyişənə istinad verilir. Yəni onun ünvanı. 3bad086a deyin.
  • Obyektin ünvanını ehtiva edən dəyişənin üzü 3 nömrəli funksiyaya ötürülür.
  • # 4 saylı sükut səsini dinləyə bilərsiniz.
  • Şərhin 5-cü satırında qeyd olun
  • Metod üçün bir yerli dəyişən -ReferenceToTheSamePersonObject-yaratdıqdan sonra sehrli # 6 satırında görünür:
    • Bir dəyişən / arayışın istifadəçisi bit ilə kopyalanır və başqa bir ReferenceToTheSamePersonObject funksiyası daxilində keçirilir.
    • Şəxsin yeni bir nümunəsi yoxdur.
    • Həm "şəxs", həm də "anotherReferenceToTheSamePersonObject" 3bad086a eyni dəyəri saxlayır.
    • Bunu etməyin, amma man == başqa bir Rəhbərlik TərəfdaşlığıPaylaşması doğru olacaq.
    • Hər iki dəyişəndə ​​linkin TƏHLÜKƏSİZLİK KOPYALARI vardır və hər ikisi eyni şəxs obyektinə, eyni obyektə aiddir və bir kopya deyildir.

Bir şəkil min sözə layiqdir:

2019

680
12 авг. Gevorg'a 12 Avqustda cavab verildi 2011-08-12 04:22 '11 'də 4:22' də 2011-08-12 04:22 'də

Java həmişə dəyəri, heç bir istisna olmadan keçər.

Beləliklə, bunların hamısı necə qarışdırıla bilər və onlar Java-nın linkə düşdüyünü düşünürlər və ya Java-nın nümunəsi olduğunu düşünürlər? Əsas nöqtə, Java heç bir şərtdə obyektlərin dəyərlərinə birbaşa daxil olmur . Obyektlərə yalnız giriş bu obyektin bir keçididir. Java obyektlərinə həmişə birbaşa deyil, birbaşa keçiddən istifadə edildiyindən, metodun sahələri, dəyişənləri və dəlilləri pedantik olaraq obyektlərin yalnız istinadları olduqda obyektlərdir. Çaşqınlıq bu nomenklatura dəyişikliklərdən (səhvən, yanlış) dəyişiklikdir.

Belə ki, üsul çağırırıq

  • İbtidai arqumentlər ( int , long və s.) Üçün dəyər atlama primitivin əsl dəyəridir (məsələn, 3).
  • Obyektlər üçün, obyektin referans dəyəri dəyərlə qəbul edilir.

Beləliklə, əgər doSomething(foo)doSomething(foo) public void doSomething(Foo foo) { .. } , iki Foos eyni obyektlərə işarə edən əlaqələri kopyaladılar.

Təbii ki, obyekt arayışından keçərək, obyekti referansla keçərək praktikada çox fərqli (və fərqləndirilə bilməyən).

599
02 сент. Cavab SCdF 02 Sentyabr verilir. 2008-09-02 23:19 '08 at 11:19 pm 2008-09-02 23:19

Java istinadları dəyəri ilə keçir.

Beləliklə, ötürülən keçidləri dəyişə bilməzsiniz.

290
02 сент. Cavab verildi ScArcher2 02 Sentyabr. 2008-09-02 23:20 '08 at 23:20 2008-09-02 23:20

Mənə elə gəlir ki, "pass-by-reference vs pass-by-value" haqqında mübahisə çox faydalı deyil.

Əgər deyirsinizsə: "Java pass-by-whatever (reference / value)" hər halda, tam cavab vermirsiniz. Ümid edirəm ki, yaddaşda nə baş verdiyini başa düşməyinizə kömək edəcək bəzi əlavə məlumatlar.

Java tətbiqinə getmədən əvvəl yığma / yığındakı Crash kursu: Dəyərlər yemək otağında bir plitə yığmağı kimi səciyyələnməlidir. Heap yaddaş (dinamik yaddaş kimi də tanınır) dağınık və dağınıkdır. JVM sadəcə onu istifadə edə biləcək bir yer tapır və onu buraxır, çünki onu istifadə edən dəyişənlər artıq lazım deyildir.

Yaxşı Birincisi, yerli primitivlər yığmaya gedirlər. Belə ki, bu kod:

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

aşağıdakılara gətirib çıxarır:

2019

210
11 сент. cavab verildi . 2013-09-11 14:34 '13 'da 14:34 2013-09-11 14:34

Kontrast göstərmək üçün aşağıdakı C ++Java parçacıqlarını müqayisə edin:

C ++ da: Qeyd. Kötü kod - yaddaş sızıntısı! Ancaq bu nöqtəni göstərir.

 void cppMethod(int val, int  Dog obj, Dog  Dog *objPtr, Dog * { 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" } 

Java'da,

 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 yalnız köçürmələrin yalnız iki növünə malikdir: daxili növləri üçün dəyər və obyekt tipləri üçün pointer dəyəri ilə.

172
17 сент. Cavab Eclipse 17 sep tərəfindən verilir . 2008-09-17 20:38 '08 at 8:38 pm 2008-09-17 20:38

Java obyekt istinadlar dəyərinə keçir.

152
02 сент. Cavab John Channing tərəfindən verilir 02 Sep. 2008-09-02 23:23 '08 at 23:23 2008-09-02 23:23

Prinsipcə, bir obyektin parametrlərinin yenidən təyin edilməsi arqumenti təsir etmir, məsələn,

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

"Hah!" əvəzinə null . Bu işin səbəbi baz dəyərinin surətidir, çünki sadəcə "Hah!" Həqiqi bir istinad olarsa, foo baz null yenidən təyin edərdi.

145
02 сент. Hank Gay yayımladı 02 Sentyabr 2008-09-02 23:21 '08 at 11:21 pm 2008-09-02 23:21

Heç kimə Barbara Liskovdan bəhs etmədim. 1974-cü ildə CLU-nı hazırladığı zaman, eyni terminoloji problemi ilə qarşılaşdı və o, bu xüsusi hal üçün "bölüşdürməklə" (obyektlərin paylaşılması və obyekt tərəfindən çağırılması ilə dəvət olunur) "mübahisə" termini hazırladı. link ".

143
08 сент. Cavab Jörg W Mittag tərəfindən verilmişdir 08 Sep. 2010-09-08 01:07 '10 at 1:07 2010-09-08 01:07

Aşağı xətt isə, "linkə baxın" sözündən bir söz link Java-dakı söz linkinin adi mənasından tamamilə fərqli bir şey deməkdir.

Ümumiyyətlə, Java-da istinadlar bir obyekt istinadı deməkdir. Ancaq bir proqramlaşdırma dili nəzəriyyəsindən istinad / dəyərlə ötürülən texniki şərtlər, tamamilə fərqli bir dəyişən bir yaddaş hücresinə istinad edirlər.

103
12 янв. Jan 12-də JacquesB tərəfindən verilmiş cavab 2009-01-12 23:50 '09 at 11:50 pm 2009-01-12 23:50

Point pnt1 = new Point(0,0); hər şey istinad edilir, buna görə bir Point pnt1 = new Point(0,0); : Point pnt1 = new Point(0,0); Java aşağıdakı tədbirləri həyata keçirir:

  • Yeni bir Point obyekti yaradır.
  • Bir nöqtəyə yeni bir müraciət yaradır və əvvəlcədən hazırlanmış bir Point obyektində bir istinad nöqtəsinə (bax) bu istinaddan başlayır.
  • Burada, Point obyektinin həyatı boyunca, bu obyektə pnt1 Yardımı vasitəsilə daxil olacaqsınız. Buna görə də, biz Java-da bir obyekti onun keçid vasitəsilə manipulyasiya etdiyini söyləyə bilərik.

2019

76
21 нояб. Cavab 21 noyabrda verilir. 2013-11-21 19:04 '13 at 19:04 2013-11-21 19:04

Java həmişə istinad edərək deyil, dəyəri ilə keçir.

Hər şeydən əvvəl, dəyərlə keçdiyini və referansla keçdiyini anlamaq lazımdır.

Qiymətə keçid deməkdir ki, keçən parametrenin əsl dəyərinin yaddaşında bir surət çıxarırsınız. Bu, faktiki parametrenin məzmununun surətidir .

Referansla keçmək (həmçinin ünvana keçid adlanır) faktiki parametrlərin ünvanının surətinin saxlandığı anlamına gəlir .

Bəzən Java bir keçid keçən illüziya verə bilər. Aşağıdakı nümunəni necə istifadə etdiyinə baxın:

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

Bu proqramın nəticəsi:

 changevalue 

Adım-addımızı anlayaq:

 Test t = new Test(); 

Как мы все знаем, он создаст объект в куче и вернет исходное значение обратно в t. Например, предположим, что значение t равно 0x100234 (мы не знаем фактического внутреннего значения JVM, это просто пример).

2019

69
ответ дан Ganesh 17 окт. '13 в 10:54 2013-10-17 10:54

Ссылка всегда представляет собой значение, если оно представлено, независимо от того, какой язык вы используете.

Получив внешний вид окна, просмотрите сборку или некоторое управление памятью низкого уровня. На уровне ЦП ссылка на что-либо сразу становится значением, если она записывается в память или в один из регистров CPU. (Вот почему указатель является хорошим определением. Это значение, которое имеет цель в то же время).

Данные в памяти имеют Местоположение , и в этом месте есть значение (байт, слово, что угодно). В Assembly мы имеем удобное решение для предоставления Name для определенного Location (aka variable), но при компиляции кода ассемблер просто заменяет Name > с назначенным местоположением так же, как ваш браузер заменяет имена доменов IP-адресами.

В основе ядра технически невозможно передать ссылку на что-либо на любом языке, не представляя его (когда оно сразу становится значением).

Предположим, что у нас есть переменная Foo, ее Местоположение находится в 47-м байте в памяти, а ее Значение равно 5. У нас есть другая переменная Ref2Foo strong > , который находится в памяти 223 байта, а его значение будет 47. Этот Ref2Foo может быть технической переменной, явно не созданной программой. Если вы просто посмотрите на 5 и 47 без какой-либо другой информации, вы увидите только две Значения . Если вы используете их в качестве ссылок, то для достижения 5 нам нужно путешествовать:

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

Вот как работают прыжки-таблицы.

Если мы хотим вызвать метод/функцию/процедуру с помощью значения Foo, существует несколько возможных способов передать переменную методу в зависимости от режима > и нескольких способов его вызова:

  • 5 копируется в один из регистров CPU (например, EAX).
  • 5 получает PUSHd в стек.
  • 47 копируется в один из регистров CPU
  • 47 PUSHd в стек.
  • 223 копируется в один из регистров CPU.
  • 223 получает PUSHd в стек.

В каждом случае выше значение - копия существующего значения - было создано, теперь до метода приема обрабатывается. Когда вы пишете "Foo" внутри метода, он либо считывается из EAX, либо автоматически разыменовывается или дважды разыменован, процесс зависит от того, как работает язык и/или какой тип Foo диктует, Это скрыто от разработчика, пока она не оборвет процесс разыменования. Таким образом, ссылка представляет собой значение, когда оно представлено, потому что ссылка - это значение, которое необходимо обработать (на уровне языка).

Теперь мы прошли 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 внутри метода ( Foo = 12 ), вы, вероятно, будете FUBAR для выполнения программы (aka. Segfault), потому что вы будете писать в другую память, чем ожидалось, вы даже можете изменить область, которая предназначена для выполнения исполняемой программы и записи на нее будет изменен код запуска (Foo теперь не в 47 ). BUT Значение Foo 47 не изменилось глобально, а только один внутри метода, потому что 47 также был копией метода.
  • в случае 5 и 6. если вы изменяете 223 внутри метода, он создает тот же хаос, что и в 3. или 4. (указатель, указывающий на теперь плохое значение, которое снова используется как указатель) но это все еще локальная проблема, так как 223 был скопирован . Однако, если вы можете разыменовать Ref2Foo (то есть 223 ), достигните и измените указанное значение 47 , скажем, на 49 , это повлияет на Foo глобально , потому что в этом случае методы получили копию 223 , но ссылочный 47 существует только один раз, и изменение этого значения на 49 приведет к неправильному значению каждого двойного разыменования Ref2Foo .

Отбрасывая незначительные детали, даже языки, которые передают по ссылке, передают значения функциям, но эти функции знают, что они должны использовать его для разыменования. Этот параметр pass-the-reference-as просто скрыт от программиста, потому что он практически бесполезен, а терминология - только по ссылке.

Строгое значение pass-by-value также бесполезно, это означало бы, что массив 100 Мбайт должен быть скопирован каждый раз, когда мы вызываем метод с массивом в качестве аргумента, поэтому Java не может быть строго переданным по значению. Каждый язык передавал бы ссылку на этот огромный массив (как значение) и использовал механизм copy-on-write, если этот массив может быть локально изменен внутри метода или позволяет использовать метод (как Java) для изменения массива глобально (от представление вызывающего абонента), а несколько языков позволяют изменять значение самой ссылки.

Итак, вкратце и в собственной терминологии Java, Java имеет значение pass-by-value, где значение может быть: либо реальное значение , либо значение , которое представляет собой представление a ссылка .

65
ответ дан karatedog 13 дек. '13 в 15:23 2013-12-13 15:23

Нет, он не проходит по ссылке.

Java передается по значению в соответствии со спецификацией Java >

Когда вызывается метод или конструктор (§15.12), значения фактических выражений аргументов инициализируют вновь созданные переменные параметра , каждый из объявленного типа, перед выполнением тела метода или конструктор. Идентификатор, который появляется в DeclaratorId, может использоваться как простое имя в теле метода или конструктора для ссылки на формальный параметр .

50
ответ дан George Paolo Flores 12 окт. '12 в 6:00 2012-10-12 06:00

Насколько я знаю, 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); } 

Это заполнит Hello World, а не World Hello, потому что в функции swap вы используете копии, которые не влияют на ссылки в main. Но если ваши объекты не являются неизменными, вы можете его изменить, например:

 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, он произведет только Hello, потому что String неизменен. Məsələn:

 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, подобную этой, которая позволила бы ей использовать ее со строками:

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

edit: я считаю, что это также причина использования StringBuffer, когда дело доходит до "добавления" двух строк, потому что вы можете модифицировать исходный объект, который не может быть с неизменяемыми объектами, такими как String.

50
ответ дан kukudas 02 апр. '09 в 0:33 2009-04-02 00:33

Java - это вызов по значению.

Как это работает

  • Вы всегда передаете копию бит значения ссылки!

  • Если это примитивный тип данных, эти биты содержат значение самого примитивного типа данных. Поэтому, если мы изменим значение заголовка внутри метода, то оно не отражает изменения вне.

  • Если это тип данных объекта, например Foo foo = new Foo(), то в этом случае копия адреса объекта проходит как ярлык файла, предположим, что у нас есть текстовый файл abc.txt на C:\desktop и предположим, что мы делаем ярлык тот же файл и поместите это внутри C:\desktop\abc-shortcut, поэтому, когда вы получаете доступ к файлу из C:\desktop\abc.txt и пишите 'Stack Overflow' и закрываете файл, и снова вы открываете файл из ярлыка, тогда вы write 'является крупнейшим онлайн-сообществом для программистов, чтобы узнать, что тогда полное изменение файла будет "on123.ru - это самое большое онлайн-сообщество для программистов, чтобы учиться", что означает, что не имеет значения, откуда вы открываете файл, каждый раз, когда мы обращались к нему тот же файл, здесь мы можем считать Foo в качестве файла и предположим, что foo хранится на адресе 123hd7h (исходный адрес, например C:\desktop\abc.txt ) и 234jdid (скопированный адрес, такой как C:\desktop\abc-shortcut, который фактически содержит исходный адрес файла внутри). Поэтому для лучшего понимания создайте ярлык и почувствуйте...

50
ответ дан Himanshu arora 08 апр. '17 в 8:51 2017-04-08 08:51

Позвольте мне попытаться объяснить свое понимание с помощью четырех примеров. 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);  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);  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:

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

 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 see Student student = new Student(10, "Nikhil"); String output = method(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] 
46
ответ дан spiderman 13 мая '15 в 0:24 2015-05-13 00:24

Вы никогда не можете передавать по ссылке в Java, и один из способов, который является очевидным, - это когда вы хотите вернуть более одного значения из вызова метода. Рассмотрим следующий бит кода в С++:

 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 будет содержать возвращаемое значение. Очевидно, вы можете сделать это другими способами, такими как структурирование вашего кода, чтобы это не было необходимо, или создание класса, который может содержать возвращаемое значение или разрешить его установку. Но простой шаблон, доступный вам в С++ выше, недоступен в Java.

44
ответ дан Jared Oberhaus 08 марта '09 в 9:28 2009-03-08 09:28

Я думал, что внес свой ответ, чтобы добавить более подробную информацию из Спецификаций.

Во-первых, В чем разница между передачей по ссылке или передачей по значению?

Передача по ссылке означает, что параметр вызываемых функций будет то же, что и переданный аргумент вызывающих (не значение, а тождество - сама переменная).

Передача по значению означает, что параметр вызываемых функций будет копией переданный аргумент вызывающих.

Или из wikipedia, на тему передачи по ссылке

В оценке по запросу (также называемой pass-by-reference), функция получает неявную ссылку на переменная, используемая как аргумент, а не копия ее значения. Эта обычно означает, что функция может изменять (т.е. назначать) переменная, используемая в качестве аргумента, - то, что будет видно ее вызывающей стороне.

И по вопросу пропущенного значения

В вызове по значению вычисляется выражение аргумента, а результирующее значение привязано к соответствующей переменной в функции [...]. Если функция или процедура могут присваивать значения ее параметрам назначается только локальная копия [...].

Во-вторых, нам нужно знать, что использует Java в своих методах. Спецификация языка Java содержит

При вызове метода или конструктора (§15.12), значения выражения фактического аргумента инициализируют вновь созданный параметр переменные , каждый из объявленного типа, перед выполнением тела метод или конструктор.

Таким образом, он присваивает (или связывает) значение аргумента соответствующей переменной параметра.

Каково значение аргумента?

Давайте рассмотрим ссылочные типы, Спецификация виртуальной машины Java сообщает

Существует три типа ссылочных типов : типы классов, типы массивов, и типы интерфейсов. Их значения - это ссылки на динамически созданные экземпляры классов, массивы или экземпляры классов или массивы, которые реализовать интерфейсы соответственно.

В Спецификация языка Java также указано

Опорные значения (часто просто ссылки) являются указателями на эти объекты и специальной нулевой ссылкой, которая ссылается на отсутствие объекта.

Значение аргумента (некоторого ссылочного типа) является указателем на объект. Обратите внимание, что переменная, вызов метода с типом возвращаемого типа ссылочного типа и выражение создания экземпляра ( 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 - это пропускная способность .

Тот факт, что вы можете следовать ссылке для вызова метода или доступа к полю ссылочного объекта, совершенно не имеет отношения к разговору. Определение pass-by-reference было

Это обычно означает, что функция может изменять (т.е. назначать) переменная, используемая в качестве аргумента, - то, что будет видно ее вызывающей стороне.

В Java изменение переменной означает ее перераспределение. В Java, если вы переназначили переменную в методе, она останется незамеченной для вызывающего. Изменение объекта, на которое ссылается переменная, - это совсем другое понятие.


Примитивные значения также определены в Спецификации виртуальной машины Java, здесь . Значение типа является соответствующим значением интеграла или с плавающей запятой, соответствующим образом закодированным (8, 16, 32, 64 и т.д.).

43
ответ дан Sotirios Delimanolis 03 июля '14 в 9:48 2014-07-03 09:48

Различие, или, может быть, только то, как я помню, поскольку я имел такое же впечатление, что и исходный плакат: Java всегда проходит по значению. Все объекты (в Java, все, кроме примитивов) в Java - это ссылки. Эти ссылки передаются по значению.

34
ответ дан shsteimer 03 сент. '08 в 22:42 2008-09-03 22:42

Как уже упоминалось ранее, Java всегда имеет значение pass-by-value

Вот еще один пример, который поможет вам понять разницу ( классический пример подкачки ):

 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 являются новыми локальными ссылочными переменными, которые имеют одинаковое значение переданных ссылок (они указывают на a и b соответственно). Таким образом, попытка изменить ссылки iA или iB будет меняться только в локальной области, а не вне этого метода.

32
ответ дан pek 03 сент. '08 в 23:01 2008-09-03 23:01

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(); } 
31
ответ дан Gaurav 10 июля '13 в 9:31 2013-07-10 09:31

В Java передаются только ссылки и передаются по значению:

Аргументы Java передаются по значению (ссылка копируется при использовании метода):

В случае примитивных типов поведение Java просто: Значение копируется в другом экземпляре примитивного типа.

В случае объектов это одно и то же: Переменные объекта - это указатели (ведра), содержащие только объекты адрес , которые были созданы с использованием "нового" ключевого слова и скопированы как примитивные типы.

Поведение может отличаться от поведения примитивов: поскольку скопированная объектная переменная содержит один и тот же адрес (к одному и тому же объекту) Объект content/members все еще может быть изменен в рамках метода и более поздний доступ снаружи, что дает иллюзию, что сам объект (содержащий) был передан по ссылке.

"String" Объекты кажутся идеальным встречным примером для городской легенды, говорящей, что "Объекты передаются по ссылке":

Фактически, в рамках метода, который вы никогда не сможете, обновите значение строки, переданной как аргумент:

Объект String содержит символы с объявленным массивом final , которые нельзя изменить. Только адрес объекта может быть заменен другим, используя "новый". Использование "нового" для обновления переменной не позволит объекту получить доступ извне, поскольку переменная была первоначально передана по значению и скопирована.

30
ответ дан user1767316 24 июля '16 в 11:29 2016-07-24 11:29

Я всегда думаю об этом как "проход по копии". Это копия значения, будь она примитивной или справочной. Если это примитив, это копия битов, которые являются значением, и если это объект, это копия ссылки.

 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

Примитивные классы-оболочки и строки неизменяемы, поэтому любой пример с использованием этих типов не будет работать так же, как другие типы/объекты.

29
ответ дан SWD 08 сент. '08 в 17:48 2008-09-08 17:48

Я создал поток, посвященный этим типам вопросов для любых языков программирования здесь .

Также упоминается Java . Вот краткое резюме:

  • Java передает свои параметры по значению
  • "по значению" - единственный способ в java передать параметр методу
  • использование методов из объекта, заданного как параметр, изменит объект в качестве точки ссылки на исходные объекты. (если это сам метод изменяет некоторые значения)
24
ответ дан sven 08 сент. '08 в 17:54 2008-09-08 17:54

Короче говоря, Java объекты имеют некоторые очень специфические свойства.

В общем, Java имеет примитивные типы ( int , bool , char , double и т.д.), которые передаются непосредственно по значению. Тогда у Java есть объекты (все, что происходит от java.> ). Объекты на самом деле всегда обрабатываются посредством ссылки (ссылка является указателем, который вы не можете коснуться). Это означает, что по сути объекты передаются по ссылке, так как ссылки обычно не интересны. Тем не менее, это означает, что вы не можете изменить, на какой объект указывается, поскольку сама ссылка передается по значению.

Это звучит странно и запутанно? Давайте рассмотрим, как C реализует передачу по ссылке и передает значение. В C по умолчанию принято передать значение. void foo(int x) передает значение int по значению. void foo(int *x) - это функция, которая не хочет int a , а указатель на int: foo(> . Это можно использовать с оператором > для передачи адреса переменной.

Возьмем это на С++, и у нас есть ссылки. Ссылки в основном (в этом контексте) синтаксического сахара, которые скрывают указательную часть уравнения: void foo(int > вызывается foo(a) , где сам компилятор знает, что это ссылка и адрес без ссылки a должен быть принят. В Java все переменные, относящиеся к объектам, фактически относятся к ссылочному типу, фактически вызывая вызов по ссылке для большинства целей и целей без мелкозернистого управления (и сложности), предоставляемого, например, С++.

23
ответ дан Paul de Vrieze 02 сент. '08 в 23:53 2008-09-02 23:53

Несколько поправок к некоторым сообщениям.

C не поддерживает передачу по ссылке. Он ВСЕГДА проходит по значению. С++ поддерживает передачу по ссылке, но не является по умолчанию и довольно опасен.

Не имеет значения, какое значение находится в Java: примитивный или адрес (грубо) объекта, он ВСЕГДА передается по значению.

Если объект Java ведет себя так, как будто он передается по ссылке, это свойство изменчивости и не имеет абсолютно никакого отношения к передающим механизмам.

Я не уверен, почему это так запутанно, возможно, потому, что так много "программистов" Java официально не обучены и, следовательно, не понимают, что действительно происходит в памяти?

23
ответ дан Rusty Shackleford 26 дек. '09 в 23:19 2009-12-26 23:19

Java передает параметры по VALUE и по значению ТОЛЬКО .

Сокращение длинного рассказа:

Для тех, кто приходит из С#: НЕТ параметра "out".

Для тех, кто приходит из PASCAL: НЕТ параметра "var" .

Это означает, что вы не можете изменить ссылку из самого объекта, но вы всегда можете изменить свойства объекта.

Обходным путем является использование параметра StringBuilder вместо String . И вы всегда можете использовать массивы!

21
ответ дан Christian 19 марта '15 в 0:35 2015-03-19 00:35

Другие вопросы по меткам или Задайте вопрос