Jestem przyzwyczajony do wykonywania następujących czynności w C
:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
A wynik to:
foo
Jednak w Javie to nie działa. Zakładam, ponieważ String
obiekt jest kopiowany, a nie przekazywany przez przywołanie . Myślałem, że ciągi znaków to obiekty, które zawsze są przekazywane przez odniesienie.
Co tu się dzieje?
java
string
pass-by-reference
Soren Johnson
źródło
źródło
Odpowiedzi:
Masz trzy możliwości:
Użyj StringBuilder:
Utwórz klasę kontenera i przekaż instancję kontenera do swojej metody:
Utwórz tablicę:
Z punktu widzenia wydajności, StringBuilder jest zwykle najlepszą opcją.
źródło
StringBuilder
jest szybszy,s += "..."
ponieważ nie przydziela nowych obiektów String za każdym razem. W rzeczywistości, kiedy piszesz kod w Javies = "Hello " + name
, kompilator wygeneruje kod bajtowy, który tworzy aStringBuilder
i wywołujeappend()
dwa razy.W Javie nic nie jest przekazywane przez odniesienie . Wszystko jest przekazywane przez wartość . Odwołania do obiektów są przekazywane według wartości. Dodatkowo ciągi znaków są niezmienne . Więc kiedy dołączasz do przekazanego String, po prostu otrzymasz nowy String. Możesz użyć wartości zwracanej lub zamiast tego przekazać StringBuffer.
źródło
Dzieje się tak, że odwołanie jest przekazywane przez wartość, tj. Przekazywana jest kopia odniesienia. Nic w java nie jest przekazywane przez odwołanie, a ponieważ ciąg jest niezmienny, to przypisanie tworzy nowy obiekt ciągu, na który wskazuje teraz kopia odwołania . Oryginalne odniesienie nadal wskazuje na pusty ciąg.
Byłoby to takie samo dla każdego obiektu, tj. Nadanie mu nowej wartości w metodzie. Poniższy przykład po prostu sprawia, że to, co się dzieje, jest bardziej oczywiste, ale konkatenacja łańcucha to tak naprawdę to samo.
źródło
java.lang.String jest niezmienna.
Nienawidzę wklejania adresów URL, ale https://docs.oracle.com/javase/10/docs/api/java/lang/String.html jest niezbędny do przeczytania i zrozumienia, jeśli jesteś w java-land.
źródło
obiekty są przekazywane przez odwołanie, prymitywy są przekazywane przez wartość.
Ciąg nie jest prymitywem, jest obiektem i jest specjalnym przypadkiem obiektu.
Ma to na celu oszczędzanie pamięci. W JVM istnieje pula ciągów. Dla każdego utworzonego łańcucha JVM spróbuje sprawdzić, czy ten sam łańcuch istnieje w puli ciągów i wskaże go, jeśli już istniał.
/* WYNIK */
== sprawdza adres pamięci (odniesienie), a .equals sprawdza zawartość (wartość)
źródło
Wszystkie argumenty w Javie są przekazywane według wartości. Gdy przekazujesz a
String
do funkcji, przekazana wartość jest odwołaniem do obiektu String, ale nie możesz zmodyfikować tego odwołania, a bazowy obiekt String jest niezmienny.Przydzial
jest równa:
Oznacza to, że (lokalnie) ponownie przypisuje parametr
zText
jako nowe odniesienie, które wskazuje na nowe miejsce w pamięci, w którym jest nowe,String
które zawiera oryginalną zawartośćzText
z"foo"
dołączoną.Oryginalny obiekt nie jest modyfikowany, a
main()
zmienna lokalna metodyzText
nadal wskazuje na oryginalny (pusty) ciąg.wydruki:
Jeśli chcesz zmodyfikować ciąg, możesz, zgodnie z opisem,
StringBuilder
lub jakiś kontener (tablicę lubAtomicReference
niestandardową klasę kontenera), który zapewnia dodatkowy poziom niezależności wskaźnika. Alternatywnie, po prostu zwróć nową wartość i przypisz ją:wydruki:
Jest to prawdopodobnie najbardziej podobne do Javy rozwiązanie w ogólnym przypadku - zobacz Efektywny element Java „Korzystna niezmienność”.
Jak już wspomniano,
StringBuilder
często zapewnia lepszą wydajność - jeśli masz dużo do zrobienia, szczególnie wewnątrz pętli, użyjStringBuilder
.Ale spróbuj przekazać niezmienne,
Strings
a nie zmienne,StringBuilders
jeśli możesz - Twój kod będzie łatwiejszy do odczytania i łatwiejszy w utrzymaniu. Rozważ utworzenie parametrówfinal
i skonfigurowanie środowiska IDE, aby ostrzegało Cię, gdy ponownie przypisujesz parametr metody do nowej wartości.źródło
Ciąg jest niezmiennym obiektem w Javie. Możesz użyć klasy StringBuilder, aby wykonać zadanie, które próbujesz wykonać, w następujący sposób:
Inną opcją jest utworzenie klasy z ciągiem jako zmienną zakresu (zdecydowanie odradzane) w następujący sposób:
źródło
Odpowiedź jest prosta. W java ciągi znaków są niezmienne. Stąd przypomina to użycie modyfikatora „final” (lub „const” w C / C ++). Tak więc raz przypisany nie możesz go zmienić tak, jak to zrobiłeś.
Można zmienić których wartość, do której ciąg punktów, ale nie można zmienić rzeczywistej wartości, do których ten ciąg jest obecnie wskazującego.
To znaczy.
String s1 = "hey"
. Możesz zrobićs1 = "woah"
, i to jest całkowicie w porządku, ale tak naprawdę nie możesz zmienić podstawowej wartości ciągu (w tym przypadku: "hey") na coś innego po przypisaniu go za pomocą plusEquals itp. (Tj.s1 += " whatup != "hey whatup"
).Aby to zrobić, użyj klas StringBuilder lub StringBuffer lub innych zmiennych kontenerów, a następnie po prostu wywołaj .toString (), aby przekonwertować obiekt z powrotem na łańcuch.
Uwaga: Ciągi znaków są często używane jako klucze mieszające, dlatego jest to jeden z powodów, dla których są one niezmienne.
źródło
=
na odniesienie NIGDY nie wpływa na obiekt, na który wskazywał, niezależnie od klasy.String to specjalna klasa w Javie. Jest bezpieczny dla wątków, co oznacza „Po utworzeniu instancji String zawartość instancji String nigdy się nie zmieni”.
Oto, co się dzieje
Najpierw kompilator Java pobierze wartość instancji zText String, a następnie utworzy nową instancję String, której wartością jest zText z dołączonym „foo”. Więc wiesz, dlaczego instancja, na którą wskazuje zText, nie uległa zmianie. To zupełnie nowa instancja. W rzeczywistości nawet String „foo” jest nową instancją String. Tak więc dla tej instrukcji Java utworzy dwie instancje typu String, jedna to „foo”, a druga to wartość z zText append „foo”. Zasada jest prosta: wartość instancji String nigdy nie zostanie zmieniona.
W przypadku metody fillString możesz użyć StringBuffer jako parametru lub zmienić go w ten sposób:
źródło
W Javie ciągi znaków są niezmienne .
źródło
To działa przy użyciu StringBuffer
Jeszcze lepiej użyj StringBuilder
źródło
Ciąg jest niezmienny w java. nie można modyfikować / zmieniać istniejącego literału ciągu / obiektu.
String s = "Witaj"; s = s + "cześć";
W tym przypadku poprzednie odniesienie s zostało zastąpione nowym odniesieniem s wskazującym na wartość „HelloHi”.
Jednak w celu wprowadzenia zmienności mamy StringBuilder i StringBuffer.
StringBuilder s = new StringBuilder (); s.append ("Cześć");
spowoduje to dodanie nowej wartości „Cześć” do tego samego odniesienia. //
źródło
Aaron Digulla ma jak dotąd najlepszą odpowiedź. Odmianą jego drugiej opcji jest użycie klasy opakowania lub kontenera MutableObject biblioteki commons lang w wersji 3+:
zapisujesz deklarację klasy kontenera. Wadą jest zależność od wspólnej biblioteki lang. Ale biblioteka ma sporo przydatnych funkcji i prawie każdy większy projekt, nad którym pracowałem, używał jej.
źródło
Aby przekazać obiekt (w tym String) przez odwołanie w java, można przekazać go jako element członkowski otaczającego adaptera. Oto rozwiązanie z generycznym:
źródło
Dla bardziej ciekawych
źródło