Programmierung in Java

Java Virtual Machine

Das Besondere an Java ist wohl, daß der Programmcode nicht vom Prozessor selbst ausgeführt wird, sondern in einer Virtuellen Maschine (JVM).

Vorteile sind :

Nachteile sind :

Die JVM arbeitet mit einer assemblerartigen Sprache, die sich Byte Code nennt. Ein Java-Compiler übersetzt ein Programm in diesen Byte Code. Da dieser standardisiert ist, ist auch der übersetzte Code portabel und kann dadurch auch ohne Quelltext vertrieben werden. (Es gibt jedoch Programme, die aus dem Byte Code wieder Java Code erzeugen können, da viele Informationen dazu im Dateiformat des Byte Codes stehen.)

Der Byte Code kann auch mit anderen Methoden erzeugt werden, z. B. mit einem "Byte Code Assembler", wodurch aber die kontrollierte Umgebung nicht umgangen werden kann.

Der Byte Code steht normalerweise in einem Class File oder einem Java Archive (JAR), kann jedoch auch aus anderen Quellen stammen, z. B. über das Netzwerk übertragen werden. Die Virtuelle Maschine läd den benötigten Byte Code aus diesen Dateiformaten zur Laufzeit. Dadurch kann ein Programm entscheiden, welcher Code woher geladen werden soll.

Die Virtuelle Maschine kann ein eigenständiges Programm sein oder auch in ein Anwendungsprogramm eingebaut und von ihm kontrolliert sein, z. B. in einem Browser.

Java

Objekt Orientierte Programmierung

Eine Klasse ist eine Zusammenfassung von Attributen und Methoden. Eine Klasse ist eine statische Beschreibung entsprechend einem Programm-Modul.

Objekte sind Instanzen von Klassen. Diese werden im Programm zur Laufzeit dynamisch erzeugt und nach Gebrauch vernichtet.

Attribute entsprechen Programm-Variablen. Diese können einfache Daten, z. B. Zahlen, oder Referenzen zu Objekten enthalten. Jedem Attribut ist ein Datentyp zugeordnet, der den Wertebereich bestimmt.

Jedes Objekt hat seine eigenen Instanzen der Attribute.

Methoden sind der eigentliche Programmcode. Methoden werden nicht wie Prozeduren oder Funktionen aufgerufen, sondern auf Objekte angewandt.

Eine besondere Fähigkeit von Klassen ist deren Ableitung. Eine Klasse kann dadurch eine Erweiterung einer anderen Klasse (Oberklasse, Superklasse oder Basisklasse) darstellen. Die abgeleitete Klasse wird als Unterklasse (Subklasse) bezeichnet. Auf Objekte dieser Klasse können alle Methoden der Oberklasse angewandt werden. Ebenso können alle ihre Attribute verwendet werden. Diesen Vorgang bezeichnet man als Vererbung.

Alle Klassen bilden durch die Vererbung eine Hierarchie.

Sprache

Die "Muttersprache" von Java ist der Unicode (ISO 10646), genauer UCS-2. Dies ist eine Zusammenfassung vieler gebräuchlicher Zeichensätze. Ein Zeichen wird hierbei durch 16 Bit dargestellt.

Der hierzulande gebräuchliche Code ISO 8859-1 liegt hierin im Bereich 0x0000 bis 0x00FF, der ASCII-Code damit im Bereich 0x0000 bis 0x007F.
Das Euro-Zeichen hat z. B. den Code 0x20AC.

Eine gebräuchliche Darstellung des Unicodes ist UTF-8. Diese codiert die ASCII-Zeichen in einem Byte, die Zeichen höherer Codes dagegen mit bis zu 6 Bytes (Siehe RFC 2279).

Um Java-Programme in üblichem ISO 8859-1 bearbeiten zu können, Wandelt der Java-Compiler diesen intern in Unicode um.

Man kann beliebige Unicode-Zeichen ersatzweise darstellen in der Form \unnnn, wobei nnnn den Zeichencode darstellt.

Kommentare haben in Java folgenden Aufbau :

Identifier (symbolische Namen) bestehen aus Buchstaben, Ziffern oder dem Zeichen "_" (Underscore), sie dürfen jedoch nicht mit einer Ziffer beginnen. Es sind alle Zeichen, die im Unicode als Buchstaben oder Ziffern bezeichnet sind, zulässig, also z. B. auch die deutschen Umlaute und griechische Buchstaben.

Pakete

Um ein gewisses Maß an Ordnung zu schaffen, werden alle Klassen in verschiedene Pakete zusammengefaßt. Paketnamen können aus mehreren Namen mit "." (Punkt) getrennt bestehen. Hierdurch läßt sich eine Hierarchie aufbauen.

Das Paket java.lang enthält alle Klassen, die unmittelbar mit der Sprache Java zusammenhängen.

Die Paketnamen java und javax und alle ihre Unterpakete sind reserviert für die Sprache selbst und ihre Erweiterungen.

Paketnamen werden kleingeschrieben.

Jede Übersetzungseinheit beginnt mit der Angabe des Paketes, zu dem sie gehört :

    package elug;
Dies gibt an, daß alles Nachfolgende zum Paket elug gehört.

Alle anderen Klassen aus diesem Paket und dem Paket java.lang werden automatisch importiert.

Es können auch Klassen aus anderen Paketen importiert werden :

    import java.util.List;
    import java.io.*;
Hier wird die Klasse List aus dem Paket java.util und alle Klassen des Paketes java.io importiert.

Importierte Klassen können im Folgenden mit ihrem einfachen Namen verwendet werden, solange dieser eindeutig ist. Nicht importierte Klassen müssen mit ihrem vollen Namen (mit Paketnamen) angegeben werden.

Klassen

Klassen werden mit dem Schlüsselwort class definiert :
    public class HelloWorld
    {
      ...
    }

Die Angabe public bewirkt, daß die Klasse in allen Klassen verwendbar ist. Fehlt die Angabe, so kann die Klasse nur innerhalb des selben Paketes verwendet werden.

Eine Angabe final verhindert, daß die Klasse abgeleitet werden kann.

Eine Angabe abstract verhindert, daß von der Klasse Objekte erzeugt werden können. Hierdurch lassen sich Klassen unvollständig definieren, die erst als Unterklassen verwendbar sind.

Zur Ableitung wird das Schlüsselwort extends verwendet :

    class Square extends Rectangle
    {
      ...
    }
Wird keine entsprechende Angabe gemacht, so erfolgt die Ableitung von der Klasse Object.

Object ist die oberste Klasse der Hierarchie aller Klassen.

Klassennamen werden in gemischter Groß-/Kleinschrift mit großem Anfangsbuchstaben gebildet.

Die Hierarchie der Klassen ist von der Hierarchie der Pakete unabhängig. Eine Klasse in einem Paket kann also von einer Klasse eines anderen Paketes abgeleitet werden.

Datentypen

Es gibt einfache Datentypen :

Bezeichnung Beschreibung Literale Wrapper Class
long Ganzzahl, 64 Bit, mit Vorzeichen 0x12345678ABCD, 123456L Long
int Ganzzahl, 32 Bit, mit Vorzeichen 01234567, 1234 Integer
short Ganzzahl, 16 Bit, mit Vorzeichen   Short
byte Ganzzahl, 8 Bit, mit Vorzeichen   Byte
char Schriftzeichen, Unicode, 16 Bit, ohne Vorzeichen 'X', '\t' Character
float Gleitkomma, 32 Bit, IEEE 754 1E12F, 2F Float
double Gleitkomma, 64 Bit, IEEE 754 3.14159, 1.234E-56 Double
boolean Wahrheitswert true, false Boolean

Daneben gibt es für jede Klasse den zugehörigen Referenz-Datentyp. Dieser ist mit dem jeweiligen Klassennamen identisch.
Der besondere Wert null ist für alle Referenzen verwendbar.

Für alle einfachen Datentypen gibt es Wrapper Classes. Die umfassen den jeweiligen Datentyp und stellen Methoden zur Verfügung, z. B. zur Umwandlung und Ausgabeformatierung.

Eine einfache Möglichkeit der Umwandlung ähnlicher Datentypen is das Type Casting. Sei a eine Variable vom Typ long und b eine Variable vom Typ short.

    a = (long) b;

Strings (Zeichenketten) stehen als Java-Klasse String zur Verfügung. Jedes String-Objekt stellt jeweils einen konstanten String dar.

Die String-Klasse stellt u. a. Methoden zur Bestimmung der Länge eines Strings und zur Umwandlung eines Strings in eine Zahl zur Verfügung.

Im Programm können String-Literale direkt in der Form "my String" angegeben werden.

Zur Bearbeitung von Strings gibt es die Klasse StringBuffer. Diese kann mehrere Strings zusammenfügen und anschließend das Ergebnis als String liefern.

Arrays werden als Objekte einer dynamisch erzeugten Klasse behandelt. Nach Erzeugung haben sie eine feste Anzahl Elemente eines bestimmten Datentyps. Über das Attribut length läßt sich die Länge des Arrays ermitteln.

Arrays können auch Arrays enthalten, diese wiederum mit einer unterschiedlichen Anzahl Elemente.

Feldvariablen

Attribute werden in Java als Feldvariablen bezeichnet. Sie stellen objektbezogene Variablen dar.

Der Zugriff auf diese Variablen von anderen Klassen aus läßt sich einschränken :

public
bewirkt, daß die Variable von allen Klassen verwendbar ist.
protected
bewirkt, daß die Variable von dieser und allen Unterklassen verwendbar ist.
private
bewirkt, daß die Variable nur von dieser Klasse verwendbar ist.
Fehlen diese Angaben, so ist die Variable von dieser Klasse, allen Unterklassen und allen anderen Klassen dieses Paketes verwendbar (Friend Classes).

Die Angabe static bewirkt, daß die Variable nicht objektbezogen, sondern klassenbezogen ist. Das bedeutet, daß alle Objekte dieser Klasse die Variable gemeinsam besitzen.

Die Angabe final bewirkt, daß die Variable nur einmal einen Wert zugewiesen bekommen kann (meistens als Konstanten durch Initialisierung).

Feldvariablennamen werden in gemischter Groß-/Kleinschrift mit kleinem Anfangsbuchstaben gebildet, bei Konstanten jedoch in Großschrift.

Feldvariablen werden immer mit einem Datentyp deklariert :

    int x, y, z;
    String myString;
    boolean [] boolArray;
    double dblValues [];
    MyClass myObj;

Feldvariablen können mit einem Initializer versehen werden, der ihnen einen Anfangswert zuweist :

    int count = 0;
    String [] names = { "FirstName", "SecondName" };
    boolean permissions [] = { true, true, false, true };
    char separatorChar = ',';

Kombinierte Beispiele :

    public static final String DEFAULT_USER_NAME = "guest";
    private static final int SPECIAL_VALUE = -1;
    private int usageCount = 0;
    protected int listLength;

Methoden

Methodennamen werden in gemischter Groß-/Kleinschrift mit kleinem Anfangsbuchstaben gebildet.

Der Zugriff auf Methoden von anderen Klassen aus läßt sich einschränken :

public
bewirkt, daß die Methode von allen Klassen verwendbar ist.
protected
bewirkt, daß die Methode von dieser und allen Unterklassen verwendbar ist.
private
bewirkt, daß die Methode nur von dieser Klasse verwendbar ist.
Fehlen diese Angaben, so ist die Methode von dieser Klasse, allen Unterklassen und allen anderen Klassen dieses Paketes verwendbar (Friend Classes).

Normalerweise werden Methoden auf Objekte angewandt. Eine Methode kann auf dieses Objekt mit dem Schlüsselwort this zugreifen.

Die Angabe static bewirkt, daß die Methode nicht objektbezogen, sondern klassenbezogen ist. Das bedeutet, daß sie ohne ein Objekt aufgerufen wird. this kann in diesem Fall nicht verwendet werden. Demzufolge können sowohl andere Methoden als auch Feldvariablen der Klasse nur dann (direkt) verwendet werden, wenn sie auch als static deklariert sind.

Die Angabe final bewirkt, daß die Methode nicht überschrieben werden kann.

Methoden können einen Wert zurückliefern. Der Datentyp dieses Wertes wird bei der Definition der Methode angegeben. Wird kein Wert geliefert, so wird ersatzweise void angegeben.

Methoden können Parameter übergeben bekommen. Diese müssen ebenfalls mit Datentyp angegeben werden.

Beispiel :

    public class Counter
    {
      protected int count;
      protected int incr;
      public void initValue ()
      {
        this.count = 0;
        this.incr = 1;
      }
      public void setValue (int count, int incr)
      {
        this.count = count;
        this.incr = incr;
      }
      public int nextValue ()
      {
        this.count = this.count + this.incr;
        return (this.count);
      }
    }

Die Verbindung von Methodenname und Parameterliste (Anzahl und Datentypen) bezeichnet man als Signatur.

Es ist zulässig, beliebig viele Methoden desselben Namens mit unterschiedlicher Signatur zu definieren. Dies wird als Überladung (Overloading) bezeichnet. Bei ihrer Anwendung unterscheidet der Compiler sie durch die angegebenen Parameter.

Wird eine Methode einer Oberklasse in einer Unterklasse (mit der selben Signatur) neu definiert, so stellt dies eine Überschreibung (Overriding) dar. Hierbei muß der Datentyp des Rückgabewertes übereinstimmen. Falls die Methode der Oberklasse jedoch als private oder final deklariert ist, wird sie hierdurch nicht überschrieben, sondern einfach neu definiert. In diesem Fall darf sich auch der Datentyp unterscheiden.

Innerhalb einer Methode kann das Schlüsselwort super für die nächsthöhere Oberklasse bzw. das Objekt this als Instanz der Oberklasse verwendet werden. Dadurch können in überschreibenden Methoden die überschriebenen Methoden aufgerufen werden. Dies kann ein vollständiges Ersetzen einer Methode vermeiden.

Aus obiger Klasse Counter ließe sich z. B. folgende Klasse ableiten :

    public class ResettableCounter extends Counter
    {
      protected int start;
      public void saveValue ()
      {
        this.start = this.count;
      }
      public void initValue ()  // overridden
      {
        super.initValue ();
        saveValue ();
      }
      public void setValue (int count, int incr)  // overridden
      {
        super.setValue (count, incr);
        saveValue ();
      }
      public void resetValue ()
      {
        this.count = this.start;
      }
    }

In einer als abstract deklarierten Klasse kann eine Methode als abstract deklariert werden, wenn zwar ihre Signatur beschrieben wird, die Methode aber nicht definiert wird. Die Definition kann später in einer Unterklasse nachgeholt werden :

    public abstract class HtmlPrinter
    {
      public abstract void print (String str);
      public void printTag (String name)
      {
        print ("<");
        print (name);
        print (">");
      }
      public void printEndTag (String name)
      {
        print ("</");
        print (name);
        print (">");
      }
    }
    public class HtmlPrintStream extends HtmlPrinter
    {
      private PrintStream out;
      public void print (String str)
      {
        this.out.print (str);
      }
    }

Konstruktoren

Konstruktoren sind den Methoden ähnlich, haben jedoch keinen Rückgabewert und werden nicht vererbt.

Der Sinn eines Konstruktors liegt darin, die Feldvariablen eines Objektes zu initialisieren.

Konstruktoren werden ähnlich wie Methoden definiert. Als Name wird einfach der Klassenname genommen und der Return-Datentyp wird weggelassen.

Die Angaben public, protected und private haben die gleiche Bedeutung wie bei den Methoden.

Mit den Schlüsselwörtern this und super können die Konstruktoren der selben Klasse bzw. der Oberklasse aufgerufen werden.

Wird kein Konstruktor definiert, so wird ein Default-Konstruktor erzeugt, der keine Parameter hat, public ist und einen leeren Inhalt hat.

Konstruktoren für die obigen Klassen Counter, ResettableCounter und HtmlPrintStream könnten so aussehen :

      public Counter (int count, int incr)
      {
        setValue (count, incr);
      }
      public Counter (int count)
      {
        this (count, 1);
      }
      public Counter ()
      {
        this (0);
      }
      public ResettableCounter (int count, int incr)
      {
        super (count, incr);
      }
      public ResettableCounter (int count)
      {
        this (count, 1);
      }
      public ResettableCounter ()
      {
        this (0);
      }
      public HtmlPrintStream (PrintStream out)
      {
        this.out = out;
      }
      public HtmlPrintStream ()
      {
        this (System.out);
      }

Lokale Variablen

Lokale Variablen existieren nur vorübergehend, z. B. während der Ausführung einer Methode.

Sie werden innerhalb von Methoden ähnlich den Feldvariablen deklariert.

Die Angabe final und die Initialisierung ist entsprechend, die Angaben public, protected, private und static können hierbei jedoch nicht verwendet werden.

Variablennamen werden in Kleinschrift gebildet, bei Konstanten jedoch in Großschrift.

Beispiele :

    final int MINIMUM_VALUE = 1;
    int i, j;

Der Existenzbereich einer Variable ist der jewelige Block, in dem sie sich befindet. Ein Block wird durch die Zeichen { und } geklammert. Der äußerste Block ist die jeweilige Methode.

Die Parameter einer Methode sind innerhalb der Methode wie lokale Variablen verwendbar.

Operatoren

Es stehen viele der aus C bekannten Operatoren zur Verfügung :

Berechnungen + - * / % << >> >>> | & ^ ~;
String-Verkettung +
Vergleiche == != < <= > >=
Logische Operationen || && !
Zuweisungen = += -= *= /= %= <<= >>= >>>= |= &= ^=
Besonderes ++ -- ? :

Die String-Verkettung wird mit Hilfe der Klasse StringBuffer durchgeführt. Ist einer der beiden Operanden ein Objekt einer anderen Klasse, so erfolgt automatisch die Anwendung der Methode toString auf dieses Objekt :

    MyClass myObj;
    String str;
    str = "Value: " + myObj;
    // str = "Value: " + myObj.toString();

Es gibt noch einen Operator instanceof, mit dem sich feststellen läßt, ob ein Objekt einer Klasse angehört :

    MyClass ref;
    if (ref instanceof MySubClass)
    {
      MySubClass subref = (MySubClass) ref;
      ...
    }

Es lassen sich weder neue Operatoren definieren, noch die vorhandenen Operatoren überladen (Operator Overloading).

Ausdrücke, Statements

Ausdrücke werden mit Operatoren gebildet oder sind Methoden-Aufrufe. Ausdrücke lassen sich schachteln, ggf. mit runden Klammern ( ).

Methoden werden über ihren Namen und der Liste ihrer Parameter aufgerufen. Dazu ist die Angabe des Objektes nötig, auf das die Methode anzuwenden ist :

    MyClass obj;
    obj.doSomething ("now");

Statische Methoden werden über ihre Klasse aufgerufen :

    MyClass.doSomethingStatic ("without an object");

Der Zugriff auf die Feldvariablen oder Methoden der eigenen Klasse erfolgt durch this. Dies kann jedoch entfallen, wenn es keine Namenskonflikte gibt.

Ein Ausdruck gefolgt von einem Semikolon ; ist ein Statement. Dies bewirkt, daß sein Wert ignoriert wird. Dies ist für Zuweisungen sinnvoll, oder für Aufrufe von Methoden, die keinen Wert liefern.

Indexe für Arrays werden in eckigen Klammern [ ] angegeben. Sie sind ausschließlich numerisch und beginnen bei 0.

Erzeugung von Objekten

Ein Objekt wird mit dem Operator new erzeugt. Dazu wird der Klassenname und eine Liste von Parametern angegeben :
    MyClass obj;
    obj = new MyClass ("use this value", 0);

Bei der Erzeugung des Objektes wird derjenige Konstruktor der betreffenden Klasse, der die passenden Parameter hat, aufgerufen.

Die benötigte Klasse wird, sofern noch nicht geschehen, vom Class Loader geladen, normalerweise aus einem Class File oder einem Java Archive.

Auf ähnliche Weise lassen sich auch Arrays erzeugen :

    int [] [] a;
    a = new int [2] [];
    a [0] = new int [3];
    a [1] = new int [4];
    a [1] [2] = 1;
    int [] b = a [0];
    b [0] = 0;

Zerstörung von Objekten

Objekte werden nicht explizit zerstört. Stattdessen läuft in der JVM ein Garbage Collector, der regelmäßig nicht mehr referenzierte Objekte auffindet und zerstört.

Unmittelbar vor einer Zerstörung wird die Methode finalize auf das Objekt angewandt. Eine Klasse kann diese Methode überschreiben, um darin Aufräumarbeiten durchzuführen.

Kontrollstrukturen

Es stehen die üblichen Kontrollstrukturen zur Verfügung :
    if (a == 0)
      doZero ();
    else
      doNonZero (a);
    switch (value)
    {
      case 1 :
        doFirst ();
        break;
      case 2 :
        doSecond ();
        break;
      default :
        doOther ();
    }
    while (n > 0)
      doIteration (n);
    do
      doIteration (n);
    while (n > 0);
    for (int i = 0; i < n; i++)
      doIteration (i);
    {
      int i = 0;
      while (i < n)
      {
        doIteration (i);
        i++;
      }
    }

Eine Methode kann einen Wert mittels eines return-Statements zurückgeben :

    return n - 1;

Exceptions

Exceptions sind eine Möglichkeit, den Programmablauf zu unterbrechen und von vielen Stellen aus in eine ggf. weit höhere Ebene zurückzukehren. An geeigneter Stelle können sie abgefangen und behandelt werden.

Eine Exception ist ein Objekt einer Unterklasse von Exception. Diese enthält einen optionalen String, der als Fehlernachricht verwendet werden kann.

    public class MyException extends Exception
    {
      public MyException ()
      {
        super ();
      }
      public MyException (String s)
      {
        super (s);
      }
    }

Die Auslösung erfolgt durch das throw-Statement :

    throw new MyException ("Unexpected Error");

Wird eine Methode durch eine Exception direkt verlassen (ohne return), so wird auch kein Wert zurückgeliefert.

Abgefangen wird sie durch ein try-Statement :

    try
    {
      doSomethingQuestionable ();
    }
    catch (MyException ex)
    {
      System.out.println ("MyException caught: " + ex);
      throw ex;  // re-throw Exception
    }
    finally
    {
      cleanupStuff ();
    }

Es können mehrere catch-Klauseln angegeben werden.

Die optionale finally-Klausel wird in jedem Falle ausgeführt, ob eine Exception auftritt oder nicht, ebenso bei der Ausführung eines return-Statements. Dies kann für Aufräumarbeiten verwendet werden.

Die Exception kann nach dem Abfangen nochmals ausgelöst werden. Dadurch wird das nächsthöhere try-Statement erreicht.

Die Exception-Klasse kann beliebig viele zusätzliche Informationen enthalten.

Jede Methode, die direkt oder indirekt eine Exception auslösen kann, muß dies deklarieren. Hierzu dient die throws-Klausel :

    public void doSomethingQuestionable () throws MyException
    {
      if (currentState == STATE_UNKNOWN)
        throw new MyException ("State unknown");
      doSomethingReasonable ();
    }

Exceptions der Unterklassen von RuntimeException brauchen nicht als solche deklariert zu werden. Darunter fallen z. B. :

NullPointerException
Dereferenzierung einer null-Referenz
ArrayIndexOutOfBoundsException
Anwendung eines ungültigen Array-Indexes
NumberFormatException
Ungültige Stringdarstellung einer Zahl (bei Umwandlung)
ArithmeticException
Ungültige mathematische Operation, z. B. Division durch0
ClassNotFoundException
Fehlen einer benötigten Klasse
ClassCastException
Verwendung eines Objektes einer falschen Klasse
AccessControlException
Versuch einer Operation, die die Umgebung der JVM nicht erlaubt.

Interfaces

Ein Interface beschreibt, welche Feldvariablen und Methoden es gibt, ohne sie zu definieren. Es ist vergleichbar mit einer abstrakten Klasse mit ausschließlich abstrakten Methoden.

Interfaces werden ähnlich wie Klassen, jedoch mit dem Schlüsselwort interface definiert :

    public interface MyInterface
    {
      public int someMethod (int value);
    }

Interfaces können von beliebig vielen Interfaces abgeleitet werden.

Jede Klasse kann beliebig viele Interfaces implementieren. Dies wird erreicht, indem dies durch die implements-Klausel deklariert wird und alle darin beschriebenen Feldvariablen und Methoden definiert werden :

    public class MyImplementation implements MyInterface
    {
      public int someMethod (int value)  // Part of MyInterface
      {
        return (value * 2);
      }
      ...
    }

Eine Referenz auf ein Objekt einer (beliebigen) Klasse, die ein Interface implementiert, wird ähnlich einer Referenz auf ein Objekt einer Klasse definiert. Mit dieser Referenz können alle Feldvariablen und Methoden des Interfaces benutzt werden :

    MyInterface myint;
    myint = new MyImplementation ();
    int val = myint.someMethod (3);

Threads

Die JVM ist in der Lage, gleichzeitig mehrere Threads (Programmläufe) auszuführen.

Dies kann benutzt werden, wenn ein Programm mehrere Aufgaben erledigen soll, die weitgehend unabhängig laufen können. Dies ist auch sinnvoll, wenn ein Programm auf mehrere Ereignisse warten soll, die unabhängig voneinander verschieden bearbeitet werden müssen.

Ein Thread ist eine Klasse, die das Runnable-Interface implementiert. Dieses enthält eine Methode run :

    public class MyThread implements Runnable
    {
      private Object someData;
      private Object retrieveData ()
      {
        Object data;
        data = receiveDataFromNetwork ();  // This may take long to execute
        return data;
      }
      public void run ()  // Part of Runnable
      {
        someData = retrieveData ();
        // returning here causes the thread to stop
      }
      public Object getData ()
      {
        return someData;
      }
      public MyThread ()
      {
        someData = null;
      }
    }

Ein Thread wird gestartet, indem ein Objekt der Klasse Thread erzeugt und gestartet wird :

    MyThread myrun;
    Thread runthread;
    myrun = new MyThread ();
    runthread = new Thread (myrun);
    runthread.start ();

Hiernach kann der Thread über das Thread-Objekt kontrolliert werden :

Der Thread wird beendet, wenn dessen run-Methode zurückkehrt. Dies kann auch durch das Auftreten einer Exception geschehen.

Threads werden häufig in Programmen mit graphischer Oberfläche und in Applets benutzt, da diese eventorientiert sind.

Um Probleme beim gleichzeitigen Zugriff verschiedener Threads auf gemeinsame Daten zu vermeiden, gibt es das Schlüsselwort synchronized.

Es kann auf einen Block angewandt werden :

    private int count;
    private int nextValue ()
    {
      return (++count);
    }
    public void applyNextValue ();
    {
      int value;
      synchronized (this)
      {
        value = nextValue ();
      }
      applyValue (value);
    }
Die Angabe synchronized (this) sorgt dafür, daß nie mehrere Threads gleichzeitig den darin befindlichen Programmcode für das selbe Objekt durchlaufen.

Dies läßt sich auch durch die Methoden-Deklaration erreichen :

    private int count;
    private synchronized int nextValue ()
    {
      return (++count);
    }
    public void applyNextValue ();
    {
      applyValue (nextValue ());
    }

Der bereits erwähnte Garbage Collector ist als Thread implementiert.

Hilfreiche Klassen

Es lohnt sich, in der Dokumentation der Java Fundamental Classes zu stöbern. Dort finden sich viele nützliche Pakete und Klassen :

Paket Inhalt
java.lang grundlegende Klassen der Sprache Java
java.io Klassen für die Ein- und Ausgabe
java.util Klassen für verschiedene Zwecke
java.net Klassen für Netzwerk-Zugriffe
java.sql Klassen für Datenbank-Zugriffe
java.applet Klassen für Applets
javax.swing Klassen für die graphische Oberfläche

Ein Programm

Ein Programm muß eine Methode main haben :
    package elug;
    public class HelloWorld
    {
      public static void main (String [] args)
      {
        System.out.println ("Hello, World !");
      }
    }

Die Methode wird beim Aufruf der JVM von ihr aufgerufen.

Umgang mit Java-Programmen

Vorbereitung

Zur Verwendung der Java-Hilfsprogramme müssen folgende Environment Variablen gesetzt sein :
PATH
sollte das Directory der Java-Hilfsprogramme enthalten. Dadurch kann man verschiedene JDK-Versionen verwenden.
CLASSPATH
muß eine Liste der JAR-Dateien und Directories der verwendeten Java-Klassen enthalten.
Beispiel :
    JAVA_HOME=/usr/lib/jdk1.3
    MYJCLASSES=$HOME/java/classes
    PATH=$JAVA_HOME/bin:$PATH
    CLASSPATH=${MYJCLASSES}:$JAVA_HOME/lib/tools.jar
    export JAVA_HOME PATH CLASSPATH

Compiler

Um Java-Klassen kompilieren zu können, muß als Dateiname der Quelldatei der Klassenname mit der Endung .java verwendet werden. Der Quellcode der Klasse elug.HelloWorld muß also in der Datei HelloWorld.java stehen.

Der Java-Compiler wird dann folgendermaßen aufgerufen :

    javac -d $MYJCLASSES HelloWorld.java
Das erzeugte Class File heißt dann HelloWorld.class und steht im Directory $MYJCLASSES/elug.

Programmaufruf

Zum Programmaufruf wird der vollständige Klassenname, also mit Paketnamen, und gegebenenfalls Parameter angegeben :
    java elug.HelloWorld "Dies ist ein Parameter"

Dokumentation

Es kann automatisch eine Klassendokumentation erstellt werden, wenn geeignete Informationen im Quelltext untergebracht werden :
    package elug;
    /**
     * Hello-World-Klasse
     */
    public class HelloWorld
    {
      /**
       * Konstante zum Test
       */
      private static final String WORLD_TYPE = "strange";
      /**
       * Zusammenbau der Grußformel
       * @param  wtype  Art der Welt
       * @return        Vollständige Grußformel
       */
      private static String greeting (String wtype)
      {
        return ("Hello, " + wtype + " World !");
      }
      /**
       * Haupt-Methode, wird von der JVM aufgerufen
       * @param  args  Array der Parameter-Strings vom Programmaufruf
       */
      public static void main (String [] args)
      {
        System.out.println (greeting (WORLD_TYPE));
      }
    }
Das Programm zur Erzeugung der Dokumentation wird folgendermaßen aufgerufen :
    TITLE="My Class"
    OPTS="-private -d $HOME/java/doc"
    javadoc $OPTS -windowtitle "$TITLE" HelloWorld.java
Dies legt im Directory $HOME/java/doc eine Reihe Dateien an. Die eigentliche Dokumentation der Klasse ist in $HOME/java/doc/elug/HelloWorld.html zu finden.

Um eine Übersicht über alle eigenen Klassen zu bekommen, erstellt man zweckmäßigerweise eine Datei elugpackages, die alle Paketnamen auflistet :

    elug
    elug.lib
    elug.test
Dann gibt man diese Datei und das Directory der Quelltexte an :
    TITLE="My Classes"
    OPTS="-private -d $HOME/java/doc -sourcepath $HOME/java/sources"
    javadoc $OPTS -windowtitle "$TITLE" @elugpackages
Unter $HOME/java/doc/overview-tree.html ist dann die Baumstruktur aller Klassen zu finden mit Verweisen auf deren Dokumentation.


Thomas Conze <thomas.conze@gmx.net>