Zum Hauptinhalt

Weitere Grundlegendende Programmierkonzepte in Java

Website: Hamburg Open Online University
Kurs: Programmieren mit Java
Buch: Weitere Grundlegendende Programmierkonzepte in Java
Gedruckt von: Gast
Datum: Sonntag, 24. November 2024, 03:11

Beschreibung

Hier erfährst Du, wie du Deine entwickelten Inhalte lesen und speichern kannst und lernst Methoden, Zahlen und Arrays kennen.

Dateien lesen und schreiben

Unserem Tagebuch fehlt noch etwas ganz entscheidendes. Die Eingaben gehen immer gleich wieder verloren, wenn das Programm beendet wird. Wir brauchen also eine Möglichkeit die einzelnen Einträge zu speichern. Computer bieten zum Speichern von Daten vielfältige Möglichkeiten, wie Dateien oder Datenbanken. Alle diese Möglichkeiten stehen uns auch im Java zur Verfügung. Wir begnügen uns aber ersteinmal mit einfachen Textdateien.

Es gibt eine Vielzahl von unterschiedlichen Klassen in Java, die zum Lesen und Schreiben von Dateien genutzt werden können. Sie haben verschiedene Vorteile in manchen Einsatzbereichen, sind aber vielfach etwas komplex. Die hier vorgestellten zählen mit zu den Einfachsten.

Bevor wir beginnen wollen wir einmal die Main-Methode anpassen. Wir schreiben vor die geschweifte Klammer des Codeblocks throws Exception. Hiermit wird Java mitgeteilt, dass falls Fehler bei der Ausführung auftreten, diese nicht behandelt werden sollen. Das Programm stürzt in diesen Fällen einfach ab. Fehler könnten hier z.B. sein, dass das Programm eine Datei lesen soll, die nicht existiert oder an einen Ort schreiben soll, der schreibgeschützt ist. Wir werden uns später damit beschäftigen, wie wir besser mit Fehlern umgehen können.

Für das Schreiben von Dateien wollen wir die PrintWriter Klasse verwenden. Hierfür benötigt man als Erstes eine Instanz der Klasse File. Hiermit kann eine Datei beschrieben werden. Diese Datei muss aber nicht zwingend existieren. Man kann File-Instanzen mit absoluten Namen erstellen new File("C:/datei.txt") oder aber mit relativen Namen new File("datei.txt"). Bei relativen Namen wird sie in dem Verzeichnis angelegt, in dem das Programm ausgeführt wird. In Eclipse ist das das Projektverzeichnis. Wenn wir das Program aber später direkt starten, so kann dies anders sein. Haben wir eine File-Instanz, so kann eine PrintWriter-Instanz erstellt werden durch new PrintWriter(datei);. Ab jetzt können wir beliebigen Text in die Datei schreiben mit der Methode print("Text"). Es ist gute Praxis den PrintWriter mit close() zu schließen, wenn er nicht mehr verwendet werden soll.

Damit Java weiß welche Klassen verwendet werden sollen, ist es nötig diese erst zu importieren. Dafür schreiben wir ganz an den Anfang die Zeilen.


import java.io.File; import java.io.PrintWriter;

Danach kann es losgehen.


File datei = new File("datei.txt"); PrintWriter printWriter = new PrintWriter(datei); printWriter.print(text); printWriter.close();

Der PrintWriter legt eine neue Datei an, falls diese noch nicht existiert. Sollte schon eine Datei existieren, so wird sie überschrieben. 

Als Nächstes wollen wir den Inhalt unserer Datei wieder einlesen. Hier können wir wieder die Scanner-Klasse verwenden. Dieses Mal übergeben wir aber nicht System.in als Parameter sondern unsere File-Instanz. Das alleine reicht aus um einen Scanner zu erzeugen, der aus einer Datei ließt. 

Es ist hierbei wichtig, dass die Datei bereits existiert, da das Programm sonst abstürzt. Dafür verwenden wir die datei.exists() Methode der File Klasse.


String text = ""; File datei = new File("datei.txt"); if (datei.exists()) { Scanner scanner = new Scanner(datei); while (scanner.hasNextLine()) { text += scanner.nextLine() + "\n"; } scanner.close(); }

Mit der Methode scanner.hasNextLine() kann geprüft werden, ob es weitere Zeilen in der Datei gibt. Wenn ja, kann mit der Methode scanner.nextLine() die Zeile gelesen werden. 

Mit diesem neuen Wissen können wir nun unser Tagebuch erheblich verbesser:


import java.io.File; import java.io.PrintWriter; import java.util.Scanner; public class Tagebuch { public static void main(String[] args) throws Exception { System.out.println("Wie heißt du?"); Scanner scanner = new Scanner(System.in); String name = scanner.next(); if (name.equals("Jan")) { System.out.println("Hallo " + name + "!"); String text = ""; // Bisherige Einträge laden File datei = new File("tagebuch-" + name + ".txt"); if (datei.exists()) { Scanner scnr = new Scanner(datei); while (scnr.hasNextLine()) { text += scnr.nextLine() + "\n"; } scnr.close(); } System.out.println("Geladener Text: " + text); System.out.println("Bitte Text eingeben. Zum Beenden 'quit' eingeben."); // Schleife zum Einlesen des Textes while (true) { String eingabe = scanner.next(); // "quit" beendet die Schleife if (eingabe.equals("quit")) { break; } text += eingabe; } System.out.println("Eingegeben wurde: " + text); // Neuen Tagebuchinhalt speichern PrintWriter printWriter = new PrintWriter(datei); printWriter.print(text); printWriter.close(); } else { System.out.println("Der Benutzer ist abgelehnt."); } scanner.close(); } }

Methoden bzw. Funktionen

Unser Tagebuch Programm ist wieder gewachsen und neigt dazu, trotz der Kommentare, schwierig lesbar zu werden. Um dieses Problem wieder inden Griff zu bekommen, wollen wir es in Methoden aufteilen. Ein weiterer Vorteil liegt darin, dass wir einzelne Teile des Programms später in einem anderen Kontext wieder verwenden können.

Wie im Buch "Hallo Welt programmieren, compilieren und ausführen in der Konsole" beschrieben, ist eine Methode ein Abschnitt im Quelltext des Programms, mit klar definierten Eingaben und einem Rückgabetyp. Es ist üblich im Umfeld von objektorientierten Sprachen von Methoden zu sprechen. Bei den übrigen Sprachen spricht man von Funktionen und meint prinzipiell das Gleiche.
Bei der Deklaration unserer Main-Methode hatten wir eine ganz klare Vorgabe, wie diese auszusehen hat. Es musste public static void main(String[] args) {} geschrieben werden. Diese Beschreibung, die sogenannte Signatur, ist für die Main-Methode festgelegt. Die Einzige erlaubte Abweichung ist die Ergänzung throws Exception die wir Nachträglich eingefügt haben.

Aber was sagen die einzelnen Worte der Signatur eigentlich? Das Schlüsselwort public bezieht sich auf die Sichtbarkeit der Methode. Es sagt aus, dass die Methode überall im Programm verwendet werden dürfte. Wir wollen an dieser Stelle nicht genauer auf Sichtbarkeiten eingehen. Sie sind ein wichtiger Bestandteil der objektorientierten Programmierung. Für uns genügt es, wenn wir bei jeder Methode public verwenden.

Auch das Schlüsselwort static stammt aus der objektorientierten Programmierung. Es weist darauf hin, dass die Methode nicht an eine Instanz einer Klasse gebunden ist. Sie kann daher aufgerufen werden ohne, dass vorher eine Instanz von unserer Klasse erstellt werden muss. Bei uns sollen alle Methoden static sein, um der Komplexität der Objektorientierung ersteinmal aus dem Wege zu gehen.

Als nächstes geben wir den Rückgabetyp der Methode an. Beim Beispiel der Main-Methode ist der Rückgabetyp void, das heißt es soll nichts zurück gegeben werden. Wir können bei unseren Methoden jeden beliebigen Typ verwenden. Wenn wir einen anderen Typ als void angeben, müssen wir zwingend einen Wert zurückgeben.

Als nächstes sieht die Signatur den Namen der Methode vor. In unserem Beispiel “main”. Bei selbst definierten Methoden kann der Name frei gewählt werden. Es empfiehlt sich natürlich einen möglichst aussagekräftigen Namen zu wählen. Ein mögliches Benennungsverfahren ist die Kombination aus einem Verb und einem Nomen. So lässt sich gut ausdrücken, was die Methode tun soll. Beispiele wären “leseDatei”, “pruefeEingabe” oder “berechneSumme”. Es ist in Java üblich, dass Methodennamen mit einem Kleinbuchstaben beginnen und jedes neue Wort mit einem Großbuchstaben aber ohne Trennzeichen angehängt werden. Der sogenannte Camel Case. Es ist außerdem üblich Umlaute und ß zu ersetzen weil diese bei der Weitergabe des Codes, an andere Programmierer, öfter Schwierigkeiten verursachen.

Der nächste Abschnitt der Signatur ist die Parameterliste, welche in runden Klammern steht. Parameter sind Variablen, die beim Aufruf der Methode übergeben werden müssen. Für eine leere Liste schreibt man nur die Klammern “()”. Benötigt man mehrere Parameter, so werden die einzelnen Parameter durch Kommata getrennt. Es wird vor jedem Parameter der Typ geschrieben. Eine Methode “schreibeSumme” mit zwei Zahlen als Eingabe könnte wie folgt deklariert werden:


public static void schreibeSumme(int zahl1, int zahl2) { System.out.println(zahl1 + zahl2); }

Ein Aufruf dieser Methode könnte z.B. so aussehen schreibeSumme(1, 2); 

Anstatt die Summe auszugeben kann sie auch zurückgegeben werden um sie im weiteren Programmablauf verwenden zu können. Hierfür muss der Rückgabetyp der Methode richtig definiert werden und es muss ein “return” definiert werden. Mit dem “return” wird festgelegt, welcher Wert zurück gegeben werden soll.


 public static void berechneSumme(int zahl1, int zahl2) {
     return zahl1 + zahl2;
 }

Jeder Code-Pfad soll mit einem “return” enden. Code, der nach einem “return” folgt, wird nicht mehr ausgeführt. 

Als letzter Teil der Signatur kann eine ‘throws’ Anweisung mit einem Fehlerdatentyp angegeben werden. Ersteinmal soll es ausreichen bei jeder Methodendeklaration “throws Exception” anzugeben. Wir werden im Abschnitt “Fehlerbehandlung” darauf genauer eingehen.

Wir haben jetzt alles was wir brauchen um unser Beispiel in mehrere Methoden aufzuteilen. Unsere Main-Methode soll dabei nur noch die einzelnen Aktionen starten. Als erstes prüft sie den Namen des Benutzers. Erst, wenn der Name der Richtige ist, werden Daten aus der Datei geladen. Dies soll in der Methode leseAusDatei() geschehen. Sie bekommt als Parameter eine Datei. Existiert die Datei so wird der Inhalt zurückgegeben, sonst nur ein leerer String. 

Die zweite Methode leseEingaben() soll den Nutzer auffordern etwas einzugeben und die Eingaben zurück geben. Die dritte Methode schreibeDaten() gibt nichts zurück. Sie speichert den übergebenen String in die Datei. Dieses Programm kann wie folgt umgesetzt werden:


import java.io.File; import java.io.PrintWriter; import java.util.Scanner; public class Tagebuch { public static void main(String[] args) throws Exception { System.out.println("Wie heißt du?"); Scanner scanner = new Scanner(System.in); String name = scanner.next(); if (name.equals("Jan")) { System.out.println("Hallo " + name + "!"); File datei = new File("tagebuch-" + name + ".txt"); String text = leseAusDatei(datei); text .= leseEingaben(scanner); schreibeDaten(text, datei); } else { System.out.println("Der Benutzer ist abgelehnt."); } scanner.close(); } public static String leseAusDatei(File datei) throws Exception { String text = ""; if (datei.exists()) { Scanner scnr = new Scanner(datei); while (scnr.hasNextLine()) { text += scnr.nextLine() + "\n"; } scnr.close(); } System.out.println("Geladener Text: " + text); return text; } public static String leseEingaben(Scanner scanner) { String text = ""; System.out.println("Bitte Text eingeben. Zum Beenden 'quit' eingeben."); // Schleife zum Einlesen des Textes while (true) { String eingabe = scanner.next(); // "quit" beendet die Schleife if (eingabe.equals("quit")) { break; } text += eingabe; } System.out.println("Eingegeben wurde: " + text); return text; } public static void schreibeDaten(String text, File datei) throws Exception { PrintWriter printWriter = new PrintWriter(datei); printWriter.print(text); printWriter.close(); } }

Zahlen und Operatoren

Im Bisherigen verlauf dieser Einheit haben wir uns nur mit Zeichenketten und deren Ein- und Ausgabe auf Konsole und in Dateien beschäftigt. Wir können durch Zeichenketten alle möglichen Dinge lesbar darstellen. Leider haben sie aber viele Einschränkungen, da der Computer nicht verstehen kann, was dort abgebildet wird. Um z.B. Berechnungen durchführen zu können, wollen wir im nächsten Abschnitt weitere Datentypen für Zahlen in den Fokus nehmen. Für diese Datentypen haben wir in unserem Tagebuch keine Verwendung, sie sind aber elementarer Bestandteil der Programmiersprache und sollen daher nicht fehlen.

Datentypen für Zahlen

Den Datentyp “int” zur Abbildung von ganzen Zahlen haben wir bereits kennengelernt. Die Details dieses Datentyps haben wir aber noch nicht genauer betrachtet. Als “int”, eigentlich Integer genannt, können wir leider nicht jede beliebige ganze Zahl abbilden. Es können nur Zahlen zwischen -2.147.483.648 und +2.147.483.647 verarbeitet werden. Das liegt daran, dass für diesen Datentyp nur 32 Bit verwendet werden. Ein Bit ist in der Informatik die kleinste Informationseinheit. Es ist eine Variable die nur den Wert 1 oder 0 haben kann. Das Erste der 32 Bits dient der Abbildung des Vorzeichens. Mit den verbleibenden 31 Bit können genau 2^31 = 2.147.483.648 Zustände also unterschiedliche Zahlen dargestellt werden. Der positive Bereich ist um eins kleiner, da die 0 als kleinste positive Zahl auch einen Zustand abbekommen muss.

Sollte man den Bedarf haben noch größere Zahlen zu verarbeiten, so empfiehlt es sich den Datentyp long zu verwenden. Hiermit können Ganzzahlen mit 64 Bit abgebildet werden. Der Wertebereich reicht von -9.223.372.036.854.775.808 bis +9.223.372.036.854.775.807. Wenn wir eine Zahl im Quelltext schreiben, die vom Typ Long sein soll, so hängen wir hinten ein kleines l an long zahl = 0l;.

Die Theorie hinter der Speicherung von Dezimalzahlen ist leider wesentlich komplexer. Es ist mit dem endlichen Speicher des Computers natürlich nicht einmal möglich die unendliche vielen Reellen Zahlen zwischen 0 und 1 abzubilden. Um dennoch Dezimalzahlen abbilden zu können, verwendet man eine sogenannte Gleit- oder Fließkommadarstellung. Die Zahlen werden vom Computer als Produkt einer Zahl mit einer Zehnerpotenz angenähert. Dadurch, dass die Zahlen nur angenähert werden, kann es aber Ungenauigkeiten geben. Diese können unter Umständen erheblich sein.

Auch für Fließkommazahlen gibt es einen Datentyp mit 32 Bit Speicher. Er heißt float und es gibt double welcher 64 Bit für die Speicherung verwendet. Die Notation einer Double Zahl sieht vor, dass ein Dezimalpunkt wie im Englischen üblich verwendet wird double zero = 0.0;. Für Float muss zusätzlich ein kleines f angehängt werden float one = 1.0f;. Float hat einen kleineren Wertebereich und eine wesentlich geringere Genauigkeit. Dies lässt sich leicht mit folgendem Code demonstrieren:


float f = 2345678.88f; System.out.println(f); // zeigt 2345679.0 double d = 2345678.88; System.out.println(d); // zeigt 2345678.88

Diese Datentypen für Zahlen haben alle gemeinsam, dass sie automatisch mit 0 initialisiert werden, wenn wir dies nicht explizit anders tun.

Für den Umgang mit int und long sollte man wissen, dass diese überlaufen können. In diesem Fall kipp das Vorzeichen. Addiert man also zu int 2147483647 eins dazu, so erhält man -2147483648. Der Computer gibt keinen Fehler aus.

Operatoren

Als nächstes wollen wir uns die vielen Möglichkeiten anschauen, die uns die Operatoren in Java zur Verfügung stellen. Operatoren verbinden ein oder zwei Aussagen zu einer neuen. Dabei ist genau definiert, welcher Operator welches Ergebnis liefern muss. Es ist aber auch definiert, welche Datentypen er als Eingabe akzeptiert.

= (Wertzuweisungsoperator) 

Mit = wird einer Variable ein Wert zuwiesen. Die Variable muss vom selben Typ sein wie der Wert. Der hat auch einen Rückgabe wert, dies ist der zugewiesene Wert. Aus diesem Grund funktionieren auch Mehrfachzuweisungen int x, y = 0;.

+ (Additionsoperator)

Der Additionsoperator addiert zwei Zahlen miteinander und gibt das Ergebnis zurück.

Es ist nicht erforderlich, dass die beiden Zahlen vom selben Typ sind. Der Rückgabetyp ist immer so klein wie möglich aber so groß wie nötig.

Einige Beispiele:

int + int = int
int + long = long
int + float = float
int + double = double
long + float = float
long + double = double
float + double = double

Wir hatten bereits für den Typ String gelernt, dass der Additionsoperator als Konkatenation verstanden wird also als Aneinanderreihen der Strings. Das selbe geschieht, wenn man ein String mit einer Zahl “addiert” System.out.println("Es gibt " + 8 + " Planeten in unserem Sonnensystem.");.

- (Subtraktionsoperator)

Der Subtraktionsoperator subtrahiert die zweite Eingabezahl von der Ersten und gibt das Ergebnis zurück.

Für den Rückgabetyp gelten die selben Bedingungen wie bei der Addition.
 

 * (Multiplikationsoperator)

Der Multiplikationsoperator multipliziert zwei Zahlen und gibt das Ergebnis zurück.

Für den Rückgabetyp gelten die selben Bedingungen wie bei der Addition.

 / (Divisionsoperator)


Der Divisionsoperator teilt die erste Eingabezahl durch die Zweite und gibt das Ergebnis zurück.

Für den Rückgabetyp gelten die selben Bedingungen wie bei der Addition. Man muss aber wissen, dass im Falle eines ganzzahligen Ergebnistyps die Stellen hinter dem Komma einfach verworfen werden. Beispielsweise ergibt int i = 3 / 2; das Ergebnis i = 1.

Die Division durch 0 ist wie in der Mathematik üblich nicht möglich. Das Programm stürzt mit einem Fehler ab. Dies gilt zumindest für die int 0 und die long 0. Eine Division durch double oder float 0.0 ergibt Unendlich, was als int ausgedrückt 2^31-1 ist.

 % (Modulooperator)

Durch die Verwendung von Modulo erhalten wir den ganzzahligen Rest einer Division. Zum Beispiel ergibt int i = 3 / 2; das Ergebnis i = 1;.

Für den Rückgabetyp gelten die selben Bedingungen wie bei der Addition.

Man hat sehr oft Situationen, in denen man den Wert einer Variable lesen, ändern und in der selben Variable speichern muss z.B. zahl = zahl + 1;. Aus diesem Grund gibt es Kurzschreibweisen:
Kurzschreibweisen
Kurzschreibweisen von Jan Martens

 

 

 

Das Inkrementieren oder Dekrementieren um eins kann sogar noch kürzer geschrieben werden. zahl++ ist gleichbedeutend zu zahl += 1 und zahl-- entspricht zahl -= 1.

Neben den eben beschriebenen arithmetischen Operatoren gibt es auch einige Vergleichsoperatoren. Alle Vergleichsoperatoren liefern ein Ergebnis vom Typ Boolean, also nur wahl oder falsch.

 Vergleichsoperatoren

Vergleichsoperatoren
Vergleichsoperatoren von Jan Martens

 

 

Ein paar Fallstricke gibt es leider noch im Umgang mit den Vergleichsoperatoren. Wir können generell zwei Zahlen unterschiedlichen Typs miteinander vergleichen. Dabei bringt Java die Zahlen wieder auf einen brauchbaren gemeinsamen Typ. Bei einem Vergleich von einer int Zahl mit einer long Zahl ist dies unkritisch. Vergleichen wir aber z.B. eine int 1 mit einer float 1.5 so wird als gemeinsamer Typ int festgelegt und die Zahlen sind für Java gleich. Aber auch der vergleich von Float und Double mit Zahlen gleichen Typs kann wegen der Ungenauigkeit bei der Abbildung unerwartete Ergebnisse liefer. Der Vergleich 100000000.0f == 100000001.0f liefert beispielsweise wahr als Ergebnis, was mathematisch natürlich nicht stimmt.

Der Gleichheits- und der Ungleichheitsoperator sind nicht nur für Zahlen definiert, sondern für alle Datentypen. Bisher haben wir uns nur mit String beschäftigt und zum Vergleich immer die .equals()-Methode verwendet. Das war auch besser, denn für die meisten Datentypen, ist die Gleichheit nicht als Gleichheit des dargestellten Wertes definiert. Der Operator prüft “nur” ob es sich um den selben Speicherbereich handelt und das ist nicht immer einfach nachzuvollziehen. Ein Beispiel mit Erklärung:
 
 Scanner scanner = new Scanner(System.in);
 String a = ".";
 String b = a;
 String c = ".";
 String d = scanner.next(); // Wir geben nur einen . ein und drücken Enter.

 System.out.println(a == b); 
 // ergibt true
 // Da wir b = a gesetzt haben, handelt es sich um den selben Speicher.

 System.out.println(a == c); 
 // ergibt true
 // Der Compiler hat festgestellt, dass beide Strings gleich aussehen und lässt beide Variablen auf den selben Speicher zeigen.

 System.out.println(a == d);
 // ergibt false
 // Der Compiler hat einen neuen Speicherbereich vorgesehen, da er nicht wusste was eingegeben wird.
    

Für den Programmierer ist es oftmals nicht ersichtlich, welche Optimierungen der Compiler vornehmen wird und es ist für ihn meistens auch irrelevant. Wichtig ist, dass man den Operator nur einsetzt, wenn man weiß was er wirklich vergleicht und im Zweifel lieber die .equals()-Methode verwendet, welche meistens die erwarteten Ergebnisse bringt. Als dritte Gruppe wollen wir uns noch einmal die boolschen Operatoren anschauen.

 ! (Verneinungsoperator)

Durch eine Verneinung wird eine Wahrheitsaussage in das Gegenteil verwandelt.

&& (logisches Und)

Durch das logische Und werden zwei Wahrheitsaussagen verbunden. Nur, wenn beide der Aussagen wahr sind, ist das Ergebnis wahr.

 || (logisches Oder)

Das logische Oder liefert als Ergebnis wahr, wenn mindestens eine der zwei Wahrheitsaussagen wahr ist.

 

Es ist wichtig zu wissen, dass zuerst die linke Aussage ausgewertet wird. Ist sie wahr, so wird die Rechte ignoriert. Nur wenn die Linke falsch ist, wird die rechte Aussage ausgewertet. Wenn die rechte Aussage jetzt durch einen Methodenaufruf ermittelt wird und die Methode vielleicht mehr tut als nur das Ergebnis zurück zu geben, so kann dies wesentliche Auswirkungen haben.
Es ist erlaubt mehrere Operatoren in einem Ausdruck zu kombinieren. Hierbei muss aber auf die Kompatibilität der Rückgabetypen und die Reihenfolge der Auswertung geachtet werden. Die folgende Tabelle stellt die Reihenfolge dar:
 
Reihenfolge mehrerer Operationen
Reihenfolge mehrerer Operationen von Jan Martens

 

 

Es gilt also Punkt- vor Strich. Erst danach werden Vergleiche durchgeführt und die logischen Operatoren zuletzt angewandt. Diese Reihenfolge kann durch Klammerung angepasst werden:

 

 
 System.out.println( 2 + 3 * 4 );   // Ergebnis 14
 System.out.println( (2 + 3) * 4 ); // Ergebnis 24

Konvertierung

Wir wollen uns nun noch mit der Umwandlung, von einem Datentyp in einen Anderen, befassen. Wir haben schon gesehen, dass Java dies in manchen Situationen automatisch durchführt. Aber es gibt auch Situationen, in denen Java dies nicht tut. Nämlich immer dann, wenn unter Umständen Informationen verloren gehen könnten.


 long l = 1;  // funktioniert 
 int  i = 1l; // Erzeugt einen Compilerfehler
In diesen Situationen ist es nötig, dass wir einen expliziten Typecast, also eine Typenkonvertierung, durchführen. Hierfür schreiben wir den gewünschten Typ in klammern vor den Ausdruck, der konvertiert werden soll int i = (int)1l;. Die Typenkonvertierung wird von Java durchgeführt, bevor andere Operatoren ausgewertet werden. Aus diesem Grund muss ggF. mit Klammern die Auswertungsreihenfolge angepasst werden:


 System.out.println( (int)1.5 * 2 ); // Ergebnis 2
 System.out.println((int)(1.5 * 2)); // Ergebnis 3
Wie wir eine Zahl in einen String umwandeln hatten wir schon gesehen. Wir verbinden einen String mit der Zahl. Dies kann auch der leere String sein: String s = 1 + "";.

Der Umgekehrte Vorgang erfordert leider ein wenig mehr Aufwand. Es gibt für unsere vier Zahlen-Typen jeweils eine Klasse, die diesen Datentyp auch darstellen kann. Sie heißen Integer, Long, Float und Double. Jede dieser Klassen hat eine Klassen-Methode, die aus einem String eine Zahl macht. Mittels Integer.parseInt(string) können wir aus einem String die Zahl machen, die er darstellt. Die anderen Datentypen funktionieren analog. Es ist aber erforderlich, dass in dem String nur Ziffern enthalten sind, er ggf. ein Vorzeichen hat und bei Double und Float, darf er einen Dezimalpunkt enthalten. Wird diese Form nicht eingehalten, so bricht Java mit einem Fehler ab.

Arrays

Wir haben mittlerweile die Möglichkeit Variablen mit einer Vielzahl von unterschiedlichen Datentypen zu definieren. Wir müssen aber für jede Variable, die wir verwenden wollen, eine Deklaration durchführen und das statisch während wir programmieren. Es ist uns nicht möglich eine variable Anzahl von Werten zu verarbeiten, deren genaue Anzahl sich erst zur Laufzeit des Programms herausstellt.

Eine Lösung für dieses Problem wäre ein sogenanntes Array. Ein Array ist eine Liste von Variablen des gleichen Typs. Der große Vorteil einer solchen Liste ist, dass man deren Größe, also die Anzahl der Elemente, zur Laufzeit festlegen kann.

Für die Deklaration eines Arrays genügt es dem Datentyp eckige Klammern anzufügen. Für die Initialisierung benötigen wir das new Schlüsselwort und geben nach dem Datentyp die Größe in eckigen Klammern an. Mit int[] list = new int[10]; kann eine Liste mit Platz für 10 Zahlen erstellt werden. Diese, bei der Deklaration angegebene, Größe kann nicht nachträglich geändert werden ohne das Array durch ein Neues zu ersetzen. Auch ist es empfehlenswert, in jedes einzelne Element erst einen definierten Wert einzutragen, bevor man beginnt Werte daraus zu verwenden.

Um mit einem Wert in einem Array zu arbeiten, werden wieder die eckigen Klammern verwendet. So kann mit list[0] = 1; das erste Element mit dem Wert erste belegt werden. Arrays in Java sind null-basiert, das heißt, dass das erste Element mit der 0 angesprochen wird und das Hunderste mit dem Index 99. Dies muss einem beim Umgang mit Arrays bewusst sein! Mit der Eigenschaft length des Arrays kann jederzeit geprüft werden, wie groß das Array ist. Mit list[list.length - 1] kann zum Beispiel auf das letzte Element zugegriffen werden. Auch hier ist zu beachten, dass bei einem Array, dass mit der Größe 5 initialisiert wurde, die length-Eigenschaft den Wert 5 hat, das letzte Element aber mit dem Index 4 angesprochen werden muss!

Es ist im Zusammenspiel mit Arrays üblich, die for-Schleife zu verwenden. Eine simple Initialisierung mit aufsteigenden Zahlen könnte zum Beispiel durch das folgende Beispiel geschehen.


 int[] zahlen = new int[25];
 for(int i = 0; i < zahlen.length; i++) {
     zahlen[i] = i + 1;
 }
Ein Array kann für jeden beliebigen Java Datentyp deklariert werden. Es ist also auch möglich Arrays zu deklarieren, die Arrays enthalten. Wir nennen dies ein mehrdimensionales Array. Ein Feld mit den Dimensionen 3 x 4 könnte durch int[][] feld = new int[3][4]; erstellt werden. Es kann beispielsweise mit feld[0][0] = 1 der erste Wert gesetzt werden.