Einführung in das Ueberschreiben von Deklarationen für Exemplare in Java im Rahmen der Lehre des Programmierens mit der Programmiersprache Java. (super Java super.super Java), Lektion, Seite 721612
https://www.purl.org/stefan_ram/pub/java_ueberschreiben_de (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram
Java-Kurs

Schreiben von Basisklassen und Erweiterungen in Java

Sichtbarkeit und Vererbung

public-Einträge sind von überall aus sichtbar.

protected-Einträge können nur in Unterklassen oder in Klassen aus demselben Paket gesehen werden.

Einträge ohne »public«, »protected« oder »private« können nur aus dem gesamten Paket, in dem sie deklariert sind, heraus gesehen werden. Sie werden nicht an Unterklassen aus anderen Paketen vererbt.

private-Einträge können nur aus der Klasse heraus aufgerufen werden, in der sie deklariert sind. Sie werden nicht vererbt.

Sichtbarkeit "public" "protected" (Fehlen) "private"

Klasse selber Ja Ja Ja Ja

Paket Ja Ja Ja

Unterklasse Ja Ja

Rest Ja

Sichtbarkeit in Unterklassen

Die Zugriffsmöglichkeiten auf eine Methode dürfen durch Ersetzen der Methode nicht abgeschwächt werden, sondern nur unverändert gelassen oder verstärkt werden.

Daher ist die Deklaration der Klasse "E0" der Quelldatei "Test.java" in dem folgenden Quelltext zulässig, während die auskommentierte Deklaration der Klasse "E1" nicht erlaubt ist.

Quelltext
   class B0 { void m(){} } class E0 extends B0 { public void m(){} }
// class B1 { public void m(){} } class E1 extends B1 { void m(){} }

Formales und semantisches Ersetzen

Beim semantischen Ersetzen erfüllt die Implementation einer Methode in einer Klasse die Kontrakt dieser Methode in allen Oberklassen.

Als Grundlage des folgenden Beispiels zum Ersetzen in Java  dient die Klasse "Account.java" zur Modellierung eines Kontos.

Account.java
class Account 
{ private double balance;
/** Initialize the account. */
Account(){ balance = 0.; }
/** Converts a string representation of a number 
to a double value.  
@param text the string to be converted 
@return the double value of the text */
protected static double val( final String text ) 
{ return new Double( text ).doubleValue(); }
/** Represents a double value by a string.  
@param number the number to be represented. 
@return the string representing the number */
protected static String str( final double number ) 
{ return new Double( number ).toString(); }
/** Deposit money into an account. 
@param deposit the money to be deposited into the account 
@return the money accepted and added to the balance */
String deposit( final String deposit ) 
{ double amount = val( deposit ); 
balance += amount; return str( amount ); }
/** Retrieve the balance of the account. 
@return the current balance of the account */
String balance(){ return str( balance ); }
/** Retrieve a description of the account. 
@return the description of the account */
String description(){ return "standard account"; }}

Die Klasse "Account" enthält eine Methode "description", die eine kurze Beschreibung  des Konto ergibt.

Nun ist es denkbar, daß ein solches Konto benötigt wird, aber die Beschreibung in Deutsch  sein soll. Um die gewünschte Kontoklasse herzustellen, kann die Klasse "Account" „erweitert“ werden. Dabei wird diesmal aber nicht wirklich eine Erweiterung hinzugefügt, sondern die unpassende Methode "description" ersetzt. Daher ist in diesem Fall der Name „Erweiterung“ nicht ganz passend, man könnte eher von einer „Veränderung“ sprechen, bei der allerdings das Original nicht angetastet wird, sondern eine Art von Kopie erstellt und dann teilweise verändert wird. Eine Klasse, die mit dem Schlüsselwort "extends" von einer Basisklasse abgeleitet ist und dann nur Methoden dieser Basisklasse ersetzt, wird hier auch als eine Ersetzung  bezeichnet.

Vergleiche kann man dies mit einer Situation, in der jemand wissen will, wie die Mona Lisa  aussieht, wenn sie nicht lächelt. Dazu könnte er das Lächeln der Mona Lisa  übermalen. Es kann aber wünschenswert sein, die originale Mona Lisa  nicht zu verändern, da sie auch weiterhin noch in ihrem ursprünglichen Zustand benötigt wird. Dann kann eine Kopie der Mona Lisa  angefertigt werden, die nun übermalt werden kann, während das Original weiterhin unverändert bleibt.

Wenn in der erweiternden Klasse eine Methode mit der gleichen Signatur wie eine Methode der Basisklasse deklariert wird, dann wird die durch diese Signatur bestimmte Operation von Objekten der erweiternden Klasse durch jene in der Deklaration der erweiternden Klasse deklarierte Methode implementiert. Wird also eine parameterlose Exemplarmethode "description" in der erweiternden Klasse deklariert, dann verwenden Exemplare der erweiternden Klasse diese  Methode für die Operation "description()". Dadurch kann diese Methode dort nun so definiert werden, daß sie eine deutschsprachige Kontobezeichnung zurückgibt, während alle anderen Methoden und Felder von der Basisklasse übernommen werden.

AccountGerman.java
class AccountGerman extends Account 
{ /** Retrieve a description of the account. 
@return the description of the account */
String description(){ return "Standardkonto"; }}

AccountGerman [UML class diagram]
.--------------------------------------. 
| Account | 
|--------------------------------------| 
|--------------------------------------| 
| + deposit( String ) : String | 
| + balance() : String | 
| + description() : String | 
'--------------------------------------' 

/_\ 


.--------------------------------------. 
| AccountGerman | 
|--------------------------------------| 
|--------------------------------------| 
| + description() : String | 
'--------------------------------------'

Durch diese Art der „Veränderung“ einer Klasse wird das Offen-Geschlossen-Prinzip gewahrt: Die Basisklasse "Account" war „offen“, um zur Herstellung einer anderen Klasse genutzt werden zu können; sie blieb „geschlossen“, da sie selber dabei nicht verändert wurde.

Nun kann ein Exemplar der Klasse "AccountGerman" wie ein Exemplar der Klasse "Account" verwendet werden, der Unterschied besteht nur in der Rückgabe eines deutschsprachigen Textes als Kontobeschreibung.

Test.java
public final class Test 
{ final public static void main( String[] args ) 
{ AccountGerman a = new AccountGerman(); 
a.deposit( "20" ); java.lang.System.out.println( a.description() );  
a.deposit( "10" ); java.lang.System.out.println( a.balance() ); }}

System.out
Standardkonto 
30.0

Spezifikationstreues Ersetzen

Das Ersetzen von Methoden in Erweiterungen einer Klasse sollte aber semantisch treu hinsichtlich der Spezifikation der Operation erfolgen. Das setzt natürlich voraus, daß es überhaupt eine Spezifikation gibt.

spezifikationstreue Ersetzen Zu jeder Operation einer Klasse sollte es eine Spezifikation  geben, welche neben der Signatur die Rückgabespezifikation enthält und das Verhalten (Wert und Wirkung) der Operation und seine Abhängigkeit von Umständen des Aufrufs (z.B. Argumentwerten) beschreibt. Wenn solch eine Operation einer Klasse dann in einer Unterklasse ersetzt wird, sollte die Spezifikation der Operation beachtet werden: Die Ersetzung sollte die Spezifikation der ersetzten Operation erfüllen.

Eine Forderung mit ähnlichem Sinn ist auch als Liskov-Substitutions-Prinzip  (LSP ) bekannt. Da das LSP  aber recht kompliziert formuliert ist und der Bezug auf eine Spezifikation fehlt, wird es hier nicht verwendet.

Die Spezifikation der Operation "description()" legt beispielsweise fest, daß diese eine Beschreibung des Kontos vom Typ "String" ergibt, aber nicht, in welcher Sprache diese Beschreibung geschrieben ist.

description() [specification]
/** Retrieve a description of the account. 
@return the description of the account */
String description();

Beide Implementationen von "description()" sind dieser Spezifikation treu, denn die Signatur, der Rückgabetyp und die Beschreibung werden von beiden Implementationen so verwirklicht, wie die Spezifikation dies verlangt.

Wenn man sagt, daß die Spezifikation einer Operation ihre Bedeutung festlegt, dann kann man das Treue-Ersetzung-Prinzip auch so formulieren: Durch das Ersetzen einer Operation in einer Unterklasse sollte die Bedeutung der Operation nicht verändert werden.

Was die Bedeutung  einer Operation ist, wird aber durch ihre Spezifikation  festgelegt. Wenn die Spezifikation der Operation "balance()" die Rückgabe einer englischsprachigen  Kontobeschreibung verlangen würde, dann  wäre das Ersetzen der Operation, wie in der Klasse "AccountGerman" geschehen, nicht  spezifikationstreu. Da die Spezifikation die Sprache  der Kontobeschreibung aber offenläßt, ist es spezifikationstreu, sie in Englisch auszugeben, genauso, wie es spezifikationstreu ist, die Kontobeschreibung in Deutsch auszugeben.

Der Java -Übersetzer kann nur erkennen, daß die Signatur einer ersetzten Methode mit der Signatur einer Methode der Basisklasse übereinstimmt, er kann Spezifikationen aber nicht lesen und verstehen und daher die fehlende Spezifikationstreue einer Ersetzung nicht erkennen.

Mit den Begriffen „formaler Untertyp“ und „semantischer Untertyp“ kann man dies auch so formulieren: Die Java -Implementation stellt sicher, daß eine Erweiterung einer Klasse ein formaler Untertyp  der Klasse ist, der Programmierer sollte aber darüber hinaus noch sicherstellen, daß sie auch eine semantischer Untertyp  ist.

Die Dynamische Bindung  nicht-statischer Methoden

Die Anwendung "a.description()" aktiviert die Methode "AccountGerman#description". Um die Methode "Account#description" der Oberklasse aufzurufen, könnte man nun auf die Idee kommen, den Ausdruck "( Account )a" zu verwenden, der den Typ der Oberklasse "Account" hat.

Test1.java
public final class Test1 
{ final public static void main( String[] args ) 
{ AccountGerman a = new AccountGerman(); 
java.lang.System.out.println( a.description() );  
java.lang.System.out.println( (( Account )a ).description() ); }}

System.out
Standardkonto 
Standardkonto

Doch tatsächlich wird bei der Auswertung der Anwendung "(( Account )a ).description()" wieder  die Methode "AccountGerman#description" der Klasse des Objekts  "a" und nicht der Klasse des Ausdrucks  aufgerufen. Dies liegt daran, daß die Zuordnung von Nachrichten zu nicht-statischen Methode nach dem „dynamischen“ Typ  des Empfängerobjekts und nicht nach dem „statischen“ Typ des Empfängerausdrucks vorgenommen wird. Die Formung erzeugt einen Ausdruck mit dem Typ "Account" der aber weiterhin ein Objekt des Typs "AccountGerman" referenziert. Die Formung verändert also nur den statischen Typ  eines Ausdrucks, nicht aber den dynamischen Typ  des referenzierten Objekts.

Der Ausdruck "( Account )a"
                                               _____ 
.-' '-. 
.' '. 
/ Von \  
.--------------. ; "( Account )a" ; 
| ( Account )a --------------------->| referenziertes | 
'--------------' ; Objekt ; 
\ / 
'. .' 
'-._____.-'
Account AccountGerman 
(statischer Typ) (dynamischer Typ)

Durch eine Typformung wird die Zuordnung von Nachrichten zu Exemplarmethoden nicht beeinflußt. (Es kann nur beeinflußt werden, ob bestimmte Nachrichten überhaupt akzeptiert werden.)

Das folgende Beispielprogramm zeigt die dynamische Bindung noch einmal mit allen beteiligten Klassen und mit einer main -Methode ohne Anwendungen des Selektors "System.out.println".

Test2.java
class Basis 
{ void methode(){ java.lang.System.out.println( " Basis#methode" ); }}
class Erweiterung extends Basis 
{ void methode(){ java.lang.System.out.println( "Erweiterung#methode" ); }}
public final class Test2 
{ final public static void main( final java.lang.String[] args ) 
{ Erweiterung e = new Erweiterung(); 
e .methode();  
(( Basis )e ).methode(); }}

System.out
Erweiterung#methode 
Erweiterung#methode

Der erzeugte Bytecode erweckt zunächst den Eindruck, als würde im zweiten Falle die Methode "Basis#methode" gerufen, doch tatsächlich erfolgt hier nur der Aufruf über die Angabe "Basis.methode". Die Instruktion "invokevirtual" ermittelt dann die aktivierte Methode anhand des Typs des Empfängerobjektes.

main [Bytecode, 1.4.2 beta, javap -c -l -s -verbose]
public static final void main(java.lang.String[]); 
Signature: ([Ljava/lang/String;)V
Code: 
Stack=2, Locals=2, Args_size=1 
0: new #2; //class Erweiterung
3: dup 
4: invokespecial #3; //Method Erweiterung."<init>":()V 
7: astore_1
8: aload_1 
9: invokevirtual #4; //Method Erweiterung.methode:()V
12: aload_1 
13: invokevirtual #5; //Method Basis.methode:()V
16: return

Statischer Bindung statischer Methoden (Verdeckung)

Über einen Ausdruck mit Klassentyp können auch statische  Methoden aufgerufen werden. Dies sollte vermieden werden, da die statischen Methoden konzeptuell einer Klasse und nicht einem ihrer Objekte zugehörig sind. Falls dies aber doch geschieht so wird bei Verdeckung einer statischen Methode einer Basisklasse die aufzurufende Methode über den statischen Typ  des Ausdrucks ermittelt, so daß eine Typformung hier relevant ist.

Test3.java
class Basis 
{ static void methode(){ java.lang.System.out.println( " Basis#methode" ); }}
class Erweiterung extends Basis 
{ static void methode(){ java.lang.System.out.println( "Erweiterung#methode" ); }}
public final class Test3 
{ final public static void main( final java.lang.String[] args ) 
{ Erweiterung e = new Erweiterung(); 
e .methode();  
(( Basis )e ).methode(); }}

System.out
Erweiterung#methode 
Basis#methode

Wenn eine nicht-statische  Methode in einer Erweiterung mit derselben Signatur wie eine nicht-statische  Methode in der Basisklasse deklariert wird, dann wird die nicht-statische Methode der Basisklasse ersetzt. Wenn eine statische  Methode in einer Erweiterung mit derselben Signatur wie eine statische  Methode in der Basisklasse deklariert wird, dann wird die nicht-statische Methode der Basisklasse verdeckt.

Verdeckte Methoden werden nach dem „statischen“ Typ des Empfänger-Ausdrucks ermittelt werden und ersetzte nach dem „dynamischen“ Typ des Empfänger-Objekts.

Die statische Auflösung von Überladung

Main.java

class Alpha {}
class Beta extends Alpha {}

public final class Main
{ public static String m( final Alpha alpha ){ return "Alpha"; }
public static String m( final Beta beta ){ return "Beta"; }

public static void main(String[] args)
{ final Alpha alpha = new Beta();
java.lang.System.out.println( m( alpha )); }}

transcript
Alpha

Die dynamische Auflösung von Ersetzung

Main.java

class Alpha { public String m(){ return "Alpha"; } }
final class Beta extends Alpha { public String m(){ return "Beta"; }}

public final class Main
{
public static void main(String[] args)
{ final Alpha alpha = new Beta();
java.lang.System.out.println( alpha.m() ); }}

transcript
Beta

Kovariantes Ersetzen ist beim Rückgabetyp möglich

Main.java

class Alpha { public Object m(){ return "Alpha"; } }
final class Beta extends Alpha { public String m(){ return "Beta"; }}

public final class Main
{
public static void main(String[] args)
{ final Alpha alpha = new Beta();
java.lang.System.out.println( alpha.m() ); }}

transcript
Beta

Kovariantes Ersetzen ist nicht beim Parametertyp möglich

Dieses Thema wird in einem anderen Abschnitt dieser Lektion ausführlicher behandelt.

Main.java

class Alpha { public void m( Object o ){ java.lang.System.out.println( "Alpha" ); } }
class Beta extends Alpha { public void m( String s ){ java.lang.System.out.println( "Beta" ); }}

public final class Main
{
public static void main(String[] args)
{ final Alpha beta = new Beta();
beta.m( "beta" ); }}

transcript
Alpha

Veränderung der Methodengattung

Eine Veränderung der Gattung einer Methode  ist nicht möglich. In einer Erweiterung einer Basisklasse kann also keine statische  Methode mit derselben Signatur wie eine nicht-statische  Methode der Basisklasse deklariert werden. Umgekehrt kann in einer Erweiterung einer Basisklasse kann keine nicht-statische  Methode mit derselben Signatur wie eine statische  Methode der Basisklasse deklariert werden.

Statischer Zuordnung überladener Methodennamen

Methoden mit unterschiedlichen Signaturen sind unabhängig voneinander. Wird in der Erweiterung eine Methode mit dem gleichen Namen  (Selektor) aber anderer Signatur  wie eine Methode in der Basisklasse deklariert, so beeinflussen diese Deklarationen einander nicht.

Überladung in der Erweiterung

In einer Erweiterung kann ein Methodennamen auch überladen  werden.

Test4.java
class Basis 
{ void exemplarmethode() 
{ java.lang.System.out.println( "Basis#exemplarmethode" ); }
static void klassenmethode() 
{ java.lang.System.out.println( "Basis#klassenmethode" ); }}
final class Erweiterung extends Basis 
{ void exemplarmethode( double i ) 
{ java.lang.System.out.println( "Erweiterung#exemplarmethode" ); }
static void klassenmethode( double i ) 
{ java.lang.System.out.println( "Erweiterung#klassenmethode" ); }}
public final class Test4 
{ private static void zeile( String s ) 
{ java.lang.System.out.print( s + " " ); }
public static void main( String[] args ) 
{ Erweiterung e = new Erweiterung(); 
;; zeile( "e0" ); e .exemplarmethode( 2. ); 
// zeile( "e1" ); (( Basis )e ).exemplarmethode( 2. ); 
;; zeile( "e2" ); e .exemplarmethode( ); 
;; zeile( "e3" ); (( Basis )e ).exemplarmethode( ); 
;; zeile( "k0" ); e .klassenmethode ( 2. ); 
// zeile( "k1" ); (( Basis )e ).klassenmethode ( 2. ); 
;; zeile( "k2" ); e .klassenmethode ( ); 
;; zeile( "k3" ); (( Basis )e ).klassenmethode ( ); }}

System.out
e0 Erweiterung#exemplarmethode 
e2 Basis#exemplarmethode 
e3 Basis#exemplarmethode 
k0 Erweiterung#klassenmethode 
k2 Basis#klassenmethode 
k3 Basis#klassenmethode

Die Anwendung in der Zeile "e0" aktiviert die Exemplarmethode, die in der Erweiterung definiert wurde. Die Deklaration einer Methode mit gleichem Namen aber anderer Signatur in der Basisklasse stört dies nicht.

In der Zeile "e1" wird dieselbe Nachricht wie in der vorherigen Zeile an einen Ausdruck geschickt, dessen Typ die Basisklasse ist. Obwohl der Typ des Exemplars wie zuvor ist, wird nun zur Übersetzungszeit geprüft, ob der Typ des Ausdrucks die Operation "exemplarmethode( double )" überhaupt enthält. Da dies nicht so ist, wird diese Zeile nicht übersetzt und daher dadurch auch keine Methode aktiviert (deswegen wurde diese Zeile auskommentiert).

In den beiden nächsten Zeilen wird die Exemplarmethode der Basisklasse aufgerufen.

Die Situation bei den statischen Methoden ist entsprechend.

Das Schlüsselwort "super"

Aussprachehinweise
super ˈsupɚ

Die Methode "AccountGerman#description" ergibt eine deutschsprachige Beschreibung  ihres Kontos. Es kann aber auch wünschenswert sein, hinter dieser deutschsprachigen Kurzbeschreibung die englische  Kurzbeschreibung in Klammern anzugeben. Dazu könnte die Methode "AccountGerman#description" die Methode "Account#description" aufrufen, was aber nicht direkt möglich ist, da die Anwendung "this.description()" die Methode "AccountGerman#description" selber wieder aufriefe. Auch eine Formung "( Account )this.description()" erlaubt es nicht, die Methode "Account#description" aufzurufen, da die Zuordnung der Nachricht zu einer nicht-statischen Methode immer über den dynamischen Typ des Objekts  erfolgt, der durch die Formung nicht verändert werden kann.

Um nun wirklich die Methode der Basisklasse aufzurufen, kann das Schlüsselwort "this" durch das Schlüsselwort "super" ersetzt werden, welches zwar ebenfalls für das Objekt "this" steht, aber die Nachrichten an die Basisklasse weiterleitet, auch wenn sie zu nicht-statischen Methoden gehören.

AccountGerman1.java
class AccountGerman1 extends Account 
{ /** Retrieve a description of the account. 
@return the description of the account */
String description() 
{ return "Standardkonto (" + super.description() + ")"; }}

Test5.java
public final class Test5 
{ final public static void main( String[] args ) 
{ AccountGerman1 a = new AccountGerman1(); 
a.deposit( "20" ); java.lang.System.out.println( a.description() );  
a.deposit( "10" ); java.lang.System.out.println( a.balance() ); }}

System.out
Standardkonto (standard account) 
30.0

Wenn die Basisklasse selber eine Erweiterung eines Basisbasisklasse ist, so liegt es nahe zu vermuten, daß eine in der Basisklasse ersetzte Methode der Basisbasisklasse mit dem Quelltext "super.super" oder einem ähnlichen Quelltext erreicht werden kann. Dies ist aber nicht möglich, es würde auch die Kapselung der Basisklasse zu sehr durchbrechen, da der Aufruf von dort mit Bedacht ersetzten Methoden, höchst fehleranfällig ist. Mit dem Schlüsselwort "super" kann also immer nur eine Methode der direkten Basisklasse einer Erweiterung aufgerufen werden.

Zugriff auf statische Methoden  der Basisklasse

Aus einer statischen Methode heraus kann weder das Schlüsselwort "this" noch das Schlüsselwort "super" verwendet werden, da diese immer ein aktuelles Objekt voraussetzen. Wenn aus einer statischen Methode eine verdeckte Methode der Basisklasse aufgerufen werden soll, so kann eine entsprechende Nachricht an eine Angabe der Klasse geschickt werden, wie in der Versendung "Basis.klassenmethode()".

In einer Exemplarmethode kann sowohl das Schlüsselwort "super" als auch eine Formung mit dem Operator "( Basis )" verwendet werden, um eine verdeckte statische Klassenmethode zu erreichen. Die zweite Möglichkeit ist zu bevorzugen, da statische Klassenmethoden auch über eine statische Klassenangabe und nicht über nicht-statische Variablen aufgerufen werden sollten.

Test4.java
class Basis 
{ static void klassenmethode() 
{ java.lang.System.out.println( "Basis#klassenmethode" ); }}
class Erweiterung extends Basis 
{ static void klassenmethode() 
{ // static context here,  
// but "super" and "this" are non-static variables 
// super.klassenmethode(); 
// (( Basis )this ).klassenmethode(); 
Basis.klassenmethode(); 
java.lang.System.out.println( "Erweiterung#klassenmethode" ); }
void exemplarmethode() 
{ super.klassenmethode();  
(( Basis )this ).klassenmethode(); 
java.lang.System.out.println( "Erweiterung#exemplarmethode" ); }}
public final class Test4 
{ public static void main( String[] args ) 
{ Erweiterung e = new Erweiterung();  
e.klassenmethode (); 
e.exemplarmethode (); }}

System.out
Basis#klassenmethode 
Erweiterung#klassenmethode 
Basis#klassenmethode 
Basis#klassenmethode 
Erweiterung#exemplarmethode

Ersetzungsblockade für Methoden mit "final"

Wenn es verhindert werden soll, daß eine Methode in Erweiterungen ersetzt wird, dann kann diese Methode mit dem Methodenmodifizierer "final" gekennzeichnet werden. Ersetzungen sind dann nicht erlaubt.

Basis.java
class Basis 
{ final void exemplarmethode() 
{ java.lang.System.out.println( "Basis#exemplarmethode" ); }}
class Erweiterung extends Basis 
{ void exemplarmethode() 
{ java.lang.System.out.println( "Erweiterung#exemplarmethode" ); }}

Konsole
Basis.java:5: exemplarmethode() in Erweiterung cannot override exemplarmethode() in Basis; overridden method is final

Gelegentlich wird empfohlen, Methoden als "final" zu kennzeichnen, weil sie dann schneller aufgerufen werden könnten. Bei neueren Java -Implementationen ist solch ein Geschwindigkeitsvorteil aber nicht mehr oder nicht mehr in dem Maße erkennbar wie bei älteren Java -Implementationen. Daher sollte dieser nicht ohne weitere Untersuchungen zum Anlaß genommen werden, etwa möglichst viele Methoden mit dem Methodenmodifizierer "final" zu kennzeichnen. Nur wenn es einen guten, sich aus dem Begriff  einer Klasse ergebenden, Grund dafür gibt, daß eine Methode nicht ersetzt werden soll, dann sollte dieser Methodenmodifizierer verwendet werden.

Ersetzungsblockade für Klassen mit "final"

Wenn es verhindert  werden soll, daß eine Klasse erweitert  wird, dann kann diese Klasse mit dem Klassenmodifizierer "final" gekennzeichnet werden. Die Verwendung des Namens solch einer Klasse in einer extends -Klausel ist dann nicht erlaubt.

Basis.java
final class Basis {} class Erweiterung extends Basis {}

Konsole
Test.java:1: cannot inherit from final Basis 
final class Basis {} class Erweiterung extends Basis {} 
^

Exemplare einer Erweiterung können ja anstelle eines Exemplars einer Basisklasse verwendet werden. Wenn eine Standardklasse, wie die Klasse "String", ersetzt werden könnte, dann könnte eine beliebige Funktionalität in der Erweiterung implementiert werden, die möglicherweise die Operationen der Klasse "String" nicht spezifikationstreu ersetzt. Teile der Java -Standard-Pakete verlassen sich aber darauf, daß ein von einem String-Ausdruck referenziertes Objekt die Operationen der Klasse "String" spezifikationstreu implementiert. Eine finale Klasse ist vor solch einer Störung geschützt.

Im Programm "BadExtension.java" wird die Methode "doppel( int )" in der Erweiterung ersetzt. Wenn man annimmt, daß die Dokumentation der Operation "doppel" der Basis spezifiziert, daß das Ergebnis das Doppelte eines hinreichend kleinen Argumentwerts ist, dann ist die Methodendeklaration in der Erweiterung nicht spezifikationstreu. Die Operation "printdoppel( int, Basis )" verwendet ein Exemplar der Basisklasse, um das Doppelte eines hinreichend kleinen Argumentwerts auszugeben. In der Hauptmethode "main" wird der Operation "printdoppel( int, Basis )" dann der Wert "7" und ein Exemplar der Erweiterung übergeben, das von dem Parameter "b" der Methode "Client#printdoppel( int, Basis )" referenziert werden kann, weil es ein Untertyp des Parametertyps "Basis" ist.

Da die Erweiterung aber nicht spezifikationstreu ist, arbeitet die Operation "printdoppel( int, Basis )" dann nicht richtig und gibt für hinreichend kleine Werte des ersten Arguments das Vierfache  aus. Wenn die Klasse "Basis" mit dem Klassenmodifizierer "final" gekennzeichnet worden wäre, dann könnte die Methode "Client#printdoppel( int, Basis )" jedoch sicher sein, ein Exemplar der Klasse "Basis" zu referenzieren. Dies wäre dann vorteilhaft, wenn der Autor dieser Methode der Spezifikationstreue der Basisklasse mehr vertraut als der Spezifikationstreue eventueller Erweiterungen, etwa weil ihm der Autor der Basisklasse bekannt ist, aber beliebige Autoren Erweiterungen schreiben könnten.

NotFinal.java
class Basis  
{ /** Returns twice the argument value. 
@param i must be less than 1073741823 
@return twice the argument value */
int doppel( final int i ){ return 2 * i; }}
class Erweiterung extends Basis  
{ int doppel( final int i ){ return 4 * i; }}
class Client  
{ /** Prints twice the argument value. 
@param i must be less than 1073741823 */
void printdoppel( final int i, final Basis b ) 
{ java.lang.System.out.println( b.doppel( i )); }}
public final class BadExtension 
{ public static void main( final String[] args ) 
{ new Client().printdoppel( 7, new Erweiterung()); }}

System.out
28

Aufrufe ersetzter Methoden in der Basisklasse

Wenn in einer Basisklassenmethode eine „Methode der Basisklasse aufgerufen wird“, dann wird dadurch möglicherweise tatsächlich eine Methode der Erweiterung aufgerufen. Dies geschieht, dann wenn die aufgerufene Methode in der Erweiterung ersetzt wurde.

In dem folgenden Programmbeispiel wird in der Methode "versendung" „die Methode "exemplarmethode" aufgerufen“. Doch wenn der Typ des Objekts "this" eine Erweiterung ist, in der diese aufgerufene Methode ersetzt wurde, dann wird hier anhand des Objekttyps tatsächlich die ersetzte Methode der Erweiterung aufgerufen. Daher gibt das Programm "Beispiel5.java" einmal den Text "Basis#exemplarmethode" und einmal den Text "Erweiterung#exemplarmethode" aus.

Beispiel5.java
class Basis 
{ static void klassenmethode() 
{ java.lang.System.out.println( " Basis#klassenmethode" ); }
void exemplarmethode() 
{ java.lang.System.out.println( " Basis#exemplarmethode" ); }
final void finalmethode() 
{ java.lang.System.out.println( " Basis#finalmethode" ); }
void versendung() 
{ this.klassenmethode(); 
this.exemplarmethode();  
this.finalmethode(); }}
class Erweiterung extends Basis 
{ static void klassenmethode() 
{ java.lang.System.out.println( "Erweiterung#klassenmethode" ); }
void exemplarmethode() 
{ java.lang.System.out.println( "Erweiterung#exemplarmethode" ); }}
public final class Beispiel5 
{ public static void main( String[] args ) 
{ Basis b = new Basis (); b.versendung();  
Erweiterung e = new Erweiterung(); e.versendung(); }}

System.out
      Basis#klassenmethode 
Basis#exemplarmethode 
Basis#finalmethode 
Basis#klassenmethode 
Erweiterung#exemplarmethode 
Basis#finalmethode

Das Verhalten bei diesem Aufruf zeigt, daß die eingangs in Anführungszeichen wiedergegeben Ansicht, es werde in der Methode "versendung" eine bestimmte  Methode aufgerufen, falsch ist. Die tatsächlich aktivierte Methode  hängt eben von Umständen ab, die durch den Quelltext der Methode "versendung" nicht festgelegt sind.

Deswegen ist es besser zu sagen, hier werde eine Nachricht an einen Ausdruck geschickt. An welches  Objekt diese Nachricht dann weitergeleitet wird, hängt vom Wert dieses Ausdrucks ab. Dieser Wert ist zunächst eine Referenz, die ein bestimmtes Objekt referenziert. Die Nachricht führt dann zur Aktivierung der zur Signatur der Nachricht passenden Methode dieses Objekts.

Welches Objekt  diese Nachricht erhält und welche Methode  dadurch schließlich aktiviert wird hängt von Umständen ab, die im allgemeinen erst zur Laufzeit des Programms bekannt sind, eben vom Wert des Ausdrucks "this". Es wird also nicht eine zur Schreibzeit bestimmte  Methode aufgerufen.

Eine solche Nachrichtenversendung an ein Objekt, dessen Typ erst zur Laufzeit feststeht, wird als dynamische Versendung  oder dynamische Bindung  bezeichnet. Der Übersetzer kann bei solch einer dynamischen Versendung nicht einfach einen Aufruf einer bestimmten  Methode erzeugen, sondern muß einen Aufruf einer unbestimmten Methode mit der Instruktion "invokevirtual" erzeugen, die zur Laufzeit anhand der Nachricht und des Typs des Empfängerobjekts die zu aktivierende Methode ermittelt. Ein Aufruf einer statischen Klassenmethode wird hingegen mit der Instruktion "invokestatic" durchgeführt.

Konsole
javap -c Basis
(...)
Method void versendung() 
0 invokestatic #6 <Method void klassenmethode()> 
3 aload_0 
4 invokevirtual #7 <Method void exemplarmethode()> 
7 aload_0 
8 invokevirtual #8 <Method void finalmethode()> 
11 return

Oft ist die Instruktion "invokevirtual" etwas langsamer als eine Aufruf einer im Programmtext festgelegten Methode, da sie zur Laufzeit erst anhand des Objekttyps die aufzurufende Methode ermitteln muß.

Die Versendung einer Nachricht an ein Objekt sollte im allgemeinen nicht als „Aufruf einer Methode“ angesehen werden, weil durch den Quelltext eben nicht eine bestimmte Methode festgelegt ist. Diese wird im allgemeinen erst zur Laufzeit anhand des Typs des Empfängerobjekts ermittelt.

Ersetzung von Feldern  (Exemplarvariablen)

Felder können in Java nicht ersetzt  werden. Sie werden wie statische Methoden verdeckt. Dafür reicht es ein Feld mit dem gleichen Namen  in einer Erweiterung zu deklarieren, es braucht nicht den gleichen Typ wie das gleichnamige Feld der Basisklasse haben (Felder haben keine Signaturen).

Welches Feld gemeint ist, wird durch den statischen Typ des Ausdrucks  bestimmt, dessen Feld verwendet wird.

Beispiel6.java
public final class Beispiel6 
{ public static void main( String[] args ) 
{ Basis b = new Basis ();  
java.lang.System.out.println( "main: " + b.i ); b.printi();  
Erweiterung e = new Erweiterung();  
java.lang.System.out.println( "main: " + e.i );  
java.lang.System.out.println( "main: " + (( Basis )e ).i );  
e.printi(); e.printi1(); }}
class Basis 
{ int i = 4; void printi() 
{ java.lang.System.out.println( "printi: " + i );  
java.lang.System.out.println( "printi: " + this.i ); }}
final class Erweiterung extends Basis 
{ int i = 5; void printi1() 
{ java.lang.System.out.println( "printi1: " + i );  
java.lang.System.out.println( "printi1: " + this.i );  
java.lang.System.out.println( "printi1: " + (( Basis )this ).i );  
java.lang.System.out.println( "printi1: " + super.i ); }}

System.out
main: 4 
printi: 4 
printi: 4 
main: 5 
main: 4 
printi: 4 
printi: 4 
printi1: 5 
printi1: 5 
printi1: 4 
printi1: 4

Ersetzung und Verdeckung

Als Ersetzen  (overriding ) einer Exemplarmethode  einer Klasse wird die Reimplementation dieser Methode in einer Erweiterung jener Klasse bezeichnet. Dabei wird ein Exemplarmethode der gleichen Signatur  in der Erweiterung deklariert. Welche der beiden Methoden aufgerufen wird, wenn man eine Nachricht mit der Signatur der Methoden an einen Referenzausdruck versendet, wird durch den Typ des Objektes  dieses Referenzausdrucks bestimmt.

Bei der Verdeckung  (hiding ) wird in einer Erweiterung ein Feld  mit dem gleichen Namen wie ein Feld der Basisklasse oder eine statische Methode  mit der gleichen Signatur wie eine statische Methode der Basisklasse deklariert. Der verdeckende Eintrag wird dann verwendet, wenn der statische Typ  des Ausdrucks auf der linken Seite des Punktes "." die Erweiterung ist. Ist der statische Typ jedoch die Basisklasse, so werden deren Einträge verwendet.

Zur Auflösung wird beim Ersetzen also der dynamische Typ des Objekts links vom Punkt "." verwendet und beim Verdecken der statische Typ der Angabe links vom Punkt ".".

Die Nachrichtenzustellung

Man kann unterscheiden zwischen

In manchen anderen Sprachen kann eine Nachricht auch mehrere Empfänger haben: Bei den sogenannten Multioperationen („Multimethoden“) wird die Nachricht dann unter Berücksichtigung des Typs aller (mehrerer) Empfängerobjekte zugestellt. Das ist in Java  nicht möglich und muß bei Bedarf vom Programmierer durch das „Besuchermuster“ (“visitor pattern ”) nachgebildet werden.

Polymorphie in der Oberklasse

In einer Oberklasse werden unter Umständen Methoden der Unterklasse aufgerufen, da hier die späte Bindung zum Einsatz kommt. Dadurch kann eine Unterklasse einer Oberklasse beschädigen. Dies ist einer der Gründe, aus denen Klassen nicht ohne final deklariert werden sollten, außer die Vererbung wurde von Anfang an in diesem Sinne eingeplant, daß solche Effekte durch Unterklassen erwünscht sind.

Main.java

class Base
{

public void m()
{ java.lang.System.out.println( "A" );
this.m(); }}

class Extension extends Base
{

public void m()
{ java.lang.System.out.println( "B" ); }

public void main()
{ super.m(); }}

public final class Main
{

public static void main( final java.lang.String[] args )
{ new Extension().main(); }}

transcript
A
B

super.super

Es gibt kein »super.super« (dies könnte die Semantik der Oberklasse durchbrechen, welche sich bewußt entschieden hat, eine bestimmte Methode zu ersetzen.

Beispiel

Main.java

class Base
{ public void m( final java.lang.Object o )
{ java.lang.System.out.println( "Base + " + o ); }}

class Derived extends Base
{ public void m( final java.lang.String o )
{ java.lang.System.out.println( "Derived + " + o ); }}

public final class Main
{ public static void main( final java.lang.String[] args )
{ final Base o = new Derived();
o.m( "alpha" ); }}

transcript
Base + alpha

Der Compiler sucht in dem Typ des Ausdrucks o nach einer passenden Methode und findet dort m(Object). Daher wird m(Object) aufgerufen. Zur Laufzeit wird dann Base.m aufgerufen, da Derived.m nicht die passende Signatur m(Object) hat.

Main.java

class Base
{ public void m( final java.lang.Object o )
{ java.lang.System.out.println( "Base + " + o ); }}

final class Derived extends Base
{ public void m( final java.lang.String o )
{ java.lang.System.out.println( "Derived + " + o ); }}

public final class Main
{ public static void main( final java.lang.String[] args )
{ final Derived o = new Derived();
o.m( "alpha" ); }}

transcript
Derived + alpha

Der Compiler sucht in dem Typ des Ausdrucks o nach einer passenden Methode und findet dort m(String). Daher wird m(String) aufgerufen. Zur Laufzeit wird dann Derived.m() aufgerufen, da dies die passende Signatur hat.

Main.java

class Base
{ public void m( final java.lang.Object o )
{ java.lang.System.out.println( "Base + " + o ); }}

class Derived extends Base
{ public void m( final java.lang.Object o )
{ java.lang.System.out.println( "Derived + " + o ); }}

public final class Main
{ public static void main( final java.lang.String[] args )
{ final Base o = new Derived();
o.m( "alpha" ); }}

transcript
Derived + alpha

Der Compiler sucht in dem Typ des Ausdrucks o nach einer passenden Methode und findet dort m(Object). Daher wird m(Object) aufgerufen. Da dies ersetzt ist, wird dann zur Laufzeit Derived.m() aufgerufen.

Übungsfragen

Übungsfrage
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage.java

public final class Uebungsfrage
{ public static void main( String[] args )
{ (( Basis )new Erweiterung()).m(); }}

class Basis
{ int i = 4; void m(){ java.lang.System.out.println( i ); }}

final class Erweiterung extends Basis
{ int i = 5; void m(){ java.lang.System.out.println( i ); }}

Übungsfrage 1
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage1.java

public final class Uebungsfrage1
{ public static void main( String[] args )
{ final Basis b = new Erweiterung(); b.m(); }}

class Basis
{ void m(){ java.lang.System.out.println( "Basis" ); }}

final class Erweiterung extends Basis
{ void m(){ java.lang.System.out.println( "Erweiterung" ); }}

Übungsfrage 2
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage2.java

public final class Uebungsfrage2
{ public static void main( String[] args )
{ final Basis b = new Erweiterung(); b.m(); }}

class Basis
{ static void m(){ java.lang.System.out.println( "Basis" ); }}

final class Erweiterung extends Basis
{ static void m(){ java.lang.System.out.println( "Erweiterung" ); }}

Übungsfrage 3
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage3.java

public final class Uebungsfrage3
{ public static void main( String[] args )
{ final Basis b = new Erweiterung(); b.m(); }}

class Basis
{ void m(){ java.lang.System.out.println( "Basis" ); }}

final class Erweiterung extends Basis
{ void m( String s ){ java.lang.System.out.println( "Erweiterung" ); }}

Übungsfrage 4
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage4.java

public final class Uebungsfrage4
{ public static void main( String[] args )
{ final Erweiterung e = new Erweiterung(); e.m(); }}

class Basis
{ final String t = "Basis"; void m(){ this.m( t ); }}

final class Erweiterung extends Basis
{ final String t = "Erweiterung";
void m( String s ){ java.lang.System.out.println( s ); }}

Übungsfrage 5
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage5.java

public final class Uebungsfrage5
{ public static void main( String[] args )
{ Erweiterung e = new Erweiterung(); e.m( "main" ); }}

class Basis
{ final String t = "Basis"; void m(){ m(); }}

final class Erweiterung extends Basis
{ final String s = "Erweiterung";
void m( String s ){ java.lang.System.out.println( t ); }}

Übungsfrage 6
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage6.java

public final class Uebungsfrage6
{ public static void main( String[] args )
{ Erweiterung e = new Erweiterung(); e.m(); }}

class Basis
{ static void m(){ java.lang.System.out.println( "Basis" ); }}

final class Erweiterung extends Basis
{ void m(){ java.lang.System.out.println( "Erweiterung" ); }}

Übungsfrage 7
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage7.java

public final class Uebungsfrage7
{ public static void main( String[] args )
{ Erweiterung e = new Erweiterung(); e.m(); }}

class Basis
{ void m(){ java.lang.System.out.println( "Basis" ); }}

final class Erweiterung extends Basis
{ void m(){ super.m(); java.lang.System.out.println( "Erweiterung" ); }}

Übungsfrage 8
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage8.java

public final class Uebungsfrage8
{ public static void main( String[] args )
{ Erweiterung e = new Erweiterung(); e.m(); }}

class Basis
{ String t;
void m(){ t = "Basis"; m( "Java" ); }
void m( String s ){ java.lang.System.out.println( t ); }}

final class Erweiterung extends Basis
{ void m(){ super.m(); java.lang.System.out.println( "Erweiterung" ); }
void m( String s ){ java.lang.System.out.println( s ); }}

Übungsfrage 9
Wird das folgende Programm vom Übersetzer akzeptiert und was gibt es gegebenenfalls aus?
Uebungsfrage9.java

class Base { Base(){ method(); } void method() {} }

public final class Main extends Base
{ double field = 0.0;

void method(){ field = 1.0; }

public static void main( final java.lang.String[] args )
{ final Main o = new Main(); java.lang.System.out.println( o.field ); }}

Übungsfrage 10

Warum wird hier nicht dynamisch aufgelöst?

Main.java

class Alpha { public static String m(){ return "Alpha"; } }
final class Beta extends Alpha { public static String m(){ return "Beta"; }}

public final class Main
{
public static void main(String[] args)
{ final Alpha alpha = new Beta();
java.lang.System.out.println( alpha.m() ); }}

transcript
Alpha
Übungsaufgabe
Schreiben Sie eine Klasse "English" mit drei nicht-statischen parameterlosen Wertmethoden, welche das zu ihrem Namen gehörende englische Wort (“house ”, “book ”, bzw. “color ”) als Objekt der Klasse "String" zurückgeben und somit zur Übersetzung dienen können.
English [Spezifikation]

String haus()

Returns a word for the German word "Haus".

String buch()

Returns a word for the German word "Book".

String farbe()

Returns a word for the German word "Farbe".

Schreiben Sie eine weitere Klasse "British", welche dieselbe Spezifikation erfüllt, jedoch britische Wörter zurückgibt (dabei wird “colour ” für „Farbe“ verwendet). Die Klasse "British" soll dabei möglichst viele Methoden von der Klasse "English" übernehmen.

Zum Erweitern und Implementieren

java.lang.Override (Seit Java SE 6 auch für Schnittstellenimplementation verwendbar)

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Eine Verbindung zur Stefan-Ram-Startseite befindet sich oben auf dieser Seite hinter dem Text "Stefan Ram".)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. Schlüsselwörter zu dieser Seite/relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram721612 stefan_ram:721612 super Java super.super Java source, source code,sourcecode, Programmier sprache java, Java Programm Java Programmiersprache Java Sprache Java Programmierung Programmieren in Java, Die Programmiersprache Java Javapgrammierung, Java-Programmierung, Java2, Java1.2, Java1.3, Java1.4, Java1.5, Java 2, Java 1.2, Java 1.3, Java 1.4, Java 1.5, Java 1.6, Java 1.7, Java 1.8, Java 8, Java 1.9, Java 9, Java 1.10, Java 10, Tiger, Dolphin, objektorientierte Programmierung, Java sun, , Softwareentwicklung, Software-Entwicklung, Software Entwicklung, Softwareprogrammierung, Software-Programmierung, Software Programmierung, Softwareherstellung, Software-Herstellung, Software Herstellung, Softwareerstellung, Software-Erstellung, Software Erstellung, Softwareengineering, Software-Engineering, Software Engineering, Softwareenginering, Software-Enginering, Software Enginering, Softwaremethodik, Software-Methodik, Software Methodik, Programmiermethodik, Programmier-Methodik, Programmier Methodik, Softwaredidaktik, Software-Didaktik, Software Didaktik, Methodik des Programmierens, Didaktik des Programmierens, Fachdidaktik des Programmierens, Fach-Didaktik des Programmierens, Programmieren, Programmierung, Computer Science, Informatik, Informatik, Informationspädagogik, Informationspaedagogik, Programmierer, programmieren, Quellcode, Quelltext, Quelle, Quellen, Informatiker, Fachinformatiker, Entwickler, mit Stil, mit Stiel, mit Still, , Einführung, Einfuehrung, Einführungen, Einfuehrungen, eLearning-Modul, e-Learning-Modul, eLearning-Module, e-Learning-Module, Kurs Berlin, Kurse Berlin, Kursus, Vortrag, Vorträge, Vortraege, Lehrgang, Lehrgänge, Lehrgaenge, kostenloses Lehrmaterial, kostenlose Lehrmaterialien, Vorlesung, Vorlesungen, Unterrichtseinheit, Unterrichtseinheiten, kostenloses Unterrichtsmaterial im Internet, kostenlose Unterrichtsmaterialien im Internet, Ausbildung, Ausbildungen, für die Ausbildung, Fortbildung, Fortbildungen, Weiterbildung, Weiterbildungen, Schulung Berlin, Schulungen Berlin, Internetschulung, Webschulung, Kursunterlage, Kursunterlagen, trainer Berlin, Didaktik, Informatikunterricht, Primer, Skript FH, Skripte FH, Skriptum FH, Skripts FH, Script FH, Scripte FH, Scriptum FH, Scripts FH, howto, how-to, how to, Lehrbuch, Buch, Bücher, Buecher, Bericht, Crash Kurs Berlin, Crash-Kurs Berlin, Crashkurs Berlin, Report, Sachtext, Sachtexte, Übung, Übungen, Uebung, Uebungen, course, PbclevtugFgrsnaEnz, lecture note, lecture notes, Tutorial, Tutor, Tutorium, Teacher, Lehrer Berlin, Trainer Berlin, Beratung Berlin, Anleitung zum, Anleitungen zum, Einführung, Einfuehrung, Einführungen, Einfuehrungen, Handbuch für, Handbücher, Handbuecher, Support, Diplomarbeit, Facharbeit, Hausarbeit, Hausarbeiten, Werk, Werke, Text, Übungsblatt, Uebungsblatt, Lösung, Loesung, Lösungen, Loesungen, Auflösung, Aufloesung, Auflösungen, Aufloesungen, Facharbeit, Facharbeiten, Forum, Training, manual, Folie, Folien, Lehrmittel, beratung, Definition von, Allgemeine Erklärung, Allgemeine Erklaerung, Allgemeine Erklärungen, Allgemeine Erklaerung, Allgemeine Erklaerungen, Einfache Erklärung einfach, Einfache Erklaerung einfach, Einfache Erklärungen, Einfache Erklaerung, Einfache Erklaerungen, zum Thema, FAQ, FAQs, Konzept, Lernkurs, Lern-Kurs, Lernkurse, Lern-Kurse, eine kurze Erklärung, Begriff, Begriffe, Erklärung zu Begriffen, begriffbestimmung, begriffbestimmung, Begriffsbestimmung, Begriffserklärung, Begriffserklaerung, Wort, Worterklärung, Worterklaerung, Definition, Beispiel, Beispiele, Bedeutung, Bedeutungen, was bedeutet, Begriffsdefinition, für Anfänger, fuer Anfaenger, für Einsteiger, für Beginner, zum selber Lernen, handout, hand out, web based training, WBT, Net Based Training, NBT, computer based training, CBT, virtual learning environment, virtual university, für die Schule, Seminar, Seminare, Vorgehensweise, Pädagogik, Paedagogik, Akademie, Onlineacademie, Onlineakademie, Wissen, Unterrichtsvorbereitung für das Fach, Unterricht, für Lehrer, für Lehrerinnen, Referat, Referate, Hausaufgabe, Hausaufgaben, Tip, Tipp. Tipps und Tricks, Tips und Tricks, Methode, Methoden, Methodik, Funktion, Funktionsweise, Aufbau, Prinzip, Grundlage, Grundlagen, Internetschulung, Unterlage, Unterlagen, Struktur, Frage, Fragen, Antwort, Antworten, Schulunterricht, zum erlernen, was ist das?, wie?, lerne, Onlineausbildung, Onlinelehrgang, Onlinekurs, Onlinebuch, Webbuch, Academy, Fachhochschule, FH, TFH, Hochschule, Universität, Universitaet, Uni, Schule, Berufsschule, Gymnasium, Erste Schritte mit, Dozent, Dozenten, Zusammenfassung, Übersicht, Term, Fachwort, Fachbegriff, Fachbegriffe, Grundbegriff, Grundbegriffe, Lektion, Lektionen, Bedienungsanleitung, Bedienungsanleitungen, Spezialist, Spezialisten, Coaching, Coach, Fachbuch, technologie, Unterschied zwischen, Unterschiede zwischen, lehren, unterrichten, für das Studium, für Studenten, für Studentinnen, für Studierende, für Schüler, für Schülerinnen, Wie kann ich, Wie kann man, Hilfe, selber, erstellen, Erstellung, Beginner, Online-Seminar, Online-Kurs, Online-Schulung, ich, gebrauchsanweisung, gebrauchsanleitung, Bedienungsanweisung, Einweisung, Kurzinfo, Internet-Kurs, Internet-Kurse, Online-Training, Intensiv, Intensivkurs, Vortragsreihe, Präsentation, Grundzüge, Mitschrift, Mitschriften, Verständnis, verstehen, anwenden, Hintergrundwissen, Hintergrund, Hintergründe, content, Funktionsprinzip, Schritt für Schritt; E-Learning; elearning; online learning; Fernlehre, Fernlehrgang, Fernlehrgänge, Referent, Referenten, Fernkurs, Fernkurse, Fernstudium, Fernschule, Fernuniversität, Fernlehrer, Denken, Qualifizierungsmaßnahme, Qualifizierung, Qualifizierungen, Volkshochschulkurs, Volkshochschulkurse, Internet-Learning-Plattform, Online-Learning-Plattform, E-Learning-Plattform, Internetlearning-Plattform, Onlinelearning-Plattform, Elearning-Plattform, jetzt, Kochbuch, einfach erklärt, Kenntnis, Kenntniss, Kenntnisse, deutsche Hilfe, lernen und verstehen; blended learning; courseware, Profi, Profis, professionell, professionelle, professionelles, profesionell, profesionelle, profesionelles, professionel, gekonnt, für Könner, Grundkurs, Leistungskurs, Aufbaukurs, Lehrtext, Lehrtexte, Fachtext, Fachtexte, Ausarbeitung, distance learning, Online Seminar, distance education, Online Tutorium, Online Tutorien, technology-based learning, Computer based learning, CBL, Internet Based learning, IBL, Web based learning, WBL, online-learning, OL, online-training, OT, CAI, Computer Assisted Instruction, CUU, Computerunterstützter Unterricht, Computer-unterstützter Unterricht, Know how, Grundkenntnis, Grundkenntnisse, Kompetenz, Schulungsunterlagen, Insiderwissen, Rat, Lehrerinnen und Lehrer, ABC, Überblick, Arbeitsblatt, Sekundarstufe, Oberstufe, Material, Materialien, Unterrichtsentwurf, Unterrichtsentwürfe, Lerntip, Lerntips, Lerntipp, Lerntipps, ebook, schnell lernen, Lerner, Lernender, Lernende Erwachsenenbildung, Frage und Antwort, Selbststudium, Selbstudium, Umschulung, , kostenlos, kostenlose, kostenloses, kosten los, kosten lose, kosten loses, gratis, free, frei, freie, freies, privat, private, privates, homepage, home-page, home page, website, web site, webpage, web-page, web page, webpages, web-pages, web pages, webseite, Web-Seite, Webseite, Webseiten, Web-Artikel, Web Artikel, online, on-line, on line, download downloaden, down load, or, deutsch, deutsche, deutsches, deutscher, Deutschland, deutschsprachig, deutschsprachige, deutschsprachiges, german, germany, and, uebersicht, Uerbersicht, uebersichten, Uebersichten, Übersicht, übersicht, Übersichten, übersichten, Uebersicht, uebersicht, Uebersichten, uebersichten, Info, Infos zu, Information über, ueber, Informationen über, Auskunft, Auskünfte, Auskuenfte, Dienst, PbclevtugFgrsnaEnz, Angebot, Angebote, Anbieter, server, für, fuer, in, an, keyword, keywords, key word, keywords, internet, internets, net, network, net work, networks, net works, web, www, world wide web, Theorie, Praxis, Anwendung, DV, IT, glossar, fachwörterbuch it-glossar, computer lexikon, Computerlexikon, Computer-Lexikon, Lexikon, computer lexicon, Computerlexicon, Computer-Lexicon, Lexicon, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 was ist ein, was ist das, was sind, definiere, definition, define, Inhalt, html, xhtml, free im netz, PDF Text, digital, digitale, binär, binäre, elektronisch, elektronische, fachbegriff, fachbegriffe, konzept, Begriff, allgemein, allgemeine, allgemeines, Technik, Datentechnik, Digitaltechnik, Binätechnik, online lesen; , Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd721612, slrprddef721612, PbclevtugFgrsnaEnz Erklärung, Beschreibung, Info, Information, Hinweis,

Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram.
https://www.purl.org/stefan_ram/pub/java_ueberschreiben_de