Informatik Test 14.10.2026

Stoff: Grundlagen der Objektorientierung in Java

  • Folien „Objektorientierung” (Klassen, Objekte, Kapselung, Konstruktoren, static, Strings, StringBuilder)
  • Folien „Pakete und Vererbung 1_2” (Pakete, import, Vererbung, super, Überschreiben, Casts, instanceof, Polymorphie)

So lernst du damit

  1. Teil 1 und 2 durchlesen (Theorie)
  2. Teil 3 (Code-Fallen) genau anschauen
  3. Teil 4 und 5 selbst lösen, erst dann die Lösung aufklappen

Alle Ausgaben in den Übungen wurden mit Java getestet.


Teil 1 – Objektorientierung

1.1 Klassen und Objekte

  • Klasse = selbst definierter Datentyp = Bauplan für Objekte
  • Objekt = Exemplar / Instanz einer Klasse
  • Eine Klasse beschreibt:
    • Attribute (was das Objekt hat) → Variablen / Felder / Instanzvariablen → Zustand
    • Operationen (was das Objekt kann) → Methoden → Verhalten
  • Merksatz: „Ich bin, ich habe, ich kann …”
  • Klassen können wie eingebaute Datentypen verwendet werden (ähnlich wie Arrays oder Strukturen/struct in C)
  • Klassen fassen mehrere Elemente zu einem neuen Element unter einem gemeinsamen Namen zusammen
  • Begriffe: Attribute + Operationen heißen zusammen Eigenschaften eines Objekts; oft werden aber nur die Attribute als Eigenschaften bezeichnet
public class Car {
    private String name;
    private int numberOfWheels;
}
 
Car a, b;          // a und b sind vom Typ Car, zunächst null
a = new Car();     // Objekt erzeugen, Konstruktor wird aufgerufen
b = new Car();     // gleicher Bauplan, aber andere Identität
  • new → die Speicherverwaltung sucht freien Speicher und gibt eine Referenz auf das neue Objekt zurück
  • Die Klammern bei new Car() sind der Konstruktoraufruf

1.2 Datenkapselung (Information Hiding)

  • Attribute sollen von außen verborgen bleiben → private
  • Methoden bleiben von außen zugänglich → public
  • Zugriff auf Attribute nur über Getter/Setter

1.3 Zugriffsattribute (Sichtbarkeit)

ZugriffsstufeKlassePaketUnterklasseÜberall
public✅✅✅✅
protected✅✅✅❌
default (kein Modifier, package-private)✅✅❌❌
private✅❌❌❌

Reihenfolge von offen nach geschlossen: public → protected → default → private

1.4 Referenzvariablen, Zuweisung, Vergleich

  • Car a = new Car(); → a ist eine Referenzvariable
  • Zugriff: referenz.attribut bzw. referenz.methode()
  • Referenzvariablen gleichen Typs kann man einander zuweisen → beide zeigen auf dasselbe Objekt
  • Jeder Referenzvariable darf null zugewiesen werden
  • == und != vergleichen die Referenzen (Adressen), nicht die Inhalte!
  • Der Typ der Variable muss mit dem Typ des erzeugten Objekts kompatibel sein
  • Direkter Zugriff auf private Attribute von außen sollte vermieden werden (innerhalb der Klasse aber möglich). Besser über public Methoden zugreifen.

1.5 Speicherverwaltung

StackHeap
lokale Variablenmit new erzeugte Objekte
Methodenaufrufe mit ParameternSpeicher für alle Attribute + Verwaltungsinfos
  • Garbage Collector (GC): automatische Speicherbereinigung. Er räumt nicht mehr referenzierte Objekte auf und gibt den Speicher frei.
  • Heapgröße seit Java 5: ¼ des Hauptspeichers (max. 1 GiB)
    • Anzeigen (Windows): java -XX:+PrintFlagsFinal -version | findstr /i "HeapSize PermSize ThreadStackSize"
  • Bei kleinen Programmen arbeitet der GC kaum
  • Verschiedene JVMs haben verschiedene GC-Algorithmen

1.6 Methoden

Vier Fälle (wie in C):

  1. ohne Parameter, ohne Rückgabe → void a()
  2. mit Parameter, ohne Rückgabe → void a(int x)
  3. ohne Parameter, mit Rückgabe → int a()
  4. mit Parameter, mit Rückgabe → int a(int x)
  • Methoden stehen immer in Klassen
  • Statische Methoden (static) gehören zur Klasse und werden ohne Objekt aufgerufen

In Java gibt es nur Call by Value

Bei Objekten wird der Wert der Referenz kopiert. Die Methode kann also das Objekt verändern (p.x = 99), aber die Referenz-Variable des Aufrufers nicht umbiegen (p = new ... wirkt nur lokal).

1.7 Die this-Referenz

  • Jedes Objekt hat eine this-Referenz, einen Zeiger auf sich selbst
  • In jeder (nicht-statischen) Methode automatisch definiert
  • Unterscheidet Objektvariable von gleichnamiger lokaler Variable / Parameter
class Car {
    String name;                       // Objektvariable
    void printName(String name) {      // Parameter
        System.out.println(name);      // Parameter
        System.out.println(this.name); // Objektvariable
    }
}

1.8 Getter und Setter

private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }

Vorteile: Kontrolle der Werte (z.B. kein negatives Alter), Hilfe bei der Fehlersuche, Information Hiding

1.9 Konstruktoren

  • Spezielle Methode, die bei new automatisch aufgerufen wird → Attribute initialisieren
  • Gleicher Name wie die Klasse, kein Rückgabetyp (auch nicht void!), kann Parameter haben
  • Eine Klasse kann mehrere Konstruktoren haben (Überladen); der passende wird ausgewählt
  • Parameter am besten final, damit nicht versehentlich color = color; statt this.color = color; passiert
    • final-Parametern kann man nichts zuweisen, Änderungen am Objekt sind aber erlaubt

Default-Konstruktor

Schreibt man keinen Konstruktor, erzeugt Java automatisch einen leeren Default-Konstruktor.
Schreibt man mindestens einen Konstruktor, gibt es keinen Default-Konstruktor mehr. Dann muss man public Car() {} selbst schreiben.

Konstruktorverkettung mit this(...) (muss die erste Anweisung sein):

public Car(final int numberOfWheels, final String name) {
    this.numberOfWheels = numberOfWheels;
    this.name = name;
}
public Car(final String name) {
    this(4, name);   // ruft den anderen Konstruktor auf
}

Kopierkonstruktor (statt a = b, das nur die Referenz kopiert):

public Car(final Car c) {
    this.name = c.name;
    this.numberOfWheels = c.numberOfWheels;
}
Car car4 = new Car("Fiat", 4);
Car car5 = new Car(car4);   // neues Objekt, gleiche Werte
  • Flache Kopie: enthält das Objekt selbst Objekte oder Arrays, werden nur deren Referenzen kopiert. Änderungen wirken sich dann auf das Original aus.
  • Tiefe Kopie: echte Kopie inklusive aller Werte

1.10 Statische Komponenten (static)

  • Mit static: gehört zum Klassenobjekt, existiert einmal pro Klasse und wird von allen Objekten geteilt
  • Hintergrund: Auch die Klasse ist ein Objekt (Klassenobjekt = Schablone, die das Aussehen der Klasse festlegt). Klassenobjekt und Objekte können Attribute und Methoden haben.
  • Ohne static: gehört zum einzelnen Objekt, jedes Objekt hat eine eigene Kopie
class BankAccount {
    private String name;
    private double amount;
    private static int bankAccountCounter = 0;   // gemeinsamer Zähler
 
    public BankAccount(final String name, final double amount) {
        this.name = (name != null) ? name : "";
        this.amount = (amount > 0.0) ? amount : 0.0;
        bankAccountCounter++;
    }
    public static int getCounter() { return bankAccountCounter; }
}

Objektbezogen vs. Statisch (Folie 29):

ObjektbezogenStatisch
Deklarationohne staticmit static
existierenin jedem Objektnur einmal pro Klasse
Felder werden angelegtwenn das Objekt erzeugt wirdwenn die Klasse geladen wird
Felder werden freigegebenvom Garbage Collector, wenn keine Referenz mehr auf das Objekt existiertwenn die Klasse entladen wird (am Ende des Programms)
Konstruktor wird aufgerufenwenn das Objekt erzeugt wirdwenn die Klasse geladen wird (statischer Initialisierer)
Felder ansprechenobj.field, this.fieldClass.field
Methoden aufrufenobj.m();, this.m();Class.m();

Zugriffsregeln:

  • Objektmethoden und Konstruktoren → dürfen auf Objekt- und Klassenfelder/-methoden zugreifen
  • Klassenmethoden (static) → dürfen nur auf Klassenfelder/-methoden zugreifen. Es gibt kein this!
  • Aufruf: Klassenname.feld / Klassenname.methode(); innerhalb der Klasse geht es auch ohne Klassenname
  • main ist eine statische Methode

Statischer Initialisierer: kein Name, keine Parameter, nur einer pro Klasse. Läuft beim Laden der Klasse:

static {
    bankAccountCounter = 0;
}

1.11 Strings

								- String-Variablen enthalten **Referenzen** auf String-Objekte
  • Operator + und += zum Verketten
  • Strings sind unveränderlich (immutable): Jede „Änderung” erzeugt ein neues Objekt
  • Konstruktoren:
String s1 = "Hallo";
String s2 = new String();               // ""
String s3 = new String(s1);             // "Hallo" (neues Objekt!)
char[] charArr = {'S','W','E','2'};
String s4 = new String(charArr);        // "SWE2"
String s5 = new String(charArr, 1, 2);  // "WE"  (ab Index 1, 2 Zeichen)

Vergleiche:

String s1 = "Hallo";
String s2 = "Hallo";
System.out.println(s2 == s1);                      // true  (gleiches Literal → gleiches Objekt im String-Pool)
String s3 = new String(s1);
System.out.println(s3 == s1);                      // false (new → neues Objekt)
System.out.println(s1.equals(s3));                 // true  (Inhalt gleich)
System.out.println(s1.equals("HALLO"));            // false
System.out.println(s1.equalsIgnoreCase("HALLO"));  // true
String s4 = "Halla";
String s5 = "Hallo SWE2";
System.out.println(s1.compareTo(s2));  // 0   → gleich
System.out.println(s1.compareTo(s4));  // 14  → 'o'(111) - 'a'(97)
System.out.println(s1.compareTo(s5));  // -5  → Längen: 5 - 10

compareTo

  • 0 → gleich
  • < 0 → erster String kommt alphabetisch vorher
  • > 0 → erster String kommt nachher
  • Wert = Differenz der ersten unterschiedlichen Zeichen (Unicode). Ist ein String Anfang des anderen, ist der Wert die Längendifferenz.

Hilfreiche Methoden (Indizes beginnen bei 0!):

String s1 = "Das ist ein spezieller Test";
//           0123456789...
s1.length()            // 27
s1.charAt(0)           // D
s1.indexOf('i')        // 4   erstes 'i'
s1.lastIndexOf('i')    // 16  letztes 'i'
s1.indexOf('i', 5)     // 9   erstes 'i' ab Index 5
s1.lastIndexOf('i', 10)// 9   letztes 'i' bis Index 10 (rückwärts suchen)
s1.indexOf("ei")       // 8
s1.substring(8)        // "ein spezieller Test"   ab Index 8 bis Ende
s1.substring(4, 15)    // "ist ein spe"           Index 4 bis 14 (Ende exklusiv!)

substring(von, bis)

bis ist exklusiv: substring(4, 15) liefert die Zeichen 4 bis 14, also 15 - 4 = 11 Zeichen.
indexOf liefert -1, wenn nichts gefunden wird.

1.12 StringBuilder

  • Verhält sich wie ein String, ist aber veränderbar
  • Gut zum Aufbauen von Zeichenketten, weil nicht bei jeder Verkettung ein neues Objekt entsteht
  • Am Ende mit toString() in einen String umwandeln
  • Methoden geben den StringBuilder zurück, deshalb kann man sie verketten
StringBuilder sb = new StringBuilder("Test");
sb.append(" SWE2");          // "Test SWE2"
sb.insert(4, " und");        // "Test und SWE2"
sb.delete(4, 8);             // "Test SWE2"       (löscht Index 4–7)
sb.replace(4, 9, "string");  // "Teststring"      (ersetzt Index 4–8)
sb.append(" ").insert(sb.length() - 1, " SWE2").append(sb.charAt(0));
System.out.println(sb.toString() + "est");
// Ausgabe: Teststring SWE2 Test

Erklärung letzte Zeile

"Teststring" → append(" ") → "Teststring " → insert(10, " SWE2") (vor das Leerzeichen) → "Teststring SWE2 " → append('T') → "Teststring SWE2 T" → + "est" → Teststring SWE2 Test
(Auf der Folie ist die Ausgabe auf zwei Zeilen umgebrochen; tatsächlich steht alles in einer Zeile.)


Teil 2 – Pakete und Vererbung

2.1 Pakete

  • Java hat eine riesige Standardbibliothek (Java-API)
  • Paket = Gruppe thematisch zusammengehöriger Typen (Klassen); Unterpakete durch Punkt getrennt
  • Standardbibliothek: Pakete beginnen mit java / javax → eigene Pakete nicht mit java beginnen
  • Paketnamen klein, keine Sonderzeichen, am besten Englisch
  • Pakete sind als Ordnerstruktur angelegt
  • Es gibt auch Pakete ohne Typen (nur mit Unterpaketen)

Klasse aus anderem Paket verwenden, zwei Möglichkeiten:

// 1. Voll qualifiziert
java.awt.Point p = new java.awt.Point();
 
// 2. Import am Dateianfang
import java.awt.Point;
Point p = new Point();
  • java.lang ist automatisch importiert (String, Math, System, …)
  • Stern-Import * = alle Typen eines Pakets, nur auf der letzten Ebene erlaubt:
Importgültig?
import java.awt.*;✅
import java.io.*;✅
import *;❌
import java.awt.Po*;❌
import java.*;✅ syntaktisch korrekt, aber sinnlos (dort gibt es nur Unterpakete)
  • Unterpakete werden durch * nicht mit importiert
  • Vorsicht bei gleichnamigen Klassen: java.util.Date vs. java.sql.Date
  • Viele Imports = Zeichen für eine komplexe Klasse; viele Sterne verstecken die Abhängigkeiten

Eigene Pakete: Domain umdrehen, z.B. fallmerayer.it → Paket it.fallmerayer → Ordner it/fallmerayer/

package it.fallmerayer;      // ganz oben in der Datei
 
public class DatePrinter { ... }
  • Ohne package → default package (unnamed): nicht empfohlen, Typen daraus können nicht importiert werden
  • Statischer Import: import static java.lang.System.out; → dann geht out.println("Hello World!");

Aufbau einer .java-Datei (Kompilationseinheit):

  1. Paketdeklaration → maximal eine
  2. import-Deklarationen → beliebig viele
  3. Typdeklarationen (Klassen) → beliebig viele

2.2 Beziehungen zwischen Klassen

BeziehungBedeutungBeispiel
Assoziationhat-einAuto hat Reifen, Mensch hat Beine
Vererbungist-eine-Art-von (Generalisierung/Spezialisierung)Cabrio ist ein Auto, Mensch ist ein Säugetier

2.3 Vererbung in Java

class Cabrio extends Auto { ... }
  • Schlüsselwort extends
  • Unterklasse = Subklasse, Kindklasse, Erweiterungsklasse
  • Oberklasse = Superklasse, Elternklasse
  • Nur von Klassen erben, nicht von primitiven Datentypen
  • Ohne extends erbt jede Klasse automatisch von java.lang.Object → class Car ≡ class Car extends Object
    • Deshalb hat jede Klasse z.B. toString()
  • Keine Mehrfachvererbung in Java (anders als in C++ oder Python)
    • Grund u.a. das Diamantproblem: O hat x; K1 und K2 erben von O; U erbt von K1 und K2 → Woher kommt x? Hat K1 oder K2 x verändert?
    • Bei gleichnamigen Methoden in 2 Oberklassen muss man in C++ die Oberklasse mit :: angeben

2.4 Konstruktoren und Vererbung

  • Konstruktoren werden nicht vererbt (gehören zum statischen Teil der Klasse)
    • in Python dagegen schon
  • Mit dem Konstruktor der Oberklasse kann man kein Objekt der Unterklasse erzeugen
  • Die Unterklasse sammelt alle sichtbaren Eigenschaften (Attribute + Methoden) der Oberklasse
  • Die Eigenschaften der Oberklasse müssen ggf. in deren Konstruktor initialisiert werden. Deshalb wird er immer mit aufgerufen.
  • Jede Unterklasse braucht eigene Konstruktoren
  • Java ruft in jedem Konstruktor automatisch zuerst super() auf, den Standardkonstruktor der Oberklasse
// Das schreibst du:            // Das macht die Laufzeitumgebung daraus:
class Car { }                   class Car {
class Cabriolet extends Car { }     Car() { super(); }        // ruft Object()
                                }
                                class Cabriolet extends Car {
                                    Cabriolet() { super(); }  // ruft Car()
                                }

super(...):

  • Muss die erste Anweisung im Konstruktor sein
  • Kann mit Argumenten aufgerufen werden, wenn die Oberklasse keinen Standardkonstruktor hat
class Car {
    public String name;
    Car(String name) { this.name = name; }
}
class Cabriolet extends Car { }       // ❌ COMPILERFEHLER: Car() existiert nicht!
 
class Cabriolet extends Car {
    Cabriolet() { super("Volvo"); }   // ✅
}

super vs. this:

  • super() und this() müssen beide die erste Anweisung sein → nicht gemeinsam möglich
  • Lösung: super(...) bleibt erste Anweisung, der restliche Code kommt in eine private Methode (z.B. initialize()) oder wird über Setter erledigt
  • super.super.methode() ist nicht möglich. Eine Klasse kennt nur ihre direkte Oberklasse.

2.5 Überladen vs. Überschreiben

Überladen (Overloading)Überschreiben (Overriding)
Wo?in derselben Klassein der Unterklasse
Namegleichgleich
Parameterlisteunterschiedlich (Anzahl/Typen)gleich (gleiche Signatur)
Rückgabetypegalmuss gleich sein
  • Signatur = Methodenname + Parameterliste

2.6 toString() überschreiben

  • Jede Klasse erbt toString() von Object → Standardausgabe: Car@48cf768c (nicht aussagekräftig)
  • Sollte überschrieben werden: Klassenname + Zustand (Attributwerte)
  • @Override davor schreiben (Annotation; der Compiler prüft, ob wirklich überschrieben wird)
public class Testarossa extends Ferrari {
    public int hubraum = 2000;
 
    @Override
    public String toString() {
        return getClass().getSimpleName() + " [name=" + name + ", hubraum=" + hubraum + "]";
        // Alternative mit format:
        // return String.format("%s[name=%s, hubraum=%d]", getClass().getSimpleName(), name, hubraum);
        // Oberklassen-Version erweitern:
        // return super.toString() + "[name=" + name + ", hubraum=" + hubraum + "]";
    }
}
// Ausgabe: Testarossa [name=Ferrari, hubraum=2000]
// mit super.toString(): skriptum.Testarossa@4fca772d[name=Ferrari, hubraum=2000]
  • System.out.println(obj) ruft automatisch obj.toString() auf

2.7 Typen in Hierarchien (Upcast / Downcast)

Hierarchie aus den Folien:

        Object
          │
       Ferrari   (# String name)   ← protected
        /    \
California  Testarossa  (- int hubraum)

Implizite Typumwandlung (Upcast), geht automatisch:

Ferrari testarossaIsFerrari = new Testarossa();   // Unterklasse → Oberklasse ✅
Object  testarossaIsObject  = new Testarossa();   // ✅

Referenztyp vs. Objekttyp

  • Referenztyp (Compilertyp) = Typ der Variable → bestimmt, was man aufrufen darf
  • Objekttyp (JVM-/Laufzeittyp) = Typ von new ... → bestimmt, welche überschriebene Methode ausgeführt wird
Ferrari f = new Testarossa();
System.out.println(f.name);      // ✅ name ist in Ferrari
System.out.println(f.hubraum);   // ❌ Compilerfehler: hubraum cannot be resolved
                                 //    (Compiler sieht nur Ferrari!)
 
Testarossa t = f;                // ❌ Type mismatch: cannot convert from Ferrari to Testarossa
Testarossa t = (Testarossa) f;   // ✅ expliziter Cast (Downcast)
System.out.println(t.hubraum);   // ✅

Fehler beim Cast: ClassCastException (zur Laufzeit!)

Ferrari californiaIsFerrari = new California();
Testarossa meinFerrari = (Testarossa) californiaIsFerrari;
// kompiliert ✅, aber zur Laufzeit: ClassCastException
// (California ist kein Testarossa)

2.8 Liskovsches Substitutionsprinzip

  • Benannt nach Barbara Liskov
  • Überall, wo ein Objekt der Oberklasse erwartet wird, darf auch ein Objekt einer Unterklasse verwendet werden
public static void showNameAndHubraum(Ferrari ferrari) {
    System.out.println(ferrari.name);
}
showNameAndHubraum(testarossaIsFerrari);  // ✅
showNameAndHubraum(californiaIsFerrari);  // ✅
// println(Object) nimmt deshalb jedes beliebige Objekt an

2.9 instanceof

  • Prüft zur Laufzeit, ob eine Referenz ≠ null ist und einen bestimmten Typ hat
  • Die Typen müssen in einer Hierarchie stehen, sonst Compilerfehler
"Hallo" instanceof StringBuilder   // ❌ Compilerfehler (String/StringBuilder nicht verwandt)
 
Object meinFerrari = new Testarossa();
meinFerrari instanceof Testarossa  // true
meinFerrari instanceof Ferrari     // true
meinFerrari instanceof Object      // true
meinFerrari instanceof California  // false
meinFerrari instanceof String      // false
null instanceof Object             // false

Typisch: erst prüfen, dann casten:

if (obj instanceof Testarossa) {
    Testarossa t = (Testarossa) obj;
}

2.10 final bei Klassen und Methoden

Wirkung
final classEs können keine Unterklassen gebildet werden (z.B. String, StringBuilder, Integer, Double, Math, System)
final Methodekann in Unterklassen nicht überschrieben werden
final Variable/Parameterkann nach der Zuweisung nicht mehr geändert werden
  • private Methoden können nicht überschrieben werden, sie werden verdeckt (wie Attribute)

2.11 Polymorphie und dynamisches Binden

Testarossa a = new Testarossa();
Ferrari    b = new Testarossa();
Object     c = new Testarossa();
 
System.out.println(a.toString());  // Testarossa [name=Ferrari, hubraum=2000]
System.out.println(b.toString());  // Testarossa [name=Ferrari, hubraum=2000]
System.out.println(c.toString());  // Testarossa [name=Ferrari, hubraum=2000]
  • Der Compiler würde bei b und c toString() aus Object erwarten. Die Laufzeitumgebung schaut aber auf den echten Objekttyp und nimmt die überschriebene Methode → spätes / dynamisches Binden = Polymorphie

Ausnahmen: NICHT dynamisch gebunden werden

  • private Methoden
  • static Methoden
  • final Methoden
  • Attribute (Felder) werden nie dynamisch gebunden. Es zählt der Referenztyp!
int a = 1;   // in Oberklasse
int a = 2;   // in Unterklasse (verdeckt das Attribut)
// in der Unterklasse:
System.out.println(super.a);  // 1
System.out.println(a);        // 2

Übungen aus den Folien

Übung Auto (Folie 17)

  • Klasse Car mit Attributen Name, Zulassungsjahr, Höchstgeschwindigkeit, Farbe (Datentyp selbst wählen)
  • main-Methode: drei Exemplare erzeugen und sinnvolle Werte zuweisen
  • Methoden ohne Rückgabewert:
    • fahre(speed) → Ausgabe z.B. „Fiat fährt 100 Km/h.”
    • gibFarbeAus() → gibt die Farbe aus
    • gibAlterAus() → berechnet das Alter des Autos

Übung Rechteck (Folie 22)

  • Eigenschaften: Länge, Breite, Fläche, Umfang, Länge der Diagonalen
  • Klasse Rectangle mit den Konstruktoren:
    • Rectangle() → Länge 1, Breite 1
    • Rectangle(float l, float b) → Länge l, Breite b
  • a) nur mit Konstruktoren arbeiten und alle Werte dort ausgeben
  • b) Getter für alle Attribute schreiben und die Werte in main ausgeben
  • Formeln: Fläche l * b, Umfang 2 * (l + b), Diagonale Math.sqrt(l*l + b*b)

Deine Lösungen liegen in Informatik/Skripte/01_Uebung und 02_Uebung.


Teil 3 – Typische Fallen (Kurzüberblick)

SituationErgebnis
a == b bei Objektenvergleicht Referenzen, nicht Inhalte
"abc" == "abc"true (String-Pool)
new String("abc") == "abc"false
s.toUpperCase(); ohne Zuweisungs bleibt unverändert (immutable)
eigener Konstruktor mit Parametern, dann new X()Compilerfehler, kein Default-Konstruktor mehr
Oberklasse hat nur X(String), Unterklasse ohne super("...")Compilerfehler
super() / this() nicht als erste ZeileCompilerfehler
super() und this() im selben KonstruktorCompilerfehler
Ober o = new Unter(); o.methodeNurInUnter();Compilerfehler
Unter u = (Unter) new Ober();kompiliert, ClassCastException zur Laufzeit
Ober o = new Unter(); o.ueberschriebeneMethode()Methode der Unterklasse (dynamisches Binden)
Ober o = new Unter(); o.attributAttribut der Oberklasse (Referenztyp zählt)
Ober o = new Unter(); o.staticMethode()Methode der Oberklasse (static wird nicht dynamisch gebunden)
this in static MethodeCompilerfehler
Objektvariable in static Methode verwendenCompilerfehler
class B extends A, CCompilerfehler, keine Mehrfachvererbung
class X extends StringCompilerfehler, String ist final
1 + 2 + "3" + 4 + 5"3345" (links nach rechts: erst 3, dann String)

Teil 4 – Übungen: Was ist die Ausgabe?

Aufgabe 1 – Vererbung, Konstruktoren, Polymorphie

class Tier {
    protected String name = "Tier";
    int a = 1;
    static int zaehler = 0;
 
    Tier() { System.out.println("Konstruktor Tier"); zaehler++; }
    Tier(String n) { this(); name = n; System.out.println("Tier(" + n + ")"); }
 
    public String laut() { return "..."; }
    public static String info() { return "static Tier"; }
    @Override public String toString() { return "Tier[" + name + "]"; }
}
 
class Hund extends Tier {
    int a = 2;
    Hund() { super("Hund"); System.out.println("Konstruktor Hund"); }
    @Override public String laut() { return "Wuff"; }
    public static String info() { return "static Hund"; }
    void zeige() { System.out.println(a + " " + super.a + " " + this.a); }
}
 
// in main:
Tier t = new Hund();
System.out.println(t.laut());
System.out.println(t);
System.out.println(t.a);
System.out.println(((Hund) t).a);
System.out.println(t.info());
((Hund) t).zeige();

Aufgabe 2 – instanceof, static, Cast

class Katze extends Tier {
    @Override public String laut() { return "Miau"; }
}
 
// in main, direkt nach Aufgabe 1:
Tier k = new Katze();
System.out.println(k.laut() + " " + k.name);
System.out.println(Tier.zaehler);
System.out.println(k instanceof Tier);
System.out.println(k instanceof Hund);
System.out.println(t instanceof Hund);
Hund h = (Hund) k;

Aufgabe 3 – Call by Value und Referenzen

class Punkt { int x; Punkt(int x) { this.x = x; } }
 
static void aendere(int z, Punkt p) {
    z = 99;
    p.x = 99;
    p = new Punkt(5);
    p.x = 7;
}
 
// in main:
int z = 1;
Punkt p = new Punkt(1);
aendere(z, p);
System.out.println(z + " " + p.x);
Punkt q = p;
q.x = 3;
System.out.println(p.x + " " + (p == q));
Punkt r = new Punkt(3);
System.out.println(p == r);

Aufgabe 4 – Strings

String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1.equals(s3));
System.out.println(s1.compareTo("Javb"));
 
String s = "Informatik";
System.out.println(s.length() + " " + s.charAt(2) + " " + s.indexOf('o')
        + " " + s.substring(2, 6) + " " + s.lastIndexOf('a'));
s.toUpperCase();
System.out.println(s);

Aufgabe 5 – StringBuilder

StringBuilder sb = new StringBuilder("abc");
sb.append("def").insert(0, "X").delete(1, 3);
System.out.println(sb + " " + sb.length());
sb.replace(0, 2, "Hi");
System.out.println(sb);

Aufgabe 6 – Kompiliert das? (Ja / Nein + Grund)

class A { A(int x) { } }
class B extends A { }                              // (a)
 
class C { C(String s) { } }
// in main: C c = new C();                         // (b)
 
class D {
    int wert;
    static void m() { System.out.println(this.wert); }   // (c)
}
 
class E extends D {
    E() { this(5); super(); }                      // (d)
    E(int x) { }
}
 
final class F { }
class G extends F { }                              // (e)
 
Object o = new String("x");
System.out.println(o.length());                    // (f)
 
import java.*;                                     // (g)
import java.util.Da*;                              // (h)

Teil 5 – Übungen: Code vervollständigen

Aufgabe 7 – Klasse Person

Vervollständige die Klasse:

  • private Attribute name (String) und alter (int)
  • statischer Zähler anzahl, der jedes erzeugte Objekt mitzählt
  • Konstruktor Person(String name, int alter)
  • Konstruktor Person(String name) → Alter 0, Konstruktorverkettung verwenden
  • Kopierkonstruktor
  • Getter für beide Attribute, Setter für alter (negative Werte → 0)
  • statischer Getter für anzahl
  • toString() überschreiben → Person [name=Anna, alter=17]
package it.fallmerayer;
 
class Person {
    _______ String name;
    _______ int alter;
    _______ _______ int anzahl = 0;
 
    public Person(final String name, final int alter) {
        ____________________
        ____________________
        ____________________
    }
 
    public Person(final String name) {
        ____________________
    }
 
    public Person(final Person p) {
        ____________________
    }
 
    // Getter, Setter, getAnzahl, toString ...
}

Aufgabe 8 – Unterklasse Schueler

Erstelle Schueler als Unterklasse von Person mit dem zusätzlichen Attribut klasse (String). Der Konstruktor soll den Konstruktor der Oberklasse verwenden. toString() soll die Version der Oberklasse erweitern.

class Schueler ________ Person {
    private String klasse;
 
    public Schueler(final String name, final int alter, final String klasse) {
        ____________________
        ____________________
    }
 
    ____________
    public String toString() {
        return ____________________ + "[klasse=" + klasse + "]";
    }
}

Aufgabe 9 – Was gibt main aus? (mit Aufgabe 7 + 8)

public class Main {
    public static void main(String[] args) {
        Person a = new Person("Anna", 17);
        Person b = new Person("Ben");
        Person c = new Person(a);
        Person s = new Schueler("Leon", 17, "4BT");
        b.setAlter(-5);
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
        System.out.println(s);
        System.out.println(Person.getAnzahl());
        if (s instanceof Schueler) {
            Schueler sch = (Schueler) s;
            System.out.println(sch.getName());
        }
    }
}

Aufgabe 10 – Ergänze die Lücken (Theorie)

  1. Eine Klasse ist ein ____ für Objekte.
  2. Attribute sollten ____ sein, Methoden meist ____.
  3. Der Konstruktor hat den gleichen Namen wie die ____ und keinen ____.
  4. == vergleicht bei Objekten die ____, equals() den ____.
  5. Objekte liegen im ____, lokale Variablen im ____.
  6. Jede Klasse erbt direkt oder indirekt von ____.
  7. Mit dem Schlüsselwort ____ erbt eine Klasse von einer anderen.
  8. super() muss die ____ Anweisung im Konstruktor sein.
  9. Überschreiben: gleiche ____, Überladen: unterschiedliche ____.
  10. Methoden, die ____, ____ oder ____ sind, werden nicht dynamisch gebunden.
  11. Das Paket ____ wird automatisch importiert.
  12. Ein falscher Downcast führt zur Laufzeit zu einer ____.

Begriffe auf einen Blick

BegriffKurz erklärt
KlasseBauplan / selbst definierter Datentyp
Objekt / Instanzkonkretes Exemplar einer Klasse
AttributVariable im Objekt (Zustand)
MethodeOperation (Verhalten)
KapselungAttribute private, Zugriff über Getter/Setter
KonstruktorInitialisierung bei new
thisReferenz auf das eigene Objekt
staticgehört zur Klasse, nicht zum Objekt
Garbage Collectorlöscht nicht mehr referenzierte Objekte
PaketGruppe zusammengehöriger Klassen (Ordner)
Vererbungist-ein-Beziehung, extends
Assoziationhat-ein-Beziehung
superZugriff auf die Oberklasse
Überschreibengleiche Signatur in der Unterklasse, @Override
Überladengleicher Name, andere Parameter
UpcastUnter → Ober, automatisch
DowncastOber → Unter, explizit (Typ), eventuell ClassCastException
instanceofTypprüfung zur Laufzeit
PolymorphieLaufzeit wählt die überschriebene Methode des echten Objekts
LiskovUnterklasse darf überall statt Oberklasse eingesetzt werden
finalKlasse: kein Erben; Methode: kein Überschreiben; Variable: konstant