Mengapa string tidak dapat diubah dalam Java dan .NET?

191

Mengapa mereka memutuskan untuk membuat Stringkekal di Java dan .NET (dan beberapa bahasa lainnya)? Mengapa mereka tidak membuatnya bisa berubah?

chrissie1
sumber
13
Saya memang memiliki pemikiran yang sama, tetapi memeriksa lokasi poster asli dan menemukan bahwa mereka berasal dari Belgia. Mengingat bahwa ini berarti mereka tidak mungkin menjadi penutur bahasa Inggris asli. Ditambah dengan fakta bahwa sebagian besar penduduk asli tidak memahami bahasa tersebut, saya memutuskan untuk mengendurkannya.
belugabob
8
Terima kasih belugabob, tapi aku bukan dia, aku seorang dia. Tampaknya orang tidak mempertimbangkan perbedaan budaya.
chrissie1
7
Maafkan saya - chrissie (umumnya) adalah nama seorang gadis di Inggris - membuat saya menjadi korban dari perbedaan budaya yang lain :-)
belugabob
Sekedar catatan, dalam. NET Stringsebenarnya bisa berubah secara internal. StringBuilderdi .NET 2.0 memutasi string . Saya hanya akan meninggalkannya di sini.
Alvin Wong
Sebenarnya NET string yang bisa berubah. Dan itu bahkan bukan omong kosong.
Bitterblue

Jawaban:

205

Menurut Java Efektif , bab 4, halaman 73, edisi ke-2:

"Ada banyak alasan bagus untuk ini: Kelas yang tidak berubah lebih mudah untuk merancang, mengimplementasikan, dan menggunakan daripada kelas yang bisa berubah. Mereka kurang rentan terhadap kesalahan dan lebih aman.

[...]

" Objek yang tidak dapat diubah adalah sederhana. Sebuah objek yang tidak dapat diubah dapat berada dalam satu keadaan, keadaan di mana ia dibuat. Jika Anda memastikan bahwa semua konstruktor membuat invarian kelas, maka dijamin bahwa invarian ini akan tetap benar untuk selamanya, dengan tidak ada usaha di pihak Anda.

[...]

Objek yang tidak dapat diubah secara inheren aman dari benang; mereka tidak memerlukan sinkronisasi. Mereka tidak dapat rusak oleh beberapa utas mengaksesnya secara bersamaan. Ini adalah pendekatan yang paling mudah untuk mencapai keselamatan benang. Bahkan, tidak ada utas yang dapat mengamati efek utas lain pada objek yang tidak dapat diubah. Oleh karena itu, objek yang tidak berubah dapat dibagikan secara bebas

[...]

Poin kecil lainnya dari bab yang sama:

Anda tidak hanya dapat berbagi objek yang tidak dapat diubah, tetapi Anda juga dapat membagikan bagian dalamnya.

[...]

Benda-benda yang tidak dapat berubah membuat blok bangunan yang bagus untuk benda-benda lain, apakah bisa berubah atau tidak berubah.

[...]

Satu-satunya kelemahan nyata dari kelas yang tidak dapat diubah adalah bahwa mereka membutuhkan objek yang terpisah untuk setiap nilai yang berbeda.

FLUFF PRINCESS
sumber
22
Baca kalimat kedua dari jawaban saya: Kelas yang tidak dapat berubah lebih mudah untuk dirancang, diimplementasikan, dan digunakan daripada kelas yang bisa berubah. Mereka kurang rentan terhadap kesalahan dan lebih aman.
PRUCESS FLUFF
5
@ PRINCESSFLUFF Saya akan menambahkan bahwa berbagi string yang dapat diubah bisa berbahaya bahkan pada satu utas. Misalnya, menyalin laporan: report2.Text = report1.Text;. Kemudian, di tempat lain, memodifikasi teks: report2.Text.Replace(someWord, someOtherWord);. Ini akan mengubah laporan pertama dan juga yang kedua.
phoog
10
@ Sam dia tidak bertanya "mengapa mereka tidak bisa berubah", dia bertanya "mengapa mereka memutuskan untuk membuat kekal" yang ini menjawab dengan sempurna.
James
1
@ PRINCESSFLUFF Jawaban ini tidak membahas string secara khusus. Itu adalah pertanyaan OP. Ini sangat frustasi - ini terjadi setiap saat pada SO dan dengan pertanyaan immutabilitas String juga. Jawabannya di sini berbicara tentang manfaat umum dari kekekalan. Jadi mengapa tidak semua tipe tidak berubah? Bisakah Anda kembali dan membahas String?
Howiecamp
@Howiecamp Saya pikir itu tersirat oleh jawaban bahwa string bisa berubah (tidak ada yang mencegah kelas string yang dapat dirubah hipotetis dari yang ada). Mereka hanya memutuskan untuk tidak melakukannya dengan cara yang sederhana, dan karena itu mencakup 99% kasus penggunaan. Mereka masih menyediakan StringBuilder untuk 1% kasing lainnya.
Daniel García Rubio
102

Setidaknya ada dua alasan.

Pertama - http://www.javafaq.nu/java-article1060.html keamanan

Alasan utama mengapa String membuat tidak dapat diubah adalah keamanan. Lihat contoh ini: Kami memiliki metode buka file dengan cek masuk. Kami meneruskan String ke metode ini untuk memproses otentikasi yang diperlukan sebelum panggilan akan diteruskan ke OS. Jika String bisa diubah, mungkin saja entah bagaimana memodifikasi kontennya setelah pemeriksaan otentikasi sebelum OS mendapatkan permintaan dari program, maka dimungkinkan untuk meminta file apa pun. Jadi, jika Anda memiliki hak untuk membuka file teks dalam direktori pengguna tetapi kemudian dengan cepat ketika entah bagaimana Anda berhasil mengubah nama file Anda dapat meminta untuk membuka file "passwd" atau yang lainnya. Kemudian file dapat dimodifikasi dan dimungkinkan untuk login langsung ke OS.

Kedua - Efisiensi memori http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

JVM secara internal memelihara "String Pool". Untuk mencapai efisiensi memori, JVM akan merujuk objek String dari kumpulan. Itu tidak akan membuat objek String baru. Jadi, setiap kali Anda membuat string literal baru, JVM akan memeriksa dalam kumpulan apakah sudah ada atau belum. Jika sudah ada di pool, cukup berikan referensi ke objek yang sama atau buat objek baru di pool. Akan ada banyak referensi menunjuk ke objek String yang sama, jika seseorang mengubah nilainya, itu akan mempengaruhi semua referensi. Jadi, matahari memutuskan untuk membuatnya tidak berubah.

Jorge Ferreira
sumber
Ini poin bagus tentang penggunaan kembali, dan terutama benar jika Anda menggunakan String.intern (). Mungkin saja untuk digunakan kembali tanpa membuat semua string tidak berubah, tetapi kehidupan cenderung rumit pada saat itu.
jsight
3
Tak satu pun dari mereka yang tampaknya menjadi alasan yang sangat valid bagi saya di zaman sekarang ini.
Brian Knoblauch
1
Saya tidak terlalu yakin dengan argumen efisiensi memori (yaitu, ketika dua atau lebih objek String berbagi data yang sama, dan satu dimodifikasi, maka keduanya bisa dimodifikasi). Objek CString di MFC menyiasatinya dengan menggunakan penghitungan referensi.
RobH
7
keamanan sebenarnya bukan bagian dari Raison d'être untuk string yang tidak dapat diubah - OS Anda akan menyalin string ke buffer mode-kernel dan melakukan akses-periksa di sana, untuk menghindari serangan waktu. Ini benar-benar semua tentang benang pengaman & kinerja :)
snemarch
1
Argumen efisiensi memori juga tidak berfungsi. Dalam bahasa asli seperti C, konstanta string hanyalah penunjuk ke data di bagian data yang diinisialisasi - mereka tetap saja-baca / tidak dapat diubah. "jika seseorang mengubah nilainya" - lagi, string dari kolam hanya-baca.
wj32
57

Sebenarnya, alasan string tidak dapat diubah di java tidak ada hubungannya dengan keamanan. Dua alasan utama adalah sebagai berikut:

Keamanan Thead:

String adalah tipe objek yang sangat banyak digunakan. Oleh karena itu lebih atau kurang dijamin untuk digunakan dalam lingkungan multi-threaded. String tidak dapat diubah untuk memastikan bahwa string aman untuk dibagikan di antara utas. Memiliki string yang tidak dapat diubah memastikan bahwa ketika meneruskan string dari thread A ke thread B lainnya, thread B tidak dapat secara tak terduga memodifikasi string thread A.

Ini tidak hanya membantu menyederhanakan tugas pemrograman multi-utas yang sudah cukup rumit, tetapi juga membantu dengan kinerja aplikasi multi-utas. Akses ke objek yang dapat diubah harus entah bagaimana disinkronkan ketika mereka dapat diakses dari beberapa utas, untuk memastikan bahwa satu utas tidak berusaha membaca nilai objek Anda saat sedang dimodifikasi oleh utas lainnya. Sinkronisasi yang tepat keduanya sulit dilakukan dengan benar untuk programmer, dan mahal saat runtime. Objek yang tidak dapat diubah tidak dapat dimodifikasi dan oleh karena itu tidak perlu sinkronisasi.

Kinerja:

Sementara String magang telah disebutkan, itu hanya merupakan keuntungan kecil dalam efisiensi memori untuk program Java. Hanya string literal yang diinternir. Ini berarti bahwa hanya string yang sama dalam kode sumber Anda akan berbagi Objek String yang sama. Jika program Anda secara dinamis membuat string yang sama, mereka akan direpresentasikan dalam objek yang berbeda.

Lebih penting lagi, string yang tidak berubah memungkinkan mereka untuk berbagi data internal mereka. Untuk banyak operasi string, ini berarti bahwa array karakter yang mendasarinya tidak perlu disalin. Misalnya, Anda ingin mengambil lima karakter pertama dari String. Di Jawa, Anda akan memanggil myString.substring (0,5). Dalam hal ini, apa yang dilakukan oleh metode substring () adalah hanya membuat objek String baru yang membagikan karakter dasar myString [] tetapi siapa tahu bahwa itu dimulai pada indeks 0 dan berakhir pada indeks 5 dari char itu []. Untuk meletakkan ini dalam bentuk grafis, Anda akan berakhir dengan yang berikut:

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

Ini membuat jenis operasi ini sangat murah, dan O (1) karena operasi tidak tergantung pada panjang string asli, atau pada panjang substring yang perlu kita ekstrak. Perilaku ini juga memiliki beberapa manfaat memori, karena banyak string dapat berbagi karakter yang mendasarinya [].

LordOfThePigs
sumber
6
Menerapkan substring sebagai referensi yang berbagi dasar char[]adalah keputusan desain yang agak dipertanyakan. Jika Anda membaca seluruh file menjadi satu string dan mempertahankan referensi ke hanya substring 1 karakter, seluruh file harus disimpan dalam memori.
Gabe
5
Tepat, saya bertemu dengan gotcha tertentu itu sambil membuat crawler situs web yang hanya perlu mengekstrak beberapa kata dari seluruh halaman. Seluruh halaman kode HTML berada di memori, dan karena substring berbagi char [], saya menyimpan seluruh kode HTML meskipun saya hanya membutuhkan beberapa byte. Solusi untuk itu adalah dengan menggunakan String baru (original.substring (.., ..)), konstruktor String (String) membuat salinan kisaran yang relevan dari array yang mendasarinya.
LordOfThePigs
1
Adendum untuk mencakup perubahan berikut: Sejak Jave 7, String.substring()lakukan salinan lengkap, untuk mencegah masalah yang disebutkan dalam komentar di atas. Di Java 8, dua bidang yang memungkinkan char[]berbagi, yaitu countdan offset, dihapus, sehingga mengurangi jejak memori instance String.
Christian Semrau
Saya setuju dengan bagian Thead Safety, tetapi meragukan kasing kasus.
Gqqnbig
@LoveRight: Kemudian periksa kode sumber java.lang.String ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ), sudah seperti itu sampai Java 6 (yang saat ini ketika jawaban ini ditulis). Saya tampaknya telah berubah di Jawa 7.
LordOfThePigs
28

Keselamatan dan kinerja benang. Jika sebuah string tidak dapat dimodifikasi, maka aman dan cepat untuk menyerahkan referensi di antara banyak utas. Jika string dapat diubah, Anda harus selalu menyalin semua byte dari string ke instance baru, atau memberikan sinkronisasi. Aplikasi tipikal akan membaca string 100 kali untuk setiap kali string perlu dimodifikasi. Lihat wikipedia tentang kekekalan .

Matt Howells
sumber
11

Orang harus benar-benar bertanya, "mengapa X harus bisa berubah?" Lebih baik default ke kekekalan, karena manfaat yang telah disebutkan oleh Princess Fluff . Seharusnya pengecualian bahwa sesuatu bisa berubah.

Sayangnya sebagian besar bahasa pemrograman saat ini standar untuk mutabilitas, tetapi mudah-mudahan di masa depan standarnya lebih pada ketidakmampuan (lihat Daftar Keinginan untuk Bahasa Pemrograman Mainstream Berikutnya ).

Esko Luontola
sumber
7

Wow! Saya tidak percaya informasi yang salah di sini. StringMenjadi abadi tidak memiliki apa-apa dengan keamanan. Jika seseorang sudah memiliki akses ke objek dalam aplikasi yang sedang berjalan (yang harus diasumsikan jika Anda mencoba untuk menjaga terhadap seseorang 'peretasan' Stringdi aplikasi Anda), mereka tentu akan menjadi banyak peluang lain yang tersedia untuk peretasan.

Ini adalah ide yang cukup baru bahwa ketidakmampuan Stringmengatasi masalah threading. Hmmm ... Saya memiliki objek yang sedang diubah oleh dua utas berbeda. Bagaimana saya mengatasi ini? menyinkronkan akses ke objek? Naawww ... jangan biarkan siapa pun mengubah objek sama sekali - itu akan memperbaiki semua masalah konkurensi berantakan kami! Bahkan, mari kita membuat semua objek tidak berubah, dan kemudian kita dapat menghapus konstruksi yang disinkronkan dari bahasa Java.

Alasan sebenarnya (ditunjukkan oleh orang lain di atas) adalah optimasi memori. Sangat umum dalam aplikasi apa pun untuk string string yang sama digunakan berulang kali. Adalah sangat umum, pada kenyataannya, bahwa beberapa dekade yang lalu, banyak penyusun membuat optimasi menyimpan hanya satu contoh tunggal dari Stringliteral. Kelemahan dari optimasi ini adalah bahwa kode runtime yang memodifikasi Stringliteral menimbulkan masalah karena memodifikasi contoh untuk semua kode lain yang membagikannya. Sebagai contoh, akan tidak baik untuk suatu tempat fungsi dalam aplikasi untuk mengubah Stringliteral "dog"untuk "cat". A printf("dog")akan menghasilkan "cat"ditulis ke stdout. Untuk alasan itu, perlu ada cara untuk menjaga terhadap kode yang berusaha berubahStringliteral (yaitu, membuatnya tidak berubah). Beberapa kompiler (dengan dukungan dari OS) akan mencapai ini dengan menempatkan Stringliteral ke dalam segmen memori hanya baca khusus yang akan menyebabkan kesalahan memori jika upaya penulisan dilakukan.

Di Jawa ini dikenal sebagai magang. Kompiler Java di sini hanya mengikuti optimasi memori standar yang dilakukan oleh kompiler selama beberapa dekade. Dan untuk mengatasi masalah yang sama dari Stringliteral - literal ini yang sedang dimodifikasi saat runtime, Java hanya membuat Stringkelasnya tidak dapat diubah (mis. E, tidak memberi Anda setter yang memungkinkan Anda mengubah Stringkonten). StringTidak harus abadi jika pemusnahan Stringliteral tidak terjadi.

deHaar
sumber
3
Saya sangat tidak setuju tentang kekekalan dan komentar threading, menurut saya Anda tidak cukup mengerti maksudnya. Dan jika Josh Bloch, salah satu pelaksana Java, mengatakan bahwa itu adalah salah satu masalah desain, bagaimana itu bisa salah informasi?
javashlook
1
Sinkronisasi mahal. Referensi ke objek yang bisa berubah harus disinkronkan, tidak demikian untuk tidak berubah. Itulah alasan untuk membuat semua objek tidak berubah kecuali mereka harus bisa berubah. String dapat berubah, dan karenanya melakukan itu membuatnya lebih efisien dalam banyak utas.
David Thornley
5
@ Jim: Optimalisasi memori bukan alasan 'THE', itu alasan 'A'. Keamanan benang juga merupakan alasan 'A', karena objek yang tidak dapat diubah secara inheren aman untuk benang dan tidak memerlukan sinkronisasi yang mahal, seperti yang disebutkan David. Keamanan benang sebenarnya adalah efek samping dari objek yang tidak dapat diubah. Anda dapat menganggap sinkronisasi sebagai cara untuk membuat objek "sementara" tidak berubah (ReaderWriterLock akan membuatnya hanya-baca, dan kunci biasa akan membuatnya tidak dapat diakses sama sekali, yang tentu saja membuatnya tidak berubah juga).
Triynko
1
@ Davidvidhorn: Penciptaan beberapa jalur referensi independen ke pemegang nilai yang dapat berubah secara efektif mengubahnya menjadi entitas, dan membuatnya lebih sulit untuk dipikirkan bahkan selain dari masalah threading. Secara umum, objek yang dapat berubah lebih efisien daripada objek yang tidak berubah dalam kasus di mana tepatnya satu jalur referensi akan ada untuk masing-masing, tetapi objek yang tidak dapat diubah memungkinkan konten objek untuk dibagikan secara efisien dengan berbagi referensi. Pola terbaik dicontohkan oleh Stringdan StringBuffer, tetapi sayangnya beberapa jenis lainnya mengikuti model itu.
supercat
7

String bukan tipe primitif, namun Anda biasanya ingin menggunakannya dengan semantik nilai, yaitu seperti nilai.

Nilai adalah sesuatu yang bisa Anda percayai tidak akan berubah di belakang Anda. Jika Anda menulis: String str = someExpr(); Anda tidak ingin itu berubah kecuali ANDA melakukan sesuatu dengannya str.

Stringsebagai Objectmemiliki pointer semantik alami, untuk mendapatkan nilai semantik juga perlu berubah.

deHaar
sumber
7

Salah satu faktornya adalah, jika String s dapat berubah, benda yang menyimpan Strings harus berhati-hati untuk menyimpan salinan, jangan sampai perubahan data internal mereka tanpa pemberitahuan. Mengingat bahwa Strings adalah tipe seperti angka yang cukup primitif, alangkah baiknya bila seseorang dapat memperlakukannya seolah-olah nilai-nilai itu dilewatkan, bahkan jika dilewatkan dengan referensi (yang juga membantu menghemat memori).

Evan DiBiase
sumber
6

Aku tahu ini benjolan, tapi ... Apakah mereka benar-benar abadi? Pertimbangkan yang berikut ini.

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

Anda bahkan bisa menjadikannya metode ekstensi.

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

Yang membuat pekerjaan berikut ini

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

Kesimpulan: Mereka dalam kondisi tidak berubah yang dikenal oleh kompiler. Tentu saja di atas hanya berlaku untuk string .NET karena Java tidak memiliki pointer. Namun string dapat sepenuhnya dapat diubah menggunakan pointer di C #. Ini bukan bagaimana pointer dimaksudkan untuk digunakan, memiliki penggunaan praktis atau digunakan dengan aman; Namun itu mungkin, sehingga menekuk seluruh aturan "bisa berubah". Anda biasanya tidak dapat mengubah indeks langsung dari string dan ini adalah satu-satunya cara. Ada cara yang bisa dicegah dengan melarang instance pointer dari string atau membuat salinan ketika string diarahkan, tetapi tidak dilakukan, yang membuat string dalam C # tidak sepenuhnya tidak dapat berubah.

Bauss
sumber
1
+1. .NET string tidak benar-benar berubah; sebenarnya, ini dilakukan setiap saat di kelas String dan StringBuilder karena alasan perf.
James Ko
3

Untuk sebagian besar tujuan, "string" adalah (digunakan / diperlakukan sebagai / dianggap / dianggap) unit atom yang bermakna , seperti halnya angka .

Bertanya mengapa karakter individu dari string tidak bisa berubah adalah seperti menanyakan mengapa bit individu dari integer tidak bisa berubah.

Anda harus tahu alasannya. Pikirkan saja itu.

Saya benci mengatakannya, tapi sayangnya kami memperdebatkan ini karena bahasa kami payah, dan kami mencoba menggunakan satu kata, string , untuk menggambarkan konsep atau kelas objek yang kompleks dan terletak secara kontekstual.

Kami melakukan perhitungan dan perbandingan dengan "string" yang mirip dengan yang kami lakukan dengan angka. Jika string (atau bilangan bulat) bisa berubah, kita harus menulis kode khusus untuk mengunci nilai-nilai mereka ke dalam formulir lokal yang tidak dapat diubah untuk melakukan segala jenis perhitungan dengan andal. Oleh karena itu, yang terbaik adalah memikirkan string seperti pengenal angka, tetapi alih-alih panjangnya 16, 32, atau 64 bit, panjangnya bisa ratusan bit.

Ketika seseorang mengatakan "string", kita semua memikirkan hal-hal yang berbeda. Mereka yang menganggapnya hanya sebagai seperangkat karakter, tanpa tujuan tertentu, tentu akan terkejut bahwa seseorang hanya memutuskan bahwa mereka seharusnya tidak dapat memanipulasi karakter tersebut. Tetapi kelas "string" bukan hanya array karakter. Ini STRING, bukan char[]. Ada beberapa asumsi dasar tentang konsep yang kita sebut sebagai "string", dan secara umum dapat digambarkan sebagai unit atom yang bermakna dari data kode seperti angka. Ketika orang berbicara tentang "memanipulasi string", mungkin mereka benar-benar berbicara tentang memanipulasi karakter untuk membangun string , dan StringBuilder sangat bagus untuk itu.

Pertimbangkan sejenak bagaimana rasanya jika string bisa berubah. Fungsi API berikut ini dapat diperdaya untuk mengembalikan informasi untuk pengguna yang berbeda jika string nama pengguna yang dapat diubah secara sengaja atau tidak sengaja diubah oleh utas lain ketika fungsi ini menggunakannya:

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

Keamanan bukan hanya tentang 'kontrol akses', tetapi juga tentang 'keamanan' dan 'menjamin kebenaran'. Jika suatu metode tidak dapat dengan mudah ditulis dan diandalkan untuk melakukan perhitungan atau perbandingan sederhana dengan andal, maka tidak aman untuk menyebutnya, tetapi akan aman untuk mempertanyakan bahasa pemrograman itu sendiri.

Triynko
sumber
Dalam C #, sebuah string dapat diubah oleh penunjuknya (gunakan unsafe) atau hanya melalui refleksi (Anda bisa mendapatkan bidang yang mendasarinya). Hal ini membuat titik pada kekosongan keamanan, karena siapa saja yang sengaja ingin mengubah string, dapat melakukannya cukup mudah. Namun, ini memberikan keamanan bagi programmer: kecuali jika Anda melakukan sesuatu yang istimewa, string dijamin tidak dapat diubah (tetapi bukan threadsafe!).
Abel
Ya, Anda dapat mengubah byte dari objek data apa pun (string, int, dll.) Melalui pointer. Namun, kita berbicara tentang mengapa kelas string tidak dapat diubah dalam arti bahwa tidak ada metode publik yang dibuat untuk memodifikasi karakternya. Saya mengatakan bahwa string sangat mirip angka dalam memanipulasi karakter individu tidak lebih masuk akal daripada memanipulasi bit individu dari suatu angka (ketika Anda memperlakukan string sebagai token keseluruhan (bukan sebagai byte array), dan angka sebagai nilai numerik (bukan sebagai bidang bit). Kita berbicara di tingkat objek konseptual, bukan di tingkat sub-objek.
Triynko
2
Dan hanya untuk memperjelas, pointer dalam kode berorientasi objek secara inheren tidak aman, tepatnya karena mereka menghindari antarmuka publik yang ditentukan untuk kelas. Apa yang saya katakan, adalah bahwa suatu fungsi dapat dengan mudah diakali jika antarmuka publik untuk sebuah string memungkinkannya untuk dimodifikasi oleh utas lainnya. Tentu saja, itu selalu bisa diakali dengan mengakses data secara langsung dengan pointer, tetapi tidak dengan mudah atau tidak sengaja.
Triynko
1
'pointer dalam kode berorientasi objek secara inheren tidak aman' kecuali jika Anda menyebutnya referensi . Referensi di Java tidak berbeda dengan pointer di C ++ (hanya pointer aritmatika yang dinonaktifkan). Konsep yang berbeda adalah manajemen memori yang dapat dikelola atau manual, tetapi itu adalah hal yang berbeda. Anda dapat memiliki referensi semantik (pointer tanpa aritmatika) tanpa memiliki GC (yang sebaliknya akan lebih sulit dalam arti bahwa semantik jangkauan akan lebih sulit untuk membuat bersih, tetapi tidak terhindarkan)
David Rodríguez - dribea
Hal lain adalah bahwa jika string hampir tidak berubah, tetapi tidak begitu, (saya tidak cukup tahu CLI di sini), itu bisa sangat buruk untuk alasan keamanan. Dalam beberapa implementasi Java yang lebih lama, Anda dapat melakukannya, dan saya menemukan potongan kode yang menggunakannya untuk menginternalisasi string (coba temukan string internal lain yang memiliki nilai yang sama, bagikan pointer, lepaskan blok memori lama) dan gunakan backdoor untuk menulis ulang isi string yang memaksa perilaku yang salah di kelas yang berbeda. (Pertimbangkan menulis ulang "SELECT *" ke "DELETE")
David Rodríguez - dribeas
3

Kekekalan tidak terkait erat dengan keamanan. Untuk itu, setidaknya di .NET, Anda mendapatkan SecureStringkelas.

Sunting nanti: Di ​​Jawa Anda akan menemukan GuardedString , implementasi yang serupa.

Andrei Rînea
sumber
2

Keputusan untuk memiliki string yang bisa berubah dalam C ++ menyebabkan banyak masalah, lihat artikel yang sangat bagus ini oleh Kelvin Henney tentang Penyakit SAPI Gila .

SAPI = Salin Saat Menulis.

Motti
sumber
2

Ini adalah trade off. Strings pergi ke Stringkolam dan ketika Anda membuat beberapa identikString mereka berbagi memori yang sama. Para perancang memperkirakan teknik penghematan memori ini akan bekerja dengan baik untuk kasus umum, karena program-program cenderung untuk mengerjakan banyak hal yang sama.

Kelemahannya adalah bahwa rangkaian membuat banyak tambahan Stringyang hanya transisi dan hanya menjadi sampah, benar-benar merusak kinerja memori. Anda memiliki StringBufferdan StringBuilder(dalam Java, StringBuilderjuga dalam. NET) untuk digunakan untuk menyimpan memori dalam kasus ini.

aaronroyer
sumber
1
Perlu diingat bahwa "kumpulan string" tidak secara otomatis digunakan untuk SEMUA string kecuali Anda secara eksplisit menggunakan string "inter ()".
jsight
2

Strings di Java tidak benar-benar berubah, Anda dapat mengubah nilainya menggunakan refleksi dan atau pemuatan kelas. Anda tidak boleh bergantung pada properti itu untuk keamanan. Sebagai contoh, lihat: Trik Sulap Di Jawa

deHaar
sumber
1
Saya percaya bahwa Anda hanya akan dapat melakukan trik seperti itu jika kode Anda berjalan dengan kepercayaan penuh, karena itu tidak ada kerugian keamanan. Anda juga bisa menggunakan JNI untuk menulis langsung di lokasi memori tempat string disimpan.
Antoine Aubry
Sebenarnya saya percaya Anda dapat mengubah objek abadi dengan refleksi.
Gqqnbig
0

Kekekalan baik. Lihat Java Efektif. Jika Anda harus menyalin sebuah String setiap kali Anda menyebarkannya, maka itu akan menjadi banyak kode rawan kesalahan. Anda juga bingung tentang modifikasi mana yang memengaruhi referensi mana. Dengan cara yang sama seperti Integer harus tetap untuk berperilaku seperti int, Strings harus berperilaku tidak berubah untuk bertindak seperti primitif. Dalam C ++ melewatkan string dengan nilai melakukan ini tanpa menyebutkan secara eksplisit dalam kode sumber.

Tom Hawtin - tackline
sumber
0

Ada pengecualian untuk hampir setiap aturan:

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}
Lu4
sumber
-1

Ini sebagian besar karena alasan keamanan. Jauh lebih sulit untuk mengamankan sistem jika Anda tidak dapat mempercayai bahwa Anda Stringrusak.

jsight
sumber
1
Bisakah Anda memberi contoh apa yang Anda maksud dengan "tamperproof". Jawaban ini terasa sangat di luar konteks.
Gergely Orosz