Datei-Handling Anonyme Klassen
2 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: SICHTBARKEIT KLASSEN-BEZEICHNER { enum ENUM-BEZEICHNER, public enum Slot { PROCESSOR, RAM, 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: Slot.PROCESSOR
3 Inner-Class In Java können Klassen/Interfaces innerhalb von Klassen/Interfaces definiert werden MODIFIKATOREN KLASSEN-BEZEICHNER { class enum public interface Pluggable { MODIFIKATOREN class enum KLASSEN-BEZEICHNER { public static enum Slot { PROCESSOR, RAM, Der Zugriff auf innere Klassen erfolgt analog zu Klassenattributen über den umschließenden Klassennamen Pluggable.Slot slot = Pluggable.Slot.PROCESSOR; Nur bei inneren Klassen kann der Modifikator static genutzt werden
4 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: GraphicsCard.Resolution resolution = new GraphicsCard.Resolution(); GraphicsCard.Resolution resolution = new GraphicsCard.Resolution(); // Resolution statisch // Resolution nicht statisch Instanzen nicht statischer innerer Klassen können nur innerhalb von Instanzen der umschließenden Klasse erzeugt werden: public class GraphicsCard { public class Resolution { public Resolution createresolution(int width, int height) { return new Resolution(width, height); Jede Instanz einer nicht statischen inneren Klasse hat Zugriff auf die Instanz, die sie erzeugt hat und damit auf deren Attribute und Methoden
5 Fehlerbehandlung durch Exceptions 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 MODIFIKATOREN RÜCKGABETYP BEZEICHNER ( throws EXCEPTION-TYP, ; public void plugin(pluggable pluggable) throws Exception{ Innerhalb der Methode wird eine Exception-Instanz mittels throw geworfen: if (!slotavailablefor(pluggable.getslot())) { throw new Exception("FEHLERMELDUNG"); 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
6 Fehlerbehandlung durch Exceptions Man betrachte folgende Methodensignatur und Methodenaufruf public Object createsomething() throws Exception{ Object something = createsomething(); System.out.print(something); 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
7 Try-Catch-Finally try { AUFRUF EINER METHODE, DIE MIT THROWS DEFINIERT WURDE catch ( EXCEPTION-TYP BEZEICHNER_1 ) { FEHLERBEHANDLUNG catch ( EXCEPTION-TYP BEZEICHNER_N ) { finally { optional 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" );
8 Eigene Exceptions Da Exception eine Java-Klasse ist, kann von ihr geerbt werden Durch Vererbung von Exception können eigene Exceptions definiert werden public class ConfigurationException extends Exception { public ConfigurationException(String message) { super(message); public void plugin(pluggable pluggable) throws ConfigurationException { if (!slotavailablefor(pluggable.getslot())) { throw new ConfigurationException ( "FEHLERMELDUNG" ); CircuitBoard board = ; Pluggable pluggable = ; try { board.plugin(pluggable); catch (ConfigurationException e) { System.out.println(e.getMessage() );
9 Laufzeit-Exceptions 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!
10 Anwendung Module[] modules; CircuitBoard plugin(pluggable): void throws ConfigurationException createmodule(pluggable.slot, int size): Module Module Pluggable[] slots; Pluggable.Slot slot; int max; add(pluggable): void throws ConfigurationException 1 n 1 n <<interface>> Pluggable 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: public class CircuitBoard { protected class Module { 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
11 Anwendung 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 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); 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
12 Anwendung Computer RAMModule GraphicsCard Connector <<interface>> Pluggable CircuitBoard Module Device Soundcard Harddrive Processor 1
13 Einlesen von Dateien 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: Path path = Paths.get("files/test.txt"); 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
14 Einlesen von Dateien Über die Hilfsklasse java.nio.file.files wird ein InputStream erzeugt Path path = Paths.get("files/test.txt"); InputStream stream = Files.newInputStream(path); Natürlich muss dafür die angegebene Datei existieren Ob eine Datei existiert, wird ebenfalls über Files angefragt if (Files.exists(path)) { else { 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
15 Einlesen von Dateien Ein InputStream wird durch eine sogenannte Wrapper -Klasse durch einen Puffer erweitert Path path = Paths.get("files/test.txt"); InputStream stream = Files.newInputStream(path); BufferedInputStream buffer = new BufferedInputStream(path); 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: InputStream stream = Files.newInputStream(path); InputStreamReader reader = new InputStreamReader(stream); BufferedReader buffer = new BufferedReader(reader); Kürzer: BufferedReader buffer = Files.newBufferedReader(path);
16 Einlesen von Dateien Mit der Methode readline des BufferedReader kann eine Datei nun Zeile für Zeile eingelesen werden String line; StringBuilder out = new StringBuilder(); while ((line = reader.readln())!= null) out.append(line).append("\n"); Nach dem Einlesen einer Datei muss der InputStream geschlossen werden stream.close(); 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 try { // STREAM ERZEUGEN try { // STREAM EINLESEN finally { try { // STREAM SCHLIEßEN catch (IOException io) { catch (IOException io) {
17 Einlesen von Dateien 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 try ( Closeable BEZEICHNER = ) { 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) {
18 Schreiben in Dateien 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 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) { 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: try { Files.write(path, content.getbytes()); catch (IOException io) {
19 Ordnerinhalte einlesen Ü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 Path path = if (Files.isDirectory(path)) { try (DirectoryStream stream = Files.newDirectoryStream(path)) { for (Object object : stream) { System.out.println(object); catch (IOException io) { 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
20 Ordnerinhalte filtern 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: boolean accept(object entry) throws IOException; Ein möglicher Filter, der nur Dateien mit der Endung.txt liefert: 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"); Einbinden: MyFilter filter = new MyFilter(); try (DirectoryStream stream = Files.newDirectoryStream(path, filter)) { catch (IOException io) {
21 Anonyme Klassen 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 private DirectoryStream.Filter filter = new DirectoryStream.Filter() { public boolean accept(object entry) { // wie oben ; 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
22 Anonyme Klassen 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: 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) { Insbesondere können so Parameter an die Klasse übergeben werden Allerdings müssen die Parameter dafür als final deklariert werden 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); ;
23 Aufgabe Lassen Sie die Klasse Harddrive das Interface FileHandler implementieren 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; ;