Auch Funktionen bzw. Prozeduren sollten eingeschränkt verwendet werden. Es macht keinen Sinn, einen Parameter an eine Funktion zu übergeben, der zwar dem richtigen Datentyp angehört, aber eine andere Bedeutung hat.
Als Beispiel sollen Funktionen zur Dateibearbeitung in C dienen.
fopen()
liefert eine Adresse einer für das Anwendungsprogramm
unbedeutenden Datenstruktur. Diese repräsentiert dann die offene Datei und
wird bei Bedarf an andere Funktionen wie fread()
,
fwrite()
und fclose()
übergeben.
Man kann nicht verhindern, daß außerhalb dieser Funktionen auf die
von fopen()
gelieferte Struktur zugegriffen wird, obwohl das nicht
deren Sinn ist. Sie soll nur innerhalb der Funktionen verwendet werden.
Man kann ebensowenig verhindern, daß die Adresse einer im Anwendungsprogramm konstruierten Struktur an die Funktionen übergeben wird, solange deren Datentyp eingehalten wird. Das ist natürlich erst recht nicht Sinn der Struktur.
Andere Dateibearbeitungs-Funktionen open()
, read()
,
write()
und close()
verwenden Integerwerte zur
Identifikation offener Dateien. Dies verhindert zwar, eine selbstkonstruierte
Struktur verwenden zu können, aber dafür können beliebige
und damit ungültige Integerwerte angegeben werden.
Dies könnte, wenn überhaupt, nur zur Laufzeit des Programms überprüft werden.
Eine zu einer Klasse gehörende Funktion nennt man Methode. Eine entsprechende Variable nennt man Attribut.
Eine Methode wird nicht wie eine Funktion aufgerufen, sondern sie wird auf ein Objekt angewandt.
Objekte werden immer explizit erzeugt. Dazu hat eine Klasse Konstruktoren. Diese sind Methoden ähnlich, haben jedoch keinen Namen und liefern niemals Rückgabewerte.
Objekte werden nicht explizit zerstört, wenn sie nicht mehr benötigt werden. Stattdessen «verschwinden» sie nach deren Verwendung automatisch.
Damit Methoden und Attribute, die nur innerhalb einer Klasse verwendet werden sollen, vor dem Zugriff von außen geschützt werden können, kann man sie als privat festlegen. Auch von außen verwendbare Methoden und Attribute nennt man öffentlich.
Beispiel :
Zur Dateibearbeitung gebe es eine Klasse, die zur Bearbeitung nötige Methoden bereitstellt. Um eine Datei zu bearbeiten, wird wird die Klasse instanziiert. Dabei wird einer ihrer Konstruktoren angewandt.
Auf das dabei entstehende Objekt lassen sich dann die Methoden der Klasse anwenden, um die nötige Bearbeitung durchzuführen. Diese Methoden sind öffentlich. Möglicherweise hat die Klasse private Methoden, die innerhalb der (öffentlichen) Methoden verwendet werden.
Die Klasse wird auch Attribute besitzen, die den jeweiligen Zustand der Datei darstellen. Diese werden jedoch nur innerhalb der Methoden verwendet und sind daher privat.
Dateien können ausschließlich auf die beschriebene Weise bearbeitet werden, weil die dafür notwendigen Methoden nur auf Objekte dieser Klasse angewandt werden können und diese Objekte wiederum nur mit dieser Klasse erzeugt werden können.
Um dieses Problem zu lösen, kann man Klassen ableiten.
Klasse B
werde definiert als Ableitung der Klasse A
.
Dann ist B
eine Unterklasse von A
und
A
die Oberklasse von B
.
B
erbt alle Methoden und Attribute von A
,
kann sie jedoch überschreiben. Deren Sinn bleibt in jedem Fall
erhalten.
Außerdem kann B
zusätzliche Methoden und Attribute
definieren.
Es hat keinen Sinn, Konstruktoren zu erben, deshalb definiert jede Klasse ihre eigenen.
Objekte von B
können wie Objekte von A
betrachtet und verwendet werden, andersherum jedoch nicht.
Überschreibt B
eine Methode x()
von
A
und wird ein Objekt von B
als Objekt
von A
verwendet und darauf die Methode x()
angewandt, so wird die von B
überschriebene Methode
x()
verwendet.
Eine Klasse kann beliebig viele Unterklassen haben, eine Klasse von mehreren Oberklassen abzuleiten ist jedoch problematisch, weil Konflikte auftreten können.
Viele solcher voneinander abgeleitenen Klassen bilden eine Klassenhierarchie.
Die obigen Beispiele könnten durch eine solche Hierarchie wie folgt implementiert werden :
Die unterschiedlichen Dateiarten werden als Unterklassen einer gemeinsamen Dateiklasse definiert. Das erwähnte Programm (bzw. Methode) bekommt jeweils ein Objekt einer dieser Unterklassen übergeben. Dieses wird also als ein Objekt der (gemeinsamen) Oberklasse betrachtet. Deren Ausgabemethoden werden dann verwendet. Diese wiederum werden in den jeweiligen Unterklassen unterschiedlich definiert.
Für die graphische Oberfläche definiert man eine sehr allgemeine Element-Klasse, die alle graphischen Elementarten repräsentiert. Davon wird die Klasse für das Textanzeigefeld abgeleitet und die Darstellungsmethode(n) überschrieben. Davon wiederum wird eine allgemeine Knopf-Klasse abgeleitet, die die Möglichkeit der Betätigung bietet. (Diese alleine ist nicht besonders sinnvoll.) Davon wiederum werden die beiden Knopf-Klassen PushButton und ToggleButton abgeleitet und die Methoden zur Steuerung bzw. Betätigung überschrieben.
Einige Fundamentale Klassen, die zum Sprachumfang gehören, haben Native Methoden, die innerhalb der JVM implementiert sind.
Durch Konfiguration der JVM kann der Zugriff auf Systemressourcen eingeschränkt werden. Dies dient zur Sicherheit bei der Verwendung von fremden Programmen. Das ist insbesondere sinnvoll zur Verwendung in Browsern in Form von Applets, aber auch in Webservern als Servlets, in Application Servern oder als eingenständige Programme.
Benötigte Klassen werden durch einen Class Loader zur Laufzeit
gesucht und geladen.
Sie werden häufig als Class Files (*.class
) oder in
Form von Archivdateien als Java Archives (*.jar
)
bereitgestellt, können jedoch auch z. B. über ein Netzwerk
geladen werden. Dies ist insbesondere für Applets hilfreich.
Der Programmcode in den Klassen, der in der JVM ausgeführt wird, nennt sich Byte Code. Er ist architekturunabhängig und wird normalerweise mittels eines Compilers aus Java-Quelltext erzeugt, kann aber auch z. B. durch einen Byte Code Assembler erzeugt werden.
Es stehen folgende Primitive Datentypen zur Verfügung :
Bezeichnung | Beschreibung |
---|---|
long
| Ganzzahl, 64 Bit, mit Vorzeichen |
int
| Ganzzahl, 32 Bit, mit Vorzeichen |
short
| Ganzzahl, 16 Bit, mit Vorzeichen |
byte
| Ganzzahl, 8 Bit, mit Vorzeichen |
char
| Schriftzeichen, Unicode, 16 Bit, ohne Vorzeichen |
float
| Gleitkomma, 32 Bit, IEEE 754 |
double
| Gleitkomma, 64 Bit, IEEE 754 |
boolean
| Wahrheitswert (true oder false )
|
Primitive Datentypen sind keine Klassen.
Objekte werden ausschließlich über Referenzen verwendet.
Daher gibt es für jede Klasse den entsprechenden Referenz-Datentyp.
Das Symbol null
ist als Wert für alle Referenzen geeignet.
Klassen werden in Pakete zusammengefaßt. Dadurch braucht ein Klassenname nur innerhalb eines Paketes eindeutig zu sein.
Klassen können importiert werden. Dadurch lassen sie sich im Folgenden ohne Paketnamen verwenden.
Eine Klasse wird nach folgendem Schema definiert :
package mypkg.testpkg; import java.lang.String; import java.io.File; public class MyClass extends OtherClass { ... // Definitionen von Feldvariablen und Methoden }
Die Klausel extends
bewirkt eine Ableitung der Klasse.
Fehlt sie, so wird die Oberklasse Object
angenommen.
Diese stellt die Spitze der Hierarchie aller Klassen dar.
Feldvariablen werden mit Datentyp deklariert :
private int myVariable; // einfache Integer-Variable private File myFile; // Referenz auf Objekt der Klasse File
Methoden werden mit Return-Datentyp und Parameterliste definiert :
public boolean myMethod (int parm1, File parm2) throws MyException { ... // Programmcode }
Der Zusatz throws
erklärt, daß die Methode eine
Exception auslösen kann.
Exceptions sind kontrollierte Abbrüche des Programmablaufs.
Sie können ausgelöst und abgefangen werden.
Sie werden durch besondere Klassen repräsentiert.
Feldvariablen und Methoden können als static
deklariert
werden. Dann beziehen sie sich nicht auf Objekte, sondern auf die Klasse selber
und können daher nicht auf Objekte angewandt werden.
public class Math { public static double sin (Double val) { ... } }
Methoden können auch als abstract
deklariert werden.
Dabei wird die Methode nicht definiert, sondern nur deklariert. Die betreffende
Klasse muß dann auch abstract
deklariert werden :
public abstract class Maker { public abtract void make (); }
Abstrakte Klassen können nicht instanziiert werden, können jedoch als Oberklasse zur Ableitung dienen. Eine nicht abstrakte Unterklasse muß dann alle abstrakten Methoden überschreiben :
public class MyClass extends Maker { public void make () { makeSomething (); } }
Konstruktoren werden wie Methoden, aber mit dem Klassennamen und ohne Datentyp definiert :
public MyClass (File f) throws MyException { ... // Programmcode }
Lokale Variablen werden ähnlich wie in C deklariert :
int x; // einfache Integer-Variable File f; // Referenz auf Objekt der Klasse File
Diverse Konstrukte stehen zur Verfügung :
// Zuweisung x = 123; // if-else if (a == b) doThis (); else doThat (); // while while (n > 0) processElement (n); // do-while do processNext () while (n > 0); // for for (i = 0; i < n; i++) handleIndex (i); // Block { i = 0; while (i < n) handleIndex (i++); } // Objekt erzeugen f = new File ("my.conf"); // Methode auf Objekt anwenden f.close (); // Statische Methode auf Klasse anwenden y = Math.sin (x); // Exception auslösen throw new FileNotFoundException ("my.conf not found in config path"); // Exception abfangen try { f.open (); // Diese Methode kann eine Exception auslösen } catch (FileNotFoundException ex) { cleanupFiles (); throw ex; // "Weiterreichen" der Exception } finally { alwaysDoThis (); } // return return i;
Innerhalb einer Methode oder eines Konstruktors referenziert this
das Objekt, auf das die Methode angewandt wird, bzw. das Objekt,
das erzeugt wird.
Durch super
können die Methoden bzw. Konstruktoren der
Oberklasse verwendet werden. Dies sollte in Konstruktoren von Unterklassen
geschehen.
Neben Klassen gibt es in Java Interfaces. Diese sind abstrakten Klassen mit ausschließlich abstrakten Methoden ähnlich. Eine Klasse kann beliebig viele Interfaces implementieren, indem sie deren Methoden definiert :
public interface Callable { public void call (); } public class Caller implements Callable { public void call () { doSomething (); } }
Referenzen können nicht nur mit Klassen, sondern auch mit Interfaces deklariert werden. Solche Referenzen können mit Objekten aller Klassen, die das entsprechende Interface implementieren, verwendet werden :
Callable c = new Caller (); c.call ();
Siehe auch die Einführung in die Programmierung in Java.