Alle Variablen und Elemente von Objekten werden in Java automatisch initialisiert. Variablen werden bei ihrer Definition initialisiert, Elemente von Objekten beim Aufruf des Konstruktors.
Anfangswerte für die einzelnen Typen:
Typ |
Anfangswert |
boolean |
false |
char |
'\u000' |
Ganzzahl (int, byte, short, long) |
0 |
Gleitkomma |
+0.0 |
andere Referenzen |
null |
1. Instanzvariablen:
Werden initialisiert, wenn ein Objekt erzeugt wird. Für eine komplexere
Initialisierung als mit den einfachen Variablenstartwerten gibt es Konstruktor-Methoden,
die ausgeführt werden, wenn eine neue Instanz der Klasse erzeugt wird.
2. Klassenvariablen:
Werden initialsiert, wenn die Klasse das erste Mal geladen wird. Man kann
Initialisierungsmethoden für Klassenvariablen schreiben (sog. statische
Initialisierer), die benötigt werden für Klassen, die native-Methoden
implementieren.
3. lokale Variablen:
Sind nur im Rumpf der Schleife und während der Initialisierung gültig.
Beispiel für 1. und 2.:
class A {
// Klassenvariable
static int Kvar;
// Instanzvariable
int ivar; }
Beispiel für 3.:
for (int i = 0; i < 100; i++);
System.out.println(i);
=> Compilerfehler, weil die lokale Variable nur bis vor dem Semikolon Gültigkeit hat
Als einfache Datentypen werden die vordefinierten Datentypen bezeichnet. Objekte und Arrays werden als nicht-einfache Datentypen bezeichnet bzw. als Referenzdatentypen.
In Java gilt:
Die Änderung einer Referenz innerhalb einer Funktion hat keine
Wirkung nach "außen":
=> clonen
=> "per Hand"
Beispiel:
Weil Objekte per Referenz übergeben werden, können 2 Variable
dasselbe Objekt referenzieren:
Button p, q;
p = new Button();
// p referenziert ein Button Objekt
q = p;
// q referenziert denselben Button
p.setLabel("OK");
// eine Änderung im Objekt durch p ...
String s = q.getLabel();
// ...ist auch durch q sichtbar, s ="OK"
Dies gilt nicht für einfache Typen:
int i = 3;
// i enthält den Wert 3
int j = i;
// j enthält eine Kopie des Wertes in i
i = 2;
// eine Änderung von i bewirkt keine Änderung von j; i == 2,
j == 3
Button a = new Button("Okay");
Button b = new Button("Cancel");
a = b;
=> die Variable a enthält eine Referenz auf das Objekt, welches
von b refereziert wird
=> das Objekt das a referenziert hat, ist verloren
Um Daten von einem Objekt in ein anderes zu kopieren, benutzt man die Methode clone(). Die clone()-Methode erzeugt ein neues Objekt desselben Typs wie das Ursprungsobjekt und initialsiert die Datenfelder des neuen, klonierten Objektes mit den aktuellen Werten der Datenfelder des Ursprungsobjektes:
Vector b = new Vector;
c = b.clone();
=> die Variable c enthält ein Duplikat des von b referenzierten Objektes
Gründe:
1. Möglichkeit: mit new
Button buttons[] = new Button [10]
=> die Elemente des Arrays werden auf den Standardwert ihres Typs
initialisiert
=> int_Array-Elemente auf 0 initialisiert
=> die Arrays von Objekten auf null initialisiert
2. Möglichkeit:
dynamisches Anlegen eines Arrays und Initialisierung mit den angegebenen
Werten
int look_up table[] =[1,2,4,8,16];
Ein mehrdimensionales Array wird mit new angelegt, indem die entsprechende Anzahl von Elementen in [] für jede Dimension angegeben wird:
byte TwoDimArray[][] = new byte[256][16];
Bei allen Arrayreferenzen wird der Index überprüft. Liegt der Index außerhalb der Grenzen, so wird eine ArrayIndexOutOfBoundsExecption erzeugt.
Die Größe eines Arrays ist nicht Teil seines Typs, d.h. es kann eine Variable deklariert werden, die z. B. vom Typ String[] ist, und ihr ein Array aus String- Objekten zuweisen, egal wie lang dieses Array ist:
String[] strings;
// diese Variable weist auf ein String-Array
strings = new String[10];
// das nur 10 Strings enthält
// oder 20
strings = new String[20];
wie in C++
Folgende Operatoren werden in Java nicht unterstützt:
Neu:
public void quaff (Coffee joe)
{
//...
if (joe instanceof Mocha) {
Mocha fancy = (Mocha) joe;
//... verwende Mochas Funktionalität
}
}
Wenn eine Variable in einen anderen Typen konvertiert werden soll, so muß ihr einfach der gewünschte Typ in Klammern vorangestellt werden:
double x = 674.14;
int i = (int)x;
Bei Konvertierungen muß berücksichtigt werden, daß mit der Konvertierung ein Verlust an Genauigkeit sowie eine Einschränkung des Wertebereiches einhergehen kann. Es gibt folgende Regeln:
wie in C++
Unterschied:
null und 0 sind in Java nicht gleich false, und Werte ungleich 0 und
nicht-null Werte sind nicht das gleiche wie true
Es gibt kein goto-Konstrukt.
In Java gibt es kein Gegenstück zum new-Operator, das ein Objekt
wieder entfernt. Auch Destruktoren im Sinne von C++ gibt es nicht. An Stelle
expliziter Freigabe von Objekten gibt es einen Mechanismus, der sich automatisch
darum kümmert: der Garbage Collector.
Der Garbage Collector prüft, ob es noch Verweise auf ein Objekt gibt.
Wird ein Objekt von niemanden mehr referenziert, wird es automatisch freigegeben.
Java gibt keine Garantien, wann die Garbage Collection stattfindet oder
in welcher Reihenfolge die Objekte freigegeben werden.
Garbage Collection gibt automatisch die Speicherressourcen von Objekten
frei, aber Objekte können noch andere Ressourcen besitzen, wie Dateideskriptoren
oder Sockets. Der Garbage Collector kann solche Ressourcen nicht freigeben,
deshalb muß man finalizer-Methoden schreiben, die z. B. offene Dateien
schließen, Netzwerkverbindungen beenden.
Wenn ein Objekt einen Finalizer hat, so wird diese Methode aufgerufen,
bevor der Garbage Collector das Objekt löscht. Nach dem Aufruf des
Finalizers werden die Objekte nicht sofort freigegeben. Der Grund ist,
daß der Finalizer ein Objekt "wiederbeleben" kann, indem
es den this-Zeiger irgendwo speichert, so daß das Objekt wieder eine
Referenz hat.
Beispiel:
/* schließt einen Stream, wenn der Garbage Collector seine
Arbeit verrichtet; überprüft jedoch zuerst, ob der Deskriptor
nicht schon geschlossen ist */
protected void finalize()throws IOException
{
if (fd != null) close();
}
Die package-Anweisung dient dazu, eine Sammlung von Klassen zu einer Einheit zusammenzufassen. Um die Zugehörigkeit einer Quelldatei zu einem Package anzugeben, wird vor der Deklaration von Klassen eine Package-Anweisung in der Quelldatei eingefügt. Dem Schlüsselwort Package folgt der Package-Name.
package graphtools;
Zum Importieren eines Packages oder einer Klasse aus einem Package definiert
Java die import-Anweisung. Diese Anweisung stellt Javaklassen für
die aktuelle Klasse unter einem abgekürzten Namen zur Verfügung.
import stellt weder die Klasse zur Verfügung , noch "liest"
es sie tatsächlich ein, sondern es spart nur Schreibarbeit und macht
den Kode lesbarer.
Es ist eine beliebige Anzahl von import-Anweisungen möglich. Sie müssen
aber nach der optionalen package Anweisung am Anfang der Datei stehen und
vor der ersten Klassen oder Schnittstellendefinition in der Datei.
Es sind 3 Formen möglich:
1. Form: import package;
ermöglicht dem angegebenen Paket durch den Namen seiner letzten Komponente
angegeben zu werden
import java.awt.image
=> java.awt.image.ImageFilter kann als image.ImageFilter aufgerufen
werden
2. Form: import package.class;
ermöglicht es der angegebenen Klasse in dem angegebenen Paket nur
durch den Klassennamen benutzt zu werden
import java.util.Hashtable;
=> nur Hashtable statt java.util.Hashtable
3. Form: import package.*;
alle Klassen des Paketes werden durch ihre Klassennamen verfügbar
gemacht
import java.lang.*
=> ist implizit in jedem Javaprogramm enthalten
Werden 2 Pakete, die Klassen mit gleichen Namen haben, importiert, kommt es zu einer Fehlermeldung, wenn eine Klasse mit ihrem nicht mehr eindeutigen kurzen Namen benutzt wird.
Durch Vererbung können Klassen definiert werden, die auf einer anderen Klasse basieren. Diese neuen Klassen stellen eine Erweiterung und Spezialisierung ihrer Basisklasse dar. Die Subklasse kann neue Methoden und Elemente hinzufügen oder bestehende Methoden überschreiben. Wenn von einer Klasse einen Subklasse abgeleitet wird, muß die Subklasse im Kopf ihrer Deklaration das Schlüsselwort extends benutzen. Hinter extends muß genau eine Klasse angegeben werden.
public class A extends B
{
...
}
=> A ist eine Subklasse von B, d.h. A erbt alle Variablen und Methoden
der Klasse B, außer die als private gekennzeichneten
Situation |
public |
default |
protected |
private protected |
private |
erreichbar für nicht-Subklasse aus dem gleichen Paket? |
ja |
ja |
ja |
nein |
nein |
erreichbar für Subklasse aus dem gleichen Paket? |
ja |
ja |
ja |
nein |
nein |
erreichbar für nicht-Subklasse aus einem anderen Paket? |
ja |
nein |
nein |
nein |
nein |
erreichbar für Subklasse aus einem anderen Paket? |
ja |
nein |
nein |
nein |
nein |
geerbt von Subklasse in gleichem Paket? |
ja |
ja |
ja |
ja |
nein |
geerbt von Subklasse in einem anderen Paket? |
ja |
nein |
ja |
ja |
nein |
Jede Klasse, die definiert wird, besitzt eine Superklasse. Wenn die
Superklasse nicht in der extends-Klausel angegeben ist, so ist die Superklasse
implizit Object. Object ist die einzige Klasse, die keine Superklasse besitzt.
Methoden, die von Object definiert werden, können von jedem Java-Objekt
aufgerufen werden.
Weil jede Klasse eine Superklasse besitzt, formen Klassen in Java eine
Klassenhierarchie, die als Baum mit Object als Wurzel dargestellt werden
kann.
Aufrufen eines Konstruktors aus der Superklasse:
public GraphicCircle(double x, double y, double r, Color outline,
Color fill)
{
super(x,y,r)
this.outline = outline;
this.fill = fill;
}
Das Schlüsselwort super dient dazu, die Konstruktor-Methode der
Superklasse aufzurufen.
Der Konstruktoraufruf der Superklasse muß als 1. Anweisung innerhalb
der Konstruktor-Methode erscheinen.
Wenn die 1. Anweisung in einem Konstruktor nicht ein explizter Aufruf des
Konstruktor der Superklasse mit super ist, dann fügt Java implizit
den super()-Aufruf ein:
Weil der Superklassen-Konstruktor immer zuerst aufgerufen wird, wird
immer als erstes der Object-Konstruktor aufgerufen, gefolgt von der Subklasse
und der Klassenhierarchie hinunter zu der Klasse, die instanziert wurde.
Ist kein Konstruktor in einer Klasse deklariert, wird der Standardkonstruktor
aufgerufen, der wiederum nichts anderes macht, als den Superklassenkonstruktor
aufzurufen.
Eine abstract-Methode in Java entspricht in etwa einer "rein virtuellen" Funktion in C++, d.h. eine virtuelle Funktion, die als = 0 deklariert ist. Javaklassen, die abstract-Methoden enthalten, können nicht instanziert werden. Ist eine Methode abstract definiert, muß sie nicht implementiert werden.
Regeln für abstract-Methoden und abstract-Klassen:
In Java dürfen Klassen nur eine Superklasse besitzen, das bedeutet,
daß die von C++ bekannte Mehrfachvererbung nicht möglich ist.
Statt dessen kennt Java die sogenannten Interfaces. Das sind reine Schnittstellen,
die keinerlei Implementierungen enthalten.
Eine Schnittstelle sieht aus wie eine abstrakte Klasse, außer, daß
sie das Schlüsselwort interface statt den Wörtern abstract und
class benutzt.
Alle Methoden in einer Schnittstelle sind implizit abstract. Jede Variable,
die in einer Schnittstelle deklariert ist, muß static und final sein
.
Eine Klasse kann mit dem Schlüsselwort implements eine oder mehrere
Schnittstellen vererben. Diese Klasse erbt alle Konstanten und die abstrakten
Methoden. Die Methoden müssen dann überschrieben werden.
Beispiel:
interface W{}
interface X extends W {}
class Y implements W {}
class Z extends Y implements X {}
Schnittstellen können Subschnittstellen besitzen. Eine Subschnittstelle
erbt alle abstrakten Methoden und Konstanten seiner Superschnittstelle
und kann neue abstrakte Methoden und Konstanten definieren.
Eine Schnittstelle, die mehr als eine Schnittstelle erweitert, erbt alle
abstrakten Methoden und Konstanten aller Schnittstellen und kann neue abstrakte
Methoden und Konstanten definieren.
Eine Klasse, die solch eine Schnittstelle implementiert, muß alle
abstrakten Methoden, die in der Schnittstelle selbst definiert sind, und
alle Methoden, die von allen Superschnittstellen geerbt wurden, implementieren.