Saya selalu berpikir Java adalah pass-by-reference .
Namun, saya telah melihat beberapa posting blog (misalnya, blog ini ) yang mengklaim tidak.
Saya rasa saya tidak mengerti perbedaan yang mereka buat.
Apa penjelasannya?
java
methods
parameter-passing
pass-by-reference
pass-by-value
pengguna4315
sumber
sumber
call-by-value
dancall-by-reference
berbeda dalam cara yang sangat penting . (Secara pribadi saya lebih suka menggunakancall-by-object-sharing
hari-hari inicall-by-value[-of-the-reference]
, karena ini menggambarkan semantik pada tingkat tinggi dan tidak membuat konflik dengancall-by-value
, yang merupakan implementasi yang mendasarinya.)swap(x,y)
tes .Jawaban:
Java selalu melewati nilai . Sayangnya, ketika kami melewatkan nilai suatu objek, kami meneruskan referensi untuk itu. Ini membingungkan bagi pemula.
Bunyinya seperti ini:
Dalam contoh di atas
aDog.getName()
masih akan kembali"Max"
. Nilai diaDog
dalammain
tidak diubah dalam fungsifoo
denganDog
"Fifi"
sebagai referensi objek dilewatkan oleh nilai. Jika disahkan dengan referensi, makaaDog.getName()
inmain
akan kembali"Fifi"
setelah panggilan kefoo
.Juga:
Dalam contoh di atas,
Fifi
adalah nama anjing setelah panggilan kefoo(aDog)
karena nama objek ditetapkan di dalamnyafoo(...)
. Setiap operasi yangfoo
berkinerjad
sedemikian rupa sehingga, untuk semua tujuan praktis, dilakukanaDog
, tetapi tidak mungkin untuk mengubah nilai variabelaDog
itu sendiri.sumber
Saya baru saja memperhatikan Anda mereferensikan artikel saya .
Java Spec mengatakan bahwa semua yang ada di Java adalah nilai per nilai. Tidak ada yang namanya "pass-by-reference" di Jawa.
Kunci untuk memahami ini adalah sesuatu seperti itu
adalah tidak Anjing; itu sebenarnya pointer ke Anjing.
Apa itu artinya, adalah ketika Anda memilikinya
Anda pada dasarnya menyampaikan alamat
Dog
objek yang dibuat kefoo
metode.(Saya katakan pada dasarnya karena pointer Java bukan alamat langsung, tetapi paling mudah untuk memikirkannya seperti itu)
Misalkan
Dog
objek berada di alamat memori 42. Ini berarti kita melewati 42 ke metode.jika Metode didefinisikan sebagai
mari kita lihat apa yang terjadi.
someDog
diatur ke nilai 42someDog
diikuti keDog
itu menunjuk ke (Dog
objek di alamat 42)Dog
(yang ada di alamat 42) diminta untuk mengubah namanya menjadi MaxDog
dibuat. Katakanlah dia di alamat 74someDog
ke 74Dog
menunjuk ke (Dog
objek di alamat 74)Dog
(yang di alamat 74) diminta untuk mengubah namanya menjadi RowlfSekarang mari kita pikirkan apa yang terjadi di luar metode:
Apakah ada
myDog
perubahan?Ada kuncinya.
Ingatlah bahwa itu
myDog
adalah pointer , dan bukan yang sebenarnyaDog
, jawabannya adalah TIDAK.myDog
masih memiliki nilai 42; masih menunjuk ke aslinyaDog
(tapi perhatikan bahwa karena baris "AAA", namanya sekarang "Max" - masih Anjing yang sama;myDog
nilainya tidak berubah.)Sangat valid untuk mengikuti alamat dan mengubah apa yang ada di akhir alamat itu; Namun, itu tidak mengubah variabel.
Java berfungsi persis seperti C. Anda dapat menetapkan sebuah pointer, meneruskan pointer ke suatu metode, mengikuti pointer dalam metode dan mengubah data yang diarahkan ke. Namun, Anda tidak dapat mengubah tempat penunjuk itu menunjuk.
Di C ++, Ada, Pascal dan bahasa lain yang mendukung pass-by-reference, Anda benar-benar dapat mengubah variabel yang diteruskan.
Jika Java memiliki semantik referensi-demi-referensi,
foo
metode yang kami definisikan di atas akan berubah ke manamyDog
menunjuk ketika ditugaskansomeDog
pada baris BBB.Pikirkan parameter referensi sebagai alias untuk variabel yang diteruskan. Ketika alias itu ditetapkan, begitu juga variabel yang dilewati.
sumber
Java selalu melewati argumen dengan nilai , BUKAN dengan referensi.
Izinkan saya menjelaskan ini melalui sebuah contoh :
Saya akan menjelaskan ini dalam langkah-langkah:
Mendeklarasikan referensi bernama
f
tipeFoo
dan menetapkan objek tipe baruFoo
dengan atribut"f"
.Dari sisi metode, referensi tipe
Foo
dengan namaa
dideklarasikan dan awalnya ditetapkannull
.Saat Anda memanggil metode
changeReference
, referensia
akan diberikan objek yang dilewatkan sebagai argumen.Mendeklarasikan referensi bernama
b
tipeFoo
dan menetapkan objek tipe baruFoo
dengan atribut"b"
.a = b
membuat tugas baru ke referensia
, bukanf
, dari objek yang atributnya adalah"b"
.Saat Anda memanggil
modifyReference(Foo c)
metode, referensic
dibuat dan ditetapkan objek dengan atribut"f"
.c.setAttribute("c");
akan mengubah atribut dari objek yangc
menunjuk ke sana, dan itu adalah objek yang sama yangf
menunjuk ke sana.Saya harap Anda mengerti sekarang bagaimana meneruskan objek sebagai argumen bekerja di Jawa :)
sumber
a
menunjuk ke objek yang sama denganf
(dan tidak pernah mendapatkan salinan sendiri dari objekf
menunjuk ke), setiap perubahan pada objek yang dibuat menggunakana
harus memodifikasif
juga (karena mereka berdua bekerja dengan objek yang sama ), jadi pada titik tertentua
harus mendapatkan salinan sendiri dari objekf
menunjuk ke.Ini akan memberi Anda beberapa wawasan tentang bagaimana Java benar-benar bekerja sampai-sampai dalam diskusi Anda berikutnya tentang Java yang lewat referensi atau lewat nilai Anda hanya akan tersenyum :-)
Langkah pertama harap hapus dari pikiran Anda bahwa kata itu dimulai dengan 'p' "_ _ _ _ _ _" ", terutama jika Anda berasal dari bahasa pemrograman lain. Java dan 'p' tidak dapat ditulis dalam buku, forum, atau bahkan txt yang sama.
Langkah kedua ingat bahwa ketika Anda melewatkan Obyek ke metode Anda melewati referensi Obyek dan bukan Obyek itu sendiri.
Sekarang pikirkan apa yang dilakukan referensi / variabel Obyek:
Berikut ini (tolong jangan mencoba untuk mengkompilasi / menjalankan ini ...):
Apa yang terjadi?
Sebuah gambar bernilai ribuan kata:
Perhatikan bahwa panah lainReferenceToTheSamePersonObject diarahkan ke Objek dan bukan ke orang variabel!
Jika Anda tidak mendapatkannya maka percayalah pada saya dan ingat bahwa lebih baik mengatakan bahwa Java adalah nilai tambah . Nah, sampaikan nilai referensi . Oh well, bahkan lebih baik adalah pass-by-copy-of-the-variable-value! ;)
Sekarang merasa bebas untuk membenci saya tetapi perhatikan bahwa mengingat ini tidak ada perbedaan antara melewati tipe data primitif dan Objek ketika berbicara tentang argumen metode.
Anda selalu memberikan salinan bit dari nilai referensi!
Tentu saja Anda dapat memotongnya dan katakan saja bahwa Java adalah nilai tambah!
sumber
public void foo(Car car){ ... }
,car
adalah lokal kefoo
dan berisi lokasi tumpukan Obyek? Jadi jika saya mengubahcar
nilainyacar = new Car()
, itu akan menunjuk ke Objek yang berbeda di heap? dan jika saya mengubah nilaicar
properti olehcar.Color = "Red"
, Object di heap yang ditunjuk olehcar
akan dimodifikasi. Juga, sama di C #? Tolong dibalas! Terima kasih!System.out.println(person.getName());
apa yang akan muncul? "Tom" atau "Jerry"? Ini adalah hal terakhir yang akan membantu saya mengatasi kebingungan ini.Java selalu lulus dengan nilai, tanpa pengecualian, selamanya .
Jadi bagaimana mungkin ada orang yang bingung dengan hal ini, dan percaya bahwa Java adalah pass by reference, atau berpikir mereka memiliki contoh Java bertindak sebagai pass by reference? Poin kuncinya adalah bahwa Java tidak pernah menyediakan akses langsung ke nilai-nilai objek itu sendiri , dalam keadaan apa pun . Satu-satunya akses ke objek adalah melalui referensi ke objek itu. Karena objek Java selalu diakses melalui referensi, bukan langsung, itu umum untuk berbicara tentang bidang dan variabel dan argumen metode sebagai objek , ketika secara Pedantis mereka hanya referensi ke objek .Kebingungan ini bermula dari perubahan nomenklatur ini (secara tegas, salah).
Jadi, saat memanggil metode
int
,,long
dll.), Nilai by pass adalah nilai aktual dari primitif (misalnya, 3).Jadi jika Anda memiliki
doSomething(foo)
danpublic void doSomething(Foo foo) { .. }
dua Foos telah menyalin referensi yang mengarah ke objek yang sama.Secara alami, memberikan nilai referensi ke objek terlihat sangat mirip (dan dalam praktiknya tidak bisa dibedakan dari) melewati objek dengan referensi.
sumber
Java melewati referensi berdasarkan nilai.
Jadi, Anda tidak dapat mengubah referensi yang diteruskan.
sumber
Saya merasa ingin berdebat tentang "pass-by-reference vs pass-by-value" tidak sangat membantu.
Jika Anda berkata, "Java adalah pass-by-anything (referensi / nilai)", dalam kedua kasus itu, Anda tidak memberikan jawaban yang lengkap. Berikut ini beberapa informasi tambahan yang diharapkan akan membantu memahami apa yang terjadi dalam memori.
Crash course on stack / heap sebelum kita sampai ke implementasi Java: Nilai-nilai berjalan dan keluar dari stack dengan cara tertib yang bagus, seperti tumpukan piring di kafetaria. Memori dalam tumpukan (juga dikenal sebagai memori dinamis) adalah acak dan tidak terorganisir. JVM hanya menemukan ruang di mana pun ia bisa, dan membebaskannya karena variabel yang menggunakannya tidak lagi diperlukan.
Baik. Pertama, primitif lokal menggunakan stack. Jadi kode ini:
hasil dalam ini:
Saat Anda mendeklarasikan dan membuat instance objek. Objek aktual berjalan di heap. Apa yang terjadi di tumpukan? Alamat objek pada heap. Pemrogram C ++ akan menyebutnya pointer, tetapi beberapa pengembang Java menentang kata "pointer". Masa bodo. Ketahuilah bahwa alamat objek berada di tumpukan.
Seperti itu:
Array adalah objek, jadi ia berjalan di heap juga. Dan bagaimana dengan objek dalam array? Mereka mendapatkan ruang tumpukan mereka sendiri, dan alamat setiap objek masuk ke dalam array.
Jadi, apa yang dilewatkan ketika Anda memanggil suatu metode? Jika Anda memasukkan objek, yang sebenarnya Anda lewati adalah alamat objek. Beberapa orang mungkin mengatakan "nilai" dari alamat tersebut, dan beberapa mengatakan itu hanya referensi ke objek. Ini adalah asal mula perang suci antara para pendukung "referensi" dan "nilai". Apa yang Anda sebut itu tidak sepenting yang Anda pahami bahwa apa yang diteruskan adalah alamat ke objek.
Satu String dibuat dan ruang untuk itu dialokasikan di heap, dan alamat ke string disimpan pada stack dan diberi pengenal
hisName
, karena alamat String kedua sama dengan yang pertama, tidak ada String baru yang dibuat dan tidak ada ruang heap baru yang dialokasikan, tetapi pengidentifikasi baru dibuat di stack. Kemudian kita memanggilshout()
: bingkai stack baru dibuat dan pengidentifikasi baru,name
dibuat dan diberi alamat dari String yang sudah ada.Jadi, nilai, referensi? Anda mengatakan "kentang".
sumber
Untuk menunjukkan kontrasnya, bandingkan cuplikan C ++ dan Java berikut :
Dalam C ++: Catatan: Kode buruk - kebocoran memori! Tapi itu menunjukkan intinya.
Di jawa
Java hanya memiliki dua jenis passing: dengan nilai untuk tipe bawaan, dan dengan nilai pointer untuk tipe objek.
sumber
Dog **objPtrPtr
contoh C ++, dengan begitu kita bisa memodifikasi apa yang pointer "tunjuk".Java meneruskan referensi ke objek berdasarkan nilai.
sumber
Pada dasarnya, menetapkan kembali parameter Objek tidak mempengaruhi argumen, misalnya,
akan mencetak
"Hah!"
alih-alihnull
. Alasan mengapa ini berhasil adalah karenabar
merupakan salinan dari nilaibaz
, yang hanya merupakan referensi"Hah!"
. Jika itu adalah referensi yang sebenarnya, makafoo
akan didefinisikan ulangbaz
menjadinull
.sumber
Saya tidak percaya bahwa belum ada yang menyebut Barbara Liskov. Ketika dia merancang CLU pada tahun 1974, dia mengalami masalah terminologi yang sama, dan dia menemukan istilah panggilan dengan berbagi (juga dikenal sebagai panggilan dengan berbagi objek dan panggilan dengan objek ) untuk kasus "panggilan berdasarkan nilai" khusus ini di mana nilainya referensi ".
sumber
Inti masalahnya adalah bahwa kata referensi dalam ungkapan "pass by reference" berarti sesuatu yang sama sekali berbeda dari arti biasa dari kata referensi di Jawa.
Biasanya dalam referensi Java berarti referensi ke objek . Tetapi istilah teknis yang dilewatkan oleh referensi / nilai dari teori bahasa pemrograman berbicara tentang referensi ke sel memori yang memegang variabel , yang merupakan sesuatu yang sangat berbeda.
sumber
Dalam java semuanya referensi, jadi ketika Anda memiliki sesuatu seperti:
Point pnt1 = new Point(0,0);
Java tidak mengikuti:Java tidak lulus argumen metode dengan referensi; itu melewati mereka dengan nilai. Saya akan menggunakan contoh dari situs ini :
Aliran program:
Membuat dua objek Point yang berbeda dengan dua referensi yang berbeda terkait.
Seperti yang diharapkan, output akan:
Pada baris ini 'pass-by-value' masuk ke dalam permainan ...
Referensi
pnt1
danpnt2
yang disahkan oleh nilai dengan metode rumit, yang berarti bahwa sekarang Anda referensipnt1
danpnt2
memiliki merekacopies
bernamaarg1
danarg2
Jadipnt1
danarg1
poin ke objek yang sama. (Sama untukpnt2
danarg2
)Dalam
tricky
metode:Selanjutnya dalam
tricky
metodeDi sini, Anda pertama kali membuat
temp
referensi Titik baru yang akan menunjuk pada tempat yang sama sepertiarg1
referensi. Kemudian Anda memindahkan referensiarg1
ke titik ke tempat yang sama sepertiarg2
referensi. Akhirnyaarg2
akan menunjuk ke tempat yang sama sukatemp
.Dari sini lingkup
tricky
metode hilang dan Anda tidak memiliki akses lagi ke referensi:arg1
,arg2
,temp
. Tapi catatan penting adalah bahwa semua yang Anda lakukan dengan referensi ini ketika mereka 'dalam hidup' secara permanen akan mempengaruhi objek yang mereka titik ke.Jadi setelah menjalankan metode
tricky
, ketika Anda kembali kemain
, Anda memiliki situasi ini:Jadi sekarang, eksekusi program sepenuhnya akan:
sumber
arg1.x = 1; arg1.y = 1; arg2.x = 2; arg2.y = 2;
, karena arg1 sekarang memegang referensi pnt2 dan arg2 memegang referensi sekarang pnt1, jadi, percetakannyaX1: 2 Y1: 2 X2: 1 Y2: 1
Java selalu lewat nilai, bukan lewat referensi
Pertama-tama, kita perlu memahami apa yang melewati nilai dan melewati referensi.
Pass by value berarti Anda membuat salinan dalam memori dari nilai parameter aktual yang dilewatkan. Ini adalah salinan konten dari parameter aktual .
Pass by reference (juga disebut pass by address) berarti bahwa salinan alamat dari parameter aktual disimpan .
Terkadang Java bisa memberikan ilusi pass by reference. Mari kita lihat cara kerjanya dengan menggunakan contoh di bawah ini:
Output dari program ini adalah:
Mari kita pahami langkah demi langkah:
Seperti yang kita semua tahu itu akan membuat objek di heap dan mengembalikan nilai referensi kembali ke t. Sebagai contoh, misalkan nilai t adalah
0x100234
(kita tidak tahu nilai internal JVM yang sebenarnya, ini hanya sebuah contoh).Ketika melewatkan referensi t ke fungsi, ia tidak akan langsung melewati nilai referensi aktual dari tes objek, tetapi akan membuat salinan t dan kemudian meneruskannya ke fungsi. Karena itu lewat nilai , itu melewati salinan variabel daripada referensi yang sebenarnya. Karena kita mengatakan nilai t adalah
0x100234
, baik t dan f akan memiliki nilai yang sama dan karenanya mereka akan menunjuk ke objek yang sama.Jika Anda mengubah sesuatu dalam fungsi menggunakan referensi f itu akan mengubah konten objek yang ada. Itu sebabnya kami mendapat output
changevalue
, yang diperbarui dalam fungsi.Untuk memahami ini dengan lebih jelas, perhatikan contoh berikut:
Apakah ini akan melempar
NullPointerException
? Tidak, karena hanya melewati salinan referensi. Dalam kasus lewat referensi, itu bisa melemparNullPointerException
, seperti yang terlihat di bawah ini:Semoga ini bisa membantu.
sumber
Referensi selalu merupakan nilai saat diwakili, tidak peduli bahasa apa yang Anda gunakan.
Untuk mendapatkan tampilan kotak di luar, mari kita lihat Majelis atau manajemen memori tingkat rendah. Pada level CPU referensi ke apa pun segera menjadi nilai jika itu ditulis ke memori atau ke salah satu register CPU. (Itulah sebabnya pointer adalah definisi yang baik. Ini adalah nilai, yang memiliki tujuan pada saat bersamaan).
Data dalam memori memiliki Lokasi dan di lokasi itu ada nilai (byte, kata, apa pun). Di Majelis, kami memiliki solusi yang mudah untuk memberikan Nama ke Lokasi tertentu (alias variabel), tetapi saat menyusun kode, assembler hanya mengganti Nama dengan lokasi yang ditentukan seperti browser Anda mengganti nama domain dengan alamat IP.
Sampai ke inti, secara teknis tidak mungkin untuk meneruskan referensi ke apa pun dalam bahasa apa pun tanpa mewakilinya (saat itu langsung menjadi nilai).
Katakanlah kita memiliki variabel Foo, yang Lokasi adalah di byte 47 dalam memori dan yang Nilai adalah 5. Kami memiliki variabel lain Ref2Foo yang di 223 byte dalam memori, dan nilainya akan 47. Ref2Foo ini mungkin menjadi variabel teknis , tidak secara eksplisit dibuat oleh program. Jika Anda hanya melihat 5 dan 47 tanpa informasi lain, Anda hanya akan melihat dua Nilai . Jika Anda menggunakannya sebagai referensi maka untuk mencapai
5
kita harus bepergian:Ini adalah cara kerja jump-tables.
Jika kita ingin memanggil metode / fungsi / prosedur dengan nilai Foo, ada beberapa cara untuk meneruskan variabel ke metode, tergantung pada bahasa dan beberapa mode pemanggilan metode:
Dalam setiap kasus di atas nilai - salinan nilai yang ada - telah dibuat, sekarang tergantung pada metode penerima untuk menanganinya. Ketika Anda menulis "Foo" di dalam metode, itu dibaca dari EAX, atau secara otomatis ditinjau ulang , atau digandakan, proses tergantung pada bagaimana bahasa bekerja dan / atau apa yang menentukan jenis Foo. Ini disembunyikan dari pengembang sampai dia menghindari proses dereferencing. Jadi referensi adalah nilai ketika direpresentasikan, karena referensi adalah nilai yang harus diproses (pada tingkat bahasa).
Sekarang kami telah mengirimkan Foo ke metode:
Foo = 9
) itu hanya memengaruhi lingkup lokal karena Anda memiliki salinan Nilai. Dari dalam metode kita bahkan tidak dapat menentukan di mana dalam memori Foo asli berada.Foo = 11
), itu bisa mengubah Foo secara global (tergantung pada bahasa, mis. Java atau seperti Pascal'sprocedure findMin(x, y, z: integer;
var m: integer);
). Namun jika bahasa tersebut memungkinkan Anda untuk menghindari proses dereference, Anda dapat mengubah47
, katakan saja49
. Pada titik itu Foo tampaknya telah diubah jika Anda membacanya, karena Anda telah mengubah pointer lokal ke sana. Dan jika Anda memodifikasi Foo ini di dalam metode (Foo = 12
) Anda mungkin akan FUBAR pelaksanaan program (alias. Segfault) karena Anda akan menulis ke memori yang berbeda dari yang diharapkan, Anda bahkan dapat memodifikasi area yang ditakdirkan untuk dapat dieksekusi program dan menulis untuk itu akan mengubah kode yang sedang berjalan (Foo sekarang tidak di47
). TAPI nilai Foo dari47
tidak berubah secara global, hanya yang ada di dalam metode, karena47
juga merupakan salinan dari metode tersebut.223
di dalam metode itu menciptakan kekacauan yang sama seperti dalam 3. atau 4. (sebuah pointer, menunjuk ke nilai sekarang buruk, yang lagi-lagi digunakan sebagai pointer) tetapi ini masih lokal masalah, karena 223 disalin . Namun jika Anda dapat melakukan dereferensiRef2Foo
(yaitu223
), menjangkau dan memodifikasi nilai rujukan47
, katakan, untuk49
, itu akan mempengaruhi Foo secara global , karena dalam kasus ini metode mendapatkan salinan223
tetapi yang direferensikan47
hanya ada satu kali, dan mengubah itu untuk49
akan menyebabkan setiapRef2Foo
dereferensi ganda ke nilai yang salah.Nitpicking pada detail yang tidak penting, bahkan bahasa yang melakukan pass-by-reference akan memberikan nilai pada fungsi, tetapi fungsi tersebut tahu bahwa mereka harus menggunakannya untuk tujuan dereferencing. Pass-the-reference-as-value ini hanya disembunyikan dari programmer karena praktis tidak berguna dan terminologinya hanya pass-by-reference .
Nilai pass-by- ketat yang ketat juga tidak berguna, itu berarti bahwa array 100 Mbyte harus disalin setiap kali kita memanggil metode dengan array sebagai argumen, oleh karena itu Java tidak dapat dengan mudah melewati nilai demi nilai. Setiap bahasa akan meneruskan referensi ke array besar ini (sebagai nilai) dan menggunakan mekanisme copy-on-write jika array itu dapat diubah secara lokal di dalam metode atau memungkinkan metode (seperti yang dilakukan Java) untuk memodifikasi array secara global (dari tampilan pemanggil) dan beberapa bahasa memungkinkan untuk mengubah Nilai referensi itu sendiri.
Jadi, singkatnya dan dalam terminologi Java sendiri, Java adalah pass-by-value di mana nilainya bisa: nilai nyata atau nilai yang merupakan representasi dari referensi .
sumber
addr
apakah itu tidak diketik,@
apakah itu dengan jenis, dan Anda dapat memodifikasi variabel yang dirujuk kemudian (kecuali yang disalin secara lokal). Tapi saya tidak mengerti mengapa Anda melakukan itu. Secarik kertas dalam contoh Anda (Objek # 24601) adalah referensi, tujuannya adalah untuk membantu menemukan array dalam memori, itu tidak mengandung data array itu sendiri. Jika Anda me-restart program Anda, array yang sama mungkin mendapatkan objek-id yang berbeda bahkan jika isinya akan sama seperti yang telah di jalankan sebelumnya.AtomicReference
dan tidak memaparkan referensi maupun targetnya, tetapi sebaliknya memasukkan metode untuk melakukan sesuatu terhadap target; begitu kode yang dilewati objek kembali,AtomicReference
[yang penciptanya menyimpan referensi langsung] harus tidak valid dan ditinggalkan. Itu akan memberikan semantik yang tepat, tetapi itu akan lambat dan menjengkelkan.Java adalah panggilan berdasarkan nilai
Bagaimana itu bekerja
Anda selalu memberikan salinan bit dari nilai referensi!
Jika itu tipe data primitif, bit-bit ini mengandung nilai tipe data primitif itu sendiri, Itu sebabnya jika kita mengubah nilai header di dalam metode maka itu tidak mencerminkan perubahan di luar.
Jika ini adalah tipe data objek seperti Foo foo = new Foo () maka dalam hal ini salinan alamat objek yang dilewati seperti pintasan file, misalkan kita memiliki file teks abc.txt di C: \ desktop dan anggaplah kita membuat pintasan dari file yang sama dan letakkan ini di dalam C: \ desktop \ abc-pintas jadi ketika Anda mengakses file dari C: \ desktop \ abc.txt dan tulis 'Stack Overflow' dan tutup file dan lagi Anda membuka file dari shortcut maka Anda tulis 'adalah komunitas online terbesar yang harus dipelajari oleh programmer' maka perubahan total file adalah 'Stack Overflow adalah komunitas online terbesar yang dipelajari oleh programmer'yang berarti tidak masalah dari mana Anda membuka file, setiap kali kami mengakses file yang sama, di sini kita dapat menganggap Foo sebagai file dan menganggap foo disimpan pada 123hd7h (alamat asli seperti C: \ desktop \ abc.txt ) address dan 234jdid (alamat yang disalin seperti C: \ desktop \ abc-shortcut yang sebenarnya berisi alamat asli file di dalamnya) .. Jadi untuk pemahaman yang lebih baik buat file shortcut dan rasakan ..
sumber
Sudah ada jawaban bagus yang membahas hal ini. Saya ingin membuat kontribusi kecil dengan membagikan contoh yang sangat sederhana (yang akan mengkompilasi) yang membedakan perilaku antara Pass-by-reference dalam c ++ dan Pass-by-value di Jawa.
Beberapa poin:
C ++ lulus dengan contoh referensi:
Java meneruskan "referensi Java" dengan contoh nilai
EDIT
Beberapa orang menulis komentar yang sepertinya mengindikasikan bahwa mereka tidak melihat contoh saya atau mereka tidak mendapatkan contoh c ++. Tidak yakin di mana putuskan, tetapi menebak contoh c ++ tidak jelas. Saya memposting contoh yang sama dalam pascal karena saya pikir pass-by-reference terlihat lebih bersih dalam pascal, tetapi saya bisa saja salah. Saya mungkin lebih membingungkan orang; Saya harap tidak.
Dalam pascal, parameter yang diteruskan oleh referensi disebut "parameter var". Dalam prosedur setToNil di bawah ini, harap perhatikan kata kunci 'var' yang mendahului parameter 'ptr'. Ketika sebuah pointer diteruskan ke prosedur ini, itu akan diteruskan dengan referensi . Perhatikan tingkah lakunya: ketika prosedur ini menyetel ptr ke nil (itu berbicara pascal untuk NULL), itu akan menetapkan argumen ke nil - Anda tidak dapat melakukannya di Jawa.
EDIT 2
Beberapa kutipan dari "THE Java Programming Language" oleh Ken Arnold, James Gosling (orang yang menemukan Java) , dan David Holmes, bab 2, bagian 2.6.5
Dia melanjutkan untuk membuat titik yang sama tentang benda. . .
Dan menjelang akhir bagian yang sama ia membuat pernyataan yang lebih luas tentang java yang hanya lewat nilai dan tidak pernah lewat referensi.
Bagian buku ini memiliki penjelasan yang bagus tentang parameter yang lewat di Jawa dan perbedaan antara pass-by-reference dan pass-by-value dan oleh pencipta Java. Saya akan mendorong siapa pun untuk membacanya, terutama jika Anda masih tidak yakin.
Saya pikir perbedaan antara kedua model sangat halus dan kecuali Anda telah melakukan pemrograman di mana Anda benar-benar menggunakan pass-by-reference, mudah untuk kehilangan di mana dua model berbeda.
Saya harap ini menyelesaikan perdebatan, tapi mungkin tidak.
EDIT 3
Saya mungkin sedikit terobsesi dengan posting ini. Mungkin karena saya merasa bahwa pembuat Java secara tidak sengaja menyebarkan informasi yang salah. Jika alih-alih menggunakan kata "referensi" untuk petunjuk yang mereka gunakan sesuatu yang lain, katakanlah dingleberry, tidak akan ada masalah. Anda bisa mengatakan, "Java melewati dingleberry dengan nilai dan bukan dengan referensi", dan tidak ada yang akan bingung.
Itulah alasan mengapa hanya pengembang Java yang bermasalah dengan ini. Mereka melihat kata "referensi" dan berpikir mereka tahu persis apa artinya itu, sehingga mereka bahkan tidak repot-repot mempertimbangkan argumen yang berlawanan.
Ngomong-ngomong, saya perhatikan komentar di posting lama, yang membuat analogi balon yang sangat saya sukai. Sedemikian rupa sehingga saya memutuskan untuk merekatkan beberapa clip-art untuk membuat satu set kartun untuk menggambarkan intinya.
Melewati referensi dengan nilai - Perubahan ke referensi tidak tercermin dalam ruang lingkup pemanggil, tetapi perubahan pada objek. Ini karena referensi disalin, tetapi yang asli dan salinan merujuk ke objek yang sama.
Lulus dengan referensi - Tidak ada salinan referensi. Referensi tunggal dibagikan oleh penelepon dan fungsi yang dipanggil. Setiap perubahan pada referensi atau data Objek tercermin dalam ruang lingkup pemanggil.
EDIT 4
Saya telah melihat posting pada topik ini yang menggambarkan implementasi level rendah dari passing parameter di Jawa, yang menurut saya hebat dan sangat membantu karena membuat ide abstrak menjadi konkret. Namun, bagi saya pertanyaannya lebih tentang perilaku yang dijelaskan dalam spesifikasi bahasa daripada tentang implementasi teknis dari perilaku tersebut. Ini adalah kutipan dari Spesifikasi Bahasa Jawa, bagian 8.4.1 :
Yang berarti, java membuat salinan dari parameter yang diteruskan sebelum menjalankan suatu metode. Seperti kebanyakan orang yang belajar compiler di perguruan tinggi, saya menggunakan "The Dragon Buku" yang merupakan THE kompiler buku. Ini memiliki deskripsi yang baik tentang "Nilai-panggilan-" dan "Referensi-berdasarkan-panggilan" di Bab 1. Deskripsi panggilan-menurut-nilai cocok dengan Java Specs dengan tepat.
Kembali ketika saya mempelajari kompiler-di 90-an, saya menggunakan edisi pertama buku dari tahun 1986 yang pra-tanggal Jawa sekitar 9 atau 10 tahun. Namun, saya hanya menemukan salinan Edisi ke - 2 dari tahun 2007 yang sebenarnya menyebutkan Java! Bagian 1.6.6 yang berlabel "Mekanisme Passing Parameter" menggambarkan parameter yang lewat dengan cukup baik. Berikut adalah kutipan di bawah judul "Call-by-value" yang menyebutkan Java:
sumber
Sejauh yang saya tahu, Java hanya tahu panggilan berdasarkan nilai. Ini berarti untuk tipe data primitif Anda akan bekerja dengan salinan dan untuk objek Anda akan bekerja dengan salinan referensi ke objek. Namun saya pikir ada beberapa jebakan; misalnya, ini tidak akan berfungsi:
Ini akan mengisi Hello World dan bukan World Hello karena dalam fungsi swap Anda menggunakan copys yang tidak berdampak pada referensi di main. Tetapi jika objek Anda tidak berubah, Anda dapat mengubahnya misalnya:
Ini akan mengisi Hello World di baris perintah. Jika Anda mengubah StringBuffer menjadi String, itu hanya akan menghasilkan Halo karena String tidak dapat diubah. Sebagai contoh:
Namun Anda dapat membuat pembungkus untuk String seperti ini yang akan membuatnya dapat menggunakannya dengan Strings:
sunting: saya percaya ini juga alasan untuk menggunakan StringBuffer ketika datang untuk "menambahkan" dua Strings karena Anda dapat memodifikasi objek asli yang Anda tidak dapat dengan objek abadi seperti String.
sumber
swap(a, b)
yang (1) bertukara
danb
dari POV pemanggil, (2) adalah tipe-agnostik sejauh memungkinkan pengetikan statis (artinya menggunakannya dengan jenis lain tidak memerlukan apa pun selain mengubah jenis yang dinyatakana
danb
) , dan (3) tidak mengharuskan penelepon untuk secara eksplisit melewati pointer atau nama, maka bahasa mendukung lewat referensi.Tidak, ini bukan referensi.
Java dinyatakan bernilai menurut Spesifikasi Bahasa Jawa:
sumber
Biarkan saya mencoba menjelaskan pengertian saya dengan bantuan empat contoh. Java adalah pass-by-value, dan bukan pass-by-reference
/ **
Lewati Nilai
Di Jawa, semua parameter dilewatkan oleh nilai, yaitu menetapkan argumen metode tidak terlihat oleh pemanggil.
* /
Contoh 1:
Hasil
Contoh 2:
/ ** * * Nilai Pass By * * /
Hasil
Contoh 3:
/ ** 'Nilai Lulus Ini memiliki perasaan' Lulus Referensi '
Beberapa orang mengatakan tipe primitif dan 'String' adalah 'pass by value' dan objek adalah 'pass by reference'.
Tetapi dari contoh ini, kita dapat memahami bahwa itu hanya lewat nilai, mengingat bahwa di sini kita meneruskan referensi sebagai nilai. yaitu: referensi dilewatkan oleh nilai. Itu sebabnya mampu berubah dan tetap berlaku setelah lingkup lokal. Tetapi kami tidak dapat mengubah referensi aktual di luar lingkup asli. apa artinya itu ditunjukkan oleh contoh PassByValueObjectCase2 berikutnya.
* /
Hasil
Contoh 4:
/ **
Selain apa yang disebutkan dalam Example3 (PassByValueObjectCase1.java), kami tidak dapat mengubah referensi aktual di luar ruang lingkup asli. "
Catatan: Saya tidak menempelkan kode untuk
private class Student
. Definisi kelas untukStudent
sama dengan Example3.* /
Hasil
sumber
Anda tidak pernah dapat melewati referensi di Java, dan salah satu cara yang jelas adalah ketika Anda ingin mengembalikan lebih dari satu nilai dari pemanggilan metode. Pertimbangkan sedikit kode berikut dalam C ++:
Terkadang Anda ingin menggunakan pola yang sama di Jawa, tetapi Anda tidak bisa; setidaknya tidak secara langsung. Sebaliknya Anda bisa melakukan sesuatu seperti ini:
Seperti yang dijelaskan dalam jawaban sebelumnya, di Jawa Anda memberikan pointer ke array sebagai nilai
getValues
. Itu sudah cukup, karena metode ini kemudian memodifikasi elemen array, dan dengan konvensi Anda mengharapkan elemen 0 mengandung nilai kembali. Jelas Anda bisa melakukan ini dengan cara lain, seperti menyusun kode Anda jadi ini tidak perlu, atau membangun kelas yang bisa berisi nilai balik atau membiarkannya diatur. Tetapi pola sederhana yang tersedia untuk Anda di C ++ di atas tidak tersedia di Jawa.sumber
Saya pikir saya akan berkontribusi jawaban ini untuk menambahkan lebih detail dari Spesifikasi.
Pertama, Apa perbedaan antara passing dengan referensi vs passing dengan nilai?
Atau dari wikipedia, dengan subjek pass-by-reference
Dan tentang masalah pass-by-value
Kedua, kita perlu tahu apa yang digunakan Java dalam doa metodenya. The Java Language Specification negara
Jadi itu memberikan (atau mengikat) nilai argumen ke variabel parameter yang sesuai.
Apa nilai argumennya?
Mari kita pertimbangkan jenis referensi, Spesifikasi Java Virtual Machine menyatakan
The Java Language Specification juga menyatakan
Nilai argumen (dari beberapa jenis referensi) adalah penunjuk ke objek. Perhatikan bahwa variabel, doa metode dengan tipe pengembalian tipe referensi, dan ekspresi pembuatan instance (
new ...
) semua menyelesaikan ke nilai tipe referensi.Begitu
semua mengikat nilai referensi ke
String
instance ke parameter metode yang baru dibuatparam
,. Inilah yang dijelaskan oleh definisi pass-by-value. Dengan demikian, Java adalah pass-by-value .Fakta bahwa Anda dapat mengikuti referensi untuk memanggil metode atau mengakses bidang objek yang direferensikan sama sekali tidak relevan dengan percakapan. Definisi pass-by-reference adalah
Di Jawa, memodifikasi variabel berarti menugaskannya kembali. Di Jawa, jika Anda menugaskan kembali variabel dalam metode, itu akan luput dari perhatian ke pemanggil. Memodifikasi objek yang dirujuk oleh variabel adalah konsep yang sama sekali berbeda.
Nilai-nilai primitif juga didefinisikan dalam Spesifikasi Mesin Virtual Java, di sini . Nilai tipe adalah nilai integral atau floating point yang sesuai, dikodekan dengan tepat (8, 16, 32, 64, dll. Bit).
sumber
Di Jawa, hanya referensi yang diteruskan dan diberikan nilai:
Argumen Java semuanya diteruskan oleh nilai (referensi disalin saat digunakan oleh metode):
Dalam kasus tipe primitif, perilaku Java sederhana: Nilai disalin dalam contoh lain dari tipe primitif.
Dalam kasus Objects, ini adalah sama: Variabel objek adalah pointer (ember) yang hanya memegang alamat Obyek yang dibuat menggunakan kata kunci "baru", dan disalin seperti tipe primitif.
Perilaku dapat muncul berbeda dari tipe primitif: Karena variabel objek yang disalin berisi alamat yang sama (ke Objek yang sama). Objek konten / anggota mungkin masih dimodifikasi dalam metode dan akses kemudian luar, memberikan ilusi bahwa (yang mengandung) Object itu sendiri disahkan oleh referensi.
Objek "String" tampaknya menjadi contoh yang bagus untuk legenda urban yang mengatakan bahwa "Objek dilewatkan dengan referensi":
Akibatnya, menggunakan metode, Anda tidak akan pernah bisa, untuk memperbarui nilai String yang dilewatkan sebagai argumen:
Objek String, menampung karakter dengan array yang dinyatakan final yang tidak dapat dimodifikasi. Hanya alamat Objek yang dapat digantikan oleh yang lain menggunakan "baru". Menggunakan "baru" untuk memperbarui variabel, tidak akan membiarkan Objek diakses dari luar, karena variabel awalnya disahkan oleh nilai dan disalin.
sumber
strParam.setChar ( i, newValue )
. Yang mengatakan, string, seperti hal lain, dilewatkan oleh nilai dan, karena String adalah tipe non-primitif, nilai itu adalah referensi ke hal yang dibuat dengan yang baru, dan Anda bisa memeriksanya dengan menggunakan String.intern () .Perbedaannya, atau mungkin hanya dengan cara yang saya ingat seperti dulu berada di bawah kesan yang sama dengan poster aslinya adalah ini: Jawa selalu dinilai berdasarkan nilai. Semua objek (di Jawa, apa pun kecuali primitif) di Jawa adalah referensi. Referensi-referensi ini diteruskan oleh nilai.
sumber
Seperti yang banyak orang sebutkan sebelumnya, Java selalu dianggap sebagai nilai tambah
Berikut adalah contoh lain yang akan membantu Anda memahami perbedaannya ( contoh pertukaran klasik ):
Cetakan:
Ini terjadi karena iA dan iB adalah variabel referensi lokal baru yang memiliki nilai yang sama dari referensi yang diteruskan (mereka masing-masing menunjuk ke a dan b). Jadi, mencoba mengubah referensi iA atau iB hanya akan berubah dalam cakupan lokal dan tidak di luar metode ini.
sumber
Java hanya memiliki nilai by pass. Contoh yang sangat sederhana untuk memvalidasi ini.
sumber
obj
(null
) diteruskan keinit
, bukan referensi keobj
.Saya selalu menganggapnya sebagai "pass by copy". Ini adalah salinan nilai baik primitif atau referensi. Jika itu adalah primitif itu adalah salinan dari bit yang merupakan nilainya dan jika itu adalah Obyek itu adalah salinan referensi.
output dari java PassByCopy:
Kelas pembungkus primitif dan String tidak dapat diubah sehingga contoh apa pun yang menggunakan tipe-tipe itu tidak akan berfungsi sama dengan tipe / objek lainnya.
sumber
Tidak seperti beberapa bahasa lain, Java tidak memungkinkan Anda untuk memilih antara pass-by-value dan pass-by-reference — semua argumen dilewati oleh nilai. Pemanggilan metode dapat meneruskan dua jenis nilai ke metode — salinan nilai primitif (mis., Nilai int dan dobel) dan salinan referensi ke objek.
Ketika metode memodifikasi parameter tipe primitif, perubahan pada parameter tidak berpengaruh pada nilai argumen asli dalam metode pemanggilan.
Ketika datang ke objek, objek itu sendiri tidak dapat diteruskan ke metode. Jadi kami melewati referensi (alamat) objek. Kami dapat memanipulasi objek asli menggunakan referensi ini.
Bagaimana Java membuat dan menyimpan objek: Ketika kami membuat objek, kami menyimpan alamat objek dalam variabel referensi. Mari kita menganalisis pernyataan berikut.
"Akun akun1" adalah jenis dan nama variabel referensi, "=" adalah operator penugasan, "baru" meminta jumlah ruang yang diperlukan dari sistem. Konstruktor di sebelah kanan kata kunci baru yang membuat objek disebut secara implisit oleh kata kunci baru. Alamat objek yang dibuat (hasil nilai kanan, yang merupakan ekspresi yang disebut "ekspresi instance kelas") ditugaskan ke nilai kiri (yang merupakan variabel referensi dengan nama dan tipe yang ditentukan) menggunakan operator yang ditugaskan.
Meskipun referensi objek dilewatkan oleh nilai, suatu metode masih dapat berinteraksi dengan objek yang direferensikan dengan memanggil metode publik menggunakan salinan referensi objek. Karena referensi yang disimpan dalam parameter adalah salinan referensi yang dilewatkan sebagai argumen, parameter dalam metode yang dipanggil dan argumen dalam metode panggilan merujuk ke objek yang sama dalam memori.
Melewati referensi ke array, bukan objek array itu sendiri, masuk akal karena alasan kinerja. Karena semua yang ada di Jawa diteruskan oleh nilai, jika objek array dilewatkan, salinan dari setiap elemen akan diteruskan. Untuk array besar, ini akan menghabiskan waktu dan menghabiskan banyak penyimpanan untuk salinan elemen.
Pada gambar di bawah ini Anda dapat melihat kami memiliki dua variabel referensi (Ini disebut pointer di C / C ++, dan saya pikir istilah itu membuatnya lebih mudah untuk memahami fitur ini.) Dalam metode utama. Variabel primitif dan referensi disimpan dalam memori tumpukan (sisi kiri pada gambar di bawah). variabel referensi array1 dan array2 "titik" (seperti yang disebut programmer C / C ++) atau masing-masing referensi ke a dan b, yang merupakan objek (nilai yang dipegang variabel referensi ini adalah alamat objek) dalam memori tumpukan (sisi kanan pada gambar di bawah) .
Jika kita meneruskan nilai variabel referensi array1 sebagai argumen ke metode reverseArray, variabel referensi dibuat dalam metode dan variabel referensi mulai menunjuk ke array yang sama (a).
Jadi, jika kita katakan
dalam metode reverseArray, itu akan membuat perubahan dalam array a.
Kami memiliki variabel referensi lain dalam metode reverseArray (array2) yang menunjuk ke sebuah array c. Jika kita mengatakannya
dalam metode reverseArray, maka array variabel referensi1 dalam metode reverseArray akan berhenti menunjuk ke array a dan mulai menunjuk ke array c (garis putus-putus pada gambar kedua).
Jika kita mengembalikan nilai array variabel referensi2 sebagai nilai balik metode reverseArray dan menetapkan nilai ini ke variabel referensi array1 dalam metode utama, array1 pada main akan mulai menunjuk ke array c.
Jadi mari kita tuliskan semua hal yang telah kita lakukan sekaligus sekarang.
Dan sekarang metode reverseArray selesai, variabel referensi (array1 dan array2) hilang. Yang berarti kita sekarang hanya memiliki dua variabel referensi dalam metode utama array1 dan array2 yang masing-masing mengarah ke array c dan b. Tidak ada variabel referensi yang menunjuk ke objek (array) a. Jadi memenuhi syarat untuk pengumpulan sampah.
Anda juga bisa menetapkan nilai array2 di main ke array1. array1 akan mulai menunjuk ke b.
sumber
Saya telah membuat utas yang ditujukan untuk pertanyaan semacam ini untuk bahasa pemrograman apa pun di sini .
Java juga disebutkan . Berikut ini ringkasan singkatnya:
sumber
Singkatnya, objek Java memiliki beberapa properti yang sangat aneh.
Secara umum, Java memiliki tipe primitif (
int
,bool
,char
,double
, dll) yang disahkan langsung oleh nilai. Kemudian Java memiliki objek (semua yang berasal darijava.lang.Object
). Objek sebenarnya selalu ditangani melalui referensi (referensi menjadi pointer yang tidak dapat Anda sentuh). Itu berarti bahwa pada dasarnya, objek dilewatkan dengan referensi, karena referensi biasanya tidak menarik. Namun itu berarti bahwa Anda tidak dapat mengubah objek yang ditunjuk sebagai referensi itu sendiri dilewatkan oleh nilai.Apakah ini terdengar aneh dan membingungkan? Mari kita pertimbangkan bagaimana C mengimplementasikan pass by reference dan pass by value. Dalam C, konvensi standar adalah nilai lewat.
void foo(int x)
melewati int dengan nilai.void foo(int *x)
adalah fungsi yang tidak inginint a
, tetapi pointer ke int:foo(&a)
. Orang akan menggunakan ini dengan&
operator untuk mengirimkan alamat variabel.Bawa ini ke C ++, dan kami punya referensi. Referensi pada dasarnya (dalam konteks ini) gula sintaksis yang menyembunyikan bagian penunjuk persamaan:
void foo(int &x)
dipanggil olehfoo(a)
, di mana kompiler itu sendiri tahu bahwa itu adalah referensi dan alamat non-referensia
harus dilewatkan. Di Jawa, semua variabel yang merujuk ke objek sebenarnya dari tipe referensi, sebagai akibatnya memaksa panggilan dengan referensi untuk sebagian besar maksud dan tujuan tanpa kontrol berbutir halus (dan kompleksitas) yang diberikan oleh, misalnya, C ++.sumber