Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 1 Schon wieder eine neue Sprache?
  gp 1.1 Der erste Kontakt
  gp 1.2 Historischer Hintergrund
  gp 1.3 Eigenschaften von Java
    gp 1.3.1 Bytecode und die virtuelle Maschine
    gp 1.3.2 Kein Präprozessor
    gp 1.3.3 Keine überladenen Operatoren
    gp 1.3.4 Zeiger und Referenzen
    gp 1.3.5 Bring den Müll raus, Garbage-Collector
    gp 1.3.6 Ausnahmenbehandlung
    gp 1.3.7 Objektorientierung in Java
    gp 1.3.8 Java-Security-Model
    gp 1.3.9 Wofür Java nicht geeignet ist
  gp 1.4 Java im Vergleich zu anderen Sprachen
    gp 1.4.1 Java und JavaScript
    gp 1.4.2 Normierungsversuche
    gp 1.4.3 Die Rolle von Java im Web
    gp 1.4.4 Vollwertige Applikationen statt Applets
  gp 1.5 Entwicklungs- und Laufzeitumgebungen
    gp 1.5.1 Aller Anfang mit dem Java SDK
    gp 1.5.2 Die Entwicklungsumgebung von Sun: Sun ONE Studio und NetBeans
    gp 1.5.3 IBM und Eclipse
    gp 1.5.4 JBuilder von Borland
    gp 1.5.5 Together
    gp 1.5.6 Ein Wort zu Microsoft, Java und zu J++
    gp 1.5.7 Direkt ausführbare Programme
  gp 1.6 Installationsanleitung für das Java 2
    gp 1.6.1 Das Java 2 SDK beziehen
    gp 1.6.2 Java SDK unter Windows installieren
    gp 1.6.3 Compiler und Interpreter nutzen
    gp 1.6.4 Installation der Java Laufzeitumgebung (1.4) unter Linux
  gp 1.7 Das erste Programm compilieren und testen
    gp 1.7.1 Häufige Compiler- und Interpreterprobleme
  gp 1.8 Eclipse
    gp 1.8.1 Eclipse starten
    gp 1.8.2 Das erste Projekt anlegen
    gp 1.8.3 Eine Klasse hinzufügen
    gp 1.8.4 Übersetzen und Ausführen
    gp 1.8.5 J2SDK statt JRE
    gp 1.8.6 Start eines Programmes ohne Speicheraufforderung
    gp 1.8.7 Java 5.0 in Eclipse
    gp 1.8.8 Projekt einfügen oder Workspace für die Aufgaben wechseln
    gp 1.8.9 Plugins für Eclipse


Galileo Computing

1.3 Eigenschaften von Javdowntop

Java ist eine objektorientierte Programmiersprache, die sich durch einige zentrale Eigenschaften auszeichnet. Diese machen sie universell einsetzbar und für die Industrie als robuste Programmiersprache interessant. Da Java objektorientiert ist, spiegelt es den Wunsch der Entwickler wider, moderne und wieder verwertbare Softwarekomponenten zu programmieren.


Galileo Computing

1.3.1 Bytecode und die virtuelle Maschine  downtop

Zunächst ist Java eine Programmiersprache wie jede andere auch. Nur im Gegensatz zu herkömmlichen Übersetzern einer Programmiersprache, die Maschinencode für eine spezielle Plattform generieren, erzeugt der Java-Compiler Programmcode für eine virtuelle Maschine, den so genannten Bytecode. Bytecode ist vergleichbar mit Mikroprozessorcode für einen erdachten Prozessor, der Anweisungen wie arithmetische Operationen, Sprünge und Weiteres kennt. Ein Java-Compiler, etwa der von Sun, der selbst in Java implementiert ist, generiert diesen Bytecode.

Damit aber der Programmcode des virtuellen Prozessors ausgeführt werden kann, führt nach der Übersetzungsphase die Laufzeitumgebung (auch Run-Time-Interpreter genannt), die Java Virtuelle Maschine, den Bytecode aus. Somit ist Java eine compilierte, aber auch interpretierte Programmiersprache – von der Hardwaremethode einmal abgesehen.

Das Interpretieren bereitet noch Geschwindigkeitsprobleme, da das Erkennen, Dekodieren und Ausführen der Befehle Zeit kostet. Im Schnitt sind Java-Programme drei bis zehn Mal langsamer als C(++)-Programme. Die Technik der Just-In-Time(JIT)-Compiler mildert das Problem. Ein JIT-Compiler beschleunigt die Ausführung der Programme, indem die Programmanweisungen der virtuellen Maschine für die physikalische übersetzt werden. Es steht anschließend ein auf die Architektur angepasstes Programm im Speicher, das ohne Interpretation schnell ausgeführt wird. Auch Netscape übernahm im Windows-Communicator 4.0 einen JIT (ein Produkt von ehemals Symantec), um an Geschwindigkeit zuzulegen – obwohl diese Variante noch nicht den gesamten 1.1 Standard beherrschte. (Erst in der Version 4.06 von Netscape kam die volle Unterstützung für Java 1.1.) Mit dieser Technik liegt die Geschwindigkeit zwar in vielen Fällen immer noch unter der von C, aber der Abstand ist geringer.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Java on a chip

Dieser virtuelle Prozessor wurde mittlerweile auch in Silizium gegossen – eine Entwicklung, die verstärkt von Sun beziehungsweise Lizenznehmern verfolgt wird. Der Prototyp dieses Prozessors (genannt PicoJava) ist verfügbar und findet bald Einzug in so genannte Network-Computer. Das sind Computer ohne bewegliche Peripherie, wie Festplatten, die als Terminal am Netz hängen. Bei der Entwicklung des Prozessors stand nicht die maximale Geschwindigkeit im Vordergrund, sondern die Kosten pro Chip, um ihn in jedes Haushaltsgerät einbauen zu können. Das Interesse an einem Java –on –a Chip ist inzwischen stark zurückgegangen.

Noch mehr Programmiersprachen, die Bytecode erstellen

Doch nicht nur aus der Programmiersprache Java lässt sich Bytecode erzeugen. Zurzeit gibt es bei verschiedenen Herstellern Entwicklungen von ADA-Compilern und sogar C–Compilern, die Bytecode erstellen. Auch die OOP-Programmiersprache EIFFEL (SmartEiffel) – unter der Leitung von Bertrand Meyer entwickelt – generiert Java-Bytecode. Ebenso gibt es eine Scheme11-Umgebung, die komplett in Java programmiert ist. Der Compiler erstellt für den LISP-Dialekt ebenfalls Java-Bytecode. Eine Webseite, die Programmiersprachen für die JVM aufzeigt, ist http://flp.cs.tu-berlin.de/~tolk/vmlanguages.html.

Mittlerweile ist Java nicht nur interpretierte Sprache, sondern zugleich auch interpretierende Sprache. Das zeigen unterschiedliche Computer- und Prozessor-Emulationsprogramme.


Galileo Computing

1.3.2 Kein Präprozessor  downtop

In C(++) ersetzt ein Präprozessor Makros etwa für bedingte Compilierung oder Header-Dateien. Einen Präprozessor gibt es in Java nicht und entsprechend keine Header-Dateien. Diese sind in Java nicht nötig, da der Compiler die benötigten Informationen über die Softwareschnittstellen von Klassen direkt aus den Klassendateien liest. Ein schmutziger Trick wie


#define private public
#include "allesMoegliche"

oder Makros, die Fehler durch doppelte Auswertung erzeugen, sind damit von vornherein ausgeschlossen. Im Übrigen findet sich der Private/Public-Hack im Quellcode von Suns StarOffice. Mit der oberen Definition wird jedes Auftreten von private durch public ersetzt, mit der Konsequenz, dass der Zugriffsschutz ausgehebelt ist.

Leider ist damit auch eine bedingte Compilierung mit #ifdef nicht mehr möglich. Dies führt vereinzelt dazu, dass ein externer Präprozessor benutzt werden muss, um den Quellcode entsprechend zu bearbeiten.


Galileo Computing

1.3.3 Keine überladenen Operatoredowntop

Wenn wir Operatoren wie das Plus- oder das Minuszeichen verwenden und damit Ausdrücke zusammenfügen, machen wir dies meistens mit bekannten Rechengrößen. So fügt ein Plus zwei Ganzzahlen, aber auch zwei Fließkommazahlen (Gleitkommazahlen) zusammen. Einige Programmiersprachen – meistens Skriptsprachen – erlauben auch das »Rechnen« mit Zeichenketten, mit einem Plus können diese beispielsweise aneinander gehängt werden. Die meisten Programmiersprachen erlauben es jedoch nicht, die Operatoren mit neuer Bedeutung zu versehen und damit Objekte zu verknüpfen. In C++ ist jedoch das Überladen von Operatoren möglich, sodass etwa das Pluszeichen dafür genutzt werden kann, zum Beispiel geometrische Punktobjekte zu addieren. Dies ist praktisch bei umfangreicheren Rechnungen mit Objekten, da dort umständliche Verbindungen nicht über die Methoden geschaffen werden, sondern angenehm kurze über ein Operatorzeichen. Obwohl zuweilen ganz praktisch – das Standardbeispiel sind Objekte für komplexen Zahlen und Brüche –, verführt die Möglichkeit, Operatoren durch den Programmierer zu überladen, oft zu unsinnigem Gebrauch. In Java ist daher das Überladen der Operatoren bisher nicht möglich. Es kann aber sein, dass sich dies in Zukunft ändert.

Die Grundrechenarten sind für Ganzzahlen und Gleitkommazahlen überladen und ebenso ein einfaches Oder, Und oder Xor für Ganzzahlen und boolesche Werte. Der einzige auffällige überladene Operator in Java für Objekte ist das Pluszeichen bei Strings. Zeichenketten können damit leicht zusammengesetzt werden. Informatiker verwenden in dem Zusammenhang auch gerne das Wort Konkatenation (selten Katenation). Bei einem String »Hallo« und »du da« ist »Hallo du da« die Konkatenation der Zeichenketten.


Galileo Computing

1.3.4 Zeiger und Referenzen  downtop

In Java gibt es keine Zeiger (engl. pointer), wie sie aus anderen Programmiersprachen bekannt und gefürchtet sind. Da eine objektorientierte Programmiersprache aber ohne Verweise nicht funktioniert, werden Referenzen eingeführt. Eine Referenz repräsentiert ein Objekt, und eine Variable speichert diese Referenz. Die Referenz hat einen Typ, der sich nicht ändern kann. Ein Auto bleibt ein Auto und kann nicht als Laminiersystem angesprochen werden. Eine Referenz unter Java ist nicht als Zeiger auf Speicherbereiche zu sehen.


Beispiel   Dass das Pfuschen in C++ leicht möglich ist, und wir Zugriff auf private Elemente über eine Zeigerarithmetik bekommen können, zeigt das folgende Programm. Für uns Java-Programmierer ist dies ein abschreckendes Beispiel.

#include <string.h>
#include <iostream.h>

class Ganz_unsicher {
public:
  Ganz_unsicher() { strcpy(passwort, "geheim"); }

private:
  char passwort[100];
};

void main()
{
  Ganz_unsicher gleich_passierts;
  char *boesewicht = (char*)&gleich_passierts;

  cout << "Passwort: " << boesewicht << endl;
}

Dieses Beispiel demonstriert, wie problematisch der Einsatz von 
Zeigern sein kann. Der Zeiger, der zunächst als Referenz auf die 
Klasse Ganz_unsicher gedacht war, mutiert durch die explizite 
Typumwandlung zu einem Char-Pointer boesewicht. Problemlos können über 
diesen die Zeichen byteweise aus dem Speicher ausgelesen werden. Dies 
erlaubt auch einen indirekten Zugriff auf die privaten Daten.

In Java ist es nicht möglich, auf beliebige Teile des Speichers zuzugreifen. Auch sind private Variablen erst einmal sicher. (Ganz so richtig ist das aber auch nicht. Mit Reflection lässt sich da schon etwas machen.) Aber zumindest würde der Compiler eine Fehlermeldung geben oder das Laufzeitsystem eine Ausnahme (Exception) auslösen, wenn ein Attributzugriff versucht wird.


Galileo Computing

1.3.5 Bring den Müll raus, Garbage-Collector  downtop

In Programmiersprachen wie C++ lässt sich etwa die Hälfte der Fehler auf falsche Speicher-Allokation zurückführen. Arbeiten mit Objekten heißt unweigerlich: Anlegen und Löschen. Die Java-Laufzeitumgebung sorgt sich jedoch selbstständig um die Verwaltung dieser Objekte – die Konsequenz ist: Sie müssen nicht freigegeben werden, ein Garbage-Collector (kurz GC) entfernt sie. Der GC ist Teil des Laufzeitsystems von Java. Das Generieren eines Objekts in einem Block mit anschließender Operation zieht eine Aufräumaktion des GCs nach sich. Nach Verlassen des Wirkungsbereichs erkennt das System das nicht mehr referenzierte Objekt. Ein weiterer Vorteil des GCs: Bei der Benutzung von Unterprogrammen werden oft Objekte zurückgegeben, und in herkömmlichen Programmiersprachen beginnt wieder die Diskussion, welcher Programmteil das Objekt jetzt löschen muss oder ob es nur eine Referenz ist. In Java ist das egal, auch wenn ein Objekt nur Rückgabewert einer Methode ist (anonymes Objekt).

Der GC ist ein spezieller Thread-Prozess, der Objekte markiert, auf die nicht mehr verwiesen wird. Dann entfernt er sie von Zeit zu Zeit. Damit macht der Garbage-Collector die Funktionen free() aus C oder delete() aus C++ überflüssig. Wir können uns über diese Technik freuen, denn viele Probleme sind damit verschwunden. Nicht freigegebene Speicherbereiche gibt es in jedem größeren Programm, und falsche Destruktoren sind vielfach dafür verantwortlich. An dieser Stelle sollte nicht verschwiegen werden, dass es auch ähnliche Techniken für C(++) gibt.


Galileo Computing

1.3.6 Ausnahmenbehandlundowntop

Java unterstützt ein modernes System, um mit Laufzeitfehlern umzugehen. In der Programmiersprache wurden Exceptions eingeführt: Objekte, die zur Laufzeit generiert werden und einen Fehler anzeigen. Diese Problemstellen können durch Programmkonstrukte gekapselt werden. Die Lösung ist in vielen Fällen sauberer als die mit Rückgabewerten und unleserlichen Ausdrücken im Programmfluss. In C++ gibt es ebenso Exceptions, diese werden aber nicht so intensiv wie in Java benutzt.

Aus Geschwindigkeitsgründen wird die Überwachung von Array-Grenzen (engl. Range-Checking) in C(++) nicht durchgeführt. Und der fehlerhafte Zugriff auf das Element n + 1 eines Felds der Größe n kann zweierlei bewirken: Ein Zugriffsfehler tritt auf oder, viel schlimmer, andere Daten werden beim Schreibzugriff überschrieben, und der Fehler ist nicht nachvollziehbar. Schon in PASCAL wurde eine Grenzüberwachung mitcompiliert. Das Laufzeitsystem von Java überprüft automatisch die Grenzen eines Arrays. Diese Überwachungen können nicht, wie es diverse PASCAL-Compiler erlauben, abgeschaltet werden, sondern sind immer eingebaut. Eine clevere Laufzeitumgebung kann herausfinden, ob keine Überschreitung möglich ist, und diese Abfrage dann wegoptimieren.


Galileo Computing

1.3.7 Objektorientierung in Java  downtop

Die Sprache Java ist nicht bis zur letzten Konsequenz objektorientiert, so wie Smalltalk es vorbildlich zeigt. Primitive Datentypen (beispielsweise Ganzzahlen oder Fließkommazahlen) werden nicht als Objekte verwaltet. Der Grund ist vermutlich in der Performance zu sehen. Der Compiler ist somit besser in der Lage, die Programme zu optimieren.

Java ist als Sprache entworfen worden, die es einfach machen soll, fehlerfreie Software zu schreiben. In C-Programmen erwartet uns statistisch gesehen alle 55 Programmzeilen ein Fehler. Selbst in großen Softwarepaketen, die erst ab einer Million Codezeilen anfangen, findet sich, unabhängig von der zugrunde liegenden Programmiersprache, im Schnitt alle 200 Programmzeilen ein Fehler. Selbstverständlich gilt es, diese Fehler zu beheben, obwohl bis heute noch keine umfassende Strategie für die Softwareentwicklung im Großen gefunden wurde. Viele Arbeiten der Informatik beschäftigen sich mit der Frage, wie Tausende Programmierer über Jahrzehnte miteinander arbeiten und Software entwerfen können. Dieses Problem ist nicht einfach zu lösen und wurde im Zuge der Softwarekrise in den Sechzigerjahren heftig diskutiert.


Galileo Computing

1.3.8 Java-Security-Model  downtop

Das Java-Security-Model gewährleistet den sicheren Programmablauf auf den verschiedensten Ebenen. Der Verifier liest Code und überprüft die strukturelle Korrektheit und Typsicherheit. Der Klassenlader (engl. class loader) lädt Dateien entweder von einem externen Medium wie Festplatte oder auch Netzwerk und überträgt die Java-Binärdaten zum Interpreter. Dort überwacht ein Security-Manager Zugriffe auf das Dateisystem, die Netzwerk-Ports, externe Prozesse und die Systemressourcen. Treten Sicherheitsprobleme auf, so werden diese durch Exceptions zur Laufzeit gemeldet. Das Sicherheitsmodell ist vom Programmierer erweiterbar.


Galileo Computing

1.3.9 Wofür Java nicht geeignet ist  toptop

Java-Fanatiker sehen oft nicht, dass Java zwar eine Programmiersprache ist, die für große Anwendungsgebiete geeignet ist, aber auch nicht für alle. Jede Programmiersprache hat ihren Platz – ja, auch Perl.

In erster Linie kann ein Projekt mit Java nicht durchgeführt werden, wenn Eigenschaften gefordert werden, die in Java nicht machbar sind. Java ist plattformunabhängig entworfen worden, sodass alle Funktionen auf allen Systemen lauffähig sind. Benutzerrechte etwa können von Java nicht erfragt oder modifiziert werden, da schon die Rechteverwaltungen von Unix und Windows total anders aussehen. Besonders systemnahe Eigenschaften wie Taktfrequenz sind nicht sichtbar und sicherheitsproblematische Manipulationen wie der Zugriff auf bestimmte Speicherzellen (das PEEK und POKE) sind ebenso untersagt. Weitere Beschränkungen sind:

gp  Anzahl freier Bytes im Dateisystem, Laufwerkstyp erkennen (CDRom), CD auswerfen, Verknüpfungen folgen
gp  Bildschirm auf der Textkonsole löschen, Cursor positionieren und Farben setzen
gp  Grafische Applikationen mit einem Tray Icon ausstatten
gp  Auf niedrige Netzwerk-Protokolle wie ICMP zugreifen
gp  Zugriff auf USB oder Firewire

Aus den genannten Nachteilen, dass Java nicht auf die Hardware zugreifen kann, folgt, dass die Sprache nicht so ohne Weiteres für die Systemprogrammierung eingesetzt werden kann. Treibersoftware, die etwa Grafikkarten oder Soundkarten ansprechen, lassen sich in Java nicht realisieren. Genau das gleiche gilt für den Zugriff auf die allgemeinen Funktionen des Betriebssystems, zum Beispiel die Funktion, die Windows, Linux oder ein anderes System bereitstellt. Typische System-Programmiersprachen sind C(++).

Aus diesen Beschränkungen heraus geht hervor, dass Java auch C(++) nicht ersetzen kann. Aber das muss die Sprache auch gar nicht! Jede Sprache hat ihr bevorzugtes Terrain und Java ist eine allgemeine Applikations-Programmiersprache – C(++) darf immer noch für virtuelle Java-Maschinen herhalten. Soll ein Java-Programm trotzdem systemnahe Eigenschaften nutzen, bietet sich zum Beispiel der native Aufruf einer Systemfunktion an. Native Funktionen sind Funktionen, die nicht in Java implementiert werden, sondern in einer anderen Programmiersprache, häufig C(++). In manchen Fällen lässt sich auch ein externes Programm mit System.exec() aufrufen und so etwa die Windows Registry manipulieren und Dateirechte setzen. Es läuft aber immer darauf hinaus, dass für jede Plattform die Lösung immer neu implementiert werden muss.






1  Die Idee des Bytecodes (FrameMaker schlägt hier als Korrekturvorschlag »Bote Gottes« vor) ist schon alt. Die Firma Datapoint schuf um 1970 die Programmiersprache PL/B, die Programme auf Bytecode abbildet. Auch verwendet die Originalimplementierung von UCSD-Pascal, etwa Anfang 1980, einen Zwischencode – kurz p-code.

2   Diese Idee ist auch schon alt: HP hatte um 1970 JIT-Compiler für BASIC-Maschinen.

3   Netscape hört es gar nicht gerne, wenn der Web-Browser als Navigator bezeichnet wird. Hier im Tutorial verwenden wir dies allerdings synonym. Die Firma versteht den Communicator als Web-Lösung, die nicht nur aus einem Web-Browser besteht. Es wird gemunkelt, dass Mitarbeiter aus der Firma rausfliegen, wenn sie das Wort »Navigator« nur in den Mund nehmen ...

4   http://smarteiffel.loria.fr/

5   Dies beweist Hob, ein portabler ZX-Spectrum-Emulator, der komplett in Java geschrieben ist. Auf der Web-Seite http://www.emuunlim.com/hob/ gibt es noch viele Spiele dazu, die als Applet ausprobiert werden können. Ebenfalls gibt es den C=64-Emulator-Versuch jc64 unter http://sourceforge.net/projects/jc64.

6   Ein bekannter Garbage-Collector stammt von Hans-J. Boehm, Alan J. Demers und Mark Weiser. Er ist unter http://reality.sgi.com/boehm_mti/gc.html zu finden. Der Algorithmus arbeitet jedoch konservativ, das heißt, er findet nicht garantiert alle unerreichbaren Speicherbereiche, sondern nur einige. Eingesetzt wird der Boehm-Demers-Weiser-GC unter anderem in der X11-Bibliothek. Dort sind die malloc()- und free()-Funktionen einfach durch neue Methoden ausgetauscht.

7   In C++ ließe sich eine Variante mit einem überladenen Operator lösen.





Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de