OOP Klassen: Statische vs Instanz-Methoden, Attribute & Beziehungen
Dieser Beitrag ist eine umfassende Übersicht der OOP Klassenbestandteile – inklusive statischer vs. Instanz-Elemente, Beziehungen und Generics mit praktischen Beispielen.
In a Nutshell
Klassen bestehen aus Attributen, Methoden, Sichtbarkeiten und Verträgen. Statische Elemente betreffen die Klasse als Ganzes, Instanz-Elemente einzelne Objekte. Generics erhöhen Typsicherheit und Wiederverwendbarkeit.
Kompakte Fachbeschreibung
Eine Klasse definiert Struktur und Verhalten über Attribute, Methoden, Sichtbarkeiten, Konstruktoren, Invarianten und Verträge.
Statische vs. Instanz-Elemente:
Statische Elemente (Class-level)
- Statische Attribute: Gehören zur Klasse, nicht zu Objekten
- Statische Methoden: Können ohne Objekt aufgerufen werden
- Verwendung: Zähler, Caches, Fabriken, Konstanten
- Zugriff: Über Klassennamen, nicht über Objekt
Instanz-Elemente (Object-level)
- Instanzattribute: Jedes Objekt hat eigene Kopie
- Instanzmethoden: Operieren auf Objektzustand
- Verwendung: Objektspezifische Daten und Verhalten
- Zugriff: Nur über Objektinstanz
Klassenbeziehungen (UML):
- Assoziation: Einfache Beziehung zwischen Klassen
- Aggregation: “hat-ein” Beziehung mit unabhängigen Teilen
- Komposition: “ist-Teil-von” Beziehung mit abhängigen Teilen
- Spezialisierung: Unterklasse erbt von Basisklasse
Generics:
- Typsicherheit: Kompilierzeit-Typprüfung
- Wiederverwendung: Ein Code für verschiedene Typen
- Container:
List<T>,Map<K,V>,std::vector<T>
Prüfungsrelevante Stichpunkte
- Statische Attribute: Gehören zur Klasse, gemeinsamer Wert für alle Objekte
- Instanzattribute: Jedes Objekt hat eigene Kopie
- Statische Methoden: Ohne Objekt aufrufbar, kein this-Zeiger
- Instanzmethoden: Operieren auf Objektzustand mit this-Zeiger
- Klassenbeziehungen: Assoziation, Aggregation, Komposition, Vererbung
- UML-Notation: Verschiedene Symbole für Beziehungsarten
- Generics: Typ-Schablonen für typsichere Container
- IHK-relevant: Fundamentales Verständnis für OOP-Entwicklung
Kernkomponenten
- Statische Attribute: Klassenvariablen für gemeinsamen Zustand
- Instanzattribute: Objektvariablen für individuellen Zustand
- Statische Methoden: Klassenmethoden ohne Objektbezug
- Instanzmethoden: Objektmethoden mit Zustandszugriff
- Konstruktoren: Objektinitialisierung
- Beziehungen: Strukturelle Verbindungen zwischen Klassen
- Generics: Typ-parameterisierte Klassen und Methoden
- UML: Grafische Notation für Klassendesign
Praxisbeispiele
1. Statische vs Instanz Elemente in Java
public class Mitarbeiter {
// Statische Attribute (gehören zur Klasse)
private static int anzahlMitarbeiter = 0;
private static final String FIRMA = "TechCorp";
private static double mindestgehalt = 2000.0;
// Instanzattribute (gehören zum Objekt)
private int mitarbeiterId;
private String name;
private double gehalt;
// Statischer Initialisierungsblock
static {
System.out.println("Mitarbeiter-Klasse wird geladen");
anzahlMitarbeiter = 0;
}
// Konstruktor (Instanz-Initialisierung)
public Mitarbeiter(String name, double gehalt) {
this.mitarbeiterId = ++anzahlMitarbeiter;
this.name = name;
this.gehalt = Math.max(gehalt, mindestgehalt);
System.out.println("Mitarbeiter " + name + " erstellt (ID: " + mitarbeiterId + ")");
}
// Statische Methode (kann ohne Objekt aufgerufen werden)
public static int getAnzahlMitarbeiter() {
return anzahlMitarbeiter;
}
public static String getFirma() {
return FIRMA;
}
public static void setMindestgehalt(double mindestgehalt) {
if (mindestgehalt > 0) {
Mitarbeiter.mindestgehalt = mindestgehalt;
}
}
// Instanzmethode (benötigt Objekt)
public void erhoeheGehalt(double prozent) {
this.gehalt *= (1 + prozent / 100);
System.out.println(name + "s Gehalt erhöht auf " + gehalt);
}
public void anzeigen() {
System.out.println("ID: " + mitarbeiterId + ", Name: " + name +
", Gehalt: " + gehalt + ", Firma: " + FIRMA);
}
// Getter/Setter für Instanzattribute
public String getName() {
return name;
}
public double getGehalt() {
return gehalt;
}
}
// Verwendung der Klasse
public class MitarbeiterDemo {
public static void main(String[] args) {
// Statische Methoden aufrufen (ohne Objekt)
System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
System.out.println("Firma: " + Mitarbeiter.getFirma());
Mitarbeiter.setMindestgehalt(2500.0);
// Objekte erstellen (Instanzen)
Mitarbeiter alice = new Mitarbeiter("Alice", 3000.0);
Mitarbeiter bob = new Mitarbeiter("Bob", 2800.0);
// Instanzmethoden aufrufen
alice.erhoeheGehalt(5.0);
bob.anzeigen();
// Statische Methode nach Objekterstellung
System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
// Fehler: this in statischer Methode nicht verfügbar
// public static void fehlerhafteMethode() {
// System.out.println(this.name); // Fehler: kann nicht auf this zugreifen
// }
}
}
2. Klassenbeziehungen mit UML-Beispielen
// Assoziation: Dozent unterrichtet Kurse
public class Dozent {
private String name;
private List<Kurs> unterrichteteKurse = new ArrayList<>();
public Dozent(String name) {
this.name = name;
}
public void addKurs(Kurs kurs) {
unterrichteteKurse.add(kurs);
kurs.setDozent(this); // Rückreferenz
}
public void zeigeKurse() {
System.out.println(name + " unterrichtet:");
for (Kurs kurs : unterrichteteKurse) {
System.out.println(" - " + kurs.getTitel());
}
}
}
public class Kurs {
private String titel;
private Dozent dozent; // Rückreferenz
public Kurs(String titel) {
this.titel = titel;
}
public void setDozent(Dozent dozent) {
this.dozent = dozent;
}
public String getTitel() {
return titel;
}
}
// Aggregation: Abteilung hat Mitarbeiter (können existieren ohne Abteilung)
public class Abteilung {
private String name;
private List<Mitarbeiter> mitarbeiter = new ArrayList<>();
public Abteilung(String name) {
this.name = name;
}
public void addMitarbeiter(Mitarbeiter mitarbeiter) {
this.mitarbeiter.add(mitarbeiter);
}
public void removeMitarbeiter(Mitarbeiter mitarbeiter) {
this.mitarbeiter.remove(mitarbeiter);
// Mitarbeiter existiert weiter
}
}
// Komposition: Bestellung hat Bestellpositionen (existieren nur mit Bestellung)
public class Bestellung {
private String bestellId;
private List<Bestellposition> positionen = new ArrayList<>();
public Bestellung(String bestellId) {
this.bestellId = bestellId;
}
public void addPosition(String produkt, int menge, double preis) {
Bestellposition position = new Bestellposition(produkt, menge, preis);
positionen.add(position);
}
public double berechneGesamtbetrag() {
return positionen.stream()
.mapToDouble(Bestellposition::getGesamtpreis)
.sum();
}
// Innere Klasse für Komposition
private class Bestellposition {
private String produkt;
private int menge;
private double einzelpreis;
public Bestellposition(String produkt, int menge, double einzelpreis) {
this.produkt = produkt;
this.menge = menge;
this.einzelpreis = einzelpreis;
}
public double getGesamtpreis() {
return menge * einzelpreis;
}
}
}
3. Generics mit statischen Elementen
// Generische Klasse mit statischen Elementen
public class Container<T> {
// Statische Attribute (sind nicht generisch!)
private static int anzahlContainer = 0;
private static final String VERSION = "1.0";
// Instanzattribute (sind generisch)
private T inhalt;
private int id;
public Container(T inhalt) {
this.inhalt = inhalt;
this.id = ++anzahlContainer;
}
// Statische Methode (kann nicht auf T zugreifen)
public static int getAnzahlContainer() {
return anzahlContainer;
}
public static String getVersion() {
return VERSION;
}
// Instanzmethode (kann auf T zugreifen)
public T getInhalt() {
return inhalt;
}
public void setInhalt(T inhalt) {
this.inhalt = inhalt;
}
public void anzeigen() {
System.out.println("Container #" + id + ": " +
(inhalt != null ? inhalt.toString() : "leer"));
}
// Generische Methode (statisch)
public static <U> Container<U> create(U inhalt) {
return new Container<>(inhalt);
}
}
// Verwendung
public class ContainerDemo {
public static void main(String[] args) {
// Statische Methoden aufrufen
System.out.println("Container-Version: " + Container.getVersion());
// Verschiedene Container-Typen
Container<String> stringContainer = new Container<>("Hallo");
Container<Integer> intContainer = new Container<>(42);
Container<Double> doubleContainer = Container.create(3.14);
stringContainer.anzeigen();
intContainer.anzeigen();
doubleContainer.anzeigen();
System.out.println("Anzahl Container: " + Container.getAnzahlContainer());
// Fehler: Statische Attribute sind nicht generisch
// Container<String>.getAnzahlContainer(); // Syntax-Fehler
}
}
4. Factory Pattern mit statischen Methoden
public class FahrzeugFactory {
// Statische Factory-Methoden
public static Fahrzeug erstellePKW(String marke, int leistung) {
return new PKW(marke, leistung, 4);
}
public static Fahrzeug erstelleMotorrad(String marke, int leistung) {
return new Motorrad(marke, leistung, false);
}
public static Fahrzeug erstelleLKW(String marke, int leistung, double ladung) {
return new LKW(marke, leistung, ladung);
}
// Statische Methode mit Validierung
public static Fahrzeug erstelleFahrzeug(String typ, String marke, int leistung) {
switch (typ.toLowerCase()) {
case "pkw":
return erstellePKW(marke, leistung);
case "motorrad":
return erstelleMotorrad(marke, leistung);
case "lkw":
return erstelleLKW(marke, leistung, 1000.0);
default:
throw new IllegalArgumentException("Unbekannter Fahrzeugtyp: " + typ);
}
}
}
// Abstrakte Basisklasse
abstract class Fahrzeug {
protected String marke;
protected int leistung;
public Fahrzeug(String marke, int leistung) {
this.marke = marke;
this.leistung = leistung;
}
public abstract void anzeigen();
}
// Konkrete Klassen
class PKW extends Fahrzeug {
private int anzahlTueren;
public PKW(String marke, int leistung, int anzahlTueren) {
super(marke, leistung);
this.anzahlTueren = anzahlTueren;
}
@Override
public void anzeigen() {
System.out.println("PKW: " + marke + ", " + leistung + " PS, " + anzahlTueren + " Türen");
}
}
class Motorrad extends Fahrzeug {
private boolean hatSeitenwagen;
public Motorrad(String marke, int leistung, boolean hatSeitenwagen) {
super(marke, leistung);
this.hatSeitenwagen = hatSeitenwagen;
}
@Override
public void anzeigen() {
System.out.println("Motorrad: " + marke + ", " + leistung + " PS, " +
(hatSeitenwagen ? "mit" : "ohne") + " Seitenwagen");
}
}
class LKW extends Fahrzeug {
private double ladung;
public LKW(String marke, int leistung, double ladung) {
super(marke, leistung);
this.ladung = ladung;
}
@Override
public void anzeigen() {
System.out.println("LKW: " + marke + ", " + leistung + " PS, " + ladung + " kg Ladung");
}
}
// Verwendung der Factory
public class FactoryDemo {
public static void main(String[] args) {
// Statische Factory-Methoden verwenden
Fahrzeug golf = FahrzeugFactory.erstellePKW("Volkswagen", 110);
Fahrzeug harley = FahrzeugFactory.erstelleMotorrad("Harley", 80);
Fahrzeug scania = FahrzeugFactory.erstelleLKW("Scania", 500, 20000.0);
golf.anzeigen();
harley.anzeigen();
scania.anzeigen();
// Dynamische Erstellung
Fahrzeug bmw = FahrzeugFactory.erstelleFahrzeug("pkw", "BMW", 150);
bmw.anzeigen();
}
}
UML-Notation für Klassenbestandteile
Klasse mit statischen und Instanz-Elementen
+---------------------------+
| Mitarbeiter |
+---------------------------+
| - anzahlMitarbeiter: int | <<static>>
| - FIRMA: String | <<static>>
| - mitarbeiterId: int |
| - name: String |
| - gehalt: double |
+---------------------------+
| + getAnzahlMitarbeiter(): int | <<static>>
| + setMindestgehalt(double): void | <<static>>
| + erhoeheGehalt(double): void |
| + anzeigen(): void |
+---------------------------+
Beziehungen in UML
Dozent 1..* --* Kurs (Assoziation)
Abteilung 1 --o* Mitarbeiter (Aggregation)
Bestellung 1 --* Bestellposition (Komposition)
Fahrzeug <|-- PKW (Vererbung)
Statische vs. Instanz - Entscheidungshilfe
Wann statische Elemente verwenden?
Statische Attribute:
- Zähler für alle Instanzen
- Konstanten für die gesamte Klasse
- Gemeinsame Ressourcen (Datenbank-Verbindung)
- Caches für die Klasse
Statische Methoden:
- Factory-Methoden zur Objekterstellung
- Utility-Methoden ohne Zustand
- Konvertierungsmethoden
- Validierungsmethoden
Wann Instanz-Elemente verwenden?
Instanzattribute:
- Objektspezifische Daten
- Zustand, der sich ändert
- Konfiguration pro Objekt
Instanzmethoden:
- Methoden, die auf Objektzustand zugreifen
- Verhalten, das von Instanzdaten abhängt
- Methoden, die this-Zeiger benötigen
Vorteile und Nachteile
Vorteile von statischen Elementen
- Speichereffizienz: Nur eine Kopie für alle Objekte
- Einfacher Zugriff: Ohne Objektinstanz aufrufbar
- Gemeinsamer Zustand: Für alle Objekte gleich
- Factory-Pattern: Einfache Objekterstellung
Nachteile
- Globale Zustände: Erschwert Testbarkeit
- Thread-Safety: Bei parallelem Zugriff
- Flexibilität: Keine Polymorphie möglich
- Initialisierung: Komplexe Abhängigkeiten
Häufige Prüfungsfragen
-
Was ist der Unterschied zwischen statischen und Instanz-Attributen? Statische Attribute gehören zur Klasse (eine Kopie), Instanzattribute zum Objekt (pro Objekt eine Kopie).
-
Können statische Methoden auf Instanzattribute zugreifen? Nein, da sie kein this-Zeiger haben und nicht wissen, zu welchem Objekt sie gehören.
-
Erklären Sie die Aggregation vs Komposition! Aggregation: Teile können ohne Ganzes existieren, Komposition: Teile existieren nur mit Ganzem.
-
Warum sind statische Attribute nicht generisch? Sie gehören zur Klasse, nicht zur Instanz, daher gibt es nur eine Version pro Klasse.
Wichtigste Quellen
- https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
- https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
- https://www.uml-diagrams.org/class-diagrams.html