Saya sudah lama pengembang Java dan akhirnya, setelah mengambil jurusan, saya punya waktu untuk mempelajarinya dengan baik untuk mengikuti ujian sertifikasi ... Satu hal yang selalu mengganggu saya adalah String menjadi "final". Saya mengerti ketika membaca tentang masalah keamanan dan hal-hal terkait ... Tapi, serius, apakah ada yang punya contoh nyata tentang itu?
Misalnya, apa yang akan terjadi jika String tidak final? Sepertinya tidak ada di Ruby. Saya belum pernah mendengar keluhan yang datang dari komunitas Ruby ... Dan saya mengetahui StringUtils dan kelas terkait yang harus Anda implementasikan sendiri atau cari melalui web untuk hanya menerapkan perilaku itu (4 baris kode) yang Anda bersedia.
java.io.File
tidakfinal
. Oh, tidak. Bersetubuh.Jawaban:
Alasan utamanya adalah kecepatan:
final
kelas tidak dapat diperpanjang yang memungkinkan JIT melakukan semua jenis optimisasi saat menangani string - tidak pernah perlu memeriksa metode yang diganti.Alasan lain adalah keamanan thread: Immutables selalu aman karena thread harus benar-benar membangunnya sebelum dapat diteruskan ke orang lain - dan setelah membangun, mereka tidak dapat diubah lagi.
Juga, para penemu runtime Java selalu ingin berbuat salah di sisi keamanan. Mampu memperluas String (sesuatu yang sering saya lakukan di Groovy karena sangat nyaman) dapat membuka seluruh kaleng cacing jika Anda tidak tahu apa yang Anda lakukan.
sumber
final
sebagai pemeriksaan cepat bahwa suatu metode tidak diganti ketika mengkompilasi kode.final
kata kunci di kelas. Array karakter yang dikandungnya adalah final, membuatnya tidak berubah. Membuat kelas itu sendiri akhir menutup loop sebagai @abl menyebutkan karena subkelas yang dapat diubah tidak dapat diperkenalkan.Ada alasan lain mengapa
String
kelas Java harus final: penting untuk keamanan dalam beberapa skenario. JikaString
kelas tidak final, kode berikut akan rentan terhadap serangan halus oleh penelepon jahat:Serangan: penelepon jahat dapat membuat subkelas String
EvilString
,, di manaEvilString.startsWith()
selalu mengembalikan true tetapi di mana nilaiEvilString
adalah sesuatu yang jahat (misalnya,javascript:alert('xss')
). Karena subclassing, ini akan menghindari pemeriksaan keamanan. Serangan ini dikenal sebagai kerentanan waktu-ke-waktu-ke-waktu-penggunaan (TOCTTOU): antara waktu ketika pemeriksaan dilakukan (bahwa url dimulai dengan http / https) dan waktu ketika nilai digunakan (untuk membuat cuplikan html), nilai efektif dapat berubah. JikaString
tidak final, maka risiko TOCTTOU akan meluas.Jadi, jika
String
belum final, sulit untuk menulis kode aman jika Anda tidak dapat mempercayai penelepon Anda. Tentu saja, itulah posisi perpustakaan Java: mereka mungkin dipanggil oleh applet yang tidak tepercaya, sehingga mereka tidak berani mempercayai penelepon mereka. Ini berarti sulit untuk menulis kode perpustakaan aman, jikaString
tidak final.sumber
char[]
dalam string, tetapi membutuhkan sedikit keberuntungan dalam pengaturan waktu.char[]
di dalam string; oleh karena itu, mereka tidak dapat mengeksploitasi kerentanan TOCTTOU.String
itu belum final. Selama beberapa waktu model ancaman ini relevan, maka menjadi penting untuk dipertahankan. Selama penting untuk applet dan kode tidak dipercaya lainnya, itu bisa dibilang cukup untuk membenarkan pembuatanString
final, bahkan jika itu tidak diperlukan untuk aplikasi tepercaya. (Bagaimanapun, aplikasi tepercaya sudah dapat melewati semua langkah-langkah keamanan, terlepas dari apakah itu final.)Jika tidak, aplikasi java multithreaded akan berantakan (bahkan lebih buruk dari yang sebenarnya).
IMHO Keuntungan utama dari string akhir (tidak dapat diubah) adalah bahwa mereka secara inheren thread-safe: mereka tidak memerlukan sinkronisasi (menulis kode itu kadang-kadang cukup sepele tetapi lebih dari kurang jauh dari itu). Jika itu bisa berubah, jaminan hal-hal seperti toolkit windows multithread akan sangat sulit, dan hal-hal itu sangat diperlukan.
sumber
final
kelas tidak ada hubungannya dengan sifat tidak menentu kelas.Keuntungan lain untuk
String
menjadi final adalah memastikan hasil yang dapat diprediksi, seperti halnya kekekalan.String
, meskipun sebuah kelas, dimaksudkan untuk diperlakukan dalam beberapa skenario, seperti tipe nilai (kompilator bahkan mendukungnya melaluiString blah = "test"
). Oleh karena itu, jika Anda membuat string dengan nilai tertentu, Anda mengharapkannya memiliki perilaku tertentu yang mewarisi string apa pun, mirip dengan harapan yang Anda miliki dengan bilangan bulat.Pikirkan tentang ini: Bagaimana jika Anda sub-kelas
java.lang.String
dan over-rodeequals
atauhashCode
metode? Tiba-tiba dua Strings "test" tidak bisa lagi sama satu sama lain.Bayangkan kekacauan jika saya bisa melakukan ini:
beberapa kelas lain:
sumber
Latar Belakang
Ada tiga tempat di mana final dapat muncul di Jawa:
Membuat final kelas mencegah semua subklasifikasi kelas. Membuat metode akhir mencegah subclass dari metode menimpanya. Membuat field final mencegahnya agar tidak diubah nanti.
Kesalahpahaman
Ada optimasi yang terjadi di sekitar metode dan bidang akhir.
Metode terakhir memudahkan HotSpot untuk mengoptimalkan melalui inlining. Namun, HotSpot melakukan ini bahkan jika metode ini tidak final karena berfungsi dengan asumsi bahwa itu belum ditimpa sampai dibuktikan sebaliknya. Lebih lanjut tentang ini di SO
Variabel terakhir dapat dioptimalkan secara agresif, dan lebih lanjut tentang itu dapat dibaca di bagian JLS 17.5.3 .
Namun, dengan pemahaman itu orang harus sadar bahwa tak satu pun dari optimasi ini adalah tentang membuat kelas final. Tidak ada perolehan kinerja dengan membuat final kelas.
Aspek terakhir dari suatu kelas tidak ada hubungannya dengan ketidakberubahan juga. Seseorang dapat memiliki kelas yang tidak dapat diubah (seperti BigInteger ) yang tidak final, atau kelas yang bisa berubah dan final (seperti StringBuilder ). Keputusan tentang apakah kelas harus final adalah masalah desain.
Desain Akhir
String adalah salah satu tipe data yang paling banyak digunakan. Mereka ditemukan sebagai kunci untuk peta, mereka menyimpan nama pengguna dan kata sandi, itu adalah apa yang Anda baca dari keyboard atau bidang pada halaman web. String ada di mana - mana .
Peta
Hal pertama yang perlu dipertimbangkan tentang apa yang akan terjadi jika Anda dapat mensubktrasikan String adalah menyadari bahwa seseorang dapat membuat kelas String yang bisa berubah yang akan tampak seperti String. Ini akan mengacaukan Peta di mana-mana.
Pertimbangkan kode hipotetis ini:
Ini adalah masalah dengan menggunakan kunci yang bisa berubah-ubah secara umum dengan Peta, tetapi hal yang saya coba dapatkan di sana adalah bahwa tiba-tiba sejumlah hal tentang istirahat Peta. Entri tidak lagi berada di tempat yang tepat di peta. Dalam HashMap, nilai hash telah (seharusnya) berubah dan dengan demikian tidak lagi berada di entri yang benar. Di TreeMap, pohon sekarang rusak karena salah satu node berada di sisi yang salah.
Karena menggunakan String untuk kunci-kunci ini sangat umum, perilaku ini harus dicegah dengan membuat String menjadi final.
Anda mungkin tertarik membaca Mengapa String tidak dapat diubah di Jawa? untuk lebih lanjut tentang sifat abadi String.
String jahat
Ada sejumlah opsi jahat untuk Strings. Pertimbangkan jika saya membuat sebuah String yang selalu mengembalikan nilai true ketika equal dipanggil ... dan meneruskannya ke pemeriksaan kata sandi? Atau membuatnya agar penugasan ke MyString akan mengirim salinan String ke beberapa alamat email?
Ini adalah kemungkinan yang sangat nyata ketika Anda memiliki kemampuan untuk membuat subkelas String.
Optimalisasi Java.lang String
Sementara sebelumnya saya menyebutkan bahwa final tidak membuat String lebih cepat. Namun, kelas String (dan kelas-kelas lain di
java.lang
) sering menggunakan perlindungan tingkat paket dari bidang dan metode untuk memungkinkanjava.lang
kelas - kelas lain dapat bermain-main dengan internal daripada melalui API publik untuk String sepanjang waktu. Fungsi seperti getChars tanpa pengecekan rentang atau lastIndexOf yang digunakan oleh StringBuffer, atau konstruktor yang berbagi array yang mendasarinya (perhatikan bahwa Java 6 merupakan hal yang diubah karena masalah memori).Jika seseorang membuat subkelas String, itu tidak akan dapat membagikan optimasi tersebut (kecuali jika itu bagian dari
java.lang
juga, tapi itu paket yang disegel ).Lebih sulit untuk merancang sesuatu untuk ekstensi
Merancang sesuatu yang bisa diperpanjang itu sulit . Ini berarti bahwa Anda harus mengekspos bagian internal Anda untuk hal lain agar dapat dimodifikasi.
String yang dapat diperpanjang tidak dapat memiliki kebocoran ingatannya diperbaiki. Bagian-bagian itu perlu terkena subclass dan mengubah kode itu berarti subclass akan rusak.
Java bangga dengan kompatibilitas mundur dan dengan membuka kelas inti untuk ekstensi, seseorang kehilangan beberapa kemampuan untuk memperbaiki hal-hal sambil tetap mempertahankan kemampuan komputasi dengan subkelas pihak ketiga.
Checkstyle memiliki aturan yang diberlakukan (yang benar-benar membuat saya frustrasi ketika menulis kode internal) yang disebut "DesignForExtension" yang memberlakukan bahwa setiap kelas adalah:
Yang rasional untuk itu adalah:
Mengizinkan perluasan kelas implementasi berarti bahwa subclass mungkin dapat merusak keadaan kelas yang menjadi dasarnya dan membuatnya sehingga berbagai jaminan yang diberikan superclass tidak valid. Untuk sesuatu yang kompleks seperti String, hampir pasti bahwa mengubah bagian itu akan merusak sesuatu.
Pengembang hurbis
Itu bagian dari menjadi pengembang. Pertimbangkan kemungkinan bahwa setiap pengembang akan membuat subclass String mereka sendiri dengan koleksi utilities mereka sendiri di dalamnya. Tetapi sekarang subkelas ini tidak dapat secara bebas ditugaskan kembali satu sama lain.
Cara ini menyebabkan kegilaan. Casting ke String di semua tempat dan memeriksa untuk melihat apakah String adalah turunan dari kelas String Anda atau jika tidak, membuat String baru berdasarkan yang itu dan ... hanya, tidak. Jangan.
Saya yakin Anda dapat menulis kelas String yang baik ... tetapi menulis beberapa implementasi string kepada orang-orang gila yang menulis C ++ dan harus berurusan dengan
std::string
danchar*
dan sesuatu dari boost dan SString , dan yang lainnya .Java String magic
Ada beberapa hal ajaib yang dilakukan Java dengan String. Ini membuatnya lebih mudah bagi seorang programmer untuk berurusan dengan tetapi memperkenalkan beberapa inkonsistensi dalam bahasa. Mengizinkan subclass pada String akan mengambil beberapa pemikiran yang sangat signifikan tentang bagaimana menangani hal-hal ajaib ini:
String Literals (JLS 3.10.5 )
Memiliki kode yang memungkinkan seseorang untuk melakukan:
Ini tidak harus bingung dengan tinju dari jenis numerik seperti Integer. Anda tidak dapat melakukannya
1.toString()
, tetapi Anda dapat melakukannya"foo".concat(bar)
.The
+
operator (JLS 15.18.1 )Tidak ada jenis referensi lain di Jawa yang memungkinkan operator untuk menggunakannya. String itu spesial. Operator concatenation string juga bekerja pada level kompiler sehingga
"foo" + "bar"
menjadi"foobar"
saat dikompilasi daripada saat runtime.Konversi String (JLS 5.1.11 )
Semua objek dapat dikonversi menjadi String hanya dengan menggunakannya dalam konteks String.
String Interning ( JavaDoc )
Kelas String memiliki akses ke kumpulan String yang memungkinkannya untuk memiliki representasi kanonik objek yang dihuni pada tipe kompilasi dengan String literal.
Mengizinkan subkelas String akan berarti bahwa bit-bit ini dengan String yang membuatnya lebih mudah diprogram akan menjadi sangat sulit atau tidak mungkin dilakukan ketika jenis-jenis String lainnya dimungkinkan.
sumber
Jika String belum final, setiap peti alat programmer akan berisi "String hanya dengan beberapa metode pembantu yang bagus". Masing-masing akan tidak kompatibel dengan semua peti alat lainnya.
Saya menganggapnya sebagai pembatasan konyol. Hari ini saya yakin ini adalah keputusan yang sangat masuk akal. Itu membuat Strings menjadi Strings.
sumber
final
di Jawa bukan hal yang sama sepertifinal
di C #. Dalam konteks ini, itu sama dengan C #readonly
. Lihat stackoverflow.com/questions/1327544/…public final class String
.