In dieser kurzen Zusammenfassung werden ich die wichtigsten Eigenschaften von Java unter dem Gesichtspunkt der Appletprogrammierung zusammenfassen. Dabei ist es auch Online zu lesen, da ich viele Links auf die Java-Dokumentationen bei uns in der TU-Chemnitz eingebaut habe. Die Dokumentationen stammen von Sun und sind sicher nach irgendeinem Copyright geschützt. Alle Warenzeichen sind Eigentum der jeweiligen Eigentümer....u.s.w.
Java ist eine Sprache, die als Bytecode compiliert auf einer Virtuellen Maschine abläuft. Damit wird eine Palattformunabhängigkeit erreicht. Der Syntax ähnelt dem von C, die Sicherheit ist ADA-like, von SmallTalk sind einige Konzepte der Objektorientierung verwendet worden.
<applet class=filename.class codebase=dirname width=###
height=### name=instanceName>
<param name=name1 value=value1>
<param name=name2 value=value2>
<param name=namen value=valuen>
Alternativer Text
</applet>
import java.applet.Applet; import java.awt.Graphics; public class HelloWorld extends Applet { public void paint(Graphics g) { g.drawString("Hello world!", 50, 25); } }Alle Anweisungen werden mit einem Semikolon abgeschlossen (wie in C).
<HTML> <HEAD> <TITLE> A Simple Program </TITLE> </HEAD> <BODY> Here is the output of my program: <APPLET CODE="HelloWorld.class" WIDTH=150 HEIGHT=25> </APPLET> </BODY> </HTML>
Packages ähneln den Units in Pascal bzw. den includes in C. Die verschiedenen Klassen des JDK (in 1.0.2 etwa 210) werden zu Packages zusammengefaßt, um einen besseren Überblick zu haben, die Compilierung zu Beschleunigung und Namenskonflikte zu vermeiden. Wird nichts anderes angegeben, so gehört jede Klasse zum default-package.
specifyer class MyClass extends ParentClass implements interface1, interface2,... {
specifyer Type1 attribut11, attribut12,...; specifyer Type2 attribut21, attribut22,...; ....
specifyer MyClass () {...} //Constructor specifyer MyClass (Type param1, Type param2, ...) {...} //Constructor
specifyer Type methode1 () {...} //Methode ohne Parameter specifyer Type methode2 (Type param1, Type param2, ...) {...} //Methode mit Parameter
}
Beispiel:
import java.awt.Point; public class Rechteck [extends java.lang.Object] {
private Point aufPunkt; // (*) private int breite=0, höhe=0; // Umlaute sind erlaubt, aber nicht erwünscht
Rechteck () { super (); // Parent-Constructor aufrufen aufPunkt=new Point(0,0); // auch gleich bei (*) möglich }
public Rechteck (int b, int h) { this(); // eigenen Constructor Rechteck() aufrufen breite=b; höhe=h; }
public Rechteck (int x, int y, int breite, int höhe) { this(breite, höhe); // die Parameter und nicht die Attribute werden verwendet aufPunkt=new Point(x,y); } public void moveTo (Point neuerAufPunkt) { aufPunkt=neuerAufPunkt; // alter aufPunkt wird automatisch gelöscht } public void moveTo (int x, int y) { //überladene Methode aufPunkt=new Point(x,y); } public String toString () { //überschriebene Methode return "Rechteck von "+aufPunkt.toString()+" Breite "+breite+" Höhe "+höhe; } private normalisiere () { ... }
public static int area (Rechteck r) { return r.breite*r.höhe; }
}
Um den Zugriff auf Klassen, Attribute, Construktoren und Methoden zu beschränken wurden die Specifyers eingeführt.
Für Klassen gelten folgende drei: public, final, abstract.
public | Alle Klassen können eine Instanz dieser Klasse anlagen - auch die außerhalb des Paketes. Standardmäßig ist eine Klasse nur innerhalb eines Paketes sichtbar. |
final | Von dieser Klasse kann keine Unterklasse erzeugt werden. |
abstract | Diese Klasse muß weiter abgeleitet werden, damit von ihr eine Instanz erzeugt werden kann. |
Attribute, Construktoren und Methoden (kurz Members) werden durch folgende Specifyers beschränkt. Es gibt die Specifyers für Zugriff, Ableitung, Synchronisation, native Implementierung oder Statische Members
private | Nur Objekte dieser Klasse dürfen auf die Members zugreifen - nicht einmal Subklassen. Standard ist der Zugriff nur von Klassen des gleichen Packages erlaubt. |
protected | Auch Subklassen in anderen Packages haben Zugriff auf diesen Member. |
public | Jeder Klasse hat vollen Zugriff auf den mit public gekennzeichneten Member. |
final | Ein so bezeichneter Member darf in einer Subklasse nicht überschrieben werden. (Anwendung, wenn eine final-Klasse zu "schwer" ist. |
abstract | Eine mit abstract Methode gekennzeichnete Methode muß in der Subklasse definiert werden. Ist ein Member einer Klasse abstract, so ist es auch die Klasse selbst. |
synchronized | Für diese Methoden wird ein Monitor angelegt. Damit wir die Synchronisation in Threads ermöglicht. Alle Klassen des JDK sind "threadsafe". |
static | Die so gekennzeichneten Member sind als Klassen-Attribute/-Methoden nur ein einziges mal pro Klasse verfügbar. Die Referenzierung kann auch mit ClassName.Member erfolgen. |
Beispiele sind im Tutorial zu finden.
Java unterstützt keine Mehrfach-Vererbung. Um aber trotzdem einen Teil dieser Funktionalität zu ermöglichen, wurden die Interfaces erfunden. Man kann sich ein Interface als eine Art vollständig abstrakte Klasse vorstellen. Jedoch dürfen nicht einmal Attribute in einem Interface definiert werden - nur Konstante. Bei Interfaces gibt es Mehrfach-Vererbung. Es ist auch möglich, ein Interface als Typ zu nutzen. (z.B. Thread(Runnable target))
Oft benutzte Interfaces:
Beispiel:
interface Composeable { public void putElement(Object anObject); public void putElementAt(Object anObject, int anIndex); public boolean hasElements (); public Object getElementAt(int anIndex); public java.util.Enumeration elements(); } class Tabelle implements Composeable { public Tabelle (int Zeilen, int Spalten) { ... }; public void putElement(Object anObject) { ... }; public void putElementAt(Object anObject, int anIndex) { ... }; public boolean hasElements () { ... }; public Object getElementAt(int anIndex) { ... }; public java.util.Enumeration elements() { ... }; public String toString () { ... }; }
Java unterscheidet zwei Arten von Typen: Referenzen und Primitives.
Arrays und Strings sind in Java Objekte.
Neue Objekte werden mit new angelegt. Nach new folgt ein Construktor. Es ist nicht möglich, Speicherplatz explizit freizugeben. Das mach der GarbageCollector. Ein Objekt wird entfernt, sobald es keine Referenz darauf meht gibt. Es wird automatisch der Virtuelle Speicher des Betriebssystems verwendet. Mit den Referenzen, die Java benutzt läßt sich nicht rechnen! Es sind eher Nummern, die einem Objekt zugeordnet werden.
Es gelten die "normalen" C++ Operatoren. Die Prioritäten stehen hier.
|
Zum Rechnen mit Zahlen |
|
logische Ausdrücke |
|
als prefix oder postfix |
|
zum Vergleichen |
|
für Bitmanipulation |
if (expr) anweisung1; [else anweisung2;]
switch (ordinal) { case value1: anweisung1;break; case value2: anweisung2;break; ... case valuen: anweisungn;break; [default: anweisung_d;] }
for ([initialisierung]; [bedingung]; [iteration]) // komma ist möglich für "längere" ausdrücke anweisung;
while (bedingung) anweisung;
do { anweisung; } while (bedingung);
Goto ist zwar ein reserviertes Schlüsselwort,. es wird aber nicht genutzt. Statt dessen können Schleifen mit labels versehen werden, die dann mit break oder continue verlassen werden.
wenn mit break [label] eine Schleife verlassen wird, dann wird ans Ende der Schleife gesprungen und die Schleife verlassen
der Aufruf von continue [label] führt dazu, daß mit dem nächsten Schleifendurchlauf der Schleife fortgesetzt wird.
mit return, return primitive, return Object werden Methoden verlassen. Der Parameter richtet sich nach dem Rückgabetyp der Methode. Return verläßt die Methode sofort, ohne die (möglichen) Anweisungen nach return zu berücksichtigen.
Es gibt 4 wichtige Methoden, die das Leben eines Applets beeinflussen. Das sind: init, start, stop, destroy.
Ein Applet braucht keinen Construktor. Es gibt auch noch weitere wichtige Methoden: paint(Graphics), handleEvent(Event), action(Event, Object).
Das AWT (Abstract Window(ing) Toolkit) ist die Grundlage für die Graphische Oberfläche von Java. Es bildet die Schnittstelle zum Betriebssystem. Die einzelnen Elemente werden logisch in einer Baumstruktur angeordnet. Die graphische Verwaltung übernehmen Layoutmanager, die dafür sorgen, daß der Platz nach einer bestimmten Strategie verwendet wird. Die Elemente bestimmen ihre Größe selbst. Die Layoutmanager bestimmen dann die Lage auf dem Bildschirm. Es wird in Container und Components unterschieden. Jeder Container ist aber auch ein Component, so daß eine Verschachtelung möglich ist. Es wird die Methode add(Component) verwendet, um Components zu einem Container hinzuzufügen.Panel, Applet, Window, Dialog und Frame des Packages java.awt sind Container.
Das AWT stellt eine Reihe von GUI-Elementen zur Verfügung:
Dabei ist zu beachten, daß die Elemente je nach OS verschieden aussehen können. Das liegt daran, daß auf die GUI-Libraries der jeweiligen Systeme zurückgegriffen wird. Doch mit Hilfe der LayoutManager ist es kein großes Problem, die Elemente an die richtige Stelle auf den Bildschirm zu bekommen.
In Java überläßt man das Layout der Komponenten des GUI i.d.R. den Layoutmanagern. Mit der Methode setLayout(LayoutManager) wird einem Container ein Layout zugeordnet. Es gibt 5 verschiedene Layoutmanager:
Diese Methode hat sich bei Verwendung des GridBagLayouts als sehr nützlich erwiesen:
private void addItem(GridBagLayout gbl, Component c,int x, int y, int b, int h, double dx,double dy) { GridBagConstraints gbc=new GridBagConstraints(); gbc.fill=GridBagConstraints.BOTH; gbc.insets= new Insets(5,5,5,5); gbc.gridx=x; gbc.gridy=y; gbc.gridwidth=b; gbc.gridheight=h; gbc.weightx=dx; gbc.weighty=dy; gbl.setConstraints(c, gbc); add(c); }
Java ist eine Eventgeteuerte Sprache. Jedes Component kann Events aussenden. Außerdem gibt es noch hardwarenahe Events wie MouseMove oder MouseDown oder die TastaturEvents. Alle Events werden vom OS an das Applet gemeldet. Das ermittelt das betroffene Component und gibt das Event an dessen handleEvent(Event). Es wird ein Baum entsprechend des GUI-Aufbaus abgearbeitet. Wird ein Event erfolgreich bearbeitet, so gibt handleEvent true zurück und die Bearbeitung ist zu ende. Beim Überschreiben von handleEvent ist es angebracht anstelle von false als letzten return-wert, super.handleEvent(Event) zurückzugeben. Das Component verteilt einige Events automatisch an andere Methoden:
Diese Methoden haben alle noch eine Reihe Extra-Parameter, die aber alle aus dem Event gefiltert werden.
Beispiel:
public boolean handleEvent(Event e) { if (e.id==Event.MOUSE_MOVE) { doMouseMove(); //Maus über Component bewegt -- besser mouseMove() überschreiben return true; } if (e.id==Event.ACTION_EVENT) { if (e.target == button1) { doButton1(); //button1 geklickt -- besser action() überschreiben return true; } if (e.arg.equals("Exit")) { doExit(); //ein Objekt mit der Aufschrift Exit feuert Action-Event return true; } } return super.handleEvent(e); }
In Java wird nicht direkt auf den Bildschirm gezeichnet, sondern in einen Gerätekontext.
Jedes Component besitzt die Methode paint(Graphics g). Diese wird aufgerufen, wenn sich das Component zeichnen soll. Die Methode update(Graphics g) wird vorher aufgerufen. In ihr wird der Hintergrund gelöscht. Um bei Animationen ein Flackern zu vermeiden wird update oft umgeschrieben nach:
public void update (Graphics g) { paint(g); }
Es gibt verschiedene Methoden, um ein Image zu erzeugen:
Auf die leeren Images kann man dann zeichnen, indem man ein Graphics-Objekt anfordert. Da Images (java.awt.Image) auch Objekte sind, kann man sie in einer Datenstruktur zusammenfassen und damit Animationen erstellen.
Als Thread werden Programmabläufe bezeichnet, die scheinbar gleichzeitig zu anderen Programmen im System ablaufen. In Java gehören Threads schon zur Sprache dazu. Sie werden durch die Klasse java.lang.Thread dargestellt. Die Arbeit verrichten Threads in einer run()-Methode. Es gibt 2 Varianten, einen Thread zu erzeugen:
MyThread extends java.lang.Thread() { public void run () { //do something } }
MyThread aMyThred =new MyThread();
aMyThread.start();
MyClass extends Object implements Runnable { public void run() { //do something } }
aMyClassInstance MyClass=new MyClass();
anOtherThread = new Thread(aMyClassInstance);
anOtherThread.start();
Jede dieser Methoden hat Vor- und Nachteile. Wenn man nur einen einzigen Thread hat, dann ist die Variante 2 besser. Variante 1 ist bei vielen Threads der gleichen Sorte zu empfehlen.
Es gibt dann noch die Möglichkeit, einen Thread zu stoppen (stop) und zu starten (start) bzw. zu anzuhalten (suspend) oder weiterlaufen zu lassen (resume). Alle Methoden stehen in der API-Doc erklärt.
Wichtig sind noch die Methoden yield() und currentThread(). Mit yield() wird der Thread gezwungen die Kontrolle abzugeben - ein anderer darf rechnen. Mit currentThread() (eine static-Methode von Thread) wird der aktuell laufende Thread ermittelt. Den Threads wird eine Priorität zugeordnet (max=10, min=0, norm=5). Die mit hoher Priorität erhalten Vorrang. Außerdem können Threads in einer ThreadGroup (java.lang.ThreadGroup) zusammengefaßt werden.
Mit Exceptions hat Java eine sehr günstige Methode der Fehlerverarbeitung. Eine Exception ist ein Objekt (was sonst), daß im Falle eines unerwarteten Fehlers erzeugt und "aufgeworfen" wird. Irgendwo muß dann diese Exception gefangen werden. Wird dies nicht gemacht, so wird eine Ausgabe auf die Konsole erzeugt. Das Applet bricht aber nicht ab. (Es sei denn, es sind sehr schwerwiegende Gründe - Klasse nicht gefunden...)
Die Exceptions werden entweder mit einer try-catch-finally-Konstruktion abgefangen oder per throws-Statement nach "oben" delegiert:
try { //Anweisungen, die eine Exception aufwerfen können } catch (ExceptionKlasse varname) { //Exception Behandlung; } [catch (ExceptionKlasse2 varname) { //Exception Behandlung; }]* [finally { //Aufräumarbeiten }]
Wenn innerhalb des try {} Blocks Exceptions entstehen, dann wird versucht, diese innerhalb des Catch-Blockes zu behandeln. Wenn nicht wird versucht, die Fehler weiterzureichen.
Ist ein Bestandteil des Methodenkopfes:
Returntype Name (params) throws ExceptionType { ... }
Tritt in dieser Methode eine Exception auf, so wird diese automatisch an die rufende Methode weitergeleitet. Das try-catch hat aber innerhalb der Methode Vorrang vor throws.
Eigene Exceptions werden von der Klasse Exception abgeleitet. Es reicht eigentlich schon, den Namen neu zu schreiben:
MyException extends Exception { } // das ist schon genug
Eine Exception kann auch vom Programmierer aufgeworfen werden. Dafür gibt es das Schlüsselwort throw - als Parameter erwartet es ein Throwable-Objekt: (Throwable ist eine Klasse, obwohl es nach Interface klingt - es ist die Oberklasse aller Exceptions und Errors)
throw new MyException("Aus diesem Grund");
Errors sollten nicht "gefangen" werden, da sie auf schwere Fehler hinweisen.
Zum Abschluß noch einige oft genutzte Klassen:
Allgemein bleibt nur zu sagen: Die Klassen sind der Kern der Sprache - wer sie kennt, der kennt Java - nur es kommen täglich neue dazu - in JDK 1.1.1. sind es schon 480...und die Entwicklung geht weiter.
Zur Homepage Chris Hübsch; 19.05.1997 |