2. Grundlagen von Java

In diesem Kapitel werden nach einer kurzen Ausführung zur Entwicklungsgeschichte von Java die wichtigsten Eigenschaften von Java vorgestellt. Genauere Informationen zur Entwicklung von Java finden sich auf den Java-Seiten von Sun Microsystems.

2.1 Historisches

Die Entwicklung von Java begann, als Sun Microsystems 1990 ein Team von sechs Softwareentwicklern zusammenstellte, das eine Programmiersprache für Geräte der Unterhaltungselektronik, wie Videorecorder und Settop-Boxen, entwickeln sollte. Damals dachte man an Programme für das interaktive Fernsehen. Wichtigster Punkt, den das sogenannte "Green Project" bei der Entwicklung beachten mußte, war dabei die Portabilität der Programmiersprache, die mit dieser Sprache geschriebenen Programme sollten also hardwareunabhängig sein. Um nicht wieder eine völlig neue Programmiersprache zu entwickeln, die alle Softwareentwickler erst neu erlernen müßten, wurde "Oak", wie die Sprache zunächst genannt wurde, stark an C++ angelehnt.
Der Markt für das interaktive Fernsehen wuchs jedoch nicht so schnell wie erwartet, jedoch übertraf die Entwicklung des Internet durch die Einführung des World Wide Web alle Erwartungen. So wurden 1994 alle Anstrengungen von "First Person", eine Sun-Tochter, die aus dem Green Project hervorging, in die Entwicklung eines Programmiersystems für Online-Multimedia-Anwendungen gesetzt. 1995 präsentierte Sun das fertige Ergebnis mit dem Namen "Java" der Öffentlichkeit. Gleichzeitig wurde ein Softwareentwicklungssystem, das JDK (Java Development Kit) und ein WWW-Browser mit dem Namen "HotJava", der Java-Programme ausführen kann, vorgestellt.
Die Resonanz in den Fachkreisen und der Öffentlichkeit war sehr groß. Kurz nach der Präsentation von Java lizenzierten viele namhafte Soft- und Hardwarehersteller Java, darunter Netscape, IBM und Microsoft. Seither ist der Boom ungebrochen und mit der Vorstellung des JDK 1.2 Anfang 1998 ist bestimmt noch nicht der letzte Punkt erreicht.

2.2 Eigenschaften von Java

Die Programmiersprache Java besitzt einige Eigenschaften, die sie von anderen Programmiersprachen abhebt. Im folgenden wird auf die wichtigsten dieser Eigenschaften näher eingegangen.

2.2.1 Java als Programmiersprache für verteilte Applikationen

Eine der wichtigsten Eigenschaften von Java ist, daß es mit Java möglich ist, verteilte Applikationen zu erzeugen. Java-Programme können dabei aus mehreren Teilen bestehen, die sich in einem Netzwerk auf verschiedenen Rechnern befinden können. Auch die Daten müssen nicht aus der gleichen Quelle wie das Programm stammen und können sich auf unterschiedlichen Rechnern befinden. Durch diese Eigenschaft ist Java in idealer Weise dazu geeignet, um für Applikationen im Internet eingesetzt zu werden.

2.2.2 Plattformunabhängigkeit

Bevor ein Java-Programm ausgeführt werden kann, wird der Quelltext kompiliert, wobei der sogenannte Bytecode entsteht. Im folgenden Beispiel, das aus BACK (1996) entnommen wurde, ist ein kleines Java-Programm mit dazugehörigem Bytecode dargestellt:

class Loop {
static public void main (String args[])
  {
    int i;
    for (i = 0; i < 10; i++)
      System.out.println(i);       // Ausgabe der Zahlen 0 bis 9 auf die Console
  }
}

---------------------------------------
 
Compiled from loop.java
class Loop extends java.lang.Object {
public static void main(java.lang.String []);
public Loop();
Method void main(java.lang.String [])
0  iconst_0 // Lade 0 auf Stack
1  istore_1 // Lade 0 in Variable <1> (i)
2  goto 15
5  getstatic #6 <Field java.lang.System.out Ljava/io/PrintStream;> // Laedt Adresse des Print-Streams
8  iload_1 // Lade Variable <1> auf den Stack
9  invokevirtual #5 <Method java.io.PrintStream.println(I)V> // die aufgerufene Methode invalidiert den Stack vor ihrer Rückkehr
12 iinc 1 1 // i++
15 iload_1  // Lade Variable <1> auf den Stack
16 bipush 10 // 10 auf den Stack
18 if_icmplt 5 // if (I<10) goto 5
21 return // Rückkehr in Objekt, das die Message geschickt hat
Method Loop()
0  aload_0
1  invokenonvirtual #7 <Method java.lang.Object.<init>()V> // Lade Objektreferenz_<0> auf den Stack 
4  return
Der aus der Compilierung entstandene Bytecode stellt das ausführbare Java-Programm dar. Dieser Bytecode wird bei der Ausführung von der Java Virtual Machine (JVM) interpretiert. Somit sind also Java-Programme auf allen Systemen ausführbar, für die eine Java Virtual Machine existiert, zum Beispiel als Teil eines WWW-Browsers. Die Java Virtual Machine stellt dem Java-Programm ein komplettes virtuelles Computersystem zur Verfügung. Sie übernimmt dann durch die Interpretation des Bytecodes die Umsetzung des Bytecodes in Maschinencode.
Vorteile der Plattformunabhängigkeit von Java-Programmen sind unter anderem:

2.2.3 Objektorientierung

Java ist eine objektorientierte Programmiersprache. In Java wurden mit einem Klassenkonzept, Vererbung, Polymorphismus und dynamisches Binden wichtige Konzepte der objektorientierten Programmierung realisiert. Aus der Objektorientierung resultieren verschiedene Vorteile, wie die Erweiterbarkeit und die Wiederverwendbarkeit von in Java geschriebenen Programmen. Der Bereich der Objektorientierung kann und soll allerdings im Rahmen dieser Seminararbeit nur angesprochen werden.

2.2.4 Performance

Da Java-Programme zunächst kompiliert werden, wobei der Java-Bytecode entsteht, werden Java-Programme schneller abgearbeitet, als vergleichbare interpretierte Sprachen. Wie man im obigen Beispiel eines Java-Bytecodes sieht, ist der Bytecode sehr maschinennah gestaltet, wodurch der Interpreter der Java Virtual Machine die Umsetzung in Maschinencode sehr schnell vornehmen kann.
Weiterhin bietet Java die Möglichkeit, Applikationen zu entwickeln, die Multithreading unterstützen, wodurch eine weitere Möglichkeit zur (subjektiven) Geschwindigkeitssteigerung besteht. Multithreading bedeutet, daß verschiedene Teile eines Programms parallel ablaufen können. Ein Beispiel wäre das Neuzeichnen einer Grafik mit einem Grafikprogramm, während im selben Grafikprogramm eine andere Grafik gespeichert wird. Der Anwender kann also mit einer Applikation weiterarbeiten, während von der Anwendung im Hintergrund eine umfangreiche Aufgabe ausgeführt wird.
Eine weitere Möglichkeit zur Steigerung der Ausführungsgeschwindigkeit von Java-Programmen bietet das Linken von Modulen, die in einer anderen Programmiersprache, beispielsweise C++, geschrieben wurden. Dies ist allerdings nur unter dem Verlust der Portabilität möglich.

2.2.5 Leichte Erlernbarkeit

Bei der Entwicklung von Java wurde vor allem darauf geachtet, daß ein Programmierer, der schon Erfahrungen mit anderen Programmiersprachen hat, schnell lernen kann, Java-Programme zu schreiben. Aus diesem Grund ist Java von der syntaktischen und lexikalischen Struktur stark an C++, der verbreitesten objektorientierten Programmiersprache, angelehnt.
In Java wurden jedoch einige kritische Punkte von C++ weggelassen beziehungsweise vereinfacht. So wurde in Java zum Beispiel keine Zeigerarithmetik implementiert, da daraus viele potentielle Fehlerquellen entstünden. Als plattformunabhängige Programmiersprache ist es mit Java dem Programmierer nicht möglich, direkten Zugriff auf den Speicher des Rechners zu erlangen. Auch die zusammengesetzten Datentypen von C++ (struct, union) wurden in Java weggelassen, da die gleiche Funktionalität auch mit Klassen möglich ist. Weiterhin wurde der Mechanismus der Mehrfachvererbung (Polymorphismus) geändert. Eine Auflistung der wichtigsten Unterschiede zwischen Java und C++ zeigt folgende Tabelle:
 
Feature Java C++
Zeiger - int *a,b; a=&b;
Zeichen 16-Bit-Unicode Byte
Zeichenketten String-Objekte Arrays
Boolesche Werte Boolean ist Basisdatentyp -
Felder (Arrays) Felder sind Objekte, es findet eine Überprüfung der Grenzen statt (bound-checking) statische Allozierung des Speichers, es findet kein bound-checking statt
Aufzählungstyp - enum {...};
Typdefinition - typedef {...};
Zusammengesetzte Datentypen - struct a {...}; 
union b {...};
Preprocessor - vorhanden
Einbinden von Modulen import <interface> #include
Mehrfachvererbung class a extends b implements c {...} class a: public b, public c {...}
Templates - template <class a>
Überladen von Operatoren - möglich
Thread-Support vorhanden -
Heap-Verwaltung Automatische Garbage Collection im Hintergrund Manuelle Freigabe von Blöcken durch den Programmierer
Tabelle 2.1: Unterschiede zwischen Java und C++ (Quelle: BACK 1996)

2.2.6 Sicherheit

Bei einer Programmiersprache, deren Programme hauptsächlich durch ein Netzwerk (dem Internet) vertrieben werden sollen, ist es besonders wichtig, Wert auf ein umfassendes Sicherheitskonzept zu legen. Die Entwickler von Sun wollten somit vermeiden, daß man sich mit einem Java-Programm einen Virus oder ein trojanisches Pferd auf den Rechner lädt. Das Sicherheitskonzept besteht aus folgenden Komponenten:

Sprachgestaltung von Java

Schon bei der Sprachgestaltung von Java wurde versucht, potentielle Risiken so weit wie möglich auszuschalten. So ist beispielsweise durch die fehlende Zeigerarithmetik kein Zugriff auf den Hauptspeicher des Rechners möglich, wodurch einige Mißbrauchsmöglichkeiten ausgeschlossen werden. Weiterhin erfolgen die Funktions- und Variablenaufrufe über den Namen und nicht über eine Speicheradresse, so daß auch hier Gefahrquellen weitgehend ausgeschlossen sind.

Bytecode-Prüfungen

Der Java-Compiler überprüft nur die syntaktische Korrektheit eines Java-Programms. Außerdem kann der Bytecode theoretisch nach der Kompilierung noch geändert werden. Vor dem Start eines Java-Applets bzw. einer Java-Anwendung wird der Bytecode daher durch einen Bytecode-Checker überprüft. Damit soll sichergestellt werden, daß nur korrekte Member-Variablen und -Methoden in den Bytecode-Interpreter gelangen.
Zunächst werden die vom Compiler erstellten Prüfsummen auf ihre Korrektheit überprüft. Weiterhin wird sichergestellt, daß die Datentypen aller Parameter einer Funktion mit den in den Funktionsaufrufen verwendeten übereinstimmen. Eventuell wird dabei eine Casting-Operation durchgeführt. Es erfolgt auch eine Überprüfung, ob Variablen oder -Methoden verwendet werden, die auf Objekte verweisen, auf die sie keinen Zugriff besitzen.
Das Java-System ist somit in der Lage festzustellen, ob der Bytecode eines Java-Programms im Nachhinein (absichtlich oder unabsichtlich) verändert wurde. Dadurch verringert sich zum einen die Gefahr, daß ein Java-Programm durch fehlerhafte Anweisungen abstürzt, zum anderen wird es dadurch sehr schwierig, durch Veränderungen am Bytecode ein Virus zu erstellen.

Class-Loader

Der Class-Loader ist für die sogenannte Class Name Space verantwortlich. Die Class Name Space ist der Speicherplatz für Java-Klassen. Das Java-System unterscheidet zwischen Systemklassen und importierten Klassen. Systemklassen stellen die Schnittstelle zum lokalen Rechnersystem dar und haben daher alle Möglichkeiten, auf das Rechnersystem zuzugreifen. Importierte Klassen hingegen werden als Teile eines Java-Applets aus dem Internet geladen und haben nur eingeschränkte Zugriffsrechte. Aus diesem Grund werden die Systemklassen und importierten Klassen getrennt voneinander gespeichert.
Beim Aufruf einer Methode oder Verweis auf eine Variable wird zunächst die Name Space der Systemklassen, dann die Name Space der importierten Klassen durchsucht. Dadurch wird verhindert, daß ein Applet als Systemklasse angesehen wird.

Beschränkter Zugriff auf Systemressourcen

Je nach Herkunft des Java-Programms wird der Zugriff auf die Systemressourcen des lokalen Rechnersystems eingeschränkt. Hierbei wird zwischen den Applikationen, also den Stand-Alone-Programmen und den Applets, die im Rahmen einer HTML-Seite aus dem Internet geladen werden unterschieden. Für die Applikationen, gelten wie auch bei anderen Programmiersprachen keinerlei Einschränkungen, wohingegen den Applets der Zugriff auf die Systemressourcen, wie zum Beispiel die Festplatte, verwehrt bleibt. Die Applets laufen dabei in der sogenannten Sandbox ab (vgl. Abb. 2.2)


Abb. 2.2: Zugriff von Applikationen und Applets auf die Systemressourcen (Quelle:  LUCKHARDT 1997)

Weiterhin besitzen Applets nicht das Recht, fremde Programme, wie zum Beispiel ein Festplattenformatierungsprogramm, auf dem Rechnersystem, auf dem sie laufen, zu starten. Auch Netzwerkverbindungen dürfen nur zu dem Rechner aufgebaut werden, von dem das Applet geladen wurde.

Probleme des Java-Sicherheitskonzepts

Das hohe Niveau an Sicherheit, das Java bietet, schafft auch einige Probleme: Zum einen erfordern die vielen Prüfungen einen gewissen Zeitaufwand, so daß die Ausführungsgeschwindigkeit der Java-Programme leidet. Zum anderen ist es schwierig, vernünftige Anwendungsgebiete für Applets zu finden, wenn diesen der Zugriff auf das lokale Rechnersystem völlig verwehrt bleibt. Dieses Problem wurde jedoch erkannt und zur Lösung werden mit dem JDK 1.2 signierte Applets eingeführt. Ein Applet kann dabei mit einer digitalen Signatur versehen werden, womit Integrität und Herkunft eines Applets zweifelsfrei überprüft werden können. Ein solches Applet kann dann von dem Anwender die Freigabe bestimmter Ressourcen auf dem lokalen Rechnersystem verlangen. Da die Herkunft und die Integrität des Applets gesichert ist, geht der Anwender mit der Freigabe bestimmter Ressourcen nur noch ein sehr geringes Risiko ein.

2.3 Integration von Java-Applets in eine Webpage

Die Integration eines Java-Programms in eine Webpage gestaltet sich dank des neuen HTML-Tags <APPLET> sehr einfach. Ein Applet bekommt dabei einen bestimmten Teil der Seite zugewiesen, in dem es "residiert". Folgendes Beispiel soll dies verdeutlichen:

<HTML>
<HEAD>
<APPLET CODE=a.class width=200 height=200>
<PARAM name=param1 value=21>
</APPLET>
...
</HTML>

In diesem Beispiel bekommt das Applet mit dem Namen "a" einen Bereich von 200 mal 200 Punkten auf der HTML-Seite zugewiesen. Wie man sieht, ist auch eine Parameterübergabe an das Applet möglich. Hier wird dem Parameter mit dem Namen "param1" der Wert "21" zugewiesen. Der restliche Teil des HTML-Codes ist genauso aufgebaut wie bei einer HTML-Seite ohne ein Java-Applet. Eine große Zahl an Applets findet sich zum Beispiel unter http://java.sun.com/applets/
Ein Vorteil von Java bleibt dabei, daß nur der Bytecode über das Netz übertragen wird, den Sourcecode kann also niemand sehen. Damit ist das geistige Eigentum des Entwicklers weitgehend geschützt.

[Zurück]          [Inhalt]          [Weiter]