Klassen sind Strukturen aus Daten und Methoden.
   public class Kreis
   { public double x,y;  // Koordinaten
     public double r;    // Radius
     public double umfang()
     { return 2*3.14159*r;
     }
     public double fläche()
     { return 3.14159*r*r;
     }
   }
Klassen dienen (meist) dazu, den Inhalt und die Fähigkeiten von
Objekten zu beschreiben.
Objekte sind Instanzen von Klassen.
Sie leben z.B. in Variablen von geeignetem Typ 
(siehe Seite ![[*]](cross_ref_motif.gif) ).
).
    Kreis c;
und werden grundsätzlich dynamisch erzeugt:
    c = new Kreis();
Zugriff auf Objekte-Daten durch Feld-Selektion:
    Kreis c = new Kreis();
    c.x = 2.0;
Aufruf von Instanzmethoden durch Methoden-Selektion:
    Kreis c = new Kreis();
    double f;
    c.r = 2.7;
    f = c.fläche();
Technische Realisierung: Übergabe der Selbstreferenz  this
(siehe Seite ![[*]](cross_ref_motif.gif) )
an den Code, der  fläche implementiert.
)
an den Code, der  fläche implementiert.
    Kreis c = new Kreis();
Ablauf:
Konstruktoren initialisieren Objekte.
Wichtige Eigenschaften:
   public Kreis (double x, double y, 
                  double r)
   { this.x = x;  // 'this' löst
     this.y = y;  // Namens-
     this.r = r;  // konflikt
   }
   ...
   Kreis c = new Kreis(1.4,2.0.5.3);
Der parameterlose Konstruktor
Kreis();heißt Default-Konstruktor. Er wird vom Compiler zur Verfügung gestellt, falls es keinen Benutzer-definierten Konstruktor gibt.
 
Der Compiler-generierte Default-Konstruktor ruft lediglich den
Default-Konstruktor der Oberklasse auf.
 
Wir bekommen also Compiler-Fehlermeldungen,
this findet in Konstruktoren Anwendung, die andere Konstruktoren verwenden:
   public Kreis (double x, double y, 
                  double r)
   { this.x = x;
     this.y = y;
     this.r = r;
   }
   public Kreis(double r)
   { this(0.0, 0.0, r);
   }
   public Kreis(Kreis c)
   { this(c.x, c.y, c.r);
   }   
   public Kreis()
   { this(0.0, 0.0, 1.0);
   }
 
Andere typische Anwendungen von  this siehe 
Seite ![[*]](cross_ref_motif.gif) und Seite
 und Seite ![[*]](cross_ref_motif.gif) .
.
Solche Aufrufe anderer Konstruktoren stehen vor anderen Anweisungen im Konstruktorrumpf.
 
Solch ein expliziter Konstruktor-Aufruf über  this
All dieses führt zu Compiler-Fehlermeldungen.
   public class Tisch
   { Bein[] beine;
     int anz_beine;
     public Tisch()
     { this(anz_beine-1); // FALSCH!
     }
     public Tisch(int n)
     { anz_beine = n;
     }
   }
Der Java-Compiler meldet:
   Can't reference anz_beine before the 
   superclass constructor has been called.
    { this(anz_beine-1);
           ^
 
Klassenvariablen beschreiben im Gegensatz
zu Instanzvariablen Eigenschaften der Klasse
(siehe auch Seite ![[*]](cross_ref_motif.gif) ).
).
Klassenvariablen werden mit dem Schlüsselwort static deklariert:
   public class Kreis
   { static int anz_Kreise = 0;
     public double x,y;  // Koordinaten
     public double r;    // Radius
     public Kreis (double x, double y, 
                   double r)
     { this.x = x; this.y = y; this.r = r;
       anz_Kreise++;
     }
   }
Zugriff erfolgt außerhalb der Klassen qualifiziert mit dem Klassennamen:
    if (Kreis.anz_kreise == 12)
       System.out.println(``Hello'');
static-Felder werden außerdem benutzt für
   public class Kreis
   { public double x,y;  // Koordinaten
     public double r;    // Radius
     // Instanz-Methode:
     public Kreis bigger(Kreis k)
     { if (k.r > r) 
          return k;
       else return this;
     }
     // Klassen-Methode:
     public static Kreis bigger(Kreis k1, 
                                Kreis k2)
     { if (k1.r > k2.r) 
          return k1;
       else return k2;
     }
   }
Verwendung der beiden Methoden:
Kreis a = new Kreis(12.3); Kreis b = new Kreis(3.414); Kreis c = a.bigger(b); Kreis c = Kreis.bigger(a,b);
Besonderheiten:
     // Klassen-Methode:
     public static Kreis bigger(Kreis k1, 
                                Kreis k2)
     { if (k1.r > k2.r && 
           x > 0.0)         // FALSCH! 
          return k1;
       else return k2;
     }
Java-Compiler:
   Can't make a static reference to 
   nonstatic variable x in class Kreis.
     { if (k1.r > k2.r && x > 0.0) 
                          ^
als auch für
   class Kreis
   { double r = 1.0;
     static int kreiszahl = 100;
     ...
   }
Initialisierung wird jedesmal bei Erzeugung eines Objektes ausgeführt.
Initialisierung wird nur einmal durchgeführt, wenn die Klasse geladen wird.
    class Rechteck
    { static float max_sv = 2.0;
      static float max_h = 10.0
      static float max_b = max_sv * max_h;
      ...
    }
Die Regeln:
 
 Daher bekommen wir hiermit 2 Compiler-Fehlermeldungen:
    class FalscheStaticInits
    { static float f = j;     // VORWÄRTS!
      static int j = 1;
      public int max;
      static int k = max + 1; // INSTANZVAR!
      ...
    }
 
Für komplexere Initialisierungen, die nicht als einfacher Ausdruck geschrieben werden können, gibt es die statischen Initialisierungsblöcke:
   public class Kreis
   { static private double pi = 3.14;
     static private double[] sins 
                  = new double[1000];
     // Statischer Initialisierungsblock
     static
     { double x = 0.0;
       double delta_x = pi / 2 / (1000-1);
       int i;
       for (i=0; i < 1000; i++)
         { sins[i] = Math.sin(x);
           x += delta_x;
         }
     }
   }
Es darf mehrere solcher Blöcke geben. Sie werden behandelt wie ein
einziger, der durch Hintereinanderhängung entsteht.
   public class Tisch
   { Bein[] beine;
     int anz_beine = 4;
   }
Hier gelten ähnliche Regeln wie bei den Klassenvariablen 
(Seite ![[*]](cross_ref_motif.gif) ):
):
 
 Daher bekommen wir hiermit 2 Compiler-Fehlermeldungen:
    class FalscheInstanzInits
    { float f = j;     // VORWÄRTS!
      int j = 1;
      int k = k + 1;   // REKURSIV!
      ...
    }
Für komplexerer Initialisierung, die nicht als einfacher Ausdruck geschrieben werden können, gibt es die Instanz-Initialisierungsblöcke (ab Java1.1):
public class Tisch
{ String platte = "Holz";
  String[] beine;
  int anz_beine = 4;
  { int i;
    for (i = 0; i < anz_beine; i++)
      beine[i] = "Bein " + i;
  }
  
  public Tisch(String p_material)
    { platte = p_material;
    }
}
Es darf mehrere solcher Blöcke geben. Sie werden behandelt wie ein
einziger, der durch Hintereinanderhängung entsteht.
Nun kennen wir alle Akteure, die bei der Erzeugung neuer Objekte eine Rolle spielen:
Wann werden neue Objekte erzeugt?
new Tisch(); new Kreis(3.2);
![[*]](cross_ref_motif.gif) ).
). 
Für
existiert zur Laufzeit eines Java-Programms ein Objekt der Klasse Class
 
Diese Objekte können nur von der virtuellen Maschine
erzeugt werden, nicht vom Benutzer-Programm.
 
Zu jedem Objekt findet man das Klassenobjekt durch  getClass()
aus der Klasse  Object: 
Class c = new Diesel().getClass();
Man kann nun z.B. den Klassennamen ausgeben:
Class c = new Diesel().getClass(); System.out.println(c.getName());
Nachzulesen in der API-Dokumentation zu java.lang.Class.
 
[Ende des Einschubs.]
Der Ablauf der Objekterzeugung:
Reserviere einen Speicherbereich, groß genug für
Gelingt dies nicht, gibt es einen OutOfMemoryError-Fehler.
 
Gelingt es, werden anschließend alle Instanzvariablen (auch die
der Oberklassen) des neuen Objektes mit ihren Default-Werten
initialisiert.
| Typ | Inhalt | Größe | Default | 
| boolean | true oder false | 1 bit | false | 
| char | Unicode Character | 16 bits | \u0000 | 
| byte | signed integer | 8 bits | 0 | 
| short | signed integer | 16 bits | 0 | 
| int | signed integer | 32 bits | 0 | 
| long | signed integer | 64 bits | 0 | 
| float | IEEE 754 floating point | 32 bits | 0.0 | 
| double | IEEE 754 floating point | 64 bits | 0.0 | 
| Klasse, Interface, Array | Referenz | / | null | 
 
null ist ein Ausdruck von einem speziellen Nulltyp, der keinen Namen hat.
null kann an jede Array-, Objekt- oder Interface-Variable zugewiesen werden.
Die Größe von Referenzen wird von der virtuellen Maschine als ein 
``Wort'' definiert (i.d.R. 32 oder 64 bits).
Ablauf:
 
 
 
Nicht unter der Kontrolle des Benutzerprogramms sondern durch einen automatischen Garbage Collector.
Der weiß zur Laufzeit, welche Objekte existieren und prüft, auf welche Objekte nicht mehr verwiesen wird.
 
 Technisch: Low-priority-thread, der im Hintergrund mitläuft.
Nutzt z.B. die Zeiten des Wartens auf Benutzereingabe.
 
Hohe Priorität bekommt der Garbage Collector nur, wenn der Interpreter
keinen Speicher mehr hat. 
Für die Entsorgung von Objekten muß also nichts getan werden.
Ein Beispielfall, wo wir dem Garbage Collector helfen können:
   public static void meth ()
   { int big_array[] = new int[100000];
     // rechne auf big_array
     int result = compute(big_array);
     // brauchen big_array nicht mehr
     // nach verlassen von meth kann es
     // entsorgt werden. 
     // falls meth aber NICHT verlassen 
     // wird:
     big_array = null;
     for (;;)    // für immer
       handle_user_input();
   }
 
Hier ist die finalizer-Methode aus
java.io.FileOutputStream:
   public void finalize()
     throws IOException; 
     // overrides Object
   { // close the stream
     if (fd != null) 
        close();
   }
Regeln für Finalizer:
Grund: finalize-Methoden können Objekte ``wiederauferstehen'' lassen (z.B. durch Speichern des this-Zeigers).2
GC muß also erneut prüfen.
In Java 1.0:
Ziel: Definition von Hilfsklassen möglichst nahe an der Stelle, wo sie gebraucht werden.
Geschachtelte und innere Klassen sind wesentlich motiviert durch das
neue Ereignismodell im AWT von Java1.1.  
Klassen oder Interfaces, die static deklariert sind und in andere ``top-level''-Klassen oder Interfaces eingeschachtelt sind.
Interfaces sind immer static, d.h. für Interfaces ist nur die ``top-level''-Variante möglich.
 
Geschachtelte ``top-level'' Klassen und Interfaces verhalten sich wie
andere Paket-Elemente auf der äußersten Ebene, außer daß ihrem Namen
der Name der umgebenden Klasse vorangestellt wird.
 
public class LinkedList
{ // interface verkettungselement
  public static interface Linkable
  { public Linkable getNext();
    public void setNext(Linkable n);
  }
  // kopf der liste:
  Linkable head;
  // einfügen:
  public void insert(Linkable n)
  { n.setNext(head);
    head = n;
  }
  // kopfzugriff:
  public Linkable getHead()
  { return head;
  }
}
Benutzung:
a) Definition der Verkettungselemente:
class LinkableInteger 
implements LinkedList.Linkable
{ 
  int i;
  LinkedList.Linkable Next;
  // konstruktor:
  LinkableInteger(int i)
  { this.i = i;
  }
  // versprochene Methoden:
  public LinkedList.Linkable getNext()
  { return Next;
  }
  public void setNext(LinkedList.Linkable n)
  { Next = n;
  }
  // für die ausgabe: String-Umwandlung
  // überschreibe toString aus Object
  public String toString()
  { return i + "";
  }
}
b) Testlauf:
public class LiMain
{
  public static void main (String[] argv)
    {
      LinkedList li = new LinkedList();
      li.insert(new LinkableInteger(16));
      li.insert(new LinkableInteger(6));
      li.insert(new LinkableInteger(-4));
      
      LinkedList.Linkable l;
      for (l = li.getHead(); 
           l != null; 
           l = l.getNext())
        System.out.println(l);
    }
}
c) Ergebnis:
-4 6 16
Im Gegensatz zu den static-deklarierten geschachtelten ``top-level''-Klassen, die mehr oder weniger nur zur Strukturierung dienen, sind Elementklassen echte innere Klassen.
 
 
 
Damit kann das Objekt der Elementklasse implizit auf die
Instanzvariablen der umgebenden Klasse zugreifen (auch auf
private).
 
Die umgebende Klasse hat Zugriffrechte auf alle Felder 
aller ihrer Elementklassen.
Zwei Elementklassen der selben umgebenden Klasse haben Zugriffrechte
auf die Elemente des jeweilig anderen.
 
Elementklassen dürfen keine statischen Elemente (Attribute, Methoden, 
Klassen, Interfaces) besitzen. Gilt auch für lokale Klassen
(Seite ![[*]](cross_ref_motif.gif) )
und anonyme Klassen (Seite
)
und anonyme Klassen (Seite ![[*]](cross_ref_motif.gif) ).
).
Beispielprojekt: Einbau eines Iterators in eine Buchstabenmenge
 
 Beschreibung der Buchstabenmenge:
 
| Name | Charset | 
| Attribut | chars vom Typ BitSet | 
| Konstruktor | Charset(String) | 
| Methode | toString() | 
| schreibt die Elemente in einen String. | 
 
 Ziel: ein Iterator für Charset.
 
Typisches Verwendungsmuster für einen solchen Iterator:
   Charset cs = new Charset(somestring);
   Enumeration e = cs.characters();
   while (e.hasMoreElements())
     machwasmit(e.nextElement());
 
(Anderes Iteratorbeispiel auf Seite ![[*]](cross_ref_motif.gif) ).
).
 
 
 a) Originalversion der Buchstabenmenge:
import java.util.BitSet;
class Charset
{ // Buchstabenmenge:
  private BitSet chars = new BitSet();
  // Konstruktor:
  public Charset(String str)
  { for (int i = 0; i < str.length(); i++)
    chars.set(str.charAt(i));
  }
  // Umwandlung in String:
  public String toString()
  { String result="[";
    int size = chars.size();
    for (int i = 0; i < size; i++)
        if (chars.get(i))
          result += (char)i;
    return result +"]";
  }
}
b) Testen der Originalversion der Buchstabenmenge:
public class CharsetMain
{ // hauptprogramm
  public static void main(String argv[])
    { // erstes kommandozeilenargument
      Charset cs = new Charset(argv[0]);
      System.out.println(cs + "");
    }
}
c) Testlauf:
 
Eingabe:
java CharsetMain JavaProgrammierung
 
Ausgabe:
[JPaegimnoruv]
c) Einbau einer inneren Iteratorklasse
import java.util.*
// für BitSet, Enumeration
// und NoSuchElementException
class Charset
{ // Buchstabenmenge:
  private BitSet chars = new BitSet();
  // Konstruktor:
  public Charset(String str)
  { for (int i = 0; i < str.length(); i++)
    chars.set(str.charAt(i));
  }
  // innere Elementklasse:
  private class Cs_iterator 
  implements Enumeration
  { private int pos;
    private int setsize = chars.size();
    
    // versprochenes "hasMoreElements":
    public boolean hasMoreElements()
    { while (pos < setsize && 
             !chars.get(pos))
        pos++;
      return pos < setsize;
    }
    // versprochenes "nextElement":
    public Object nextElement()
    throws NoSuchElementException
    { if (hasMoreElements())
        return new Character((char) pos++);
      else
        throw new NoSuchElementException();
    }
  }
  
  // liefert einen Iterator:
  public Enumeration characters()
    { return new cs_iterator();
    }
}
d) Testen des neuen Charset mit Iterator:
import java.util.*;
public class It_CharsetMain
{ public static void main(String argv[])
  { // erstes kommandozeilenargument
    Charset cs = new Charset(argv[0]);
    Enumeration e = cs.characters();
    while (e.hasMoreElements())
      System.out.println(e.nextElement());
  }
}
 e) Testlauf:
 
Eingabe:
java It_CharsetMain Java
 
Ausgabe:
J a v
Syntax-Erweiterungen und neue Regeln für Elementklassen
 
 a)
Wie sprechen ``innere Objekte'' das ihnen zugeordnete Objekt der
umgebenden Klasse an?
 
Im  Charset-Beispiel:
 
 
 
 
 pos und  setsize sind Attribute der Selbstreferenz 
 this .
Ihr expliziter Name wäre also this.pos, bzw. this.setsize.
 
Um  chars explizit anzusprechen, schreiben wir:
    Charset.this.chars
b) Tiefere Schachtelung von Elementklassen?
 
Beliebig möglich. Allerdings darf keine 
der eingeschachtelten Klassen so heißen
wie eine der sie umgebenden!
 
 Das bedeutet:
Obige neue Syntax:
    classname.this
public class A
{ public string name = "a";
  public class B
  { public string name = "b";
    public class C
    { public string name = "c";
      public void print()
      { System.out.println(name);
        System.out.println(this.name);
        System.out.println(C.this.name);
        System.out.println(B.this.name);
        System.out.println(A.this.name);
      }
    }
  }
}
c) Welches ist das umgebende Objekt?
 
In unserem Beispiel haben wir den Default-Fall benutzt:
  public Enumeration characters()
    { return new cs_iterator();
    }
Hier: Keine Angabe des umgebenden Objektes für die neue Instanz der inneren Klasse. Daher wird die Selbstreferenz this als umgebendes Objekt angenommen:
 
Äquivalent zu obigem ist die neue  explizite Schreibweise:
  public Enumeration characters()
    { return this.new cs_iterator();
    }
Für andere Fälle brauchen wir die neue allgemeine Syntax:
containing_instance.new inner_class(...)
wobei  containing_instance eine Instanz der umfassenden Klasse
von  inner_class ist.
d) Innere Objekte außerhalb der umgebenden Klasse erzeugen?
 
Geht, wenn die innere Klasse nicht wie in unserem 
 Charset-Beispiel privat ist.
 
Also ändern:
  // innere Elementklasse:
  public class Cs_iterator 
  implements Enumeration
  { private int pos;
    ....
  }
 
Dann funktioniert auch folgendes Hauptprogramm:
  public static void main(String argv[])
    { // erstes kommandozeilenargument
      Charset cs = new Charset(argv[0]);
      Enumeration e = cs.new cs_iterator();
      while (e.hasMoreElements())
        System.out.println(e.nextElement());
    }
Noch ein Beispiel für die externe Erzeugung innerer Objekte.
Klassenhierarchie von Seite ![[*]](cross_ref_motif.gif) :
:
A a = new A; // A Instanz A.B b = a.new B(); // B Instanz in a A.B.C c = b.new C(); // C Instanz in b c.print(); // Methode aus C
 
e) Kann man von inneren Klassen erben?
 
Ja ( ``strange as it may seem'').
Damit gibt es aber auch eine zweite Art, wie Objekte innerer Klassen erzeugt werden können: mit dem Oberklassen-Konstruktoraufruf super.
 
Zusätzlich zur Syntax 
containing_instance.new inner_class(...)
containing_instance.super(...)
f) Ändern sich durch innere Klassen die Regeln für die Namensbindung?
 
Ja. Wir haben jetzt  drei Hierarchien in Java-Programmen:
Aufgabe der Namensanalyse: Finde zu jedem Auftreten eines Bezeichners die laut Sprachdefinition dazugehörige Definition.
 
 Beispiel:
 
Eine Methode der Klasse K verwendet in einer Anweisung den Bezeichner x.
 
Ist  x
Vererbungshierarchie und Klassenschachtelung im Bild:
 
 
 
Die beiden Hierarchien sind völlig unabhängig voneinander.
Es ist wichtig, sie nicht zu verwechseln!
Konfliktfall zwischen umfassender Klasse und Oberklasse
 
In Java 1.1. gelöst zugunsten der Oberklasse, d.h.
der Bezeichner x der Oberklasse verdeckt den der umfassenden Klasse.
 
 Zusätzlich zu dieser Regel erfordert Java im Konfliktfall die
 explizite Benennung des Bezeichners, d.h.
 
 
Sehr ähnlich zu Elementklassen (siehe Seite ![[*]](cross_ref_motif.gif) )
mit folgenden wichtigen Unterschieden:
)
mit folgenden wichtigen Unterschieden:
![[*]](cross_ref_motif.gif) ) z.B. innerhalb von
Anweisungsblöcken definiert.
) z.B. innerhalb von
Anweisungsblöcken definiert. 
Anonyme Klassen haben keinen Namen. Sie entstehen immer zusammen mit einem Objekt.
import java.io.*;
// gibt die Namen aller .java-Dateien im 
// angegebenen Verzeichnis aus
public class Dirlist
{public static void main(String argv[])
 { File f = new File(argv[0]);
   String[] list = 
     f.list (new FilenameFilter() 
     { public boolean accept(File f, 
                             String s)
       { return s.endsWith(".java");
       }
     }
             ); // Formatierung schwierig!
   for (int i = 0; i < list.length; i++)
     System.out.println(list[i]);
 }
}
Die Syntax für anonyme Klassen ist
new-expression class-body
Für die anonyme Klasse kann man keine extends- oder implements-Klauseln angeben.
 
 Daher:
 
In unserem Beispiel
implementiert die anonyme Klasse ein Interface:
   f.list(new FilenameFilter() 
          { ...
          }
         ):
 
Da anonyme Klassen keinen Namen haben, können sie auch keine
Konstruktoren
haben. Deshalb hat Java 1.1 
die 
Instanzinitialisierungsblöcke eingeführt
(Seite ![[*]](cross_ref_motif.gif) ).
).
Die Sichtbarkeitsspezifikationen
geben an, wo Attribute und Methoden einer Klasse benutzt werden können.
 
| Benutzbar in | Sichtbarkeit | |||
|  |  |  |  | |
| Selber Klasse | ja | ja | ja | ja | 
| Klasse, selbes Paket | ja | ja | ja | nein | 
| Unterklasse, anderes Paket | ja | ja | nein | nein | 
| Klasse, anderes Paket | ja | nein | nein | nein | 
Besonderheit im Zugriff auf protected-Elemente aus Unterklassen:
 
 Erlaubt ist der Zugriff auf  protected-Elemente des eigenen
Oberklassenanteils. 
Nicht Erlaubt ist der Zugriff auf protected-Elemente in anderen Objekten vom Oberklassentyp.
 
 Beispiel:
 
Klasse  A im Paket  pack: 
   package pack;
   public class A
   { protected int x;
   }
 
Klasse  B in einem anderen Paket: 
   class B extends pack.A
   { void doit()
     { x = 18;
       pack.A va = new pack.A();
      va.x = 19;    // NICHT ZULÄSSIG !!
     }
   }