Crash-Kurs Java
- Vorwort
- Programmiersprache Java
- Vergleich mit ähnlichen Bibliotheken und Protokollen
- Unterschiede zu C++
- Sprachsyntax
- Programmaufbau
- Lexikalische Elemente
- Datentypen
- Variablen
- Klassen und Interfaces
- Anweisungen
- Auswahlanweisungen
- Iterative Anweisungen
- Sprunganweisungen
- Kontrollanweisungen
- Multitasking in Java
- Wichtige Packages
- Beispielprogramme
- Ein einfaches Java-Programm
- Ein einfaches Java-Applet
- Einbinden von "Native-Methods"
- WWW-Scriptsprachen
Vorwort
C++ bietet Vorteile, bei zeitkritischem Code. Wie dem auch sei, Java bietet natürlich auch Vorteile, die manch einen zum Umstieg verleitet haben, nicht zuletzt wegen der unkomplizierteren Codierung und der höheren Wahrscheinlichkeit zur fehlerfreien Ausführung (dass wird verusacht, weil man in C++ instabilen Code erzeugen kann, wenn man etwas "nachlässig" codiert - besonders mit Zeigern). Also für alle denen Java Vorteile bietet, hier eine kurze (und hoffentlich brauchbare) Einführung
Programmiersprache Java
Die Programmiersprache Java ist eine objektorientierte, relativ einfache, plattformunabhängige, interpretative Programmiersprache. Java hat mit Absicht eine C++-ähnliche Syntax gewählt, um den Programmierern den Umstieg auf Java zu erleichtern. Es hat einen eingebaute Garbage Collection Mechanism, um nicht referenzierte Speicherblöcke wieder freizugeben. Java-Programme sind binärkompatibel zwischen den verschiedenen Rechnerplattformen. Sie werden zu Bytecodes kompiliert und so ist die Implementierung der Programme nicht so einfach nachzuvollziehen (Begriff: reverse engineering). Deshalb sind die Urheberrechte bei Java-Programmen besser gesichert, als z.B. bei Smalltalk-Programmen. Außerdem kann man die Bytecodes schneller interpretieren, als reinen ASCII-Text. Die Bytecodes sind eigentlich ebenfalls Maschinenbefehle, die wie normale Maschinenbefehle aussehen (Befehlcode-Modifier-Datenbytes ...), aber sie sind Befehlscodes eines virtuellen Prozessors, den es so nirgendwo gibt. Diese virtuelle Maschine muß auf allen Plattformen emuliert werden. Somit werden alle maschinenspezifische Eigenschaften vor den Java-Programmen verborgen und man gewinnt zusätzlich mehr Sicherheit. Die Java-Programme dürfen keine Systemaufrufe durchführen oder direkt auf irgendwelche Ports zugreifen. Wegen fehlender Zeigerarithmetik können die Java-Programme auch nicht das Java-Laufzeitsystem irgendwie destabilisieren. Somit haben die vierenverseuchten oder schlecht programmierten Java-Programme wenig Chancen, dem Zielsystem Schaden zuzufügen. Die Interpretation der Bytecodes ist fast so schnell, wie die von C oder C++ Code. Für besonders zeitkritische Aufgaben kann man auch den Bytecode vor der Ausführung in direkten Maschinencode des Zielsystems konvertieren.
Kompilierte Java-Programme enthalten keine Adressen der Methodenaufrufe, sondern symbolische Links, die zur Laufzeit in Adressen umgerechnet werden (late binding). Dadurch kann man eine Oberklasse modifizieren, ohne alle davon abgeleiteten Klassen neu übersetzen zu müssen. Nur die Parameter und Namen der Methoden müssen gleich bleiben. Man kann aber auch neue Methoden hinzufügen. Das Linken geht sehr schnell und kann deshalb auf dem Zielsystem zur Laufzeit erfolgen.
Der Java-Klassen-Loader durchsucht immer zuerst das lokale System, um Namensreferenzen aufzulösen. Dadurch kann keine externe Klasse mit demselben Namen ein Sicherheitsrisiko für das Zielsystem werden. Doch auch dann traut der Java-Interpreter keinem Code. Jeglicher Code (auch lokale Klassen) wird vor der Ausführung verifiziert, um unbefugte Modifikationen des Speichers (überschreiten der Arraygrenzen usw.), Verletzungen der Zugriffsrechte (private, protected, public), Stackoverflows usw. zu entdecken.
Java enthälten Sprachelemente für die nebenläufige Ausführung von Programmteilen (Klassen: Thread, Runnable, etc.) sowie Kontrollmaßnahmen, um die nebenläufige Ausführung der Programmteile zu koordinieren (synchronised).
Vergleich mit ähnlichen Bibliotheken und Protokollen
Einige Bibliotheken und Protokolle (CORBA, OLE, OpenDoc) realisieren zum Teil ähnliche Aufgaben wie Java. Sie lassen sich nicht direkt mit Java vergleichen, denn Java ist eine Programmiersprache, CORBA eine Spezifikation und OpenDoc sowie OLE Bibliotheken. OpenDoc und OLE sind entwickelt worden, um Verbunddokumente zu erstellen. Dabei sollte jedes "Objekt" im Dokument "wissen", mit welcher Anwendung sie erstellt worden sind. Solche "intelligenten Objekte" können in einem Dokument eingebettet oder gelinkt werden. Wenn der Anwender auf sie mit dem Maus klickt, so wird entweder die entsprechende Anwendung aufgerufen und die Daten werden in der Anwendung geladen oder der Anwendung arbeitet im Hintergrund und man kann die Daten direkt in dem Verbunddokument editieren.
In der Programmiersprache Java kann man Applets erstellen, die man in WWW-Seiten einbinden kann. Diese Applets implementieren interaktive Komponenten der WWW-Seite. Sie können auf Benutzereingaben reagieren und beliebige Aktionen durchführen. Da sie ihre Daten selbst anzeigen, können somit (genau wie mit OLE und OpenDoc) beliebige Daten gemischt werde. Darüber hinaus muß bei Java das genaue Datenformat dem Client nicht bekannt sein. Falls er dieses Datenformat noch nicht kennt, so lädt er vom Server das entsprechende Programm dazu. Eines der Ziele von Java war auch, mit Hilfe der Applets den Netzwerkehr zu verringern. Man kann statt kompletter Rasterbilder (Geschäftsgraphiken, Börsenkurse usw.) nur die nummerische Daten und falls notwendig ein Java-Programm übertragen, das das Bild auf der Clientseite erstellt. Java-Applets sind in einer WWW-Seite nicht miteinander verbunden, sondern akzeptieren nur die Eingaben und Verantwortung über ihren eigenen Bereich auf der WWW-Seite. Um Applets miteinader zu verbinden, kann man die Programmiersprache JavaScript benutzen. OLE und OpenDoc sind inzwischen ebenfalls netzwerkfähig. Java ist es schon von Grund auf. Außerdem unterstützt Java Threads und Hintergrundbearbeitung. Fazit: Mit Java lassen sich viele ähnliche Aufgaben wie mit OLE oder OpenDoc realisieren, aber es ist nicht mit diesem Ansatz entwickelt worden.
Unterschiede zu C++
Java wurde mit Absicht C++-ähnlich gestaltet, um den Programmierern den Umstieg auf die neue Sprache zu erleichtern. Es gibt aber mehrere wichtige Unterschiede:
- Java ist eine interpretative Sprache, C und C++ Compiler generieren den direkten Maschinencode des Zielsystems. Dadurch sind Java-Programme etwas langsamer als C++ - Programme. Andererseits sind sie besser portierbar. Für Userinterfaces braucht man nicht unbedingt den schnellsten Code und zeitkritische Programmabschnitte kann man vor der Ausführung in direkten Maschinencode konvertieren. Die Bytecodes von Java werden etwas schneller interpretiert, als reine ASCII-Text (Smalltalk, Perl etc.). Es ist ein Kompromiß zwischen Ausführungsgeschwindigkeit und Flexibilität.
- C++ ist im wesentlichen ein C mit objektorientierten Erweiterungen - eine gemischte Sprache, die nicht unbedingt einen sauberen Programmierstil erzwingt. Java dagegen ist rein objetorientiert. Allerdings existieren in Java auch primitive numerische Typen (int, long, float, char). Alles andere ist aber ein Objekt.
- Zeichen in Java (char) sind nicht 8-Bit ASCII Zeichencodes, sondern 16-Bit Unicodezeichen. Java-Zeichen können in Arrays zusammengefaßt werden, aber sie sind deshalb noch nicht Strings. Für Strings benutzt man die Java-Klassen String (Zeichenketten konstanter Länge und Inhalt) und StringBuffer (Zeichenketten variabler Länge). Java-Strings werden auch nicht durch ein NULL Zeichen terminiert.
- Java hat keine Headerdateien und benötigt keinen Precompiler.
- Java kennt keine impliziten Typumwandlungen, außer der Umwandlung von numerischen Datentypen in den selben Datentyp mit größerer Genauigkeit (z.B int nach long). Würde man jedoch bei der Konvertierung an Genauigkeit verlieren, so muß man eine explizite Typumwandlung vornehmen.
- Java kennt keine Zeigerarithmetik. Es gibt nur Objekte und Arrays von Objekten. Alles, außer primitiven numerischen Typen, sind Objekte und die Variablen beinhalten Referenzen auf Objekte, nicht die Objekte selbst. Vor einem Zugriff auf ein neues Objekt muß man dieses also vorher immer mit dem new Operator anlegen. Arraygrenzen werden zur Laufzeit überprüft. Ein Array kann nach dem Anlegen nicht mehr vergrößert oder verkleinert werden.
- Alle Objekte und Arrays muß man explizit mit dem new Operator allokieren. Es gibt aber keinen delete Operator. Speicher wird durch den Garbage-Collector zurückgefordert.
- Alle primitiven Datentypen in Java haben einen festen Speicherbedarf und eine Genauigkeit, die nicht von der Zielplattform abhängig ist.
- Es gibt keine Structs, Unions, Typedefs oder Enums, sondern nur Klassen.
- Es gibt keine vorzeichenlosen Datentypen.
- Wahrheitswerte lassen sich nicht in numerische (und umgekehrt) umwandeln.
- Man kann die mathematischen Operatoren nicht überladen.
- Java enthält direkten Suppert für Threads, kritische Programmmabschnitte und Synchronisierung von Threads.
- Java enthält keine goto-Befehle, kennt aber Labels. Mit einem break, continue oder throw Befehl wird aus einer verschachtelten Schleife herausgesprungen.
- Java implementiert eine eigene Form der Mehrfachvererbung.
- Java benutzt "late binding". Statt Methodenadressen werden Symbole benutzt, die erst während der Laufzeit durch direkte Adressen ersetzt werden.
- Es gibt keine Funktionen, Prozeduren und globale Variablen.
Sprachsyntax
In diesem Abschnitt möchte ich einen kurzen Überblick über die Syntax von Java-Programmen geben. Als Grundlage für die hier beschriebene Syntax gilt die Java-Sprachspezifikation von 30. Oktober 1995 und das JDK Version 1.0 (beta2). Die Beispielprogramme sind JDK, Java-Tutorial und verschiedenen Zeitschriften entnommen und unter Windows NT Version 3.51 getestet. Der Vortrag ist mehr auf C++ Programmierer ausgelegt. Ich beschreibe hier die Sprachsyntax möglichst vollständig, aber konzentriere mich mehr auf die Unterschiede zu C++, denn Java ist auch am Vorbild von C++ entwickelt worden.
Programmaufbau
Java-Quelldateien haben den Suffix .java und sind im Unicode Zeichensatz geschrieben. Die kompilierte Bytecodedateien haben den Suffix .class.
Eine Quelldatei kann aus folgenden Elementen bestehen:
- Package-Spezifikation: package Name;
Legt fest, zu welchem Package diese Klasse gehört. Fehlt die package Anweisung, gehört die Klasse zu einem namenslosen Package. So kann man schnell Klassen implementieren, Testen und später unter einem global eindeutigen Packagenamen freigeben. Die global eindeutigen Packagenamen sollten wie Internetadressen aussehen z.B.
DE.TU-ILMENAU.PRAKINF.TESTAPP
. Packages sind wie Bibliotheken in C++. Pro Quelldatei darf nur eine öffentliche Klasse oder Interface deklariert sein, aber mehrere Quelldateien können zu einer Package gehören.
- Import Befehl: import Name;
Importiert ein anderes Packages, das diese Klasse verwendet bzw. von dem es abgeleitet ist.
- Typdeklarationen und/oder Interfacedeklarationen: Jede Quelldatei kann nur eine öffentliche Klasse oder Interface deklarieren (und implementieren).
Lexikalische Elemente
Java-Quelldateien können auch in ASCII-Zeichensatz geschreiben werden, wenn man für jedes Unicode-Zeichen ein Unicodeescape im ASCII-Zeichensatz in der Form \uxxxx eingibt, wobei xxxx der Hexcode des Unicodezeichens ist.
Die Zeilen der Quelldateien werden durch die ASCII-Zeichenkombination< CR><LF> terminiert. Der Java-Compiler überliest beim Kompilieren alle Leerzeichen, horizontale und vertikale Tabulatoren sowie Zeilenenden und Kommentare. Als Kommentare gelten:
- /* normaler Kommentar, kann sich auch über mehrere Zeilen erstrecken */
- // Kommentar bis zum Zeilenende
- /** Kommentar für die automatische Generierung von Dokumentationen */
Reservierte Wörter sind:
abstract do implements package
throw boolean double import private
throws break else inner protected
transient byte extends instanceof public
try case final int rest
var cast finally interface return
void catch float long short
volatile char for native static
while class future new super
byvalue const generic null switch continue
goto operator synchronized default if
outer this
Die Wörter
byvalue, cast, const, future, generic, goto, inner, operator, outer, rest
und
var
sind zwar reserviert, werden jedoch nicht benutzt.Die Namen von Variablen, Klassen und Methoden sind beliebig lange Unicode-Zeichenketten, beginnend mit einem Zeichen und gefolgt von Zeichen und Nummern.Literale sind Werte von primitiven Datentypen und die Klasse String.
- Ganzahlige Werte: kann man zur Basis 10, 16 (0xnnnn) oder 8 (0nnnn) eingeben. Ganze Zahlen haben einen Wertebereich von -2^31 bis 2^31-1. Lange ganze Zahlen (mit dem Suffix l bzw. L) haben einen Wertebereich von -2^63 bis 2^63-1.
- Gebrochene Zahlen: entweder mit einfacher Genauigkeit (optionales Suffixs f, von 1.40239846e-45f bis 3.40282347e+38f) oder doppelter Genauigkeit (Suffix d, von 4.94065645841246544e-324 bis 1.79769131486231570e+308)
- Wahrheitswerte: true und false.
- Zeichen: sind 16-Bit Unicodezeichen oder Escapes (\b, \t, \n, \r, \", \', \\ und \unnn) und werden in einfache Hochkommata eingeschlossen.
- Zeichenketten: werden in doppelte Hochkommata eingeschlossen und dienen als Werte für Instanzen der Klasse String. Arrays-Zeichen sind keine Zeichenketten. Zeichenketten kann man mit dem + Operator verbinden.
Opratoren sind:
= > < ! ~ ? : == <= >=
!= && || ++ -- + - * /
& | ^ % << >> >>> += -=
*= /= &= |= ^= %= <<= >>= >>>=
Datentypen
Datentypen teilen sich in primitive Datentypen, Klassen, Interfaces und Arrays. Referencen sind "Zeiger" auf dynamisch allokierte Objekte (Instanzen und Arrays). Primitive Typen sind:
- byte: 8-bit Ganzahl. (von -256 bis 255)
- short: 16-bit Ganzahl (von -32768 bis 32767)
- int: 32-bit Ganzahl (von -2147483648 bis 2147483647)
- long: 64-bit Ganzahl (von -9223372036854775808 bis 9223372036854775807)
- float: 32-bit IEE 754 gebrochene Zahl (von 1.40239846e-45f bis 3.40282347e+38f)
- double: 64-bit IEE 754 gebrochene Zahl (von 4.94065645841246544e-324 bis 1.79769131486231570e+308).
- char: 16-bit Unicode Zeichen
- boolean: Wahrheitswert (true oder false)
Als Parameterwerte für Methoden kann man primitive Werte und Referenzen übergeben. Zahlen kann man in andere numerische Typen und Zeichen umwandeln und umgekehrt, aber nicht in Warheitswerte. Typumwandlungen, bei denen die Genauigkeit wächst (byte -> int ->long ->float) können implizit durchgeführt werden. In die andere Richtung müssen sie jedoch explizit angegeben werden. Alle primitiven Typen haben einen Standardanfangswert - 0 bzw. null bzw. '\u0000'. Zahlen kann man nicht in Adressen oder Referenzen umwandeln. Als Typumwandlungsoperator dient der (<Typname>) Operator.
Bei ganzahliger Division durch 0 wird eine ArithmeticException ausgelöst. Bei gebrochenen Zahlen entsteht bei der Division durch 0 Unendlich und es wird keine Exception ausgelöst. Negative und Positive 0 sind gleich (0.0 == -0.0).Instanzen und Arrays müssen immer durch den new Operator dynamisch allokiert werden. Variable, die Instanzen enthalten, sind immer Refrenzen. Arrayelemente dürfen Arrays, Interfaces, Instanzen oder primitive Typen sein. Als Indizes benutzt man ganze Zahlen.
Variablen
Variablen besitzen einen Typ (primitiv oder Referenz) und eine Speicheroption. Lokale Variablen werden auf dem Stack allokiert und beim Verlassen des Blocks freigegeben. Statische Variablen werden allokiert, wenn eine Instanz aus ihrer umschließenden Klasse angelegt wird. Mit dem Freigeben der letzten Instanz dieser Klasse wird auch die statische Variable freigegeben. Dynamische Variablen werden mit dem
new
Operator allokiert. Wird die letzten Referenz auf dieses Objekt freigegeben, werden sie automatisch freigegeben. Die Sichtbarkeit der Variablen ist durch die folgende Hierarchie festgelegt:
0. Host's Packages
1. Quelldatei
2. Datentyp (Klasse oder Interface Deklaration)
3. Parameter der Methode
4. lokaler Block
5. for Befehl
Beim Auflösen einer Namensreferenz wird in dieser Hiearchie von unten nach oben gesucht. Der Zugriff auf Instanzvariablen wird folgendermaßen bestimmt:
- public: öffentlich
- protected: für die Methoden dieser Klasse und davon abgeleitete Klassen
- private: nur für die Methoden dieser Klasse
- ist nichts angegeben, so kann man auf diese Instanzvariable von allen Klassen in dieser Quelldatei (Package) zugreifen, nicht aber aus anderen Packages.
Variablen, die als
final
deklariert wurden, sind Konstanten.Ein
import
Befehl z.B.
import java.util.Vector;
macht eine Klasse oder ein Interface in diesem Package nach seinem letzten Komponenten bekannt. Danach kann man Felder dieser Klasse oder Interfaces entweder als
Vector.foo()
oder
util.Vector.foo()
oder
java.util.Vector.foo()
ansprechen. Wenn man eine andere Klasse nicht importiert hat, so muß man sie mit ihrem vollen Namen anprechen, z.B.
java.applet.Applet
. Man kann sowohl eine einzelne Klasse als auch alle Klassen aus einem Package importieren. Zum Beispiel:
import java.applet.Applet; // Importiert die Klasse Applet import java.applet.*;
// Importiert alle Klassen und Interfaces
// aus dem Package java.applet
Ohne einen Importbefehl kann man die Klassen nicht benutzen. Der Compiler gibt eine Fehlermeldung aus. Allerdings wird die Klasse
java.lang
immer implizit importiert.
Klassen und Interfaces
Eine Klassendefinition führt einen neuen Datentyp ein. Eine Klassendefinition hat die Form:
[<Doc-Komment>] [<Klassentyp>] class Klassenname [extends <Oberklasse>]
[implements <Interfaces>] { <Felder>};
Zum Beispiel:
/** Person */public class Person { public String m_strVorname;
public String m_strNachname; }; /** Programmierer */ class Programmierer
extends Person implements Arbeitnehmer { public float m_fGehalt;
public float HatGehalt() { return m_fGehalt; }};
Klassentyp kann sein:
- abstract: Eine Abstrakte Klasse kann abstrakte Methoden enthalten. Abstrakte Methoden werden in dieser oder einer abstrakten Oberklasse nur deklariert, aber nicht definiert. Eine Klasse, die abstrakte Methoden enthält, muß auch als abstrakt deklariert werden. Von einer abstrakten Klasse kann man keine Instanzen anlegen.
- final: Von dieser Klasse kann man keine Unterklassen ableiten. Eine Klasse darf nicht gleichzeitig als final und abstrakt deklariert sein.
- public: Öffentliche Klasse. Pro Quelldatei darf nur eine öffentliche Klasse oder ein Interface deklariert werden.
Eine Oberklasse kann eine beliebige andere Klasse sein. Wenn man keine Oberklasse angibt, so wird die Klasse von der Klasse
Object
abgeleitet. Es darf keine kreisförmige Ableitungslinien geben, d.h. wenn die Klasse A von B und B von C abgeleitet ist, so darf C nicht von A abgeleitet werden.
Man kann Referenzen zwischen einer Ober- und Unterklasse umdefinieren. Wenn die Klasse A eine Unterklasse von B ist, so kann man einen Referenz vom Typ A ohne explizite Redefinition als eine Referenz von Typ B zuweisen. In die andere Richtung muß man eine explizite Typumwandlung stattfinden. Zum Beispiel:
A a; B b; a = b; b = (A)a;
Felder können Variablen- und Methodendeklarationen sein. Methodendefinitionen haben die Form:
[<Doc-Komment>] [<Methodentyp>] [<Rückgabetyp>] <Methodenname>
[throws <Exceptions>] ([<Parameterliste>]) { <Methodenkörper> }
Konstruktoren haben denselben Namen wie die Klasse, in der sie deklariert sind. Konstruktoren haben keinen Rückgabewert. Falls kein Konstruktor deklariert wird, so erhält die Klasse implizit einen Konstruktor (public und ohne Parameter). Instanzen werden mit dem Operator
new
angelegt. Java-Klassen haben keinen Destruktor, können aber eine
finalize()
Methode haben. Der Speicher von Instanzen wird vom Garbage-Collector freigegeben, wenn die Instanzen nicht mehr referenziert werden. Die Methode finalize() wird vom Garbage-Collector nur einmal aufgerufen. In dieser Methode können Betriebssystemressourcen (Graphics Contexc, Brushes, Pens, Dateideskriptoren usw.) freigegeben oder die Freigabe der Instanz verzögert werden.Die Freigabe der Instanz wird verzögert, indem eine Referenz auf diesen Instanz in einer Variable abgelegt wird.
Alle Exceptions, die in dieser Methode auftreten können und nicht vom Typ
Error
oder
RunTimeException
sind, müssen deklariert werden. Alle Exceptions müssen von
Throwable
abgeleitet sein.
Eine Methode darf keine lokale Variable definieren, die den gleichen Namen verwendet, wie einer ihrer Parameter. Überladene Methoden müssen unterschiedliche Paramaterlisten haben. Ein unterschiedlicher Rückgabetyp ist nicht aureichend. Mathematische Operatoren dürfen nicht überladen werden.
Die Zugriffsrechte auf Methoden werden durch die Schlüsselwörter
public, private
und
protected
festgelegt. Wird nichts angegeben, so sind die Methoden innerhalb des Package öffentlich. Als
final
deklarierte Methoden dürfen nicht überschrieben werden. Solche Methoden erlauben dem Kompiler, Optimierungen vorzunehmen (z.B. Ersetzen der Methodenaufrufe mit seinem Körper). Abstrakte Methoden haben keinen Methodenkörper und müssen in einer abgeleiteten Klasse implementiert sein. Als
native
deklarierte Methoden haben keine Methodenkörper (in Java) und müssen in eine anderen Programmiersprache (z.B. C) implementiert werden. Statische Methoden sind Klassenmethoden, d.h. sie können nur auf Klassen (statische) Variablen zugreifen und können vor dem Instanziieren aufgerufen werden. Das Schlüsselwort
synchronised
deklariert eine Methode oder umschließt einen Block und wird durch einen Monitor vor der gleichzeitigen Ausführung geschützt.
Instanzvariablen haben Zugriffsrechte (private, protected, public, "friendly"), einen Datentyp, Initialisatoren. Falls die Instanzvariablen nicht explizit initialisiert werden, setzt der Java-Interpreter sie je nach Typ auf 0, false, '\0' oder "". Java-Klassen können auch Initialisierungsblöcke enthalten. Das sind statische Codeblöcke, die zur Initialisierungszeit ausgeführt werden. Zum Beispiel:
class Test extends A { int x = 2;
float f = 0.34; String str = "das ist ein " + "Test";
int y; {
//statischer Initialisierungsblock x = 4;
//Überschreibt die Anfangswerte. f = 3.67;
y = "das ist ein".length(); }
public Test(int x, float g, String s) {
super(s); //Aufruf des Konstruktors der Basisklasse
this.x = x;
//hier muß man mit this.x die Instanzvariable
//adressieren, weil der Parameter auch x heißt.
f = g; //zum dritten Mal initialisieren
y = 23; } };
In diesem Beispiel werden die Instanzvariablen insgesamt dreimal initialisiert. Zum ersten werden sie durch eine direkte Zuweisung initialisiert. Die Instanzvariable y hat keinen direkte Initialisator und wird deshalb mit 0 belegt. Anschließend wird der statische Initialisierungsblock ausgeführt. Es kann beliebig viele solche Initialisierungsblöcke geben und sie werden alle in der Reihenfolge ausgeführt, wie sie in den Klassendeklarationen vorkommen. Schließlich werden die Instanzvariablen auch in dem Konstruktor initialisiert. Hier ist auch zu sehen, wie man den Konstruktor der Basisklasse aufruft -
super(<Argumente>);
. Der Konstruktor der Basisklasse kann auch implizit aufgerufen werden, falls die Basisklasse einen parameterlose Konstruktor besitzt. In diesem Beispiel ist weiterhin zu sehen, wie Strings behandelt werden. Der Kompiler ersetzt zur Kompilezeit jede Zeichenkette durch eine Instanz der Klasse String. Da die Klasse String einen + Operator hat, kann man die Zeichenketten mit + verbinden. Außerden kann man für Zeichenketten gleich eine Methode der Klasse String aufrufen. Man könnte die String - Variablen auch folgendermaßen intialisieren:
String str = new String("das ist ein Test");
Das ist aber nich so effizient, denn in diesem Fall erzeugt der Kompiler aus der Zeichenkette einen String und übergibt diesen einem weiteren Konstruktor der Klasse String.
Parameternamen, die den gleichen Namen als ein Instanzvariable haben, verdecken diesen. Diese Instanzvariablen kann man dann mit this (oder super für Basiklasse) adressieren. Instanzvariablen und Methoden werden mit <Instanzname>.<Feldname> adressiert. Statische Varibalen und Methoden werden mit:< Klassenname>.<Feldname> adressiert.
Variablen können auch folgendermaßen deklariert sein:
- volatile: Variable wird möglicherweise in einen Thread geändert, während ein anderer sie ausliest.
- final: statische Variable, muß einen Initialisator haben.
- transient: Diese Eigenschaft ist noch nicht vollständig implementiert. Falls es in der Zukunft OODBMS auf der Basis von Java gibt, so werden mit transient Variablen markiert, die nicht zum persistenten Teil der Objekte gehören, sondern zur Laufzeit ständig unterschiedliche werte besitzen und daher nicht in der DB gespeichert werden. Transiente Variablen dürfen nicht statisch oder final sein.
Die Programmiersprache Java unterstützt nur einfache Ableitungen. Java löst einige Probleme, die andere Programmiersprachen (z.B. C++) durch mehrfache Ableitung umgehen, mit Hilfe der Interfaces. Wenn eine nicht abstrakte Klasse ein Interface implementiert, so muß sie für alle Methoden des Interface eine Implementierung liefern. Abstrakte Klassen könne einige Methoden des Interface für ihre Unterklassen zur Implementierung zulassen. Interfaces haben folgende Form:
[<Interfacetyp>] interface <Interfacename> { <Felder>};
Zum Beispiel:
public interface Arbeitnehmer { public float HatGehalt(); };
Interfaces legen ein gemeinsames Teilverhalten für mehrere Klassen fest, die so nicht viel voneinander wissen müssen. So kann man z.B. für Arrayelemente ein gemeinsames Interface definieren, ohne alle Elementklassen von einer gemeinsamen abstrakten Oberklasse abzuleiten. Interfaces können private (default) oder public sein. Alle Methoden in Interfaces sind immer public und abstract und alle Variablen in Interfaces sind immer static, public und final. Methoden in Interfaces dürfen nicht synchronised, transient oder volatile sein. Interfaces verhalten sich beim Anlegen von Instanzvariablen genauso wie Klassen. Da der eigentliche Typ hierbei aber erst zur Laufzeit ermittelt wird, sind sie etwas langsamer.
Der Schlüsselwort
this
verweist auf diese Instanz und das Schlüsselwort
super
auf die Oberklasse. Wenn in einer Unterklasse eine gleichnamige Instanzvariable deklariert wird, so wird die Instanzvariable der Oberklasse nicht überschrieben, sondern nur unsichtbar gemacht. Man kann diese mit Hilfe der Schlüsselwortes super adressieren. Dasselbe gilt für Methoden.
Anweisungen
In der Programmiersprache Java dürfen Anweisungen Labels enthalten. Mittels Sprüngen kann man die Ausführung an solchen Labels fortsetzen. Zum Beispiel:
Marke1 : while(true) { while(true) {
if(<Bedingung>) continue label1;
} }
Der letzte Sprungbefehl springt aus dem inneren Block. Die speziellen Labels
case< Bedingung>:
und
default
dürfen nur innerhalb eines
switch
Blockes auftreten. Der
continue
Befehl kann nur Labels aktivieren, die auf Iterationsbefehle verweisen. Die Labels in einfachen Anweisungen können nur mit
break
und
continue
Befehlen innerhalb eines Blockes aktiviert werden. Blöcke können sowohl Anweisungen, als auch lokale Variablendeklarationen enthalten. Anweisungen werden mit einem Semikolon terminiert. Blöcke könne überall dort existieren, wo auch Anweisungen stehen.
Auswahlanweisungen
Auswahlanweisungen sind:
Das Resultat der Anweisung im switch Befehl wird in int umgewandelt. Das Resultat der Bedingung in if Anweisungen ist vom Typ boolean.
Iterative Anweisungen
Iterationsanweisungen sind:
- while ( <Bedingung> ) Anweisung
- do Anweisung while ( <Bedingung> );
- for ( <ForInit>;<Bedingung>;<ForIncr>) Anweisung
Im Initialisierungsteil der For-Anweisung <ForInit> darf man auch lokale Variablen deklarieren. Falls die Bedingung in der For-Anweisung fehlt so wird die Schleife nicht terminiert.
Sprunganweisungen
Sprunganweisunge sind:
- break [<Labelname>];
- continue [<Labelname>];
- return [<Anweisung>];
- throw [<Anweisung>];
Die Anweisungen
break, continue
und
return
führen vor dem Sprung noch die Anweisungen unter dem Label
finally
aus.
Kontrollanweisungen
Kontrollanweisungen sind:
- synchronised ( <Anweisung 1> )
- try <Block 1> finally <Block n>
- try <Block 1> [catch ( <Argument>) <Block>]+ [finally <Block n>]
Die Anweisung 1 in der
synchronised
Anweisung muß eine Referenz auf ein Objekt oder Array liefern. Vor der Ausführung der Anweisung 2 wird ein Mutex auf dieses Objekt geholt und anschließen wieder freigegeben. Die
try
Anweisung führt immer zuerst den Block 1 aus. Falls dort eine Exception auftritt, wird ein passender Handler unter den
catch
Blöcken gesucht und anschließen noch, falls vorhanden, der
finally
Block ausgeführt. Das Argument für den
catch
Block muß vom Typ Exception sein.
Bei den Operatoren sind nur + für das Verbinden von Strings und >>> für ein vorzeichenloses Leftschift neu. Das Resultat von n>>>s ist gleich dem n>>s, falls n größer null ist, sonst aber (n>>s)+(2<<(k-s-1)), wobei k 32, falls n vom Typ int und 64, falls n vom Typ long ist.
Multitasking in Java
Die Programmiersprache Java unterstützt Multitasking mit speziellen Sprachelementen für die Erzeugung und Synchronisation von Threads. Threads sind kleinere Hintergrundprozesse (lightweight process), die meistens von einem Hauptprozeß erzeugt werden, um komplexere Aufgaben im Hintergrund zu bearbeiten und dadurch bessere Reaktionszeiten zu haben. Würde man alle Aufgaben in demselben Prozess bearbeiten, dann wird die ganze Anwendung für die Dauer der Berechnungen gesperrt. Man kann diese Aufgabe auch nicht abbrechen, denn die Anwendung nimmt keine Ereignisse entgegen, solange die Aufgabe nicht beendet ist. Verlagert man die komplexe Aufgaben aber in einem Hintergrundprozess, so ist die Hauptanwendung (also das Benutzerinterface) sofort wieder bereit, Benutzereingaben entgegenzunehmen.
In der Programmiersprache Java hat man zwei Möglichkeiten, Threads zu benutzen. Man kann seine Klasse von der Klasse
Thread
ableiten oder als Implementierung das Interface
Runnable
definieren. Beide Möglichkeiten haben ihre Vor- und Nachteile. Im allgemeinen: falls eine Klasse von einer anderen Klasse ableiten muß, zum Beispiel von der Klasse Applet, dann kann sie nur das Interface Runnable benutzen. Anderenfalls sollte sie von der Klasse Thread abgeleitet sein.
// // SimpleThread.java // class SimpleThread extends Thread {
public SimpleThread(String name) { super(name);
} public void run() { for(int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try { sleep((int)Math.random() * 1000);
} catch(InterruptedException e) {} }
System.out.println("DONE! " + getName()); } }
Listing 1: Listing von SimpleThread.java
// // TwoThreadsTest.java // class TwoThreadsTest {
public static void main(String args[]) {
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start(); } }
Listing 2: Listing von TwoThreadsTest.java
In diesem Beispiel wird eine Unterklasse von Thread - SimpleThread - definiert, die 10 Mal ihren Namen auf den Standardausgabestrom schreibt und dann eine zufällige Anzahl von Sekunden wartet. Durch den Aufruf der Methode sleep() erhält der andere Thread die Möglichkeit zum Laufen. Die Threads haben gleiche Prioritäten und würden sonst nicht abwechselnd aktiviert.
Die Benutzung von Runnable Interface wird in dem Kapitel "Ein einfaches Java-Applet" demonstriert.
Jeder Thread muß eine Methode run() haben, die den Code für den Hintergrundprozess enthält. Threads haben einen Zustand (runnable, not runnable, dead, new thread) und eine Priorität. Java's virtuelle Maschine implementiert ein einfaches prioritätsgetriebenes Scheduling. Zu jeder Zeit läuft der Thread mit der höchsten Priorität. Falls mehrere Threads mit gleicher Priorität gerade lauffähig sind, so wählt der Scheduler den ersten und läßt ihn laufen. Der Thread wird nicht abgerochen, um andere Threads mit gleicher Priorität laufen zu lassen, es sei denn, daß das Ziel-Betriebssystem eine solche Schedulingsstrategie implementiert. Alle Threads gehören zu Gruppen, die gemeinsame Eigenschaften haben. Threads können Daemons sein. Daemons sind Threads, die im Hintergrund mit einer niedrigeren Priorität laufen und Aufgaben auf Anfrage von anderen Threads erledigen.
Bild 4: Zustände der Java-Threads
Wenn ein Java-Thread angelegt wird, ist er in dem Zustand
New Thread
. Mit einem Aufruf der Methode
start()
wird der Thread in den Zustand
Runnable
überführt. Runnable Threads werden vom Scheduler ausgeführt, wenn CPU-Zeit frei ist. Ein Thread kann die Kontrolle über die CPU freiwillig mit der Methode
yield()
abgeben, um andere Threads mit gleicher Priorität laufen zu lassen. Der Thread kann jederzeit mit der Methode
stop()
angehalten werden. In diesem Zustand ist er nicht mehr lauffähig. Man kann aber seine Instanzvariablen lesen. Threads können zeitweilig mit der Methode
suspend()
angehalten und anschließend wieder mit der Methode
resume()
gestartet werden. Threads, die auf einen Monitor warten (Methode
wait()
), müssen ein Signal vom Monitor (Methode:
notify()
) erhalten, um wieder laufen zu können.
Die Methode
isAlive()
liefert true, wenn der Thread gestartet und noch nicht gestoppt wurde (Zustand runnable oder not runnable). Die Priorität eines Threads kann man mit Hilfe der Methode
setPriority()
setzen. Die Priorität muß zwischen MIN_PRIORITY und MAX_PRIORITY liegen (Konstanten der Klasse Thread). Die Methode
isDaemon()
liefert true, wenn der Thread ein Daemon-Thread ist. Um einen Daemon-Thread zu erzeugen, muß man die Methode
setDaemon()
mit dem Wert true aufrufen. Die run()-Methode der Daemon-Threads ist typischeweise eine ewige Warteschleife auf Anfragen von anderen Threads.
Für die Synchronisation von Threads bietet Java wiedereintrittsfähige Monitore.
// // Producer.java // class Producer extends Thread { private CubbyHole
cubbyhole; private int number; public Producer(CubbyHole c, int number) {
cubbyhole = c; this.number = number; }
public void run() { for(int i = 0; i < 10; i++) {
cubbyhole.put(i); System.out.println("Producer #" + this.number + " put: " + i);
try { sleep((int)(Math.random() * 100)); }
catch(InterruptedException e) {} } } }
Listing 3: Listing von Producer.java.
// // Consumer.java // class Consumer extends Thread {
private CubbyHole cubbyhole; private int number;
public Consumer(CubbyHole c, int number) { cubbyhole = c;
this.number = number; } public void run() {
int value = 0; for(int i = 0; i < 10; i++) {
value = cubbyhole.get(i); System.out.println("Consumer #" + this.number +
" got: " + value); } } }
Listing 4: Listing von Consumer.java
// // CubbyHole.java // class CubbyHole { private int seq;
private boolean available = false; public synchronised int get()
{ while(available == false) { try {
wait(); }
catch(InterruptedException e) {} }
available == false; return seq; }
public synchronised void put(int value) {
seq = value; available = true; notify(); } }
Listing 5: Listing von CubbyHole.java
// // ProducerConsumerTest.java // class ProducerConsumerTest { public static void main(String args[]) {
CubbyHole c = new CubbyHole(); Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1); p1.start(); c1.start(); } }
Listing 6: Listing von ProducerConsumerTest.java
In diesem Beispiel sieht man die Verwendung von Monitoren. Die Klasse Producer erzeugt 10 ganze Zahlen, schreibt sie in eine Puffer (CubbyHole) und wartet dann ein zufällige Zeit. In dieser Zeit wird der Consumer aktiv und holt sich die neueste Zahl. Die Klasse CubbyHole definiert die synchronisierten Methoden get() und put(). Java erzeugt für jede Instanz einer Klasse, die synchrosniserte Methoden oder Codeblöcke enthält, einen Monitor. Wenn ein Objekt eine solche Methode aufruft, sperrt es automatisch den Monitor und gibt ihn nach Verlassen der Methode wieder frei. Falls das Objekt in dieser Methode wait() aufruft, wird der Monitor ebenfalls freigegeben. Das Objekt sperrt den Monitor wieder, sobald es in den Zustand runnable zurückkehrt.
Wichtige Packages
Java hat selbst keine E/A-Routinen. Für Java gibt es einige vorgefertigte Bibliotheken (Packages). Zum Beispiel:
- java.lang: Allgemeine Klassen für Java: Runnable, Boolean, System usw. Dieses Package wird als einziges immer implizit importiert.
- java.util: Hilfsklassen: Date, Vector, Hashtable
- java.io: E/A Klasse: File, InputStream, OutputStream
- awt: Abstract Window Toolkit: Benutzerinterfaceklassen
- net: Netzwerkspezifische Klassen
- applet: enthält die Klassen Applet und AudioClip für Audioanwendungen.
Beispielprogramme
In Java kann man eigenständige Programme schreiben, die ohne die Hilfe eines Java-fähigen WWW-Browsers arbeiten oder aber Java-Applets, die nur als Teil einer WWW-Seite arbeiten. Außerdem kann man Protokollhandler enwickeln, die ein bestimmtes Protokoll wie ftp: oder http: implementieren. Bei Bedarf kann man auch einige Methoden einer Java-Klasse in einer anderen Sprache (z.B. C, C++) implementieren. Solche Methoden sind möglicherweise etwas schneller, aber da sie als Maschinencode eines bestimmten Zielsystems kompiliert werden, sind sie nicht mehr binärkompatibel zu anderen Systemen.
Ein einfaches Java-Programm
Eigenständige Java-Programme müssen die Methode main() überschreiben. Zum Beispiel:
// // TestApp.java // class TestApp { public static void main(String args[])
{ System.out.println("Test App!"); } }
Listing 7: Listing von TestApp.java
Nach der Kompilierung wird daraus die Binärdatei TestApp.class erstellt, die man anschließend mit dem Java-Interpreter ausführen kann:
javac TestApp.java
java TestApp
Ein einfaches Java-Applet
Alle Applets sind von der Klasse
Applet
abgeleitet. Die wichtigsten Methoden eines Applets sind:
- void init(): Initialisierung von Applet. Laden der WWW-Seite.
- void start(): Aktivierung von Applet. Anzeigen der WWW-Seite.
- void stop(): Deaktivierung von Applet. Verlassen der WWW-Seite.
- void destroy(): Terminierung von Applet. Verlassen des Browsers.
- void paint(Graphics): Zeichnet den Bereich, der dem Applet zugewiesen wurde.
Man muß nicht alle diese Methoden überschreiben. Hier ist ein einfaches Beispielapplet.
// // TestApplet.java // import java.awt.Graphics;
//graphische Klassen import java.applet.Applet;
//Deklaration der Klasse Applet public class TestApplet extends Applet {
public void init() { resize(150, 25); }
public void paint(Graphics g)
{ g.drawString("Ausgabe von Test Applet!", 50, 25); } }
Listing 8: Listing von TestApplet.java
Diesen Quellcode kompiliert man mit den Java-Compiler (javac). Dazu gibt man auf der Kommandozeile den Befehl:
javac TestApplet.java
ein . Der Java-Compiler erzeugt die Datei TestApplet.class. Die neue Klasse muß jetzt in eine WWW-Seite eingebunden werden, zum Beispiel so:
<HTML>
<HEAD>
<TITLE> "Titel" </TITEL>
</HEAD>
<BODY>
"Das ist ein Applet!"
<APPLET CODE=TestApplet.class WIDTH=150 HEIGHT=25>
</BODY>
</HTML>
Listing 9: Listing von TestApplet.html
Nun kann man die neue WWW-Seite in den Browser laden. Man sollte den Browser nicht aus dem Verzeichnis, in dem die WWW-Seiten gespeichert sind, aufrufen. Sonst kann man die Datei - nach einigen Programmänderungen - nicht erneut laden.
Damit die Applets quasiparallel ablaufen können und sich nicht gegenseitig blockieren, sollte man für längere Abläufe in Applets Hintergrundthreads benutzen. Dafür muß das Applet zusätzlich von der Klasse
Runnable
abgeleitet sein. Hier ein kleines Beispiel:
// // ThreadApplet.java // import java.awt.*; import java.applet.Applet;
public class ThreadApplet extends Applet implements Runnable {
Thread testThread = null; int count = 0;
String meldung = null; public void run() {
while(true) { sleep(100);
count++; meldung = "Schleifenzähler: " + count;
repaint(); } }
public void paint(Graphigc g) {
g.clearRect(0,0,size().width-1, size().height-1);
g.drawString(meldung, 5, 15); }
public void init() { resize(500, 20); } public void start() {
if(testThread == null) {
testThread = new Thread(this, "Test Thread");
testThread.start(); } }
public void stop() { testThread.stop();
testThread = null; } }
Listing 10: Listing von ThreadApplet.java.
Einbinden von "Native-Methods"
"Native-Methods" sind Methoden einer Java-Klasse, die man in einer anderen Programmiersprache implementiert hat. So kann man Aufgaben lösen, die die Programmiersprache Java nicht unterstützt oder einfach schnellere Methoden für zeitkritische Programmeteile erzeugen. Solche Methoden sind natürlich nicht mehr plattformunabhängig.
Eine "native methode" deklariert man in der Java-Klasse ohne eine Implementierung. Um die Methodendeklaration mit der Implementierung zu verbinden, muß man in einem statischen Initialisierungsblock mit Hilfe der Methode
loadLibrary()
der Klasse System eine DLL (oder shared Library unter UNIX) mit der Implementierung laden.
// // HelloWorld.java // class HelloWorld {
public native void displayHelloWorld(); //native Methode deklarieren
static { System.loadLibrary("hello");
//DLL mit implementierung laden }
public static void main(String args[]) {
//Hauptprogramm displayHello(); } }
Listing 11: Listing von HelloWorld.java.
Diese Quelldatei kann man ganz normal
(javac HelloWorld.java)
kompilieren. Für die Implementierung in C muß man mit Hilfe des Programms javah aus der Java-Klasse eine Headerdatei und eine Stubs-Datei generieren.
javah HelloWorld //erzeugt HelloWorld.h
javah -stubs HelloWorld //erzeugt HelloWorld.c
Das Programm javah generiert eine HelloWorld.h-Datei, die eine C-Struct enthält und eine externe Funktion deklariert. Dieses Struct hat die Aufbau der Java-Klasse und ermöglicht der C-Methode, auf die Instanzvariablen der Java-Klasse zuzugreifen. Die generierte Stub-Datei enthält den Rumpf der C - Funktion, die als Java-Methode aufgerufen wird. Aus dieser Datei erzeugt man die DLL.
// // HelloWorld.c // #include #include "HelloWorld.h" #include
void HelloWorld_displayHelloWorld(struct HHelloWorld *this) {
printf("Hello World!\n"); return; }
Listing 12: Listing von HelloWorld.c
Die Headerdatei
StubPreamble.h
enthält Deklarationen des internen Java-Laufzeitsystems für die C-Funktion. Die Headerdatei
HelloWorld.h
enthält die Deklaration der C-Struct, die der Java-Klasse entspricht. Ein Zeiger auf diese Struct wird der C - Funktion übergeben, damit sie auf die Instanzvariablen der Java-Klasse zugreifen kann.
Die kompilierte DLL muß in einem Verzeichnis liegen, das in der PATH Umgebungsvariable angegeben ist. Unter UNIX muß man die Umgebungsvariable LD_LIBRARY_PATH entsprechend setzen.
WWW-Scriptsprachen
Mit der Programmiersprache Java kann man kleine Applets für die WWW-Seiten erstellen. Java-Applets bleiben aber isoliert vom HTML-Dokument und voneinander. Um die Applets miteinander und mit dem HTML-Dokument zu verbinden, Tastatur-, Maus- und andere Ereignisse zu bearbeiten und möglichst viele Aufgaben schon auf der Clientseite zu erledigen, sind die Scriptsprachen vorgesehen. Scripts werden nicht in Bytecodes übersetzt, sondern direkt in die HTML-Dokumente eingebunden. Sie werden zur Laufzeit interpretiert. Diese Scripts können auf die Benutzereingaben reagieren, Berechnungen durchführen, Inhalte und Aussehen der HTML - Elemente ändern usw. Somit machen sie vieles, was vorher die CGI-Scripts auf Serverseite erledigt haben. Da jetzt nicht alle Benutzereingaben übers Netzwerk transportiert werden müssen, verringert sich die Netzbelastung und die Abarbeitung wird beschleunigt.
Sowohl Netscape als auch Microsoft haben angekündigt, solche Scriptsprachen zu entwickeln. Microsofts Scriptsprache soll Visual-Basic-Script heißen und auch Windows-Systemaufrufe durchführen können. Netscape hatte vorher eine eigene Scriptsprache namens LiveScript entwickelt. Nach einer Vereinbarung mit Sun wurde diese Sparche zu JavaScript umbenannt und ist jetzt in der neuesten Version des Netscape Browsers eingebaut. JavaScript ist noch nicht ganz fertig. Viele Sprachelemente sind entweder noch nicht implementiert oder nicht dokumentiert. Außerdem hat man viele weitere Änderungen an der Sprache angekündigt.
JavaScript
Grundlegende Sprachelemente des JavaScript stammen von Suns Java. JavaScript arbeitet mit den Inhalten der HTML-Dokumente. Die Elemente eines HTML-Dokuments werden als Objekte behandelt, die Methoden und Instanzvariablen (Properties) haben. JavaScript kennt nur feste Objekte, die der Benutzer nicht ändern kann. Sie sind in folgender Hierarchie angeordnet:
Bild 5: Objekthierarchie der Programmiersprache JavaScript
- Window: ist der Bereich, in dem Netscape den Text darstellt, nicht aber das Netscape Fenster selbst. Ab Version 2.0 unterstützt der Netscape Browser mehrere Fenster (Frames). Die Methoden der Windows können Warnungen und sonstige Texte in einem kleinen Fenster anzeigen (alert) und Benutzer um eine Bestätigung bitten (confirm). Die Methode confirm liefert true oder false, je nach der Reaktion des Benutzers.
- Location: hat Properties, die den URL der aktuellen WWW-Seite und seiner Komponenten (Protokoll, Host usw.) enthalten.
- History: bietet den Zugriff auf die URLs der bisher gelesenen Seiten. Mit seinen Methoden kann man sich innerhalb dieser URLs bewegen.
- Document: hat Methoden für die Textausgabe und viele Properties, die den Titel und andere Komponenten des Dokuments enthalten. Unter anderem hat es auch Arrays von Formularen, Links und Anchors.
- Forms: sind die Inhalte der HTML-Dokumente. Sie haben folgende untergeordnete Elemente: Text Fields, Text Areas, Check Boxes, Radio Button, Selections, Buttons. Die Properties dieser Elemente enthalten die Namen und Inhalte (Text bei Textfeldern und boolesche Werte bei den Buttons). Man kann für die Elemente Ereignis-Handler definieren.
- Links und Anchors: sind Verweise auf andere HTML-Dokumente oder Dateien. Links entstehen durch das HTML-Element href und Anchors durch das Element name.
- Plug-Ins, Applets, Elements: sind in der Version 2.0 (beta 5) des Netscape Browsers noch nicht implementiert. Plug-Ins sind eine Schnittstelle für Fremdformate wie z.B. Postscript oder PDF. Verschiedene Firmen (z.B. Adobe) entwickeln zur Zeit solche Plug-Ins.
JavaScripts kann man mit dem Element namens
script
in eine HTML-Seite einbinden. Man kann keine neuen Objekte definieren, aber man kann Funktionen in dem HTML-Dokumentenkopf (head) definieren, die man später aufrufen will. Methoden der vordefinierten Objekte werden über die Namen der Objekt aufgerufen (z.B. document.write("Text") ). Der
this
- Zeiger zeigt auf das aktuelle Objekt. Properties sind wie öffentliche Instanzvariablen. sie werden einfach über deren Namen mit referenziert (z.B. document.title). Möglich ist auch die Array-Notation - document['title'] oder document[0], falls man den Index des Property kennt.
Die Formularelemente haben folgende Ereignis-Handler und Methoden:
Tabelle 1: Ereignis-Handler und Methoden der Formularelemente
Listing 13: einfaches JavaScript Beispiel.