Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage |
<< | < | > | >> | API | Kapitel 2 - Schnelleinstieg |
Eine neue Programmiersprache lernt man nur, wenn man selbst Programme in dieser Sprache schreibt. Je mehr, desto besser. Bei der Lektüre des Buchs ist es daher sinnvoll, von Anfang an eigene Experimente durchzuführen und so viele Beispielprogramme wie möglich auszuprobieren, abzuändern, zu übersetzen und auszuführen. In diesem Abschnitt wollen wir ein paar informelle Tips geben, die das erleichtern sollen.
Hier noch einmal in Kurzform die Schritte vom Quellcode bis zum lauffähigen Programm:
Für die ersten Schritte in einer neuen Sprache benötigt man immer auch I/O-Routinen, um einfache Ein- und Ausgaben vornehmen zu können. Glücklicherweise kann man in Java nicht nur grafikorientierte Programme schreiben, sondern auch auf die Standardein- und -ausgabe zugreifen. Damit stehen für kleine Programme einfache I/O-Routinen zur Verfügung, die wie in den meisten konventionellen Programmiersprachen verwendet werden können.
Mit Hilfe des Kommandos System.out.println können einfache Ausgaben auf den Bildschirm geschrieben werden. Nach jedem Aufruf wird eine Zeilenschaltung ausgegeben. Mit System.out.print kann diese auch unterdrückt werden. Beide Methoden erwarten ein einziges Argument, das von beliebigem Typ sein kann. Zwar sind print und println nicht so flexibel wie etwa printf in C. Mit Hilfe des Plus-Operators können aber immerhin Zeichenketten und numerische Argumente miteinander verknüpft werden, so daß man neben Text auch Zahlen ausgeben kann:
001 /* Listing0202.java */ 002 003 public class Listing0202 004 { 005 public static void main(String[] args) 006 { 007 System.out.println("1+2=" + (1+2)); 008 } 009 } |
Listing0202.java |
Die Ausgabe des Programms ist:
1+2=3
Der erste String enthält den Wert "1+2=" und wird mit dem Ergebnis des Ausdrucks (1+2) verkettet (also mit dem Wert 3). Ohne die Klammern würde der Compiler das zweite Plus ebenfalls als String-Verkettungsoperator ansehen und die Ausgabe des Programms wäre 1+2=12 gewesen. |
|
Leider ist es etwas komplizierter, Daten zeichenweise von der Tastatur zu lesen. Zwar steht ein vordefinierter Eingabe-Stream System.in zur Verfügung. Er ist aber nicht in der Lage, die eingelesenen Zeichen in primitive Datentypen zu konvertieren. Statt dessen muß zunächst eine Instanz der Klasse InputStreamReader und daraus ein BufferedReader erzeugt werden. Dieser kann dann dazu verwendet werden, die Eingabe zeilenweise zu lesen und das Ergebnis in einen primitiven Typ umzuwandeln. Weitere Informationen zur streambasierten Ein-/Ausgabe sind in Kapitel 18 und Kapitel 19 zu finden.
Das folgende Listing zeigt ein Programm, das zwei Ganzzahlen einliest, sie zusammenzählt und das Ergebnis auf dem Bildschirm ausgibt:
001 /* Listing0203.java */ 002 003 import java.io.*; 004 005 public class Listing0203 006 { 007 public static void main(String[] args) 008 throws IOException 009 { 010 int a, b, c; 011 BufferedReader din = new BufferedReader( 012 new InputStreamReader(System.in)); 013 014 System.out.println("Bitte a eingeben: "); 015 a = Integer.parseInt(din.readLine()); 016 System.out.println("Bitte b eingeben: "); 017 b = Integer.parseInt(din.readLine()); 018 c = a + b; 019 System.out.println("a+b="+c); 020 } 021 } |
Listing0203.java |
Die import-Anweisung am Anfang des Listings dient dazu, die Klassen des Pakets java.io bekanntzumachen. Ohne diese Anweisung würden sowohl BufferedReader als auch InputStreamReader vom Compiler nicht gefunden und es gäbe eine entsprechende Fehlermeldung. Auch in nachfolgenden Beispielprogrammen tauchen von Zeit zu Zeit import-Anweisungen auf, die java.lang oder andere Pakete einbinden. In Kapitel 13 werden wir ausführlich auf die Verwendung und das Erstellen von Paketen eingehen. |
|
Werden die Zahlen 10 und 20 eingegeben, so lautet die Ausgabe des
Programms:
Bitte a eingeben:
10
Bitte b eingeben:
20
a+b=30
Das Ergebnis von din.readLine ist ein String, der den Inhalt der Eingabezeile enthält. Sollen keine numerischen Datenwerte, sondern Zeichenketten eingelesen werden, kann der Rückgabewert auch direkt verwendet werden. |
|
Es ist bekannt, daß man sich über die Formatierung von Quelltexten und die Einrückung von Deklarationen und Anweisungen streiten kann. Jeder Entwickler hat seinen eigenen Stil und kennt gute Argumente, genau diesen zu verwenden. Bei Java-Programmen gibt es einige große Lager, denen man sich anschließen kann. Im professionellen Umfeld ist man sogar meist gezwungen, sich einem vorgegebenen Stil anzupassen. Wir wollen uns diesen fruchtlosen Diskussionen nicht anschließen und keinesfalls behaupten, die in diesem Buch verwendete Art der Quelltextformatierung wäre die einzig richtige. Wir haben jedoch versucht, die Beispielprogramme konsistent zu formatieren und dabei einige wenige, leicht verständliche Regeln einzuhalten.
Bei Klassen- und Methodendefinitionen stehen die geschweiften Klammern unterhalb der Deklarationsanweisung, und die eigentliche Deklaration ist eingerückt:
001 import java.io.*; 002 003 public class Listing0204 004 { 005 public static void main(String[] args) 006 { 007 //Hier steht der Methodenrumpf 008 } 009 } |
Bei Kontrollanweisungen innerhalb einer Methode schreiben wir die öffnende Klammer dagegen in dieselbe Zeile wie die einleitende Anweisung:
001 for (int i = 0; i < aNeighbours.length; ++i) { 002 if (p1.x + aNeighbours[i][0] == p2.x) { 003 if (p1.y + aNeighbours[i][1] == p2.y) { 004 return true; 005 } 006 } 007 } |
Dies gilt auch für fortgesetzte Anweisungen wie beispielsweise else oder else if:
001 if (cmd.equals("Größer")) { 002 d.height *= 1.05; 003 d.width *= 1.05; 004 } else if (cmd.equals("Kleiner")) { 005 d.height *= 0.95; 006 d.width *= 0.95; 007 } else { 008 x = 10; 009 } |
Diese Technik verwenden wir meist auch, wenn bei einem Methodenaufruf nicht alle Argumente in eine Zeile passen:
001 System.out.println( 002 "Grüße aus Hamburg".regionMatches( 003 8, 004 "Greetings from Australia", 005 8, 006 2 007 ) 008 ); |
Diese einfachen Regeln lassen sich in den meisten Fällen anwenden, es gibt aber auch Fälle, in denen sie versagen. So zum Beispiel, wenn der Testausdruck einer if-Anweisung über mehrere Zeilen geht, wenn die Parameterdeklaration einer Methode nicht in eine Zeile paßt oder schlicht, wenn die Verschachtelung bereits sehr tief ist und keine weitere Einrückung zuläßt. In diesem Fall sei der Leser um Nachsicht gebeten und aufgefordert, den ästhetischen Anspruch an das Programm den jeweiligen pragmatischen Erwägungen unterzuordnen.
Wie in allen Programmiersprachen, gibt es auch in Java Konventionen für die Vergabe von Namen. Sie sind zwar nicht zwingend erforderlich, erleichtern aber das Verständnis der Quelltexte ungemein und sollten daher unbedingt eingehalten werden. Die wichtigsten sind:
Weitere Konventionen werden, wenn erforderlich, im Buch beschrieben.
Immer wieder wird gefragt, wie man Java-Programme möglichst einfach unter Windows aufrufen kann. Das Starten aus der DOS-Box erscheint vielen Lesern zu umständlich, und bei einem Java-Programm mit grafischer Oberfläche stört das überflüssige Konsolenfenster. Im Gegensatz zu com- oder exe-Dateien ist ein kompiliertes Java-Programm zwar nicht direkt ausführbar, aber es gibt doch einige Möglichkeiten, das Starten unter Windows zu vereinfachen.
Eine erste Vereinfachung besteht darin, eine Batchdatei zu erstellen,
aus der der java-Interpreter mit dem Java-Programm als Argument aufgerufen
wird. Soll beispielsweise das Java-Programm Listing3701,
dessen Bytecode Listing3701.class im
Verzeichnis c:\tmp liegt, mit dem java-Interpreter,
der in das Verzeichnis c:\jdk1.4 installiert
wurde, gestartet werden, kann dazu folgende Batch-Datei verwendet
werden:
@echo off
c:
cd \tmp
c:\jdk1.4\bin\java Listing3701
Wird diese beispielsweise go3701.bat genannt und in ein Verzeichnis gelegt, das über die PATH-Angabe zu erreichen ist, kann unser Java-Programm durch einfache Eingabe des Kommandos go3701 aus jedem Verzeichnis heraus oder aus dem »Ausführen«-Dialog des Startmenüs gestartet werden.
Ebenso leicht ist es möglich, ein Java-Programm durch einen Doppelklick auf ein Desktop-Symbol zu starten. Dazu wird eine Verknüpfung angelegt, die den java-Interpreter mit dem Programmnamen als Argument aufruft. Nach einem rechten Mausklick auf den Windows-Desktop ist dazu zunächst der Menüpunkt »Neu.Verknüpfung« aufzurufen. Anschließend sind die Angaben für Programm, Arbeitsverzeichnis und Icon so zu machen, daß der spätere Eigenschaften-Dialog des neu angelegten Symbols wie folgt aussieht (beispielhaft für Windows 98):
Abbildung 2.5: Eine Verknüpfung auf dem Windows-Desktop
Das Java-Programm kann nun wie jedes andere Windows-Programm per Doppelklick vom Desktop aus gestartet werden.
Soll ein java-Programm, das eine grafische Oberfläche hat, ohne Konsolenfenster gestartet werden, kann anstelle des Standard-Interpreters java der alternative Interpreter javaw verwendet werden. Er arbeitet genauso wie java und akzeptiert dieselben Optionen (siehe Abschnitt 50.2), stellt der Anwendung aber kein Konsolenfenster zur Verfügung. Zu beachten ist allerdings, daß alle Ausgaben auf System.out und System.err ins Leere gehen.
Nachfolgend noch einmal eine kurze Zusammenfassung der häufigsten Probleme beim Erstellen, Übersetzen und Starten eines Java-Programms, inklusive Beschreibung, wie man sie behebt oder umgeht.
Alle Werkzeuge des JDK arbeiten kommandozeilenorientiert. Anders als typische GUI-Programme wie Adobe Photoshop, Microsoft Excel oder Netscape Navigator besitzen sie also keine grafische Oberfläche, sondern werden aus einer »DOS-Box« (bzw. einer UNIX-Shell) heraus aufgerufen. Diese wird unter Windows-Betriebsystemen als »MS-DOS-Eingabeaufforderung« bezeichnet und ist meist im Startmenü (unter »Programme«) zu finden. In der DOS-Box werden Befehle und ihre Parameter über die Tastatur eingegeben und starten so die zugehörigen Programme. Um in einer DOS-Box zurecht zu kommen, sollte man deren Kommandosyntax und die wichtigsten Befehle zum Starten von Programmen und für Datei- und Verzeichnisoperationen kennen. Beides wird in unzähligen Büchern und (zum Teil) in den Hilfedateien des Betriebssystems beschrieben.
Das UNIX-Pendant zu einer DOS-Box ist die Shell. Auch sie dient primär dazu, Benutzerkommandos zu empfangen und auszuführen. UNIX-Shells sind wesentlich mächtiger als DOS-Boxen unter Windows, und im Laufe der Jahre haben sich verschiedene Varianten entwickelt (Bourne-Shell, C-Shell, Korn-Shell, BASH etc.). Obwohl viele der elementaren Shell-Kommandos den DOS-Box-Pendants gleichen oder ihnen recht ähnlich sind, lassen sich mit ihrer detaillierten Beschreibung leicht ganze Bücher füllen. Das Kommando man liefert eine Beschreibung der Shell und aller zugehörigen UNIX-Kommandos. |
|
Wer nicht kommandozeilenorientiert arbeiten will, kann statt der im Buch beschriebenen JDK-Werkzeuge natürlich auch eine integrierte Entwicklungsumgebung zum Erlernen der Java-Programmierung verwenden (z.B. Borland JBuilder). In diesem Fall stehen Editor, Compiler, Interpreter und alle anderen Werkzeuge unter einer gemeinsamen und einheitlichen grafischen Oberfläche zur Verfügung und können auf Knopfdruck gestartet werden. Der Nachteil dieser integrierten Werkzeuge ist allerdings eine etwas höhere Einarbeitungszeit, denn von der Vielzahl der Möglichkeiten wird man zunächst erschlagen. Zudem sind wichtige Techniken und Arbeitsschritte oftmals hinter Hilfsprogrammen und Assistenten verborgen und erschweren so das Verständnis grundlegender Konzepte.
Wenn unter Windows der Java-Interpreter java gestartet werden kann, beim Aufruf des Compilers javac aber eine Fehlermeldung kommt, liegt das meist daran, daß der PATH nicht korrekt gesetzt wurde. Während die JDK-Installation den Interpreter unter anderem auch in Verzeichnisse kopiert, auf die der PATH bereits verweist (so daß dieser ohne weiteres Zutun aus jedem Verzeichnis aufgerufen werden kann), ist das für den Compiler und die übrigen JDK-Werkzeuge nicht der Fall. Diese können nur dann ohne vorangestellten Pfad aufgerufen werden, wenn der PATH auf das Unterverzeichnis bin des Installationsverzeichnisses verweist. Bei der in diesem Buch angenommen Standard-Installation muß die PATH-Variable also das Verzeichnis c:\jdk1.4\bin enthalten.
Einer der Standardmechanismen zur Konfiguration von Programmen besteht darin, Umgebungsvariablen zu setzen. Diese werden vom Anwender (meist innerhalb einer DOS-Box bzw. Shell mit Hilfe des set-Kommandos) gesetzt und vom Programm gelesen und zur Konfiguration verwendet. Die beiden für das JDK wichtigsten Umgebungsvariablen sind PATH und CLASSPATH. Über erstere wird dem Betriebssystem mitgeteilt, wo es nach ausführbaren Programmen suchen soll, also etwa nach dem Compiler oder Interpreter des JDK. Die Umgebungsvariable CLASSPATH (umgangssprachlich »der CLASSPATH« genannt) ist JDK-spezifisch. Sie zeigt dem Compiler und Interpreter an, in welchen Verzeichnissen er nach Klassendateien suchen soll. Im Gegensatz zu PATH kann der CLASSPATH bei der Standardinstallation eines aktuellen JDKs (Version 1.3 oder später) ungesetzt bleiben. Compiler und Interpreter finden ihr eigenes Laufzeitsystem automatisch, und die selbst geschriebenen Klassen werden im aktuellen Verzeichnis gesucht.
Da es zu umständlich ist, häufig benötigte Umgebungsvariablen nach jedem Aufruf einer DOS-Box neu zu setzen, bieten alle Betriebssysteme die Möglichkeit, sie permanent festzulegen. Abschnitt 2.1.2 erläutert dies am Beispiel der PATH-Variable.
Wenn der Compiler javac die .java-Datei nicht findet, liegt dies in aller Regel daran, daß sie nicht existiert. Der Compiler unterscheidet zwischen Groß- und Kleinschreibung; die Datei Test1.java ist somit streng von test1.java oder TEST1.JAVA zu unterscheiden. Zudem muß die Dateierweiterung .java lauten, denn andere Erweiterungen werden nicht akzeptiert. Hat der Editor fälschlicherweise die Erweiterung .txt angehängt, wird die Datei vom Compiler nicht gefunden. Mitunter kommt es auch vor, daß Beispieldateien beim Kopieren oder Entpacken in das MS-DOS-8.3-Format konvertiert werden. Die Datei Test1.java würde dann TEST1.JAV heißen und vom Compiler nicht akzeptiert werden.
Wichtig ist auch, daß der Compiler korrekt aufgerufen wird. Anders als beim Interpreter wird die Dateierweiterung stets angegeben. Ein Aufruf von javac Test1 wird also nicht funktionieren, javac Test1.java dagegen sehr wohl. Schließlich ist zu beachten, daß die zu übersetzende Klasse denselben Basisnamen hat wie die Datei, in der sie definiert wurde. In der Datei Test1.java muß also eine öffentliche Klasse mit dem Namen Test1 definiert worden sein.
Der häufigste Fehler beim Aufruf des Interpreters ist ein »NoClassDefFoundError«. Ein trivialer Fehler besteht darin, daß der Interpreter falsch aufgerufen wurde. Im Gegensatz zum Compiler möchte er keinen Dateinamen als Argument übergeben bekommen, sondern einen Klassennamen. Folglich darf die Erweiterung .java oder .class nicht angegeben werden. Die in die Datei Test1.class übersetzte Klasse Test1 wird also durch Aufruf von java Test1 gestartet, und nicht durch java Test1.class.
Das funktioniert allerdings nur, und das ist auch schon die zweite Fehlerquelle, wenn Test1 auch tatsächlich eine Methode public static void main enthält. Diese versucht nämlich der Interpreter zu starten, und wenn sie nicht vorhanden ist, gibt es obige Fehlermeldung.
Ist der Aufruf korrekt und die main-Methode ebenso vorhanden wie die nötige Klassendatei, kann es trotzdem vorkommen, daß der Interpreter die Klasse nicht starten kann. Das liegt dann meist daran, daß etwas mit dem CLASSPATH nicht stimmt. Der CLASSPATH ist eine Umgebungsvariable, die eine Liste von Verzeichnissen und Archivdateien enthält, die vom Interpreter der Reihe nach durchsucht werden, um die auszuführenden Klassen zu finden. Die genaue Interpretation des CLASSPATH hat sich über die Jahre etwas geändert, aber bei aktuellen JDK-Versionen kann sie wie folgt zusammengefaßt werden:
Ist gar kein CLASSPATH angegeben, sucht der Interpreter im aktuellen Verzeichnis (bzw. bei Klassen in Unterpaketen in darin befindlichen Unterverzeichnissen) nach der Klassendatei. Ist dagegen eine Umgebungsvariable CLASSPATH vorhanden, durchsucht der Interpreter dessen Elemente der Reihe nach. Befindet sich darin nicht das aktuelle Verzeichnis (entweder absolut oder als ».« angegeben), so werden auch keine Klassen gefunden, die im aktuellen Verzeichnis liegen. Für unsere ersten Versuche ist es bei aktuellen JDKs also am besten, den CLASSPATH gar nicht zu setzen und alle Klassen in das aktuelle Verzeichnis zu legen.
Mitunter wird der CLASSPATH von anderen installierten Java-Programmen verändert und sorgt so unbeabsichtigt dafür, daß die eigenen Klassen nicht mehr gefunden werden. Das ist zwar kein guter Stil, kommt in der Praxis aber dennoch manchmal vor. In einem solchen Fall muß der CLASSPATH während der eigenen Experimente zurückgesetzt oder so geändert werden, daß das aktuelle Verzeichnis (bzw. das mit den eigenen Klassendateien) darin enthalten ist.
Neben der CLASSPATH-Umgebungsvariable kann der Klassenpfad dem Compiler und Interpreter auch direkt beim Aufruf über Kommandozeilenschalter bekanntgemacht werden (die entsprechenden Argumente lauten -cp bzw. -classpath). Werden also beispielsweise Compiler und Interpreter über Batch-Dateien bzw. Shell-Scripte aufgerufen, könnten darin entsprechende Parameter enthalten sein und das Auffinden der eigenen Klassen erfolgreich verhindern. Auch dies ist eine Fehlerquelle, die in der Praxis mitunter eine Rolle spielt. |
|
Gibt es beim Ausführen eines Beispielprogramms die Fehlermeldung, daß eine andere als die aufgerufene Klasse nicht gefunden werden kann, liegt das meist daran, daß deren .class-Datei nicht mit aus dem Verzeichnis mit den Beispieldateien in das aktuelle Verzeichnis kopiert wurde. Prominentester Kandidat ist der WindowClosingAdapter, der von fast allen Beispielprogrammen benötigt wird, die eine grafische Oberfläche haben. Weitere Hinweise sind in Abschnitt 2.2.2 zu finden.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0.1 |
<< | < | > | >> | API | © 1998-2003 Guido Krüger, http://www.javabuch.de |