Perbedaan tumpukan JAVA
Penulis:Eve Cole
Waktu Pembaruan:2009-11-30 17:08:19
-
1. Stack dan heap adalah tempat yang digunakan Java untuk menyimpan data di Ram. Tidak seperti C++, Java secara otomatis mengelola tumpukan dan heap, dan pemrogram tidak dapat mengatur tumpukan atau heap secara langsung.
2. Keuntungan dari stack adalah kecepatan aksesnya lebih cepat dibandingkan heap, nomor dua setelah register yang terletak langsung di CPU. Namun kelemahannya adalah ukuran dan masa pakai data yang disimpan dalam tumpukan harus ditentukan dan kurangnya fleksibilitas. Selain itu, data tumpukan dapat dibagikan, lihat poin 3 untuk detailnya. Keuntungan dari heap adalah dapat mengalokasikan ukuran memori secara dinamis, dan masa pakainya tidak perlu diberitahukan kepada kompiler terlebih dahulu. Pengumpul sampah Java akan secara otomatis mengumpulkan data yang tidak lagi digunakan. Namun kelemahannya adalah karena kebutuhan untuk mengalokasikan memori secara dinamis saat runtime, kecepatan aksesnya lambat.
3. Ada dua tipe tipe data di Java.
Salah satunya adalah tipe primitif, total ada 8 tipe yaitu int, short, long, byte, float, double, boolean, char (perhatikan tidak ada tipe dasar string). Jenis definisi ini didefinisikan dalam bentuk seperti int a = 3; long b = 255L; Perlu dicatat bahwa variabel otomatis menyimpan nilai literal, bukan instance kelas, artinya, variabel tersebut bukan referensi ke kelas. Tidak ada kelas di sini. Misalnya, int a = 3; di mana a adalah referensi yang menunjuk ke tipe int, menunjuk ke nilai literal 3. Data dari nilai literal ini diketahui dalam ukuran dan masa pakainya (nilai literal ini ditentukan secara tetap dalam blok program tertentu, dan nilai bidang menghilang setelah blok program keluar). ada di tumpukan.
Selain itu, stack memiliki fitur khusus yang sangat penting, yaitu data yang disimpan di stack dapat dibagikan. Misalkan kita juga mendefinisikan:
ke dalam a = 3;
ke dalam b = 3;
Kompiler pertama-tama memproses int a = 3; pertama-tama ia membuat referensi ke variabel a di tumpukan, dan kemudian mencari alamat dengan nilai literal 3. Jika tidak menemukannya, ia membuka alamat untuk menyimpan literal nilai 3, dan kemudian a menunjuk ke alamat 3. Selanjutnya, int b = 3 diproses; setelah membuat variabel referensi b, karena sudah ada nilai literal 3 di tumpukan, b langsung diarahkan ke alamat 3. Dengan cara ini, terdapat situasi di mana a dan b keduanya menunjuk ke 3 secara bersamaan.
Penting untuk dicatat bahwa referensi nilai literal ini berbeda dengan referensi objek kelas. Asumsikan bahwa referensi dari dua objek kelas menunjuk ke objek yang sama pada waktu yang sama. Jika satu variabel referensi objek mengubah keadaan internal objek, maka variabel referensi objek lainnya akan segera mencerminkan perubahan tersebut. Sebaliknya, mengubah nilai referensi literal tidak menyebabkan nilai referensi lain ke literal juga berubah. Seperti pada contoh di atas, setelah kita menentukan nilai a dan b, kita kemudian menetapkan a=4, maka b tidak akan sama dengan 4, tetapi tetap sama dengan 3. Di dalam kompiler, ketika menemukan a = 4;, ia akan mencari ulang apakah ada nilai literal 4 di tumpukan, jika tidak, ia akan membuka kembali alamat untuk menyimpan nilai 4; , itu akan langsung mengarahkan a ke alamat ini. Oleh karena itu, perubahan nilai a tidak akan mempengaruhi nilai b.
Yang lainnya adalah kelas pembungkus data, seperti Integer, String, Double dan kelas lain yang membungkus tipe data dasar yang sesuai. Semua jenis data ini ada di heap. Java menggunakan pernyataan new () untuk secara eksplisit memberi tahu kompiler bahwa data tersebut akan dibuat secara dinamis sesuai kebutuhan saat runtime, sehingga lebih fleksibel, tetapi kelemahannya adalah memerlukan lebih banyak waktu. 4. String adalah tipe pembungkus khusus data. Artinya, dapat dibuat dalam bentuk String str = new String( "abc" );, atau dapat dibuat dalam bentuk String str = "abc" ; (sebagai perbandingan, sebelum JDK 5.0, Anda belum pernah terlihat Integer i = 3; ekspresi, karena kelas dan nilai literal tidak dapat digunakan secara bergantian, kecuali untuk String. Di JDK 5.0, ekspresi ini dimungkinkan! Karena kompiler melakukan konversi Integer i = new Integer(3) di latar belakang) . Yang pertama adalah proses pembuatan kelas yang distandarisasi, yaitu di Java, semuanya adalah objek, dan objek adalah turunan dari kelas, semuanya dibuat dalam bentuk baru (). Beberapa kelas di Java, seperti kelas DateFormat, dapat mengembalikan kelas yang baru dibuat melalui metode getInstance() kelas tersebut, yang tampaknya melanggar prinsip ini. Tidak terlalu. Kelas ini menggunakan pola tunggal untuk mengembalikan instance kelas, tetapi instance ini dibuat di dalam kelas melalui new(), dan getInstance() menyembunyikan detail ini dari luar. Lalu mengapa di String str = "abc" ;, instance tidak dibuat melalui new(). Apakah itu melanggar prinsip di atas? Sebenarnya tidak.
5. Tentang cara kerja bagian dalam String str = "abc". Java secara internal mengubah pernyataan ini menjadi langkah-langkah berikut:
(1) Pertama-tama tentukan variabel referensi objek bernama str ke kelas String: String str;
(2) Cari tumpukan untuk melihat apakah ada alamat dengan nilai "abc". Jika tidak, buka alamat dengan nilai literal "abc", lalu buat objek baru o dari kelas String, dan tambahkan objek o dari kelas String. karakter o Nilai string menunjuk ke alamat ini, dan objek referensi o dicatat di sebelah alamat ini pada tumpukan. Jika sudah ada alamat dengan nilai "abc", objek o ditemukan dan alamat o dikembalikan.
(3) Arahkan str ke alamat objek o.
Perlu dicatat bahwa umumnya nilai string di kelas String disimpan secara langsung. Namun seperti String str = "abc"; dalam hal ini, nilai string menyimpan referensi ke data yang disimpan di tumpukan!
Untuk menggambarkan masalah ini dengan lebih baik, kami dapat memverifikasinya melalui kode berikut.
String str1 = "abc" ;
String str2 = "abc" ;
Sistem.keluar.println(str1==str2); //benar
Perhatikan bahwa kami tidak menggunakan str1.equals(str2); di sini, karena ini akan membandingkan apakah nilai kedua string sama. == tanda, menurut instruksi JDK, hanya mengembalikan nilai true ketika kedua referensi menunjuk ke objek yang sama. Apa yang ingin kita lihat di sini adalah apakah str1 dan str2 keduanya menunjuk ke objek yang sama.
Hasilnya menunjukkan bahwa JVM membuat dua referensi str1 dan str2, tetapi hanya membuat satu objek, dan kedua referensi menunjuk ke objek tersebut.
Mari melangkah lebih jauh dan ubah kode di atas menjadi:
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
Sistem.keluar.println(str1 + "," + str2); //bcd, abc
Sistem.keluar.println(str1==str2); //salah
Artinya perubahan tugas mengakibatkan perubahan referensi objek kelas, dan str1 menunjuk ke objek baru lainnya! Dan str2 masih menunjuk ke objek aslinya. Dalam contoh di atas, ketika kita mengubah nilai str1 menjadi "bcd", JVM menemukan bahwa tidak ada alamat untuk menyimpan nilai ini di tumpukan, sehingga membuka alamat ini dan membuat objek baru yang nilai stringnya mengarah ke alamat ini. .
Faktanya, kelas String dirancang agar tidak dapat diubah. Jika Anda ingin mengubah nilainya, Anda bisa, tetapi JVM secara diam-diam membuat objek baru berdasarkan nilai baru saat runtime, dan kemudian mengembalikan alamat objek tersebut ke referensi ke kelas aslinya. Meskipun proses pembuatan ini sepenuhnya otomatis, proses ini membutuhkan lebih banyak waktu. Dalam lingkungan yang sensitif terhadap kebutuhan waktu, hal ini akan menimbulkan dampak buruk tertentu.
Ubah lagi kode aslinya:
String str1 = "abc" ;
String str2 = "abc" ;
str1 = "bcd" ;
Tali str3 = str1;
Sistem.keluar.println(str3);
String str4 = "bcd" ;
Sistem.keluar.println(str1 == str4); //benar
Referensi ke objek str3 menunjuk langsung ke objek yang ditunjuk oleh str1 (perhatikan bahwa str3 tidak membuat objek baru). Setelah str1 mengubah nilainya, buat referensi String str4 dan arahkan ke objek baru yang dibuat karena str1 mengubah nilainya. Dapat ditemukan bahwa str4 tidak membuat objek baru kali ini, sehingga mewujudkan pembagian data di tumpukan lagi.
Mari kita lihat kembali kode berikut ini.
String str1 = String baru( "abc" );
String str2 = "abc" ;
Sistem.keluar.println(str1==str2); //salah
Dua referensi dibuat. Dua objek dibuat. Kedua referensi tersebut masing-masing menunjuk pada dua objek yang berbeda.
String str1 = "abc" ;
String str2 = String baru( "abc" );
Sistem.keluar.println(str1==str2); //salah
Dua referensi dibuat. Dua objek dibuat. Kedua referensi tersebut masing-masing menunjuk pada dua objek yang berbeda.
Dua potongan kode di atas menggambarkan bahwa selama new() digunakan untuk membuat objek baru, maka objek tersebut akan dibuat di heap, dan nilai stringnya disimpan secara terpisah meskipun sama dengan data di tumpukan , itu tidak akan dibagikan dengan data di tumpukan.
6. Nilai kelas pembungkus tipe data tidak dapat diubah. Tidak hanya nilai kelas String yang tidak dapat diubah, tetapi semua kelas pembungkus tipe data tidak dapat mengubah nilai internalnya. 7. Kesimpulan dan saran:
(1) Saat kita mendefinisikan kelas menggunakan format seperti String str = "abc";, kita selalu menganggap bahwa kita membuat objek str dari kelas String. Khawatir tentang jebakan! Objek tersebut mungkin belum dibuat! Satu-satunya hal yang pasti adalah referensi ke kelas String telah dibuat. Adapun apakah referensi ini menunjuk ke objek baru, harus dipertimbangkan sesuai konteksnya, kecuali Anda secara eksplisit membuat objek baru melalui metode new(). Oleh karena itu, pernyataan yang lebih akurat adalah kita membuat variabel referensi str yang menunjuk ke objek kelas String. Variabel referensi objek ini menunjuk ke kelas String dengan nilai "abc". Pemahaman yang jelas tentang hal ini sangat membantu dalam menghilangkan bug yang sulit ditemukan dalam program.
(2) Menggunakan String str = "abc"; dapat meningkatkan kecepatan berjalan program sampai batas tertentu, karena JVM akan secara otomatis menentukan apakah perlu membuat objek baru berdasarkan situasi sebenarnya dari data di tumpukan . Sedangkan untuk kode String str = new String("abc");, objek baru selalu dibuat di heap, terlepas dari apakah nilai stringnya sama atau perlu membuat objek baru, sehingga menambah beban pada program tersebut. Ide ini seharusnya menjadi ide mode kelas terbang, namun tidak diketahui apakah implementasi internal JDK menerapkan mode ini.
(3) Saat membandingkan apakah nilai di kelas pengemasan sama, gunakan metode sama dengan(); saat menguji apakah referensi dari dua kelas pengemasan menunjuk ke objek yang sama, gunakan ==.
(4) Karena sifat kelas String yang tidak dapat diubah, ketika variabel String perlu sering mengubah nilainya, Anda harus mempertimbangkan untuk menggunakan kelas StringBuffer untuk meningkatkan efisiensi program.