Programmieren in Java Stephan Fischli Winter 2016/17 Inhalt ● Einführung ● Datentypen ● Klassen und Objekte ● Enumerations ● Packages ● Vererbung ● Interfaces ● Innere Klassen ● Standardbibliothek ● Exception‐Handling ● Collections ● Generics ● Threading Einführung Warum Java? Einführung ● Einfachheit und Robustheit der Sprache ● Plattformunabhängigkeit (write once – run everwhere) ● Vielseitigkeit der Anwendungen ● Grosse Verbreitung 6 Java‐Plattform ● ● ● ● ● Sprache Laufzeitumgebung Virtuelle Maschine (JVM), Garbage Collector, Hotspot‐Compiler Standardbibliothek Collections, Ein‐/Ausgabe, GUI, Datenbanken, Networking, ... Tools Compiler, Interpreter, Dokumentation, ... Editionen Standard (Java SE), Enterprise (Java EE), Micro (Java ME) Einführung 7 Java‐Architektur Java Sourcecode Java Compiler Java Bytecode Java Virtual Machine (JVM) Betriebssystem Hardware Einführung 8 Entwicklungszyklus ● ● ● Implementierung in Datei Hello.java public class Hello { public static void main(String[] args) { System.out.println("Hello " + (args.length > 0 ? args[0] : "world")); } } Kompilation erzeugt Datei Hello.class C:\> javac Hello.java Ausführung der Klasse C:\> java Hello John Einführung 9 Geschichte von Java Jahr 1995 1997 1998 2000 2002 2004 2006 2011 2014 2016 Einführung Version JDK 1.0 JDK 1.1 J2SE 1.2 J2SE 1.3 J2SE 1.4 J2SE 5 Java SE 6 Java SE 7 Java SE 8 Java SE 9 Features Erste Version Innere Klassen, Reflection, Datenbanken Collections, Swing HotSpot‐Compiler Asserts, reguläre Ausdrücke, neue I/O‐Bibliothek Generics, Annotationen, Enums, Concurrency Utilities Web Services Schliessen von Ressourcen, Multicatch von Exceptions Lambda‐Expressions, Streams Modulsystem 10 Java‐Community ● ● ● Java Community Process (JCP) Prozess zur Entwicklung neuer Java‐Spezifikationen https://jcp.org/en/home/index Java Specification Request (JSR) Vorschläge für Erweiterungen der Java‐Plattform https://jcp.org/en/jsr/overview OpenJDK Open‐Source Entwicklung der Java‐Plattfom http://openjdk.java.net/ Einführung 11 Literatur ● ● ● Einführung The Java Tutorial Raymond Gallardo et.al. (Addison‐Wesley) Effective Java Joshua Bloch (CreateSpace) Einführung in Java Kai Günster (Rheinwerk) 12 Datentypen Primitive Typen Primitive Typen ● repräsentieren Zahlen (byte, short, int, long, float, double), Zeichen (char) oder Wahrheitswerte (boolean) ● haben eine festgelegte Speichergrösse und Wertebereich ● können implizit oder explizit ineinander umgewandelt werden (Cast) ● ● erlauben bestimmte Operationen (Rechnen, Vergleiche, Bit‐ Manipulationen, Logische Verknüpfungen) haben Wertsemantik int a = 42; int b = a; int c; Datentypen a 42 b 42 c 0 14 Objekttypen Objekttypen ● repräsentieren Objekte und Arrays ● haben Referenzsemantik ● können null sein Account a = new Account("John", "12345", 1000.0); Account b = a; Account c; a John 12345 b c 1000.0 null Datentypen 15 Referenzsemantik ● ● ● Datentypen Nach Zuweisungen zeigen Referenzen auf dasselbe Objekt a.customer = "Mary"; System.out.println(b.customer); System.out.println(c.customer); Bei der Parameterübergabe werden Referenzen übergeben void deposit(Account x, double amount) { x.balance += amount; } deposit(a, 200.0); System.out.println(a.balance); Bei Vergleichen werden Referenzen verglichen System.out.println(a == b); System.out.println(a == c); 16 Wrapper‐Klassen Wrapper‐Klassen (Byte, Short, Integer, Long, Float, Double, Character, Boolean) ● repräsentieren Objekte, die einen primitiven Typ enthalten ● haben zusätzliche Funktionalität (z.B. Parsen von Strings) ● können explizit oder implizit umgewandelt werden Integer A = Integer.valueOf(42); int a = A.intValue(); Integer A = 42; int a = A; A a // Autoboxing // Unboxing 42 42 Datentypen 17 Arrays Arrays ● enthalten eine feste Anzahl primitiver Werte oder Objekte eines Typs ● werden vor ihrer Verwendung deklariert, konstruiert und initialisiert ● kennen ihre Länge (Attribut length) Account[] a = new Account[5]; a[0] = new Account("John", "12345", 1000.0); for (int i = 0; i < a.length; i++) System.out.println(a[i].getCustomer()); a John null 12345 null 1000.0 null null Datentypen 18 Strings Strings ● sind Zeichenketten, die als Objekte der Klasse String repräsentiert werden ● können nicht verändert werden (immutable) ● können mit dem Operator + konkateniert werden String copyright = "\u00a9 Copyright by ..."; if ("nobody".equals(myName)) ... Sytem.out.println("Server startup time: " + time + " ms"); Datentypen 19 Klassen und Objekte Definition einer Klasse ● Eine Klasse definiert einen Datentyp bestehend aus Feldern (Daten) und Methoden (Funktionen), welche auf die Felder zugreifen pubic class Account { // Felder String customer; String pin; double balance = 0.0; } Klassen und Objekte // Methoden void deposit(double amount) { balance += amount; } void withdraw(double amount) { balance -= amount; } Account customer pin balance deposit() withdraw() 22 Erzeugen von Objekten ● Objekte einer Klasse werden mit dem new‐Operator erzeugt ● Jedes Objekt erhält eine eigene Kopie der Felder ● Über die Objektreferenz kann auf die Felder und Methoden des Objekts zugegriffen werden Account a = new Account(); a: Account a.customer = "John"; a.pin = "12345"; a.balance = 1000.0; customer="John" pin="12345" balance=1200.0 a.deposit(200.0); Klassen und Objekte 23 Zugriffsrechte von Feldern und Methoden ● ● Private Felder sind nur innerhalb der Klasse zugreifbar (Daten‐ kapselung), ebenso private Methoden Public Felder und Methoden sind von überall aus zugreifbar public class Account { private String customer; private String pin; private String balance = 0.0; public String getCustomer() { return customer; } public double getBalance() { return balance; } } public void deposit(double amount) { ... } public void withdraw(double amount) { ... } Account a = new Account(); a.balance = 1000.0; Klassen und Objekte // Selektoren // Modifikatoren // Fehler 24 Initialisieren von Objekten ● ● Konstruktoren werden implizit bei der Erzeugung eines Objekts aufgerufen und dienen der Initialisierung der Felder Wird kein Konstruktor definiert, so erzeugt der Compiler einen Defaultkonstruktor, der die Felder mit Defaultwerten initialisiert public class Account { private String customer; private String pin; private double balance = 0.0; } public Account(String name, String code) { customer = name; pin = code; } ... // Konstruktor Account a = new Account("John", "12345"); Klassen und Objekte 25 Überladen von Methoden ● ● Mehrere Methoden können denselben Namen haben, sofern sie sich in den Parametern unterscheiden Bei einem Aufruf entscheidet der Compiler anhand der Argumente, welche Methode ausgeführt wird (Argument Matching) public class Account { ... public void withdraw(double amount) { balance -= amount; } public void withdraw(String code, double amount) { if (pin.equals(code)) withdraw(amount); } } Account a = new Account(); a.withdraw("12345", 200.0); Klassen und Objekte 26 Mehrfache Konstruktoren ● ● Auch Konstruktoren können überladen werden Mittels this() kann ein Konstruktor einen anderen Konstruktor aufrufen (muss das erste Statement sein) public class Account { ... public Account(String name, String code, double amount) { customer = name; pin = code; balance = amount; } public Account(String name, String code) { this(name, code, 0.0); } ... } Account a = new Account("John", "12345", 1000.0); Klassen und Objekte 27 this‐Referenz ● ● Jeder Methode wird implizit die Referenz this auf das eigene Objekt übergeben this wird oft in Konstruktoren verwendet, um Namenskonflikte zwischen Feldern und Parametern aufzulösen public class Account { private String customer; private String pin; private double balance = 0.0; } Klassen und Objekte public Account(String customer, String pin) { this.customer = customer; this.pin = pin; } ... 28 Klassenfelder und ‐methoden ● ● Klassenfelder existieren unabhängig von Objekten einmal pro Klasse und werden zur Speicherung globaler Daten verwendet Klassenmethoden werden nicht auf einem Objekt sondern auf der Klasse aufgerufen und können nur auf Klassenfelder zugreifen public class Account { private static double withdrawLimit; ... public static int getWithdrawLimit() { return withdrawLimit; } public void withdraw(double amount) { if (amount <= withdrawLimit) balance -= amount; } } Account withdrawLimit customer pin balance getWithdrawLimit() deposit() withdraw() double limit = Account.getWithdrawLimit(); Klassen und Objekte 29 Konstante Felder ● Konstante Felder müssen bei ihrer Definition oder im Konstruktor initialisiert werden, danach dürfen sie nicht mehr verändert werden public class Account { private static final double WITHDRAW_LIMIT = 5000.0; ... public void withdraw(double amount) { if (amount <= WITHDRAW_LIMIT) balance -= amount; } } Klassen und Objekte 30 Enumerations Definition von Enums Enum‐Typen ● werden verwendet, um Konstanten zu definieren ● sind Klassen mit einer vordefinierten Menge von Objekten ● sind implizit von der Standardklasse Enum abgeleitet und erben allgemeine Methoden public enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } for (Month m : Month.values()) System.out.println(m.name() + " has ordinal " + m.ordinal()); Enumerations 32 Verwendung von Enums Enum‐Werte ● ● werden mit dem Enum‐Namen und dem Punktoperator referenziert können mit dem Vergleichsoperator verglichen und in switch‐ Statements verwendet werden Month month = Month.AUG; if (month == Month.JAN) ... int days = 0; switch (month) { case JAN: days = 31; break; case FEB: days = 28; break; ... case DEC: days = 31; } Enumerations 33 Enums mit Feldern und Methoden ● ● Enums können auch Felder und Methoden haben Felder von Enums sind final und müssen mit einem privaten Konstruktor initialisiert werden public enum Month { JAN(31), FEB(28), MAR(30), ..., DEC(31); private final int days; private Month(int days) { this.days = days; } public int days() { return days; } } Month month = Month.AUG; System.out.println(month + " has " + month.days() + "days"); Enumerations 34 Packages Definition eines Package ● ● ● Packages sind hierarchisch aufgebaute Sammlungen von Klassen und definieren Namensräume Um eine Klasse zu einem Package hinzuzufügen, wird am Anfang des Sourcefiles ein package‐Statement verwendet Ohne package‐Statement gehören die Klassen zum Default‐Package ohne Namen package org.bank.account; public class Account { ... } Packages 36 Importieren eines Package ● ● ● Der vollständige Name einer Klasse setzt sich aus dem Package‐ und dem Klassennamen zusammen Mit einem import‐Statement können Klassen mit dem Klassennamen allein verwendet werden Die Klassen aus dem Package java.lang werden defaultmässig importiert package org.bank; import org.bank.account.Account; import org.bank.account.*; // alle Klassen public class Bank { private Account[] accounts; public static void main(String[] args) { ... } ... } Packages 37 Klassenpfad ● ● ● ● ● Packages Die Package‐Hierarchien müssen sich in der Verzeichnisstruktur widerspiegeln Der Klassenpfad enthält die Root‐Verzeichnisse der Package‐ Hierarchien und muss dem Compiler und Interpreter übergeben werden Directory‐Struktur: C:\project\src\org\bank\Bank.java C:\project\src\org\bank\account\Account.java Kompilation: C:\project> javac -d bin src\org\bank\account\Account.java C:\project> javac -cp bin -d bin src\org\bank\Bank.java Ausführung: C:\project> java -cp bin org.bank.Bank 38 Vererbung Ableiten einer Klasse Eine abgeleitete Klasse ● erbt alle Felder und Methoden der Basisklasse ● kann neue Felder und Methoden hinzufügen (Erweiterung) ● definiert einen zum Typ der Basisklasse kompatiblen Datentyp public class SavingsAccount extends Account { private double withdrawLimit; } public double getWithdrawLimit() { return withdrawLimit; } Account customer pin balance deposit() withdraw() SavingsAccount withdrawLimit getWithdrawLimit() Vererbung 40 Konstruktorverkettung ● ● ● Konstruktoren werden nicht vererbt Mittels super() kann ein Konstruktor der abgeleiteten Klasse einen Konstruktor der Basisklasse aufrufen (muss das erste Statement sein) Andernfalls wird implizit der Defaultkonstruktor der Basisklasse aufgerufen public class SavingsAccount extends Account { private double withdrawLimit; } public SavingsAccount(String customer, String pin, double limit) { super(customer, pin); withdrawLimit = limit; } ... Vererbung 41 Zugriffsrecht protected ● Protected Felder und Methoden sind in der eigenen Klasse, in Klassen desselben Package und in abgeleiteten Klassen zugreifbar public class Account { protected String customer; protected String pin; protected double balance; ... } public class SavingsAccount extends Account { ... private boolean isWithdrawalValid(double amount) { return amount <= withdrawLimit && amount <= balance; } } Vererbung 42 Überschreiben von Methoden ● ● Eine Methode der abgeleiteten Klasse überschreibt eine Methode der Basisklasse, wenn sie denselben Namen und dieselben Parameter hat Der Typ des Rückgabewerts beider Methoden muss übereinstimmen public class SavingsAccount extends Account { private double withdrawLimit; ... public void withdraw(double amount) { if (isWithdrawalValid(amount)) balance -= amount; } } Account customer pin balance deposit() withdraw() SavingsAccount withdrawLimit withdraw() isWithdrawalValid() Vererbung 43 Aufruf überschriebener Methoden ● Eine überschriebene Methode kann in der abgeleiteten Klasse weiterhin über die Referenz super aufgerufen werden public class SavingsAccount extends Account { private double withdrawLimit; ... public void withdraw(double amount) { if (isWithdrawalValid(amount)) super.withdraw(amount); } } Vererbung 44 Dynamische Bindung von Methoden ● ● Eine Referenz vom Typ einer Basisklasse kann auch auf ein Objekt einer abgeleiteten Klasse zeigen (Polymorphismus) Beim Aufruf einer überschriebenen Methode entscheidet der aktuelle Objekttyp, welche Methode ausgeführt wird Account a; a = new Account(...); a.withdraw(200.0); // Methode der Klasse Account a = new SavingsAccount(...); a.withdraw(200.0); // Methode der Klasse SavingsAccount Vererbung 45 Finale Methoden und Klassen ● ● Eine finale Methode kann in abgeleiteten Klassen nicht überschrieben werden Eine finale Klasse kann nicht abgeleitet werden public class Account { ... public final String getCustomer() { return customer; } public final double getBalance() { return balance; } } public final class SavingsAccount { ... } Vererbung 46 Abstrakte Methoden und Klassen ● Abstrakte Methoden sind Methoden ohne Implementierung ● Hat eine Klasse eine abstrakte Methode, so ist die Klasse abstrakt ● Abstrakte Klassen können nicht instanziiert werden, dienen aber als Basisklassen anderer Klassen public abstract class Account { public void deposit(double amount) { ... } Account public abstract void withdraw(double amount); deposit() } withdraw() class SavingsAccount extends Account { public void withdraw(double amount) { ... } } Savings Personal class PersonalAccount extends Account { Account Account public void withdraw(double amount) { ... } } withdraw() withdraw() Vererbung 47 Interfaces Definition eines Interface ● ● Ein Interface definiert einen abstrakten Datentyp und beschreibt das Verhalten von Klassen Alle Methoden eines Interface sind implizit public und abstract public interface Comparator { int compare(Object o1, Object o2); } Interfaces 50 Implementieren eines Interface ● ● ● Eine Klasse implementiert ein Interface, indem sie die Methoden des Interface implementiert Eine Klasse kann mehrere Interfaces implementieren Eine Klasse ist typkompatibel zu den von ihr implementierten Interfaces public class AccountComparator implements Comparator { public int compare(Object o1, Object o2) { Account a1 = (Account) o1; Account a2 = (Account) o2; return (int) (a1.getBalance() - a2.getBalance()); } Comparator } compare() AccountComparator compare() Interfaces 51 Anwendung von Interfaces ● ● Interfaces werden oft als Parameter generischer Methoden verwendet Als Argumente können Objekte aller Klassen übergeben werden, die das Interface implementieren public class Util { public static void sort(Object x[], Comparator c) { for (int i = x.length - 1; i > 0; i--) for (int j = 0; j < i; j++) if (c.compare(x[j], x[j + 1]) > 0) { Object t = x[j]; x[j] = x[j + 1]; x[j + 1] = t; } } } Util sort() Comparator compare() Account[] accounts = ... Util.sort(accounts, new AccountComparator()); Interfaces 52 Vererbung von Interfaces ● Ein Interface kann von anderen Interfaces abgeleitet werden, wodurch es alle Methoden des Basis‐Interface erbt public interface Figure { void move(int dx, int dy); void rotate(int angle); void setColor(Color c); } interface ClosedFigure extends Figure { void setFillColor(Color c); } Interfaces Figure move() rotate() setColor() ClosedFigure setFillColor() 53 Innere Klassen Statische innere Klassen ● Statische innere Klassen sind Klassen, die innerhalb einer andern Klasse definiert werden und somit zu deren Namensraum gehören public class Account { private double balance; ... public static class BalanceComparator implements Comparator { public int compare(Object o1, Object o2) { Account a1 = (Account) o1; Account a2 = (Account) o2; return (int) (a1.balance - a2.balance); } } } Account[] accounts = ... Util.sort(accounts, new Account.BalanceComparator()); Innere Klassen 56 Nicht‐statische innere Klassen Nicht‐statische innere Klassen ● ● werden mit einem umgebenden Objekt instanziiert können auf die Felder und Methoden der umgebenden Klasse zugreifen public class Account { private Transaction tx = new Transaction(); private double balance; ... public void deposit(int amount) { tx.execute(Transaction.DEPOSIT, amount); } private class Transaction { static final int DEPOSIT = 1, WITHDRAWAL = -1; void execute(int type, double amount) { balance += type*amount; } } } Innere Klassen 57 Anonyme Klassen Anonyme Klassen ● ● haben keinen Namen und werden gerade dort definiert, wo sie gebraucht werden können auf lokale Variablen zugreifen, die final sind Account[] accounts = ... final boolean ascending = ... Util.sort(accounts, new Comparator() { public int compare(Object o1, Object o2) { Account a1 = (Account) o1; Account a2 = (Account) o2; return ascending ? (int) (a1.getBalance() - a2.getBalance()) : (int) (a2.getBalance() - a1.getBalance()); } }); Innere Klassen 58 Standardbibliothek Packages ● ● Die Standard‐Bibliothek umfasst über 4000 Klassen Kernklassen sind im Package java, speziellere im Package javax enthalten Package java.lang java.util java.io, java.nio java.math java.net java.security java.sql java.text java.time java.awt Standardbibliothek Inhalt Basisklassen (z.B. Object, String, Thread) Hilfsklassen wie Collections Ein‐ und Ausgabe Arithmetik langer Zahlen Netzwerkkommunikation Kryptographie Anbindung relationaler Datenbanken Internationalisierung Zeit‐ und Datumswerte Grafische Benutzeroberflächen 60 Klassenhierarchie ● In Java gibt es keine Mehrfachvererbung und alle Klassen sind implizit von der Klasse Object abgeleitet Object String Thread ArrayList InputStream FileInputStream OutputStream ByteArrayInputStream Math Socket Standardbibliothek 61 Klasse Object ● Die Methoden der Klasse Object definieren ein gemeinsames Verhalten aller Objekte: Standardbibliothek – toString() erzeugt eine Stringdarstellung eines Objekts – equals() vergleicht zwei Objekte – hashCode() erzeugt einen Hashcode eines Objekts – clone() kopiert ein Objekt – finalize() wird aufgerufen, bevor ein Objekt zerstört wird – notify(), notifyAll() und wait() dienen der Thread‐Synchronisation – getClass() gibt die Klasse eines Objekts zurück 62 Darstellung von Objekten als Strings ● ● Um eine Darstellung von Objekten als Strings zu erzeugen, kann die Methode toString() überschrieben werden Diese wird implizit von der Methode String.valueOf() und dem Konkatenationsoperator aufgerufen public class Account { private String customer; private String pin; private double balance; ... public String toString() { return "Account of " + customer + " with balance " + balance; } } Account a = new Account(...); System.out.println(a + " created"); Standardbibliothek 63 Vergleichen von Objekten ● ● Um den Zustand von Objekten vergleichen zu können, muss die Methode equals() überschrieben werden Diese Methode wird u.a. beim Suchen von Objekten in Listen verwendet public class Account { ... public boolean equals(Object obj) { if (obj == null || !(obj instanceof Account)) return false; Account a = (Account) obj; return a.customer.equals(customer); } } Account a1 = new Account(...); Account a2 = new Account(...); if (a1.equals(a2)) ... Standardbibliothek 64 Hashcode von Objekten ● ● Um Objekte effizienter vergleichen zu können, kann die Methode hashCode() überschrieben werden (wobei der Hashcode gleicher Objekte gleich sein muss) Diese Methode wird u.a. zum Auffinden von Objekten in Hashtabellen verwendet public class Account { ... public int hashCode() { return customer.hashCode(); } } Account a1 = new Account(...); Account a2 = new Account(...); if (a1.hashCode() == a2.hashCode()) if (a1.equals(a2)) ... Standardbibliothek 65 Exception‐Handling Einführung Exceptions ● ● ● ● repräsentieren Fehler und ermöglichen, den normalen Programm‐ ablauf von Fehlersituationen zu trennen enthalten Informationen über den aufgetretenen Fehler werden geworfen und im Call Stack des Programms automatisch nach oben propagiert können irgendwo abgefangen und behandelt werden Exception‐Handling 68 Exception‐Typen ● ● Die Information über den aufgetretenen Fehler besteht primär aus dem Typ der geworfenen Exception Es gibt in der Java‐Klassenbibliothek eine grosse Anzahl vordefinierter Exception‐Klassen Exception IOException ParseException RuntimeException FileNotFoundException EOFException NullPointerException IllegalArgumentException IndexOutOfBoundsException Exception‐Handling 69 Definieren einer Exception ● ● ● Exception‐Klassen werden von der Standardklasse Exception abgeleitet und repräsentieren Fehlerkategorien Dem Basiskonstruktor kann eine Fehlermeldung übergeben und mit der Methode getMessage wieder abgefragt werden Detailinformationen über den aufgetretenen Fehler können in zusätzlichen Feldern enthalten sein public class TransactionException extends Exception { public TransactionException(String message) { super(message); } Exception ... getMessage() } printStackTrace() TransactionException Exception‐Handling 70 Werfen einer Exception ● ● Mittels throw kann in einer Methode eine Exception geworfen werden, wodurch die Methode unmittelbar verlassen wird Exceptions müssen in der throws‐Klausel der Methode deklariert werden public class Account { private String customer; private String pin; private double balance; ... public void withdraw(double amount) throws TransactionException { if (amount > balance) throw new TransactionException("Insufficient funds"); balance -= amount; } } Exception‐Handling 71 Abfangen einer Exception ● ● Eine Exception kann mit einem try‐catch‐Statement abgefangen und behandelt werden Tritt im try‐Block eine Exception auf, so wird unmittelbar in den catch‐ Block verzweigt, andernfalls wird der catch‐Block übersprungen try { Account account = findAccount(nr); account.checkPin(pin); account.withdraw(amount) ... } catch (TransactionException e) { System.err.println("Error: " + e.getMessage()); } Exception‐Handling 72 Abfangen verschiedener Exceptions ● ● ● Verschiedene Exceptions können mit mehreren catch‐Blöcken unterschiedlich behandelt werden Es wird der erste catch‐Block ausgeführt, dessen Parametertyp zur geworfenen Exception passt Mit einem catch‐Block können auch mehrere Exceptions gleichzeitig abgefangen werden try { Account account = findAccount(nr); account.checkPin(pin); account.withdraw(amount); ... } catch (IllegalArgumentException e) { ... } catch (CredentialsException | TransactionException e) { ... } catch (Exception e) { ... } // fallback Exception‐Handling 73 Weitergeben einer Exception ● ● Nicht abgefangene Exceptions werden im Callstack automatisch nach oben propagiert, müssen aber deklariert werden Wird eine Exception nirgends abgefangen, so wird der Stack Trace der Exception ausgegeben und das Programm terminiert public class Bank { ... public void withdraw(int nr, String pin, double amount) throws CredentialsException, TransactionException { Account account = findAccount(nr); account.ckeckPin(pin); account.withdraw(amount); } } Exception‐Handling 74 Finally‐Block ● ● Oft müssen nach einer Fehlerbehandlung Aufräumarbeiten gemacht werden (z.B. Ressourcen freigeben) Das try‐catch‐Statement hat einen optionalen finally‐Block, der in jedem Fall am Ende ausgeführt wird try { Account account = findAccount(nr); account.checkPin(pin); account.withdraw(amount); ... } catch (IllegalArgumentException e) { ... } catch (CredentialsException | TransactionException e) { ... } finally { log("end of withdrawal"); } Exception‐Handling 75 Unchecked Exceptions Unchecked Exceptions ● repräsentieren unerwartete System‐ oder Programmierfehler ● sind von der Klasse RuntimeException abgeleitet ● müssen weder deklariert noch abgefangen werden public class Account { private String customer; private String pin; private double balance; ... public Account(String customer, Sting pin) { if (customer == null || pin == null) throw new IllegalArgumentException(); ... } } Exception‐Handling Exception RuntimeException IllegalArgumentException 76 Collections Übersicht ● ● ● Collections erlauben mehrere Objekte zu verwalten Im Gegensatz zu Arrays haben Collections keine feste Grösse und bieten zahlreiche Methoden zur Manipulation ihrer Elemente Das Collection‐Framework besteht aus mehreren Interfaces und verschiedenen Implementierungen Collection List ArrayList LinkedList Set HashSet TreeSet Map HashMap TreeMap Collections 78 Listen ● Elemente bleiben in der Reihenfolge, mit der sie hinzugefügt werden ● Mit dem Index kann direkt auf die Elemente zugegriffen werden ● Listen werden mithilfe eines Arrays oder als verkettete Liste implementiert List cities = new ArrayList(); cities.add("London"); cities.add("Paris"); cities.add("London"); cities.add("New York"); ... for (int i = 0; i < cities.size(); i++) { String city = (String) cities.get(i); System.out.println(city); } accounts.remove(0); Collections 79 Sets ● ● ● Sets enthalten keine Duplikate, und die Elemente haben keine bestimmte Reihenfolge (ausser bei TreeSets) Um auf die Elemente zuzugreifen, muss mit einer for‐each‐Schlaufe über das Set iteriert werden Sets werden mithilfe von Maps implementiert Set cities = new HashSet(); cities.add("London"); cities.add("Paris"); cities.add("London"); cities.add("New York"); ... for (Object city : cities) { System.out.println(city); } cities.remove("London"); Collections 80 Maps ● ● ● Maps bilden Objekte (Schlüssel) auf andere Objekte (Werte) ab HashMaps verwenden den Hashcode der Schlüsselobjekte, um die Wertobjekte zu finden, und sind somit sehr effizient TreeMaps haben zusätzlich eine Ordnung auf ihren Schlüsseln Map population = new HashMap(); population.put("London", 8_538_689); population.put("Paris", 2_240_621); population.put("London", 8_538_694); population.put("New York", 8_491_079); ... for (Object city : population.keySet()) { System.out.println(city + ": " + population.get(city)); } population.remove("London"); Collections 81 Iteratoren ● ● Alle Collections implementieren das Interface Iterable und stellen somit einen Iterator zur Verfügung Iteratoren sind vor allem nützlich, wenn während einer Iteration Elemente aus der Collection entfernt werden müssen List accounts = new LinkedList(); ... Iterator iter = accounts.iterator(); while (iter.hasNext()) { Account a = (Account) iter.next(); if (a.getCustomer() == null) iter.remove(); } Collections 82 Generics Typisierte Collections ● ● ● Collections können mit einem Typparameter versehen werden Es können dann nur Objekte vom entsprechenden Typ hinzugefügt werden (Typsicherheit) Beim Lesen der Elemente sind keine Typumwandlungen notwendig List<Account> accounts = new ArrayList<Account>(); accounts.add(new Account(...)); accounts.add("London"); // Compiler-Fehler ... for (int i = 0; i < accounts.size(); i++) { Account a = accounts.get(i); System.out.println(a); } Generics 84 Typisierte Iteratoren und Comparator ● ● Iteratoren typisierter Collections haben denselben Typparameter wie die Collection Zum Sortieren von Collections können typisierte Comparator verwendet werden Iterator<Account> iter = accounts.iterator(); while (iter.hasNext(); ) { Account a = iter.next(); System.out.println(a); } Collections.sort(accounts, new Comparator<Account>() { public int compare(Account a1, Account a2) { return (int) (a1.getBalance() - a2.getBalance()); } }); Generics 85 Generische Klassen ● Eine Klasse kann typisiert werden, indem sie mit einem formalen Typparameter versehen wird public class Box<E> { private E elem; public void put(E elem) { if (this.elem != null) throw IllegalStateException(); this.elem = elem; } E take() { Box E temp = elem; elem = null; return temp; elem } put() } E take() Box<Account> box = new Box<Account>(); box.put(new Account(...)); Account a = box.take(); Generics 86 Generische Methoden ● ● Generische Klassen können als Parameter‐ oder Rückgabetyp von Methoden verwendet werden Der entsprechende Typparameter muss in der Signatur der Methode aufgeführt werden public class Util { public static <E> void reverse(List<E> elems) { for (int i = 0; i < elems.size() / 2; i++) { int j = elems.size() - i - 1; E e = elems.get(i); elems.set(i, list.get(j)); elems.set(j, e); } } } List<Account> accounts = ... Util.reverse(accounts); Generics 87 Beschränkte Typparameter ● Ein beschränkter Typparameter steht für Typen, die eine gemeinsame Basisklasse haben public class Util { public static <E extends Number> double sum(List<E> elems) { double sum = 0; for (E elem : elems) { sum += elem.doubleValue(); } return sum; } } List<Integer> integers = ... Util.sum(integers); Generics 88 Generics und Vererbung ● ● Es gibt keine Typbeziehung zwischen den Instanziierungen einer typisierten Collection, selbst wenn die Typparameter kompatibel sind Der Grund liegt darin, dass nach einer Zuweisung die Typsicherheit nicht mehr gewährleistet wäre List<Integer> integers = new ArrayList<>(); integers.add(42); ... List<Number> numbers = integers; // Compiler-Fehler numbers.add(3.14159); integers.get(1); Generics 89 Wildcard‐Typen ● ● ● Ein Wildcard‐Typ ist eine Collection, deren Elemente von unbe‐ kanntem Typ sind Jede Instanziierung der entsprechenden Collection ist typkompatibel zum Wildcard‐Typ Wildcard‐Typen dürfen keine Elemente hinzugefügt werden List<Integer> integers = new ArrayList<>(); integers.add(42); ... List<?> elems = integers; Object elem = elems.get(0); elems.add(3.14159); // Compiler-Fehler Generics 90 Ein‐ und Ausgabe Dateien und Pfade ● Die Klasse File repräsentiert Dateipfade und stellt u.a. folgende Methoden zur Verfügung: Ein‐ und Ausgabe – exists() gibt an, ob eine Datei mit dem Pfad existiert – length() gibt die Grösse der Datei zurück – isFile(), isDirectory() geben den Typ der Datei an – canRead(), canWrite(), canExecute() geben die Zugriffsrechte an – createNewFile() erzeugt eine neue Datei – mkdir() erzeugt ein Verzeichnis – delete() löscht die Datei 92 Verzeichnisse ● ● Die Methode listFiles() der Klasse File erlaubt, die Dateien eines Verzeichnis abzufragen Mit einem FileFilter können die Dateien zudem gefiltert werden public class ListDir { public static void main(String[] args) { File dir = new File(args[0]); File[] files = dir.listFiles(new FileFilter() { public boolean accept(File file) { return file.isFile(); } }); for (File file : files) { System.out.println(file.getName() + " (" + file.length() + " bytes)"); } } } Ein‐ und Ausgabe 93 Streams ● Die Ein‐ und Ausgabe erfolgt über sogenannte Streams ● Streams erlauben das iterative Lesen und Schreiben von Daten ● Streams können zu einer Pipeline verkettet werden ● Es gibt Streams zur eigentlichen Ein‐/Ausgabe bzw. Streams zur Verarbeitung von Daten Verarbeitung Programm Ein‐ und Ausgabe Ein‐/Ausgabe Daten quelle 94 Ein‐/Ausgabe‐Streams Je nach Art der zu lesenden bzw. schreibenden Daten wird zwischen Byte‐ und Character‐Streams unterschieden Datenquelle Byte‐Streams Character‐Streams File FileInputStream FileOutputStream FileReader FileWriter Array ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter StringReader StringWriter String Pipe PipedInputStream PipedOutputStream PipedReader PipedWriter Ein‐ und Ausgabe 95 Verarbeitungs‐Streams Verarbeitung Byte‐Streams Umwandlung von Bytes in Characters Character‐Streams InputStreamReader OutputStreamWriter Umwandlung primi‐ tiver Typen in Bytes DataInputStream DataOutputStream Umwandlung von Objekten in Bytes ObjectInputStream ObjectOutputStream Pufferung von Daten BufferedInputStream BufferedReader BufferedOutputStream BufferedWriter Filterung von Daten FilterInputStream FilterOutputStream FilterReader FilterWriter Formatierung PrintStream PrintWriter Zudem können mit der Utility‐Klasse Scanner primitive Typen einfach gelesen werden Ein‐ und Ausgabe 96 Schliessen von Streams ● ● Streams sollten geschlossen werden, wenn sie nicht mehr gebraucht werden Da Streams das Interface AutoCloseable implementieren, kann dazu das try‐with‐resources‐Statement verwendet werden try (FileReader reader = new FileReader(...)) { int c = reader.read(); ... } catch (IOException e) { System.err.println("Fehler: " + e.getMessage()); } Ein‐ und Ausgabe 97 Lesen und Schreiben von Binärdaten ● Ein InputStream erlaubt das Lesen, ein OutputStream das Schreiben von Binärdaten public class Copy { public static void main(String[] args) throws IOException { try (FileInputStream in = new FileInputStream(args[0]); FileOutputStream out = new FileOutputStream(args[1])) { } Ein‐ und Ausgabe } } byte[] buffer = new byte[1024]; while (true) { int nbytes = in.read(buffer); if (nbytes == -1) break; out.write(buffer, 0, nbytes); } 98 Lesen und Schreiben von Textdaten ● Ein Scanner erlaubt das zeilenweise Lesen, ein PrintWriter das zeilenweise Schreiben von Textdaten public class Note { public static void main(String[] args) throws IOException { try (Scanner in = new Scanner(System.in); PrintWriter out = new PrintWriter(new FileWriter(args[0]))) { } } } while (true) { String line = in.nextLine(); if (line.isEmpty()) { break; } out.println(line); } Ein‐ und Ausgabe 99 Objekt‐Serialisierung ● ● Objekt‐Serialisierung ist ein Mechanismus zum Lesen und Schreiben ganzer Objektbäume Damit die Objekte einer Klasse serialisiert werden können, muss die Klasse das leere Interface Serializable implementieren public class Message implements Serializable { private static final long serialVersionUID = 1L; private Date date = new Date(); private String text; } Ein‐ und Ausgabe public Message(String text) { this.text = text; } public Date getDate() { return date; } public String getText() { return text; } 100 Schreiben von Objekten ● Die Klasse ObjectOutputStream erlaubt das Schreiben von Objekten public class PostIt { public static void main(String[] args) throws Exception { try (Scanner scanner = new Scanner(System.in); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(args[0]))) { } } } String text = scanner.nextLine(); Message msg = new Message(text); out.writeObject(msg); Ein‐ und Ausgabe 101 Lesen von Objekten ● Die Klasse ObjectInputStream erlaubt das Lesen von Objekten public class ReadIt { public static void main(String[] args) throws Exception { try (ObjectInputStream in = new ObjectInputStream( new FileInputStream(args[0]))) { } Ein‐ und Ausgabe } Message msg = (Message) in.readObject(); Date date = msg.getDate(); String text = msg.getText(); System.out.println(date + "\n" + text); } catch (FileNotFoundException e) {} 102 Threads Einführung ● Threads sind parallele Ablaufeinheiten innerhalb eines Prozesses und werden u.a. eingesetzt bei – Ein‐/Ausgabe‐Operationen, die blockieren können – der Behandlung externer Ereignisse – der Ausführung periodischer Aufgaben Haupthread start join Nebenthread run Threads 104 Klasse Thread ● ● ● In Java werden Threads durch Objekte der Standard‐Klasse Thread repräsentiert Ein Thread‐Objekt enthält in der run()‐Methode den Code, der im zugehörigen Thread ausgeführt werden soll Über das Thread‐Objekt kann der Thread gesteuert werden: – start() startet den Thread – join() wartet auf die Terminierung des Threads – sleep() unterbricht den Thread für eine bestimmte Zeit – interrupt() setzt ein Flag, um den Thread vorzeitig zu beenden – setPriority() bestimmt die Priorität des Thread Threads 105 Thread‐Zustände ● Ein Thread durchläuft verschiedene Zustände – Created: das Thread‐Objekt existiert, aber der zugehörige Thread wurde noch nicht gestartet – Runnable: der Thread wurde gestartet und ist lauffähig – Non‐Runnable: der Thread ist nicht lauffähig, weil er z.B. in einer Ein‐/Ausgabe‐Operation blockiert ist – Dead: der Thread hat terminiert Created Runnable Dead Non‐Runnable Threads 106 Erzeugen eines Threads (Variante 1) ● ● Um einen Thread zu erzeugen, wird von der Standardklasse Thread eine Klasse abgeleitet, die run()‐Methode überschrieben, die Klasse instanziiert und die start()‐Methode aufgerufen Das Runtime‐System erzeugt dann den eigentlichen Thread, welcher die run()‐Methode des Thread‐Objekts ausführt public class CounterThread extends Thread { private int max; public CounterThread (int max) { this.max = max; } public void run() { for (int i = 1; i <= max; i++) System.out.println(i); } } CounterThread thread = new CounterThread(100); thread.start(); ... thread.join(); Threads 107 Erzeugen eines Threads (Variante 2) ● Um ein Thread‐Objekt zu erzeugen, kann auch die Klasse Thread instanziiert und dem Konstruktor ein Objekt übergeben werden, welches das Interface Runnable implementiert public class CounterRunnable implements Runnable { private int max; public CounterRunnable(int max) { this.max = max; } public void run() { for (int i = 1; i <= max; i++) System.out.println(i); } } CounterRunnable runnable = new CounterRunnable(100); Thread thread = new Thread(runnable); thread.start(); ... thread.join(); Threads 108 Terminieren eines Threads ● ● Ein Thread terminiert, wenn er die run()‐Methode verlässt Um einen Thread vorzeitig zu beenden, kann mittels interrupt() ein Flag gesetzt werden, welches der Thread mittels interrupted() periodisch prüft public class CounterThread extends Thread { public void run() { int i = 1; while (!Thread.interrupted()) { System.out.println(i++); } } } CounterThread thread = new CounterThread(); thread.start(); ... thread.interrupt(); Threads 109 Synchronisieren von Methoden ● ● Um Zugriffskonflikte auf die Felder eines Objekts zu vermeiden, können die Methoden als synchronized markiert werden Für dasselbe Objekt kann dann immer nur ein Thread gleichzeitig eine der entsprechenden Methoden ausführen (exklusiver Ausschluss) public class Buffer { private Object[] elems = new Object[...]; private int size = 0; } Threads public synchronized void put(Object obj) { for (int i = size++; i > 0; i--) { elems[i] = elems[i - 1]; } elems[0] = obj; } public synchronized Object get() { return elems[--size]; } 110 Warten auf eine Bedingung ● ● Mittels wait() kann ein Thread innerhalb einer synchronisierten Methode auf eine Bedingung warten, bis diese von einem andern Thread mittels notify() im gleichen Objekt signalisiert wird Warten mehrere Threads, so können alle Threads mittels notifyAll() geweckt werden public class SynchronizedBuffer { ... public synchronized void put(Object obj) { ... notify(); } public synchronized Object get() throws InterruptedException { while (size == 0) wait(); return elems[--size]; } } Threads 111