Kurz: Eine Klasse ist ein Bauplan, ein Objekt ein daraus gebautes Exemplar. Eine Klasse bündelt Daten (Felder) und Verhalten (Methoden).
Teil des Kurses Java
Eine Klasse ist ein Bauplan, ein Objekt ein daraus gebautes Exemplar. Eine Klasse bündelt Daten (Felder) und Verhalten (Methoden).
public class Hund {
// Felder (Zustand)
private String name;
private int alter;
// Konstruktor: legt das Objekt an
public Hund(String name, int alter) {
this.name = name;
this.alter = alter;
}
// Methoden (Verhalten)
public String bellen() {
return name + " sagt: Wuff!";
}
public void geburtstag() {
alter++;
}
// Getter
public String getName() { return name; }
public int getAlter() { return alter; }
@Override
public String toString() {
return "Hund[" + name + ", " + alter + "]";
}
public static void main(String[] args) {
Hund rex = new Hund("Rex", 3);
Hund mimi = new Hund("Mimi", 5);
rex.geburtstag();
System.out.println(rex.bellen() + " " + rex.getAlter());
System.out.println(rex + " " + mimi);
}
}Ausgabe:
Rex sagt: Wuff! 4
Hund[Rex, 4] Hund[Mimi, 5]new Hund(...)erzeugt ein Objekt und ruft den Konstruktor aufthisist das aktuelle Objektprivateversteckt Felder; von außen sind sie nur über Methoden erreichbar (Kapselung)toString()bestimmt die Textdarstellung
Kapselung und Sichtbarkeit
| Modifikator | Sichtbar für |
|---|---|
public | alle |
protected | gleiches Paket und Unterklassen |
| (ohne) | nur das gleiche Paket |
private | nur die Klasse selbst |
Halte Felder private und biete kontrollierte Zugriffe an. So kann die Klasse ihre Regeln durchsetzen:
public class Konto {
private double stand;
private final String inhaber;
private static int anzahl = 0; // gehört der Klasse, nicht einem Objekt
public Konto(String inhaber, double startwert) {
if (startwert < 0) throw new IllegalArgumentException("Startwert < 0");
this.inhaber = inhaber;
this.stand = startwert;
anzahl++;
}
public Konto(String inhaber) { this(inhaber, 0); } // Konstruktoren verketten
public void einzahlen(double betrag) {
if (betrag <= 0) throw new IllegalArgumentException("Betrag muss positiv sein");
stand += betrag;
}
public void abheben(double betrag) {
if (betrag > stand) throw new IllegalStateException("Nicht genug Guthaben");
stand -= betrag;
}
public double getStand() { return stand; }
public String getInhaber() { return inhaber; }
public static int getAnzahl() { return anzahl; }
public static void main(String[] args) {
Konto k = new Konto("Mia", 100);
k.einzahlen(50);
k.abheben(30);
System.out.println(k.getInhaber() + ": " + k.getStand());
new Konto("Tom");
System.out.println("Konten: " + Konto.getAnzahl());
try {
k.abheben(1000);
} catch (IllegalStateException e) {
System.out.println("Fehler: " + e.getMessage());
}
}
}Ausgabe:
Mia: 120.0
Konten: 2
Fehler: Nicht genug Guthabenequals und hashCode
Standardmäßig vergleicht equals nur die Speicheradresse. Soll Gleichheit nach Inhalt gelten, überschreibst du equals und hashCode:
import java.util.*;
public class Punkt {
private final int x, y;
public Punkt(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Punkt p)) return false;
return x == p.x && y == p.y;
}
@Override public int hashCode() { return Objects.hash(x, y); }
@Override public String toString() { return "(" + x + "|" + y + ")"; }
public static void main(String[] args) {
Punkt a = new Punkt(1, 2), b = new Punkt(1, 2);
System.out.println((a == b) + " " + a.equals(b));
Set<Punkt> menge = new HashSet<>(List.of(a));
System.out.println(menge.contains(b) + " " + menge);
}
}Ausgabe:
false true
true [(1|2)]Wer equals überschreibt, muss hashCode passend mitüberschreiben, sonst funktionieren HashSet und HashMap nicht richtig. Records (nächste Kapitel) erledigen das automatisch.
Initialisierung und Reihenfolge
Beim Erzeugen eines Objekts passiert in dieser Reihenfolge: statische Teile der Klasse (einmal), Felder mit Anfangswerten, Initialisierungsblöcke, Konstruktor.
public class Reihenfolge {
static { System.out.println("1 statischer Block (einmal)"); }
private int a = init("2 Feld");
{ System.out.println("3 Initialisierungsblock"); }
Reihenfolge() { System.out.println("4 Konstruktor"); }
static int init(String s) { System.out.println(s); return 1; }
public static void main(String[] args) {
new Reihenfolge();
new Reihenfolge();
}
}Ausgabe:
1 statischer Block (einmal)
2 Feld
3 Initialisierungsblock
4 Konstruktor
2 Feld
3 Initialisierungsblock
4 KonstruktorObjekte im Speicher
- Lokale Variablen mit primitiven Typen liegen im Stack; Objekte im Heap, die Variable enthält nur einen Verweis.
- Der Garbage Collector gibt Objekte frei, auf die niemand mehr zeigt. Du musst nichts manuell löschen.
nullbedeutet “kein Objekt”. Zugriff darauf löst eineNullPointerExceptionaus.
public class Verweise {
static class Box { int wert; Box(int w) { wert = w; } }
public static void main(String[] args) {
Box a = new Box(1);
Box b = a; // beide zeigen auf dasselbe Objekt
b.wert = 99;
System.out.println(a.wert);
a = null; // b hält das Objekt noch fest
System.out.println(b.wert);
String s = null;
try { s.length(); } catch (NullPointerException e) { System.out.println("NPE"); }
}
}Ausgabe:
99
99
NPEOptional statt null
Optional<T> macht “kein Wert” im Typ sichtbar:
import java.util.*;
public class Opt {
static Optional<String> finde(List<String> l, String prefix) {
return l.stream().filter(s -> s.startsWith(prefix)).findFirst();
}
public static void main(String[] args) {
List<String> namen = List.of("Anna", "Ben", "Clara");
System.out.println(finde(namen, "B").orElse("keiner") + " " + finde(namen, "X").orElse("keiner"));
finde(namen, "C").ifPresent(n -> System.out.println("gefunden: " + n));
System.out.println(finde(namen, "A").map(String::toUpperCase).get());
}
}Ausgabe:
Ben keiner
gefunden: Clara
ANNAMerke
- Klasse = Bauplan (Felder + Methoden), Objekt = Exemplar;
newruft den Konstruktor - Kapselung: Felder
private, Zugriff über Methoden thisist das aktuelle Objekt;staticgehört zur KlasseequalsundhashCodeimmer gemeinsam überschreiben- Objekte liegen im Heap, Variablen halten Verweise;
nullvermeiden,Optionalnutzen
Übungsaufgabe
Schreibe eine Klasse Rechteck mit Breite und Höhe (Konstruktor, Prüfung auf positive Werte), flaeche(), umfang() und toString().
Quiz zur Selbstkontrolle
Wofür sind private-Felder gut?
- Kapselung: Zugriff nur über kontrollierte Methoden (richtig)
- Sie sind schneller
- Sie sparen Speicher
- Sie sind für Konstanten
Was passiert bei equals ohne Überschreiben?
- Es vergleicht nur die Speicheradresse (richtig)
- Es vergleicht alle Felder
- Es wirft einen Fehler
- Es vergleicht nur Strings
Was gibt der Garbage Collector frei?
- Objekte, auf die niemand mehr zeigt (richtig)
- Alle Variablen
- Dateien
- Threads
Weiter im Kurs
Zurück: Collections: List, Set und Map
Weiter: Vererbung, Interfaces und Polymorphie
Alle Kapitel: Java im Überblick