Variablen und Ausdrücke

Inhalt

 

 

Einführung

 

Datentypen

 

Strings

 

Arithmetische Operatoren

 

Mathematische Funktionen

 

Mehrfache Zuweisungen

 

Übungsaufgaben

Einführung

Wir wollen unser erstes Programm

/* Mehrzeiliger Kommentar:
Das "Hello World" Beispiel.
*/
public class HelloWorld
{   
    public static void main (String args[])
    {   
        System.out.println("Hello World"); // Kommentar
    }
}

ausweiten. Zunächst sollen die einzelnen Zeilen erläutert werden.

public class HelloWorld
{...
}

Leitet die Definition einer Klasse ein. Ein Java-Programm besteht aus einer Reihe von Klassen. Die Klasse, die main enthält, muss öffentlich sein (public). Die Java-Datei muss den gleichen Namen haben, wie die öffentliche Klasse, die in ihr deklariert ist, in diesem Fall also HelloWorld.java.

public static void main (String args[])
{...
}

Definition eines Unterprogramms mit Namen main. Dieses Unterprogramm wird vom Java-Interpreter automatisch gestartet, weil es so heißt und genau diese Argumente hat. Es muss dazu auch öffentlich (public) sein und statisch (static). Das Unterprogramm main des Beispiels gibt kein Ergebnis zurück (void) und benutzt seine Parameter (Strings args[]) nicht.

Unterprogramme werden später detailliert erklärt. Vorläufig begnügen wir uns mit main.

System.out.println("Hello World");

Aufruf des Unterprogramms println im Objekt System.out. Dieses Unterprogramm kann den als Parameter übergebenen String "Hello World" auf der Kommandozeile ausgeben.

Die Zeile ist ein Beispiel für eine Anweisung. Jede Anweisung wird mit einem ; abgeschlossen.

... // Kommentar

Ein Zeilenkommentar. Wird vom Compiler ignoriert.

/* ... */

Mehrzeiliger Kommentar. Der Compiler ignoriert alles zwischen /* und */

Vorläufig können wir die Zeilen

public class Test
{   
    public static void main (String args[])
    {   
        ...
    }
}

als Rahmenprogramm auffassen. Die drei Punkte ... werden durch die relevanten Programmteile ersetzt. Im obigen Beispiel wurde hier einfach ein Text ausgegeben.

Wir wollen nun dieses Programm verändern. Dazu kopieren wir die Datei HelloWorld.java nach Test.java.

copy HelloWorld.java Test.java

(bzw. cp für Unix) und verändern den Inhalt von Test.java. Dazu öffnen wir diese Datei in unserem Lieblingseditor und geben folgenden Text ein.

public class Test
{   
    public static void main (String args[])
    {   
        int n;
        n = 3*4;
        System.out.println(n);
    }
}

Dies berechnet das Produkt der Zahlen 3 und 4, also 12, speichert das Ergebnis auf einer Variablen ab, und gibt den Wert der Variablen aus. Beachten Sie, dass sich auch der Name der Klasse geändert hat und nun mit dem Namen der Datei Test.java übereinstimmt. Um das Beispiel zu testen, geben Sie unter DOS ein

javac Test.java
java Test

Die Ausgabe sollte

12

sein. Es gibt in diesem Beispiel folgende neue Sprachelemente.

int n;

Deklaration einer Variablen mit Namen n. Die Variable wird auf dem Stack gespeichert und steht nur in dem Unterprogramm main lokal zur Verfügung. Nach Beendigung des Unterprogramms steht sie nicht mehr zur Verfügung. Da unser Programm dann ebenfalls beendet ist, ist dies nicht weiter relevant.

Die Variable ist vom Typ int (integer, d.h. ganze Zahl). Sie kann Werte von -2'147'483'648 bis 2'147'483'647 annehmen.

Die allgemeine Form der Variablendeklaration lautet

datentyp variablenname;

In diesem Fall ist der Datentyp int und der Variablenname n.

n = 3*4;

Dies ist eine Zuweisung. An n wird der Wert zugewiesen, der sich aus der Berechnung des Ausdrucks 3*4 ergibt. Dieser Ausdruck ist vom Typ int, da er eine Operation mit zwei int-Konstanten ist.

Natürlich ergibt sich der Typ eines Ausdrucks aus dem Typ der Operanden. In diesem Fall sind beide Konstanten, 3 und 4, vom Typ int und damit auch das Ergebnis 3*4. Diese allgemeine Regel gilt immer.

Regel: Der Typ der Operanden bestimmt den Typ des Ergebnisses.

Datentypen

Es gibt folgende elementare Datentypen. In der Tabelle sind jeweils der Java-Name des Datentyps, Beispiele für Konstanten des Datentyps und die möglichen Werte wiedergegeben, die Variablen dieses Datentyps annehmen können.

Datentyp

Konstanten

Wertemenge

short

 

-2^15 ... 2^15-1

int

3, -3, 0, 0x3FF (hexadezimal), 0377 (octal)

-2^31 ... 2^31-1

long

3l, -30000000000l

-2^63 ... 2^63-1

float

 

ca 6-stellige Mantisse und zweistelliger Exponent.

double

3.1415, 1e20, 1e-10, -1E10

ca. 16-stellige Dezimal-Mantisse und 3-stelliger Exponent.

byte

 

0 ... 2^8-1=255. Normale Speichereinheit.

char

'A', 'a', ' ', '\t', '\n', '\xFE', '\u0121'

Einfache einzelne Zeichen, spezielle Zeichen mit \, Unicode-Zeichen.

boolean

true, false

Wahr oder falsch.

Der Typ von Konstanten, wie 3 und 4, ergibt sich aus Ihrer Schreibweise. Bei Zahlen ohne Dezimalpunkt ist er int, bei Zahlen mit Dezimalpunkt double, z.B. 3.4, -1.55 oder auch 1.5e10. long-Konstanten haben ein angehängtes l.

Ein Ausdruck bleibt solange vom Typ int als keine double-Werte in ihm enthalten sind.

Achtung! Der Ausdruck 1/3 ergibt 0, da er ganzzahlig berechnet wird.

Will man 0.333... als Ergebnis erhalten, so kann man 1.0/3 schreiben. In diesem Fall ist der erste Oparand double, und daher auch das Ergebnis. Natürlich geht auch 1.0/3.0, oder wie wir weiter unten sehen werden (double)1/3.

Nun können wir etwa einen komplizierteren Ausdruck berechnen.

public class Test
{   
    public static void main (String args[])
    {   
        double x = 1.5, y;
        y = 1+x+x*x/2+x*x*x/6+x*x*x*x/24;
        System.out.println("y = " + y);
    }
}

Es ist also möglich mehrere Variablen in einer Zeile zu deklarieren und ihnen auch gleich Werte zuzuweisen. Wird einer Variablen kein Wert zugewiesen, kann sie nicht verwendet werden. Der Compiler wird dies monieren.

Außerdem wird hier die Variable x als Operand in Ausdrücken eingesetzt. Bisher haben wir nur Konstanten in Ausdrücken verwendet.

Interessant ist weiter, wie 1+x+... berechnet wird. 1 ist nämlich eine Konstante vom Typ int, aber x ist eine Variable vom Type double. Hier ergibt sich das Ergebnis aus dem höherwertigem Typ der Operanden, in diesem Fall double. Natürlich wird die Summe aus mehreren Summanden von links nach rechts berechnet. Java beherrscht dabei die Regel "Punkt vor Strich" und berücksichtigt auch Klammersetzungen mit runden Klammern (). Die Potenzierung muss mit Math.pow(x,y) berechnet werden.

Man beachte die Addition eines Strings und einer double-Variablen bei der Ausgabe. Hier wird zuerst der double-Wert zu einem String umgewandelt und danach werden beide Strings aneinandergehängt. Wir werden das weiter unten noch detaillierter erläutern.

Das Programm ergibt folgende Ausgabe

y = 4.3984375

Was passiert, wenn ein Ausdruck eines niederen Typs auf eine Variable eines höheren Typs gespeichert wird? In diesem Fall wird automatisch umgewandelt. Umgekehrt gibt es eine Fehlermeldung des Compilers.

Regel: Automatische Umwandlungen sind nur erlaubt, wenn das Ziel den Wert in jedem Fall aufnehmen kann.

int n = 3;
long l = n; // automatische Umwandlung

Umgekehrt ist ein type cast notwendig.

long l = 20000;
int n = (int)l; // type cast von long nach int

Damit lassen sich Datentypen beliebig ineinander umwandeln. Für Fehler trägt der Programmierer die Verantwortung.

public class Test
{   
    public static void main (String args[])
    {   
        long c = 1000000000000l; // das l ist notwendig!
        System.out.println(c); // druckt korrekten Wert
        System.out.println((int)c); // druckt -727379968
    }
}

Bei Überlauf wird kein Laufzeitfehler erzeugt. Division durch 0 oder ähnliche Fehler erzeugen allerdings eine arithmetic exception. Exceptions und ihre Behandlung vertagen wir vorerst.

Arithmetische Operatoren

Für die arithmetischen Datentypen (alle außer boolean) sind die üblichen Operatoren +, -, *, / und % (modulo) definiert. Das Ergebnis ist jeweils vom gleichen Typ. Deswegen

int n=3;
System.out.println(n/4); // druckt 0

Die Ausführungsreihenfolge von Operatoren ist streng festgelegt, entspricht aber der üblichen Konvention ("Punkt vor Strich", und Berücksichtigung von Klammern).

System.out.println((3.0+7.0)/(4.0*5.0+80.0)); // druckt 0.1
System.out.println((3.0+7)/(4*5+8)); // druckt 0.1
System.out.println((double)(3+7)/(4*5+8)); // druckt auch 0.1
System.out.println((3+7)/(4*5+80)); // druckt aber 0 !!!

Vergleiche von Zahlen ergeben true oder false vom Typ boolean. Es gibt die Vergleiche <, <= (kleiner oder gleich), >, >=, == (gleich), != (ungleich). Beispiel:

double x=3;
System.out.println(1/x >= 0.3); // druckt true

Ausdrücke vom Typ boolean werden mit && (und) und || (oder) verknüpft. Sie werden ins Gegenteil verkehrt mit ! (nicht).

Solche boolschen Ausdrücke werden wir später besonders für die Ablaufsteuerung von Programmen benötigen, zum Beispiel für bedingte Sprünge und Anweisungen.

Außerdem gibt es eine Reihe von speziellen bitweisen Operatoren für Zahlen, nämlich | (oder), & (und) ^ (entweder oder) und ~ (bitweises Komplement, NOT). Daneben gibt es noch Operatoren, die bitweise nach rechts und links schieben, nämlich >> (Verschieben nach rechts, wobei negative Zahlen negativ bleiben), << und >>> (Verschieben nach rechts mit Auffüllen von 0). Z.B. entspricht n<<2 einer Multiplikation von n mit 4. Diese Operatoren sind nur dann von Interesse, wenn Zahlen im Dualsystem behandelt werden sollen.

Es gibt auch die Oparatoren ++ und --, und zwar nachgestellt und vorausgestellt. Diese Operatoren erhöhen, bzw. erniedrigen, einen ganzahligen Wert um 1. ++n tut dies vor der Verwendung von n, und n++ danach. Man kann diese Operatoren auch als Anweisungen verwenden. Beispiel:

int i=1;
i++;
System.out.println(i); // druckt 2.

Es ist fraglich, ob es nicht besser ist, i=i+1 zu schreiben, was deutlicher ist, und damit weniger fehlerträchtig. Jedoch wurde i++ von früheren Compilern in Code übersetzte, der schneller ausgeführt wird, so dass sich diese Schreibweise eingebürgert hat.

Außerdem existieren Kombinationen zwischen Operatoren und =. Beispiel

int i = 1;
i += 2;
System.out.println(i); // druckt 3.

Hier ist die Frage, ob i=i+2 nicht deutlicher ist, und dennoch genauso schnell ausgeführt wird.

Strings

Strings sind eigentlich keine elementaren Datentypen sondern verhalten sich wie Klassen. Da sie aber für nicht-mathematische Beispiele entscheidend wichtig sind, behandeln wir sie schon hier. Später werden wir uns noch einmal detaillierter um Strings kümmern.

String-Konstanten haben wir schon benutzt. Solche Konstanten werden durch Gänsefüßchen eingeschlossen. Beispiel

String s="Dies ist ein Teststring.";

Die wichtigste Rechenoperation mit Strings ist die Addition +. Diese Addition hängt beide Strings hintereinander. Man kann auch elementare Datentypen zu Strings addieren. Diese Werte werden dann zuerst in eine String-Representation umgewandelt. Dadurch kann man numerische Ausgaben kommentieren.

double pi = Math.PI;
System.out.println("Pi zum Quadrat ist " + (pi*pi));

Hier wird zuerst pi*pi berechnet, dann wird dieser Wert in einen String umgewandelt, und schließlich wird

Pi zum Quadrat ist 9.869604401089358

ausgegeben. Java gibt alle relevanten Stellen aus und enthüllt damit Rechenungenauigkeiten, die bei der Ausgabe mit anderen Sprachen weggerundet werden, obwohl sie intern dort genauso vorhanden sind. Will man auf eine bestimmte Stellenzahl formatieren, so muss man die Fomatierungsroutinen von Java in String.format verwenden, die wir später behandeln.

Hier ist ein anderes Beispiel, dass den Buchstaben ausgibt, der den Code 67 hat.

public class Test
{   
    public static void main (String args[])
    {   
        int n = 67;
        System.out.println(
            "Der Buchstabe Nummer " + n + " ist " +(char)n);
    }
}

n wird einmal als int-Wert und einmal als char-Wert behandelt, und dementsprechend verschieden in einen String umgewandelt. Das Ergebnis ist

Der Buchstabe Nummer 67 ist C

Hexadezimale Zahlen können als 0xff eingegeben werden, was für 255 steht.

Mathematische Funktionen

Die mathematischen Funktionen sind in der Klasse Math statisch erklärt. Sie werden daher wie im folgenden Beispiel aufgerufen:

System.out.println(Math.exp(1)); // Druckt den Wert von e

Bevor wir Klassen genauer erläutern, soll dies einfach als eine Schreibweise zum Aufruf von mathematischen Funktionen aufgefasst werden.

Es gibt die üblichen Funktionen abs, sin, cos, exp, log, asin, acos und sqrt definiert. Daneben existieren Funktionen mit zwei Variablen, wie pow (berechnet x^y), atan2 (berechnet den Winkel von x,y), max und min. Alle diese Funktionen müssen mit vorangestelltem Math. aufgerufen werden.

Daneben existieren die Funktionen floor und ceil, die zur nächsten ganzzahligen Funktion ab-, bzw. aufrunden. Die Funktion round rundet zur nächsten ganzen Zahl, ergibt aber einen long-Wert.

Schließlich erzeugt die Funktion random eine Zufallszahl zwischen 0 und 1.

Mehrfache Zuweisungen

Man kann auch mehrere Zuweisungen gleichzeitig durchführen.

int n,m;
n = m = 3;

Dies liegt daran, dass eine Zuweisung wie m=3 in Wirklichkeit ein Ergebnis (nämlich 3) hat, das dann von n= weiterverwendet wird. Normalerweise wird dieses Ergebnis verworfen. Man kann das Ergebnis auch in anderen Fällen verwenden. Jedoch führt dies meist zu schlecht lesbarem Code.

Beispielsweise ist nicht auf den ersten Blick klar, wie

double a;
double y = Math.sin(a = Math.PI/4));

eigentlich funktioniert. Dagegen liest sich

double a=Math.PI/4;
double y=Math.sin(a);

wesentlich leichter.

 Wichtige Details

Übungsaufgaben

  1. Schreiben Sie ein Programm, das den Umfang eines Kreises mit Radius 2.0 ausgibt. Geben Sie auch den Radius des Kreises aus. Die Ausgabe sollte so aussehen:
    Der Umfang eines Kreises mit Radius 2.0 ist 12.566370614359172
  2. Was ist das Ergebnis des Ausdrucks 1000000*1000000? Warum entsteht dieses Ergebnis?
  3. Was ergibt 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1-1.0? Warum ist das nicht exakt 0.0?
  4. Berechnen Sie (1+1/n)^n für wachsende Werte von n. Wie groß muss man n wählen, damit dieser Ausdruck von e weniger als 0.01 entfernt ist?
  5. Geben Sie das Zeichen mit der Nummer 124 aus.
  6. Geben Sie einen Ausdruck an, der genau dann wahr ist, wenn x im Intervall [1,2] liegt.
  7. Probieren Sie aus, was passiert, wenn man die Wurzel aus -1 berechen will.

Lösungen (bitte vorher selbst probieren!).

Aufgaben ohne Lösung

  1. Man kann mit dem Datentyp char Rechnen. Er wird dann wie int behandelt, indem sein Code in eine Zahl umgewandelt wird. Berechnen Sie den 10. Buchstaben nach dem 'A'. Wieviele Buchstaben liegen zwischen 'A' und 'Z'? Wo liegen die kleinen, und wo die großen Buchstaben, wo die Zahlen?
  2. Testen Sie die Verschiebe-Operatoren >>, >>> und <<. Was bewirken diese Operatoren und warum?
  3. Was passiert bei einem type cast von double nach int? Rundet der Rechner oder schneidet er die Zahl ab? Wie rundet er? Wie behandelte er negative Zahlen?
  4. Testen Sie, was passiert wenn n++ zweimal in einem Ausdruck vorkommt, oder wenn n++ vorkommt und danach n nochmals. Was ist der Unterschied zu ++n?
  5. Was passiert, wenn long auf int umgewandelt wird. Wird immer der höherwertige Teil abgeschnitten. Bleiben negative Zahlen negativ?

Zurück zum Java-Kurs