JAVA-B1
Klassen
public class
instance
System.out.println()
{@link Klasse}
{@param Klasse}
getClass().getSimpleName()
extends
Objekte
Interfaces
FAQ classes
Erweitern von Klassen mit extends
finale Konstanten
(@see
Finalisierung von Klassen
)
Polymorphismus
Nested Classes
@Override Annotation
innere Klassen
lokale Klassen
anonyme Klassen
Lamda Expressions
FAQ Nested Classes
Klassen im Detail
Overview
return-Statements
return & void
return bei primitiven Typen
return bei Objekten
return-Statements von Methoden mit/ohne return
Rückkehr ohne return
Rückkehr über return
throws Exception
Kopie oder Referenz
Covariant Return Types
this
this mit Objekt-Feldern
this mit Methoden und im Konstuktor
Zugriffsmodifikatoren
public protected private
Class Member
static
public static final PI
Initialisierung
Initialisierung von Feldern
Initialisierung von Klassenfeldern
Static Block
Lambda-Expressions
Methoden-Referenzen
Enumerationen und enum-Types
Enumerationskonstanten
Enumerationen mit Werten
Samstag/Sonnabend Example @!
Enumerationen FAQ
Enum @!Descr/Exmpl/Index
Interfaces
Einfache Interfaces (Marker)
Interfaces mit Methoden
Default-Methoden
static
Interfaces FAQ
Vererbung / Inheritance
Vererbung / Inheritance
super Keyword
method-overriding-and-hiding
hiding-of-fields
object-as-superclass
Finalisierung
Finale Klassen und Methoden
Finale Methoden
Finale Utility-Klasse "IdGenerator"
"Template-Methode"-Pattern
DataProcessor process(){ r(), p(), w() }
Composition-over-Inheritance
abstract
Abstrakte Klassen und Methoden
Beispiel
Abstrakte Klassen und Methoden
abstract
Implementierungsklassen
FAQ Vererbung
Kein this in super(..)
Verhindern Überschreiben von Membern
Generics
Generische Programmierung
Generische Typparameter
Generische Klassen
Generische Interfaces
Generische Methoden
Generische Felder
Generische Collection (ArrayList)
Diamond Operator
Generische Arrays
Example
Beispiel "GenerischerSpeicher"
Ring-Speicher-Algorithus mit generischem Array
Beispiel: GenerischePerson mit Berufsgruppe
Beispiel: Berufsgruppe mit Static/Non-Static Nested Classes
Modularisierung
Package
Overview
Package Structure
Package Info
Modules
Overview
Setup
Setup
Module Info
Java New Features Java 8+
Java Record
Record Syntax
Record Demo
Herkömmliche POJOs
Lösung mit Records
Records Zusammenfassung
INDEX
Finalisieren
Finalisieren | Klassen und Methoden
In diesem Themenblock befassen wir uns mit dem Schreiben finaler Klassen und Methoden sowie Verfahren, wie man ab JAVA 17 Klassen und Methoden versiegeln kann, diesen Schutz aber für ausgewählte Klassen aber wiederum erlauben kann.
Das Finalisieren und Schützen von Klassen und Methoden ist insbesondere für Utility-Klassen sowie für Methoden zweckmäßig, welche direkt aus Konstruktoren aufgerufen werden.
Beispiel
JAVA-B1 | Beispiel
Prinzip
Finalisieren | Prinzip
Man kann in JAVA manche oder auch alle Methoden
einer Klasse mit Hilfe des Keywords final
als «final» deklarieren. Die entsprechenden
Member können in Unterklassen nicht mehr überschrieben werden.
Bereits die Klasse java.lang.Object
hat einige finalisierte Methoden.
Tipp: Immer dann, wenn man Methoden
hat, deren Implementierung auf keinen Fall verändert werden sollte,
weil anderenfalls der Zustand eines Objekts inkonsistent werden können,
ist es anzuraten, diese Methoden mit final
zu finalisieren.
Im Schach beginnt immer weiß
Beispiel: Im Schachspiel gibt es zwei Farben für die zwei Spieler,
weiß und schwarz. Um sicherzustellen, dass auch ein von ChessAlgorithm
abgeleite Klasse auf keinen Fall die getFirstPlayer() Methode
überschreiben kann, lässt sich diese als final
derklarieren.
class ChessAlgorithm {
enum ChessPlayer { WHITE, BLACK }
...
final ChessPlayer getFirstPlayer() {
return ChessPlayer.WHITE;
}
...
}
Anmerkung: Member ohne das Keyword public
gelten in JAVA als public, in
C# .NET aber als privat.
enum ChessPlayer: JAVA ermöglicht nicht nur innere Klassen sondern auch innere Enumerationen.
Quelle: https://docs.oracle.com/javase/tutorial/java/IandI/final.html
Finale Utility-Klassen
Finalisieren | Finale Utility-Klassen
Ein beliebtes Entwurfsmuster sind Utility-Klassen, welche weder instanziiert noch erweitert werden sollen.
Das nachfolgende Beispiel zeigt eine Klasse, welche in einer Anwendung dazu dient, auf Anfrage jeweils eine neue ID als Nummer als String «ID-0» bis «ID-n» zu erzeruegen.
package com.stuelken.java.b1.finalization;
/**
* Finale Klasse welche nicht erweitert werden kann und von welcher darüber
* hinaus in diesem Fall auch keine Objekte erzeugt werden können.
*
* @author t2m
*/
public final class IdGenerator {
/**
* Ein Zähler für alle vom {@link IdGenerator} erzeugten IDs.
*/
private static int counter = 0;
/**
* Privater Konstruktor zu Verhinderung einer Instanziierung von Objekten.
*/
private IdGenerator() {
}
/**
* Ein synchronisierte, Thread-sichere Methode, welche eine neue ID generieren
* kann.
*
* @return
*/
public static synchronized String next() {
return "ID-" + (++counter);
}
/**
* Testprogramm
*
* @param args Keine Parameter
*/
public static void main(String[] args) {
System.out.println(IdGenerator.next());
System.out.println(IdGenerator.next());
}
}
Klasseneigenschaften
Mit private static int counter wird
eine private Klasseneigenschaft deklariert. Damit diese einen
Startwert beim Programmstart hat, wird dieses Feld mit 0
initialisiert.
Privater Konstruktor
Mit private IdGenerator() { }
wird verhindert, dass man Objekte dieser Klasse wie sonst
üblich mit dem new Keyword
erstellen kann, dann da der Konstruktor privat ist, ist eine
Adresse dieser Methode von außen nicht mehr möglicht. Der
Konstruktor kann also nur noch immer der Klasse selbst
adressiert werden.
Thread-Safe
public static synchronized String next()
ist nun eine öffentliche Klassenmethode, bereits synchronisiert
für den Fall, dass diese Methode auch bei nebenläufiger Programmierung
zum Einsatz kommen soll. Mit next() wird
ein String «ID-» gefolgt vom int-Wert von
counter geliefert.
Diese Klasse ist also «Thread-Safe».
Klassenmethoden adressieren
System.out.println(IdGenerator.next());
zeigt, wie man die statische Methode nun aufrufen kann, ohne
ein Objekt dieser Klasse erzeugen zu können.
Finalisierte Klassen
public final class * { }
bewirkt, dass die Klasse auch außerhalb des Package
verwendet werden kann, eine Erweiterung der Klasse und
damit eine Subklasse ist wegen final
allerdings nicht möglich.
Template-Methode-Pattern
Finalisieren | Template-Methode-Pattern
Ein beliebtes Entwurfsmuster ist das Template-Methode-Pattern.
Um die Reihenfolge der Ausführung von Methoden und auch die Verfügbarkeit von Methoden in Objekten abgeleiter Klassen sicherzustellen und gleichzeitig verhindern zu können, dass jemand die zentrale Superklasse als Vorgabe verändert, definiert man die Superklasse abstrakt, schafft für die Reihenfolge von Methoden eine Template-artige finale Methode, die nicht überschrieben werden kann, und ermöglicht dann Entwicklern die Programmierung von Subklassen als Implementierungsklassen.
public abstract class DataProcessor {
// Template-Methode: feste Abfolge der Verarbeitungsschritte
public final void process() {
readData();
processData();
writeData();
}
// Abstrakte Hook-Methoden, die in Unterklassen spezifiziert werden.
protected abstract void readData();
protected abstract void processData();
protected abstract void writeData();
}
public class CSVDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("CSV-Daten werden gelesen...");
}
@Override
protected void processData() {
System.out.println("CSV-Daten werden verarbeitet...");
}
@Override
protected void writeData() {
System.out.println("CSV-Daten werden geschrieben...");
}
}
// Demo-Klasse
public class TemplateMethodDemo {
public static void main(String[] args) {
DataProcessor processor = new CSVDataProcessor();
processor.process();
}
}
Template-Method-Pattern
Das Template-Method-Pattern nutzt eine finale Methode, die eine feste Reihenfolge an Aufrufen ("Schablone") definiert und sogenannte "Hooks" (abstrakte oder optionale Methoden) anbietet, die spezifisch implementiert werden können.
Reihenfolge der zu implementierenden Methoden
Die finale Methode process() stellt das fest definierte Vorgehen dar. Da man diese Methode nicht überschreiben kann und die abstrakten deklarierten Methoden sicherstellen, dass eine erweiterte Subklasse diese Methoden haben wird, kann schlussendlich jedes Objekt, welches über eine Implementierungsklasse erzeugt wurde, diese Methoden in der richtigen Reihenfolge ausführen.
Implementierung durch Unterklassen
Spezifische Details wie das Einlesen oder Verarbeiten der Daten werden in den Unterklassen implementiert. Der maßgebende oft benutzte Begriff ist Implementierungsklasse oder Implementierung einer abstrakten Methode.
Konsistente API
So wird eine konsistente API geschaffen, die dennoch erweiterbar bleibt.
Template-Methode-Pattern
Finalisieren | Composition over Inheritance
Wenn fast alle Methoden final sind, ist es oft sinnvoller, Komposition statt Inheritance zu nutzen.
In einem Beispiel zeigen wir, wie man Klassen durch Einbetten von Objekten kombiniert, statt von diesen Klassen erben zu wollen.
// Beispiel einer Komponente
public class Engine {
public void start() {
System.out.println("Motor gestartet.");
}
public void stop() {
System.out.println("Motor gestoppt.");
}
}
// Klasse, die eine Engine als Teil ihrer Zusammensetzung (Composition) enthält
public class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
public void drive() {
engine.start();
System.out.println("Das Auto fährt.");
engine.stop();
}
}
// Demo-Klasse
public class CompositionDemo {
public static void main(String[] args) {
Car car = new Car();
car.drive();
}
}
Klasseneigenschaften
Anstatt von der Engine-Klasse zu erben, wird hier eine Engine als Attribut innerhalb des Car-Objekts genutzt. Dies fördert lose Kopplung, erhöht die Flexibilität und vermeidet starre Vererbungsstrukturen.
Prinzip der Delegation
Dieses Entwurfsmuster kennt man in einer Abwandlung als das Prinzip der Delegation.
FootNotes, Keywords, Tags
Hinweise, Rechte, Marken
UIO3 Es ist einfacher als Du denkst.
Stelle noch heute Deine Anfrage.
