Di Jawa kami melihat banyak tempat di mana final
kata kunci dapat digunakan tetapi penggunaannya tidak umum.
Sebagai contoh:
String str = "abc";
System.out.println(str);
Dalam kasus di atas, str
bisa final
tetapi ini biasanya ditinggalkan.
Ketika suatu metode tidak akan ditimpa, kita dapat menggunakan kata kunci terakhir. Demikian pula dalam kasus kelas yang tidak akan diwarisi.
Apakah penggunaan kata kunci akhir dalam salah satu atau semua kasus ini benar-benar meningkatkan kinerja? Jika demikian, lalu bagaimana? Tolong jelaskan. Jika penggunaan yang tepat final
sangat penting untuk kinerja, kebiasaan apa yang harus dikembangkan oleh seorang programmer Java untuk memanfaatkan kata kunci dengan sebaik-baiknya?
Jawaban:
Biasanya tidak. Untuk metode virtual, HotSpot melacak apakah metode tersebut benar-benar telah ditimpa, dan mampu melakukan optimasi seperti menggarisbawahi asumsi bahwa metode belum ditimpa - sampai memuat kelas yang menimpa metode, pada titik mana itu dapat membatalkan (atau membatalkan sebagian) optimasi tersebut.
(Tentu saja, ini dengan asumsi Anda menggunakan HotSpot - tapi sejauh ini JVM yang paling umum, jadi ...)
Menurut saya, Anda harus menggunakan
final
berdasarkan pada desain yang jelas dan mudah dibaca daripada untuk alasan kinerja. Jika Anda ingin mengubah apa pun karena alasan kinerja, Anda harus melakukan pengukuran yang sesuai sebelum membengkokkan kode yang paling jelas - sehingga Anda dapat memutuskan apakah kinerja tambahan yang dicapai sepadan dengan keterbacaan / desain yang lebih buruk. (Dalam pengalaman saya hampir tidak pernah sepadan; YMMV.)EDIT: Seperti bidang terakhir telah disebutkan, ada baiknya mengangkat bahwa mereka sering merupakan ide yang baik, dalam hal desain yang jelas. Mereka juga mengubah perilaku yang dijamin dalam hal visibilitas cross-thread: setelah konstruktor selesai, setiap bidang terakhir dijamin akan terlihat di utas lainnya segera. Ini mungkin adalah penggunaan paling umum
final
dalam pengalaman saya, meskipun sebagai pendukung Josh Bloch "desain untuk warisan atau melarangnya" aturan praktis, saya mungkin harus menggunakanfinal
lebih sering untuk kelas ...sumber
final
umumnya direkomendasikan karena membuat kode lebih mudah dipahami, dan membantu menemukan bug (karena itu membuat maksud programer eksplisit). PMD mungkin merekomendasikan untuk digunakanfinal
karena masalah gaya ini, bukan karena alasan kinerja.Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
. SelanjutnyaAn immutable object can be in exactly one state, the state in which it was created.
vsMutable objects, on the other hand, can have arbitrarily complex state spaces.
. Dari pengalaman pribadi saya, menggunakan kata kuncifinal
harus menyoroti niat pengembang untuk condong ke arah ketidakberdayaan, bukan untuk "mengoptimalkan" kode. Saya mendorong Anda untuk membaca bab ini, menarik!final
kata kunci pada variabel dapat mengurangi jumlah bytecode, yang dapat menghasilkan efek pada kinerja.Jawaban singkat: jangan khawatir tentang itu!
Jawaban panjang:
Ketika berbicara tentang variabel lokal akhir, ingatlah bahwa menggunakan kata kunci
final
akan membantu kompiler mengoptimalkan kode secara statis , yang pada akhirnya dapat menghasilkan kode yang lebih cepat. Sebagai contoh, String terakhira + b
dalam contoh di bawah ini digabungkan secara statis (pada waktu kompilasi).Hasil?
Diuji pada Java Hotspot VM 1.7.0_45-b18.
Jadi berapa peningkatan kinerja yang sebenarnya? Saya tidak berani mengatakannya. Dalam kebanyakan kasus mungkin marjinal (~ 270 nanodetik dalam tes sintetis ini karena rangkaian string dihindari sama sekali - kasus yang jarang terjadi), tetapi dalam kode utilitas yang sangat optimal mungkin menjadi faktor. Bagaimanapun jawaban untuk pertanyaan awal adalah ya, itu mungkin meningkatkan kinerja, tetapi sedikit di terbaik .
Selain manfaat kompilasi waktu, saya tidak dapat menemukan bukti bahwa penggunaan kata kunci
final
memiliki efek terukur terhadap kinerja.sumber
String
penggabungan adalah operasi yang jauh lebih mahal daripada menambahkanIntegers
. Melakukannya secara statis (jika mungkin) lebih efisien, itulah yang ditunjukkan oleh tes ini.Ya itu bisa. Ini adalah contoh di mana final dapat meningkatkan kinerja:
Kompilasi bersyarat adalah teknik di mana baris kode tidak dikompilasi ke dalam file kelas berdasarkan kondisi tertentu. Ini dapat digunakan untuk menghapus banyak kode debug dalam build produksi.
pertimbangkan hal berikut:
Dengan mengubah atribut doSomething menjadi atribut final, Anda telah memberi tahu kompiler bahwa setiap kali ia melihat doSomething, ia harus menggantinya dengan false sesuai dengan aturan substitusi waktu kompilasi. Lulus pertama dari compiler mengubah kode untuk sesuatu seperti ini:
Setelah ini selesai, kompiler akan memeriksanya lagi dan melihat bahwa ada pernyataan yang tidak terjangkau dalam kode. Karena Anda bekerja dengan kompiler berkualitas tinggi, itu tidak suka semua kode byte yang tidak dapat dijangkau. Jadi itu menghapus mereka, dan Anda berakhir dengan ini:
sehingga mengurangi kode yang berlebihan, atau pemeriksaan bersyarat yang tidak perlu.
Sunting: Sebagai contoh, mari kita ambil kode berikut:
Ketika mengkompilasi kode ini dengan Java 8 dan mendekompilasi dengan
javap -c Test.class
kita mendapatkan:Kita dapat mencatat bahwa kode yang dikompilasi hanya mencakup variabel non-final
x
. Ini membuktikan bahwa variabel akhir memiliki dampak pada kinerja, setidaknya untuk kasus sederhana ini.sumber
Menurut IBM - tidak untuk kelas atau metode.
http://www.ibm.com/developerworks/java/library/j-jtp04223.html
sumber
Saya kagum bahwa tidak ada yang benar-benar memposting beberapa kode nyata yang tidak dikompilasi untuk membuktikan bahwa setidaknya ada beberapa perbedaan kecil.
Untuk referensi ini telah diuji terhadap
javac
versi8
,9
dan10
.Misalkan metode ini:
Mengkompilasi kode ini sebagaimana adanya, menghasilkan kode byte yang sama persis seperti ketika
final
akan ada (final Object left = new Object();
).Tapi yang ini:
Menghasilkan:
Meninggalkan
final
untuk hadir menghasilkan:Kode ini cukup jelas, jika ada konstanta waktu kompilasi, kode itu akan dimuat langsung ke tumpukan operan (tidak akan disimpan ke dalam array variabel lokal seperti contoh sebelumnya melalui
bipush 12; istore_0; iload_0
) - yang agak masuk akal karena tidak ada yang bisa mengubahnya.Di sisi lain mengapa dalam kasus kedua kompiler tidak menghasilkan di
istore_0 ... iload_0
luar saya, tidak seperti slot0
yang digunakan dengan cara apa pun (itu bisa menyusutkan variabel array dengan cara ini, tetapi mungkin saya kehilangan beberapa detail internal, tidak bisa katakan pasti)Saya terkejut melihat optimasi seperti itu, mengingat betapa kecilnya yang
javac
dilakukan. Seharusnya kita selalu menggunakanfinal
? Saya bahkan tidak akan menulisJMH
tes (yang saya ingin awalnya), saya yakin bahwa diff adalah dalam urutanns
(jika memungkinkan untuk ditangkap sama sekali). Satu-satunya tempat ini bisa menjadi masalah, adalah ketika suatu metode tidak dapat digarisbawahi karena ukurannya (dan menyatakanfinal
akan mengecilkan ukuran itu dengan beberapa byte).Ada dua lagi
final
yang perlu ditangani. Pertama adalah ketika suatu metodefinal
(dariJIT
perspektif), metode tersebut adalah monomorfik - dan ini adalah yang paling dicintai olehJVM
.Lalu ada
final
variabel instan (yang harus ditetapkan di setiap konstruktor); ini penting karena mereka akan menjamin referensi yang dipublikasikan dengan benar, seperti disentuh sedikit di sini dan juga ditentukan secara tepat olehJLS
.sumber
javac -g FinalTest.java
), mendekompilasi kode menggunakanjavap -c FinalTest.class
dan tidak mendapatkan hasil yang sama (denganfinal int left=12
, saya dapatkanbipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn
). Jadi ya, bytecode yang dihasilkan bergantung pada banyak faktor dan sulit untuk mengatakan apakah memilikifinal
atau tidak menghasilkan efek pada kinerja. Tetapi karena bytecode berbeda, beberapa perbedaan kinerja mungkin ada.Anda benar-benar bertanya tentang dua (setidaknya) kasus berbeda:
final
untuk variabel lokalfinal
untuk metode / kelasJon Skeet sudah menjawab 2). Sekitar 1):
Saya tidak berpikir itu membuat perbedaan; untuk variabel lokal, kompilator dapat menyimpulkan apakah variabelnya final atau tidak (hanya dengan memeriksa apakah variabel tersebut ditugaskan lebih dari satu kali). Jadi jika kompiler ingin mengoptimalkan variabel yang hanya ditugaskan satu kali, ia dapat melakukannya tidak peduli apakah variabel tersebut benar-benar dinyatakan
final
atau tidak.final
mungkin membuat perbedaan untuk bidang kelas yang dilindungi / publik; di sana sangat sulit bagi kompiler untuk mencari tahu apakah field sedang diset lebih dari sekali, karena bisa terjadi dari kelas yang berbeda (yang bahkan mungkin tidak dimuat). Tetapi meskipun demikian JVM dapat menggunakan teknik yang dijelaskan Jon (optimalkan secara optimis, kembalikan jika kelas dimuat yang mengubah bidang).Singkatnya, saya tidak melihat alasan mengapa itu harus membantu kinerja. Jadi optimasi mikro semacam ini tidak mungkin membantu. Anda dapat mencoba membandingkannya untuk memastikan, tetapi saya ragu itu akan membuat perbedaan.
Edit:
Sebenarnya, menurut jawaban Timo Westkämper,
final
dapat meningkatkan kinerja untuk bidang kelas dalam beberapa kasus. Saya berdiri dikoreksi.sumber
final
, untuk memastikan Anda tidak menetapkannya dua kali. Sejauh yang saya bisa lihat, logika pemeriksaan yang sama dapat (dan mungkin) digunakan untuk memeriksa apakah suatu variabel bisafinal
.javac
untuk mengoptimalkan dengan lebih baik. Tapi ini hanya spekulasi.final
variabel adalah tipe primitif atau tipeString
dan segera ditugaskan dengan konstanta waktu kompilasi seperti pada contoh pertanyaan, kompiler harus memasukkannya, karena variabelnya adalah konstanta waktu kompilasi per spesifikasi. Tetapi untuk sebagian besar kasus penggunaan, kode mungkin terlihat berbeda, tetapi masih tidak ada bedanya apakah konstanta digariskan atau dibaca dari variabel kinerja lokal-bijaksana.Catatan: Bukan ahli java
Jika saya ingat java saya dengan benar, akan ada sedikit cara untuk meningkatkan kinerja menggunakan kata kunci terakhir. Saya selalu tahu ada untuk "kode yang baik" - desain dan keterbacaan.
sumber
Saya bukan ahli, tetapi saya kira Anda harus menambahkan
final
kata kunci ke kelas atau metode jika tidak akan ditimpa dan biarkan variabel sendirian. Jika ada cara untuk mengoptimalkan hal-hal seperti itu kompiler akan melakukannya untuk Anda.sumber
Sebenarnya, saat menguji beberapa kode terkait OpenGL, saya menemukan bahwa menggunakan pengubah akhir pada bidang pribadi dapat menurunkan kinerja. Inilah awal dari kelas yang saya uji:
Dan ini adalah metode yang saya gunakan untuk menguji kinerja berbagai alternatif, di antaranya kelas ShaderInput:
Setelah iterasi ke-3, dengan pemanasan VM, saya secara konsisten mendapatkan angka-angka ini tanpa kata kunci terakhir:
Dengan kata kunci terakhir:
Perhatikan angka-angka untuk tes ShaderInput.
Tidak masalah apakah saya menjadikan ladang itu publik atau pribadi.
Kebetulan, ada beberapa hal lagi yang membingungkan. Kelas ShaderInput mengungguli semua varian lainnya, bahkan dengan kata kunci terakhir. Ini luar biasa b / c pada dasarnya adalah kelas pembungkus array float, sedangkan tes lainnya memanipulasi array secara langsung . Harus memikirkan ini. Mungkin ada hubungannya dengan antarmuka ShaderInput yang lancar.
System.arrayCopy juga sebenarnya agak lebih lambat untuk array kecil daripada hanya menyalin elemen dari satu array ke array lainnya dalam for. Dan menggunakan sun.misc.Unsafe (dan juga java.nio.FloatBuffer langsung, tidak ditampilkan di sini) berkinerja buruk.
sumber
Final (Setidaknya untuk variabel dan parameter anggota) lebih untuk manusia daripada untuk mesin.
Ini praktik yang baik untuk membuat variabel menjadi final sedapat mungkin. Saya berharap Java telah membuat "variabel" final secara default dan memiliki kata kunci "Mutable" untuk memungkinkan perubahan. Kelas yang tidak dapat diubah mengarah ke kode ulir yang lebih baik, dan hanya dengan melirik kelas dengan "akhir" di depan setiap anggota akan dengan cepat menunjukkannya sebagai tidak dapat diubah.
Kasus lain - Saya telah mengonversi banyak kode untuk menggunakan @ NonNull / @ Nullable annotations (Anda dapat mengatakan parameter metode tidak boleh nol maka IDE dapat memperingatkan Anda setiap tempat Anda melewati variabel yang tidak ditandai @) NonNull - semuanya menyebar ke tingkat yang konyol). Jauh lebih mudah untuk membuktikan variabel anggota atau parameter tidak boleh nol ketika ditandai final karena Anda tahu itu tidak ditugaskan kembali di tempat lain.
Saran saya adalah membiasakan menerapkan final untuk anggota dan parameter secara default, Ini hanya beberapa karakter tetapi akan mendorong Anda ke arah meningkatkan gaya pengkodean Anda jika tidak ada yang lain.
Final untuk metode atau kelas adalah konsep lain karena ini melarang bentuk penggunaan kembali yang sangat valid dan tidak terlalu banyak memberi tahu pembaca. Penggunaan terbaik mungkin adalah cara mereka membuat String dan tipe intrinsik lainnya menjadi final sehingga Anda dapat mengandalkan perilaku konsisten di mana-mana - Itu mencegah banyak bug (walaupun ada kalanya saya akan MENYUKAI untuk memperpanjang string .... oh the kemungkinan)
sumber
Seperti disebutkan di tempat lain, 'final' untuk variabel lokal, dan pada tingkat yang sedikit lebih rendah dari variabel anggota, lebih merupakan masalah gaya.
'final' adalah pernyataan bahwa Anda bermaksud variabel untuk tidak berubah (yaitu, variabel tidak akan bervariasi!). Kompiler kemudian dapat membantu Anda keluar dengan mengeluh jika Anda melanggar kendala Anda sendiri.
Saya berbagi sentimen bahwa Java akan menjadi bahasa yang lebih baik jika pengidentifikasi (Maaf, saya tidak bisa menyebut hal yang tidak bervariasi sebagai 'variabel') adalah final secara default, dan mengharuskan Anda untuk secara eksplisit mengatakan bahwa mereka adalah variabel. Tetapi setelah mengatakan itu, saya biasanya tidak menggunakan 'final' pada variabel lokal yang diinisialisasi dan tidak pernah ditugaskan; sepertinya terlalu berisik.
(Saya menggunakan final pada variabel anggota)
sumber
Anggota menyatakan dengan final akan tersedia di seluruh program karena tidak seperti yang nonfinal, jika anggota ini belum digunakan dalam program, tetap saja mereka tidak akan dirawat oleh Pengumpul Sampah sehingga dapat menyebabkan masalah kinerja karena manajemen memori yang buruk.
sumber
final
kata kunci dapat digunakan dalam lima cara di Jawa.Kelas adalah final: kelas adalah final berarti kita tidak dapat diperpanjang atau warisan berarti warisan tidak mungkin.
Demikian pula - Objek adalah final: beberapa waktu kita tidak mengubah keadaan internal objek sehingga dalam kasus seperti itu kita dapat menentukan objek adalah objek akhir. Tujuan akhir berarti bukan variabel juga final.
Setelah variabel referensi dibuat final, itu tidak dapat dipindahkan ke objek lain. Tapi bisa mengubah konten objek selama bidangnya belum final
sumber
final
artinya, tetapi apakah menggunakanfinal
mempengaruhi kinerja.