“Text” ve new String (“text”) arasındaki fark nedir?


195

Aşağıdaki iki ifade arasındaki fark nedir?

String s = "text";

String s = new String("text");


Herkes lütfen bunu cevapla. Dize a = "Java"; String b = "Java"; System.out.println (a == b); true // ancak System.out.println ("a == b?" + a == b); // yanlış ...
Enerji

bazı yorum eklediğimde anlamıyorum ("a == b?) => sonuç YANLIŞ olur. neden?
Enerji

@Energy Sonuç olarak, falseişlem sırası + operatörünün önce gittiğini ve "a == b?" ile bir dize oluşturmak için "a == b? Java". Sonra ifade "a==b?Java" == bfalse olarak değerlendirilir.
Allison B

@AllisonB anladı, çok teşekkür ederim!
Enerji

Yanıtlar:


187

new String("text"); açıkça bir Stringnesnenin yeni ve referans olarak farklı bir örneğini oluşturur ; varsa, sabit dize havuzundanString s = "text"; bir örneği yeniden kullanabilir.

Sen çok nadiren hiç kullanmak istersiniz new String(anotherString)yapıcısı. API'dan:

String(String original): Yeni oluşturulan bir String nesneyi, bağımsız değişkenle aynı karakter sırasını temsil edecek şekilde başlatır ; diğer bir deyişle, yeni oluşturulan dize argüman dizesinin bir kopyasıdır. Orijinalin açık bir kopyası gerekmedikçe, dizeler değişmez olduğundan bu yapıcıyı kullanmak gereksizdir.

İlgili sorular


Referans ayrımının anlamı

Aşağıdaki snippet'i inceleyin:

    String s1 = "foobar";
    String s2 = "foobar";

    System.out.println(s1 == s2);      // true

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true

==iki referans tipinde bir referans kimlik karşılaştırmasıdır. equalsMutlaka gerekli olmayan iki nesne ==. ==Referans türlerinde kullanmak genellikle yanlıştır ; çoğu zaman equalsbunun yerine kullanılması gerekir.

Bununla birlikte, herhangi bir nedenle iki dize oluşturmanız gerekir, equalsancak ==dize oluşturmazsanız , yapıcıyı kullanabilirsiniz new String(anotherString). Bununla birlikte, bunun çok tuhaf olduğu ve nadiren niyet olduğu tekrar söylenmelidir .

Referanslar

İlgili konular


3
İ yazarsam: String s = new String ("abc"); Ve şimdi yazıyorum: String s = "abc"; Will String s = "abc"; dize havuzunda yeni bir dize değişmez oluşturmak?
Kaveesh Kanwal

Neden kimse önceki soruya cevap vermiyor?
zeds

2
@KaveeshKanwal Hayır, hazır bilgi kopyalanmayacak. Gördüğünüz gibi 2 "abc"s var. Bunlardan yalnızca biri String havuzuna gider, diğeri ise havuzuna başvurur. Sonra suygun yeni nesne olacak.
Kayaman

1
@Kaveesh Kanwal - String s = new String ("abc") yalnızca "abc" değerine sahip yeni bir String nesnesi oluşturur. Ve 2. deyim, String Pool'da zaten bir "abc" string değişmezinin bulunup bulunmadığını kontrol eder. Eğer zaten mevcutsa, mevcut olana referans döndürülür ve eğer değilse o zaman String havuzunda yeni değişmez değer ("abc") oluşturulur. Umarım sorgunuzu çözer !!
user968813

Bu konuda 'mayıs' diye bir şey yok. Derleyici dize değişmezlerini bir araya getirmelidir . JLS 3.10.5 .
Lorne Marquis

119

Dize değişmezleri Dize Sabit Havuzu'na gider .

Aşağıdaki fotoğraf daha uzun süre hatırlamak için görsel olarak anlamanıza yardımcı olabilir .

resim açıklamasını buraya girin


Nesne oluşturma satır satır:

String str1 = new String("java5");

Yapıcıda dize değişmezi "java5" kullanarak, dize sabit havuzunda yeni bir dize değeri saklanır. Yeni işleç kullanılarak, yığınta değer olarak "java5" ile yeni bir dize nesnesi oluşturulur.

String str2 = "java5"

"Str2" başvurusu, dize sabit havuzunda önceden depolanmış değere işaret ediyor

String str3 = new String(str2);

Yığında "str2" referansıyla aynı değere sahip yeni bir dize nesnesi oluşturulur

String str4 = "java5";

Referans "str4", dize sabit havuzunda önceden depolanmış değere işaret ediyor

Toplam nesne: Yığın - 2, Havuz - 1

Oracle topluluğu hakkında daha fazla okuma


1
İyi cevap .. ama şimdi str1 = "java6" değerini değiştirmek için giong olduğumu bilmek weant sonra str4 değerini değiştirir?
CoronaPintu

2
evet ben str4 değerini değiştirmeyecek kontrol vardı
CoronaPintu

@Braj Yanıtlayanınızın iddiasının belgelerini verebilir misiniz?
Basil Bourque

@Braj: Tablodaki 'Yığın' ve 'havuz' başlıklarının ters olması mı gerekiyor?
Rahul Kurup

Doğru değil. Sabit havuz, yürütme zamanında değil, derleme zamanında oluşturulur. Alıntı yapılmayan metinler için alıntı biçimlendirme kullanmayın.
Lorne Marquis

15

Bir de bir String oluşturur Dize Constant Havuzu

String s = "text";

diğeri sabit havuzda ( "text") bir dize ve normal yığın alanında ( s) başka bir dize oluşturur . Her iki dize de "metin" ile aynı değere sahip olacaktır.

String s = new String("text");

s daha sonra kullanılmazsa kaybolur (GC için uygundur).

Öte yandan dize değişmezleri yeniden kullanılır. "text"Sınıfınızın birden çok yerinde kullanırsanız , aslında bir ve sadece bir String (havuzdaki aynı dizeye birden fazla referans) olacaktır.


Sabit havuzdaki dizeler asla kaybolmaz. Daha sonra kullanılmazsa 's' nin kaybolduğunu mu demek istediniz?
Marquis of Lorne

@EJP: evet, "s" demek istedim. Fark ettiğiniz için teşekkürler. Soruyu düzeltirim.

9

JLS

Konsept JLS tarafından "stajyerlik" olarak adlandırılır.

JLS 7'den ilgili pasaj 3.10.5 :

Ayrıca, bir dizgi değişmezi her zaman String sınıfının aynı örneğini ifade eder. Bunun nedeni, dize değişmezlerinin - veya daha genel olarak, sabit ifadelerin (§15.28) değerleri olan dizelerin, String.intern yöntemini kullanarak benzersiz örnekleri paylaşmak için "interned" olmasıdır.

Örnek 3.10.5-1. Dize Değişmezleri

Derleme biriminden oluşan program (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

ve derleme birimi:

package other;
public class Other { public static String hello = "Hello"; }

çıktı üretir:

true true true true false true

JVM'lerle

JVMS 7 5.1 diyor ki :

Dize değişmezi, String sınıfının bir örneğine başvurudır ve bir sınıfın veya arabirimin ikili gösterimindeki CONSTANT_String_info yapısından (§4.4.3) türetilir. CONSTANT_String_info yapısı, dizgi değişmezini oluşturan Unicode kod noktalarının sırasını verir.

Java programlama dili, aynı dize değişmezlerinin (yani, aynı kod noktaları sırasını içeren değişmez değerlerin) aynı Dize sınıfı örneğine (JLS §3.10.5) başvurmasını gerektirir. Ayrıca, String.intern yöntemi herhangi bir dizede çağrılırsa, sonuç, bu dize değişmez olarak görünürse döndürülecek aynı sınıf örneğine başvurudur. Bu nedenle, aşağıdaki ifade true değerine sahip olmalıdır:

("a" + "b" + "c").intern() == "abc"

Bir dizgi değişmezi türetmek için Java Sanal Makinesi, CONSTANT_String_info yapısı tarafından verilen kod noktalarının sırasını inceler.

  • String.intern yöntemi daha önce, CONSTANT_String_info yapısı tarafından verilenle aynı Unicode kod noktaları dizisini içeren bir sınıf String örneğinde çağrılmışsa, dize değişmez türetmesinin sonucu, aynı sınıf String örneğine başvurudur.

  • Aksi takdirde, CONSTANT_String_info yapısı tarafından verilen Unicode kod noktalarının sırasını içeren yeni bir String sınıfı örneği oluşturulur; bu sınıf örneğine başvuru, dizgi değişmezinin türetilmesinin sonucudur. Son olarak, yeni String örneğinin stajyer yöntemi çağrılır.

Bytecode

OpenJDK 7'deki bayt kodu uygulamasına bakmak da öğreticidir.

Biz kodalar:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

sürekli havuzumuz var:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

ve main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Nasıl yapıldığına dikkat edin:

  • 0ve 3: aynı ldc #2sabit yüklenir (değişmez değerler)
  • 12: yeni bir dize örneği oluşturulur ( #2argüman olarak)
  • 35: ave cnormal nesnelerle karşılaştırılır.if_acmpne

Sabit dizelerin temsili bayt kodunda oldukça büyülüdür:

ve yukarıdaki JVMS alıntısı, Utf8'in işaret ettiği her zaman aynı olduğunda, aynı örneklerin yüklendiğini söylüyor ldc.

Alanlar için benzer testler yaptım ve:

  • static final String s = "abc"ConstantValue Özelliği aracılığıyla sabit tabloyu gösterir
  • son olmayan alanlar bu özelliğe sahip değildir, ancak yine de ile başlatılabilir ldc

Sonuç : dize havuzu için doğrudan bayt kodu desteği vardır ve bellek gösterimi etkilidir.

Bonus: bunu doğrudan bayt kodu desteği olmayan (yani analog olmayan ) Tamsayı havuzuyla karşılaştırın CONSTANT_String_info.


2

@Braj: Sanırım başka bir yoldan bahsettiniz. Yanlışım varsa lütfen düzelt

Nesne oluşturma satır satır:

Dize str1 = yeni Dize ("java5")

   Pool- "java5" (1 Object)

   Heap - str1 => "java5" (1 Object)

Dize str2 = "java5"

  pool- str2 => "java5" (1 Object)

  heap - str1 => "java5" (1 Object)

String str3 = yeni Dize (str2)

  pool- str2 => "java5" (1 Object)

  heap- str1 => "java5", str3 => "java5" (2 Objects)

Dize str4 = "java5"

  pool - str2 => str4 => "java5" (1 Object)

  heap - str1 => "java5", str3 => "java5" (2 Objects)

str1değerine dahil edilmez str2veya str3veya str4herhangi bir şekilde.
Lorne Marquis

1

"bla"Sihirli bir fabrika olmayı düşünün Strings.createString("bla")(sahte). Fabrika, bu şekilde oluşturulan tüm dizelerden oluşan bir havuz tutuyor.

Eğer çağrılırsa, havuzda bu değere sahip dize olup olmadığını kontrol eder. True olursa, bu dize nesnesini döndürür, dolayısıyla bu yolla elde edilen dize aslında aynı nesnedir.

Değilse, dahili olarak yeni bir dize nesnesi oluşturur, bunu havuza kaydeder ve sonra döndürür. Böylece, bir dahaki sefere aynı dize değeri sorgulandığında, aynı örneği döndürür.

El ile oluşturma new String(""), dize hazır bilgi havuzunu atlayarak bu davranışı geçersiz kılar. Bu nedenle, eşitlik her zaman equals()nesne referans eşitliği yerine karakter dizisini karşılaştıran kontrol edilmelidir .


Bahsettiğiniz 'sihirli fabrika' Java derleyicisinden başka bir şey değildir. Bu işlemin çalışma zamanında meydana gelmiş gibi yazılması bir hatadır.
Lorne Marquis

1

Farkı anlamanın basit bir yolu aşağıdadır: -

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

çıktı

s==s1 is true
s==s2 is false

Böylece yeni String () her zaman yeni bir örnek oluşturur.


1

Herhangi bir String değişmezi, string değişmez havuzu içinde oluşturulur ve havuz yinelemelere izin vermez. Dolayısıyla, iki veya daha fazla dize nesnesi aynı değişmez değerle başlatılırsa, tüm nesneler aynı değişmezi gösterecektir.

String obj1 = "abc";
String obj2 = "abc";

"obj1" ve "obj2" aynı dize değişmezine işaret edecek ve dize değişmez havuzu yalnızca bir "abc" değişmezine sahip olacaktır.

Yeni anahtar sözcüğü kullanarak bir String sınıfı nesnesi oluşturduğumuzda, bu şekilde oluşturulan dize yığın belleğinde saklanır. Ancak String sınıfının yapıcısına parametre olarak iletilen herhangi bir dize değişmezi dize havuzunda depolanır. Yeni işleçle aynı değeri kullanarak birden çok nesne oluşturursak, her seferinde öbekte yeni bir nesne oluşturulacaktır, çünkü bu yeni işleçten kaçınılmalıdır.

String obj1 = new String("abc");
String obj2 = new String("abc");

"obj1" ve "obj2" öbekteki iki farklı nesneyi işaret edecek ve dize değişmez havuzunda yalnızca bir "abc" değişmezi bulunacaktır.

Ayrıca, dizelerin davranışı ile ilgili kayda değer bir şey, dizede yapılan herhangi bir yeni atama veya birleştirmenin bellekte yeni bir nesne oluşturmasıdır.

String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";

Şimdi yukarıdaki durumda:
Satır 1: "abc" değişmezi dize havuzunda depolanır.
Satır 2: "abcdef" değişmezi dize havuzunda depolanır.
Satır 3: Dize havuzunda yeni bir "xyz" değişmezi depolanır ve "str1" bu değişmezi göstermeye başlar.
Satır 4: Değer başka bir değişkene eklenerek üretildiğinden, sonuç yığın belleğinde saklanır ve "ghi" eklenen değişmez değer, dize havuzundaki varlığı için denetlenir ve içinde bulunmadığından oluşturulur. yukarıdaki durum.


0

Bir programcı açısından aynı görünse de, büyük performans etkisi vardır. İlk formu neredeyse her zaman kullanmak istersiniz.


0
String str = new String("hello")

Dize sabit havuz zaten Dize "merhaba" içerip içermediğini kontrol eder? Varsa, String sabit havuzuna bir girdi eklemez. Mevcut değilse, String sabit havuzuna bir girdi ekler.

Yığın bellek alanında bir nesne oluşturulur ve str oluşturulacak ve yığın bellek konumunda oluşturulan nesneye referans noktaları olacaktır.

strString sabit havuzunda bulunan bir nokta nesnesine başvuru yapmak istiyorsanız , o zaman açıkça çağrılmalıdırstr.intern();

String str = "world";

Dize sabit havuz zaten Dize "merhaba" içerip içermediğini kontrol eder? Varsa, String sabit havuzuna bir girdi eklemez. Mevcut değilse, String sabit havuzuna bir girdi ekler.

Yukarıdaki her iki durumda da, strbaşvuru "world"Sabit havuzda bulunan Dize'ye işaret eder .


'Bu' Java derleyicisidir. Dize değişmezi, derleme zamanında sabit havuzda benzersiz bir girdi oluşturur. Bu süreci sanki zamanında gerçekleşiyormuş gibi açıklamak yanlıştır ..
Lorne Marquis

Lütfen bu yazıda neyin yanlış olduğunu açıklayabilir misiniz?
Jayesh

Bu yazıda yanlış olan şey, daha önce de söylediğim gibi, dize değişmezinin tam zamanında toplanmasıdır. Cevabınızdaki gibi kodu yürütürken değil.
Lorne Marquis

@EJP Yanıtınız için teşekkür ederim. Lütfen cevapta yanlış olan kesin çizgiyi gösterebilir misiniz? Yukarıdaki tüm cevapların yazdıklarım ile aynı olduğunu görüyorum. Lütfen yardım et, anlayışımı düzeltmek istiyorum. Teşekkürler.
Jayesh

Tüm süreç hakkında, kod satırı yürütüldüğünde, sanki tekrar tekrar söylediğim gibi, durum böyle değil gibi gerçekleşmiş gibi yazdınız. Tüm bunları cevabınızda yanlış olan tek bir 'kesin çizgiye' indirgeyemezsiniz.
Lorne Marquis

0

Bir Dizeyi şu şekilde sakladığınızda

String string1 = "Hello";

doğrudan, daha sonra JVM, String sabit havuz adı verilen ayrı bir bellek bloğu sırasında verilen fiyatla bir String nesnesi oluşturur.

Ve ne zaman başka bir String üretmeye

String string2 = "Hello";

JVM, yepyeni bir nesne yapmak yerine String sabit havuzunda sabit fiyatlı herhangi bir String nesnesinin bulunup bulunmadığını doğrular. JVM, varolan nesnenin başvurusunu yeni değişkene atar.

Ve String'i

String string = new String("Hello");

yeni anahtar kelimeyi kullanarak, String sabit havuzunun içeriği ne olursa olsun, verilen fiyata yepyeni bir nesne yapılır.

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.