Unterlagen: Praktische Informatik 2, Sommersemester 2005 Prof. Dr. Manfred Schmidt-Schauß Fachbereich Informatik Johann Wolfgang Goethe-Universität Postfach 11 19 32 D-60054 Frankfurt E-mail:[email protected] URL: http://www.ki.informatik.uni-frankfurt.de/ Tel: 069 / 798 28597 Fax: 069/ 798 28919 5. April 2005 Inhaltsverzeichnis 1 WWW, OO und Java 1.1 Internet + WWW . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.0.1 IP Version 6; neue Version der IP-Adressen . . . 1.1.0.2 Domain Name System (DNS) . . . . . . . . . . . 1.2 Objektorientierte Programmierung am Beispiel von Java . . . . . 1.2.1 Was hat Java mit dem WWW zu tun? . . . . . . . . . . . 1.2.2 Versionen und Kompatibilität mit Browsern . . . . . . . . 1.2.3 Objektorientierte Programmierung am Beispiel Java . . . . . . . . . . . . . . . . . . . . . . . 1.2.4 Objekte und Methoden . . . . . . . . . . . . . . . . . . . 1.2.4.1 Methoden . . . . . . . . . . . . . . . . . . . . . . 1.2.5 Nachrichten; Methodenaufruf . . . . . . . . . . . . . . . . 1.2.5.1 Art der Verarbeitung: Call by reference und value 1.2.6 Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.7 Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.8 Klassen und Vererbung . . . . . . . . . . . . . . . . . . . 1.2.9 Wesentliche Eigenschaften der objektorientierten Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.10 Wiederverwendbarkeit; Reusability . . . . . . . . . . . . . 1.2.11 Programmierhinweise . . . . . . . . . . . . . . . . . . . . 1.2.11.1 UML: Unified Modelling Language . . . . . . . . 1.2.12 Eigenschaften von Java . . . . . . . . . . . . . . . . . . . 1.2.12.1 Typische Phasen der Java-Entwicklung und Ausführung . . . . . . . . . . . . . . . . . . . . . 1.3 Programmierung in Java . . . . . . . . . . . . . . . . . . . . . . . 1.3.0.2 Primitive Datentypen . . . . . . . . . . . . . . . 1.3.1 Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3 Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.1 if . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.2 switch . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.3 while . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.4 do-while . . . . . . . . . . . . . . . . . . . . . . 1.3.3.5 for . . . . . . . . . . . . . . . . . . . . . . . . . 1 3 3 4 4 8 8 9 10 11 11 12 12 13 15 15 17 18 19 19 20 22 22 22 23 23 23 23 24 24 24 24 2 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.4 1.5 1.3.3.6 Markierungen . . . . . . . . . . . . . . . . . 1.3.3.7 Konstruktoren . . . . . . . . . . . . . . . . . 1.3.3.8 Felder . . . . . . . . . . . . . . . . . . . . . . 1.3.4 Kommentare . . . . . . . . . . . . . . . . . . . . . . . 1.3.5 Klassenkonversion, Typkonversion, cast . . . . . . . . 1.3.6 Ausnahmen, Exceptions . . . . . . . . . . . . . . . . . 1.3.7 Zugriffsrechte . . . . . . . . . . . . . . . . . . . . . . . 1.3.8 Modifikatoren . . . . . . . . . . . . . . . . . . . . . . . 1.3.9 Applets und Applikationen . . . . . . . . . . . . . . . 1.3.9.1 Methoden eines Applets . . . . . . . . . . . . 1.3.10 Schnittstellen . . . . . . . . . . . . . . . . . . . . . . . 1.3.11 Klassenbibliotheken in Java . . . . . . . . . . . . . . . 1.3.12 Mathematische Bibliothek . . . . . . . . . . . . . . . . 1.3.13 Collections, Vektoren, Listen . . . . . . . . . . . . . . 1.3.13.1 Iterator . . . . . . . . . . . . . . . . . . . . . 1.3.14 Nebenläufige Berechnung: Threads . . . . . . . . . . . 1.3.15 Beispiel eines Applets: hanoi . . . . . . . . . . . . . . Graphische Programmierung (GUIs: graphical user interfaces) 1.4.1 Programmierung, Ereignissteuerung . . . . . . . . . . 1.4.2 GUIs in Java (Ereignismodell 1.0 . . . . . . . . . . . . 1.4.3 Ereignissteuerung nach dem Java Ereignis-Modell 1.1 Kritikpunkte an der objektorientierten Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 25 25 26 26 26 27 27 31 32 35 35 36 36 37 37 39 42 43 44 50 51 Kapitel 1 WWW, OO und Java 1.1 Internet + WWW Das Internet ist ein weltweiter Verbund von elektronischen Geräten (Computer, Router, Gateways, Drucker,. . . ) (Knoten), die mit Standleitungen,Telefonleitungen, Funkverbindungen, . . . verbunden sind und über Internetprotokolle kommunizieren. Der Datenaustausch ist paketvermittelt: Daten werden in Pakete zerlegt, mit Absender und Empfängeradresse versehen, dann von Sender zum Empfänger übertragen und beim Empfänger wieder zusammengesetzt. Sogenannte Intranets sind lokale Netzwerke (auch globale firmeninterne), die (logisch) getrennt vom Internet arbeiten, aber evtl die gleichen Methoden verwenden. Etwas Geschichte: • ca. 1970: Ursprünge des Netzes sind Projekte des amerikanischen Verteidigungsministeriums zur Vernetzung von Rechnern und Ausfallsicherheit. • 1983: ARPANet, danach verschiedene andere Netze (NSFNET). TCP / IP wird als Standard verwendet. Starke Nutzung in Forschungseinrichtungen und Universitäten (E-Mail) • 1988 Internet Wurm. • 1991 / 1993 Mosaic, Gopher, WWW. Die Anfänge gehen wesentlich auf Konzepte zurück, die am CERN (Conseil Europèen pour la Recherche Nuclèaire) entwickelt wurden. • 1996: 3 Millionen europäische Knoten • 2002: ca. 90 Millionen Knoten. 3 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 4 Die Knotenadressen (IP-Adressen, bzgl IPv4) sind von der Form n.n.n.n, wobei n eine Zahl zwischen 1 und 255 sein kann. Z.B. ist 141.2.10.1 die Adresse des Rechners der KI. Es sind nicht alle kombinatorisch möglichen Adressen zulässig. Manche haben feste Bedeutung: 127.0.0.1. (localhost, bzw. loopback.device), ist immer ein Rückverweis auf den eigenen Rechner. Die IPAdresse setzt sich aus Netzwerk-Adresse und Rechneradresse zusammen. Es gibt drei Arten von Netzwerken, die sich nur durch die Größe des Netzwerks und der Adresse unterscheiden. 1) Netzwerkadresse ist ein Byte, aber nur zwischen 1 und 126. Die Rechner haben dann drei Byte zur Adressierung. 2) Netzwerkadresse sind zwei Byte, das erste Byte ist zwischen 128 und 191. 3. kleine Netzwerke, deren Adresse aus drei Bytes zusammengesetzt ist. Die Adressen 192.168.n.n werden als lokale IP-Nummern verwendet. Zum Teil werden diese Nummern dynamisch vergeben, um den knappen Raum der IP-Adressen besser zu nutzen. 1.1.0.1 IP Version 6; neue Version der IP-Adressen Addressen sind 128 Bit (16 Byte) statt 32 Bit (4 Byte), das sind 21 28, bzw. ca. 3 ∗ 103 8 mögliche Adressen. IPv6 Adressen schreibt man analog zu IP-Adressen, aber mit 8 mal 2-Byte, und den Block von 2 Byte als vierstellige Hexadezimalzahl, jeweils getrennt mit :“, wobei man einen Block mit Nullen weglassen darf. ” 1234:0:0:0:0:4711:AAFF:09FB wäre eine mögliche Adresse. Da man genut Platz hat, verwendet man die erste Hälfte als Routing Information, und die zweite Hälfte als Host-Adresse. Netzblöcke (Netblocks) kann man durch Netzwerk-Nummer/ Anzahl ” Adress-Bits des Netzes“angeben. 1.1.0.2 Domain Name System (DNS) Rechner (Knoten) haben i.a. eine textuelle Adresse hera.informatik.uni” frankfurt.de“, die von verschiedenen zentralen Rechnern (Namens-Server) in eine Knotenadresse umgesetzt werden können. Als Standard wird das letzte Kürzel dem Land bzw. einem anderen Bereich zugeordnet. ISO Ländercodes: Z.B. de = Deutschland, fr = Frankreich, at = Österreich, au = Australien , jp = Japan Andere Kürzel, die ursprünglich nur Knoten in den USA bezeichneten: Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 com org net edu mil gov eu ... 5 (commercial) Industrie, Firmen, usw. nicht kommerzielle Organisationen (DV-bezogen) (Netz bezogen) (Universitäten, Schulen, ... ) (Militär, USA) (Regierung, USA) (Europa) Weitere Die wesentlichen Aktivitäten (bzw. Dienste) des Internets sind: • Email elektronische Post (E-mail, verschiedene Protokolle). Versenden und Empfangen von elektronischen Texten/Nachrichten/ Information. Einseitig, asynchron. I.a. wird das Protokoll SMTP (simple mail transport protocol) verwendet. Dies funktioniert analog zum Prinzip der Briefpost: Briefe (mit Absender und Empfänger-Adresse und Daten) werden von einer Poststelle zum Verteiler gesendet, evtl. zwischengelagert, und landen im Briefkasten des Empfängers. Dieses Protokoll kann i.a. nur 7-Bit ASCII-Texte behandeln, d.h. das 8te Bit wird ignoriert, bzw. abgeschnitten. Um damit Binärdaten zu versenden. müssen diese erst umkodiert werden (Z.B. uuencode, uudecode). Ein anderes Protokoll zum Versenden von Daten ist MIME (Multipurpose Internet Mail Extension), das mehr Dateitypen kennt und deren Kodierung und Dekodierung übernimmt (Typen sind: text,message, application,image, audio, video; es gibt noch Untertypen). Ein Problem ist unerwünschte Post (Reklame, sogenannte spam). Die könnte im Extrem das ganze System zusammenbrechen lassen. Szenario: alle Firmen senden mit einem Klick ihre Reklame an viele (alle) Mailadressen. Heute gibt es viele Filter und Vermeidungsstrategien, die unerwünschte und/oder kommerzielle Reklamepost weitgehend ausschalten. • nntp News Elektronisches Analog zu schwarzen Brettern. Alle können es lesen, wenn sie zum schwarzen Brett gehen und davor stehen. Man kann auch selbst einen Zettel festmachen. Nach einer gewissen Zeit werden alte Nachrichten entfernt. Es gibt viele sogenannte Newsgruppen zu den verschiedensten Themen. Zu unmoderierten Newsgruppen kann jeder unkontrolliert etwas beitragen. Moderierte Newsgruppen zensieren die Zettel (Nachrichten) vorher. • ssh secure shell Hier wird die Kommunikation verschlüsselt durchgeführt. Insbesondere werden die Passwörter nicht unverschlüsselt übertragen. Allerdings ist bei der ersten Kontaktaufnahme die Vereinbarung eines Schlüssels notwendig, was ein Schwachpunkt sein kann. Dies ist im Gegensatz zu telnet, das alles unverschlüsselt überträgt. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 6 • File-Transfer (ftp = file transfer protocol) Zum direkten Versenden upload“ (put) und Holen download“ (get) von ” ” Daten/Dateien/Files vom eigenen Rechner zum entfernten Rechner. Die Verbindung ist synchron. Meist wird dies genutzt unter Verwendung der Kennung anonymous, die nur eingeschränkte Berechtigungen hat. • Einwählen in andere Rechner (Telnet, remote login) Dabei dient der eigene Rechner als Bildschirm, die Ausführung der Programme geschieht auf dem entfernten Rechner. Damit kann man z.B. seine elektronische Post auf dem Fachbereichsrechner von den USA her lesen: Zunächst mit telnet in Frankfurt sich anmelden, dann die Post lesen. • WWW (World Wide Web), (http = hyper text transport protocol) Es gibt eine Organisation als Verbund von Firmen/ Forschungsinstituten, die sich um Normierungen von Werkzeugen und Sprachen im Internet kümmert (siehe www. w3c.org). WWW ist ein virtuelles Netzwerk innerhalb des physikalischen Internets. Dies ist mittlerweile sehr populär geworden. Die Arbeitsweise kann man sich folgendermaßen vorstellen: Jeder Teilnehmer stellt auf seinem Rechner (bzw. seinem ihm zugeordneten Knoten) gewisse Daten (i.a. HTML (hyper text markup language)-Dokumente) zur Verfügung, die andere sich von außerhalb anschauen dürfen. Diese Daten sind i.a. als Einheiten strukturiert, die über Adressen ( Links) verbunden sind und eine eigene weltweit eindeutige Adresse haben (URL). Der Benutzer (die Surferin) holt sich die Daten, wobei i.a. ein sogenannter Browser (z.B. Netscape Communicator, Internet Explorer, Mozilla, Safari, . . . ) verwendet wird, der die Daten in Form von Seiten (graphisch gestaltet) anzeigt. Diese Seiten sind i.a. in HTML geschrieben mit evtl. eingebetteten weiteren Aufrufen, bzw. werden als HTML von einem Programm erzeugt. Eine Erweiterung, die aktuell diskutiert wird, ist XML. Um die graphische Gestaltung sehr flexibel zu gestalten, wurde Java (von Sun) entwickelt. Java ist eine Programmiersprache, bei deren Design die Prioritäten entsprechend gesetzt wurden: Portabilität, Sicherheit, Objektorientiert, in HTML leicht verwendbar. ( Wir werden Java noch behandeln. ) Jede Seite (Datenpaket) hat eine eindeutige Adresse und wird unter Angabe dieser Adresse geladen. Man kann das WWW als großes und offenes Hypertextsystem ansehen: Als System von vernetzten Dokumenten. Das Auffinden von Information bzw. Adressen mit interessanten Inhalten kann per Mund-zu-Mund“ Propaganda geschehen, oder man sammelt ” solche Adressen. Suchmaschinen (google, yahoo, ...), die allen zugänglich sind, versuchen Adressen zu filtern und bereit zu stellen, die bestimmte Suchkriterien erfüllen. Man kann sich fragen, woher die Popularität des WWW kommt. Dazu beigetragen hat: – Einfache Bedienbarkeit, Robustheit gegenüber Fehlern. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 7 – Analogie zu bekannten Medien wie Zeitungen, gelbe Seiten, – Kostenfreie Dienste Im Internet/WWW gibt es noch viele ungeklärte (juristische, finanzielle, soziale,...) Probleme: kriminelle Aktivitäten (Kinderpornographie), politisch extreme Agitation, Copyright-Probleme, Datenschutzaspekte, nicht einheitliche Gesetzgebung in verschiedenen Ländern, Verschlüsselung, ...) Weitere aktuelle Fragen: Wann werden Gebühren für welchen Dienst erhoben?, Reklame auf WWW-Seiten?, Besteuerung von WWWDienstleistungen?; Wer ist verantwortlich für die Inhalte? • (ntp: network time protocol) Zeitsynchronisation: Hiermit kann ein Rechner über Netz einen Zeit-Server befragen, wieviel Uhr es jetzt ist. Da man Laufzeiten (Netz, Funk, Programm) zu berücksichtigen hat, ist es nicht ganz einfach, hieraus mit genügender Genauigkeit die aktuelle Zeit zu errechnen. • Telekonferenzen; direkte Übertragung von Video-, Audio-Daten (Telefon) Dies war ursprünglich ein (fast) synchroner Austausch von Text: eine schriftliche Unterhaltung war möglich. Es gibt auch Software, die Telefonieren (Audiodaten) in Realzeit überträgt, sofern man die Übertragungskapazität hat (quality of service). • Bestellen, Bezahlen, finanzielle Transaktionen, Kreditkartenbezahlung Auch dies wird z.T. über Netzdienste abgewickelt. 8 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.2 1.2.1 Objektorientierte Programmierung am Beispiel von Java Was hat Java mit dem WWW zu tun? Java ist explizit dafür entworfen worden, um Anwendungen im WWW zu implementieren. Unterstützt wird: die Verwendung graphischer Benutzeroberflächen, WWW-Netzzugriffe; Verwendung in einem Browser; beweglicher Code: Applets, Portabilität. Wenn man nur statische Dokumente hat, dann kann ein Browser auch nur die statischen Hypertextdokumente sichtbar machen. Ein solches Dokument kann über viele Ordner und auch Rechner verteilt sein. Man kann mit diesen Mitteln auch blinkende Felder u.ä. einem Betrachter bieten. Clients Server Internet Eine Entlastung des Servers mittels Verlagerung von Berechnung auf den Client und eine Erhöhung der Flexibilität erhält man erst, wenn die betrachtete Seite auf dem Rechner des Betrachters (clients) die (fast) vollen Möglichkeiten einer Programmiersprache hat. Dies wird bei Java-Applets erreicht, indem die vom Server bereitgestellte Seite den Byte-Code eines Applets enthält, der vom Browser auf den lokalen Rechner geladen wird, und dort interpretiert wird. Dazu muss der Browser nur den entsprechenden Interpreter bereitstellen. Die Vorteile dieses Verfahrens sind: der Server muss nichts über die Art (Betriebssystem, Browser, usw.) des Clients wissen; geringe Belastung des Servers und des Netzes durch die Bytecode-Übertragung. Die Sicherheitsprobleme sind nicht zu vernachlässigen, da dies analog zur Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 9 Ausführung eines unbekannten, von irgendwoher geladenen Programms ist. Dies wird abgeblockt durch eingeschränkte Rechte eines Applets (kein Dateizugriff usw.), und durch eine Validierung des Bytecodes vor der Ausführung. Hier hat Microsoft einen eigenen Sicherheitsansatz entwickelt, der signierte Programme vorsieht Mit Java kann man auch eigenständige Anwendungen programmieren. Java ist auch kompilierbar, allerdings ist die Programmierung von Applets die wesentlichste Verwendung. Eine andere Variante HTML-Seiten flexibler zu gestalten, ist die Verwendung von z.B. Javascript. Bei dieser Technik wird ebenfalls nur ein Interpreter auf der Clientseite benötigt, der die entsprechenden Programme interpretiert. Allerdings wird hierbei das ganze Programm über das Internet von Server zum Client transportiert. Dialoganwendung Eine weitergehende Problematik ist die Erweiterung einer Web-Seite zu einer Dialog-Anwendung, die Daten bzw. Anfragen zwischen Server und Client austauscht. Hierzu kann man das sogenannte CGIs verwenden (common gateway interface). Z.B. war die Praktikumsanmeldung über das WWW mittels CGI und eines dahintersteckenden Haskell-Programms realisiert. Die Datenübertragung wird hier meist durch eine spezielle Codierung in der zurückgesendeten URL durchgeführt. Die Programmiersprache, die auf dem Server dahintersteckt, ist nicht festgelegt. Von Java wird diese Form der Anwendungen unterstützt durch die Programmiermöglichkeit von Servlets. 1.2.2 Versionen und Kompatibilität mit Browsern In der Entwicklung von Java sind bisher die Hauptversionen Java 1.0 (1995), Java 1.1 (1997), Java 2 (1999) und J2SE 5 (2004) erschienen. Dazu wurden von Sun Microsystems auch die entsprechenden Entwicklungsumgebungen veröffentlicht. • Bis einschließlich Version 1.1 trug die Entwicklungsumgebung den Namen JDK (“Java Development Kit”) • Von JDK-Version 1.2 bis 1.4 fand ein Namenswechsel statt, die Versionen heißen Java 2 und die entsprechenden Entwicklungsumgebungen Java SDK (“Software Development Kit”). • Die aktuelle Version ist im Grunde JDK 1.5, wobei die Namensgebung jedoch erneut geändert wurde: Die Entwicklungsumgebung heißt wieder JDK, wobei “J” für “Java 2 Platform Standard Edition” (J2SE) steht und die Version als JDK 5 (anstelle von 1.5) bezeichnet wird1 . Ab JDK 1.02 ist das Ereignismodell 1.02 integriert, wobei in diesem Modell sämtliche Ereignisse, die während des Programmablaufs stattfinden, durch 1 Sun schreibt in http://java.sun.com/j2se/1.5.0/docs/relnotes/version-5.0.html, dass die Versionsnummer 5 die Produktversion sei, 1.5 die Entwicklerversion. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 10 das Programm fließen und von einer Methode namens handleEvent() behandelt werden. Mit JDK 1.1 wurde das Ereignismodell grundlegend geändert. Das Ereignismodell 1.1 verwendet das Konzept der Event-Listener. Die verschiedenen Listener sind verantwortlich für die Behandlung einer bestimmten Art von Ereignissen (z.B. Mouse-Listener für Mausereignisse, Key-Listener für Tastaturereignisse, etc.). Durch das Programm werden nur jene Ereignisse geleitet, für die vorher im Quelltext Listener vorgesehen wurden. So lässt sich der Verarbeitungsvorgang effizienter gestalten, da nicht mehr alle Ereignisse geprüft werden, die stattfinden. Mit Version 1.2 (bzw. Java 2) wurden die JFC2 -Bibliotheken ins SDK integriert, die insbesondere das Swing-Paket enthalten, das die Möglichkeiten der Programmierung von graphischen Benutzeroberflächen erweitert. Außerdem wurde die Arbeitsumgebung mit Java 2 durch sogenannte Just-in-TimeCompiler verbessert, die die Übersetzung von Java-Programmen erheblich beschleunigen. Die Neuerungen in Version 1.5 werden später ausführlich behandelt. Während bis JDK 1.1. die meisten WWW-Browser Java-Anwendungen standardmäßig durch eine integrierte VM unterstützten, ist dies seit Java 2 nicht mehr der Fall. Heutige moderne Webbrowser (MS IE, Netscape, Mozilla, Firefox, Safari) können Java-Applets erst ausführen, nachdem ein Plug-In und eine virtuelle Maschine (z.B. JRE3 von SUN) installiert wurden. 1.2.3 Objektorientierte Programmierung am Beispiel Java Im folgenden werden die Beispiele konform zur Syntax der objektorientierten Programmiersprache Java (von Sun) notiert, die selbst eine an C++ angelehnte Syntax hat. Allerdings erfährt Java wie jede Programmiersprache Änderungen und Erweiterungen, so dass man für genauere Informationen das entsprechende Handbuch konsultieren sollte. Wir verwenden Java 1.4 und Java 2 für diesem ersten Teil des Skriptes und die Beispiele und gehen auf Java 5 in einem extra Kapitel ein. Die zentrale Idee der Objektorientierten Programmiersprachen (OOP) ist die des Objekts als Strukturierungskonzept. Objekte sind zusammengesetzte (Daten-)Einheiten, die auch als Einheit ansprechbar sind. Ein Objekt belegt Speicherplatz und hat eine Identität. Die Objekte kommunizieren miteinander durch Senden/Empfangen von Nachrichten. Objekte sind gekapselt. Veränderungen von Objekten werden stets mittels der definierten Methoden durchgeführt. Jedes Objekte gehört zu einer Klasse – ist Instanz einer Klasse. Klassen werden in der OO-Programmiersprache definiert. Klassen enthalten die Definition 2 JavaFoundationClasses 3 J2SE Runtime Environment Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 11 der Struktur der zugehörigen Objekte und auch die Definition der Methoden, die auf Objekte dieser Klasse anwendbar sind. Klassen (Typschablonen) können definiert werden. Sie entsprechen einer Menge von Objekten mit gleichem Verhalten. Eine Realisierung eines abstrakten Datentyps entspricht einer Klasse. Gleichzeitig wird der Klassenname als Typ zur Compilezeit verwendet. Klassen in Java kann man in einer (Baum)Hierarchie anordnen; in anderen Programmiersprachen kann es auch ein gerichteter Graph sein. Die Klassenhierarchie wird verwendet, um allgemeine Aspekte von Objekten in einer Oberklasse zu definieren und zu implementieren, und dann in Unterklassen die spezielleren Eigenschaften zu implementieren. Wenn wir mit Haskell vergleichen: Eine Klasse entspricht in etwa einem Typ bzw. einer Typklasse, ein Objekt entspricht einem konstruierten Datenobjekt. Das Senden von Nachrichten entspricht einem Funktionsaufruf. Allerdings gibt es in OO-Sprachen, auch in Java, Seiteneffekte, (im Gegensatz zu Haskell), z.B. kann ein Objekt verändert werden, auch wenn verschiedene Verweise darauf existieren (sogenanntes Aliasing). In Java sind elementare Datentypen wie Zahlen keine Objekte, allerdings gibt es OOP, in denen auch elementare Datentypen als Objekte behandelt werden. In Java kann man die elementaren Datentypen durch Einpacken ebenfalls zu Objekten machen, falls dies notwendig ist. 1.2.4 Objekte und Methoden Ein Objekt besteht aus Daten (bzw. Attributen). Von außen ist das Innere (die Daten) eines Objektes nicht direkt sichtbar. Nur über erlaubte Methodenaufrufe ist das Innere des Objektes (seine Attribute) sichtbar und änderbar. Jedes Objekt gehört zu einer Klasse: man sagt auch: ist (direkte) Instanz einer Klasse. Betrachtet man nur die Daten, ist ein Objekt analog zu einem Record, bei dem die einzelnen Attribute mit Attributnamen ansprechbar sind. Eine analoge Struktur ist ein Satz (Verbund) in einer Datenbank, bestehend aus Attributen. In Haskell wäre es ein n-Tupel, bestehend aus verschiedenen Daten, wobei auch durch geeignete Definition eines Typs die Komponenten wie Attribute angesprochen werden können. 1.2.4.1 Methoden Methoden sind auf Objekten ausführbare Operationen, analog zu Prozeduren. Ein Aufruf einer Methode wird oft als Schicken einer Nachricht gedeutet, wenn die Methode an ein Objekt gekoppelt ist, bzw. auf ein Objekt angewendet wird. Hierbei entsprechen die Nachrichten den Argumenten der Prozedur. Es gibt unterschiedliche spezifische Aufgaben von Methoden: Konstruktion: Erzeugen und Initialisieren eines Objekts Destruktion: Abschlussarbeiten und Löschen eines Objekts Selektion Lesen von internen Daten des Objekts Modifikation Ändern der internen Daten des Objekts Andere Verarbeitung allgemeine Prozedur Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 12 Beispiel 1.2.1 Ein Auto kann man als Klasse Auto modellieren mit den mögliche Attributen: Marke, Farbe, Kennzeichen, Besitzer, Baujahr, Leistung in kW, Höchstgeschwindigkeit, Treibstoffverbrauch, km-Stand. Methoden: • Erzeuge neues Objekt vom Typ Auto mit den folgenden Attributwerten: ... • Ändere den Besitzer • Ändere das Kennzeichen • Ermittle das Alter des Autos • ... 1.2.5 Nachrichten; Methodenaufruf Die Anwendung von Methoden auf Objekte wird durch den Mechanismus des Sendens von Nachrichten durchgeführt. Verwendet wird i.a. die sogenannte Dot-Notation. Syntaktisch: obj.meth(para1, para2,...). Hier wird dem Objekt obj die Nachricht meth gesendet, mit den Parametern para1, para2,...). Dies ist analog zur Anwendung einer Funktion f meth auf ein Argument, wobei das erste Argument der Funktion stets das Objekt ist, dem die Nachricht gesendet wird, und die weiteren Argumente den Parametern para1, para2,...) entsprechen. D.h. als Funktionsanwendung sieht obiger Beispielaufruf so aus: f meth(obj,para1,para2,...). Das Abfragen von Attributen wird ebenfalls durch Senden einer Nachricht durchgeführt, wobei der Attributname verwendet wird; der Wert des Attributes wird als Wert der Anwendung zurückgegeben. D.h. eine Methode kann einen Seiteneffekt bewirken, und auch einen Wert (per return) zurückgegeben. Der Seiteneffekt kann darin bestehen, in dem angesprochenen Objekt, oder eventuell in anderen Objekten die Werte von Attributen zu verändern. 1.2.5.1 Art der Verarbeitung: Call by reference und value Objekte werden per Referenz und Wert angesprochen. D.h. Argumente sind Werte oder Referenzen. Bei einfachen Datentypen werden diese als Wert weitergereicht, Objekte als Argumente in Methoden werden als Referenz weitergereicht. Zwei Objekte mit gleichem Inhalt können an verschiedenen Stellen im Speicher sich befinden, haben eine unterschiedliche Identität, können als unabhängig voneinander geändert werden. Nach der Änderung bleibt die Referenz die gleiche. Das hat starke Auswirkungen auf die Prüfung der Gleichheit von Datenstrukturen / Objekten: Normalerweise wird die Referenz genommen, d.h. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 13 verschiedene Objekte mit gleichem Inahlt sind verschieden, einen Vergleich bzgl des Inhalts muss man selbst als Vergleichsmethode definieren. Entsprechend gibt es dann auch die Aliasproblematik: unterschiedliche Variaben können auf das gleiche Objekt verweisen. Beispiel 1.2.2 Gegeben sei ein Rechteck durch die Koordinaten eines Eckpunktes und der Höhe und Breite. Wir nehmen an, dass es auf dem Bildschirm dargestellt wird, und die Koordinatenangaben in Pixeln erfolgt. Das Bewegen auf dem Bildschirm könnte man durchführen mittels folgender Sequenz von Anweisungen, die in einer Methode als Programmcode stehen könnte: if (obj.left < 20) { obj.erase(); obj.move(20,0); obj.paint(); } // // // Loeschen des Bildes Verschieben des Rechtecks (der Koordinaten) Zeichnen Die Nachrichten sind jeweils left, erase(), move(20,0), paint(). 1.2.6 Klassen Klassen fassen gleichartige Objekte zusammen, wobei die Gleichartigkeit sich auch daraus ergibt, ob diese von der Implementierung gleich behandelt werden sollen. Klassen verhalten sich zu Objekten wie der Begriff (die Klasse) Auto“ ” zu einem konkreten Auto mit dem KZ B-SE-1“. Das konkrete Auto ist dann ” eine Instanz“ (Ausprägung, Element) der Klasse. ” In OOP besteht das Programmieren aus dem Definieren von Klassen, d.h. Attribute, Methoden, Hierarchie. Die Methodenprogrammierung entspricht der normalen“ Programmierung. ” Klassen können hierarchisch angeordnet werden. D.h. es gibt Oberklassen und Unterklassen. Die hierarchische Anordnung soll dazu dienen, in der Oberklasse die gemeinsamen Methoden und Attribute zu definieren, die für alle Unterklassen gelten. (Statt Unterklassen sagt man auch abgeleitete Klasse). Beispiel 1.2.3 Die Klasse Fahrzeug kann man als Oberklasse von Auto definieren mit den Attributen Hoechstgeschwindigkeit, Besitzer. Als weitere Unterklassen von Fahrzeug kann man LKW, Boot, Bus, Motorrad definieren. Bei LKW kämen als Attribute hinzu: zul. Gesamtgewicht, Art der Güter, . . . . Bei einem Bus: maximale Anzahl Passagiere, Ausstattung, ... Fahrzeug rr9 O Le LLiTLTLTTTTT r r LLL TTTT r rr TTTT LL TT rrr ... Auto LKW Boot Als UML-Diagramm sieht die Struktur so aus: 14 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 Fahrzeug Hoechstgeschwindigkeit:double Auto LKW Boot Autokennzeichen:String Gesamtgewicht:double Tiefgang:double class Fahrzeug { public double Hoechstgeschwindigkeit; private String Eigentuemer; private static long Anzahl; ....... } class Auto extends Fahrzeug { public String Autokennzeichen; ...... } class Boot extends Fahrzeug { public double Tiefgang; ........ } Beispiel 1.2.4 Rechteck mit oberer Eck-Koordinate, Breite und Höhe: Unten der Programmcode, der in einem Applet zum Zeichnen verwendet wird: class Rechteck { int x,y,b,h; // x,y Koordinaten, b,h, Breite und Hoehe void paint (Graphics g) { //Methode zum Zeichnen g.drawRect (x, y, b, h); // Aufruf einer Bibliotheksfunktion drawRect } } Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.2.7 15 Methoden In einer Klasse werden sowohl die Attribute als auch die darauf möglichen Operationen definiert. Diese Operationen nennt man Methoden. Deren Definition ist innerhalb einer Klassendefinition. Beispiel 1.2.5 class Rechteck { int x,y,b,h; //x,y Koordinaten, b,h, Breite und Hoehe void paint (Graphics g) { // Methode zum Zeichnen g.drawRect (x, y, b, h); // Aufruf einer Bibliotheksfunktion drawRect } void move (int xd; int yd) { this.x = x + xd; this.y = y + yd; } } Die Methode move verändert die Eck-Koordinate des Rechtecks um die Differenz xd,yd. Wenn einem Rechteck r die Nachricht move gesendet wird in der Form r.move(20,0), dann wird die Anfangskoordinate des Rechtecks entsprechend der Argumente verändert. Die Verwendung von this bezieht sich immer auf das Objekt, an das die Nachricht gesendet wird. In anderen OOP-Sprachen wird dies z.T. auch als self bezeichnet. Sucht man die Analogie in Haskell, so würde man r.move(20,0) etwa als move r 20 0 schreiben. Das Schlüsselwort this entspricht dann der Variablen x in einer Definition von move x y z = .... Beachte, dass die Argumente der Nachrichten nicht nur elementare Datentypen sind, sondern auch Objekte sein können. Typen bzw. Klassenangaben müssen in Java für alle Methoden, deren Argumente und deren Ergebnis gemacht werden. void steht für den Ergebnistyp einer Methode, die Seiteneffekte, aber keinen Rückgabewert hat. void move (int xd; int yd) bedeutet, dass move keinen Ergebnistyp hat, nur einen Seiteneffekt bewirkt, und dass die Argumenttypen int sein müssen. 1.2.8 Klassen und Vererbung Klassen werden, wenn möglich und von der Implementierung her sinnvoll, hierarchisch strukturiert: Vererbungshierarchie. D.h. man versucht diese so zu strukturieren, dass man zunächst eine möglichst allgemeine Klasse definiert, z.B. geometrischesObjekt: 16 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 abstract class geometrischesObjekt { String Name; geometrischesObjekt (String Name) {this.Name = Name;} void paint (Graphics g); } Die Klasse Rechteck, Gerade, Kreis, Dreieck, usw kann man dann als Unterklassen definieren, die mehr Methoden und mehr Eigenschaften haben, also spezieller sind: class Rechteck extends geometrischesObjekt { int x,y,b,h; void paint (Graphics g) { g.drawRect (x, y, b, h); } void move (int xd; int yd) { this.x = x + xd; this.y = y + yd; } } // Beispielaufruf: // x,y Koordinaten, b,h, re.move(2,-3) Eine weitere Unterklasse von Rechteck könnte ein farbiges Rechteck oder ein mit einem Muster gefülltes Rechteck sein. class farbigesRechteck extends Rechteck { Color farbe; .... } Hierbei erben die Unterklassen die Attribute und Methoden der Oberklasse; man sagt auch die Attribute werden von der Oberklasse abgeleitet. Es ist möglich, neue hinzuzufügen. Die Unterklasse hat dann die Vereinigung der Attribute und Methoden der Oberklasse und der in der Klasse definierten entsprechenden Komponenten. Es ist auch möglich und manchmal notwendig, die Methoden der Oberklasse in der Unterklasse zu überschreiben. Allerdings nur für die Nutzung in dieser Unterklasse (und deren Unterklassen). Dies ist notwendig, da für spezialisiertere Klassen die Methoden ebenfalls weiter spezialisiert werden müssen. Z.B. Die Methode paint wird bei einem Rechteck nur die Linien des Rechtecks zeichnen, bei einem farbigen Rechteck die Linien und zusätzlich die Farbe. Es ist nicht möglich, Attribute oder Methoden zu entfernen. Zur Laufzeit kann man mit dem Operator instanceof feststellen, ob ein Objekt zu einer Klasse gehört: re instanceof Rechteck;. Oft definiert man eine Oberklasse als abstrakte Klasse mit dem Zugriffsrecht abstract, wobei man nur eine Zusammenfassung meint. Es ist dann nicht Breit Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 17 möglich, Objekte zu erzeugen, die genau zu dieser Klasse gehören. Dies geht nur für konkrete“ Unterklassen. ” 1.2.9 Wesentliche Eigenschaften der objektorientierten Programmierung Das Programm besteht aus einer Menge von Klassendefinitionen, die aus Erklärungen von Attributen und Definitionen von Methoden (und Schnittstellenbeschreibungen ) bestehen. Die Verwendung von Methoden geschieht wie eine Funktionsanwendung in Attributnotation. D.h. Klassen und Methoden sind das eigentliche Programm. Objekte gibt es nur zur Ausführungszeit des Programms. Diese werden innerhalb eines Methodenaufrufs zur Laufzeit (mittels new) erzeugt. Das Senden und Empfangen von Nachrichten ist ebenfalls eine Aktion zur Laufzeit. Vererbung Klassen erben von Superklassen Methoden und Attribute. Dies gilt damit auch für die Objekte: auf diese kann man alle Methoden einer Superklasse anwenden. Man kann den Methoden in Unterklassen eine andere (spezifischere) Implementierung geben. Polymorphie Variablen müssen getypt sein (der Typ ist die Klasse), d.h. Variablen können nur Objekte einer Klasse referenzieren. Allerdings sind dann auch Objekte erlaubt, die zu Unterklassen gehören. Welchen exakten Typ das Objekt hat, kann man oft erst zur Laufzeit ermitteln. Hat man z.B. eine Variable Fahrzeug fahrzeug, und Fahrzeug hat die Unterklassen Auto, Bus, Schiff, dann kann man das Attribut Eigentuemer ermitteln mittels: fahrzeug.Eigentuemer, bzw mit einer eigenen Methode getEigentuemer() und dem Aufruf fahrzeug.getEigentuemer(), Es wäre auch möglich, dass die Methode in der Unterklassendefinition überschrieben wird. In diesem Fall wird die spezifischere Methode verwendet. Damit kann man Programmteile allgemein formulieren. Falls Unterklassen zum Programm noch hinzugefügt werden, ist es nicht notwendig, die bereits verwendeten Methodenaufrufe zu ändern. Dynamische Bindung (late binding) Ist im Zusammenhang mit Overloading, d.h. im gleichen Namensraum gibt es für den gleichen Methodennamen verschiedene Implementierungen. Das ist die oben beschriebene Art des Methodenaufrufs, der jeweils die spezifischste Methode für ein Objekt nimmt. Die Entscheidung, welche Implementierung zu verwenden ist, untersucht die Anzahl der Argumente und die Typen bzw. Klassen der Objekte, die als Argumente verwendet werden. Die Unterscheidung nach der Anzahl der Argumente kann der Kompiler vornehmen (statisches Binden), während man nicht immer während der Kompilierung herausfinden kann, welche Klasse das Objekt hat (bzw. haben wird): Dynamisches Binden“. ” Man nennt diese Art der Auswahl der korrekten Implementierung einer Methode nach dem Typ der Argumente auch single dispatching bzw. multiple dispatching, wenn es mehrere Argumente sind. Sind Typumwandlungen Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 18 möglich (double → int, so kann es zu nicht gewollten Methodenaufrufen kommen (vermutlich abhängig von der Java-Version). Das Überladen nach dem Typ des Ergebnisses ist nicht möglich. Statische Bindung (early binding) Im Gegensatz dazu ist die statische Bindung, die (vom Compiler) verwendet werden kann, wenn der Methodenaufruf zur Compilierzeit eindeutig ermittelbar ist, (genauer: die Implementierung der Methode); Wie oben erwähnt nach der Anzahl der Argumente, aber auch wenn aufgrund der Typen der Parameter nur eine Methode möglich ist. Das gibt es in Java 1.5 bzw. Java 2 nicht: Mehrfachvererbung (von Attributen), D.h. eine Klasse kann nicht Unterklasse von zwei anderen unabhängigen Klassen sein. Dies vereinfacht die Kompilierung und macht die Vererbung eindeutiger. Allerdings sind Hierarchien in Anwendungsbereichen nicht immer baumförmig, so dass diese Beschränkung manchmal zu streng ist. Das Verbot der multiplen Vererbung kann Kopieren von Klassen und Methoden, oder eine etwas unnatürliche Klassenhierarchie erzwingen. In Java kann man zwar keine Attribute, dafür aber Methodennamen über Mehrfachvererbung weitergeben durch Verwendung von Interfaces (Schnittstellen). Pointer und Adressrechnung . generischen Klassen Dies sind Klassen, die selbst Typ-Parameter haben können. Damit kann man z.B. Listen von Zahlen als Klasse definieren. Diese Konstruktion wäre nur dann sinnvoll, wenn ein Typcheckalgorithmus in der OOP analog zum Milner- Typcheck verwendet würde. In Java kann man Listen von Objekten definieren, d.h. das sind eher heterogene Listen, wobei die Objekte in der Liste zu einer Unterklassen einer gemeinsamen Oberklasse gehören müssen. Generische Klassen gibt es in Java 5 (siehe extra Kapitel). 1.2.10 Wiederverwendbarkeit; Reusability Wiederverwendung von Programmcode in verschiedener Form ist nützlich und sinnvoll, da dadurch bereits implementierte und getestete Funktionalitäten umsonst verwendet werden können. Dies erfordert bei der ersten Strukturierung aber einen Mehraufwand, da man die Verwendung durch andere Projekte / Programmierer mitbedenken muss. Ebenso wird man dadurch angeregt, eine Abstraktion und Strukturierung vorzunehmen, die in jedem Fall zu einem besseren Verständnis des Problems und der Implementierung führt. Wiederverwendung kann direkt im eigenen Programm/Projekt sein, oder über eine Bibliothek geschehen. Generell sind einfachere Strukturen, Datentypen, leichter wiederverwendbar als komplziertere. Leider sind ähnliche Datenstrukturen / Verarbeitungen/ Methoden/ Programmm dann doch aus verschie- 19 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 denen Gründen nicht wiederverwendbar. In Java 5 wird die Wiederverwendbarkeit erleichtert durch parametrisierte Klassen. 1.2.11 Programmierhinweise Die Erstellung eines Programms in einer OOP erfordert zunächst, die Anwendung gedanklich so zu strukturieren, dass man die Klassenstruktur festlegen kann. Diese Klassenstruktur sollte natürlich den Bereich der Anwendung möglichst gut modellieren, da man nur dann erwarten kann, dass das Programm sich robust bei Veränderungen verhält. D.h. auch bei Erweiterungen sollte die Klassenstruktur nur wenig geändert werden müssen. Die Methoden und Attribute, die man in einem Programm benötigt, ergeben sich dann aus der benötigten Funktionalität. Die Superklassen haben weniger Attribute und Methoden, die in der Hierarchie weiter unten stehenden Klassen haben mehr Attribute und Methoden. 1.2.11.1 UML: Unified Modelling Language Die Analyse und Vorstrukturierung kann man zum Teil mit graphischen Werkzeugen vornehmen, die die Klassenstruktur, die Attribute, Methoden-Typen usw. graphisch darstellen, verwalten und verändern können. UML stellt zur Modellierung verschiedene Formen von Diagrammen bereit. Wir betrachten nur Teile der Möglichkeiten. Diese Werkzeuge erzeugen i.a. automatisch einen Programmrahmen, in den dann der spezifische Code noch eingetragen werden kann. Beispiele für Elemente der Diagramme und beispielhafte Diagrammstrukturen: • Klasse: Klassenname Attribute Methoden • Objekt: Objekt: Klasse Attribute = Wert interface Klasse Methode() • Schnittstelle Herbei kann man zB in der Klasse noch mit weiteren Markierungen kennzeichnen, ob die Attribute/ Methoden public oder private sind, auch die Konstruktormethode kann man angeben. Klassenname + Attribut1: Klasse33 − Attribut1 constructor Konstruktor123 + Methode1 (arg1:Double) − Methode2 () 20 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 Die mit + gekennzeichneten sind public, die mit − gekennzeichneten sind private. Beispiele für Diagrammstrukturen: • Vererbung: Oberklasse Unterklasse • Beziehung Klasse 1 Klasse 2 • Enthaltensein Teil Ganzes • Realisierung Klasse 1.2.12 Schnittstelle Eigenschaften von Java Automatische Speicherverwaltung Für neu zu erzeugende Objekte wird automatisch Speicher bereitgestellt. Objekte, auf die es keine Referenz mehr gibt, werden automatisch (irgendwann) freigegeben (garbage collection). Dies im Gegensatz zu z.B. C, C++, aber genauso wie in Haskell und Python. Typsicherheit Der Kompiler überprüft die Klassenangaben der Variablen auf Konsistenz mit den Methoden-Aufrufen und den cast-Aufrufen zur Typkonversion. API: Application Programming Interface Das ist die Menge an bereits vordefinierten Klassen, die Java im Compiler und in der Laufzeitumgebung bereitstellt (java.lang, java.util, java.awt. java.applet,.... Java (SDK bzw. alt: JDK) hat eine Vielzahl von (von Sun) mitgelieferten Bibliotheken, die die Programmierung erleichtern, aber auch für einige Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 21 Funktionalitäten notwendig sind. Applets kann man z.B. nur programmieren, wenn die entsprechenden Bibliotheken per import dazugeladen werden. Portabilität Es wird Wert darauf gelegt, dass die Ausführung der Programme auf allen Rechner und Rechnertypen gleichartig abläuft. Die nichtportablen Teile sind auf den jeweiligen Rechner installiert. In der Programmiersprache selbst erfordert das, dass es keine rechnerspezifischen Konstrukte in der Programmiersprache gibt: z.B. Adressrechnungen, oder Systemzugriffe, die Betriebssystem-abhängig sind, o. ä. Dies erfordert auch die Angabe eine operationalen Semantik, die unabhängig vom benutzten Rechner ist und die es ermöglicht, die Ergebnisse eines Programms eindeutig vorherzusagen. Die Verwendung der verschiedenen Windowsysteme und den Aktionen kann zu unterschiedlichem Verhalten/Aussehen führen. Bytecode Der Kompiler erzeugt kein lauffähiges Programm, sondern einen interpretierbaren String: den Bytecode“. Dieser wird dann von einem ent” sprechenden Interpreter ausgeführt. Dieser Bytecode ist portabel, d.h. auf allen Maschinen ablauffähig, vorausgesetzt, der entsprechende Interpreter (JVM, Java Virtual Machine) ist dort installiert. Der Interpreter ist i.a. nicht portabel. Zwar entsteht durch das Interpretieren ein Geschwindigkeitsnachteil gegenüber anderen Methoden, dies ist jedoch kein Nachteil für die Anwendungen, für die Java entwickelt wurde, nämlich solche mit hohem Anteil an Netzwerk-Kommunikation und graphischer Benutzerinteraktion. WWW Java ist u.a. konzipiert zur Programmierung von graphischen, interaktiven Programmstücken (sogenannten Applets), die von einem WebBrowser auf der Client-Seite geladen und ausgeführt werden können (der Bytecode des Applets). Man kann allerdings auch normale Programme (applications) damit schreiben, die dann von einem Interpreter ausgeführt werden können. C++ - Ähnlichkeit Die Syntax von Java ist der von C++ sehr ähnlich, auch wenn bei der Entwicklung von Java darauf geachtet wurde, solche Eigenschaften zu vermeiden, die immer wieder Probleme verursacht haben. So gibt es keine Operationen zum expliziten Referenzieren oder Dereferenzieren von Objekten oder Daten: primitive Daten werden immer dereferenziert verwendet und Objekte und Felder immer referenziert. D.h., die sogenannte Adressrechnung ist in Java verboten Sicherheit Das Fehlen von Referenzen und Zeigern, die vom Programm beliebig manipuliert werden können, sowie die Einschränkung der Zugriffsrechte für Applets, die über das Netzwerk kommen, tragen zur Sicherheit von Java bei. Ein weiterer Filter ist der Bytecode-Verifier, der den bermittelten Bytecode auf unerlaubte Aktionen hin überprüft. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 22 Browser; Versionen Da Applets vom Server zum Client gesendet werden und auf dessen Rechner bzw. in dessen Browser ablauffähig sein sollen, kann es dazu kommen, dass Applets zu modern“ sind, und auf der älteren Brow” serversion nicht richtig lauffähig sind. In diesem Falle kommt es i.a. nicht zu Fehlern, sondern es passiert einfach nichts, d.h. man sieht kein Applet. Hier gibt es teilweise unterschiedliches Verhalten in den verschiedenen Browsern wie Microsofts Internet Explorer, Netscape Communicator, usw. 1.2.12.1 Typische Phasen der Java-Entwicklung und Ausführung • • Editieren: Kompilieren: • • • Laden: Bytecode-Verifier: Ausführen: 1.3 1.3.0.2 des Programms, der Klassendefinitionen, ergibt Files mit der Extension .java. des Programms und der Klassendefinitionen. Erzeugt Bytecode: ergibt Files mit der Extension .class. Lädt die notwendigen Bytecode-Files mit der Endung .class überprüft den Bytecode daraufhin, ob er den geforderten Sicherheitsanforderungen e die JVM (Java Virtual Machine) führt den Bytecode aus Programmierung in Java Primitive Datentypen Die primitiven Datentypen von Java sind: boolean entweder true oder false byte Zahlen von −128 bis 127 (8 Bit) char 16-Bit (2 Byte) Unicode-Zeichen short Zahlen von −32768 bis 32767 (16 Bit) int Zahlen von −231 bis 231 − 1 (32 Bit) long Zahlen von −263 bis 263 − 1 (64 Bit) float Fließkommazahlen double Fließkommazahlen mit doppelter Genauigkeit Die Strings sind nicht primitiv, sondern eine vordefinierte Klasse. Es gibt verschiedene Methoden für Strings: + zum Zusammenhängen (append), length(), charAt(int), substring(anfangsindex, endindex), equals(String other). Die equalsMethode sollte man zum Testen der Gleichheit von Strings verwenden, da sonst Objektidentität geprüft wird. Umlaute und ihre Unicode-Darstellung: ä \u00E4 ö \u00F6 ü \u00FC ß \u00DF Ä \u00C4 Ö \u00D6 Ü \u00DC Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.3.1 23 Operatoren ∗, /, +, − % ++ -<, <=, >, >=, ==, ! = ! &, | &&, || ^ += op= arithmetische Infix Ops (z.T. auch für andere Typen) modulo-Rechnung 7%3 ergibt 1 addiere 1 , i++ (erst verwende i; addiere 1) ++i (addiere 1; verwende i ) subtrahiere 1 i-- (verwende i; subtrahiere 1) --i (subtrahiere 1; verwende i ) arithmetische Infix Vergleiche Negation striktes und, oder für Boolesch (werden auch für bitweise Operation verwendet) lazy und, oder exklusives oder x += y entspricht x = x + y (Allgemein:) Abkürzung: x op= a entspricht x = x op a Es gibt noch weitere Operationen, die analog zu Registeroperationen sind: bitweises und, oder, Negation, Shift, ... 1.3.2 Ausdrücke Ausdrücke sind, analog zu Haskell, Programmstücke, die bei Ausführung einen Wert haben. Diese können syntaktisch dort stehen, wo man auch einen entsprechenden Wert hinschreiben kann. Beispielsweise arithmetische Ausdrücke oder Boolesche Ausdrücke. Es ist aber auch möglich, Funktionen (bzw. Methoden) zu verwenden. Diese Methoden müssen dann mit einem entsprechenden Rückgabetyp definiert werden und mittels return den Wert zurückgeben. Der Auswahloperator: a ? b : c kann verwendet werden, um Fallunterscheidungen bei der Rückgabe von Werten leicht zu machen. Dies wirkt analog zum if. Der Auswahloperator erzeugt einen Ausdruck, keine Anweisungsfolge. Der Unterschied wirkt sich so aus, dass b,c in a ? b : c keine Anweisungen sein dürfen, sondern Ausdrücke sein müssen. (In Haskell gibt es diesen Unterschied nicht). 1.3.3 Anweisungen 1.3.3.1 if Syntax: if (h Ausdruck i) h Anweisung i else h Anweisung i Hinter if muss ein Boolescher Ausdruck stehen. Mehrere Anweisungen, mit { und } geklammert, zählen als eine Anweisung. Das else kann weggelassen werden. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 24 Beispiel 1.3.1 für Boolesche Ausdrücke: (a == b), (a != b), (a > b). Man kann auch zusammengesetzte Boolesche Ausdrücke verwenden: (a > 0 && a <= 10) (3*a +5 < 20 || b == c) 1.3.3.2 switch Syntax: switch (h Ausdruck i) { case h Konstante i: h Anweisungen i break ; ... case h Konstante i: h Anweisungen i break ; default: h Anweisungen i } Analog zu C: Der Ausdruck muss eine ganze Zahl sein, die Konstanten müssen zur Kompilierzeit bekannt sein. Die break’s sollten jeden Fall abschließen, können aber weggelassen werden. 1.3.3.3 while Syntax: while (h Ausdruck i) h Anweisung i Der Rumpf der Schleife wird ausgeführt, solange der Ausdruck den Wert true hat. Man kann mit der continue Anweisung ans Ende des Rumpfes springen. 1.3.3.4 do-while Syntax: do h Anweisung i while (h Ausdruck i) Der Rumpf der Schleife wird ausgeführt, bis der Ausdruck den Wert false hat. Der Rumpf wird auf jeden Fall einmal ausgeführt. 1.3.3.5 for Syntax: for (h Init i; h Bedingung i; h Ausdruck i) h Anweisung i Der Rumpf der for-Schleife wird ausgeführt, solange der Ausdruck den Wert true hat. Man kann mit der continue Anweisung ans Ende des Rumpfes springen. Beispiel 1.3.2 for (int i = 0; i <= 10; i++) <Anweisung>; Diese Schleife wird für i = 0, . . . 10 durchlaufen. Alternativ zum Ausdruck i++ kann man auch i = i+1 oder i += 1 schreiben. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.3.3.6 25 Markierungen Anweisungen kann man mit einer Marke (Label) versehen: marke1: while (i < 0) i--; Diese Markierung kann benutzt werden, wenn man geschachtelte Schleifen hat, und diese entweder mit break Marke1 beenden will, oder mit continue Marke1 ans Ende springen will. 1.3.3.7 Konstruktoren Die Konstruktion eines neuen Objektes mittels new erfordert bei der Klassendefinition eine spezielle Methode, den sogenannten Konstruktor, der den gleichen Namen wie die Klasse hat. Es kann verschiedene Konstruktoren geben, die Auswahl erfolgt analog zum Binden bei Methoden: abhängig von der Anzahl der Argumente und vom Typ der Argumente. Wenn Attribute sowohl in der Oberklasse als auch zusätzliche in der Unterklasse definiert sind, dann ist im Konstruktor der Zugriff auf den Konstruktor der Oberklasse mit super. möglich. 1.3.3.8 Felder Felder (arrays) sind Folgen von Objekten gleichen Typs, die mit Index angesprochen werden können. Diese können mehrdimensional (d.h. mit mehreren Indizes) definiert und benutzt werden. Wir betrachten zunächst eindimensionale Felder. Beispiel 1.3.3 int [] a; // a = new int [10] ; integer feld a ist erkl\"art // integer feld a ist jetzt ansprechbar // und hat 10 Eintraege: von 0 bis 9 for (int i = 0; i < a.length; i++) a [i] = i Man sieht, dass Felder vor Benutzung erst angelegt werden müssen und dass auf Felder die Methode length anwendbar ist, die die Länge zurückliefert. Ein einfaches Beispiel für ein zweidimensionales Feld und dessen direkte Initialisierung ist: String[][] matrix = { {"AA","AB","AC","AD"}, {"BA","BB","BC","BD"}, {"CA","CB","CC","CD"} }; Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.3.4 26 Kommentare Es gibt drei Arten von Kommentaren: • Kommentar bis Zeilenende // • Kommentare zwischen /* und */. Schachtelung ist jedoch nicht erlaubt. • Kommentare zwischen /** und */ Die letzeren können mit javadoc extrahiert und bearbeiten werden. Kommentare der letzten Art werden vom Programm javadoc aus dem Programmtext extrahiert, um zur Dokumentation der Klassen zu dienen. 1.3.5 Klassenkonversion, Typkonversion, cast Die Syntax des cast-Operators ist: (neue Klasse) Objekt Damit kann man beispielsweise schreiben: varfloat = (float) vardouble Die umgekehrte Zuweisung kann Java automatisch richtig umsetzen, da keine Information verlorengeht. vardouble = (float) varfloat ist erlaubt. Das gleiche kann man auch für Klassen verwenden: Wenn geoObj Oberklasse von Rechteck ist, dann ist die Zuweisung vargeoobj = varrechteck erlaubt, der Inhalt wird nicht verändert. Die Zuweisung varechteck = vargeoobj ist verboten, denn es könnte z.B. ein Kreisobjekt der Variablen vargeoobj zugeordnet sein. Erlaubt ist: var rechteck = (Rechteck) var geoobj . Hier kann es zu einer Ausnahme zur Laufzeit kommen, wenn das Objekt kein Rechteck, z.B. ein Kreis ist. Diese Typkonversion bei Klassen dient im wesentlichen dazu, den Typcheck zur Kompilezeit zu unterstützen und auch um die Methoden von Oberklassen verwendbar zu machen. Umgekehrt erlaubt es diese Typkonversion, Objekte verschiedener Klassen zusammenzufassen z.B. in einem Vector. 1.3.6 Ausnahmen, Exceptions Java hat eine Behandlung von Exceptions (Ausnahmen), wie Fehler bei externen Aufrufen, Division durch 0 usw. Die zugehörigen Operatoren (Schlüsselwörter), um mit Exceptions umzugehen sind try, catch, throw. Fehler, die normalerweise zum Abbruch führen, kann man Abfangen mit catch, nachdem man die Anweisung, die Fehler liefern könnte, mit try eingepackt hat. throw dient zum Erzeugen von programmierten Ausnahmen. Ein Abbruch wird erst dann erzeugt, wenn es eine Ausnahmebedingung gibt, für die keine Ausnahmebehandlung programmiert ist. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 27 Wenn eine Methode selbst Ausnahmen erzeugen soll, dann muss dies in der Definition der Schnittstelle angezeigt werden, und zwar mit throws Exception1, .... Die Verwendung von try und catch: Einem try-Block der Form try Ausdruck folgen catch-Blöcke: catch (Exception exception) Anweisung . 1.3.7 Zugriffsrechte Die wichtigen sind public, und private. Es gibt auch noch protected, das seltener verwendet wird. Z.B. .... class Rechteck extends geometrischesObjekt { private int x, y, b, h; public void move (int xd; int yd) { this.x = x + xd; this.y = y + yd; } • public bedeutet hier, dass die Methode move von außerhalb benutzt werden darf. d.h. die Nachricht move(10,10) kann an ein Objekt der Klasse Rechteck gesendet werden. • private bedeutet, dass die Attribute x,y,b,h nicht direkt geändert werden dürfen, und außerdem auch nicht abgefragt werden können. d.h. r.b ist nicht zulässig. • protected bedeutet, dass die Attribute nur von Methoden der Unterklassen geändert werden dürfen. • Wenn man die Zugriffsrechte weglässt, dann darf das Attribut von allen Methoden des Pakets geändert werden darf. 1.3.8 Modifikatoren • static: Ein als statisch gekennzeichnetes Attribut existiert in allen Objekten der ganzen Klasse nur einmal, man nennt diese auch KlassenAttribute. Für Attribute wirkt static so, als würde man dieses Attribut zu einer globalen Variable für diese Klasse erklären. Hier kann sich man z.B. die Anzahl aller Objekte merken, oder eine Liste aller Objekte mitführen. Allerdings kann das zum Verhindern der garbage collection führen. Durch die Anwesenheit in dieser Liste werden die Objekte dann referenziert. Klassen-Attribute kann man ansprechen, ohne dass ein Objekt erzeugt wurde, wobei der Aufruf als Klasse.attribut erfolgt. Statische Methoden kann man als Klassenmethoden“ ansehen, die man ” benutzen kann, ohne dass ein Objekt vorhanden ist, d.h. die Variable this Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 28 kann darin nicht verwendet werden. Die zugehörigen Methoden können benutzt werden, ohne dass ein Objekt existiert, indem man diese aufruft mit: Klassenname. Methodenname(params). Z.B. ist main in einer Java-Applikation stets eine Klassenmethode. Diese erfährt allerdings eine Sonderbehandlung, denn sie wird in einer Applikation automatisch aufgerufen, in einem Applet jedoch nicht. • final: Bewirkt, angewandt auf ein Attribut, dass dessen Wert als nicht überschreibbar angesehen wird. Damit kann Java einerseits feste Konstanten definieren, andererseits Oberklassen und Methoden, die nicht durch Unterklassen verändert (erweitert) werden dürfen. Beispiel 1.3.4 Dieses Beispiel zeigt einige Möglichkeiten, Klassen zu definieren und deren Instanzen zu erzeugen. Allerdings ist dies kein Applet, sondern ein eigenständiges Programm. // abgewandeltes Beispiel aus Hannover Skript class Fahrt { private double Geschwindigkeit; public String Fahrtrichtung; private Fahrzeug Benfahrzeug; public Fahrt (double Geschwindigkeit, String Fahrtrichtung, Fahrzeug Benfahrzeug) { this.Geschwindigkeit = Geschwindigkeit; this.Fahrtrichtung = Fahrtrichtung; this.Benfahrzeug = Benfahrzeug; } public double beschleunigt_5() { Geschwindigkeit = Geschwindigkeit +5; return Geschwindigkeit; } public double Geschwindigkeit () { return this.Geschwindigkeit ; } } class Fahrzeug { public double Hoechstgeschwindigkeit; private String Eigentuemer; Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 private static long Anzahl; static { Anzahl = 0; } public static long Anzahl() { return Anzahl; } // Konstruktur: public Fahrzeug() { Anzahl = Anzahl + 1; } public Fahrzeug(double Hoechstgeschwindigkeit, String Eigentuemer) { Anzahl = Anzahl +1; this.Eigentuemer = Eigentuemer; this.Hoechstgeschwindigkeit = Hoechstgeschwindigkeit; } public double Hoechstgeschwindigkeit () { return Hoechstgeschwindigkeit ; } public void loesche_Fahrzeug() { Anzahl = Anzahl - 1; } public String Eigentuemer () { return Eigentuemer; } } class Auto extends Fahrzeug { public String Autokennzeichen; 29 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 30 public Auto(double Hoechstgeschwindigkeit, String Eigentuemer, String kennzeichen) { super(Hoechstgeschwindigkeit, Eigentuemer); this.Hoechstgeschwindigkeit = Hoechstgeschwindigkeit; this.Autokennzeichen = kennzeichen; } } class Boot extends Fahrzeug { public double Tiefgang; public Boot(double Hoechstgeschwindigkeit, String Eigentuemer, double tiefgang) { super(Hoechstgeschwindigkeit, Eigentuemer); this.Hoechstgeschwindigkeit = Hoechstgeschwindigkeit; this.Tiefgang = tiefgang; } } class FahrzeugProg2 { public static void main(String[] args) { System.out.println("Anzahl Args: " + args.length); for (int i = 0; i < args.length; i=i+1) System.out.println(args[i] + " "); System.out.println(); System.out.println("Anzahl Fahrzeuge: " + Fahrzeug.Anzahl()); Auto MeinGolf = new Auto(180.0, "myself","B-SE-1"); System.out.println("Neues Auto, maxkm:" + MeinGolf.Hoechstgeschwindigkeit + " Besitzer:" + MeinGolf.Eigentuemer() + " AutoKZ:" + MeinGolf.Autokennzeichen) ; Boot boot = new Boot(20.0,"Gaby",2.5); System.out.println("Neues Boot, maxkm:" + boot.Hoechstgeschwindigkeit + " Besitzer:" + boot.Eigentuemer() + " Tiefgang:" + boot.Tiefgang ) ; System.out.println("Anzahl Fahrzeuge nach 2 X new: " + Fahrzeug.Anzahl()); Fahrt bootsfahrt = new Fahrt(12.0,"Norden",boot); System.out.println("Geschwindigkeit(boot): " + bootsfahrt.Geschwindigkeit()); Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 31 System.out.println("Richtung(boot): " + bootsfahrt.Fahrtrichtung); bootsfahrt.beschleunigt_5(); System.out.println("Geschwindigkeit(boot): " + bootsfahrt.Geschwindigkeit()); System.out.println("MaximalGeschwindigkeit (MeinGolf): " + MeinGolf.Hoechstgeschwindigkeit()); System.out.println("Eigent\u00FCmer (boot): " + boot.Eigentuemer()); bootsfahrt = null; boot.loesche_Fahrzeug(); boot = null; MeinGolf.loesche_Fahrzeug(); MeinGolf = null; System.out.println(); System.out.println("Anzahl Fahrzeuge nach L\u00F6schen: " + Fahrzeug.Anzahl()); } } 1.3.9 Applets und Applikationen In Java kann man zwei verschiedene Arten von lauffähigen Anwendungen schreiben: Applikationen Diese haben in der Hauptklasse eine Methode main: public static void main (String [] args) { ... die bei Aufruf der Klasse dann ausgeführt wird. Deren Argument ist standardmäßig ein Feld von Strings: Dies sind Text-Parameter, die beim Aufruf übergeben werden. Applets Dazu muss im Programmcode Import-Anweisungen: import java.applet.*; bzw. import java.swing.JApplet. Der Name der Datei und der definierten Klasse, die die ausgeführte Klasse des Applets ist, sollten übereinstimmen. Diese ausgezeichnete Klasse muss eine Unterklasse von Applet sein. d.h. die Klasse wird definiert Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 32 durch z.B. public class geoobjtest extends Applet { .... bzw. ... extends JApplet { .... Hierdurch sind automatisch Methoden definiert, die für diese Klasse überschrieben werden sollten: init(), start(), paint (Graphics g) (Bezieht sich auf Ereignismodell 1.0). Das Applet kann dann mit einem Applet-Viewer, bzw. mittels eines Browsers in einer HTML-Datei aufgerufen werden mit dem Schlüsselwort applet (man kann auch embed oder objectverwenden, siehe Java bzw. HTML-Dokumentation.) Bei Aufruf der Hauptklasse werden diese aufgerufen und erzeugen dann eine entsprechende Zeichnung bzw. interaktive graphische Anwendung. Wie oben schon erwähnt, hat ein Applet normalerweise Beschränkungen beim Zugriff auf Files der Festplatte des Klienten. 1.3.9.1 Methoden eines Applets public void init() public void start() Ein applet hat zumindest die Methoden public void paint(Graphics g) public void stop() public void destroy() Diese können bzw. sollten programmiert werden, d.h. in der Klasse überschrieben werden. Wenn der Browser das Applet aufruft, werden je nach Aktion und Zeitpunkt folgende Methoden des Applet aufgerufen: init() Einmal mal beim Laden des Applets. Typischerweise wird in dieser Methode intitialisert, falls notwendig. start() Wird vom Browser direkt nach der Methode initaufgerufen, aber auch, wenn das Applet-Fenster im Browser nach Besuch einer anderen Web-Seite wieder aktiviert wird. paint(Graphics g) Wird vom Browser aufgerufen, wenn das Fenster neu gezeichnet werden muss, z.B. nach Verdecken und Wiederaufdecken des Fensters, oder beim Vergrößern des Fensters. stop() Wird aufgerufen, wenn der Benutzer zu einer anderen Web-Seite wechselt. Kann internes Anhalten sein. destroy() Wird vom Browser aufgerufen, wenn das Applet-Fenster geschlossen wird. Beispiel 1.3.5 // ebene geometrische Objekte // erster File: Punkt.java public class Punkt { int x,y; Punkt (int x, int y) { this.x = x; this.y = y; } Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 33 } // zweiter File: geoobjtest.java import java.applet.*; import java.awt.*; public class geoobjtest extends Applet { GeoObjekt [] geos; Punkt p1,p2,p3,p4,m1; GeoObjekt lin; public void init () { p1 = new Punkt (10,10); p2 = new Punkt (50,50); p3 = new Punkt (100,10); p4 = new Punkt (80,120); lin = new Linie (p1,p4); m1 = new Punkt (50,70); geos = new GeoObjekt[3]; geos[0] = new Kreis (30, m1); geos[1] = lin; geos[2] = new Dreieck (p1,p2,p3); } public void paint (Graphics g) { for (int i = 0;i < geos.length;i++) geos[i].zeichne(g); } } abstract class GeoObjekt { public abstract void zeichne (Graphics g); public abstract boolean innen (Punkt p); } class Rechteck extends GeoObjekt { public Punkt lu; public int b,h; // x,y Koord , b,h, Breite,Hoehe Rechteck (Punkt p,int b, int h) { this.lu = p; this.b = b; this.h = h; } public void zeichne (Graphics g) { // Methode zum Zeichnen g.drawRect (lu.x, lu.y, b, h); // Aufruf Bibfu drawRect } public boolean innen (Punkt p) { return this.lu.x <= p.x && p.x <= this.lu.x+b && this.lu.y <= p.y && p.y <= this.lu.y+h; Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 34 } } class Kreis extends GeoObjekt { public int r; // Radius public Punkt m; // Mittelpunkts Kreis (int r, Punkt m) { this.m = m; this.r = r; } public void zeichne (Graphics g) { g.drawArc (m.x-r, m.y-r, 2*r, 2*r, 0, 360); // Aufruf der Bib fu drawArc } public boolean innen (Punkt p) { return (m.x-p.x)*(m.x-p.x) + (m.y-p.y)*(m.y-p.y) <= r*r; } } class Linie extends GeoObjekt { public Punkt a,b; Linie (Punkt a, Punkt b) { this.a = a; this.b = b; } public void zeichne (Graphics g) { g.drawLine (a.x, a.y, b.x, b.y); } public boolean innen (Punkt p) { Dreieck dr; dr = new Dreieck (p,this.a, this.b); return (p == this.a || p == this.b || dr.entartet() && (b.x-p.x)*(p.x-a.x) > 0 && (b.y-p.y)*(p.y-a.y) > 0); } } class Dreieck extends GeoObjekt { public Punkt p1,p2,p3; // Koordinaten der Ecken Dreieck (Punkt p1, Punkt p2, Punkt p3) { this.p1 = p1; this.p2 = p2; this.p3 = p3; } public void zeichne (Graphics g) { g.drawLine (p1.x, p1.y, p2.x, p2.y); g.drawLine (p2.x, p2.y, p3.x, p3.y); g.drawLine (p3.x, p3.y, p1.x, p1.y); 35 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 } public boolean entartet () { return (p3.x-p1.x)*(p2.y-p1.y) == (p2.x-p1.x)*(p3.y-p1.y); } public boolean innen (Punkt r) { return true; } } 1.3.10 Schnittstellen Die Möglichkeit der multiplen Vererbung auf der Methodenebene bieten sogenannte Interfaces“. Diese bindet man in die Definition der Klasse mit” tels extends .... implements interface1, interface2, ... ein. Damit kann man z.B. eine nebenläufige Berechnung erzeugen (bzw. einen eigenständigen Prozess erzeugen, sog. Thread), indem man die vordefinierte Schnittstelle Runnable verwendet. Diese Schnittstelle hat die Methode run(). Man kann auch Interfaces selbst definieren mittels public interface MeinInterface methode1 (...); } { Hierbei darf die Methode nicht implementiert werden. Nur die Namen werden vereinbart. Beispiel 1.3.6 Die Schnittstelle Comparable definiert eine totale Ordnungsbeziehung. ... implements Comparable { public int compareTo(Object other) { Fahrzeug anderesfz = (Fahrzeug)other; if (this.hoechstgeschindigkeit < anderesfz.hoechstgeschindigkeit) return -1; if (this.hoechstgeschindigkeit > anderesfz.hoechstgeschindigkeit) return 1; return 0 } Dies ist analog zu Haskells Typklasse Ord, die es erlaubt, Ausdrücke bestimmter Typen mit <, ≤, >, ≥ zu vergleichen. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.3.11 36 Klassenbibliotheken in Java Java strukturiert vordefinierte Mengen von Klassen in Pakete. Diese gehören zum application programming interface (API). Hier muss man sich jedoch, je nach Java-Version, die Dokumentation anschauen, um sich mit den jeweils vorhanden Klassenbibliotheken vertraut zu machen. Einige Beispiele für vorhandene Bibliotheken: java.lang Basispaket, das z.B. Zahlen implementiert. java.util Einige extra Basisfunktionalitäten: Hashtabellen, Datum, Random, Vector, enumeration (Liste) java. applet Erzeugt sogenannte Applets. Eine Anwendung, die ein Applet werden soll, muss als Unterklasse von applet definiert sein. java.awt Abstract window toolkit: java.swing Erweiterung von java.awt zur komfortableren Implementierung von GUIs. java.io Dient zur Verarbeitung (Lesen, Schreiben) von Files, Puffern, Pipes, Druckern, usw. java.net Verarbeitung von Netzzugriffen, Verbindungen, Lesen, Laden von Ressourcen (URLs). 1.3.12 Mathematische Bibliothek public final class Math Die Klasse Math kann keine Unterklassen haben, und somit kann keine Methode überschrieben werden. Alle Methoden und Variablen sind static. Die Ausnahmebehandlung wie Überlauf, Division durch 0, usw. werden auf verschiedenen Plattformen möglicherweise verschieden gehandhabt (d.h. nicht portabel). Es gibt folgende Konstante / Funktionen / Methoden: E,PI ceil(x), floor(x), rint(x), round(x) ganzzahliger Anteil, bzw. Rundungen cos(x), sin(x), tan(x) trigonometrische Funktionen acos(x), asin(x), atan(x) Umkehrfunktionen exp(x), log(x), sqrt(x), pow(x,y) Einige mathematische Funktionen max(x,y), min(x,y), abs(x) mathematische Funktionen random() Ergibt eine Zufallszahl zwischen 0.0 und 1.0 1.3.13 Collections, Vektoren, Listen Java hat mehrere vordefinierte Möglichkeiten, Listen, Mengen oder Multimengen von Objekten zu verwalten. Logisch gesehen kann man unterscheiden zwischen: Listen, Mengen, Multimengen, Assoziationslisten Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 37 Listen entspricht einer Liste: Es gibt eine Reihenfolge der Objekte und es ist erlaubt, dass Elemente mehrfach vorkommen. Mengen Es gibt keine Reihenfolge, Duplikate sind verboten. Multi-Mengen Es gibt keine Reihenfolge, aber Duplikate sind erlaubt. Assoziationslisten (map) Ist wie eine Funktion bzw. eine indizierte Datenbank. Es entspricht einer Liste von Paaren aus Schlüsseln und Daten, wobei die Schlüssel eindeutig sein müssen. Es gibt Methoden zum Einfügen, Löschen, Vergleichen, indiziertem Zugriff, . . . und auch zum Iterieren. Die Klasse Collection kann Objekte unterschiedlichen Typs enthalten. In diesem Fall muss man bei der Verarbeitung eine Fallunterscheidung über Typen machen. Einige Funktionen sind: boolean add(Object o) Hinzufügen void clear() Löschen boolean contains(Object o) boolean equals(Object o) Vergleich von collections boolean isEmpty() Iterator iterator() Die Klasse Vector kann verwendet werden wie eine Liste von Objekten. Da man aber keine elementaren Datentypen in Vektoren einfügen kann, benötigt man zur Verwendung von Vektoren von Zahlen (beispielsweise), eine Verpackung dieser Zahlen in ein Objekt. Dazu dienen sogenannten Wrapper-Klassen. Short,Byte, Integer, Long, Float, Double, Character, Boolean sind Wrapperklassen. Einpacken kann man Zahlen mittels Double x = new Double(3.14);, Auspacken mittels x.DoubleValue();. 1.3.13.1 Iterator Ein Iterator behandelt eine Menge von Objekten, die in einem Array, Liste, Collection usw abgelegt sind, die eine Folge bilden (analog zu einem File mit Sätzen). Man kann damit die Folge von vorne nach hinten abarbeiten. Das Interface Iterator hat die Methoden: boolean hasNext() Object next() Liefert das nächste Objekt. Als Seiteneffekt wird im Iterator eins weitergeschaltet void remove() Typische Verwendung in einem Programm: ... Iterator it = col.iterator(); while (it.hasNext()) ...println(it.next()); Wir werden sehen, dass Java 5 Iteratoren auch in for-Schleifen erlaubt. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.3.14 38 Nebenläufige Berechnung: Threads In Java kann man Unterprozesse anstoßen, sogenannte Threads (Berechnungsfäden), die logisch parallel arbeiten. Diese kann man definieren als Klasse, die Unterklasse von Thread ist, oder mittels der Schnittstelle Runnable. Die einzige Methode dieser Schnittstelle ist run(). Einige Methoden zu Threads sind: start() startet einen Thread, falls als Unterklasse von Thread definiert. Nach dem Start des Threads laufen zwei Programmteile parallel. Die Anweisungen des Threads werden ausgeführt. Ebenso wird mit der nächsten Anweisung, die dem start(); folgt, fortgefahren. sleep(int ms) Pause für die angegebene Zeit (in ms). join() Wartet auf die Beendigung eines (anderen) Threads. Die Angabe einer operationellen Semantik für Threads muss ein Konzept von unabhängig ablaufenden Evaluatoren (Prozessen) einführen. Die Semantik des Befehls sleep() beispielsweise kann nur dann deterministisch sein, wenn man festlegen würde, wieviel Ausführungsschritte jeder Befehl braucht, und wie die parallele Auswertung zeitlich genau ablaufen soll. So eine Festlegung wäre Unsinn, denn die parallelen Auswertungen sollen auch logisch unabhängig sein. Beispiel 1.3.7 Eine einfaches Beispiel, bei dem man die Aktionen der Threads im Konsolenfenster der JVM sehen kann ist: class Zaehlthread extends Thread { int thnr,pause; Zaehlthread(int thnr, int pause) { this.thnr = thnr; this.pause = pause; } public void run () { int n = 0; while (n < 100) { System.out.println("Thread: " + thnr + " : " + (n++)); try { sleep(pause); } catch(InterruptedException e) { System.out.println("Thread: " + thnr + " InterruptedException "); } } } } // Extra File: public class ThreadDemo1 { Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 39 public static void main(String[] args) { Thread t1 = new Zaehlthread(1,100); Thread t2 = new Zaehlthread(2,70); t1.start(); t2.start(); } } Die Ausgabe könnte so aussehen, wenn man die Klasse ThreadDemo1 als Applikation startet. Beachte, dass diese nicht ganz deterministisch ist. Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: Thread: ....... 1.3.15 2 1 2 1 2 1 2 2 1 2 2 1 2 1 2 1 1 2 1 2 1 2 1 2 2 1 2 2 1 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 0 0 1 1 2 2 3 4 3 5 6 4 7 5 8 6 7 9 8 10 9 11 10 12 13 11 14 15 12 Beispiel eines Applets: hanoi Im folgenden der Quell-Code einer Animation der Türme von Hanoi, abgewandelt aus der Literatur und etwas angepasst. Es ist ein Applet, das einen Thread Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 40 startet, damit es als Animation verwendbar ist. Die Methode init wird beim Start des Applets automatisch aufgerufen. Diese erzeugt einen neuen Thread, in dem die eigentliche Animation ablaufen kann. Die Methode startwird nach der Methode init ebenfalls automatisch aufgerufen uns startet diesen Thread. Die Methode Tow ist rekursiv und steuert den Ablauf. Die Methode paint wird aufgerufen, wenn man die Fenstergröße ver/ändert und das System das ganze Fenster des Applets neu zeichnen will. Die Methode getGraphics() erlaubt den Zugriff auf das Zeichenbrett als Objekt. Ruft man bei jeder Änderung die Methode paint bzw. repaint() auf, dann ergibt sich ein typisches Flackern, da stets alles neu gezeichnet wird. In diesem Programm wird nach einer Scheibenbewegung nur die Stelle, an der die Scheibe verschwindet, mit der Hintergrundfarbe überschrieben, und an der Stelle, an der die Scheibe auftauchen soll, die grüne Scheibe gezeichnet. Das System übernimmt dabei automatisch die neue Zeichnung und verändert die Anzeige auf dem Bildschirm. Dies kann asynchron zum Ablauf des Threads sein, bei schneller Scheibenbewegung fehlen manchmal Zwischenbilder. import java.applet.*; import java.awt.*; import javax.swing.*; class int int int OneTower { N; tos; t[]; public OneTower (int N) { this.N = N; tos = 0; t = new int [N]; } public void push (int i) { t[tos++] = i; } public int pop () { return t[--tos]; } // zeichnet einen Turm public void draw (Graphics g,int Y, int X, int width, int height) { drawItem (g, Y+1, X-1, N*2+1, Color.blue, width, height); for (int i = 0; i < tos; i++) { int j = 2*t[i]-1; if (j > 0) 41 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 drawItem (g, Y-i, X+N-((j+1)/2), j, Color.green, width, height); } } public void drawItem (Graphics g, int y, int x, int itemSize, Color c, int width, int height) { int deltax = width / (6*N+10); int deltay = height / (N+2); g.setColor (c); g.fillRoundRect (x*deltax, y*deltay,itemSize*deltax, deltay, 10,10); } } public class TowerOfHanoi extends JApplet Thread animator; int N; OneTower [] towers; { public void init () { N = 64; // N = 64; towers = new OneTower [3]; this.setBackground(Color.white); //this.setDoubleBuffered(true); for (int i = 0;i < 3; i++) towers [i] = new OneTower (N); for (int i = 0; i < N; i++) { towers[0].push (N-i); } animator = new Thread() { public void run() { Tow(0, 1, 2, N - 1, N - 1); } }; } public void start() { // System.out.println("Applet gestartet."); animator.start(); } void Tow (int from, int help, int to, int n, int nTowers) { if (n == 0) try { animator.sleep(20); // animator.sleep(1); Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 42 Graphics g = getGraphics(); int j1 = 2*towers[from].t[towers[from].tos-1]-1; towers[from].drawItem (g,N-towers[from].tos+1, 3+(N*2+3)*from+N-((j1+1)/2), j1/*2*towers[from].t[towers[from].tos]-1*/, Color.white, this.getSize().width, this.getSize().height); towers[to].push (towers[from].pop()); int j2 = 2*towers[to].t[towers[to].tos-1]-1; towers[to].drawItem (g, N-towers[to].tos+1, 3+(N*2+3)*to+N-((j2+1)/2), j2/*2*towers[from].t[towers[to].tos]-1*/, Color.green, this.getSize().width, this.getSize().height); } catch (InterruptedException e) { return; } else { Tow (from, to, help, n-1, nTowers); Tow (from, 0, to, 0, 0); Tow (help, from, to, n-1, nTowers); } } public void run () { Tow (0,1,2,N-1,N-1); } public synchronized void paint (Graphics g) { g.setColor(Color.white); g.clearRect(0,0,this.getSize().width, this.getSize().height); for (int i = 0; i < 3; i++) { towers[i]. draw(g,N,3+(N*2+3)*i, this.getSize().width, this.getSize().height); } } } Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 1.4 43 Graphische Programmierung (GUIs: graphical user interfaces) Da Java eine (relativ) einfache Möglichkeit bereitstellt, graphische Benutzeroberflächen zu programmieren, können wir kurz in die Programmierung und Handhabung von graphischen Benutzeroberflächen (in Applets) einführen. Da die Benutzung eines Computers heute die Verwendung einer graphischen Benutzeroberfläche fast zwangsläufig zur Folge hat, ist jeder schon mal Benutzer gewesen. Diese Oberflächen zeichnen sich durch die Präsentation von Fenstern aus, die verschiedenen Anwendungen zugeordnet sind. Fenster können aktiv oder inaktiv sein, und visuell im Hintergrund oder im Vordergrund sein. Ein weiteres Kennzeichen von GUIs ist die parallele Verwendung verschiedener Eingabekanäle: Eingabekanäle von Benutzerseite sind meist die Tastatur und eine Maus mit 1,2 oder 3 Tasten, manchmal noch weitere wie Touchscreen, Joystick, usw. Es gibt eine Vielzahl von graphischen Elementen, die verwendet werden können: Fenster mit Schiebeleisten, waagrechte/senkrechte Menüs, Radioknöpfe, Symbole zum Anklicken (Icons), Textfelder usw. Die Verbreitung von GUIs startete vor ca. 25 Jahren. Zu diesem Zeitpunkt war die Verwendung von zeichenbasierten Bildschirmen und Interaktion mit Kommandoeingabe in eine Kommandozeile vorherrschend. Auch jetzt gibt es diesen Modus der Interaktion noch als Basis, z.B. bei PC als DOS Kommandozeile, als Kommandozeile bei Unix, oder als interne Schnittstelle bei Großrechnern (Mainframes). Die große Akzeptanz der GUIs beim Benutzer kommt durch die einfache Bedienbarkeit, bei der man sich keine (textuellen) Kommandosequenzen und Syntax merken muss. Zudem kommt die graphische Oberfläche der menschlichen Wahrnehmung entgegen, die eher an Bildern orientiert ist. Möglich wurden die graphischen Oberflächen erst durch schnelle Rechner und größere Speicher, die eine schnelle Verarbeitung und Darstellung ermöglichten, heute ist diese Verarbeitung fast komplett in die Graphikkarte ausgelagert. Vorgänger waren Großrechner mit teilweise mehr als 1000 Bildschirme an einem Zentralrechner, die weitgehend passiv waren. In dieser Konstellation waren graphische Benutzeroberflächen so gut wie unmöglich, da die Rechenkapazität zur Steuerung einer graphischen Benutzeroberfläche nicht lokal war und von zu vielen Benutzern in Anspruch genommen werden konnte. Heutige Standardkonstellation ist Anschluss an ein Netzwerk (Internet) und lokale (Rechen-. und Speicher)- Kapazität zur Steuerung der graphische Benutzeroberfläche. 1.4.1 Programmierung, Ereignissteuerung Im Gegensatz zur Verwendung einer Kommandozeile, bei der ein entsprechendes Programm genau den Inhalt (einen String) der Kommandozeile sieht und entsprechend verarbeitet, muss eine programmierte graphische Benutzeroberfläche und entsprechend die Anwendungsprogramme mit einer Mehrzahl von Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 44 Interaktionen und auch Eingabemöglichkeiten umgehen können. Z.B. muss ein Textverarbeitungsprogramm unter einer GUI i.a. die Markierung eines Textstückes ermöglichen. Dazu notwendig sind: Das Programm muss wissen, was im Moment sichtbar ist, es muss herausfinden, wo die Maustaste gedrückt wurde, welche Maustaste, wohin die Maus gezogen wurde und wo die Maustaste wieder losgelassen wurde. Die interne Methode der Mitteilung an das zugehörige Programm geschieht über einen Strom von Ereignissen, die vom System, Betriebssystem oder dem GUI-System erzeugt werden und an das Benutzerprogramm gesendet werden. Dieser Strom ist nach der zeitlichen Reihenfolge des Auftretens der Ereignisse geordnet, wird aber im allgemeinen vom Betriebssystem und vom Window– Steuerung bereits gefiltert. Das Prinzip der Portabilität hier einzuhalten ist nicht ganz unproblematisch, da die verschiedenen GUIs und Systeme bezüglich der erzeugten Ereignisse nicht ganz identisch sind. In der aktuellen Version von Java-Swing ist die Plattformunabhängigkeit vollständig gewährleistet. 1.4.2 GUIs in Java (Ereignismodell 1.0 Im Folgenden geben wir die Klassen. Methoden und das Verhalten im JavaEreignismodell 1.0 an. In Swing gibt es die meisten Klassen und Methoden noch, habe aber einen neuen Namen, z.B. JLabel statt Label. Normalerweise ist die graphische Anwendung ein Applet, das durch eine Klasse definiert wird, die zur Unterklasse von applet erklärt wird. Das Bild, das der Benutzer sieht, stellen wir uns vor als einen flachen, graphischen und farbigen Bildschirm, der aus ca. 1000 X 1000 Bildpunkten, sogenannten Pixeln besteht. Dies ergibt in natürlicher Weise ein Cartesisches Koordinatensystem, das jeden Punkt in der Form (x,y) ansprechbar macht. Normalerweise gibt es einen Zeiger (Cursor), das ist eine aktuelle Position der Tastatur, und eine Mausposition, die sich innerhalb der Fläche befinden. Es gibt eine reichliche Anzahl vordefinierter Fenstertypen und graphischer Elemente: Oberste Klasse ist Component. Unterklassen sind: Button Ein Knopf zum Anklicken. Canvas Ein Zeichenfläche. Checkbox Ein Text und ein Kästchen zum Ankreuzen. Choice Ein Auswahlmenü mit Kurztexten. Container abstrakte Klasse, mit der hierarchisch ein Fenster in Unterfenster gegliedert werden kann. Die Unterklassen sind: Panel Fenster ohne irgendwelche Rahmenobjekte. Window mit Unterklassen Frame, Dialog 45 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 Label Textanzeigefeld List Senkrechte, anklickbare Menüleiste. Scrollbar Schiebeleiste Textcomponent Textanzeige Applet ist Unterklasse von Panel, was nur bedeutet, dass deren Methoden einem Applet sofort zur Verfügung stehen. Zur Klasse Component gibt es eine Menge von Methoden, die verschiedene Aspekte handhaben: Einige Beispiele seien hier aufgeführt. Layout Die Art der Verteilung von Subkomponenten kann gesteuert werden. Je nach Layout Spezifikation mittels der entsprechenden Klasse: Borderlayout North, South, Center, East, West GridLayout(...) Komponenten sind matrixartig angeordnet FlowLayout Komponenten sind untereinander angeordnet. CardLayout Komponenten sind wie in einem Kartenstapel hintereinander angeordnet. Die aktuelle Größe und Position kann ermittelt und verändert werden. bounds(), inside(), location(), move(), size(), ... Farbe des Hintergrunds, setForeground(), . . . . Vordergrunds: getBackground(), Ereignisse Ermitteln und Handhabung der durch den Benutzer verursachten Ereignisse action(), gotFocus(), keyDown(), mouseDown(), mouseEnter(), mouseUp(), handleEvent(), postEvent(), .... Zeichnen innerhalb der Komponente, Neuzeichnen des Bildes. paint(), repaint(), update() Status Feststellen, ob Komponente aktiv, usw. Bei Definition einer Unterklasse können bzw. müssen bestimmte Methoden in diesen Klasse überschrieben werden. Bemerkungen Die Klasse Container hat die Methode add, mit der man neue Komponenten hinzufügen kann. Diese werden normalerweise vom zugehörigen Layout-Manager an bestimmte, vordefinierte Positionen plaziert: Bei BoderLayout sind möglich: North, West, South, East, Center, null. Die Ereignisse, die vom Benutzer und System generiert werden, werden in der Objekthierarchie von unten nach oben durchgereicht, bis es eine Methode handle event gibt, die diese einfängt. Diese Methode kann das Ereignis auch abweisen (bzw. weiterreichen, indem false zurückgegeben wird. Programmierhinweise: Generell gilt: Man muss das an der Oberfläche sichtbare als Modell möglichst vollständig in Datenstrukturen ablegen, damit das Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 46 sichtbare Bild rekonstruierbar ist und damit der Benutzer und das Programm die gleiche Sicht der Dinge haben. Ist die zu programmierende Anwendung komplex, so ist die Handhabung und das Verarbeiten aller möglichen Ereignisfolgen eine ebenfalls komplexe und schwierige Aufgabe, da man mit allem rechnen muss und auf jedes relevante Ereignis eine sinnvolle Aktion gestartet werden soll. Um eine komplexere (gute, professionelle) Anwendung (Applet) in Java zu schreiben, ist ein aktuelles Handbuch notwendig. Denn man muss eine Unzahl von Klassen, die Hierarchie, deren Methoden, die richtige Verwendung und auch die zu behandelnden Ausnahmen kennen. Eine Einarbeitung und Orientierung wird meist erleichtert durch vorgegebene Beispiele im SDK (bzw. JDK oder auch aus anderen Quellen). Eine Anwendung sollte anwenderfreundlich sein und einfach und zuverlässig wirken und dies auch sein. Das erfordert oft etwas Nachdenken und gutes Strukturieren. Zum Beispiel ist im nachfolgenden Zeichenprogramm extra Aufwand getrieben worden, um während des Zeichnens die aktuelle Strecke(Ellipse) sichtbar zu machen. Analog verhält es sich mit Fehlermeldungen. Um diese richtig zuzuordnen und zu formulieren, sollte man sich in die Rolle eines Benutzer versetzen, der keine Ahnung von der internen Verarbeitung hat, und der nicht abgeschreckt werden soll. Beispiel 1.4.1 (Ereignismodell 1.0) Dieses Beispiel ist ein Applet, das eine Zeichenfläche realisiert, auf der man Strecken und Ellipsen zeichnen kann. Es gibt einen Schalter, der mit choice realisiert ist, mit dem man von Strecke auf Ellipse umschalten kann. Während des Zeichenvorgangs wird das jeweils aktuelle Objekt gezeichnet, bis es durch Loslassen der Maustaste bestätigt wird. // // veraenderte Version eines JDK Beispiels (alte Version der Ereignissteuerung) import java.awt.*; import java.applet.*; import java.util.Vector; public class DrawTestRechteck extends Applet { public void init() { setLayout(new BorderLayout()); DrawPanel dp = new DrawPanel(); add("Center", dp); add("South", new SKWahl(dp)); dp.init(); } Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 public boolean handleEvent(Event e) { switch (e.id) { case Event.WINDOW_DESTROY: System.exit(0); return true; default: return false; } } public static void main(String args[]) { Frame f = new Frame("DrawTest"); System.out.println("Main laeuft"); DrawTestRechteck drawTest = new DrawTestRechteck(); drawTest.init(); drawTest.start(); f.add("Center", drawTest); f.resize(300, 300); f.show(); } } class DrawPanel extends Panel { Vector lines = new Vector(); Vector streckeOderKreis = new Vector(); int x1,y1; boolean ziehen; int strecke; // 0 = strecke, 1 = kreis, 2 = Rechteck int x2,y2; public void init() { ziehen = false; strecke = 0; } public DrawPanel() { setBackground(Color.white); } public void setStrecke() { strecke = 0; } public void setRechteck() { strecke = 2; } 47 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 48 public void setKreis() { strecke = 1; } public boolean handleEvent(Event e) { switch (e.id) { case Event.MOUSE_DOWN: x1 = x2 = e.x; y1 = y2 = e.y; ziehen = true; return true; case Event.MOUSE_UP: lines.addElement(new Rectangle(x1, y1, e.x, e.y)); // leichter Missbrauch von rectangle streckeOderKreis.addElement(new Rectangle(strecke,0,0,0)); ziehen = false; repaint(); return true; case Event.MOUSE_DRAG: x2 = e.x; y2 = e.y; repaint(); return true; case Event.WINDOW_DESTROY: System.exit(0); return true; default: return false; } } public void paint(Graphics g) { int np = lines.size(); int streckei; /* Zeichne alle Strecken und Ellipsen */ for (int i=0; i < np; i++) { Rectangle streckeip = (Rectangle)streckeOderKreis.elementAt(i); streckei = streckeip.x; Rectangle p = (Rectangle)lines.elementAt(i); if (streckei == 0) g.drawLine(p.x, p.y, p.width, p.height); else { if (streckei == 1) { if (p.height > p.y) Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 g.drawOval(p.x, p.y, p.width-p.x, p.height-p.y); else g.drawOval(p.x, p.height, p.width-p.x, p.y-p.height); } else if (streckei == 2) { if (p.height > p.y) g.drawRect(p.x, p.y, p.width-p.x, p.height-p.y); else g.drawRect(p.x, p.height, p.width-p.x, p.y-p.height); } } } if (ziehen) { if (strecke == 0) {g.drawLine(x1, y1, x2, y2);} else { if (strecke == 1) { if (y2 > y1) g.drawOval(x1,y1,x2-x1,y2-y1); else g.drawOval(x1,y2,x2-x1,y1-y2); } else { if (y2 > y1) g.drawRect(x1, y1, x2-x1, y2-y1); else g.drawRect(x1, y2, x2-x1, y1-y2); } } } } } class SKWahl extends Panel { DrawPanel target; public SKWahl(DrawPanel target) { this.target = target; setLayout(new FlowLayout()); Choice SoderK = new Choice(); SoderK.addItem("Strecke"); SoderK.addItem("Ellipse"); SoderK.addItem("Rechteck"); 49 Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 50 SoderK.setBackground(Color.lightGray); add(SoderK); } public void paint(Graphics g) { Rectangle r = bounds(); g.setColor(Color.lightGray); g.draw3DRect(0, 0, r.width, r.height, false); } public boolean action(Event e, Object arg) { if (e.target instanceof Choice) { String choice = (String)arg; if (choice.equals("Strecke")) { target.setStrecke(); } else if (choice.equals("Ellipse")) { target.setKreis(); } else if (choice.equals("Rechteck")) { target.setRechteck(); } } return true; } } 1.4.3 Ereignissteuerung nach dem Java Ereignis-Modell 1.1 Die neuere Methode der Ereignisverarbeitung hat die Modellvorstellung der Ereignisse und somit zwangsläufig auch die Bibliotheken, das API, und und die Programmierung verändert. Die Modelle 1.0 und 1.1 sind leider nicht ganz kompatibel, so dass Programme von Hand umgestellt werden müssen. Die Strukturierung im Ereignis-Modell 1.1 funktioniert zeitlich so: 1. Ein Ereignis tritt bei einem Objekt auf (z.B. Button, Menü, . . . ). 2. Dieses Ereignis wird in ein Ereignisobjekt verpackt und an einen Beobachter gesendet (Listener). 3. Dieser Beobachter (Methode) enthält die Programmierung, was nach Eintreten dieses Ereignisses zu verfahren ist. Die Programmierung erfolgt so, dass an bestimmte Quell-Objekte einen Listener eingetragen wird, der selbst zu definieren ist. Statt handleEvent gibt es für jedes Ereignis eine eigene Methode. Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 51 Das neue Modell erlaubt auch andere Programmierprinzipien wie innere Klassen, um die Beobachter (Listener) geschickter programmieren zu können. (Siehe hierzu Java Handbücher). Vorteile des neuen Modells sind optimierte Verteilung der Ereignisse. Im Modell 1.0 wurden (werden) die Ereignisse relativ ungezielt an die Anwendungsprogramme weitergegeben (Gießkannenprinzip), während im neuen Modell diese Verteilung programmgesteuert ist. Nachteil des neuen Modells ist die Verwirrung durch die zeitweise Koexistenz zweier Standards, wobei der neue Standard in Browsern nur abhängig von der Version und den richtigen Plugins funktioniert (siehe Paragraph 1.2.2). Beispiel 1.4.2 // Beispiel aus Java2-Handbuch import java.awt.*; import java.awt.event.*; import java.applet.*; public class KlickKlack extends Applet { Button klickButton = new Button("KlickKlack"); boolean geklickt = true; Color klick = new Color(255,255,0); Color klack = new Color(0,0,255); public void init() { add(klickButton); klickButton.addMouseListener( new MouseAdapter () { public void mouseClicked(MouseEvent e) { Graphics g = getGraphics(); if (geklickt) g.setColor(klick); else g.setColor(klack); g.fillOval(0,0,200,200); // das original war g.fillOval(0,0,getWidth,getHeight); geklickt = !geklickt; } }); } } 1.5 Kritikpunkte an der objektorientierten Programmierung (siehe Artikel von M. Broy, Informatik Spektrum, Februar 2002) Einige Kritikpunkte: Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 52 • OO-Sprachen kennen keine Komposition von Klassen“ ” Allgemeiner: Es gibt keine Operatoren, die Klassen zu neuen Klassen verbinden. Es gibt nur die Vererbungsbeziehung zwischen Klassen. Man kann manche Kompositions-Konzepte mittels multipler Vererbung implementieren. • Das OO-Ausführungsmodell ist sequentiell“ ” Hierbei gibt es die Problematik der Seiteneffekte. Es ist schwierig und komplex, aufgrund von lokalen Eigenschaften der Programme (Methoden) deren Wirkung zu beschreiben. Parallelisierung der Ausführung wird durch Sequentialität stark behindert. z.B. kann man in a.methode1; b.methode2 die Methoden nicht parallel auswerten, auch wenn a,b, und die beiden Methoden verschieden sind, da es darauf ankommen könnte, dass die Effekte in einer bestimmten Reihenfolge durchgeführt werden. Es gibt allerdings Programmteile, z.B. Threads, die direkt durch die Art der Programmierung im Prinzip parallel ausführbar sind. • Die Objektorientierung sagt uns nicht, wie wir das Verhalten von Schnitt” stellen definieren sollen“ Mit Schnittstellen sind hier nicht Interfaces gemeint. Sagen wir mal Methodenaufruf. Hier eine operationale Semantik anzugeben erfordert i.a., den ganzen Zustandsraum (d.h. alle Objekte und deren innere Zustände) des aktuellen Programmlaufs zu berücksichtigen. Dies ist nicht modular und auch nicht lokal. • Vererbung verletzt das Geheimnisprinzip“ ” Nur wenn ausdrücklich programmiert, gibt es unsichtbare Attribute. • Letzlich gibt es nur eine einzige, syntaktische Annahme: (die Unterklasse) ” hat mindestens dieselben Attribute und Methoden wie die Oberklasse“ Da ist was dran: da es keine Verhaltensbeschreibung der Methoden gibt, und man überschreiben darf, weiss man a priori nichts außer dass die Methoden vorhanden sind, aber kann ihr Verhalten nicht in der Oberklasse irgendwie festlegen. In Haskell kann man zumindest den Typ festlegen. In Java auch, aber der Typ ist nicht so aussagekräftig wie in Haskell. • (sinngemäß:) neue Projekte werden sich i.a. eine eigene Klassenhierarchie ” schaffen, nicht auf eine bereits vorhandene Klassenhierarchie aufbauen“. Der einfache Grund ist: das bereits vorhandene System funktioniert, aber das neue müsste Änderungen in der bestehenden Hierarchie, den Attributen, Methoden durchführen. Diese bewirkt aber möglicherweise Fehler im vorhandenen System, muss also sehr vorsichtig gemacht werden, und ist somit viel aufwendiger als das Kopieren und Modifizieren, d.h. Neuerstellen der Klassen für die Zwecke des neuen Projekts. • Multiple Dispatching“ fehlt. ” Das bezog sich auf Java1 1.4. In Java 5 gibt es durch Argumenttypen Praktische Informatik 2, SS 2005, Kapitel 1, vom 9. Maerz 2005 53 überladene Klassen, somit ist diese Kritik nicht mehr stichhaltig. • statische Analyse der Fernwirkung von Methodenaufrufen ist so gut wie ” unmöglich“ Wenn man versucht, statisch (d.h. aufgrund des Programmtextes) vorherzusagen, welche Auswirkung x.methode(..) auf andere Objekte hat, dann stellt man fest, dass dies 1. aufgrund der Seiteneffekte i.a. nur durch vollständiges formales Testen möglich ist. 2. wieder einen Methodenaufruf auf das gleiche Objekt x erfordert könnte. Dies ist analog zur Rekursion. Hier kann es im Extremfall bedeuten, dass man nicht davon ausgehen kann, dass Methodenaufrufe auf Objekte immer auf Objekte in einem konsistenten Zustand passieren, sondern dass es auch unerwartete Zwischenzustände geben kann.