Dlaczego w Javie możesz dodawać ciągi za pomocą operatora +, kiedy String jest klasą? W String.java
kodzie nie znalazłem żadnej implementacji dla tego operatora. Czy ta koncepcja narusza orientację na obiekt?
Dlaczego w Javie możesz dodawać ciągi za pomocą operatora +, kiedy String jest klasą? W String.java
kodzie nie znalazłem żadnej implementacji dla tego operatora. Czy ta koncepcja narusza orientację na obiekt?
Odpowiedzi:
Przyjrzyjmy się następującym prostym wyrażeniom w Javie
int x=15;
String temp="x = "+x;
Nawróceni kompilatora "x = "+x;
język StringBuilder
wewnętrznie i zastosowań .append(int)
„dodać” liczbę całkowitą na łańcuch.
Każdy typ można przekonwertować na typ String przez konwersję ciągów.
Wartość x typu pierwotnego T jest najpierw konwertowana na wartość referencyjną, tak jakby przez podanie jej jako argumentu do odpowiedniego wyrażenia tworzenia instancji klasy (§ 15.9):
- Jeśli T jest wartością logiczną, użyj new Boolean (x).
- Jeśli T jest char, użyj nowego znaku (x).
- Jeśli T jest bajtowe, krótkie lub int, użyj new Integer (x).
- Jeśli T jest długie, użyj nowego Long (x).
- Jeśli T jest zmiennoprzecinkowe, użyj nowego Float (x).
- Jeśli T jest podwójne, użyj nowego Double (x).
Ta wartość odniesienia jest następnie konwertowana na typ String przez konwersję ciągu.
Teraz należy wziąć pod uwagę tylko wartości odniesienia:
- Jeśli odwołanie ma wartość null, jest konwertowane na łańcuch „null” (cztery znaki ASCII n, u, l, l).
- W przeciwnym razie konwersja jest wykonywana tak, jakby przez wywołanie metody toString przywoływanego obiektu bez argumentów; ale jeśli wynik wywołania metody toString ma wartość null, zamiast tego używany jest ciąg „null”.
Metoda toString jest zdefiniowana przez pierwotną klasę Object (§4.3.2). Wiele klas go zastępuje, w szczególności Boolean, Character, Integer, Long, Float, Double i String.
Szczegółowe informacje na temat kontekstu konwersji ciągów można znaleźć w §5.4.
Optymalizacja konkatenacji ciągów: implementacja może zdecydować się na wykonanie konwersji i konkatenacji w jednym kroku, aby uniknąć tworzenia, a następnie odrzucania pośredniego obiektu String. Aby zwiększyć wydajność wielokrotnego łączenia ciągów, kompilator języka Java może użyć klasy StringBuffer lub podobnej techniki w celu zmniejszenia liczby pośrednich obiektów String, które są tworzone przez ocenę wyrażenia.
W przypadku typów pierwotnych implementacja może również zoptymalizować tworzenie obiektu otoki, konwertując bezpośrednio z typu pierwotnego na ciąg.
Zoptymalizowana wersja nie wykona najpierw pełnej konwersji napisów.
To jest dobra ilustracja zoptymalizowanej wersji używanej przez kompilator, aczkolwiek bez konwersji prymitywu, gdzie można zobaczyć, jak kompilator zmienia rzeczy w StringBuilder w tle:
http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/
Ten kod java:
public static void main(String[] args) {
String cip = "cip";
String ciop = "ciop";
String plus = cip + ciop;
String build = new StringBuilder(cip).append(ciop).toString();
}
Generuje to - zobacz, jak dwa style konkatenacji prowadzą do tego samego kodu bajtowego:
L0
LINENUMBER 23 L0
LDC "cip"
ASTORE 1
L1
LINENUMBER 24 L1
LDC "ciop"
ASTORE 2
// cip + ciop
L2
LINENUMBER 25 L2
NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
ASTORE 3
// new StringBuilder(cip).append(ciop).toString()
L3
LINENUMBER 26 L3
NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
ASTORE 4
L4
LINENUMBER 27 L4
RETURN
Patrząc na powyższy przykład i jak generowany jest kod bajtowy oparty na kodzie źródłowym w podanym przykładzie, będziesz mógł zauważyć, że kompilator wewnętrznie przekształcił następującą instrukcję
cip+ciop;
w
new StringBuilder(cip).append(ciop).toString();
Innymi słowy, operator +
w konkatenacji ciągów znaków jest w rzeczywistości skrótem dla bardziej szczegółowego StringBuilder
idiomu.
Jest to funkcja kompilatora Java, która sprawdza operandy +
operatora. Na podstawie operandów generuje kod bajtowy:
Oto, co mówi specyfikacja Java :
Operatory + i
-
nazywane są operatorami addytywnymi. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpressionOperatory addytywne mają ten sam priorytet i są syntaktycznie lewostronnie asocjacyjne (grupują od lewej do prawej). Jeśli typ dowolnego operandu
+
operatora toString
, operacją jest konkatenacja łańcuchów.W przeciwnym razie typ każdego z operandów
+
operatora musi być typem, który można zamienić (§5.1.8) na pierwotny typ liczbowy lub wystąpi błąd w czasie kompilacji.W każdym przypadku typ każdego
-
operandu operatora binarnego musi być typem konwertowalnym (§5.1.8) na pierwotny typ liczbowy lub wystąpi błąd w czasie kompilacji.
W jaki sposób klasa String zastępuje operator +?
Tak nie jest. Kompilator to robi. Ściśle mówiąc, kompilator przeciąża operator + dla operandów typu String.
Przede wszystkim (+) jest przeciążony, a nie nadpisany
Język Java zapewnia specjalną obsługę operatora konkatenacji ciągów (+), który został przeciążony dla obiektów Java Strings.
Jeśli operandem po lewej stronie jest String, działa on jako konkatenacja.
Jeśli operand po lewej stronie jest liczbą całkowitą, działa jako operator dodawania
int
i wtedy mają zastosowanie normalne reguły języka Java.
Język Java zapewnia specjalną obsługę operatora konkatenacji ciągów (+) oraz konwersji innych obiektów na łańcuchy. Konkatenacja ciągów jest implementowana za pomocą klasy StringBuilder
(lub StringBuffer
) i jej append
metody.
Znaczenie +
operatora w zastosowaniu do String
jest definiowane przez język, tak jak wszyscy już napisali. Ponieważ wydaje Ci się, że nie jest to wystarczająco przekonujące, rozważ to:
Ints, float i double mają różne reprezentacje binarne, dlatego dodanie dwóch liczb całkowitych jest inną operacją, jeśli chodzi o manipulację bitami, niż dodanie dwóch liczb zmiennoprzecinkowych: W przypadku liczb całkowitych można dodawać bit po bicie, przenosząc bit i sprawdzając, czy nie ma przepełnienia; w przypadku pływaków musisz osobno zająć się mantysami i wykładnikami.
Zatem w zasadzie „dodawanie” zależy od natury „dodawanych” obiektów. Java definiuje to dla ciągów znaków, a także dla int i float (długie, podwójne, ...)
+
Operator zwykle zastąpione StringBuilder
w czasie kompilacji. Sprawdź tę odpowiedź, aby uzyskać więcej informacji na ten temat.
+
operator nie jest zastępowany przez StringBuilder
?
+
Operator plus ( ) to funkcja języka Java.