Inhalt |
|
|
|
|
|
|
|
|
|
|
|
|
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. |
... // 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.
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.
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 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.
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.
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.
Lösungen (bitte vorher selbst probieren!).