Oryginał: utworzyć ciąg Java za pomocą „” lub konstruktora? Autor: X Wang W Javie ciąg można utworzyć na dwa sposoby:
String x = "abc";
String y = new String("abc");
Jaka jest różnica między używaniem podwójnych cudzysłowów a używaniem konstruktora?
1. Podwójne cytaty vs. Konstruktor
Odpowiedź na to pytanie można uzyskać, analizując dwa proste przykłady. Przykład 1:String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
a==b
true, ponieważ a
oba b
odnoszą się do tego samego obiektu - łańcucha zadeklarowanego jako literał (literał ciągu poniżej) w obszarze metody (odsyłamy czytelnika do źródła w naszym zasobie: 8 najlepszych diagramów do zrozumienia języka Java , diagram 8). Kiedy ten sam literał ciągu jest tworzony więcej niż raz, w pamięci przechowywana jest tylko jedna kopia ciągu, tylko jedna jego instancja (w naszym przypadku „abcd”). Nazywa się to „internowaniem ciągów”. Wszystkie stałe łańcuchowe przetwarzane w czasie kompilacji są automatycznie internowane w Javie. Przykład 2:
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
c==d
false, ponieważ c
odnoszą d
się do dwóch różnych obiektów w pamięci (na stercie). Różne obiekty zawsze mają różne odniesienia. Ten diagram ilustruje dwie opisane powyżej sytuacje:
2. Internowanie ciągów na etapie realizacji programu
Autor dziękuje LukasEderowi (poniższy komentarz jest jego): Internowanie ciągów może nastąpić także podczas wykonywania programu, nawet jeśli za pomocą konstruktorów utworzone zostaną dwa ciągi:String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d); // Now true
System.out.println(c.equals(d)); // True
3. Kiedy używać cudzysłowów, a kiedy konstruktorów
Ze względu na to, że literał „abcd” jest zawsze typu String, użycie konstruktora spowoduje utworzenie dodatkowego, niepotrzebnego obiektu. Dlatego jeśli chcesz tylko utworzyć ciąg znaków, należy użyć podwójnych cudzysłowów. Jeśli rzeczywiście chcesz utworzyć nowy obiekt na stercie, powinieneś użyć konstruktora. Tutaj pokazane są przypadki użycia (oryginał) . (Poniżej zamieszczam przetłumaczony tekst. Jednak nadal gorąco polecam zapoznanie się z kodem komentatorów pod tym linkiem.)Metoda substring() w JDK 6 i JDK 7
Metoda substring() w JDK 6 i JDK 7 Autor: X Wang Metodasubstring(int beginIndex, int endIndex)
w JDK 6 i JDK 7 jest inna. Znajomość tych różnic może pomóc w lepszym korzystaniu z tej metody. Dla ułatwienia czytania poniżej substring()
będziemy mieli na myśli pełną składnię, tj. substring(int beginIndex, int endIndex)
.
1. Co robi substring()?
Metodasubstring(int beginIndex, int endIndex)
zwraca ciąg znaków rozpoczynający się od numeru beginIndex
i kończący się na znaku numer endIndex-1
.
String x = "abcdef";
x = x.substring(1,3);
System.out.println(x);
Wyjście:
bc
2. Co się stanie, gdy zostanie wywołana metoda substring()?
Być może wiesz, że ze względu na niezmiennośćx
, przypisując x wynik x.substring(1,3)
, x
wskazuje to na zupełnie nowy wiersz (patrz diagram): Jednak ten diagram nie jest całkowicie poprawny; nie pokazuje, co faktycznie dzieje się na stercie. To, co faktycznie dzieje się po wywołaniu, substring()
różni się w JDK 6 i JDK 7.
3. substring() w JDK 6
Typ ciągu jest obsługiwany przez typ tablicychar
. W JDK 6 klasa String
zawiera 3 pola: char value[]
, int offset
, int count
. Służą do przechowywania aktualnej tablicy znaków, indeksu pierwszego znaku w tablicy, liczby znaków w linii. Po wywołaniu metoda substring()
tworzy nowy wiersz, ale wartość zmiennej nadal wskazuje na tę samą tablicę na stercie. Różnica między dwoma ciągami znaków polega na liczbie znaków i wartości indeksu znaku początkowego w tablicy. Poniższy kod jest uproszczony i zawiera jedynie podstawy ilustrujące problem.
//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
4. Problem spowodowany substring() w JDK 6
Jeśli masz BARDZO długi ciąg, ale potrzebujesz tylko jego małej części, którą otrzymujesz za każdym razem, używającsubstring()
. Spowoduje to problemy z wykonaniem, ponieważ potrzebujesz tylko małej części, ale nadal musisz przechowywać cały ciąg. W przypadku JDK 6 rozwiązaniem jest poniższy kod, który rzutuje ciąg na prawdziwy podciąg:
x = x.substring(x, y) + ""
Użytkownik STepeR sformułował pytanie (patrz jego komentarz) i wydawało się konieczne dodanie punktu 4. „Problem spowodowany substring()
w JDK 6” jest bardziej rozbudowanym przykładem. Mam nadzieję, że to będzie odpowiedź i pomoże innym szybko dowiedzieć się, na czym polega problem. Oto kod:
String a = "aLongLongString";
String b = a.substring(1, 2);
String c = a.substring(2, 6);
Zatem w JDK 7 b
obiekty с
typu a String
utworzone przez wywołanie metody substring()
na obiekcie typu a String
będą odwoływać się do dwóch nowo utworzonych tablic na stercie - L
for b
, ongL
for c
. Te dwie nowe tablice będą przechowywane na stercie WRAZ z oryginalną tablicą, do aLongLongString
której odwołuje się a. Te. oryginalna tablica nigdzie nie znika. Wróćmy teraz do JDK 6. W JDK 6 sterta zawiera pojedynczą tablicę aLongLongString
. Po wykonaniu linii kodu
String b = a.substring(1, 2);
String c = a.substring(2, 6);
obiekty b
odnoszą się c
do tej samej tablicy w stercie, odpowiadającej obiektowi a
: b
- do elementów od 1. do 2. indeksu, c
- do elementów od 2. do 6. indeksu (numeracja zaczyna się od 0, dla przypomnienia). Te. Oczywiście każdy dalszy dostęp do zmiennych b
lub c w JDK 6 faktycznie spowoduje, że żądane elementy oryginalnej tablicy zostaną „wliczone” do sterty. W JDK 7 dalszy dostęp do zmiennych b
lub c spowoduje dostęp do niezbędnych mniejszych tablic, które zostały już utworzone i „żyją” na stercie. Te. Najwyraźniej JDK 7 zużywa fizycznie więcej pamięci w takich sytuacjach. Ale wyobraźmy sobie możliwą opcję: pewne podciągi zmiennej są przypisane do zmiennych b
i w przyszłości wszyscy będą używać tylko ich - obiektów i . Nikt już po prostu nie ma dostępu do zmiennej a, nie ma do niej żadnych odniesień (to właśnie ma na myśli autor artykułu). W rezultacie w pewnym momencie zostaje uruchomiony moduł zbierający śmieci i (oczywiście w najbardziej ogólnej formie) otrzymujemy 2 różne sytuacje: JDK 6 : obiekt zostaje zniszczony (zebrany śmieci) , ALE - oryginalna ogromna tablica na stosie żyje; jest stale używany i . JDK 7: obiekt a zostaje zniszczony wraz z oryginalną tablicą na stercie. To jest punkt w JDK 6, który może prowadzić do wycieku pamięci. c
a
b
c
a
b
c
5. substring() w JDK 7
Metoda została ulepszona w JDK 7. W JDK 7substring()
faktycznie tworzy nową tablicę na stercie.
//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
GO TO FULL VERSION