Saya punya pertanyaan sederhana tentang string di Jawa. Segmen kode sederhana berikut hanya menggabungkan dua string dan kemudian membandingkannya ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Ekspresi perbandingan concat=="string"
kembali false
sebagai jelas (saya mengerti perbedaan antara equals()
dan ==
).
Ketika dua string ini dinyatakan final
seperti itu,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Ekspresi perbandingan concat=="string"
, dalam hal ini kembali true
. Mengapa final
membuat perbedaan? Apakah itu harus melakukan sesuatu dengan kolam intern atau aku hanya disesatkan?
equals()
dan==
dalam konteks string, dan mengajukan pertanyaan yang lebih bermakna.String
? Saya pikir itu sangat logis, tidak konyol, untuk memiliki perbandingan konten dilakukan olehequals
, metode yang bisa kita timpa ketika kita menganggap dua objek sama, dan memiliki perbandingan identitas dilakukan oleh==
. Jika perbandingan konten dilakukan oleh==
kami tidak dapat mengesampingkannya untuk mendefinisikan apa yang kami maksud dengan "konten yang sama", dan memiliki artiequals
dan==
dibalik hanya untukString
s akan konyol. Selain itu, terlepas dari ini, saya tetap tidak melihat keuntungan dalam==
melakukan perbandingan kontenequals
.Jawaban:
Ketika Anda mendeklarasikan variabel
String
(yang tidak dapat diubah ) sebagaifinal
, dan menginisialisasinya dengan ekspresi konstanta waktu kompilasi, ia juga menjadi ekspresi konstanta waktu kompilasi, dan nilainya digarisbawahi oleh kompiler di mana ia digunakan. Jadi, dalam contoh kode kedua Anda, setelah menguraikan nilai-nilai, rangkaian string diterjemahkan oleh kompiler ke:yang bila dibandingkan
"string"
akan memberi Andatrue
, karena string literal diinternir .Dari JLS §4.12.4 -
final
Variabel :Juga dari JLS §15.28 - Ekspresi Konstan:
Ini tidak terjadi dalam contoh kode pertama Anda, di mana
String
variabel tidakfinal
. Jadi, mereka bukan ekspresi konstan waktu kompilasi. Operasi penggabungan di sana akan ditunda hingga runtime, sehingga mengarah ke penciptaanString
objek baru . Anda dapat memverifikasi ini dengan membandingkan kode byte kedua kode.Contoh kode pertama (bukan
final
versi) dikompilasi ke kode byte berikut:Jelas itu menyimpan
str
daning
dalam dua variabel terpisah, dan menggunakanStringBuilder
untuk melakukan operasi penggabungan.Sedangkan, contoh kode kedua Anda (
final
versi) terlihat seperti ini:Jadi secara langsung inline variabel akhir untuk membuat String
string
pada waktu kompilasi, yang dimuat olehldc
operasi pada langkah0
. Kemudian literal string kedua dimuat olehldc
operasi dalam langkah7
. Itu tidak melibatkan pembuatanString
objek baru saat runtime. String sudah dikenal pada waktu kompilasi, dan mereka diinternir.sumber
true
?String
objek yang baru dibuat (§12.5) kecuali ekspresi adalah ekspresi konstan (§15.28). ”Secara harfiah, menerapkan optimasi dalam versi non-final tidak diperbolehkan, karena string“ baru dibuat ”harus memiliki identitas objek yang berbeda. Saya tidak tahu apakah ini disengaja. Bagaimanapun, strategi kompilasi saat ini adalah untuk mendelegasikan ke fasilitas runtime yang tidak mendokumentasikan pembatasan tersebut.Sesuai penelitian saya, semua
final String
diinternir di Jawa. Dari salah satu posting blog:Jadi itu artinya jika Anda menelepon
String.intern()
Anda dapat membandingkan dua string menggunakan==
operator. Tapi di siniString.intern()
tidak perlu karena di Jawafinal String
internal diinternir.Anda dapat menemukan lebih banyak informasi perbandingan String menggunakan == operator dan Javadoc untuk metode String.intern () .
Lihat juga pos Stackoverflow ini untuk informasi lebih lanjut.
sumber
Jika Anda melihat metode ini
dan itu didekompilasi dengan
javap -c ClassWithTheseMethods
versi yang akan Anda lihatdan
Jadi jika Strings bukan kompiler akhir harus menggunakan
StringBuilder
untuk menyatukanstr1
danstr2
sebagainyaakan dikompilasi ke
yang berarti yang
concat
akan dibuat saat runtime sehingga tidak akan datang dari kolam String.Juga jika Strings bersifat final maka kompiler dapat mengasumsikan bahwa mereka tidak akan pernah berubah sehingga alih-alih menggunakannya
StringBuilder
dapat dengan aman menyatukan nilainya sehinggadapat diubah menjadi
dan digabung menjadi
yang berarti bahwa
concate
akan menjadi sengatan literal yang akan diinternir dalam string pool dan kemudian dibandingkan dengan string literal yang sama dari kolam itu dalamif
pernyataan.sumber
Konsep pool dan string conts pool
sumber
Mari kita lihat beberapa kode byte sebagai
final
contohPada
0:
dan2:
,String
"string"
didorong ke stack (dari kolam konstan) dan disimpan ke variabel lokalconcat
secara langsung. Anda dapat menyimpulkan bahwa kompiler membuat (menyatukan)String
"string"
dirinya sendiri pada waktu kompilasi.Kode bukan
final
byteDi sini Anda memiliki dua
String
konstanta,"str"
dan"ing"
yang harus disatukan pada saat runtime dengan aStringBuilder
.sumber
Meskipun, ketika Anda membuat menggunakan notasi String literal dari Java, itu secara otomatis memanggil metode intern () untuk memasukkan objek itu ke dalam kumpulan String, asalkan itu tidak ada di kumpulan sudah.
Compiler tahu variabel terakhir tidak akan pernah berubah, ketika kita menambahkan variabel akhir ini, output pergi ke String Pool karena
str1 + str2
ekspresi output juga tidak akan pernah berubah, Jadi akhirnya kompiler memanggil metode antar setelah output dari dua variabel terakhir di atas. Dalam hal kompiler variabel non-final jangan panggil metode intern.sumber