Neden Java ve .NET'te dizeler değiştirilemiyor?


191

Neden StringJava ve .NET'te (ve diğer bazı dillerde) değişmez olmaya karar verdiler ? Neden değişebilir hale getirmediler?


13
Aynı düşünceye sahiptim, ama orjinal posterlerin yerini kontrol ettim ve Belçika'dan olduklarını gördüm. Bunun anlamı, ana dili İngilizce olan biri olmaları muhtemel değildir. Çoğu yerlinin dili gevşek bir şekilde kavramasıyla birleştiğinde, onu biraz gevşetmeye karar verdim.
belugabob

8
Teşekkür ederim belugabob, ama ben o değilim ben o benim. Görünüşe göre insanlar kültürel farklılıkları dikkate almıyorlar.
chrissie1

7
Özür dilerim - chrissie (genellikle) İngiltere'de bir kızın adı - beni başka bir kültürel farkın kurbanı yapıyor :-)
belugabob

Sadece bir not, .NET'te Stringaslında dahili olarak değiştirilebilir. StringBuilder.NET 2.0'da bir dize değiştirir . Sadece burada bırakacağım.
Alvin Wong

Aslında .NET dizeleri vardır değişken. Ve bu bir hack heck bile değil.
Bitterblue

Yanıtlar:


205

Göre Etkili Java , bölüm 4, sayfa 73, 2. baskı:

"Bunun pek çok iyi nedeni var: Değişmez sınıfların tasarlanması, uygulanması ve kullanılması değiştirilebilir sınıflardan daha kolaydır. Hataya daha az eğilimlidirler ve daha güvenlidirler.

[...]

" Değişmez nesneler basittir. Değişmez bir nesne tam olarak tek bir durumda, oluşturulduğu durumda olabilir. Tüm kurucuların sınıf değişmezleri oluşturduğundan emin olursanız, bu değişmezlerin her zaman geçerli kalacağı garanti edilir. senin için çaba yok.

[...]

Değişmez nesneler doğası gereği iş parçacığı açısından güvenlidir; senkronizasyon gerektirmezler. Aynı anda erişen birden çok iş parçacığı tarafından bozulamazlar. Bu, iplik güvenliğini sağlamak için en kolay yaklaşımdır. Aslında hiçbir iplik, başka bir ipliğin değişmez bir nesne üzerindeki etkisini gözlemleyemez. Bu nedenle, değiştirilemeyen nesneler serbestçe paylaşılabilir

[...]

Aynı bölümdeki diğer küçük noktalar:

Sadece değişmeyen nesneleri paylaşmakla kalmaz, aynı zamanda içsel özelliklerini de paylaşabilirsiniz.

[...]

Değişmez nesneler değişebilir veya değişmez diğer nesneler için büyük yapı taşları oluşturur.

[...]

Değişmez sınıfların tek dezavantajı, her bir ayrı değer için ayrı bir nesne gerektirmesidir.


22
Cevabımın ikinci cümlesini okuyun: Değişmez sınıfları tasarlamak, uygulamak ve kullanmak, değiştirilebilir sınıflardan daha kolaydır. Hataya daha az eğilimlidirler ve daha güvenlidirler.
PRINCESS FLUFF

5
@PRINCESSFLUFF Değişken dizeleri paylaşmanın tek bir iş parçacığında bile tehlikeli olduğunu ekleyebilirim. Örneğin, bir rapor kopyalama: report2.Text = report1.Text;. Sonra, başka bir yerde, metni değiştirerek: report2.Text.Replace(someWord, someOtherWord);. Bu hem ilk raporu hem de ikinci raporu değiştirir.
phoog

10
@Sam, "neden değiştirilemezler" diye sormadı, bunun neden kusursuz cevap verdiğini "değişmez yapmaya karar verdiklerini" sordu.
James

1
@PRINCESSFLUFF Bu yanıt özellikle dizeleri ele almaz. OP sorusu buydu. Bu çok sinir bozucu - bu her zaman SO ve String değişmezliği soruları ile olur. Bu sorunun cevabı, değişmezliğin genel faydalarından bahsediyor. Öyleyse neden tüm türler değişmez değil? Lütfen geri dönüp String adresine gidebilir misin?
Howiecamp

@Howiecamp Bence dizeleri değişken olabilir cevap örtük olduğunu düşünüyorum (hiçbir şey varsayımsal mutable dize sınıfının varlığını engeller). Sadece bunu basitlik için yapmamaya karar verdiler ve% 99 kullanım vakasını kapsadıkları için. Diğer% 1 vakalar için hala StringBuilder sağladılar.
Daniel García Rubio

102

En az iki sebep var.

İlk - güvenlik http://www.javafaq.nu/java-article1060.html

String'in değişmez olmasının temel nedeni güvenlikti. Şu örneğe bakın: Giriş kontrolü ile bir dosya açma yöntemimiz var. Çağrı OS'ye geçmeden önce gerekli olan kimlik doğrulamasını işlemek için bu yönteme bir Dize iletiriz. String değiştirilebilirse, işletim sistemi programdan istek almadan önce kimlik doğrulama kontrolünden sonra içeriğini bir şekilde değiştirmek mümkün oldu, sonra herhangi bir dosya istemek mümkündür. Bu nedenle, kullanıcı dizininde metin dosyasını açma hakkınız varsa, ancak bir şekilde dosya adını değiştirmeyi başardığınızda anında "passwd" dosyasını veya başka bir dosyayı açmayı isteyebilirsiniz. Daha sonra bir dosya değiştirilebilir ve doğrudan işletim sistemine giriş yapmak mümkün olacaktır.

İkincisi - Bellek verimliliği http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

JVM dahili olarak "String Pool" u korur. Bellek verimliliğini elde etmek için JVM, String nesnesini havuzdan alacaktır. Yeni String nesnelerini oluşturmaz. Böylece, yeni bir dize hazır bilgisi oluşturduğunuzda, JVM havuzda zaten var olup olmadığını kontrol eder. Havuzda zaten varsa, aynı nesneye referans verin veya havuzda yeni bir nesne oluşturun. Aynı String nesnelerine işaret eden birçok başvuru olacaktır, biri değeri değiştirirse, tüm başvuruları etkiler. Böylece güneş onu değişmez yapmaya karar verdi.


Yeniden kullanım hakkında iyi bir nokta ve özellikle String.intern () kullanırsanız doğrudur. Tüm ipleri değişmez hale getirmeden tekrar kullanmak mümkün olurdu, ancak hayat bu noktada karmaşık hale gelme eğilimindedir.
jsight

3
Bunlardan hiçbiri bu gün ve yaşta benim için son derece geçerli nedenler gibi görünmüyor.
Brian Knoblauch

1
Ben bellek verimliliği argümanı tarafından çok ikna değilim (yani, iki veya daha fazla String nesnesi aynı verileri paylaşmak ve bir değiştirilir, sonra her ikisi de değiştirilir). MFC'deki CString nesneleri, referans sayma kullanarak bunu aşar.
RobH

7
güvenlik, değişmez dizeler için Raison d'être'nin bir parçası değildir - işletim sisteminiz dizeleri çekirdek modu arabelleklerine kopyalar ve zamanlama saldırılarını önlemek için orada erişim kontrolü yapar. Gerçekten iş parçacığı güvenliği ve performansı ile ilgili :)
snemarch

1
Bellek verimliliği argümanı da çalışmaz. C gibi bir yerel dilde, dize sabitleri basitçe başlatılmış veri bölümündeki verilere işaret eder - yine de salt okunur / değişmezdir. "Birisi değeri değiştirirse" - yine, havuzdaki dizeler yine de salt okunurdur.
wj32

57

Aslında, Java'da dize değişmez olmasının nedenlerinin güvenlikle ilgisi yoktur. İki ana neden şunlardır:

Thead Güvenliği:

Dizeler çok yaygın olarak kullanılan nesne türüdür. Bu nedenle, çok iş parçacıklı bir ortamda kullanılması az çok garanti edilir. Dizeleri, dizeler arasında paylaşmanın güvenli olduğundan emin olmak için değiştirilemez. Değişmez bir dizeye sahip olmak, ipleri A ipliğinden başka bir B ipliğine geçirirken, B ipliğinin beklenmedik bir şekilde A dişi ipliğini değiştirememesini sağlar.

Bu, çok iş parçacıklı programlamanın zaten oldukça karmaşık olan görevini basitleştirmeye yardımcı olmakla kalmaz, aynı zamanda çok iş parçacıklı uygulamaların performansına da yardımcı olur. Değişken nesnelere erişim, bir iş parçacığının, başka bir iş parçacığı tarafından değiştirilirken nesnenizin değerini okumaya çalışmadığından emin olmak için, birden çok iş parçacığından erişilebildiğinde bir şekilde eşitlenmelidir. Uygun senkronizasyonun programcı için doğru şekilde yapılması zordur ve çalışma zamanında pahalıdır. Değişmez nesneler değiştirilemez ve bu nedenle senkronizasyon gerekmez.

Verim:

String interning'den bahsedilse de, Java programları için bellek verimliliğinde sadece küçük bir kazanç sağlar. Yalnızca dizgi değişmezleri stajyerdir. Bu, yalnızca kaynak kodunuzda aynı olan dizelerin aynı Dize Nesnesini paylaşacağı anlamına gelir . Programınız dinamik olarak aynı dize oluşturursa, bunlar farklı nesnelerde temsil edilir.

Daha da önemlisi, değiştirilemeyen dizeler dahili verilerini paylaşmalarını sağlar. Birçok dize işlemi için bu, temeldeki karakter dizisinin kopyalanmasına gerek olmadığı anlamına gelir. Örneğin, String'in ilk beş karakterini almak istediğinizi varsayalım. Java'da myString.substring (0,5) öğesini çağırırsınız. Bu durumda, substring () yönteminin yaptığı, myString'in temelindeki char [] öğesini paylaşan ancak dizin 0'dan başlayıp o char [] dizin 5'inde sona erdiğini bilen yeni bir String nesnesi oluşturmaktır. Bunu grafik haline getirmek için aşağıdakilerle sonuçlanırsınız:

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

Bu, bu tür işlemleri son derece ucuz hale getirir ve O (1), işlem ne orijinal ipin uzunluğuna ne de çıkarmamız gereken alt dizenin uzunluğuna bağlı değildir. Birçok dizenin altında yatan char [] paylaşabildiğinden, bu davranışın bazı bellek faydaları da vardır.


6
Alt dizeleri temelini paylaşan referanslar olarak uygulamak char[]oldukça tartışmalı bir tasarım kararıdır. Bir dosyanın tamamını tek bir dizede okur ve yalnızca 1 karakterlik bir alt dizeye başvuruda bulunursanız, tüm dosyanın bellekte tutulması gerekir.
Gabe

5
Tam olarak, tüm sayfadan sadece birkaç kelime ayıklamak için gereken bir web sitesi tarayıcısı oluştururken bu özel gotcha ile karşılaştım. Tüm sayfa HTML kodu bellekte ve char [] paylaşan alt dize nedeniyle, sadece birkaç bayt gerekli olsa bile, tüm HTML kodunu korudum. Bunun için bir çözüm yeni String (original.substring (.., ..)) kullanmaktır, String (String) yapıcı temel dizinin ilgili aralığının bir kopyasını oluşturur.
LordOfThePigs

1
Sonraki değişiklikleri kapsayan bir zeyilname: Jave 7'den bu yana, String.substring()yukarıdaki yorumlarda belirtilen sorunları önlemek için tam bir kopyasını gerçekleştirir. Java 8'de, iki sağlayan alanlar char[]olmak üzere, paylaşma countve offsetböylece dize örnekleri bellek izi azaltmak kaldırılır.
Christian Semrau

Thead Safety kısmına katılıyorum, ancak alt dize durumundan şüpheliyim.
Gqqnbig

@LoveRight: Daha sonra java.lang.String'in kaynak kodunu kontrol edin ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ), Java 6'ya kadar ( bu cevap yazıldığında günceldi). Görünüşe göre Java 7'de değiştim.
LordOfThePigs

28

İplik güvenliği ve performansı. Bir dize değiştirilemezse, bir başvuruyu birden çok iş parçacığı arasında geçirmek güvenli ve hızlıdır. Dizeler değiştirilebilir olsaydı, her zaman dizenin tüm baytlarını yeni bir örneğe kopyalamanız veya senkronizasyon sağlamanız gerekir. Tipik bir uygulama, bir dizginin her değiştirilmesi gerektiğinde bir dizeyi 100 kez okuyacaktır. Değişmezlik hakkında wikipedia'ya bakınız .



7

Vaov! Buradaki yanlış bilgiye inanamıyorum. Stringdeğişmez olmanın güvenlikle hiçbir ilgisi yoktur. Birisi çalışan bir uygulamadaki nesnelere zaten erişebiliyorsa (uygulamanızda 'hack' birisine karşı korumaya çalışıyorsanız varsaymanız Stringgerekir), kesinlikle hack için mevcut diğer birçok fırsat olacaktır.

Değişmezliğinin Stringdiş açma meselelerini ele alması oldukça yeni bir fikir . Hmmm ... İki farklı evre tarafından değiştirilen bir cisim var. Bunu nasıl çözerim? nesneye erişimi senkronize etmek? Naawww ... kimsenin nesneyi değiştirmesine izin vermeyelim - bu da dağınık eşzamanlılık sorunlarımızın hepsini çözecek! Aslında, tüm nesneleri değiştirilemez hale getirelim ve sonra senkronize edilmiş yapıyı Java dilinden kaldırabiliriz.

Asıl sebep (yukarıda başkaları tarafından işaret edilmiştir) bellek optimizasyonudur. Herhangi bir uygulamada, aynı dize hazır bilgisinin tekrar tekrar kullanılması oldukça yaygındır. Aslında, onlarca yıl önce, birçok derleyici Stringgerçek bir kelimenin tek bir örneğini saklama optimizasyonunu yaptı . Bu optimizasyonun dezavantajı, bir Stringdeğişmez değeri değiştiren çalışma zamanı kodunun, sorunu paylaşan tüm diğer kodlar için örneği değiştirmesi nedeniyle bir sorun oluşturmasıdır. Örneğin, bir uygulamadaki herhangi bir işlev için Stringdeğişmez değeri "dog"değiştirmek iyi olmaz "cat". Bir değişmezleri (yani onları değişmez olun). Bazı derleyiciler (işletim sistemi desteği ile) bunu yerleştirerekprintf("dog") , "cat"stdout'a yazılmasıyla sonuçlanır . Bu nedenle, değişmeye çalışan koda karşı korunmanın bir yolu olmalıStringString bir yazma girişiminde bulunulursa bellek hatasına neden olabilecek özel bir salt okunur bellek segmentine dönüştürür.

Java'da bu stajyerlik olarak bilinir. Buradaki Java derleyicisi, onlarca yıldır derleyiciler tarafından yapılan standart bir bellek optimizasyonunu izliyor. Ve bu Stringdeğişmezlerin çalışma zamanında değiştirilmesiyle ilgili aynı sorunu gidermek için , Java sadece Stringsınıfı değiştirilemez hale getirir (yani, Stringiçeriği değiştirmenize izin verecek ayarlayıcılar vermez ). StringDeğişmez Stringdeğerlerin stajyerinin gerçekleşmemesi durumunda değişmez olması gerekmez.


3
Değişmezlik ve ipucu yorumu hakkında kesinlikle katılmıyorum, bana öyle geliyor ki konuya tam olarak değinmiyorsunuz. Java uygulayıcılarından Josh Bloch, bunun tasarım sorunlarından biri olduğunu söylüyorsa, bu nasıl yanlış bilgi olabilir?
javashlook

1
Senkronizasyon pahalıdır. Değişken nesnelere yapılan göndermeler, değiştirilemez için senkronize edilmemelidir. Değişken olmaları gerekmedikçe tüm nesneleri değişmez hale getirmenin bir nedeni budur. Dizeler değişmez olabilir ve bu nedenle bunları yapmak birden çok iş parçacığında daha verimli olur.
David Thornley

5
@Jim: Bellek optimizasyonu 'THE' nedeni değil, 'A' nedeni. İş parçacığı güvenliği de 'A' nedenidir, çünkü değişmez nesneler doğal olarak iş parçacığı açısından güvenlidir ve David'in belirttiği gibi pahalı bir senkronizasyon gerektirmez. İplik güvenliği aslında bir nesnenin değişmez bir yan etkisidir. Senkronizasyonu, nesneyi "geçici olarak" değiştirilemez hale getirmenin bir yolu olarak düşünebilirsiniz (ReaderWriterLock, onu salt okunur hale getirecektir ve düzenli bir kilit, ona erişilemez hale getirecektir, bu da elbette onu değiştirilemez hale getirecektir).
Triynko

1
@DavidThornley: Değişken bir değer sahibine birden fazla bağımsız referans yolunun oluşturulması, onu etkin bir şekilde varlığa dönüştürür ve iş parçacığı sorunlarını bir kenara bırakmayı zorlaştırır. Genel olarak, değiştirilebilir nesneler, her birine tam olarak bir referans yolunun olacağı durumlarda değişmez olanlardan daha etkilidir, ancak değişmez nesneler, nesnelerin içeriklerinin referansları paylaşarak verimli bir şekilde paylaşılmasına izin verir. En iyi model ile örneklenmiştir Stringve StringBufferfakat ne yazık ki birkaç diğer türleri bu modeli takip edin.
supercat

7

String ilkel bir tür değildir, ancak normalde değer anlambilimiyle, yani bir değer gibi kullanmak istersiniz.

Değer, güvenebileceğiniz, arkanızda değişmeyecek bir şeydir. Eğer yazarsanız: String str = someExpr(); SİZE bir şey yapmadıkça değişmesini istemezsiniz str.

Stringbir şekilde Object, doğal olarak işaretçi semantik sahip iyi iletmenin olması gerekmektedir bu değer semantik alır.


7

Faktörlerden biri, eğer Stringdeğiştirilebiliyorsa, Strings depolayan nesnelerin , önceden haber vermeksizin kopyalarını saklamak için dikkatli olmaları gerektiğidir. S'nin Stringsayılar gibi oldukça ilkel bir tip olduğu göz önüne alındığında, referansla geçilse bile (bu da hafızada tasarruf edilmesine yardımcı olur) onlara değer olarak geçirilmiş gibi davranabilmek güzeldir.


6

Bunun bir çarpma olduğunu biliyorum, ama ... Gerçekten değişmezler mi? Aşağıdakileri göz önünde bulundur.

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

Hatta bunu bir uzantı yöntemi haline getirebilirsiniz.

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

Aşağıdaki işler yapan

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

Sonuç: Derleyici tarafından bilinen değişmez bir durumdalar. Java için işaretçiler olmadığından, yukarıdaki yalnızca .NET dizeleri için geçerlidir. Ancak bir dize C # 'daki işaretçiler kullanılarak tamamen değiştirilebilir. İşaretçilerin kullanılması amaçlanan, pratik kullanıma sahip olmayan veya güvenli bir şekilde kullanılan bu değildir; bununla birlikte, "değişebilir" kuralın tamamını bükmek mümkündür. Normalde doğrudan bir dizenin dizinini değiştiremezsiniz ve bu tek yoldur. Bu, dizelerin işaretçi örneklerine izin vermemek veya bir dize işaret edildiğinde bir kopya yapmakla önlenmenin bir yolu vardır, ancak ikisi de yapılmaz, bu da C # 'daki dizeleri tamamen değişmez kılar.


1
+1. .NET dizeleri gerçekten değişmez değildir; aslında, bu her zaman String ve StringBuilder sınıflarında mükemmel nedenlerle yapılır.
James Ko

3

Çoğu amaç için, bir "dize" anlamlı (kullanılır / düşünülür / olduğu varsayılır / kullanılır) tıpkı bir sayı gibi atomik birim .

Bir dizgenin tek tek karakterlerinin neden değiştirilemediğini sormak, bu nedenle bir tamsayının ayrı bitlerinin neden değiştirilemediğini sormak gibidir.

Nedenini bilmelisin. Bunun hakkında düşün.

Bunu söylemekten nefret ediyorum, ama maalesef bunu tartışıyoruz çünkü dilimiz berbat ve tek bir kelime kullanmaya çalışıyoruz, karmaşık, bağlamsal olarak konumlandırılmış bir kavramı veya nesne sınıfını tanımlamak için dize.

Sayılarla yaptığımız gibi "string" ile hesaplamalar ve karşılaştırmalar yapıyoruz. Dizeler (veya tamsayılar) değiştirilebilir olsaydı, her türlü hesaplamayı güvenilir bir şekilde yapmak için değerlerini değişmez yerel formlara kilitlemek için özel kod yazmamız gerekirdi. Bu nedenle, bir dizeyi sayısal bir tanımlayıcı gibi düşünmek en iyisidir, ancak 16, 32 veya 64 bit uzunluğunda olmak yerine yüzlerce bit uzunluğunda olabilir.

Birisi "string" dediğinde hepimiz farklı şeyler düşünürüz. Bunu akılda özel bir amacı olmayan bir dizi karakter olarak düşünenler elbette birisinin bu karakterleri manipüle etmemeleri gerektiğine karar verdiklerine şaşıracaklar . Ancak "string" sınıfı sadece bir karakter dizisi değildir. Bu bir STRING, değil char[]. "Dize" olarak adlandırdığımız kavram hakkında bazı temel varsayımlar vardır ve genellikle bir sayı gibi kodlanmış verilerin anlamlı, atomik birimi olarak tanımlanabilir. İnsanlar "dizeleri manipüle etme" hakkında konuştuğunda, belki de karakterleri dizeler oluşturmak için manipüle etmekten bahsediyorlar ve bir StringBuilder bunun için harika.

Bir an için dizelerin değişebilir olmasının nasıl olacağını düşünün. Değişken kullanıcı adı dizesi bu işlev kullanılırken başka bir iş parçacığı tarafından kasıtlı veya kasıtsız olarak değiştirilirse, aşağıdaki API işlevi farklı bir kullanıcı için bilgileri döndürmek için kandırılabilir :

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 );
    }
}

Güvenlik sadece 'erişim kontrolü' değil, aynı zamanda 'güvenlik' ve 'doğruluğu garanti etme' ile ilgilidir. Basit bir hesaplama veya karşılaştırmayı güvenilir bir şekilde yapmak için bir yöntem kolayca yazılamaz ve ona güvenilemezse, onu aramak güvenli değildir, ancak programlama dilinin kendisini sorgulamak güvenli olacaktır.


C # 'da, bir dize işaretçisi (kullanımı unsafe) veya basitçe yansıması ile değiştirilebilir (alttaki alanı kolayca alabilirsiniz). Bu, kasıtlı olarak bir dizeyi değiştirmek isteyen herkesin bunu kolayca yapabileceği için güvenlik konusundaki noktayı geçersiz kılar . Ancak, programcılara güvenlik sağlar: özel bir şey yapmadığınız sürece, dizenin değişmez olduğu garanti edilir (ancak threadsafe değildir!).
Abel

Evet, herhangi bir veri nesnesinin (dize, int, vb.) Baytlarını işaretçilerle değiştirebilirsiniz. Ancak, dize sınıfının, karakterlerini değiştirmek için yerleşik genel yöntemleri olmadığı için neden değişmez olduğundan bahsediyoruz. Bir dize çok sayıda tek karakterleri manipüle bir sayı tek tek bit (bir bayt dizisi olarak değil, bayt dizisi olarak değil) ve sayı olarak işlemek daha mantıklı olduğunu söylüyordu sayısal bir değer (bit alanı olarak değil) Kavramsal nesne düzeyinde, alt nesne düzeyinde değil
Triynko

2
Ve sadece açıklığa kavuşturmak için, nesne yönelimli koddaki işaretçiler doğası gereği güvensizdir, çünkü tam olarak bir sınıf için tanımlanan ortak arayüzleri atlarlar. Ne diyordum, bir dize için ortak arabirimi diğer iş parçacıkları tarafından değiştirilmesine izin verirse bir işlev kolayca kandırılabilir oldu. Elbette, verilere doğrudan işaretçilerle erişerek her zaman kandırılabilir, ancak kolay veya kasıtsız olamaz.
Triynko

1
'nesne yönelimli koddaki işaretçiler kendilerine referans demediğiniz sürece doğal olarak güvensizdir' . Java'daki başvurular C ++ 'daki işaretçilerden farklı değildir (yalnızca işaretçi aritmetiği devre dışıdır). Farklı bir konsept, yönetilebilen veya manuel olarak yapılabilen bellek yönetimidir, ancak bu farklı bir şeydir. GC'ye sahip olmadan referans semantiğe (aritmetik olmayan işaretçiler) sahip olabilirsiniz (tam tersine, okunabilirliğin anlambiliminin temiz hale getirilmesi daha zor, ancak mümkün olmayan bir şekilde olması daha zor olurdu)
David Rodríguez - dribeas

Diğer bir şey, eğer dizeler neredeyse değişmezse, ama tam olarak öyle değilse, (burada yeterince CLI bilmiyorum), bu güvenlik nedeniyle gerçekten kötü olabilir. Bazı eski Java uygulamalarında bunu yapabilirsiniz ve ben dizeleri içselleştirmek için kullanılan bir kod snippet'i buldum (aynı değere sahip diğer dahili dize bulmaya çalışın, işaretçiyi paylaşın, eski bellek bloğunu kaldırın) ve arka kapı kullanılır farklı bir sınıfta yanlış davranışa zorlayarak dize içeriğini yeniden yazmak için. ("SELECT *"
ifadesini

3

Değişmezlik, güvenlikle o kadar yakından bağlantılı değildir. Bunun için, en azından .NET'te, SecureStringsınıfı alırsınız .

Daha sonra düzenleme: Java'da GuardedStringbenzer bir uygulama bulacaksınız .


2

C ++ 'da dize değişebilme kararı birçok soruna neden olur, Kelvin Henney'nin Mad COW Hastalığı hakkındaki bu mükemmel makalesine bakın .

COW = Yazarken Kopyala.


2

Bu bir takas. Strings Stringhavuza gider ve birden çok özdeş oluşturduğunuzda Stringaynı belleği paylaşırlar. Tasarımcılar, bu bellek tasarrufu tekniğinin ortak durum için iyi çalışacağını düşündüler, çünkü programlar aynı dizeleri çok fazla öğütme eğilimindedir.

Dezavantajı, birleştirme Stringişlemlerinin sadece geçişli olan ve sadece çöp haline gelen, aslında bellek performansına zarar veren çok fazla fazla şey yapmasıdır. Bu durumlarda belleği korumak için kullanmak zorundasınız StringBufferve StringBuilder(Java'da StringBuilderda .NET'te bulunmaktadır).


1
"İnter ()" 'ed dizelerini açıkça kullanmadığınız sürece , "string pool" işlevinin ALL dizeleri için otomatik olarak kullanılmadığını unutmayın.
jsight

2

StringJava'daki değişkenler gerçekte değişmez değildir; yansıma ve / veya sınıf yüklemesi kullanarak değerlerini değiştirebilirsiniz. Güvenlik için bu özelliğe bağlı olmamalısınız. Örnekler için bakınız: Java'da Sihir Numarası


1
Sadece kodunuzu tam bir güven ile çalışıyorsa bu tür hileler yapabileceğinize inanıyorum, bu nedenle güvenlik kaybı yok. Dizelerin depolandığı bellek konumuna doğrudan yazmak için JNI kullanabilirsiniz.
Antoine Aubry

Aslında değişmez herhangi bir nesneyi yansıtarak değiştirebileceğinize inanıyorum.
Gqqnbig

0

Değişmezlik iyidir. Bkz. Etkili Java. Bir Dizeyi her geçirdiğinizde kopyalamanız gerekiyorsa, bu hataya çok açık bir kod olurdu. Hangi değişikliklerin hangi referansları etkilediği konusunda da karışıklığınız var. Tamsayının int gibi davranmak için değişmez olması gerektiği gibi, Dizeler de ilkel gibi davranmak için değişmez davranmalıdır. C + + 'da dizeleri değere göre iletmek, bunu kaynak kodunda açıkça belirtilmeden yapar.


0

Hemen hemen her kural için bir istisna vardır:

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();
            }
        }
    }
}

-1

Büyük ölçüde güvenlik nedeniyle. Sistemlerinizin Stringkurcalamaya dayanıklı olduğuna güvenemezseniz, bir sistemi güven altına almak çok daha zordur .


1
"Kurcalamaya dayanıklı" ile ne demek istediğinize bir örnek verebilir misiniz? Bu cevaplar gerçekten bağlam dışı geliyor.
Gergely Orosz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.