Suche…


Einführung

Operatoren in der Java-Programmiersprache sind spezielle Symbole, die bestimmte Operationen an einem, zwei oder drei Operanden ausführen und dann ein Ergebnis zurückgeben.

Bemerkungen

Ein Operator ist ein Symbol (oder Symbole), das ein Java-Programm anweist, eine Operation an einem, zwei oder drei Operanden auszuführen. Ein Operator und seine Operanden bilden einen Ausdruck (siehe Thema Ausdrücke). Die Operanden eines Operators sind selbst Ausdrücke.

In diesem Thema werden ungefähr 40 verschiedene Operatoren beschrieben, die von Java definiert werden. Das separate Thema "Ausdrücke" erläutert:

  • wie Operatoren, Operanden und andere Dinge zu Ausdrücken kombiniert werden,
  • wie die Ausdrücke bewertet werden und
  • wie Ausdruckstypen, Konvertierungen und Ausdrucksauswertung funktionieren.

Der String-Verkettungsoperator (+)

Das + -Symbol kann drei verschiedene Operatoren in Java bedeuten:

  • Wenn vor dem + kein Operand vorhanden ist, ist dies der unäre Plus-Operator.
  • Wenn zwei Operanden vorhanden sind, sind beide numerisch. dann ist es der binäre Addition-Operator.
  • Wenn zwei Operanden vorhanden sind und mindestens einer von ihnen ein String , handelt es sich um den binären Verkettungsoperator.

Im einfachen Fall verknüpft der Verkettungsoperator zwei Zeichenfolgen, um eine dritte Zeichenfolge zu erhalten. Zum Beispiel:

String s1 = "a String";
String s2 = "This is " + s1;    // s2 contains "This is a String"

Wenn einer der beiden Operanden kein String ist, wird er wie folgt in einen String konvertiert:

  • Ein Operand, dessen Typ ein primitiver Typ ist, wird wie durch Aufruf von toString() für den geschachtelten Wert konvertiert.

  • Ein Operand, dessen Typ ein Referenztyp ist, wird durch Aufrufen der toString() Methode des Operanden konvertiert. Wenn der Operand null ist oder wenn die toString() Methode null zurückgibt, wird stattdessen das Zeichenfolgenliteral "null" verwendet.

Zum Beispiel:

int one = 1;
String s3 = "One is "  + one;         // s3 contains "One is 1"
String s4 = null + " is null";        // s4 contains "null is null"
String s5 = "{1} is " + new int[]{1}; // s5 contains something like
                                      // "{} is [I@xxxxxxxx"

Die Erklärung für das s5 Beispiel besteht darin, dass die toString() -Methode für Array-Typen von java.lang.Object geerbt wird und das Verhalten darin besteht, eine Zeichenfolge zu erzeugen, die aus dem Typnamen und dem Identitäts-Hashcode des Objekts besteht.

Der Verkettungsoperator wird angegeben, um ein neues String Objekt zu erstellen, außer wenn der Ausdruck ein konstanter Ausdruck ist. Im letzteren Fall wird der Ausdruck beim Kompilierungstyp ausgewertet, und sein Laufzeitwert entspricht einem String-Literal. Dies bedeutet, dass beim Aufteilen eines langen String-Literal wie folgt kein Laufzeit-Overhead entsteht:

String typing = "The quick brown fox " +
                "jumped over the " +
                "lazy dog";           // constant expression

Optimierung und Effizienz

Mit Ausnahme von Konstantenausdrücken erstellt jeder Zeichenfolgenverkettungsausdruck ein neues String Objekt. Betrachten Sie diesen Code:

public String stars(int count) {
    String res = "";
    for (int i = 0; i < count; i++) {
        res = res + "*";
    }
    return res;
}

In der obigen Methode wird bei jeder Iteration der Schleife ein neuer String , der um ein Zeichen länger ist als die vorherige Iteration. Jede Verkettung kopiert alle Zeichen in den Operanden - Strings die neuen bilden String . stars(N) werden also:

  • Erstellen Sie N neue String Objekte und werfen Sie alle außer dem letzten weg.
  • kopiere N * (N + 1) / 2 Zeichen und
  • generiere O(N^2) Bytes Müll.

Dies ist für große N sehr teuer. In der Tat kann jeder Code, der Zeichenfolgen in einer Schleife verkettet, dieses Problem haben. Ein besserer Weg, dies zu schreiben, wäre wie folgt:

public String stars(int count) {
    // Create a string builder with capacity 'count' 
    StringBuilder sb = new StringBuilder(count);
    for (int i = 0; i < count; i++) {
        sb.append("*");
    }
    return sb.toString();
}

Idealerweise sollten Sie die Kapazität des StringBuilder festlegen. Wenn dies jedoch nicht praktikabel ist, vergrößert die Klasse automatisch das Sicherungsarray, das der Builder zum Speichern von Zeichen verwendet. (Hinweis: Die Implementierung erweitert das Hintergrundarray exponentiell. Diese Strategie hält die Menge an Zeichen, die auf O(N) anstatt auf O(N^2) kopiert wird.)

Einige Leute wenden dieses Muster auf alle String-Verkettungen an. Dies ist jedoch nicht erforderlich, da JLS es einem Java-Compiler ermöglicht, Zeichenfolgenverkettungen innerhalb eines einzelnen Ausdrucks zu optimieren. Zum Beispiel:

String s1 = ...;
String s2 = ...;    
String test = "Hello " + s1 + ". Welcome to " + s2 + "\n";

wird normalerweise vom Bytecode-Compiler auf so etwas optimiert;

StringBuilder tmp = new StringBuilder();
tmp.append("Hello ")
tmp.append(s1 == null ? "null" + s1);
tmp.append("Welcome to ");
tmp.append(s2 == null ? "null" + s2);
tmp.append("\n");
String test = tmp.toString();

(Der JIT-Compiler kann dies weiter optimieren, wenn er davon ausgehen kann, dass s1 oder s2 nicht null .) Beachten Sie jedoch, dass diese Optimierung nur innerhalb eines einzelnen Ausdrucks zulässig ist.

Kurz gesagt, wenn Sie über die Effizienz von String-Verkettungen besorgt sind:

  • Optimieren Sie von Hand, wenn Sie wiederholte Verkettungen in einer Schleife (oder ähnlichem) durchführen.
  • Optimieren Sie einen einzelnen Verkettungsausdruck nicht manuell.

Die arithmetischen Operatoren (+, -, *, /,%)

Die Java-Sprache stellt 7 Operatoren zur Verfügung, die Arithmetik für Ganzzahl- und Fließkommawerte ausführen.

  • Es gibt zwei Operatoren + :
    • Der binäre Additionsoperator fügt eine Zahl zu einer anderen hinzu. (Es gibt auch einen binären + -Operator, der eine String-Verkettung ausführt. Dies wird in einem separaten Beispiel beschrieben.)
    • Der Operator unary plus tut nichts außer das Auslösen einer numerischen Promotion (siehe unten).
  • Es gibt zwei - Operatoren:
    • Der Operator für binäre Subtraktion subtrahiert eine Zahl von einer anderen.
    • Der unäre Minusoperator entspricht dem Abzug seines Operanden von Null.
  • Der binäre Multiplikationsoperator (*) multipliziert eine Zahl mit einer anderen.
  • Der Binärdivisionsoperator (/) teilt eine Zahl durch eine andere.
  • Der Operator für binäre Rest 1 (%) berechnet den Rest, wenn eine Zahl durch eine andere geteilt wird.

1. Dies wird oft fälschlicherweise als "Modulus" -Operator bezeichnet. "Rest" ist der Begriff, der von der JLS verwendet wird. "Modul" und "Rest" sind nicht dasselbe.

Operanden- und Ergebnistypen sowie numerische Promotion

Die Operatoren benötigen numerische Operanden und erzeugen numerische Ergebnisse. Bei den Operandentypen kann es sich um einen beliebigen primitiven numerischen Typ handeln (dh byte , short , char , int , long , float oder double ) oder um einen beliebigen numerischen Wrapper-Typ, der in java.lang definiert ist. dh ( Byte , Character , Short , Integer , Long , Float oder Double .

Der Ergebnistyp wird basierend auf den Typen des Operanden oder der Operanden wie folgt bestimmt:

  • Wenn einer der Operanden double oder Double , ist der Ergebnistyp double .
  • Wenn einer der Operanden float oder Float , lautet der Ergebnistyp float .
  • Andernfalls, wenn einer der Operanden long oder Long , ist der Ergebnistyp long .
  • Ansonsten ist der Ergebnistyp int . Dies umfasst byte , short und char Operanden sowie `int.

Der Ergebnistyp der Operation bestimmt, wie die arithmetische Operation ausgeführt wird und wie die Operanden behandelt werden

  • Wenn der Ergebnistyp double , werden die Operanden auf double erhöht, und die Operation wird unter Verwendung einer 64-Bit-IEE 754-Gleitkomma-Arithmetik ausgeführt.
  • Wenn der Ergebnistyp " float , werden die Operanden zum " float , und die Operation wird unter Verwendung einer 32-Bit-Gleitkomma-Arithmetik (IEE 754 mit einfacher Genauigkeit) ausgeführt.
  • Wenn der Ergebnistyp long , werden die Operanden zu long heraufgestuft, und die Operation wird mit einer 64-Bit-Zweierkomplement-Binär-Ganzzahl-Arithmetik mit Vorzeichen ausgeführt.
  • Wenn der Ergebnistyp " int , werden die Operanden zu " int , und die Operation wird unter Verwendung einer 32-Bit-Zweierkomplement-Binär-Ganzzahl-Arithmetik mit Vorzeichen ausgeführt.

Die Promotion erfolgt in zwei Schritten:

  • Wenn es sich bei dem Operandentyp um einen Wrapper-Typ handelt, wird der Operandenwert nicht mit einem Wert des entsprechenden primitiven Typs gefüllt .
  • Bei Bedarf wird der Primitivtyp auf den erforderlichen Typ hochgestuft:
    • Die Förderung von ganzen Zahlen zu int oder long ist verlustfrei.
    • Die Beförderung des float zum double ist verlustfrei.
    • Die Aufwertung einer Ganzzahl auf einen Gleitkommawert kann zu Genauigkeitsverlusten führen. Die Konvertierung wird unter Verwendung der Semantik "Round-to-next" von IEE 768 durchgeführt.

Die Bedeutung der Spaltung

Der Operator / teilt den linken Operanden n (den Dividend ) und den rechten Operanden d (den Divisor ) und erzeugt das Ergebnis q (den Quotienten ).

Java Integer Division rundet gegen Null. Der JLS-Abschnitt 15.17.2 legt das Verhalten der Java-Ganzzahldivision wie folgt fest:

Der für die Operanden n und d erzeugte Quotient ist ein ganzzahliger Wert q dessen Größe so groß wie möglich ist, während |d ⋅ q| ≤ |n| erfüllt wird |d ⋅ q| ≤ |n| . Außerdem ist q positiv, wenn |n| ≥ |d| und n und d haben dasselbe Vorzeichen, aber q ist negativ, wenn |n| ≥ |d| und n und d haben entgegengesetzte Vorzeichen.

Es gibt einige Sonderfälle:

  • Wenn n MIN_VALUE ist und der Divisor -1 ist, tritt ein Ganzzahlüberlauf auf und das Ergebnis ist MIN_VALUE . In diesem Fall wird keine Ausnahme ausgelöst.
  • Wenn d 0 ist, wird `ArithmeticException ausgelöst.

Bei der Java-Gleitkommadivision müssen mehr Randfälle berücksichtigt werden. Die Grundidee ist jedoch, dass das Ergebnis q der Wert ist, der am ehesten erfüllt ist, um d . q = n zu erfüllen d . q = n .

Gleitkommadivision führt niemals zu einer Ausnahme. Stattdessen führen Operationen, die durch Null dividieren, zu INF- und NaN-Werten. siehe unten.

Die Bedeutung von Rest

Im Gegensatz zu C und C ++ arbeitet der Restoperator in Java sowohl mit Ganzzahl- als auch mit Gleitkommaoperationen.

Für ganzzahlige Fälle ist das Ergebnis von a % b als Zahl r definiert, so dass (a / b) * b + r gleich a , wobei / , * und + die geeigneten Java-Ganzzahloperatoren sind. Dies gilt in allen Fällen, außer wenn b Null ist. In diesem Fall führt der Rest zu einer ArithmeticException .

Aus der obigen Definition folgt, dass a % b nur dann negativ sein kann, wenn a negativ ist, und es ist nur positiv, wenn a positiv ist. Außerdem ist die Größe von a % b immer kleiner als die Größe von b .

Die Floating-Point-Restoperation ist eine Verallgemeinerung des Ganzzahlfalls. Das Ergebnis von a % b ist der Rest r , der durch die mathematische Beziehung r = a - (b ⋅ q) wobei:

  • q ist eine ganze Zahl,
  • es ist nur negativ, wenn a / b negativ ist, positiv nur, wenn a / b positiv ist, und
  • seine Größe ist so groß wie möglich, ohne die Größe des wahren mathematischen Quotienten von a und b zu überschreiten.

Gleitpunktrest kann INF und NaN Werte in NaN erzeugen, beispielsweise wenn b Null ist; siehe unten. Es wird keine Ausnahme ausgelöst.

Wichtige Notiz:

Das von % berechnete Ergebnis einer Gleitkomma- Math.IEEEremainder ist nicht dasselbe wie das Ergebnis, das von der durch IEEE 754 definierten Math.IEEEremainder erzeugt wird. Der Rest der IEEE 754 kann mit der Bibliotheksmethode Math.IEEEremainder berechnet werden.

Ganzzahlüberlauf

Java-Ganzzahlwerte für 32 und 64 Bit sind vorzeichenbehaftet und verwenden eine binäre Darstellung mit zwei Komplementen. Beispielsweise kann der Zahlenbereich als (32 Bit) int -2 31 bis +2 31 - 1 dargestellt werden.

Wenn Sie zwei N-Bit-Integer (N = 32 oder 64) addieren, subtrahieren oder mehrere N-Bits addieren, ist das Ergebnis der Operation möglicherweise zu groß, um als N-Bit-Integer dargestellt zu werden. In diesem Fall führt die Operation zu einem Ganzzahlüberlauf , und das Ergebnis kann wie folgt berechnet werden:

  • Die mathematische Operation wird ausgeführt, um eine Zwillingskomplementdarstellung der gesamten Zahl zu erhalten. Diese Darstellung wird größer als N Bits sein.
  • Die unteren 32 oder 64 Bits der Zwischendarstellung werden als Ergebnis verwendet.

Es ist zu beachten, dass der Integer-Überlauf unter keinen Umständen zu Ausnahmen führt.

Gleitkomma-INF- und NAN-Werte

Java verwendet IEE 754-Gleitkommadaten für float und double . Diese Darstellungen haben einige spezielle Werte für die Darstellung von Werten, die außerhalb der Domäne der reellen Zahlen liegen:

  • Die Werte "unendlich" oder "INF" geben zu große Zahlen an. Der +INF Wert gibt Zahlen an, die zu groß und positiv sind. Der -INF Wert kennzeichnet zu große und negative Zahlen.
  • "Unbestimmt" / "keine Zahl" oder NaN bezeichnen Werte, die aus bedeutungslosen Operationen resultieren.

Die INF-Werte werden durch Floating-Operationen erzeugt, die einen Überlauf verursachen, oder durch Division durch Null.

Die NaN-Werte werden erzeugt, indem Null durch Null dividiert oder Null berechnet wird.

Überraschenderweise ist es möglich, Arithmetik mit INF- und NaN-Operanden auszuführen, ohne Ausnahmen auszulösen. Zum Beispiel:

  • Addiert man + INF und einen endlichen Wert, erhält man + INF.
  • Das Hinzufügen von + INF und + INF ergibt + INF.
  • Addieren von + INF und -INF ergibt NaN.
  • Division durch INF ergibt entweder +0.0 oder -0.0.
  • Alle Operationen mit einem oder mehreren NaN-Operanden ergeben NaN.

Ausführliche Informationen finden Sie in den entsprechenden Unterabschnitten von JLS 15 . Beachten Sie, dass dies weitgehend "akademisch" ist. Bei typischen Berechnungen bedeutet ein INF oder NaN , dass etwas schiefgegangen ist. Sie haben zB unvollständige oder falsche Eingabedaten oder die Berechnung wurde falsch programmiert.

Die Gleichheitsoperatoren (==,! =)

Die Operatoren == und != Sind binäre Operatoren, die abhängig davon, ob die Operanden gleich sind, als true oder false ausgewertet werden. Der Operator == gibt true wenn die Operanden gleich sind, andernfalls false . Der Operator != Gibt false wenn die Operanden gleich und andernfalls true sind.

Diese Operatoren können mit primitiven Typen und Referenztypen verwendet werden, das Verhalten unterscheidet sich jedoch erheblich. Laut JLS gibt es drei verschiedene Gruppen dieser Operatoren:

  • Die booleschen Operatoren == und != .
  • Die numerischen Operatoren == und != .
  • Die Referenzoperatoren == und != .

In allen Fällen ist der Ergebnistyp der Operatoren == und != Jedoch boolean .

Die numerischen Operatoren == und !=

Wenn einer (oder beide) der Operanden eines Operators == oder != Ein primitiver numerischer Typ ist ( byte , short , char , int, long int, , float oder double ), handelt es sich bei dem Operator um einen Zahlenvergleich. Der zweite Operand muss entweder ein numerischer Grundtyp oder ein numerischer Typ mit Rahmen sein.

Das Verhalten anderer numerischer Operatoren lautet wie folgt:

  1. Wenn es sich bei einem der Operanden um einen Box-Typ handelt, ist er nicht im Boxmodus.
  2. Wenn einer der Operanden jetzt ein byte , ein short oder ein char , wird er zu einem int .
  3. Wenn die Typen der Operanden nicht gleich sind, wird der Operand mit dem Typ "Kleiner" zum Typ "Größer" befördert.
  4. Der Vergleich wird dann wie folgt durchgeführt:
    • Wenn die heraufgestuften Operanden int oder long die Werte getestet, um zu sehen, ob sie identisch sind.
    • Wenn die beförderten Operanden float oder double sind, gilt float :
      • Die zwei Versionen von Null ( +0.0 und -0.0 ) werden als gleich behandelt
      • Ein NaN Wert wird als nicht gleichwertig behandelt und
      • Andere Werte sind gleich, wenn ihre IEEE 754-Darstellungen identisch sind.

Hinweis: Sie müssen vorsichtig sein, wenn Sie == und != Verwenden, um Fließkommawerte zu vergleichen.

Die booleschen Operatoren == und !=

Wenn beide Operanden boolean sind oder einer boolean und der andere Boolean , sind diese Operatoren die Booleschen Operatoren == und != . Das Verhalten ist wie folgt:

  1. Wenn einer der Operanden ein Boolean , ist er nicht in der Box.
  2. Die ungepackten Operanden werden getestet und das boolesche Ergebnis wird gemäß der folgenden Wahrheitstabelle berechnet
EIN B A == B A! = B
falsch falsch wahr falsch
falsch wahr falsch wahr
wahr falsch falsch wahr
wahr wahr wahr falsch

Es gibt zwei "Fallstricke", die es ratsam machen, == und != Sparsam mit den Wahrheitswerten zu verwenden:

Die Referenzoperatoren == und !=

Wenn beide Operanden Objektreferenzen sind, prüfen die Operatoren == und != Ob die beiden Operanden auf dasselbe Objekt verweisen . Das ist oft nicht das, was Sie wollen. Um zu testen, ob zwei Objekte nach Wert gleich sind , sollte stattdessen die Methode .equals() verwendet werden.

String s1 = "We are equal";
String s2 = new String("We are equal");

s1.equals(s2); // true

// WARNING - don't use == or != with String values
s1 == s2;      // false

Warnung: Die Verwendung von == und != Zum Vergleichen von String Werten ist in den meisten Fällen falsch . Siehe http://www.riptutorial.com/java/example/16290/pitfall-- using ---- to- compare-strings . Ein ähnliches Problem gilt für primitive Wrapper-Typen. Siehe http://www.riptutorial.com/java/example/8996/pitfall-- using ---- to- compare- primitive- wrappers- objects- such- as- integer .

Über die NaN-Randfälle

In JLS 15.21.1 heißt es:

Wenn einer der Operanden NaN , ist das Ergebnis von == false aber das Ergebnis von != Ist true . Tatsächlich ist der Test x != x true dann true wenn der Wert von x NaN .

Dieses Verhalten ist (für die meisten Programmierer) unerwartet. Wenn Sie testen, ob ein NaN Wert sich selbst entspricht, lautet die Antwort "Nein, ist es nicht!". Mit anderen Worten: == ist für NaN Werte nicht reflexiv .

Dies ist jedoch keine Java-"Kuriosität", dieses Verhalten ist in den IEEE 754-Fließkomma-Standards angegeben, und Sie werden feststellen, dass es von den meisten modernen Programmiersprachen implementiert wird. (Weitere Informationen finden Sie unter http://stackoverflow.com/a/1573715/139985 ... Sie stellen fest, dass dies von jemandem geschrieben wurde, der "im Raum war, als die Entscheidungen getroffen wurden"!

Die Inkrement- / Dekrement-Operatoren (++ / -)

Variablen können mit den Operatoren ++ und -- um 1 erhöht oder dekrementiert werden.

Wenn die Operatoren ++ und -- auf Variablen folgen, werden sie als Nachinkrement bzw. Nach-Dekrement bezeichnet.

int a = 10;
a++; // a now equals 11
a--; // a now equals 10 again

Wenn die Operatoren ++ und -- vor den Variablen stehen, werden die Operationen als Vorinkrement bzw. Vor-Dekrement bezeichnet.

int x = 10;
--x; // x now equals 9
++x; // x now equals 10

Wenn der Operator der Variablen vorangeht, ist der Wert des Ausdrucks der Wert der Variablen, nachdem sie inkrementiert oder dekrementiert wurde. Wenn der Operator der Variablen folgt, ist der Wert des Ausdrucks der Wert der Variablen, bevor sie erhöht oder dekrementiert wird.

int x=10;

System.out.println("x=" + x + " x=" + x++ + " x=" + x); // outputs x=10 x=10 x=11
System.out.println("x=" + x + " x=" + ++x + " x=" + x); // outputs x=11 x=12 x=12
System.out.println("x=" + x + " x=" + x-- + " x=" + x); // outputs x=12 x=12 x=11
System.out.println("x=" + x + " x=" + --x + " x=" + x); // outputs x=11 x=10 x=10

Achten Sie darauf, keine Nachinkremente oder Dekremente zu überschreiben. Dies geschieht, wenn Sie am Ende eines Ausdrucks einen Post-In / Decrement-Operator verwenden, der der In / Decrement-Variablen selbst neu zugewiesen wird. Das Ein- / Dekrement hat keine Auswirkungen. Obwohl die Variable auf der linken Seite korrekt inkrementiert wird, wird ihr Wert sofort mit dem zuvor ausgewerteten Ergebnis von der rechten Seite des Ausdrucks überschrieben:

int x = 0; 
x = x++ + 1 + x++;      // x = 0 + 1 + 1 
                        // do not do this - the last increment has no effect (bug!) 
System.out.println(x);  // prints 2 (not 3!) 

Richtig:

int x = 0;
x = x++ + 1 + x;        // evaluates to x = 0 + 1 + 1
x++;                    // adds 1
System.out.println(x);  // prints 3 

Der bedingte Operator (? :)

Syntax

{zu bewertende Bedingung} ? {Anweisung-ausgeführt auf-wahr} : {Anweisung-ausgeführt auf-falsch}

Wie in der Syntax gezeigt, verwendet der Bedingungsoperator (auch als der ternäre Operator 1 bezeichnet ) den ? (Fragezeichen) und : (Doppelpunkt) Zeichen, um einen bedingten Ausdruck von zwei möglichen Ergebnissen zu ermöglichen. Es kann verwendet werden, um längere if-else Blöcke zu ersetzen, um je nach Bedingung einen von zwei Werten zurückzugeben.

result = testCondition ? value1 : value2

Ist äquivalent zu

if (testCondition) { 
    result = value1; 
} else { 
    result = value2; 
}

Es kann gelesen werden als : Wenn testCondition wahr ist, setze das Ergebnis auf value1; Andernfalls setzen Sie das Ergebnis auf value2 ”.

Zum Beispiel:

// get absolute value using conditional operator 
a = -10;
int absValue = a < 0 ? -a : a;
System.out.println("abs = " + absValue); // prints "abs = 10"

Ist äquivalent zu

// get absolute value using if/else loop
a = -10;
int absValue;
if (a < 0) {
    absValue = -a;
} else {
    absValue = a;
}
System.out.println("abs = " + absValue); // prints "abs = 10"

Gemeinsame Nutzung

Sie können den bedingten Operator für bedingte Zuweisungen verwenden (z. B. Nullprüfung).

String x = y != null ? y.toString() : ""; //where y is an object

Dieses Beispiel entspricht:

String x = "";

if (y != null) {
    x = y.toString();
}

Da der Bedingungsoperator über den Zuweisungsoperatoren die zweitniedrigste Priorität hat, ist es selten erforderlich, eine Klammer um die Bedingung zu verwenden. In Kombination mit anderen Operatoren ist jedoch eine Klammer um das gesamte Konstrukt des Bedingten Operators erforderlich:

// no parenthesis needed for expressions in the 3 parts
10 <= a && a < 19 ? b * 5 : b * 7

// parenthesis required
7 * (a > 0 ? 2 : 5)

Bedingte Operatoren können auch im dritten Teil verschachtelt werden, wobei sie eher wie Verketten oder wie eine switch-Anweisung funktionieren.

a ? "a is true" :
b ? "a is false, b is true" :
c ? "a and b are false, c is true" :
    "a, b, and c are false"

//Operator precedence can be illustrated with parenthesis:

a ? x : (b ? y : (c ? z : w))

Fußnote:

1 - Sowohl die Java-Sprachspezifikation als auch das Java-Lernprogramm rufen den Operator ( ? : :) Als Bedingungsoperator auf . Das Tutorial sagt, dass es "auch als der ternäre Operator bekannt ist", da es (derzeit) der einzige von Java definierte ternäre Operator ist. Die Terminologie "Bedingter Operator" stimmt mit C und C ++ und anderen Sprachen mit einem äquivalenten Operator überein.

Die bitweisen und logischen Operatoren (~, &, |, ^)

Die Java-Sprache stellt 4 Operatoren bereit, die bitweise oder logische Operationen an ganzzahligen oder booleschen Operanden ausführen.

  • Der Complement-Operator ( ~ ) ist ein unärer Operator, der eine bitweise oder logische Invertierung der Bits eines Operanden durchführt. siehe JLS 15.15.5. .
  • Der Operator AND ( & ) ist ein binärer Operator, der ein bitweises oder logisches "und" von zwei Operanden ausführt. siehe JLS 15.22.2. .
  • Der Operator OR ( | ) ist ein binärer Operator, der ein bitweises oder logisches "Inclusive or" von zwei Operanden ausführt. siehe JLS 15.22.2. .
  • Der XOR ( ^ ) - Operator ist ein binärer Operator, der ein bitweises oder logisches "Exklusiv-Oder" von zwei Operanden ausführt. siehe JLS 15.22.2. .

Die logischen Operationen, die von diesen Operatoren ausgeführt werden, wenn die Operanden Boolesche Werte sind, können wie folgt zusammengefasst werden:

EIN B ~ A A & B A | B A ^ B
0 0 1 0 0 0
0 1 1 0 1 1
1 0 0 0 1 1
1 1 0 1 1 0

Beachten Sie, dass für Integer-Operanden die obige Tabelle beschreibt, was für einzelne Bits geschieht. Die Operatoren bearbeiten tatsächlich alle 32 oder 64 Bits des Operanden oder der Operanden parallel.

Operandentypen und Ergebnistypen.

Die üblichen arithmetischen Konvertierungen gelten, wenn die Operanden Ganzzahlen sind. Häufige Anwendungsfälle für die bitweisen Operatoren


Der Operator ~ wird verwendet, um einen booleschen Wert umzukehren oder alle Bits in einem Ganzzahloperanden zu ändern.

Der Operator & dient zum "Ausblenden" einiger Bits in einem Ganzzahloperanden. Zum Beispiel:

int word = 0b00101010;
int mask = 0b00000011;   // Mask for masking out all but the bottom 
                         // two bits of a word
int lowBits = word & mask;            // -> 0b00000010
int highBits = word & ~mask;          // -> 0b00101000

Die | Operator wird verwendet, um die Wahrheitswerte von zwei Operanden zu kombinieren. Zum Beispiel:

int word2 = 0b01011111; 
// Combine the bottom 2 bits of word1 with the top 30 bits of word2
int combined = (word & mask) | (word2 & ~mask);   // -> 0b01011110

Der ^ Operator wird zum Umschalten oder "Umdrehen" von Bits verwendet:

int word3 = 0b00101010;
int word4 = word3 ^ mask;             // -> 0b00101001

Weitere Beispiele für die Verwendung der bitweisen Operatoren finden Sie unter Bitmanipulation

Die Instanz des Operators

Dieser Operator prüft, ob das Objekt von einem bestimmten Klassen- / Schnittstellentyp ist. Instanz des Operators lautet:

( Object reference variable ) instanceof  (class/interface type)

Beispiel:

public class Test {

   public static void main(String args[]){
      String name = "Buyya";
      // following will return true since name is type of String
      boolean result = name instanceof String;  
      System.out.println( result );
   }
}

Dies würde folgendes Ergebnis ergeben:

true

Dieser Operator gibt immer noch true zurück, wenn das zu vergleichende Objekt mit dem Typ rechts kompatibel ist.

Beispiel:

class Vehicle {}

public class Car extends Vehicle {
   public static void main(String args[]){
      Vehicle a = new Car();
      boolean result =  a instanceof Car;
      System.out.println( result );
   }
}

Dies würde folgendes Ergebnis ergeben:

true

Die Zuweisungsoperatoren (=, + =, - =, * =, / =,% =, << =, >> =, >>> =, & =, | = und ^ =)

Der linke Operand für diese Operatoren muss entweder eine nicht endgültige Variable oder ein Element eines Arrays sein. Der Operand für die rechte Hand muss mit dem linken Operanden kompatibel sein. Dies bedeutet, dass entweder die Typen gleich sein müssen oder der Typ des rechten Operanden durch Kombination von Boxing, Unboxing oder Expanding in den Typ des linken Operandentyps konvertierbar sein muss. (Für vollständige Details siehe JLS 5.2 .)

Die genaue Bedeutung der Operatoren "Operation and Assign" wird in JLS 15.26.2 wie folgt angegeben:

Ein zusammengesetzter Zuordnungsausdruck der Form E1 op= E2 ist äquivalent zu E1 = (T) ((E1) op (E2)) , wobei T der Typ von E1 , außer dass E1 nur einmal ausgewertet wird.

Beachten Sie, dass vor der endgültigen Zuweisung eine implizite Typumwandlung vorliegt.

1. =

Der einfache Zuweisungsoperator: weist dem linken Operanden den Wert des rechten Operanden zu.

Beispiel: c = a + b addiert den Wert von a + b zum Wert von c und weist ihn c

2. +=

Der Operator "Hinzufügen und Zuweisen": fügt den Wert des Operanden für die linke Hand zum Wert des Operanden für die linke Hand hinzu und weist das Ergebnis dem linken Operanden zu. Wenn der linke Operand den Typ String , handelt es sich um einen Operator "Verketten und Zuweisen".

Beispiel: c += a ungefähr c = c + a

3. -=

Der Operator "subtrahieren und zuordnen": subtrahiert den Wert des rechten Operanden vom Wert des linken Operanden und weist das Ergebnis dem linken Operanden zu.

Beispiel: c -= a ungefähr c = c - a

4. *=

Der Operator "Multiplizieren und Zuweisen": multipliziert den Wert des Operanden für die rechte Hand mit dem Wert des Operanden für die linke Hand und weist das Ergebnis dem linken Operanden zu. .

Beispiel: c *= a ungefähr c = c * a

5. /=

Der Operator "Teilen und Zuweisen": Dividiert den Wert des Operanden für die rechte Hand durch den Wert des Operanden für die linke Hand und weist das Ergebnis dem linken Operanden zu.

Beispiel: c /*= a ungefähr c = c / a

6. %=

Der Operator "Modulus und Zuweisung": Berechnet den Modul des Werts des Operanden für die rechte Hand anhand des Werts des Operanden für die linke Hand und weist das Ergebnis dem linken Operanden zu.

Beispiel: c %*= a ungefähr c = c % a

7. <<=

Der Operator "Links verschieben und zuweisen".

Beispiel: c <<= 2 ungefähr c = c << 2

8. >>=

Der Operator "Arithmetik rechts verschieben und zuweisen".

Beispiel: c >>= 2 ungefähr c = c >> 2

9. >>>=

Der Operator "logisch rechts verschieben und zuweisen".

Beispiel: c >>>= 2 ungefähr c = c >>> 2

10. &=

Der Operator "bitweise und und zuweisen".

Beispiel: c &= 2 ungefähr c = c & 2

11. |=

Der Operator "bitweise oder und zuweisen".

Beispiel: c |= 2 ungefähr c = c | 2

12. ^=

Der Operator "bitweises Exklusiv- oder Zuweisen".

Beispiel: c ^= 2 ungefähr c = c ^ 2

Die Bedingungs- und Bedingungsoperatoren (&& und ||)

Java bietet einen Bedingungs- und einen Bedingungs-Operator, die beide einen oder zwei Operanden vom Typ boolean annehmen und ein boolean Ergebnis erzeugen. Diese sind:

  • && - der bedingte AND-Operator

  • || - die bedingten ODER-Operatoren. Die Auswertung von <left-expr> && <right-expr> entspricht dem folgenden Pseudocode:

    {
       boolean L = evaluate(<left-expr>);
       if (L) {
           return evaluate(<right-expr>);
       } else {
           // short-circuit the evaluation of the 2nd operand expression
           return false;
       }
    }
    

Die Bewertung von <left-expr> || <right-expr> entspricht dem folgenden Pseudocode:

    {
       boolean L = evaluate(<left-expr>);
       if (!L) {
           return evaluate(<right-expr>);
       } else {
           // short-circuit the evaluation of the 2nd operand expression
           return true;
       }
    }

Wie der obige Pseudocode zeigt, entspricht das Verhalten der Kurzschlussoperatoren der Verwendung von if / else Anweisungen.

Beispiel - Verwenden von && als Guard in einem Ausdruck

Das folgende Beispiel zeigt das am häufigsten verwendete Verwendungsmuster für den Operator && . Vergleichen Sie diese beiden Versionen einer Methode, um zu testen, ob eine bereitgestellte Integer Null ist.

public boolean isZero(Integer value) {
    return value == 0;
}

public boolean isZero(Integer value) {
    return value != null && value == 0;
}

Die erste Version funktioniert in den meisten Fällen, aber wenn das value Argument null , wird eine NullPointerException ausgelöst.

In der zweiten Version haben wir einen "Guard" -Test hinzugefügt. Der value != null && value == 0 wird ausgewertet, indem zuerst der value != null getestet wird. Wenn der null erfolgreich ist (dh als true bewertet wird), wird der value == 0 ausgewertet. Wenn die null - Test fehlschlägt, dann die Auswertung der value == 0 wird übersprungen (kurzgeschlossen), und wir haben keine bekommen NullPointerException .

Beispiel - Verwendung von &&, um eine kostspielige Berechnung zu vermeiden

Das folgende Beispiel zeigt, wie && verwendet werden kann, um eine relativ kostenintensive Berechnung zu vermeiden:

public boolean verify(int value, boolean needPrime) {
    return !needPrime | isPrime(value);
}

public boolean verify(int value, boolean needPrime) {
    return !needPrime || isPrime(value);
}

In der ersten Version sind beide Operanden von | wird immer ausgewertet, daher wird die (teure) isPrime Methode unnötig aufgerufen. Die zweite Version vermeidet den unnötigen Aufruf mit || anstelle von | .

Die Schichtoperatoren (<<, >> und >>>)

Die Java-Sprache bietet drei Operatoren für die bitweise Verschiebung von 32- und 64-Bit-Ganzzahlwerten. Dies sind alles binäre Operatoren, wobei der erste Operand der Wert ist, der verschoben werden soll, und der zweite Operand, wie weit verschoben werden soll.

  • Der << oder Linksverschiebungsoperator verschiebt den durch den ersten Operanden gegebenen Wert um die Anzahl der durch den zweiten Operanden gegebenen Bitpositionen nach links . Die leeren Stellen am rechten Ende sind mit Nullen gefüllt.

  • Der Operator ">>" oder arithmetische Verschiebung verschiebt den durch den ersten Operanden angegebenen Wert um die durch den zweiten Operanden angegebene Anzahl von Bitpositionen nach rechts . Die leeren Stellen am linken Ende werden durch Kopieren des am weitesten links befindlichen Bits gefüllt. Dieser Vorgang wird als Zeichenerweiterung bezeichnet .

  • Der Operator ">>>" oder der logische Rechtsverschiebung verschiebt den durch den ersten Operanden angegebenen Wert um die durch den zweiten Operanden angegebene Anzahl von Bitpositionen nach rechts . Die leeren Stellen am linken Ende sind mit Nullen gefüllt.

Anmerkungen:

  1. Diese Operatoren erfordern als ersten Operanden einen int oder long Wert und erzeugen einen Wert mit demselben Typ wie der erste Operand. (Sie müssen eine explizite Typumwandlung verwenden, wenn Sie das Ergebnis einer Verschiebung einer byte , short oder char Variablen zuweisen.)

  2. Wenn Sie einen Shift-Operator mit einem ersten Operanden verwenden, der ein byte , ein char oder ein short , wird er zu einem int und der Vorgang erzeugt ein int .)

  3. Der zweite Operand wird modulo um die Anzahl der Bits der Operation reduziert, um den Betrag der Verschiebung anzugeben. Weitere Informationen zum mathematischen Konzept des Mods finden Sie unter Modul-Beispiele .

  4. Die Bits, die durch die Operation nach links oder rechts verschoben werden, werden verworfen. (Java bietet keinen primitiven "Drehen" -Operator.)

  5. Der arithmetische Verschiebungsoperator ist äquivalent, indem eine Zahl (Zweierkomplement) durch eine Potenz von 2 dividiert wird.

  6. Der linke Verschiebungsoperator multipliziert eine Zahl (Zweierkomplement) mit einer Potenz von 2.

Die folgende Tabelle zeigt die Auswirkungen der drei Schichtbedienungen. (Die Zahlen wurden in binärer Schreibweise angegeben, um die Visualisierung zu erleichtern.)

Operand1 Operand2 << >> >>>
0b0000000000001011 0 0b0000000000001011 0b0000000000001011 0b0000000000001011
0b0000000000001011 1 0b0000000000010110 0b0000000000000101 0b0000000000000101
0b0000000000001011 2 0b0000000000101100 0b0000000000000010 0b0000000000000010
0b0000000000001011 28 0b1011000000000000 0b0000000000000000 0b0000000000000000
0b0000000000001011 31 0b1000000000000000 0b0000000000000000 0b0000000000000000
0b0000000000001011 32 0b0000000000001011 0b0000000000001011 0b0000000000001011
... ... ... ... ...
0b1000000000001011 0 0b1000000000001011 0b1000000000001011 0b1000000000001011
0b1000000000001011 1 0b0000000000010110 0b1100000000000101 0b0100000000000101
0b1000000000001011 2 0b0000000000101100 0b1110000000000010 0b00100000000000100
0b1000000000001011 31 0b1000000000000000 0b1111111111111111 0b0000000000000001

Es gibt Beispiele für den Benutzer von Schichtoperatoren bei der Bit-Bearbeitung

Der Lambda-Operator (->)

Ab Java 8 wird der Operator Lambda ( -> ) verwendet, um einen Lambda-Ausdruck einzuführen. Es gibt zwei gängige Syntaxtypen, wie in diesen Beispielen veranschaulicht:

Java SE 8
  a -> a + 1              // a lambda that adds one to its argument
  a -> { return a + 1; }  // an equivalent lambda using a block.

Ein Lambda-Ausdruck definiert eine anonyme Funktion oder richtiger eine Instanz einer anonymen Klasse, die eine funktionale Schnittstelle implementiert.

(Dieses Beispiel ist hier der Vollständigkeit halber enthalten. Die vollständige Behandlung finden Sie im Thema Lambda-Ausdrücke .)

Die relationalen Operatoren (<, <=,>,> =)

Die Operatoren < , <= , > und >= sind binäre Operatoren zum Vergleichen numerischer Typen. Die Bedeutung der Operatoren ist wie erwartet. Wenn zum Beispiel a und b als byte , short , char , int , long , float , double oder die entsprechenden Boxed-Typen deklariert sind:

- `a < b` tests if the value of `a` is less than the value of `b`. 
- `a <= b` tests if the value of `a` is less than or equal to the value of `b`. 
- `a > b` tests if the value of `a` is greater than the value of `b`. 
- `a >= b` tests if the value of `a` is greater than or equal to the value of `b`. 

Der Ergebnistyp für diese Operatoren ist in allen Fällen boolean .

Mit Vergleichsoperatoren können Zahlen mit unterschiedlichen Typen verglichen werden. Zum Beispiel:

int i = 1;
long l = 2;
if (i < l) {
    System.out.println("i is smaller");
}

Beziehungsoperatoren können verwendet werden, wenn eine oder beide Zahlen Instanzen von geschachtelten numerischen Typen sind. Zum Beispiel:

Integer i = 1;   // 1 is autoboxed to an Integer
Integer j = 2;   // 2 is autoboxed to an Integer
if (i < j) {
    System.out.println("i is smaller");
}

Das genaue Verhalten wird wie folgt zusammengefasst:

  1. Wenn es sich bei einem der Operanden um einen Box-Typ handelt, ist er nicht im Boxmodus.
  2. Wenn einer der Operanden jetzt ein byte , ein short oder ein char , wird er zu einem int .
  3. Wenn die Typen der Operanden nicht gleich sind, wird der Operand mit dem Typ "Kleiner" zum Typ "Größer" befördert.
  4. Der Vergleich wird mit den resultierenden Werten int , long , float oder double .

Bei relationalen Vergleichen mit Fließkommazahlen ist Vorsicht geboten:

  • Ausdrücke, die Fließkommazahlen berechnen, führen häufig zu Rundungsfehlern, da die Fließkommadaten des Computers eine begrenzte Genauigkeit aufweisen.
  • Beim Vergleich eines Ganzzahlentyps mit einem Gleitkommatyp kann die Konvertierung der Ganzzahl in Gleitkommazahl ebenfalls zu Rundungsfehlern führen.

Schließlich unterstützt Java die Verwendung von relationalen Operatoren mit anderen als den oben aufgeführten Typen. Sie können diese Operatoren beispielsweise nicht verwenden, um Zeichenfolgen, Zahlenarrays usw. zu vergleichen.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow