Inhaltsverzeichnis
- Einleitung
- Basiskonzepte der OOP & Java
- Vererbung
- Abstrakte Klassen und Methoden
- Interfaces
- Lose Kopplung
- Ausgewählte Probleme und Aspekte der OOP mit Java
- Java Collection Framework
- Sortieren von Objekten
- Bauen eines JARs von einer Klassensammlung:
jar cmf Manifest.txt name.jar *.class
- mit Programm
jar
- Benötigt wird Manifest.txt, welche Version und Hauptklasse enthält
- mit Programm
- Java ist eine interpretierbare, objektorientierte Sprache
- Java-Quellcode wird mit
javac
in Bytecode kompiliert und mit einer JVM interpretiert
- einzeilige Kommentare am Zeilenende mit
//
- Blockkommentar: Start mit
/*
, Ende mit*/
- Bezeichner müssen mit
_
,$
, oder einem Unicode-Buchstaben beginnen, aber nicht mit einer Ziffer, einem reservierten Wort oder ungültigen Zeichen (???) wie dem!
- Bezeichner sind case-sensitiv
- Namenskonventionen:
- Klassennamen mit großem CamelCase:
KlassenName
- Variablen- und Methoden-Namen mit kleinem camelCase:
compSumme()
- Konstanten in Capslock SNAKE_CASE:
MAX_SPEED
- Klassennamen mit großem CamelCase:
- primitive Datentypen sind initialisiert
- Referenzvariablen sind ggf.
null
- Literale: Ganzzahlen, Fließkommazahlen, Zeichen, null, Strings, Konstanten
- Im Gegensatz zu C / C++ dürfen in Java Methoden und Membervariablen durcheinander geschrieben werden, ohne Abhängigkeiten zu berücksichtigen. Der Compiler wird diese automatisch sortieren
- Es gibt die Zugriffsmodifier
private
,protected
undpublic
, wobeiprotected
der Standard ist - alle primitiven Datentypen existieren auch in Wrapperklassen objektorientiert
- Für zwei Referenzvariablen
a1
unda2
kann ausgeführt werden:a2 = a1
, wobei die Referenz geändert wird, das Objekt wird nicht kopiert-
$\rightarrow$ entsprechend verweisen beide Variablen auf dasselbe Objekt
-
Deklaration:
returntyp methodenName(parameterliste) {
// Methodenkörper
return something;
}
- Gleichheit von Strings: Wir betrachten folgenden Code:
String s1 = "abc", s2 = "abc", s3 = new String("abc");
- Java speichert Stringliterale in einem Pool, weshalb
s1
unds2
auf dieselbe Adresse verweisen s1 == s2
ergibttrue
, abers1 == s3
false
- Daher immer
s1.equals()
verwenden!
- 4 Grundkonzepte:
- Abstraktion: Gemeinsame Eigenschaften und Fähigkeiten werden zusammengefasst beschrieben
- Datenkapselung: Zustandsänderungen nur über dafür vorgesehene Operationen (Methoden)
- Vererbung: Erweiterung vorhandener Objekte mit zusätzlichen Eigenschaften + Operationen bei bleibender Kompatibilität zum Ursprungstyp
- Polymorphie: Laufzeitprüfung, wie auf vererbte Objekte reagiert wird
- Klassen und Objekte
$\rightarrow$ setze ich mal voraus - Unterschied Operationen und Methoden:
- Operation: was wird gemacht?
- Methode: wie wird gemacht?
- in Java werden keine Prototypen gebraucht
- wer mehrere Klassen in eine
.java
-Datei packt, kommt in die Hölle
- Verwalten Klassen und Zugriffsrechte
- erste Anweisung muss
package paketname;
sein - ohne Zugriffsmodifier an Klasse: nur für eigenes Package sichtbar
- Modifier
public
an Klasse: Klasse für alle Packages sichtbar
- in einer Klasse können mehrere Methoden desselben Namens existieren, die aber verschiedene Signaturen haben
- Ein Aspekt der statischen Polymorphie - Methoden haben in Abhängigkeit ihrer Signaturen die gleiche Funktionalität
- siehe auch: C++ (3. Semester)
- Achtung: wir sehen den Returntyp hier nicht als Bestandteil der Signatur
- Konstruktoren sind Initialisierer für Objekte und werden von dem
new
-Operator benutzt - wird ein impliziter Konstruktor genutzt, werden Membervariablen mit
null
,0
oderfalse
gefüllt
- existieren nur einmal pro Klasse
- statische Methoden können nicht auf non-statische Methoden zugreifen
- innerhalb eines Blockes deklariert und nur dort sichtbar
- haben Zugriff auf Member und Methoden der äußeren Klasse
- Nutzen: Implementierung von Funktionalität, die nur für diese Klasse gültig sein soll / relevant ist
- Anwendung folgt im Kapitel Grafische Oberflächen
- Schaffung neuer Klassen basierend auf existierenden Klassen
- Beziehung ist dauerhaft
- i.d.R. eine Erweiterung oder Spezialisierung der urspr. Klasse
- erbt alle Member der "Superklasse"
- Java-Syntax:
class Unterklasse extends Oberklasse {}
- UML: nicht ausgefüllter Pfeil von Unterklasse zu Oberklasse
- Vorteile:
- Vermeidung von Quelltextduplizierung
- einfachere Wartung
- Erweiterbarkeit von Klassenfunktionalität
- eine überschriebene Methode der Superklasse ist dennoch erreichbar via
super.methode()
- finale Klassen können nicht abgeleitet werden
- finale Methoden können nicht überschrieben werden
- Liskovsches Substitutionsprinzip: jede Unterklasse ist auch als ihre Oberklasse verwendbar
- Analog zu C++ kann man ein Array einer Oberklasse auch mit einer Instanz einer Unterklasse befüllen, wobei zur Laufzeit erkannt wird, dass überschriebene Methoden der Unterklassen auszuführen sind
- man darf die Sichtbarkeit von Methoden in Unterklassen erweitern, aber nicht weiter einschränken
- soll eine Instanz einer Unterklasse niemals auf eine überschriebene Methode der Oberklassse zurückgreifen, muss
@Override
über die Methode geschrieben werden - Jeder Konstruktor einer abgeleiteten Klasse ruft zuerst den Konstruktor der Superklasse auf
- implizit, wenn nicht parametrisiert
- muss explizit aufgerufen werden, wenn parametrisierter Konstruktor
- jede Klasse erbt von
Object
, auch implizit$\rightarrow$ also hat jede Klasse einetoString()
-Methode
Beispiel für Downcasting:
if (m instanceof Arbeiter) System.out.println("\t" + ((Arbeiter)m).getLohnsteuer());
- Delegation: "Hat ein..."
$\rightarrow$ Member-Variable - Vererbung: "Ist ein..."
$\rightarrow$ siehe oben - Delegation ist der Vererbung vorzuziehen
- Vererbung sollte verwendet werden, wenn...
- die angestrebte Unterklasse wirklich eine Spezialisierung / ein Teil von der Superklasse ist
- alle Eigenschaften wirklich vererbt werden sollen
- von abstrakten Klassen können keine Objekte erzeugt werden
- werden im Klassendiagramm kursiv geschrieben (Vorsicht: sieht man ggf. nicht gleich!)
- abstrakte Methoden können nicht
private
sein, weil das keinen Sinn ergibt - vor abstrakten Methoden, die also keine Implementierung haben, steht das Schlüsselwort
abstract
- auch benannte Schnittstellen genannt
- spezifizieren Menge von Operationen, aber keine Implementierungen
$\rightarrow$ abstrakte Methoden - Definition von Konstanten möglich
- können als Typ verwendet werden, wobei alle Klassen, die dieses Interface implementieren, dafür qualifiziert sind
$\rightarrow$ diese Klassen werden dynamische Datentypen genannt - Interfaces können durch Unter-Interfaces erweitert werden
- Unterklassen, wenn die Oberklasse erweitert werden soll
- abstrakte Klassen, wenn Instanzen der Oberklasse nicht sinnvoll / erwünscht sind
- Interfaces, wenn verschiedene Klassen einen Funktionsumfang implementieren sollen oder gemeinsame Datentypen benötigt werden
- Szenario: Klasse A nutzt eine Referenzvariable vom Typ
IB
; mehrere KlassenB1
,B2
, ... implementierenIB
- Zur Laufzeit kann entschieden werden, welche Klasse genutzt werden soll
- Wenn eine Klasse erwartet wird, sollte eine Unterklasse davon auch verarbeitet werden können
- Problemstellung: was darf eine überschreibende Methode ändern, in Bezug auf Rückgabetyp, Typen der formalen Parameter, ggf. auch Typen der Checked Exceptions?
- Invarianz: Datentypen sind in Ober- und Unterklasse gleich
- Kovarianz: Typhierarchie mit Vererbungshierarchie
$\rightarrow$ bei Verwendung einer Unterklasse ist bspw. der Rückgabetyp auch von einer Unterklasse- Bsp.: Person schreibt Dokument; Student schreibt Buch
- Kontravarianz: Typhierarchie entgegen Vererbungshierarchie
$\rightarrow$ bei Verwendung einer Unterklasse ist bspw. der Rückgabetyp von einer Oberklasse- Bsp.: Person schreibt Buch; Student schreibt Dokument
Varianz | Eingabeparameter | Rückgabeparameter |
---|---|---|
Invarianz | möglich | möglich |
Kovarianz | nicht möglich | möglich |
Kontravarianz | nicht möglich | nicht möglich |
- private Member werden ebenfalls vererbt, sind aber nicht sichtbar
- Zugriff auf private Membervariablen sollte über Getter und Setter erfolgen
- jede Klasse erbt implizit von
Object
Object
enthält Implementierungen für Operatoren wietoString()
- ...wird mittels
equals(Object o)
überprüft - zwei Objekte sind gleich, wenn alle Member denselben Wert haben
- Achtung:
o1 == o2
vergleicht Referenzen! hashCode()
liefert den Hash eines Objektes
- Implementierung komplexer Datenstrukturen über Zentrale Interfaces wie
Collection
Array
: schneller Zugriff, klassisches Array, aber statische GrößeArrayList
: aufgemotztes Array, dynamisch in der Größe, wahlfreier Zugriff schneller als in LinkedList, aber Einfügen und Löschen langsamer, da i.d.R ein Umspeichern erforderlich istLinkedList
: doppelt verkettete Liste (siehe Algo Semester 2) dynamischer Größe, passt sich zur Laufzeit an, aber direkter Zugriff langsam, da Liste sequenziell durchlaufen werden muss- sowohl
ArrayList
als auchLinkedList
erben vonList
, welches wiederum eineCollection
ist
- zwei Interfaces werden zur Verfügung gestellt:
Comparable
undComparator
Comparable
deklariert die Methodepublic int compareTo(Object o)
, welche zu implementieren ist- Sortieren wird benötigt, wenn
TreeSet
verwendet werden soll - wenn kein triviales Vergleichen möglich ist, muss eine Klasse geschrieben werden, die
Comparator
implementiert - Erinnerung stabile Sortierung: bei einem Pasch bleibt die Reihenfolge wie im Original erhalten