Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java 2 von Friedrich Esser
Designmuster und Zertifizierungswissen
Zum Katalog
gp Kapitel 2 Operatoren
  gp 2.1 Überblick
  gp 2.2 Arithmetische Operatoren
  gp 2.3 Referenz-Variable
  gp 2.4 String-Operator
  gp 2.5 Vergleichs-Operatoren
  gp 2.6 Typvergleich-Operator instanceof
  gp 2.7 Logische Operatoren
  gp 2.8 Bitmanipulationen
  gp 2.9 Zuweisungen
  gp 2.10 Ternärer Operator
  gp 2.11 Zusammenfassung
  gp 2.12 Testfragen

Kapitel 2 Operatoren

Operatoren sind die grundlegenden Bausteine von Ausdrücken in einer Sprache. Sie sind typgebunden, haben Vorrangregeln und Assoziativität.
Ziel dieses Kapitels ist es, alle Operatoren, ihre Regeln und Seiteneffekte anhand von kurzen Beispielen vorzustellen.

Ausdruck

Kombiniert man Variablen und Literale mit Operatoren, so erhält man – sofern sie gültig sind – einen Ausdruck (Expression). Einen Ausdruck erkennt man daran, dass er auf der rechten Seite einer Zuweisung stehen kann:

var = expression;

Anweisung

Diese Zuweisung ist wiederum eine einzelne Anweisung (Statement) und wird durch ein Semikolon beendet. Anweisungen sind Kommandos, welche von der JVM ausgeführt werden.

Die Operatoren bilden somit die Bausteine für Ausdrücke und Anweisungen. Die meisten Operatoren hat Java in Syntax und Semantik von C/C++ übernommen. Allerdings gibt es auch bei diesen subtile Unterschiede zu C/C++, da Java-Operationen in der Wirkung eindeutig und unabhängig von der Maschine definiert sein müssen.

Pointer vs. Referenz

Java kennt daher auch keine Pointer bzw. Pointer-Arithmetik, das Äquivalent heißt Referenz und ist ein opaker Pointer (siehe Abschnitt 2.3).


Galileo Computing

2.1 Überblick  downtop

In der nachfolgenden Tabelle wird ein Überblick über alle Java-Operatoren gegeben, gruppiert nach Typ und Wirkung.

Vorrang

Stehen mehrere Operationen ohne Klammern in einem Ausdruck, so regelt der Vorrang (V) die Reihenfolge, in der die Operationen ausgeführt werden. Durch entsprechende Klammerung kann natürlich der Vorrang geändert werden.

Assoziativität

Die Assoziativität (A) bestimmt die Richtung, in die der Operator wirkt. Bei binären Operationen wird mit Ausnahme der Zuweisung von links nach rechts (L) gerechnet. Bei unären Operationen steht der zugehörige Operand immer rechts (R) vom Operator, wobei der Inkrement-/Dekrement-Operator auch auf linke Operanden wirkt.

Tabelle 2.1   Überblick über die Operatoren, klassifiziert nach Arten
Art Operator Wirkung Anmerkung A B Beispiel
Arithmetisch ++,-- Inkrement
Dekrement
unär, postfix,
unär, präfix
L
R
1
2
x++
--i
  +,- Plus, Minus unär, präfix R 2 +c, -x
  *,/ Multiplikation, Division   L 4 7F*i
i/3.1
  % Modulo auch floating-p. L 4 -3.8%1.7
  +,- Addition, Subtraktion + wird auch für Strings benutzt L 5 i+0xA0FF
String + Aneinanderfügen neues S.-Objekt L 5 s+"ende"
Vergleich <,<=
>,>=
Kleiner (-gleich), Größer (-gleich) Resultat ist boolean L 7 '\u003f'<c 2>='a'
  ==
!=
Gleich,
Ungleich
  L 8 obj1==obj2 2.0!='a'
Typvergleich instanceof nur für Objekte L 9  
Logisch
(Boolean)
! NOT unär R 2 !false
  & AND   L 9 a&b
  ^ XOR äquivalent zu != L 10 a^b
  | OR   L 11 a|b
  && short-circuit AND Auswertung des
2. Operanden nur wenn nötig
L 12 a&&b
  || short-circuit OR   L 13 a||b
Bit-Manipulation ~ Invertieren   R 2 ~-1
  <<,>> Links/Rechts schieben Negativ links:
mit 1 auffüllen
L 6 -1>>30
  >>> Rechts schieben mit 0 auffüllen L 6 -1>>>30
  & AND   L 9 -1&3
  ^ XOR   L 10 -1^3
  | OR   L 11 -1|3
Ternär ? : 1. Operand true: Ergebnis 2. Operand, false: Ergebnis 3. Operand R 14 i!=0?1/i:i
Zuweisung =
+=,-=
*=,/=
%=
&=,|=,^=,<<=
>>=
>>>=
Zuweisung
in Kombination mit binären arithmetischen Operatoren

in Kombination mit binären
Bit- und Shift-Operatoren
R 15 i=8
i>>=2
i&=3

new-Operator

Bei Anlage von Objekten kann new als Operator angesehen werden, wobei es sich bei

new ClassType ( arguments )

syntaktisch um einen Ausdruck, eine Class Instance Creation Expression handelt, die eine Referenz zu einem neu erschaffenen Objekt der Klasse ClassType liefert. Dieser new-Ausdruck wird ausgeführt, bevor das Objekt in einer Operation verwendet wird (Beispiele siehe Ende Abschnitt 2.1).

Separatoren, Cast, Methoden-Aufruf

Separatoren wie der .Dot-Operator, die eckigen Klammern [] für Array-Elementzugriffe, Methoden-Aufruf (arguments) sowie Typ-Konvertierung (type) mittels Cast sind keine Operatoren, müssen aber ebenfalls in die Vorranghierarchie eingeordnet werden.

Dies führt zu einer erweiterten Vorrang-Tabelle:

Tabelle 2.2   Operatoren und Separatoren, geordnet nach Vorrang
Vorrang
(precedence)
Operatoren, Separatoren,
Methoden-Aufruf, Cast
Hinweis
1 new . [] (arguments) ++ -- ++,-- postfix
2 ++ -- + - ~ ! unär
3 (type) Cast
4 * / %  
5 + - binär
6 << >> >>>  
7 < <= > >= instanceof  
8 == !=  
9 &  
10 ^  
11 |  
12 &&  
13 ||  
14 ?:  
15 = += -= *= /= %= &= |= ^= <<= >>= >>= >>>=  

Nachfolgend wird Vorrang und Assoziativität anhand von drei Beispiel-Ausdrücken erläutert:

Tabelle 2.3   Beispiele zu Vorrang und Assoziativität
Initialwert Ausdruck gleichbedeutend mit Ergebnis
iarr[0]=1 ++iarr[0] ++(iarr[0]) iarr[0]=2
i=1,j=2,
b=false
i==j==b (i==j)==b true
-i/j==0==!b (((-i)/j)==0)==(!b) true

Im letzten Ausdruck wird zuerst –i und !b berechnet (Priorität 2), dann -i/j (Priorität 4) und dieses Ergebnis mit 0 verglichen (Priorität 8, L-Assoziation, d.h. von links nach rechts). Zum Schluss wird dann das boolsche Ergebnis mit !b verglichen (Priorität 8).

System.out.println(new String("").length()); 
 // :: 0

Methoden-Aufruf sowie new-Operator haben Priorität 1. Bei Auswertung von links nach rechts wird zuerst durch die Class Instance Creation Expression ein leerer String erzeugt, von dem anschließend die Methode length() aufgerufen wird.

Icon


Galileo Computing

2.1.1 Regel für Operanden-Werteberechnung  downtop

Regel zur Ermittlung von Operanden-Werten

Es gibt vermeidbare Ausdrücke, in denen eine Variable mehrfach vorkommt und als Seiteneffekt ihren Wert im Ausdruck ändert. Deshalb gilt die folgende Regel:

gp  Die Werte der Operanden werden grundsätzlich vor Beginn der Operation von links nach rechts ermittelt, und mit diesen Werten werden dann die Operationen nach ihrer Priorität ausgeführt.

Zwei einfache Beispiele zu dieser Regel :

int[][] a= new int [4][1];
a[3][0]=-1; 
int i=1;  
System.out.println(i+(i=3)*i);  // :: 10   
System.out.println(a[i][i=0]);  // :: -1

Erklärung: In der zweiten Zeile wird – von links nach rechts – für das erste i der Wert 1 festgehalten, für das zweite i der Wert 3, der dann auch für das dritte i gilt. Zu berechnen ist also 1+3*3 nach den o.a. Vorrangregeln.

Mit Hilfe der Regel sind in der letzten Zeile die Indizes 3 und 0, d.h., es wird der Wert von a[3][0] ausgedruckt.


Galileo Computing

2.2 Arithmetische Operatoren  downtop

Generell können arithmetische Operatoren auf alle primitiven Typen angewendet werden, mit Ausnahme des Typs boolean.


Galileo Computing

Unäre Operatoren  downtop

Vor Anwendung der unären Operatoren + und - wird erst eine numeric Promotion (siehe hierzu Abschnitt 1.5.1) durchgeführt. Das unäre Plus führt bei byte, short und char eine Typumwandlung nach int durch.

Im Fall des unären Minus wird der Wert negiert, d.h. einfach das Vorzeichen umgekehrt:

char c= '0';
System.out.println(-c); 
 // :: -48
c= +c;                   // C-Fehler 
(siehe 2.9)

In der letzten Anweisung bewirkt +c eine Typumwandlung von char nach int, was dann bei der Zuweisung zu einem Fehler führt.

Inkrement-/Dekrement-Operator

Die Operatoren ++ und -- erhöhen bzw. vermindern den Wert der Variablen um Eins:

char c= '0'; int i= 1; double d= 1.;
++c; ++i; d--;
System.out.println(c+","+i+","+d); 
// :: 1,2,0.0

Nach der Operation ++c oder c++ hat c dann den Wert (char)(c+1).

Präfix- und Postfix-Form
Bei ++ und -- muss man die Präfix- und Postfix-Formen nur dann unterscheiden, wenn sie Teil eines größeren Ausdrucks sind.

Verwendung der Werte bei Präfix und Postfix

In der Präfix-Form wird zuerst der Operand in-/dekrementiert und dieser Wert dann verwendet. Bei der Postfix-Form wird dagegen mit dem alten Wert gerechnet und dann erst in-/dekrementiert:

int i=1, j, k;
j= ++i; k= i++;
System.out.println(i+","+j+","+k); 
// :: 3,2,2

Ausdrücke, in denen eine Variable mehrfach in-/dekrementiert wird, gehören eigentlich in die Kategorie »Logeleien für Zweistein«:

int i= 0;
int[] a= {1,2};
a[i++]= a[--i]+1;
System.out.println(a[0]+","+a[1]); // :: 2,2

Erklärung: Gemäß der Regel in Abschnitt 2.1.1 hat in der 3. Zeile i zuerst den Wert 0. Dann wird durch i++ der Wert zuerst post-inkrementiert, um anschließend durch --i sofort wieder prä-dekrementiert zu werden. Die Anweisung ist also äquivalent zu:

a[0]= a[0]+1; bzw. a[0]++; bzw. ++a[0];

Galileo Computing

Binäre Operatoren  downtop

Die vier Grundrechenarten +, -, * und / sind hinlänglich bekannt und können auch durch numeric Promotion (siehe Abschnitt 1.5.1) für Zeichen verwendet werden.

Für »Integer« sind nur die speziellen Fälle »Division durch Null« (erzeugt eine Ausnahme zur Laufzeit) und »Überschreitungen des Wertebereichs« (ohne Fehler bzw. Warnung) zu beachten.

Modulo

Der Modulo-Operator ist %. Mit k mod m bzw. k%m wird der Divisionsrest bestimmt.

In Java ist dies abweichend von der mathematischen Definition und von C/C++ gelöst, da der Modulo-Operator für alle numerischen Typen definiert ist (d.h. auch für Floating-Point-Typen).

Die mathematische Modulo-Operation ist wie folgt definiert:

Modulo in der Mathematik

Für die ganze Zahl k und die natürliche Zahl m bezeichnet k mod m den (eindeutigen) Rest r aus {0,1,..., m-1}, wobei m die Zahl k - r teilt, kurz m | k-r.

7 mod 4 = 3 , da 4|7-3

-7 mod 4 = 1 , da 4|-7-1

Der Divisionsrest kann nach folgender Formel berechnet werden:

              x mod y = x - ëx/yû 
*y

Math.floor()

Unter ëx/yû versteht man die größte ganze Zahl, kleiner gleich x/y. Sie ist in Java als Methode Math.floor() verfügbar.

Math.IEEEremainder()

Eine mathematische Implementierung – allerdings implementiert für Double – steht als Methode Math.IEEEremainder() zur Verfügung. Sie entspricht bei ganzzahligen x und y dem Ausdruck:

         x % y = (int)(i 
- Math.floor(1.*i/j)*j)

Modulo in Java

Die Java-Implementierung ist dagegen:

         x % y = (int)(x - (int)(x/y)*y) 

In Java wird (int)(a) durch Abschneiden der Nachkommastellen von a implementiert.

Nachfolgend vier einfache Modulo-Operationen:

System.out.println(7%4); 
                      // :: 3
 System.out.println(-7%4); 
                     // :: -3  
System.out.println(Math.IEEEremainder(-7,4));  // 
:: 1.0
System.out.println(-5.7%2.5);     // :: -0.7000000000000002

x%0

Wichtig: Hat y den Wert Null, so löst x%y für integrale Werte eine Ausnahme (ArithmeticException) aus, für Floating-Point-Modulo ist das Ergebnis NaN.


Galileo Computing

2.3 Referenz-Variable  downtop

Im Folgenden werden Operationen besprochen, die nicht nur auf primitive Variablen, sondern auch auf Referenz-Variablen wirken.

Eine Variable kann folgenden Typ haben:

gp  primitiv
gp  eine Klasse bzw. Interface
gp  ein Array, ein- oder mehrdimensional

Bei den letzten beiden Typen spricht man von Referenz-Variablen.

Icon

Jede Art von Konvertierung zwischen primitiven Typen und Referenz-Typen ist in Java untersagt.


Primitive Variable vs. Referenz-Variable

Primitive Variablen enthalten direkt den Wert des zugehörigen Typs. Wie der Name bereits sagt, »zeigt« eine Referenz-Variable dagegen nur auf ein Objekt und damit auf die Felder und deren Werte.

Icon

Wichtig: Es ist essenziell, zwischen den Variablen, die Objekte referenzieren, und den Objekten selbst zu unterscheiden.



Abbildung
Abbildung 2.1   Variable referenziert Objekt

Beispiel

Die nächsten beiden Anweisungen legen nur zwei Referenzen s und iarr an, aber keine Objekte:

String s;
int[] iarr;

Die Erschaffung der Objekte geschieht erst explizit mit new oder kann bei Strings auch in der Form von Literalen erfolgen (siehe 1.4.6).

iarr= new int[5];
s= new String("1. String-Objekt");
s= "2. String-Objekt";

Die Erschaffung von Objekten ist damit äquivalent zu C++. Damit sind die Gemeinsamkeiten aber auch schon erschöpft. Denn:

In C++ können die zu Referenzen äquivalenten Pointer direkt auf Speicherbereiche gesetzt und mit Pointer-Arithmetik manipuliert werden.

Garbage
Collection:
Entfernen nicht mehr benötigter Objekte durch die JVM

In Java sind die Referenzen opak, d.h. sind von außen nicht einsehbar und bieten keinen direkten Zugriff. Die einzigen Operationen, die indirekt den internen Zeiger manipulieren, sind new und die Zuweisung.

Im Gegensatz zu C++ muss man in Java die Objekte, die nicht mehr referenziert werden, nicht selbst aus dem Speicher entfernen. Andererseits ist es auch gar nicht möglich, denn dies ist alleine Aufgabe der JVM.

Bei dem oben angegebenen Beispiel referenziert s aufgrund der dritten Anweisung das String-Literal "1. String-Objekt" nicht mehr. Damit wird es von der JVM »bei Bedarf entsorgt«.


Galileo Computing

2.4 String-Operator   downtop

Overloading:
Wiederverwendung des + für Strings

Das Aneinanderfügen (Concatenation) von Strings wird ebenfalls mit Hilfe des Operators + durchgeführt. Das Ergebnis des Aneinanderfügens ist immer ein neues String-Objekt:

"Anfang " + "Ende" Ergebnis-String: "Anfang Ende"

Icon
Regeln zu String-Additionen

Ist einer der Operanden kein String, so wird der jeweils andere vor der Operation nach folgender Regel in einen String umgewandelt:

1. Für primitive Typen wird die Methode toString() der zugehörigen Wrapper-Klasse4 aufgerufen.
2. Für Referenzen mit Wert null wird der String "null" geliefert, ansonsten wird die Methode toString() des referenzierten Objekts aufgerufen.5
3. Besteht der Ausdruck aus mehreren Additionen, sind unbedingt die Vorrangregeln zu beachten.

Beispiele

    System.out.println(3+4+"!="+3*2+1); 
// :: 7!=61

Zuerst wird 3*2 berechnet, danach werden die restlichen + Operationen von links nach rechts ausgeführt. Die Addition 3+4 ergibt 7. Danach wird 7 nach der o.a. Regel in den String "7" umgewandelt und nacheinander "!=", "6" und "1" angehängt.

Klammert man, erhält man das (vielleicht) erwartete Ergebnis:

    System.out.println((3+4)+"=="+(3*2+1)); // :: 
7==7

Galileo Computing

2.5 Vergleichs-Operatoren  downtop

Unter den Vergleichs-Operatoren sind vor allem die Gleichheits-Operatoren aufgrund unterschiedlicher Semantik interessant.


Galileo Computing

Relationale Operatoren  downtop

Die relationalen Operatoren <, <=, > und >= können nur mit Zahlen und Zeichen verwendet werden und liefern die logischen Werte true und false. Vor dem Vergleich wird, falls notwendig, eine numeric Promotion durchgeführt.

Vergleiche
mit NaN

Vergleiche mit NaN führen immer zum Ergebnis false :

   Double.POSITIVE_INFINITY > Double.NaN
   Double.NaN == Double.NaN

Die Ausdrücke liefern somit beide den Wert false.


Galileo Computing

Gleichheits-Operatoren  downtop

Icon
Vergleich der Werte bei primitiven Typen

Die Wirkung der Operatoren == und != ist für Ausdrücke vom primitiven Typ und für Referenz-Ausdrücke unterschiedlich. Dies wird in zwei Regeln festgehalten.

Primitive Typen (Wertevergleich):

1. Der Operator == liefert für primitive Typen den Wert true genau dann, wenn die Werte der Operanden gleich sind, wobei vorher eventuell eine numeric Promotion durchgeführt wird.
Der Operator != liefert genau das Gegenteil, d.h. true genau dann, wenn die Werte ungleich sind.

Vergleich der Identität bei Objekten

Referenzen (Identitätsvergleich):

2. Der Operator == liefert für Referenzen den Wert true, wenn beide Referenzen auf dasselbe Objekt zeigen. Werden zwei verschiedene Objekte referenziert, liefert == immer false, egal ob die Objekte im Wert gleich sind.
Der Operator != liefert dagegen true, wenn die Referenzen nicht auf dasselbe Objekt zeigen.

Zum Referenzvergleich siehe auch das folgende Beispiel zu Strings.

Strings

Häufig werden Strings verglichen, wobei mit der Aussage »Der String s1 ist gleich dem String s2« gemeint ist, dass in s1 und s2 alle Zeichen gleich sind.

Stringvergleich mittels Operator ==

Nach der o.a. zweiten Regel liefert der Vergleich zweier Strings mittels == nur true, wenn die Strings identisch sind. Dies bedeutet, dass zwei Referenzen auf denselben String im Speicher zeigen.

Beispiel

String s1= "gleich", s2="ch";
String s3= new String("gleich"), s4= new String("gleich");
System.out.println(s1=="gleich"); 
       // :: true
System.out.println(s1=="glei"+s2); 
       // :: false
System.out.println(s1==s3);               // :: 
false
System.out.println(s3==s4);               // :: 
false
System.out.println(s1.equals("glei"+s2)); 
// :: true
System.out.println(s1.equals("glei"+s2)); 
// :: true

Abbildung
Abbildung 2.2   Drei Strings s1, s3, s4 mit dem Wert »gleich«

Erklärung: Beim ersten Vergleich ist das Ergebnis nur deshalb true, weil im Speicher Literale, die gleich sind, nur einmal vom Compiler angelegt werden. Das heißt, das String-Literal "gleich" existiert nur einmal, wobei es von s1 ebenfalls referenziert wird.

Im zweiten Vergleich wird aus der Variablen s2 und einem Literal ein anderes zweites String-Objekt "gleich" zusammengesetzt. Damit ist das Ergebnis false.

Mittels new werden zur Laufzeit aus dem Literal "gleich" zwei neue String-Objekte erzeugt. Folglich liefern die letzten beiden Vergleiche mit == ebenfalls false.

equals()
vergleicht bei Strings die Werte

Die Methode equals() der Klasse String prüft dagegen korrekt, ob zwei Strings im Wert gleich sind, also in allen Zeichen übereinstimmen.

Arrays

Arrays.equals() vergleicht Arrays

Auch Arrays sind Referenzen. Daher wird durch == kein Elementvergleich durchgeführt.

Jedoch existiert in der Klasse java.util.Arrays die Methode equals(), die unter anderem für Arrays mit primitiven Elementen einen Wertevergleich ermöglicht:

Arrays von primitiven Typen:

int[] iarr= {1,2,3}, jarr={1,2,3};
System.out.println(iarr==jarr);                // 
:: false
System.out.println(Arrays.equals(iarr,jarr)); 
 // :: 
true

Icon

Vorsicht: Die Methode Arrays.equals() ist nicht mit der nachfolgenden Object-Methode equals() zu verwechseln. Sie ist statisch und benötigt zwei Array-Argumente.

Arrays von Referenz-Typen:

Sind die Array-Elemente wieder Referenzen auf Objekte (z.B. Strings), ruft die Methode Arrays.equals() für je zwei zu vergleichende Elemente der beiden Arrays die zugehörige Object-Methode equals() auf:

String s1= new String("hallo"),s2= new String("hallo");
String[] sarr1= {s1,s2}, sarr2={s1,s2};
System.out.println(Arrays.equals(sarr1,sarr2)); 
 // :: 
true

Abbildung
Abbildung 2.3   Ein Array von int bzw. ein Array von Strings

In der Abbildung 2.3 sind exemplarisch für die beiden Array-Typen ein Array von n int-Werten und ein Array von n String-Werten dargestellt. Beim int-Array sind die Werte direkt in der Variablen gespeichert.

Object-Methoden equals() und toString():

Nicht nur die Klasse String, sondern jede Klasse hat eine Instanz-Methode equals(), die wie toString() von der Klasse Object »geerbt« wird.

Methode equals() muss sinnvoll überschrieben werden

Allerdings liefert das von Object geerbte equals() für eine Klasse nichts anderes als der Operator ==.

Jede Klasse muss daher die Methode equals() für sich sinnvoll implementieren. Da sie ja schon existiert, spricht man dann von Überschreiben (Override). In der Klasse String wurde somit equals() überschrieben.


Galileo Computing

2.6 Typvergleich-Operator instanceof  downtop

Der linke Operand des Operators instanceof muss eine Referenz sein, der rechte ein Objekttyp, d.h. eine Klasse, Interface oder Array. Der Operator kann also nicht für primitive Typen verwendet werden.

Icon

Der Typvergleich wird nach folgender Regel durchgeführt:

Regel für instanceof-Vergleiche

1. Sollte der Compiler beim instanceof-Ausdruck feststellen, dass das Ergebnis nur false sein kann, erzeugt er einen Fehler.
2. Ansonsten: Hat die Referenz den Wert null, liefert instanceof den Wert false.
3. Ansonsten: Ist die Instanz, auf die die Referenz zeigt, vom rechts stehenden Typ bzw. kann in ihn implizit konvertiert werden, so liefert instanceof zur Laufzeit true, sonst false.

Beispiele

String s="abc";
int[] iarr= new int[3], jarr=null;
System.out.println(s instanceof String); 
    // :: true
System.out.println(iarr instanceof int[]);   // 
:: true

Unsinnige Vergleiche erzeugen Fehler beim Kompilieren

System.out.println(jarr instanceof int[]);   // 
:: false
System.out.println(s instanceof int[]);      // 
C-Fehler
System.out.println(iarr instanceof byte[]);  // 
C-Fehler

Erklärung: Die beiden letzten Typvergleiche scheitern schon beim Kompilieren, da bereits der Compiler entscheiden kann, dass diese Ausdrücke immer false liefern.

Der Typvergleich ist nützlich für Methoden, die z.B. gewisse Operationen erst durchführen dürfen, wenn sichergestellt ist, ob ein aktuelles Argument wirklich von einem bestimmten Typ ist.


Galileo Computing

2.7 Logische Operatoren  downtop

Den Namen verdanken die logischen Operatoren der Tatsache, dass ihre Operanden sowie das Ergebnis vom Typ boolean sind.

Es gibt vier logische Operationen: die unäre Verneinung (NOT) und die Operationen und (AND), oder (OR) und exklusiv-oder (XOR).

Die nachstehende Tabelle zeigt die Ergebnisse für alle Operatoren in Abhängigkeit von den Werten der Operanden a und b, wobei && bzw. || eine Sonderform von & bzw. | ist, die weiter unten besprochen wird.

Ergebnisse logischer Operationen

Tabelle 2.4   Ergebnisse der logischen Operationen
a b a&b bzw. a&&b
(AND)
a|b bzw. a||b
(OR)
a^b
(XOR)
!a
(NOT)
false false false false false true
true false false true true false
false true false true true
true true true true false

Icon

Die Operanden a und b können natürlich selbst wieder logische Ausdrücke sein.

Die Berechnung von a op b für die drei Operatoren &, | und ^ läuft wie folgt ab:

1. Berechne a.
2. Berechne b.
3. Berechne a op b.

Ist der Wert von a false bzw. true, ist der zweite und dritte Schritt für AND bzw. OR überflüssig, da das Ergebnis bereits durch a vorgegeben wird.

Deshalb gibt es jeweils zwei verschiedene Operatoren für AND und OR:

gp  Die Operatoren & bzw. | führen den zweiten und dritten Schritt immer aus, d.h. berechnen immer b.

Short-Circuit-Operatoren && und ||

gp  Die Operatoren && bzw. || führen den zweiten und dritten Schritt nur aus, wenn das Ergebnis nicht bereits durch a festliegt.

Deshalb nennt man && und || auch Short-Circuit-Operatoren.

Short-Circuit-Berechnungen kann man sich bei gewissen Ausdrücken zunutze machen.

Beispiel

int i=0, j=10;
System.out.println(i!=0 & j>0); 
      // :: false
System.out.println(i!=0 | j>0); 
      // :: true
System.out.println(!(i!=0) ^ !(j>0)); 
// :: true
System.out.println(i!=0 && j/i>1); 
   // Short-Circuit :: false
System.out.println(i!=0 & j/i>1); 
    // Laufzeit-Fehler

Erklärung: Da die logischen Operatoren mit Ausnahme von NOT im Vorrang weit unten stehen, braucht man meistens nicht zu klammern. Trotzdem sind Klammern alleine wegen der Lesbarkeit sinnvoll.

Logische Ausdrücke sollten nicht unnötig kompliziert sein. Zum Beispiel haben Ausdrücke wie !(i!=0) bzw. !(j>0) nur pädagogische Bedeutung, d.h. (i==0) bzw. (j<=0) ist wohl besser.

Wie zu erwarten, wird im vierten Ausdruck j/i>1 aufgrund von && nicht mehr ausgewertet. Dies verhindert einen unangenehmen Laufzeitfehler (ArithmeticException), der bei einer Integer-Division durch Null ausgelöst wird. Dagegen erzeugt der letzte Ausdruck wie erwartet einen Laufzeitfehler.


Galileo Computing

2.8 Bitmanipulationen  downtop

Identifiziert man false bzw. true mit dem Bit 0 bzw. 1, sind die Ergebnisse der logischen Operationen direkt auf die Bit-Operationen mit Operanden vom integralen Typ übertragbar.

Bit-Operatoren wirken auf jedes Bit

Die Bit-Operatoren & (AND), | (OR), ^ (XOR) und ~ (NOT) müssen auf jedes Bit der Operanden angewendet werden, wogegen die entsprechenden logischen Operatoren nur ein einzelnes Bit manipulieren (zu brauchen).

Zusätzlich existieren noch Schiebe-(shift-)Operationen, die – wie bereits der Name sagt – die Bits in einem integralen Operanden links oder rechts verschieben.

Bit-Operationen sind maschinenunabhängig

Java implementiert natürlich die Bit-Operationen unabhängig vom realen Prozessor in der JVM, d.h., im Gegensatz zu C/C++ führen Bit-Operationen auf allen Maschinen zum gleichen Ergebnis.


Galileo Computing

2.8.1 Duale bzw. hexadezimale Codierung   downtop

Die Auswirkung einer Bit- bzw. Schiebeoperation kann man nur verstehen, wenn die duale Repräsentation von ganzen Zahlen, speziell das 2er-Komplement für negative Zahlen bekannt ist.

Duale Codierung positiver Zahlen

Für die duale Codierung wird eine positive ganze Zahl in die Summe von 2er-Potenzen zerlegt, woraus dann die duale Darstellung sofort abzuleiten ist:

Dezimale Zahl: 43 = 4*10 + 3*10

Duale Zahl: 43 = 32 + 8 + 2 + 1

                    = 1*2 +0*2 +1*2 +0*2 +1*2 +1*2  
   
                   = 1010112

Die Zahl 43 – dual codiert – ist also 101011, wobei jedoch je nach integralem Typ mit führenden Nullen auf 8, 16, 32 oder 64 Bits aufgefüllt werden muss.

Da die Darstellung von bis zu 64 Bits nicht sehr attraktiv ist, werden jeweils vier Bit (ein Halbbyte) zu einer hexadezimalen Ziffer 0..9a..f zusammengefasst, was der Darstellung einer Zahl in Potenzen von 16 entspricht. Die 43 als Typ byte codiert ist dann:

43 = 001010112 = 2b16 = 2*16 + 11*16

Nutzt man den Wertebereich nur für positive Zahlen, können in einem Byte die Zahlen von 0..ff bzw. in zwei die Zahlen von 0..ffff codiert werden. Dies entspricht dezimal den Werten 0..255 bzw. 0..65535. Der Typ char ist vorzeichenlos, was z.B. mit einem unären Plus per numeric Promotion getestet werden kann:

System.out.println(+'\uffff'); // :: 65535

Duale Codierung negativer Zahlen

Um negative Zahlen in den dualen Zahlenraum einzubetten, muss der Wertebereich geteilt werden.

1er-Komplement

Eine Möglichkeit ist die, einfach durch Invertieren der Bits die zu einer positiven ganzen Zahl zugehörige negative Zahl zu erzeugen. Dieses so genannte 1er-Komplement wäre dann für -43 vom Typ byte:

43= 00101011-43= 11010100

1er-Komplement: Problem Zero Crossing

Da die Addition 43 + -43 = 0 ist, folgen daraus aber unmittelbar zwei duale Darstellungen für die Null vom Typ byte:

0= 00000000 = 11111111

2er -Komplement

Dieses Problem lässt sich dadurch lösen, dass nach der Invertierung noch eine 1 addiert wird. Voilà, das 2er-Komplement:

     43=  00101011
    -43=  11010101      (= 11010100 + 1)
                          
      0= 100000000

Das Ergebnis der Addition führt zwar zu einer Eins im höchstwertigen 9. Bit. Da dies aber außerhalb der erlaubten acht Bits für den Typ byte liegt, wird es einfach abgeschnitten.

Für das 2er-Komplement der Zahlen vom Typ short, int oder long ist die Rechnung nicht anders, wobei dann nur das 17., 33. bzw. 65. Bit abgeschnitten wird.

Höchstwertiges Bit: Vorzeichen-Bit

Für positive Zahlen ist das höchstwertige (linke) Bit 0, für negative Zahlen 1. Das höchstwertige Bit repräsentiert damit das Vorzeichen.

Insbesondere hat -1 in der dualen Codierung alle Bits auf 1 gesetzt.


Galileo Computing

2.8.2 Regeln für Bit-Operationen  downtop

Icon

Bit-Operationen gibt es nur für die Typen int und long. Für Bit-Operationen gelten folgende Regeln:

1. Operanden vom Typ byte, short oder char werden vor der Operation in den Typ int umgewandelt.
2. Ist einer der Operanden vom Typ long, wird der andere auch in long umgewandelt.
3. Der Typ des Ergebnisses der Operation entspricht dem Operanden, d.h. ist entweder int oder long.

Die nachfolgende Tabelle aller Bit-Operationen entspricht der Tabelle 2.4, wobei a und b nun Bits repräsentieren:

Tabelle 2.5   Ergebnisse der Bit-Operationen
a b a&b
(AND)
a|b
(OR)
a^b
(XOR)
~a
(NOT)
0 0 0 0 0 1
1 0 0 1 1 0
0 1 0 1 1  
1 1 1 1 0  


Galileo Computing

2.8.3 Invertierung (Bitwise Complementdowntop

Es werden alle Bits des Operanden invertiert (siehe o.a. Tabelle).

Beispiel

int i=10;
System.out.println(Integer.toBinaryString(i)); 
// :: 1010
System.out.println(Integer.toHexString(~i));   // 
:: fffffff5
System.out.println(~i+","+~~i);                // 
:: -11,10

toHexString()
toBinaryString()

Erklärung: Mittels der statischen Methode toHexString() bzw. toBinaryString() der Klasse Integer bzw. Long können die dualen bzw. hexadezimalen Codierungen von integralen Operanden in Strings umgewandelt werden, wobei führende Nullen unterdrückt werden (Beispiel siehe 2.8.4).

Die Ausgabe von ~i zeigt die Wirkung des höchstwertigen Bits als Vorzeichen. Die Invertierung einer positiven Zahl ist eine negative und umgekehrt. Insbesondere gilt:

    Integer.MAX_VALUE == ~Integer.MIN_VALUE
gp  Der Invertierungs-Operator ist invers zu sich selbst, d.h. ~~a == a.

Galileo Computing

2.8.4 Bitwise AND, OR, XOR  downtop

Die einzelnen Bits der Operanden werden je nach Operator entsprechend verknüpft (siehe Tabelle 2.5).

Beispiel

char c='\u00ff';
short s=527;  // 512 + 15 bzw. hex: 200+f
System.out.println(Integer.toHexString(s));   // 
:: 20f
System.out.println(Integer.toHexString(s&c)); 
// :: f
System.out.println(Integer.toHexString(s|c)); // 
:: 2ff
System.out.println(Integer.toHexString(s^c)); // 
:: 2f0

Erklärung: Die Operanden werden nach der o.a. ersten Regel (siehe 2.8.2) vor den Operationen in den Typ int umgewandelt.

Setzen, Testen, Löschen und Invertieren von Bits

Mit dem zweiten Operanden als Maske können Flag-Bits mit Hilfe des

gp  XOR-Operators invertiert werden,
gp  OR-Operators gesetzt werden,
gp  AND-Operators getestet oder gelöscht werden.

Beispiel

Ist b vom Typ byte, so

invertiert b^m das 5.-8. Bit von b für m= 0xf0 setzt b|m das 6. Bit von b für m= 0x20 testet b&m das 6. Bit von b für m= 0x20 löscht b&m das 6. Bit von b für m=~0x20
Galileo Computing

2.8.5 Bitwise-Shift-Operationen  downtop

Der Left-Shift-Operator << schiebt alle Bits des linken Operanden um die Anzahl von Bits nach links, die der rechte Operand angibt, wobei die (rechten) niederwertigen Bits mit Nullen aufgefüllt werden.

Nach rechts schieben mit oder ohne Vorzeichen

Der Signed-Right-Shift-Operator >> schiebt alle Bits des linken Operanden um die Anzahl von Bits nach rechts, die der rechte Operand angibt, wobei das Vorzeichen (des linken Operanden) erhalten bleibt.

Der Unsigned-Right-Shift-Operator >>> schiebt alle Bits des linken Operanden um die Anzahl von Bits nach rechts, die der rechte Operand angibt, wobei die höchstwertigen Bits mit Nullen aufgefüllt werden.

Wert des Shift-Operanden modulo 32 bzw. 64

gp  Da Operanden vom Typ int bzw. long nur um 0..31 bzw. 0..63 Bit nach links bzw. rechts geschoben werden können, werden vom rechten Operanden nur die ersten 5 bzw. 6 Bits berücksichtigt.

Dies entspricht bei int dem mod 32-Wert des rechten Operanden bzw. bei long dem mod 64 -Wert des rechten Operanden.

In den folgenden Beispielen wird zur Veranschaulichung der Schiebe-Operationen auch die duale Codierung dargestellt.

Beispiele

-3 << 36 Û -3 << 36%32 Û -3 << 4 Û -3 * 24

-3

1111 1111 1111 1111 1111 1111 1111 1101
-3 << 4 1111 1111 1111 1111 1111 1111 1101 0000

Multiplikation
mit 2

Das Schieben um n Stellen nach links entspricht also der Multiplikation mit 2n.  

Der Left-Shift-Operator demonstriert recht einfach einen Integer-Überlauf:

1 << 31 Û 
-1 << 31 Û 
Integer.MIN_VALUE
-2147483648 1000 0000 0000 0000 0000 0000 0000 0000

Division durch 2 für positive Zahlen

Beim Right-Shift fallen die niederwertigen Bits weg. Die höchstwertigen Bits werden beim Signed-Right-Shift-Operator für positive Zahlen mit 0, für negative mit 1 aufgefüllt. Damit entspricht das Schieben um n Stellen nach rechts der Integer-Division durch 2 für positive Zahlen.

Integer.MIN_VALUE >> 31 == 
-1
90 >> 4 Û 
90/16 Û 
5  

-90 >> 4 Û -6 ist nicht gleich 90/4 Û -5

90      
0000
0000 0000 0000 0000 0000 0101 1010
90 >> 4 0000 0000 0000 0000 0000 0000 0000 0101
-90 1111 1111 1111 1111 1111 1111 1010 0110
-90 >> 4 1111 1111 1111 1111 1111 1111 1111 1010

Die höchstwertigen Bits werden beim Unsigned-Right-Shift-Operator immer mit 0 aufgefüllt, d.h., der Operand wird nicht als Zahl, sondern als reines Bit-Muster verstanden.

Jede negative Zahl wird somit durch Schieben nach rechts positiv. Insbesondere gilt:

-1>>>1 Û 
Integer.MAX_VALUE

Galileo Computing

2.9 Zuweisungen  downtop

Simple
Assignment

Bei der Zuweisung wird der Wert des Ausdrucks der rechten Seite als Wert der Variablen auf der linken Seite gesetzt. Ist der Typ des Ausdrucks verschieden von dem der Variablen, wird eine implizite Typumwandlung versucht. Ist diese nicht möglich, ergibt sich ein Fehler beim Kompilieren. Zuweisungen existieren in zwei Formen:

Compound-Assignment

             var op= expression;

Das Symbol op steht für einen binären arithmetischen, Bit-, Shift- oder String-Operator.

Compound-Assignment bedeutet Casting

Die zweite Form ist eine Abkürzung für folgende Zuweisung:

             var 
= (varType)(var op expression);

wobei var bei op= nur einmal ausgewertet wird.

Die feine Unterscheidung, ob var ein- oder zweimal ausgewertet wird, ist nur für »obskure« Zuweisungen mit gleichzeitigem Seiteneffekt notwendig (siehe hierzu 2.9.1).

Der Cast mittels (varType) zurück in den Typ der Variablen var ist recht angenehm bei arithmetischen Operatoren, da diese ja nur mit den Typen int oder long durchgeführt werden:

   char c= 'a';
   c= c+2;       // C-Fehler, da c+2 vom Typ int
   c+=2;         // ok!

Zuweisungen haben ein Ergebnis

Die Zuweisung zur Variablen var ist eigentlich ein Seiteneffekt, da die Zuweisung als Ergebnis den Wert des Ausdrucks hat. Dies wird häufig benutzt, um zwei Anweisungen zusammenzufassen.

Verkettung von Zuweisungen

Da die Zuweisung als einziger binärer Operator rechts-assoziativ ist, werden Zuweisungen von rechts nach links ausgeführt:

         x=y=z;         x=(y=z);
          j*=i+=3;       j*=(i+=3);

Galileo Computing

2.9.1 Compound-Assignment und Seiteneffekte  downtop

In Verbindung mit mehrfach vorkommenden Variablen wird erkennbar, dass der rechte Operand bei einem Compound-Assignment nur einmal ausgewertet wird.

Beispiel

int[] iArr= {1,2,3};  int i=0;
iArr[i++]+= i;  // Wert von i links 0, rechts 1
//  also äquivalent zu: iArr[i++]= iArr[0]+i;
// nicht äquivalent zu: iArr[i++]= iArr[i++]+i;

Erklärung: Nach der Regel in 2.1.1 hat i in der letzten auskommentierten Zuweisung von links nach rechts die Werte 0, 1 und 2.


Galileo Computing

Wirkung der einfachen Zuweisung  downtop

Wie bei den Gleichheitsoperatoren ist die Wirkung der Zuweisung für primitive Typen und Referenzen auf Objekte wieder unterschiedlich, da Variablen von primitiven Typen direkt ihre Werte enthalten.

Icon

Primitive Typen (Wertkopie):

Zuweisungen
bei primitiven Typen und bei Referenzen

gp  Nach der Zuweisung ist der Wert der linken Variable der Wert des rechten Operanden.

Referenzen (Zeigerkopie):

gp  Nach der Zuweisung hat die linke Referenz-Variable den Zeigerwert des rechten Operanden.

Der Effekt der Zuweisung bei Referenzen ist, dass die linke Variable auf dasselbe Objekt zeigt, das von der rechten Seite referenziert wird.

Beispiel

Anhand von Arrays wird die Wirkung der Zuweisung bei Referenzen sehr deutlich sichtbar:

int[] iArr= {1,2,3};                   // Array-Anlage
int[] jArr= iArr;                      // kopiert 
Referenzwert
jArr[2]<<= jArr[1]<<= jArr[0];      
   // r-shift
System.out.print(iArr[1]+","+iArr[2]); // :: 4,48

Erklärung: Aufgrund der zweiten Zeile zeigen iArr und jArr auf dasselbe int-Array. Die dritte Zeile führt eine Shift-Operation um ein Bit nach rechts auf jArr[1] aus. Dieses Ergebnis shiftet dann jArr[2] wieder nach rechts (um vier Bits).

Auf die nur einmal vorhandenen Array-Elemente kann wahlweise über die Referenz iArr oder jArr zugegriffen werden (siehe Abb. 2.4).


Abbildung
Abbildung 2.4   Resultat der Zuweisung bei iArr= jArr; bzw. s1= s2;

String-Zuweisungen

Strings verhalten sich bei Zuweisungen in der Handhabung – im Gegensatz zu String-Pointern bei C/C++ – unkritisch:

   String s1, s2= "nicht änderbar";
   s1= s2;  // kopiert Referenzwert

Immutable: Strings sind unveränderbar

Beide String-Referenzen s1 und s2 zeigen zwar wie bei C++ nach einer Zuweisung auf nur ein String-Objekt (siehe Abb. 2.4), aber Strings sind unveränderbar, weder über s1 noch s2 lässt sich der String ändern.

Objekte, die sich nach der Anlage nicht mehr verändern lassen, nennt man immutable.

Da es in Java keine String-Operation gibt, die einen String nach der Anlage verändern kann, verhalten sich Strings bei der Zuweisung wie primitive Typen.

Auf Strings ist natürlich auch der Compound-Operator += anwendbar, und er verhält sich wie erwartet:

String s= "big ";
s+= "apple";     // s= "big apple"

Galileo Computing

2.10 Ternärer Operator  downtop

Konditionaler Operator

Der ternäre Operator – auch konditionaler Operator genannt – ist ein Relikt von C/C++. Der Begriff »konditional« ist deshalb treffend, weil das Ergebnis der Operation von einer Bedingung abhängt:

    booleanExpression ? trueExpression 
: falseExpression

Das Ergebnis des ersten Ausdrucks muss ein Ergebnis vom Typ boolean sein. Ist es true, wird die trueExpression als Ergebnis zurückgeliefert, ansonsten die falseExpression.

Typ-
Kompatibilität

Der Compiler stellt sicher, dass die Typen der trueExpression und falseExpression entweder gleich sind oder implizit in einen gemeinsamen Typ umgewandelt werden können.

Der ternäre Operator ist
short-circuit

Der ternäre Operator ist ein Short-Circuit-Operator, d.h., es wird entweder nur die trueExpression oder nur die falseExpression ausgewertet:

a= i!=0 ? 1/i : Double.POSITIVE_INFINITY;

Sofern i vom Typ int ist, wird bei i=0 die Integer-Division übersprungen. Mittels numeric Promotion ist der gemeinsame Typ des zweiten und dritten Operanden double. Damit muss die Variable a vom Typ double sein, da ansonsten der Compiler einen Fehler erzeugt.

Die nachfolgende Anweisung scheitert am Compiler:

System.out.print(i!=0?1/i:"Div. durch 0"); // C-Fehler

Nachfolgend eine ternäre Variante für eine mathematische Modulo-Operation:

mod= (mod=i%j)<0 && 
j>0? mod+j:mod;

Auch hier wird die Regel aus 2.1.1 genutzt: Der Wert des Modulo mod wird von links nach rechts ermittelt, bevor der ternäre Ausdruck ausgewertet wird. Das heißt, zuerst wird mod auf den Wert von i%j gesetzt und dann – sofern mod negativ und j positiv ist – durch Addition von j angepasst.


Galileo Computing

2.11 Zusammenfassung  downtop

Betrachtet man Tabelle 2.1, sind die Operanden der Java-Operationen überwiegend vom primitiven Typ.

Da die Java-Sprache unabhängig von dem Betriebssystem bzw. der Hardware sein muss, werden die Operationen in Assoziativität, Vorrang und Wirkung eindeutig definiert.

Die Ermittlung der Operandenwerte wird vor der Berechnung der Ausdrücke durchgeführt, und im Gegensatz zu C/C++ gibt es hier zwei Right-Shift-Operatoren.

Sind die Operanden Referenzen, wirken die Operatoren auf den Referenzwerten – den opaken Zeigern – und nicht auf den Objektwerten. Insbesondere ist hierbei die Semantik für Vergleiche mit == bzw. != für Zuweisungen zu beachten.

Operationen mit numerischen Operanden, gleichzeitig mit und ohne Vorzeichen, sind in C/C++ eine häufige Fehlerquelle.

Dies ist wohl ein Grund dafür, dass mit Ausnahme des Typs char die Java-Sprachdesigner nur vorzeichenbehaftete (signed) numerische Typen zulassen.

Leider wurde damit aber für Anwendungsbereiche wie z.B. Grafik bzw. Bildbearbeitung der wichtige Typ Byte (ohne Vorzeichen!) mit den zugehörigen Operationen missachtet.

Hier bietet C/C++ mit unsigned char zwar keine schöne, aber eine bessere Alternative.


Galileo Computing

2.12 Testfragen  toptop

Zu jeder Frage können jeweils eine oder mehrere Antworten bzw. Aussagen richtig sein.

1. Welche Aussagen sind zum folgenden Code-Fragment richtig?
   char c= '-';
   res= +c;
   System.out.println(res);

A: Die Variable res kann vom Typ double sein.

B: Die Variable res kann vom Typ char sein.

C: Ist die Variable res vom Typ int, ist die Ausgabe eine positive Zahl.

D: Der Code führt zu einem Fehler beim Kompilieren.

2. Welche Aussagen sind zum folgenden Code-Fragment richtig?
   int i=0, j=1;
   System.out.println(++i + j++ +" " +i +" " +j);

A: Die Ausgabe ist: 2 1 1

B: Die Ausgabe ist: 1 1 2

C: Die Ausgabe ist: 2 1 2

D: Der Code führt zu einem Fehler beim Kompilieren.

3. Welche Aussagen sind zum folgenden Code-Fragment richtig?
   char c= 1;
   System.out.println(-5%3 + c);

A: Die Ausgabe ist: 2

B: Die Ausgabe ist: -2

C: Die Ausgabe ist: -1

D: Der Code führt zu einem Fehler beim Kompilieren.

4. Welche Aussagen sind zum folgenden Code-Fragment richtig?
   byte b= 1;
   boolean bo1=true, bo2=false;
   System.out.println(bo1^!bo2 & false?++b:b--);

A: Die Ausgabe ist: 1

B: Die Ausgabe ist: 2

C: Die Ausgabe ist: 0

D: Der Code führt zu einem Fehler beim Kompilieren.

5. Welche Aussagen sind zum folgenden Code-Fragment richtig?
   int i=-1;  byte b=-1;
   i= i>>>32; b>>>=20;                            
         

A: System.out.println(i); Ausgabe: 0

B: System.out.println(i>>>10>>10>>10); Ausgabe: -1

C: System.out.println(i>>>10>>10>>10); Ausgabe: 3

D: System.out.println(b); Ausgabe: -1

E: Die Anweisung b>>>=20; ist gleichbedeutend mit b= b>>>20;

6. Welche Ausgaben sind zu den folgenden Deklarationen richtig?
   String s1= new String("100"),s2= new String("100");
   String s3= null;
   int i=100;

A: System.out.println(s1==s2); Ausgabe: true

B: System.out.println(s1+=i); Ausgabe: 100100

C: System.out.println(s2+=s3); Ausgabe: 100

D: System.out.println(i+=s1); Ausgabe: 200

E: System.out.println(s3!=null & s3.length()>0?">0":"0");

E: Ausgabe: Laufzeit-Fehler (Exception)

7. Welche Ausgaben sind zu der folgenden Deklaration richtig?
   int a=-1, b= 8, c= 3;

A: System.out.println(~a==(a^a)); Ausgabe: true

B: System.out.println(~~a==a); Ausgabe: true

C: System.out.println(!a<0 ? a:-a); Ausgabe: 1

D: System.out.println(a>>>1==Integer.MAX_VALUE); Ausgabe: false

E: System.out.println((b&c) % (b|c)); Ausgabe: 0

F: System.out.println((b|c) % (b&c)); Ausgabe: 0

8. Welche Ausgaben erzeugt true zu den folgenden Deklarationen?
   double a=0.0, b=1.0;

A: System.out.println(a/a==0.0/0.0);

B: System.out.println(b/a > 0.0/0.0);

C: System.out.println(b/a > Double.MAX_VALUE);

D: System.out.println((int)(b/a)== Integer.MAX_VALUE);

E: System.out.println(b/a == 1/0);

9. Welche Anweisungen führen zu einem Fehler beim Kompilieren für die folgenden Deklarationen?
  int[] iarr= null, jarr= {0,1};
String[] s= {"ok"};

A: System.out.println(iarr instanceof int[]);

B: System.out.println(jarr instanceof int[]);

C: System.out.println(jarr instanceof byte[]);

D: System.out.println(s[0] instanceof String);

E: System.out.println(jarr[0] instanceof int);

F: System.out.println(jarr[0] instanceof Integer);

10. Welche Aussagen sind zu den folgenden Deklarationen richtig?
int i= 1;
Integer j= new Integer(1), k= new Integer(1);

A: System.out.println(j); Ausgabe: 1

B: System.out.println(j==k); Ausgabe: true

C: System.out.println(j.equals(k)); Ausgabe: true

D: System.out.println(i==j); Fehler beim Kompilieren

E: System.out.println((i+"").equals(j+""));
E: Fehler beim Kompilieren






1    Weitere Beispiele sind in den Unterabschnitten 2.2.1, in 2.9.1 und 2.10 zu finden.

2    Die Details des Zuweisungs-Operators werden in Abschnitt 2.9 behandelt.

3    Wichtig ist anzumerken, dass das Entfernen nicht mehr referenzierter Objekte nicht vorhersehbar ist. Dies ist JVM-abhängig. Eine Programmierung, die auf garantierter Entfernung basiert, ist fehlerhaft. Garbage Collection wird im Zusammenhang mit finalize() noch näher besprochen (siehe 5.9.1).

4    Zum Beispiel wird für den Typ int die Methode Integer.toString() aufgerufen.

5    Jede Klasse hat eine Methode toString(), da alle Klassen von der Klasse Object
abgeleitet werden, die diese Methode (sehr rudimentär) implementiert.

6    Spezialfall NaN: NaN!=NaN liefert true.

7    Siehe hierzu Kapitel 5, Vererbung.

8    Beispiel: Implementierung der Methode equals()

9    Für positive wie negative Zahlen schneidet Java bei der Integer-Division einfach die Nachkommastellen ab, d.h. entspricht bei negativen Zahlen nicht der mathematischen Definition.

  

Perl – Der Einstieg




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken und speichern. Ansonsten unterliegt das <openbook> denselben Bestimmungen wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
Die Veröffentlichung der Inhalte oder Teilen davon bedarf der ausdrücklichen schriftlichen Genehmigung von Galileo Press. Falls Sie Interesse daran haben sollten, die Inhalte auf Ihrer Website oder einer CD anzubieten, melden Sie sich bitte bei: stefan.krumbiegel@galileo-press.de


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de