Datei-Handling Anonyme Klassen - uni-muenster.de · •Damit ist System.out.println(…) unter...

23
Name: Patrick Förster Programmieren in Java Datei-Handling Anonyme Klassen

Transcript of Datei-Handling Anonyme Klassen - uni-muenster.de · •Damit ist System.out.println(…) unter...

Name: Patrick Förster

Programmieren in Java Datei-Handling Anonyme Klassen

Name: Patrick Förster

Enums

• Enums sind eine Aufzählung von int-Werten, wobei jedes Element einem

bestimmten Namen zugeordnet ist • In Java werden Enums über das Schlüsselwort enum definiert und sind

spezielle Klassen:

• Lediglich die Namen werden angegeben und werden per Konvention komplett in Großbuchstaben notiert (analog zu konstanten Klassenattributen) • Die Werte werden konsekutiv angefangen bei 0 einem int-Wert zugeordnet • Ein Zugriff erfolgt über die Dereferenzierung des Klassennamens:

2 Programmieren in Java

Slot.PROCESSOR

SICHTBARKEIT KLASSEN-BEZEICHNER { enum

}

ENUM-BEZEICHNER ,

public enum Slot {

PROCESSOR,

RAM,

}

Name: Patrick Förster

Inner-Class

• In Java können Klassen/Interfaces innerhalb von Klassen/Interfaces definiert werden

• Der Zugriff auf innere Klassen erfolgt analog zu Klassenattributen über den umschließenden Klassennamen

• Nur bei inneren Klassen kann der Modifikator static genutzt werden

3 Programmieren in Java

MODIFIKATOREN KLASSEN-BEZEICHNER { class|enum

}

MODIFIKATOREN

}

KLASSEN-BEZEICHNER { class|enum

public interface Pluggable {

public static enum Slot {

PROCESSOR,

RAM,

}

}

Pluggable.Slot slot = Pluggable.Slot.PROCESSOR;

Name: Patrick Förster

Inner-Class

• Innerhalb eines Interfaces sind alle inneren Klassen automatisch static

• Geeignete Sichtbarkeit vorausgesetzt können von außen nur Instanzen von statischen inneren Klassen erzeugt werden:

• Instanzen nicht statischer innerer Klassen können nur innerhalb von Instanzen der umschließenden Klasse erzeugt werden:

• Jede Instanz einer nicht statischen inneren Klasse hat Zugriff auf die Instanz, die sie erzeugt hat und damit auf deren Attribute und Methoden

4 Programmieren in Java

GraphicsCard.Resolution resolution = new GraphicsCard.Resolution(…);

GraphicsCard.Resolution resolution = new GraphicsCard.Resolution(…); // Resolution nicht statisch

// Resolution statisch

public class GraphicsCard {

public class Resolution {…}

public Resolution createResolution(int width, int height) {

return new Resolution(width, height);

}

}

Name: Patrick Förster

• Java bietet mit Exceptions eine Alternative zur Fehlerbehandlung • Exception ist eine Java-Klasse • Die Ausführung einer Methode kann jederzeit durch das „Werfen“ einer Exception-Instanz beendet werden • Methoden, die durch eine Exception abgebrochen werden könnten, müssen diese in ihrer Signatur nach dem Schlüsselwort throws angeben

• Innerhalb der Methode wird eine Exception-Instanz mittels throw geworfen:

• Die Ausführung der Methode wird mit throw abgebrochen, d.h. nachfolgender

Code wird nicht mehr ausgeführt • Der Methodenaufruf wird keine Rückgabe liefern

Fehlerbehandlung durch Exceptions

5 Programmieren in Java

public void plugin(Pluggable pluggable) throws Exception{…}

MODIFIKATOREN RÜCKGABETYP ( BEZEICHNER … throws } EXCEPTION-TYP , … ;

if (!slotAvailableFor(pluggable.getSlot())) {

throw new Exception("FEHLERMELDUNG");

}

Name: Patrick Förster

• Man betrachte folgende Methodensignatur und Methodenaufruf

• Der Aufruf createSomething() könnte nach Signatur zu einer Exception

führen • Kommt es zum Fehlerfall, liefert der Aufruf kein Ergebnis • Die Variable something bleibt also „uninitialisiert“

• Variablen müssen in Java vor ihrer ersten Auswertung initialisiert sein, ansonsten kommt es zum Compilefehler • Damit ist System.out.println(…) unter Umständen nicht compilierbar

• Um diesen Konflikt zu vermeiden, bietet Java die Möglichkeit Codeblöcke „versuchsweise“ auszuführen • Das dafür vorgesehene Sprachkonstrukt nennt sich try-catch-Block

Fehlerbehandlung durch Exceptions

6 Programmieren in Java

public Object createSomething() throws Exception{…}

Object something = createSomething();

System.out.print(something);

Name: Patrick Förster

optional

Try-Catch-Finally

7 Programmieren in Java

{ try

}

AUFRUF EINER METHODE, DIE MIT THROWS DEFINIERT WURDE

catch ( ) EXCEPTION-TYP BEZEICHNER_1 {

}

FEHLERBEHANDLUNG

catch ( ) EXCEPTION-TYP BEZEICHNER_N { } …

CircuitBoard board = …;

Pluggable pluggable = …;

try {

board.plugin(pluggable);

… // wird nicht mehr ausgeführt

… // falls der plugin-Aufruf

… // zu eine Exception geführt

… // hat

}

catch (Exception e) {

System.out.println(

e.getMessage()

);

}

finally {

System.out.println(

"Dies wird immer ausgeführt"

);

}

finally {

}

Name: Patrick Förster

• Da Exception eine Java-Klasse ist, kann von ihr geerbt werden • Durch Vererbung von Exception können eigene Exceptions definiert werden

Eigene Exceptions

8 Programmieren in Java

public class ConfigurationException extends Exception {

public ConfigurationException(String message) {

super(message);

}

}

public void plugin(Pluggable pluggable) throws ConfigurationException {…}

CircuitBoard board = …;

Pluggable pluggable = …;

try {

board.plugin(pluggable);

}

catch (ConfigurationException e) {

System.out.println(e.getMessage() );

}

if (!slotAvailableFor(pluggable.getSlot())) {

throw new ConfigurationException (

"FEHLERMELDUNG"

);

}

Name: Patrick Förster

• Nicht alle Exceptions müssen per throws signalisiert werden • Ebenso müssen nicht alle Exceptions mit try-catch abgefangen werden:

• Bspw. kann es schnell zur NullPointerException kommen • Oder ArrayOutOfBounds-Exception beim Zugriff auf ein Array-Element • Solche Exceptions mussten aber an keiner Stelle explizit mit try-catch

abgefangen werden

• Exceptions, die weder per throws signalisiert noch per try-catch

abgefangen werden müssen, werden als Laufzeit-Exceptions bezeichnet • All Laufzeit-Exceptions erben von der Klasse RuntimeException • Wird eine RuntimeException nicht abgefangen, so kommt es unmittelbar

zum Programmabbruch!

Laufzeit-Exceptions

9 Programmieren in Java

Name: Patrick Förster

• Module repräsentiert alle Slots eines bestimmten Typs auf einer Platin

• Da jeder Slot nur mit der zugehörigen Platine existiert: Aggregation • Aggregation lässt sich im Code sehr gut durch innere Klassen abbilden:

• Die Klasse ist protected um den Zugriff„von außen“ einzuschränken • Mit max wird definiert, wie viele Slots des Typs maximal vorhanden sind • Die Methoden plugin sowie add werfen eine Exception falls das übergebene Pluggable-Objekt nicht hinzugefügt werden kann

Anwendung

10 Programmieren in Java

Module

Pluggable[] slots;

Pluggable.Slot slot;

int max;

add(Pluggable): void

throws ConfigurationException

CircuitBoard

Module[] modules;

plugin(Pluggable): void

throws ConfigurationException

createModule(Pluggable.Slot, int size): Module

<<interface>>

Pluggable

1 n 1 n

public class CircuitBoard {

protected class Module {…}

}

Name: Patrick Förster

• Die unterstützen Slot-Typen sind je nach Platine unterschiedlich • Über die Methode createModule können die unterstützten Slot-Typen als

Modul hinzugefügt werden • Im Entwurf wurde festgelegt, dass auf jeder Platine ein Prozessor sitzt

• createModule ist protected, um den Aufruf „von außen“ zu vermeiden • Die Methode plugin wird eine ConfigurationException, falls ein Pluggable-Objekt hinzugefügt werden soll, für das kein Slot-Modul existiert

Anwendung

11 Programmieren in Java

public CircuitBoard(String name, Processor processor) {

try {

Module module = try.createModule(Pluggable.Slot.PROCESSOR, 1);

module.add(processor);

}

catch (ConfigurationException e) {

throw new RuntimeException(e);

}

}

Name: Patrick Förster

Anwendung

12 Programmieren in Java

Computer

GraphicsCard

RAMModule

Device

Soundcard Harddrive

Connector

CircuitBoard

<<interface>>

Pluggable

Processor

1

Module

Name: Patrick Förster

• Stream: Fluss von Bytes zwischen einem Sender und einem Empfänger • Ein Stream, der von einer Datenquelle kommt, ist ein InputStream • Ein Stream, der an eine Datenquelle geschickt wird, ist ein OutputStream.

• In Java erfolgt das Einlesen von Dateiinhalten über Datei-Streams • Der Zugriff auf eine Datei erfolgt über ihren Dateipfad • Ein Pfadobjekt wird über die Hilfsklasse java.nio.file.Paths erzeugt:

• Dabei spielt keine Rolle, ob überhaupt eine Datei namens „test.txt“ existiert • Erst beim Zugriff (Lesen/Schreiben) wird überprüft, ob die Datei existiert • Java überprüft selbständig welches Trennzeichen für Ordner auf dem aktuellen Betriebssystem zugelassen ist • Ob also \ (Windows) oder / (Linux) ist in der Angabe egal

• Ohne führendes Trennzeichen wird dem angegebenen Pfad der Pfad der Anwendung vorangestellt • In Netbeans ist dies der Ordner, der den src-Ordner enthält

Einlesen von Dateien

13 Programmieren in Java

Path path = Paths.get("files/test.txt");

Name: Patrick Förster

• Über die Hilfsklasse java.nio.file.Files wird ein InputStream erzeugt

• Natürlich muss dafür die angegebene Datei existieren • Ob eine Datei existiert, wird ebenfalls über Files angefragt

• Nun könnte mit stream.read() Byte für Byte der Dateiinhalt gelesen werden

• Lese- und Schreibeoperationen werden standardmäßig ohne Puffer ausgeführt • D.h. jede Lese- bzw. Schreibeoperation wird an das Betriebssystem delegiert • Das kann ein Programm allerdings sehr ineffektiv machen • Daher werden InputStreams meist um einen Puffer erweitert

• Der Stream ließt dann solange von diesem Puffer bis dieser leer ist • Erst dann erfolgt ein Betriebssystemaufruf

Einlesen von Dateien

14 Programmieren in Java

Path path = Paths.get("files/test.txt");

InputStream stream = Files.newInputStream(path);

if (Files.exists(path)) {

}

else {…}

Name: Patrick Förster

• Ein InputStream wird durch eine sogenannte „Wrapper“-Klasse durch

einen Puffer erweitert

• Da BufferedInputStream selbst wieder ein InputStream ist, kann überall da wo ein InputStream benötigt wird, ein gepufferter eingesetzt werden

• Es sei nun davon ausgegangen, dass der Inhalt einer Datei ein Text ist • Liest man Byte für Byte müsste man diese Bytes zu Text „umwandeln“ • Diesen Aufwand kann man sich allerdings sparen, da die Java-API bereits Klassen anbietet, welche diese Aufgabe übernehmen:

• Kürzer:

Einlesen von Dateien

15 Programmieren in Java

InputStream stream = Files.newInputStream(path);

InputStreamReader reader = new InputStreamReader(stream);

BufferedReader buffer = new BufferedReader(reader);

BufferedReader buffer = Files.newBufferedReader(path);

Path path = Paths.get("files/test.txt");

InputStream stream = Files.newInputStream(path);

BufferedInputStream buffer = new BufferedInputStream(path);

Name: Patrick Förster

• Mit der Methode readLine des BufferedReader kann eine Datei nun Zeile

für Zeile eingelesen werden

• Nach dem Einlesen einer Datei muss der InputStream geschlossen werden

Einlesen von Dateien

16 Programmieren in Java

String line;

StringBuilder out = new StringBuilder();

while ((line = reader.readln()) != null) out.append(line).append("\n");

stream.close();

try {

// STREAM ERZEUGEN

try {

// STREAM EINLESEN

}

finally {

try {

// STREAM SCHLIEßEN

}

catch (IOException io) {…}

}

}

catch (IOException io) {…}

• Die meisten Datei-Operationen können zu einer IOException

führen • Erzeugung, Einlesen und Schließen des Streams sollten getrennt werden, um in jedem Fall die Change zu haben den Stream zu schließen

Name: Patrick Förster

• Mit Java 7 wurde die try-catch-Anweisung um das automatische

Schließen von Ressourcen erweitert • Als Ressource werden alle Objekte angesehen, die das Interface java.io.Closeable implementieren

• Ein InputStream ist Closable:

Path path = Paths.get("files/test.txt");

StringBuilder out = new StringBuilder();

try (InputStream stream = Files.newInputStream(path)) {

BufferedReader reader = new BufferedReader(new InputStreamReader(stream));

String line;

while ((line = reader.readln()) != null) out.append(line).append("\n");

}

catch (IOException io) {…}

Einlesen von Dateien

17 Programmieren in Java

( try

}

Closeable { = BEZEICHNER … )

Name: Patrick Förster

• Das Schreiben in eine Datei verläuft im Prinzip analog • Anstelle eines InputStreams wird nun ein OutputStream verwendet • Wie beim InputStream sollte ein Puffer eingesetzt werden

• Das Beispiel baut darauf das Text in eine Datei geschrieben werden soll

• Die Schreiboperation wird auf dem Puffer ausgeführt • Die wirkliche Datei ist damit noch nicht mit dem Inhalt gefüllt • Mit flush wird der Pufferinhalt in die Datei übertragen (Betriebssystem) • Obiges kann man über die Hilfsklasse Files in einem Aufruf erledigen:

Schreiben in Dateien

18 Programmieren in Java

String content = …;

Path path = …;

try (OutputStream stream = Files.newOutputStream(path)) {

BufferedWriter writer = new BufferedWriter (new OutputStreamReader(stream));

writer.write(content);

writer.flush();

}

catch (IOException io) {…}

try {

Files.write(path, content.getBytes());

catch (IOException io) {…}

Name: Patrick Förster

• Über einen DirectoryStream werden alle Dateien eines Ordners eingelesen • DirectoryStream ist ebenfalls Closeable (erbt nicht von InputStream) • Über Files kann festgestellt werden, ob ein Pfad einem Ordner entspricht

• Man beachte: Die for-Iteration läuft über Objekt-Instanzen • Man würde Path erwarten

• Dies ist in Wirklichkeit auch der Fall, nur ist es mit den bisherigen Mitteln nicht möglich den Compiler davon zu überzeugen

• Siehe: Vorlesung „Generische Datentypen“

Ordnerinhalte einlesen

19 Programmieren in Java

Path path = …

if (Files.isDirectory(path)) {

try (DirectoryStream stream = Files.newDirectoryStream(path)) {

for (Object object : stream) {

System.out.println(object);

}

}

catch (IOException io) {…}

}

Name: Patrick Förster

• Dem newDirectoryStream-Aufruf kann ein weiterer Parameter mitgegeben

werden, über den die Ordnerinhalte gefilter werden können • Der Parameter ist vom Typ java.nio.file.DirectoryStream.Filter • Filter ist ein Interface mit einer Methode:

• Ein möglicher Filter, der nur Dateien mit der Endung .txt liefert:

• Einbinden:

Ordnerinhalte filtern

20 Programmieren in Java

public static class MyFilter implements DirectoryStream.Filter {

public boolean accept(Object entry) {

Path path = (Path) entry;

path = path.getName(path.getNameCount() - 1);

return path.toString().endsWith(".txt");

}

}

boolean accept(Object entry) throws IOException;

MyFilter filter = new MyFilter();

try (DirectoryStream stream = Files.newDirectoryStream(path, filter)) {…}

catch (IOException io) {…}

Name: Patrick Förster

• Java bietet die Möglichkeit anonyme Klassen zu definieren • D.h. Klassen, für die kein Name vergeben wird • Da diese Klassen allerdings keinen Namen haben, kann man sie nicht wie gewohnt über class definieren

• Die Definition findet quasi „ad hoc“ mit der Initialisierung einer Instanz statt • Und ist immer die Erweiterung einer bestehenden Klasse oder die Implementierung eines Interfaces

• Es kann keine Instanz von DirectoryStream.Filter erzeugt werden

• Hier wird eine anonyme Klasse definiert, die das Interface implementiert • Und direkt eine Instanz dieser Klasse erzeugt • Die dann einem Instanzattribut zugewiesen wird • Anonyme Klassen sind nicht statische innere Klassen

Anonyme Klassen

21 Programmieren in Java

private DirectoryStream.Filter filter = new DirectoryStream.Filter() {

public boolean accept(Object entry) {

// wie oben

}

};

Name: Patrick Förster

• Da anonyme Klasse nicht statisch sind, kann innerhalb der Implementierung auf alle Attribute der umschließenden Klasse zugegriffen werden • Anonyme Klassen können auch innerhalb einer Methode definiert werden:

• Insbesondere können so Parameter an die Klasse übergeben werden • Allerdings müssen die Parameter dafür als final deklariert werden

Anonyme Klassen

22 Programmieren in Java

public void filter() {

private DirectoryStream.Filter filter = new DirectoryStream.Filter() {

public boolean accept(Object entry) {/* wie oben */}

};

try (DirectoryStream stream = Files.newDirectoryStream(path, filter)) {…}

catch (IOException io) {…}

}

public void filter(final String extension) {

private DirectoryStream.Filter filter = new DirectoryStream.Filter() {

public boolean accept(Object entry) {

Path path = (Path) entry;

return path.getName(path.getNameCount() - 1).toString().endsWith(extension);

}

};

}

Name: Patrick Förster

• Lassen Sie die Klasse Harddrive das Interface FileHandler implementieren

Aufgabe

23 Programmieren in Java

public interface FileHandler {

/*

Liest den Inhalt der Datei mit dem Pfad „fileName“ ein und liefert diesen zurück

Schmeißt eine IOException, falls die Datei nicht existiert oder das Einlesen fehlschlägt

*/

String read(String fileName) throws IOException;

/*

Speicher den als „content“ angegebenen Text in der Datei mit dem Pfad „fileName“ ab

Schmeißt eine IOException, falls das Schreiben fehlschlägt

*/

void save(String fileName, String content) throws IOException;

/*

Löscht die Datei mit dem Pfad „fileName“

Die Methode soll „false“ liefern, falls die angegebene Datei nicht existiert, ansonsten true

Schmeißt eine IOException falls das Löschen fehlschlägt

*/

boolean delete(String fileName) throws IOException;

/*

Liefert einen Path-Array mit allen Pfaden zu Dateien, die die Endung „extension“ haben

Schmeißt eine IOException falls der Ordnerpfad nicht existiert oder das Einlesen fehlschlägt

*/

Path[] delete(String directoryName, final String extension) throws IOException;

};