Einführung in die Programmierung mit Java (2. Semester IT + AI) Prof. Dr. Herbert Neuendorf (DH Mosbach) [email protected] (voraussichtliche) Inhalte : z 1.Teil : Grundlegende Konzepte der Java-Programmierung z Grundlagen Java: technische Grundlagen, JVM, Bytecode, .... Typkonzept, primitive Datentypen, Casts, Zuweisungen, Operatoren, ..... Der grundlegende Aufbau von Java-Programmen Kontrollstrukturen: Verzweigungen + Schleifen Methoden, Rekursion Blockkonzept, Namensraum, Sichtbarkeit … möglichst zügig wegen C … © H.Neuendorf (1) z Fortsetzung Inhalte: Klassen + Objekte Sinn der Objektorientierung Klasse, Objekt, Konstruktoren Programmieren + Verwenden von Klassen - generelle Konzepte Speziellere Konzepte : Klassenvariablen + Klassenmethoden (statische Elemente) Factories Statische Initialisierung Referenzen - Bedeutung der Objektvariable: Zeigerkonzept Kein Unterlaufen der Kapselung durch Referenzen Immutable Objects Primitive Datentypen + Referenzen : Call by Value + Call by Reference Überladen von Methodennamen ………. z Arrays – mit primitive Datentypen und Objekten z Zeichenketten (Strings) z Hüllklassen © H.Neuendorf (nur Einführung) (2) z 2.Teil : Objektorientierung z z Fortsetzung Inhalte : Vererbung - grundlegende Konzepte Vererbung von Attributen + Methoden Objekterzeugung und Konstruktoren in Vererbungshierarchien Polymorphie - Typkompatibilität zwischen Ober- und Unterklassen Upcast + Downcast, statischer + dynamischer Typ, späte Bindung Abstrakte Klassen + Methoden Finale Klassen + Methoden Interfaces Annotations Pakete + Sichtbarkeiten von Klassen + Methoden © H.Neuendorf (3) Einige Klassen aus Standardpaketen z Enums z Ausnahmebehandlung / Exceptions z Clonen von Objekten z Innere Klassen z Reflection (4) Fortsetzung Inhalte : z 3.Teil : Datenstrukturen z Dynamische Datenstrukturen: Verkettete Listen + Bäume Aufbau + typische Operationen Stack + Queue als verkettet Liste Je nach verfügbarer Zeit, evtl. eher in Übungen eingebaut … 3. Semester (Prof. Deck) Ereignisorientierte Programmierung mit AWT + Swing Threading Fortsetzung Persistenz Netzwerk-Prg, RMI, Sockets Datenströme Collection Framework Spezielle Java-Klassen .............................. © H.Neuendorf (5) Literatur Java + Einführung in Programmierung : Deck, Neuendorf: Java-Grundkurs für Wirtschaftsinformatiker (Vieweg) ⊗ H. Mössenböck: Sprechen Sie Java (dpunkt) D.Ratz, J.Scheffler, D.Seese: Grundkurs Programmieren in Java (Hanser) Vorlesungsfolien (pdf) → 2.Auflage www.dhbw-mosbach.de/index.php?id=1653 Umfassendere Überblicke über Java : ... (1000-Seiten Bücher - nicht erforderlich) ... Kein Skript kann die Lektüre eines guten Buches ersetzen !! Ressourcen : java.sun.com Java Homepage von Sun Microsystems Dort JDK: Popular Downloads → Java SE java.sun.com/javase/6/docs/ JavaSE 6 Doku www.java.de Java User Group Deutschland www.eclipse.org Eclipse-Page mit allen Downloads © H.Neuendorf (6) Voraussetzungen : keine ... aber ... Erwartungen - Interesse, Mitarbeit, Kritik(!) während(!) der Vorlesung - Immer fragen, wenn Unklarheiten auftreten !! - Feedback, ob Tempo zu hoch oder niedrig - Übungen möglichst eigenständig durchführen - Skript nicht nur abheften, sondern zur Nachbereitung verwenden ! - Anregungen zur Verbesserung der Veranstaltung ! Ablauf ….. Vorlesung: Blöcke zu 4-5 Stunden Praktische Übungen: integriert Software: SUN JDK 6 Eclipse IDE 3.5 Benotetes Programmierprojekt © H.Neuendorf (7) Historie der Programmiersprachen Maschinensprache Assembler C++ B. Stroustrup Java James Gosling (Sun) C# Gründe für Java : von C++ inspiriert, jedoch "bereinigt" ..... Vermeiden der Komplexität von C++ : hybrider Sprachumfang, echte Pointer, Speicherverwaltung ... Java : klar objektorientiert, überschaubarer Sprachumfang, Garbage Collection ......... © H.Neuendorf (8) Java Historie : Beginn: "OAK"- Projekt von SUN Projekt anfänglich orientiert an Consumer Electronics 1990: Beginn der eigentlichen Entwicklung von Java durch J. Gosling & Bill Joy 1993: WWW 1995: JDK 1.0 von Sun + Browser HotJava (Applets!) von allen gängigen Browsern unterstützt (Netscape, IE) Dez. 98: JDK 1.2 - deutliche Erweiterung (Java 2) [aktuell JDK 6] Integrierte Entwicklungsumgebungen : JBuilder (Borland) Weiterentwicklungen: NetBeans (Sun) Eclipse ..... JFC (Java Foundation Classes ), SOAP, XML, neue Sprachfeatures, ....... JDK 1.0 ≈ 210 Klassen JDK 1.1 ≈ 650 Klassen JDK 1.2 ≈ 1250 Klassen ..... Java-Plattformunabhängigkeit ⇒ Konkurrent Microsoft ⇒ net-Offensive C# Common Language Runtime Java im Vergleich zu C++ : Weniger komplex, klarer, beherrschbarer, deckt 90% aller Fälle ab ⇒ " Langsamer für die Maschine, aber schneller für den Menschen " (= Entwickler) © H.Neuendorf (9) Java = Sprache + Plattform Programmiersprache : Plattform : "100%" objektorientiert – "alles" in Klassen Fülle von Klassenbibliotheken (Paketen) : Einheitliche Schnittstellen für alle unterstützen Betriebssysteme → Aufrufe durch Laufzeitumgebung für reale Plattform umgesetzt Klare Syntax ohne Altlasten Vermeiden komplexer Sprachmittel von C++ (Mehrfachvererbung, Operatoren-Überladen, ...) Speicherverwaltung nicht programmiert, sondern durch Garbage Collector geleistet Infrastruktur für Internet-Anwendungen Umgebung, die Unterschiede der realen Betriebssysteme + Hardware "verbirgt" (kapselt) = Virtuelle Maschine : Für Entwickler stellt sich Java wie eigenständige Plattform dar ⇒ Plattformunabhängigkeit ! Java-Anwendung Editionen : Klassenbibliotheken 1. Standard Edition für Fat Clients (Java SE) 2. Enterprise Edition für App-Server (Java EE) 3. Micro Edition für Kleingeräte (Java ME) Virtuelle Maschine VM Betriebssystem (Smartphones, ....) Hardware © H.Neuendorf Wo "lebt" Java ? → Java-Laufzeitumgebungen (10) Varianten : Standalone-Applikationen : 1. Client JR für Applikationen Normale Programme - von Java-VM-Interpreter ausgeführt 2. Webbrowser mit Applets 3. Webserver mit Servlet Container Applets : 4. Application Server mit EJB-Container Spezielle Klassen, die in Browser mit integrierter JVM ablaufen - besondere Sicherheitsrestriktionen ! Alle nutzen Java-Klassenbibliotheken und werden in Java-VM ausgeführt. Servlets + JSPs : Unterschiede bestehen in Anwendungs-Struktur und Umgebungen, in denen VM arbeitet. Java-Klassen, die auf Webserver dynamisch HTML generieren Enterprise Java Beans (EJB) : Umgebung: JDK / Java SDK Services, die ein Enterprise Application Server bereitstellt - u.a. transaktionale Dienste www.java.sun.com SUN: Java Development Kit (1.6 = 6) Laufzeitsystem + Entwicklungstools JRE: Java Runtime Environment = nur JVM-Laufzeitbestandteile © H.Neuendorf VM: Java läuft überall .... !! Vom Handy bis zum Server ........ ⇒ durch Java als Sprache hat man Zugriff in fast allen möglichen Plattformen und Ebenen ....... (11) Varianten der Java-Laufzeitumgebung Java-Client Browser-Client Java-Anwendung Java-Applet Klassenbibliotheken Virtuelle Maschine VM Betriebssystem Hardware Java-VM in Browser integriert Klassenbibliotheken Virtuelle Maschine VM Webbrowser Betriebssystem Hardware 1. Frontend-Bereich: Fat Client, Browser, lokaler Rechner = ursprünglicher Anwendungsbereich von Java © H.Neuendorf (12) Varianten der Java-Laufzeitumgebung Webserver Java-App-Server Java-Servlet Enterprise Java Bean Klassenbibliotheken Bibl. + Dienste Virtuelle Maschine VM Virtuelle Maschine VM Servlet-Container EJB-Container Webserver 2. Backend-Bereich : Betriebssystem Betriebssystem Java auf dem Server Hardware Hardware + 3. Middleware-Bereich : Kommunikation von Objekten in verteilten Systemen Frontend-Backend / Backend - DB / … : Netzwerk, RMI, JDBC, Message Queues, WebServices, ..... © H.Neuendorf (13) Java-Laufzeitumgebung Konzeption von Java → Write once - run everywhere Java-Client Java-Anwendung Klassenbibliotheken Plattformunabhängigkeit ! Keine Annahmen über Zielhardware oder Betriebssystem ! VM: Zwischenschicht, die BS und Hardware kapselt "Prozessor", der in Software realisiert wurde Virtuelle Maschine a) als Maschine definiert, die eigene Maschinenbefehle interpretiert (aus Sicht des Java-Programms) Betriebssystem b) setzt Anweisungen in plattformspezifische Anweisungen um (aus Sicht der Plattform) Hardware ⇒ VM muss für jede Plattform, auf der Java-Programme laufen sollen, speziell implementiert werden Konsequenz : In Java geschriebenes Programm → wird von Java-Compiler in Code für die VM kompiliert = Bytecode → kann von jeder Java-VM ausgeführt = interpretiert werden ! → läuft auf jeder Plattform (BS, Hardware) für die VM zur Vfg steht ! ⇒ Kein erneutes Anpassen, Kompilieren, Linken, .... © H.Neuendorf (14) Java-Laufzeitumgebung Kompilierte Programmiersprache C-Compiler für Win Maschinencode für Win Windows Quellcode(s) C-Compiler für Linux Maschinencode für Linux Linux VM-Infrastruktur Java C/C++: Write once - compile everywhere Java: Write once - run everywhere Java Compiler Bytecode Windows maschinen- JVM unabhängig Java Quellcode Nachteile : a) keine direkten Hardware-Zugriffe ⇒ zB keine Treiber in Java programmierbar b) keine harten Echtzeitanforderungen erfüllbar - denn : Garbage Collection unterbricht Ausführung in unregelmäßigen Abständen © H.Neuendorf Linux (15) Interpretierte Sprachen - Bytecode kompilierte Sprachen Nicht dasselbe - inbesondere bei Performanz ! Interpreter : Ausführung ohne Optimierung → Durchläuft Programmtext, übersetzt + führt jede Zeile einzeln aus ⇒ Langsam ! Bytecode-Architektur : Zweiteilung der Programmbearbeitung (Compiler + Interpreter) → 1. Schritt : Parsen + Analysieren des Programmtextes ( + Finden von Fehlern! ) Übersetzen in optimierte VM-Instruktionen = Bytecode → 2. Schritt : Ausführen der im Bytecode enthaltenen VM-Instruktionen ⇒ "semi-kompilierte" Sprache : Viel schneller als reine Interpreter Langsamer als reine Compiler (C / C++ ) Java Java Compiler Bytecode Windows maschinenunabhängig JVM Interpreter Java Quellcode VM = zusätzliche Schicht zwischen Programm und Ausführungsplattform © H.Neuendorf ⇒ Kostet Zeit ! Linux (16) Bytecode = Maschinensprache der JVM Sequenz Opcodes (Maschinenbefehle) für JVM Analyse mit JDK-Tool javap Opcodes haben Nummer + Namen Optionen : javap –help ↵ Ausgabe Opcodes : zB: Opcode 96 = iadd = Addiere zwei Integer javap –c –private nameClassFile ↵ Beispiel : Java-Anweisung: int y = x + 5 ; JVM-Befehlsfolge: iload_1 ; lege Integerwert aus Variable 1 auf den Stack bipush 5 ; lege den konstanten Wert 5 auf den Stack iadd ; addiere die beiden Integer-Zahlen istore_2 ; speichere das Ergebnis in der Variablen 2 Werte der Parameter werden in Bytecode übernommen : In Bytecode : 21 1 16 5 iload 1 bipush 5 96 54 2 iadd istore 2 Java Java Compiler Bytecode Windows maschinenunabhängig JVM Interpreter Java Quellcode JVM Spezifikation : > 150 Instruction Opcodes Linux © H.Neuendorf (17) JVM-Anatomie Dynamischer Class Loader Garbage Collector + Class Files (Heap) Class File Verifier + Standard Java Klassen Byte Code Verifier Execution Engine < Native Methods Besonderer Aspekt : Sicherheit Native Method Linker Security Manager Java Runtime System Bsp: Appletcode Class File Verifier Byte Code Verifier Security Manager © H.Neuendorf Betriebssystem (18) Java-Virtual Machine Execution Engine : - "Herz" der JVM = virtueller Prozessor der virtuellen Maschine - Führt Bytecode-Instruktionen der JVM-Spezifikation aus Class Loader : - Lokalisieren + Laden + Verifizieren + Initialisieren + Finalisieren der für Programmausführung benötigten Klassen ( .class-Files = Bytecode ) - dynamisch: erst wenn Klasse benötigt, wird sie in JVM geladen Garbage Collector : - Objekte werden von Programmen im Hauptspeicher angelegt (new) - Speicher-Freigabe nicht durch Programm (kein delete) - sondern durch periodisch laufende Speicherverwaltung der JVM Native Method Linker : - Aufruf von Routinen, die als plattformspezifischer Maschinencode vorliegen - nicht in Java / Bytecode, sondern Kompilat anderer Sprache Security Manager : (DLLs, libs) - Java-Laufzeitsystem definiert Sicherheitsrichtlinien - werden bei Programmausführung vom Security Manager durchgesetzt : - Überwachung von Java-Programmen = Durchsetzen von Restriktionen ( zB: Zugriff auf lokales Filesystem, Konnektieren an Fremdrechner, Übertragen von Benutzerdaten, Abbrechen der JVM .......... ) © H.Neuendorf (19) Weiterentwicklung der Java VM : Java Hot Spot VM Java Bytecode Java Interpreter Java Bytecode JDK 1.4 (2002) Java Hot Spot VM von Sun in zwei Versionen ausgeliefert : JIT Compiler Just In Time Compilation Server VM und Client VM /jre/bin/client/ … /jre/bin/server/ … mit unterschiedlich optimiertem Start- und Ausführungsverhalten JVM Runtime JVM Runtime Schlechte Performance Gute Performance Bytecode wiederholt auszuführender Programmteile (hot spots) wird zur Laufzeit vor Ausführung einmal in Maschinensprache (Prozessorbefehle) übersetzt : Java Hot Spot VM © H.Neuendorf (20) Warum Java ? “Java: A simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, and dynamic language ... ” einfach: (Sun: The JavaLanguage – White Paper) Keine Zeigerarithmetik, Mehrfachvererbung, komplizierte C++ Konzepte objektorientiert: Klassenenkonzept, Vererbung, Interfaces, ….. Kleiner Sprachumfang + umfangreiche Klassenbibliotheken verteilt: Unterstützt netzbasierte Anwendungen interpretativ: Bytecode-Ansatz verbindet Vorteile interpretativer + kompilierter Sprachen robust: Fehlerquellen aus C++ vermieden, Garbage Collection, ... sicher: Bytecode Verifier, Security Manager, Code-Zertifizierung architektur- (plattform-) unabhängig: Bytecode + APIs abstrahieren von Hardware portierbar: JVM interpretiert identischen Bytecode auf allen unterstützten Plattformen leistungsstark: Langsamer als C++, aber meist ausreichend ! Thread-unterstützend: Sprachelemente + Klassen zur Synchronisation paralleler Threads dynamisch: Dynamisches Laden + Initialisieren benötigter Klassen zur Laufzeit © H.Neuendorf Programmerstelluung - kompilierte Sprachen (Java ist anders !) (21) Erfassen des Programms : Coding in Quellcodedatei Ausführen des Programms : Maschinensprache in Binärdatei Übersetzen + Ausführen : Vorteil → hohe Geschwindigkeit + Compiler checkt Programm vor(!) Ausführung auf Fehlerfreiheit Objektmodul Compiler Quellcode Fehlermeldungen Library Library Manager (Bsp: C++) Linker Executable Eingabedaten Editor BS Ausgabedaten ⇒ Optimale Übersetzung zur Ausführung für spezielles BS, Prozessorfamilie ⇒ Ausführung auf verschiedenen Plattformen (Win, Linux) erfordert Neu-Compilieren ! Aber : Nicht ideal für Anforderungen heterogener Umgebungen (Internet !) Gefordert ist eher "Write & compile once, run everywhere“ .......... © H.Neuendorf (22) Übersetzen + Interpretieren in Java Übersetzungseinheit in Java ist die Klasse JDK-Compiler ist *.java Quellcode Editor Compiler Klassen Paket Fehlermeldungen javac Bytecode *.class Bytecode von Klassen Vorteil : Plattformunabhängigkeit Bytecode Eingabedaten Bytecode deutlich effizienter interpretiert als reiner Programmcode Interpreter Java-VM Systemaufrufe Maschinensprache Prüfungen bei Compilierung + bei Ausführung durch JVM ⇒ Vorteile Interpreter + Compiler kombiniert Nachteil : Ausgabedaten BS Hardware Benötigte Klassendateien einzeln von JVM einzulesen = zeitaufwendig Compiler erzeugt plattformunabhg. Bytecode ⇒ durch Java-Laufzeitsystem interpretiert Laufzeitsystem = Umgebung unter deren Kontrolle ein Programm ausgeführt wird Java-Laufzeit = Java Virtual Machine : Kapselt Besonderheiten der Plattform Vollzieht Ausführung des Bytecodes auf spezieller Plattform Java-VMs für relevante Plattformen zur Verfügung gestellt - verstehen alle denselben Bytecode © H.Neuendorf (23) Erstellen + Ausführen von Java-Programmen Lauf-Zeit PC / Webbrowser Compile-Zeit PC Applikation / Class Loader Applet-Klassen + HTML Test.java javac Lokaler PC Netzwerk Class File Verifier Byte Code Verifier Internet Interpreter java JVM Test.class (Bytecode) Für jede im .java-File enthaltene Klasse wird eigenes .class-File vom Compiler generiert Hardware Benutzte Klassen (class-Files) können an unterschiedlichen Orten liegen. Erst zur Laufzeit bei Bedarf nachgeladen ! © H.Neuendorf Durch java ausgeführte Datei muss main() Methode enthalten ........... (24) Erstellen + Ausführen von Java Programmen PC Editor JDK: Java Development Kit Quelldode Compiler + Interpreter Test.java Compile-Zeit Werden aus Kommandozeile aufgerufen z.B. : C:\ Demos > javac Test.java Java Compiler C:\ Demos > java Test javac.exe Bytecode Test.class Lokaler PC Netzwerk Internet Internet Browser JVM Verwendung fremder Klassen : Weitegabe kompilierter classFiles ohne Quellcode genügt ! Eingabe Java Bytecode Interpreter java.exe Hardware © H.Neuendorf Lauf-Zeit Ausgabe (25) Java-Werkzeuge - Java Development Kit (JDK) Kommandozeile : javac Compiler java JVM-Launcher appletviewer Darsteller für Applets javadoc Dokumentationswerkzeug jar Archivierung (Zusammenfassung von Files) javap Bytecode-Analyse ......... ............................ (vieles mehr ...) C:\ Demos > javac Test.java C:\ Demos > java Test (Ergebnisse dort ausgegeben) Finden JDK-Werkzeuge + eigene Klassen mittels Umgebungs-Variablen : SET PATH=C:\Java\ jdk1.6.0_02\bin Kein statischer Linker da Java-Laufzeitsystem benötigte Klassen dynamisch bei Bedarf zur Laufzeit lädt durch Class Loader javac Editor Entwickler SET CLASSPATH=.;C:\MyFolder java Programmtext Bytecode Ergebnis Interpretieren + Ausführen Beispiel.class Übersetzen Beispiel.java Integrierte Entwicklungsumgebung (IDE) → Eclipse : Massive Unterstützung bei Erstellung, Verwaltung, Bearbeitung und Ausführung von Programmierprojekten ( Projektdarstellung, Wizzards, Debugger, Syntax-Colouring, ..... ) © H.Neuendorf (26) Struktur Programmiersprache Sprachdefinition: Sun → Java Language Specification Fester Wortschatz : Schlüsselwörter = reservierte Wörter Neue Bezeichner : Benutzerdefinierte Namen mittels zulässigem Zeichensatz Zeichensatz : Klein-/ Großbuchstaben in Java NICHT austauschbar! Syntax: Case Sensitive !!! Regelt Bildung sinvoller Anweisungen aus Zeichen und reservierten Wörtern Syntaxregeln = Metasprache Ziffer ::= (Backus-Naur-Form BNF-Syntax) 0|1|2 |3 |4 |5 |6 |7 |8 |9 Zahl::= Ziffer { Ziffer } Kleinbuchstabe::= a|b|c|d|e|f| ....... Großbuchstabe::= A|B|C|D|E|F| ....... Buchstabe::= Kleinbuchstabe | Großbuchstabe Bezeichner::= Buchstabe { Buchstabe | Ziffer } "Syntaktische Ableitung" Syntaxdiagramm für " Zahl " Ziffer Sematik: Bedeutung der einzelnen Sprachkonstrukte = Wirkung der Programmanweisungen " Was bedeuten die Sätze der Sprache im jeweiligen Kontext? " © H.Neuendorf Grundkonzepte von Programmiersprachen (27) Ebene oberhalb Maschinensprache ⇒ Mittel der Abstrakton und Strukturierung : Datentypen : (primitive + strukturierte) Modellierung verschiedenartiger Größen, Sicherheit durch Typprüfung Variablen + Konstanten : Verwendung von Namen anstelle von Speicheradressen für Zugriff Verknüpfung von Variablen und Konstanten zu Termen Zuweisungen : Speicherung von Ausdruckswerten in Variablen Ablaufsteuerungsanweisungen : (Verzweigungen & Schleifen) Manipulation des Programmverlaufs – Steuerung des Kontrollflusses Unterprogramm : (Prozedur, Funktion, Methode) Kapselung von Programmteilen zur Wiederverwendung und übersichtlichen Strukturierung ↓ Klassen : Zusammenfassung von Daten + Methoden in eigenständiger gekapselten Einheit © H.Neuendorf Zunehmende Komplexität Ausdrücke : Elementare Bestandteile von Java A.) Elementare Symbole : Namen : Bezeichnen Variablen, Konstanten, Typen, Methoden, Klassen, .... Bestehen nur aus Buchstaben, Ziffern, ‘_‘ und ‘$‘ Nicht ' , # * .... Erstes Zeichen muss Buchstabe, ‘_‘ oder ‘$‘ sein (keine Leerzeichen !) (Variable min ≠ Min) Groß- und Kleinbuchstaben unterschieden - Case Sensitive ! Bsp : x x12 birth_date Ungültig : 1Wert Hallo Welt class A'1 Schlüsselwörter : Reservierte Namen zur Kennzeichnung von Programmstrukturen etc. Java-Schlüsselwörter ausschließlich in Kleinbuchstaben abstract boolean break byte case catch char class const continue default do double else extends final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static super switch synchronized strictfp this throw throws transient try void volatile while Zahlen : Ganzzahlen : a) Dezimalzahlen = Ziffern 0 ... 9 Bsp : 0, 127, 1024 b) Hexadezimalzahlen = Ziffern 0 ... 9 + a,b,c,d,e,f → Kennzeichnung durch 0x (für 10,11,...,15) Bsp : 0x1A = 16+10 = 26 c) Oktal = Ziffern 0 ..... 7 → Kennzeichnung durch 0 Gleitkommazahlen : verschiedene Darstellungsweisen Bsp : © H.Neuendorf 3.2 3. 0.3E1 3E-1 3.2f Bsp : 012 = 8+2=10 Kommateil mit Punkt ! 3.2F 3.2d 3.2D (28) (29) Java-Schlüsselwörter - Kategorien Java Schlüsselwörter Klassen / Objecte class interface new extends implements instanceof return Modifizierer private public protected abstract final native static strictfp synchronized transient volatile Elementare Datentypen byte short int long float double boolean char Schleifen Bedingungen Packages do while for break continue if else switch case default package import © H.Neuendorf Fehlerbehandlung try catch throws throw finally assert Referenzen/ Konstanten this super void null true false Einfache Elemente von Java A) Zeichen : Einzelne in Hochkommata eingeschlossene Buchstaben, Ziffern - und Sonderzeichen Bsp: 'x' '+' '3' 'z' '' '\n' Zeichenketten : Folge beliebiger in doppelte Hochkommata eingeschlossener Zeichen Dürfen Zeilengrenzen nicht überschreiten Bsp: "This is a simple string." "§3 a + 2% #$" " " B) Variablen-Deklaration + Variablen-Initialisierung : Deklaration : Jede Variable muss vor Verwendung deklariert = mit ihrem Typ angemeldet werden Bei Programmausführung wird entsprechender Speicher belegt Namenszweideutigkeiten sind unzulässig Aufzählungen möglich - Deklaration endet mit Strichpunkt ; Bsp: int x ; short w, y, z ; Initialisierung : Bei Deklaration kann bereits Initialwert mitgegeben werden Spätestens vor erster Verwendung muss Variable expliziten Wert erhalten Bsp: int x = 100 ; int y = 0, z = 5 ; Kommentare : Vom Compiler ignorierte Erläuterungen im Quellcode // hiermit wird Rest der Zeile zum Kommentar = Zeilenendkommentar /* hiermit wird Klammerkommentar eingeleitet, der sich über beliebig viele Zeilen erstrecken darf; er endet mit dem Zeichen: */ Klammerkommentare dürfen nicht geschachtelt werden ! © H.Neuendorf (30) B.) Variablendeklaration : (31) Datentypen : Jede Variable muss mit Typangabe deklariert werden Vordefinierte Standardtypen + selbst deklarierte komplexe Typen ( = Klassen ) Typ legt fest: a) welche Werte der Variablen zuweisbar sind b) welche Operationen durchgeführt werden dürfen Kernkonzept moderner Programmiersprachen → Compiler kann prüfen dass : → Variablen nur zulässige Werte erhalten + nur zulässige Operationen angewendet werden ⇒ statische Typprüfung beim Übersetzen (Compilezeit) vor Programmausführung (Laufzeit) Standard-Datentypen : a) ganze Zahlen: 8 Bits = 1 Byte byte 8-Bit -2 7 .....2 7-1 (-128 ... 127) ( 1Bit fürs Vorzeichen ) short 16-Bit -2 15 .....2 15-1 (-32768 ... 32767) int 32-Bit -2 31 .....2 31-1 (-2147483648 ... 2147483647) long 64-Bit -2 63 .....2 63-1 Speicherverbrauch + Werte bei Programmausführung bedenken ! Gefahr : Bei Programmausführung können Werte vorkommen, die Bereich überlaufen ! Teilmengenbeziehung gemäß Wertebereich : long ⊃ int ⊃ short ⊃ byte ⇒ Bsp: short-Wert kann long- oder int-Variablen zugewiesen werden, nicht aber byte-Varaiblen Primitive Datentypen : Haben keine weitere Struktur, können nur einen einfachen Wert aufnehmen Im Gegensatz zu strukturierten Datentypen : Klassen → können mehrere Werte aufnehmen © H.Neuendorf (32) Standard-Datentypen b) Gleitkommazahlen: float 32-Bit größte Zahl : ≈ 3 ∗10 +38 double 64-Bit größte Zahl : ≈ 2 ∗10 +308 ⇒ double hat mehr signifikante Dezimalstellen + größeren Bereich als float ⇒ größere und genauere Gleitkommazahlen darstellbar aber : Genauigkeit prinzipiell begrenzt ⇒ Rundungsfehler ! Wichtig bei Vergleichsoperationen !! Teilmengenbeziehung : Bsp: double d = 1.0; double ⊃ float " ⊃ " long ⊃ int ⊃ short ⊃ byte float f = 2.0f; f=i; // ok i=f; // Fehler ! Rechnen mit Gleitkommazahlen für Rechner aufwändiger als mit ganzen Zahlen ! int i = 3; i = (int) f ; // ok - Typumwandlung (type cast) Bei Cast: Nachkommastellen abgeschnitten ! Default-Typ einer Java-Gleikommazahl ist double ! double d = 3.14 ; // ok float f = 3.14 ; // Fehler! float f = 3.14f ; // ok! float-Wert double d = 1.0; float f = 1.0f; Typregeln in Ausdrücken beim "Mischen" der Zahlentypen : int i = 2; ... f + i .... // float Der "kleinere" Operandentyp wird vor Ausführung der Operation in den "größeren" konvertiert . Der berechnete Ausdruck bekommt dann den gleichen Typ wie seine Operanden - zumindest aber den Typ int ! ... d * i .... // double ... s + s .... // int !! ... f / 3 .... // float © H.Neuendorf short s = 3; ... (float) i / 3 .... // float (33) Standard-Datentypen b) Gleitkommazahlen: float 32-Bit double 64-Bit Intern Darstellung von Gleitkommazahlen durch Vorzeichen, Mantisse, Exponent : z = (-1)v ∗ Mantisse ∗ 2 Exponent Darstellung mit verfügbaren Bits gemäß IEEE-Norm: v Exponent Mantisse float 1 Bit 8 Bit 23 Bit double 1 Bit 11 Bit 52 Bit Aufgrund exponentieller Zahlendarstellung können mit float (32 bit) größere Zahlen als mit long (64 bit) dargestellt werden, obgleich weniger Bits verwendet werden – allerdings evtl. auf Kosten der Genauigkeit ! Konsequenz für Wertebereich + Darstellung : float → Exponentialteil von 2 -127 bis 2 +127 (entspricht etwa 2∗10 -38 bis 2∗10 +38 ) Mantissen erlauben Darstellung mit ca. 7 Dezimalstellen Genauigkeit double → Exponentialteil von 2 -1023 bis 2 +1023 (entspricht etwa 2∗10 -308 bis 2∗10 +308 ) Mantissen erlauben Darstellung mit ca. 16 Dezimalstellen Genauigkeit Notation: Mit Dezimalpunkt Durch Suffix f oder d als float oder double kennzeichenbar (Default double) Bsp: © H.Neuendorf 1.0 2.56f 4.45e-11 2.2E9f ...... ( E oder e bedeutet 10 hoch ) Standard-Datentypen : (34) Kleine Fallen Ganzzahlen : 1. Java schneidet bei Ganzzahlrechnungen ab, um Ganzzahl zu erhalten ⇒ Kommastellen ohne Rundung abgeschnitten ! Bsp: double x = 1 / 2 ; // in x 0.0 ! double y = - 3 / 2 ; // in y – 1.0 ! 2. Ganzzahlenbereiche quasi zu Ring geschlossen Bei Überschreiten positiven Bereichs → Wiedereintritt in negativen Bereich Bsp: byte b = 127 ; b = (byte) (b + 1) ; // in b nun -128 ! byte b = 120; for( int i = 0; i<20; i++ ) { b++; } // Überlauf – kein Fehler !! 3. Bei Rechnen mit byte- und short-Variablen wandelt Java zum Typ int ! Bsp: byte a = 5 ; byte b = 10 ; byte c = a + b ; // Compilerfehler ! Typ von (a + b) ist int ! // ⇒ Stattdessen casten : byte c = (byte) (a+b) ; © H.Neuendorf // ok! ..... Kleine Fallen Fließkommazahlen : 1. Default double ! Bsp: ⇒ 1 + 1.0 ist double, (35) 1 + 1.0f ist float float f = 1 + 1.0 ; // Fehler ! 2. Rundung von Ganzzahlen auch bei Zuweisung zu Fließkommatypen ! ⇒ double d = 1 / 2 ; double d = 1.0 / 2 ; // in d 0.0 ! 1 und 2 als Ganzzahlen // in d 0.5 1.0 als double-Zahl double d = (double) 1 / 2 ; // in d 0.5 1 zu double gecastet double d = 0.8 + 1 / 2 ; 1/2 liefert 0 // in d 0.8 ! 3. Fließkomma-Berechnungen sind ungenau ! double d = 1.03 – 0.42 ; // in d steht 0.6100000000000001 Casting : → Castoperator : ( type ) x Somit Fließkommatypen im Grunde ungeeignet für monetäre Berechnungen !! explizite Typkonvertierung 1. Zuweisung an größeren Datentyp unproblematisch Bsp: int i = 17; float f = i ; // ok – 17.0f implizite automatische Typkonvertierung 2. Aber : Bei Cast zum kleineren Datentyp wird abgeschnitten ! ⇒ Wertänderung ! Bsp: float f = 17.35f ; int i = (int) f ; // in i steht 17 - Verlust Nachkommastellen ! Bsp: short s = 258 ; byte b = (byte) m ; // in b steht 2 - "überstehende" Bits abgeschnitten ! © H.Neuendorf c) Zeichen = Typ char : Standard-Datentypen .. Codierung mit 16bit = UC Einzelnes Zeichen (≠ Zeichenkette) → in einfachen Hochkommata ' ' char ch1 = 'a' ; Java-Codierung 16bit = 2 Byte-Unicode char-Variablen können Zahlenwerte [0 ; 65535] direkt zugewiesen werden – aber keine Werte < 0. Zuweisung von GanzzahlVariablen nur mittels Cast ⇒ char auch "zahlenartig" aber ≠ short-Bereich double ⊃ float ⊃ long ⊃ int "⊃ char ⊃" short ⊃ byte ⇒ Berechnungen : int n = 'c' - 'a'; (36) Bedeutung : Zeichen am Platz in UC-Tabelle // n-Wert 2. Zwei Plätze weiter in UC-Tabelle int k = 'a'; // k-Wert 97 short s = ch1; // Fehler – char sprengt positiven Bereich von short – mit int ok! char ch2 = (char) k; // Bei Variablen nur mit Cast ! Binäre Zeichen-Codierung : 1 Zeichen = 1 Byte ⇒ 256 Zeichen Unicode : 1 Zeichen = 2 Byte ⇒ 65536 Zeichen ! ASCII : ← Java Erste Zeichen von UC entsprechen den ASCII-Zeichen d) Zeichenkette = Typ String : Kein primitiver Standard-Datentyp ! → In " " : String s = "Hallo" ; String z = "37.6" ; → Mit Operator + Zeichenketten-Konkatenation : s = s + " Welt !" ; → String-Konvertierung wenn durch + String mit anderem Datentyp verknüpft : String s2 = "Alter = " + 40.5 ; © H.Neuendorf s2 = 10 + 10 + " Alter" ; // Inhalt: 20 Alter Java-Datentypen (37) Datentypen Objekttypen Referenztypen Primitive Datentypen Ganzzahltypen : byte short int long Fließkommatypen : float double boolsche Werte : boolean Zeichen : char *) Defaults zB wirksam bei Initialisierung von Objekt-Attributen und Array-Inhalten … Null-Referenz Arrays Objektreferenzen null Menge von Objekten oder primitiven Datentypen Elementare Datentypen Typ Länge Wertbereich byte 8 Bit -128.....127 (byte) 0 short 16 Bit -32768......32767 (short) 0 int 32 Bit -2147483648....2147483647 long 64 Bit < Bereich ± 2^64 > float 32 Bit -3.4028..E+38... 3.4028..E+38 0.0 f double 64 Bit -1.7976..E+308...1.7976..E+308 0.0 d true / false false Unicode ! '\u0000' = (char) 0 boolean char © H.Neuendorf Klassen / IF 16 Bit Initialisierung / Default *) 0 0L (38) Elementare Sprachkonstrukte C) Ausdrücke : Zuweisungen, Operationen : Jede Art von Anweisung in Java endet mit einem Semikolon ; (Deklaration, Zuweisung, Methodenaufruf, Objekterzeugung ....) Durch Compiler gecheckt ! Zuweisungen: x=y+1; x=x+1; Rechter Ausdruck berechnet und links stehender Variablen zugewiesen Zuweisungen nur erlaubt, wenn Typen beider Seiten zuweisungskompatibel ! Sonst Compiler Fehler → statische Typprüfung → verhindert LZ-Abbrüche oder falsche Werte → a) beide Seiten desselben Typs oder b) Typ linker Seite schließt Typ rechter Seite gemäß Typ-Teilmengenbeziehung ein int i = 2, j = 5; short s = 5; i = j ; // ok i = s ; // ok byte b = 4 ; s = i ; // Fehler! Konstanten-Deklaration : final int MAX = 527 ; Definition von Konstanten : i = 300 ; // ok b = 300 ; // Fehler! // Schlüsselwort final Compiler checkt Dürfen ihren Wert nach einmaliger Initialsierung nicht mehr verändern. Einmalige Initialisierung auch nach Deklaration möglich – sogar auch erst zur LZ ! Vorteil: Lesbarkeit + Wartbarkeit durch zentrale Pflege konstanter Werte an einer Code-Stelle © H.Neuendorf Zuweisungen und Operationen : Arithmetische Ausdrücke : (39) Elementare Sprachkonstrukte z =-(3*x+y); // "von links nach rechts ..." Berchnung eines numerischen Werts aus Variablen und Konstanten mittels zulässiger Operatoren a) Unäre Operatoren : Vorzeichenfestlegung durch + und Bsp: 4+-3 // liefert 1 → "binden" am stärksten ........ -4+3 // liefert -1 b) Binäre Operatoren : Übliche Vorrangregeln ⇒ Punkt vor Strich, durch Klammern änderbar Addition + Subtraktion - Multiplikation * Division / : Ergebnis bei Ganzzahlen eine Ganzzahl (ohne Rest) 4/3=1 Ergebnis bei Gleitkommazahlen eine Gleitkommazahl Modulo % : bei Ganzzahlen der ganzzahlige Divisionsrest bei Gleitkommazahlen: 4%3=1 ( selten verwendet ......) Gleitkomma-Divisionsrest von x/y 8.5 % 2.1 = 8.5 - 4 * 2.1 = 0.1 © H.Neuendorf -7 % 2 = -1 Divisionsrest Bei Ganzzahltypen ist /0 und %0 nicht zulässig und liefert zur LZ ArithmeticException (40) Arithmetik 1. Bei Ganzzahltypen ist /0 und %0 nicht zulässig - liefert ArithmeticException : int a = 5 / 0 // Abbruch !! 2. Fließkommatypen können jedoch gegen Unendlich "überlaufen" ! Operationen können mathematisch "uneigentliche" Werte liefern : NaN (Not a Number) infinity -infinity double d = 0.0 / 0.0 // in d steht NaN ! float f // in f steht infinty ! = 1.0f / 0f double g = 5. % 0 // nicht direkt zuweisbar, aber mittels Hüllklassen Weiterverarbeitung "pathologischer" Werte mittels Hüllklassen (s.u.) // in g steht NaN ! 3. Strenge und nichtstrenge Gleitkomma-Arithmetik strictfp anwendbar auf Klassen, Interfaces, einzelne Methoden Bezieht sich nur auf Gleitkomma-Arithmetik-Operationen strictfp : Strenge Regeln für G.A.Os. ⇒ Auf allen VM-Implementierungen erhält man bitgenau identische Ergebnisse nicht-strict : Gelockerte Regeln für G.A.Os. ⇒ VM-Implementierung darf interne Optimierungen anwenden, die zu leicht abweichenden Ergebnissen führen könnten Wenn eine nicht-strict-Methode eine strictfp-Methode aufruft, so wird sie selbst insgesamt strictfp ausgeführt. strictfp wird jedoch nicht vererbt. Konstantenausdrücke mit G.A.Os. werden immer streng ausgewertet © H.Neuendorf Arithmetische Ausdrücke : (41) Operatoren c) Inkrement- und Dekrement - Operatoren : Erhöhung oder Erniedrigung des Werts von x um 1 x++ ; ++x ; x- - ; - -x ; // entspricht x = x+1; bzw x = x-1; → nur auf Variablen anwendbar, nicht auf Ausdrücke: x = (x * y)++; // Fehler! Vorsicht bei Verwendung in Ausdrücken ! Stellung Operator bestimmt Auswertreihenfolge !! : int x = 1; int y = ++x * 3 ; // liefert 6 für y int y = x++ * 3 ; // liefert 3 für y x hat am Ende in beiden Fällen Wert 2 Präfixform ++x : Erst x manipulieren, dann weiterrechen Postfixform x-- : Erst Zuweisung durchführen, dann x manipulieren d) Zuweisungsoperatoren : Abkürzende Schreibweise für Operation und Zuweisung Für x vom Typ T ist x op= y kürzere Schreibweise für : int x += y; // x = (int)(x + (y) ); x /= y; x -= y; x %= y; In besonderen Fällen schnellere Auswertung x = (T) ( x op (y) ) ; Bsp: short a *= b+1; // a = (short)( a*(b+1) ); x *= y; …. auch für alle anderen arithmetischen, logischen und Bit-Operatoren © H.Neuendorf (42) Operatoren e) Bitoperationen (~, &, |, ^, <<, >>, >>>) nur für Ganzzahltypen – und char Verändern Bitmuster : Direkte Manipulation der Bits in Binärdarstellung ⇒ Liefern geändertes Bitmuster zurück Und, Oder, Exclusiv-Oder : Negation : a b a&b a|b a^b a ~a 0 0 0 0 0 0 1 0 1 0 1 1 1 0 1 0 0 1 1 1 1 1 1 0 Bsp : x = 1010 y = 1100 x & y = 1000 x | y = 1110 x ^ y = 0110 ~ x = 0101 ~ y = 0011 Shift-Operatoren : a << b Schiebt Bits von a um b Stellen nach links und füllt mit 0-Bits auf Bsp: a >> b Schiebt Bits von a um b Stellen nach rechts und füllt mit höchstem Bit von a auf Bsp: a >>> b a = 1100 ⇒ (a >> 2) liefert 1111 Schiebt Bits von a um b Stellen nach rechts und füllt 0-Bit auf Bsp: © H.Neuendorf a = 0110 ⇒ (a << 1) liefert 1100 a = 1100 ⇒ (a >>> 2) liefert 0011 (43) Bitoperatoren Bsp: Zahldarstellung mit fiktiven 4 bits im Zweierkomplement Negation : ~ byte b = 3 ; // ≡ 0000 0011 Höchstes Bit → Vorzeichen ! byte c = ~b ; // ≡ 1111 1100 ≡ - 4 // Wert von c ist - 4 ! Addition : & byte b1 = 3 ; // ≡ 0000 0011 byte b2 = 1 ; // ≡ 0000 0001 bye c = b1 & b2 ; // ≡ 0000 0001 ≡ +1 ; // Wert von c ist +1 ! Shift : Linksshift << und Rechtsshift >> byte b = 2 ; // ≡ 0000 0010 byte c = (b << 1) ; // ≡ 0000 0100 ≡ +4 // Wert von c ist +4 ! Schieben von Bits nach links bzw rechts entspricht Multiplikation bzw Division durch 2 ...... © H.Neuendorf 1 = - 23 Dezimal +8 +7 +6 +5 +4 +3 +2 +1 0 -1 -2 -3 -4 -5 -6 -7 -8 0=+0 Bitmuster nicht darstellbar 0111 0110 0101 0100 0011 0010 0001 0000 1111 1110 1101 1100 1011 1010 1001 1000 (44) Java-Operatoren Operatoren mathematisch Addition Subtraktion Multiplikation Division Modulo Vorzeichen Inkrement Dekrement + * / % + ++ -- logisch Vergleiche Negation ! Konj. && & Disj. || | ^ Für boolesche Ausdrücke Gleichheit Ungleich Größen Referenz Zuweisung == != < > <= >= instanceof Zuweisung = += -= *= ............. bitweise Negation ~ und & oder | xor ^ Shift >>,<<, >>> Auswertregeln : → von links nach rechts → gemäß Vorrangregeln - "Punkt vor Strich" - Klammern höchste Priorität Besonderheit : Zuweisungsoperator = x=y=z © H.Neuendorf ausgewertet als x=(y=z) ⇒ Im Zweifel Klammern setzen ! (45) Priorität der Operatoren - Vorrangregeln Zusammengesetzter Ausdruck : boolean b = 4 + 3 < 7 * 2 - 3 ; // liefert true Ausführungsreihenfolge der Operationen durch Priorität der Operatoren geregelt Im Zweifel + zur Übersicht Klammern setzen ! Bezeichnung Unäre Operatoren Expliziter Cast Multiplikative Operatoren Additive Operatoren Schiebeoperatoren Vergleichsoperatoren Vergleichsoperatoren bitweise Und bitweise XOR bitweise Oder logisches Und logisches Oder Zuweisungsoperator © H.Neuendorf Operator ++ -- + - ~ ! ( <type> ) * / % + << >> >>> < > <= >= == != & ^ | && & || | = += -= ..... hoch Operation mit höherer Priorität vor Operation niedrigerer Priorität ausgeführt Bsp : Punkt- vor Strichrechnung Setzen von Klammern ändert Auswertreihenfolge ! boolean b = 4+3 < 7 * (2 - 3); // liefert nun false! niedrig (46) Grundstruktur Java Schlüsselwort class : Java-Programm stellt selbst Klasse dar Minimales ausführbares Programm : class Name { Start des Programms mit Ausführung der zentralen Methode main() public static void main ( String[ ] args ) { // ..... Deklarationen ..... String[ ] args : String-Array, das bei Programm-Aufruf gefüllt werden kann // ..... Anweisungen ...... } } Zu jeder geöffneten Klammer (Blockanfang) gehört auch eine schließende Klammer (Blockende) class Hallo { // erstes Programm public static void main (String[ ] args) { void : Methode main() liefert keinen Wert zurück static : statische Methode - direkt aufrufbar, ohne erst Objekt der Klasse zu erzeugen public : Methode main() nach außen sichtbar, von JVM aufrufbar String gruesse = "Hello World" ; IO.writeln( gruesse ) ; 1. Speichern als gleichnamige Datei Hallo.java } 2. Wechsel ins Quellverzeichnis } Quelldatei hat gleichen Namen wie die Klasse + Extension .java 3. Aufruf JDK-Compiler : javac Hallo.java 4. Ergebnis / Compilat : Hallo.class 5. Ausführen durch Aufruf JVM : © H.Neuendorf java Hallo (47) Grundstruktur Java In Java-Programmen kollaborieren Klassen bzw. Objekte // Erstes Programm class Hallo { Objekten werden Aufträge erteilt : public static void main( String[ ] args ) { String gruesse = "Hello World" ; IO.writeln( gruesse ) ; // Text // Methodenaufruf } } Hallo = Auftraggeber Client IO Server = Auftragnehmer Klasse IO stellt Methoden zur Verfg. (s.u.) Aufrufsyntax : Objektname.Methodenname( Parameterliste ) ; Hallo writeln(..) Kollaborationsdiagramm Blöcke können geschachtelt werden Methoden können Parameter haben oder parameterlos arbeiten. Methoden können Werte zurückliefern. IO Durch Semikolon wird Anweisung abgeschlossen ; (UML) Paar von geschweiften Klammern { } mit null oder mehr Anweisungen = Block © H.Neuendorf Hallo ruft Methode writeln() der Klasse IO auf Ein Block kann überall da stehen, wo eine einzelne Anweisung verwendet werden kann - da ein Block eine (zusammengesetzte) Anweisung ist Ein- / Ausgabe (48) (I/O) Wertbelegung von Variablen durch : a) Initialisierung und Zuweisung im Quellcode b) Einlesen von Tastatur oder aus Datei (Ausgaben auf Bildschirm oder in Datei) In Java ist Ein-/ Ausgabe kein integraler Teil der Sprache, sondern Teil der Java-Pakete : → zahlreiche Klassen mit Methoden zum Einlesen / Ausgeben (von Zahlen, Texten, Bitströmen, .... ) Handhabung nicht einfach ⇒ Vorerst Arbeiten mit Ein-/Ausgabeklasse IO. java Deren Methoden verbergen die Komplexität ...... class Hallo { public static void main( String[] args ) { IO.writeln( "Hallo Mosbach!" ) ; IO.advance(3) ; IO.writeln() ; IO.writeln( 123456 ) ; } } © H.Neuendorf Tool-Klasse IO : Methoden sind public + static ⇓ Können direkt ohne Instanziierung in verwendet werden ! Konsolen-Eingabe / -Ausgabe mittels (49) IO.java Ausgaben : void advance (int n) // gibt n Leerzeilen aus void write (String s) // gibt String s aus void write (int i) // gibt Zahl i aus void writeln (String s) // gibt String s aus + Zeilenvorschub void writeln (int i) // gibt Zahl i aus + Zeilenvorschub void writeln () // Zeilenvorschub ... und noch einige mehr .... Eingaben : String readString () // liest vom Benutzer eingegebenen String von Konsole ein int readInt () // liest vom Benutzer eingegebene Ganzzahl ein …………….. Eingaben mit vorangestellter Eingabeaufforderung (prompt) : String promptAndReadString (String s) // gibt String s aus und liest String ein int promptAndReadInt (String s) // gibt String s aus und liest Integer ein char promptAndReadChar (String s) // gibt String s aus und liest ersten Buchstaben ein double promptAndReadDouble (String s) // gibt String s aus und liest double-Zahl ein Runden von Kommazahlen auf n Kommastellen : float round( float f, int n ) © H.Neuendorf double round( double d, int n ) // liefert gerundete float- bzw double-Zahl Konventionen in Java-Programmen Keine Vorschrift, jedoch üblich ! Einheitlichkeit + Konsistenz erhöht deutlich Lesbarkeit der Programme 1. Nicht mehr als einen Befehl pro Zeile schreiben. 2. Die öffnende geschweifte Blockklammer "{" steht am Ende des vorangegangenen Statements - oder in einer eigenen Zeile. 3. Wenn neuer Block mit "{" begonnen wird werden alle in diesem Block stehenden Zeilen um einige Zeichen nach rechts eingerückt. 4. Das Blockendzeichen "}" wird stets so eingerückt, daß es mit der Einrückung der Zeile übereinstimmt, in der der Block geöffnet wurde. Sinn: Vergessene Klammern sind leichter aufspürbar. Tips : 1. Zugehörige schließende und öffnende Klammern { ... } (...) [...] immer zugleich schreiben und ihren Inhalt erst nachträglich einfügen, damit schließende Klammern nicht vergessen werden. 2. Umgeben Sie Operatoren mit Leerzeichen, damit Formeln besser lesbar werden. Eventuell existieren firmeninterne Style-Guides ........ © H.Neuendorf (50) (51) Kontrollstrukturen - Verzweigungen + Schleifen A) Verzweigungen Fortgang der Verarbeitung hängt von Bedingungen ab if-Anweisung : if ( <Bedingung> ) { <Anweisungen> } else { <Anweisungen> } Überprüfung Bedingung auf Wahrheit/ Falschheit bestimmt weiteren Programmfluss Bedingung wahr ⇒ if-Zweig Aufspaltung in zwei Zweige : Bedingung falsch ⇒ else-Zweig if ( x > y ) { max = x ; } else { max = y ; } (else-Zweig kann fehlen) Bedingung : Logischer Ausdruck vom Typ boolean Anweisungsblöcke : Ausführung mehrerer Anweisungen ⇒ in Block { } zu fassen if ( x < 0 ) { x = x + 1 ; else { x=x -1; IO.writeln (x) ; } IO.writeln (x) ; } Dangling else : Verschachtelte if-Anweisungen - ein else "hängt in der Luft" : if ( a < b ) if ( a > 0 ) else test = b ; test = a ; // auf welches if bezieht sich dieses else ?? Regel : Ein else gehört immer zum direkt vorausgehenden if © H.Neuendorf (52) Datentyp boolean : ( ≠ 0,1 ! Nur Werte true und false ≠ C/C++ ! ) Bei Vergleichen (relationalen Operationen) als Ergebnis zurückgegeben Variablen zuweisbar : boolean q = x < y ; Nicht zu anderen Datentypen cast-bar ! Vergleichsoperationen : Vergleich zweier Operanden ⇒ liefern Booleschen Wert (true, false) == != > < >= <= gleich ungleich größer kleiner größer oder gleich kleiner oder gleich x == 3 x != y x>y x<y x >= y x <= y ( nicht : x = 3 ; Zuweisung, kein Vergleich ! ) Prüfung gegen NaN-Werte liefert immer false Verknüpfung von Booleschen Termen durch Operatoren : And: x && y (bzw &) Or : x || y (bzw | ) XOR : X ^ Y (entweder oder) if ( 0 <= x && x <= 10 || 100 <= x && x <= 110 ) if ( ! ( x < 10) ) { y=x; } { x = 20 ; } Vorrangregel : Im Zweifel Klammern setzen ............... ! == bindet stärker als ! bindet stärker als && bindet stärker als || a && b == c && d © H.Neuendorf hat Bedeutung von a && (b == c ) && d Not: !X (53) Kurzschlussauswertung : lazy evaluation Optimierungsleistung der JVM : Auswertung eines Vergleichsausdrucks wird abgebrochen, wenn Gesamtergebnis logisch feststeht d.h. unabhängig ist von Auswertung der nichtausgewerteten Teile &&-Verknüpfung : Wenn schon erster Teil false, dann auch Wert des Gesamtausdrucks false || - Verknüpfung : Wenn schon erster Teil true, dann auch Wert des Gesamtausdrucks true if ( y == 0 Bsp: if ( x < 0 a b w w f && ++x / y > 10 ) ...... // Wenn y != 0, dann ++x / y nicht berechnet ! || --x / y < 10 ) ...... a && b // Wenn x < 0, dann --x / y nicht berechnet ! a && b a || b a^b w w f w f w w w f f w w a f f f f f b a b Umgehung Kurzschlussauswertung durch Operatoren & (statt &&) bzw | (statt || ) Bei Operanden-Typ boolean wirken & und | als logische Operatoren, nicht als Bit-Operator a || b Ternärer Operator : © H.Neuendorf int a = 10; int b = 1; int c = a>5 ? 2*b : 3*b ; (54) Kompakte Entscheidungsabfragen // Ausführlich : char antwort = IO.promptAndReadChar( "Springt Auto an? ( j/n )" ) ; if ( antwort == ' j' ) { IO.writeln( "prima!" ) ; else { IO.writeln( "schlecht!" ) ; } // Abfrage } // Kompakte Formulierung : Zusammenfassung von Abfrage + Bedingungsprüfung if ( IO.promptAndReadChar("Springt Auto an? ") == ' j ' ) { IO.writeln( "prima!" ) ; } else { IO.writeln( "schlecht!" ) ; } // Zugleich Initialisierung Variable zur weiteren Verwendung char antwort ; if ( (antwort = IO.promptAndReadChar("Springt Auto an? ") ) == ' j ' ) { /* … */ } // Verwendung von antwort …… Prinzip : Abfrage wird in Bedingung der Verzweigung eingebettet Abfrage zuerst ausgewertet → © H.Neuendorf liefert Wert zurück, der dann für Vergleich zur Vfg. steht Kontrollstrukturen : switch -Anweisung Wert-Prüfung eines ganzzahligen Ausdrucks vom Typ Davon abhängig wird verzeigt : int month ; (55) Mehrweg-Verzweigung int, short, byte oder char → Muss mit case-Label beginnen …. int days ; month = IO.promptAndReadInt( "Monats-Nummer = " ) ; switch( month ) Jeder einzelne case-Fall muss eindeutig sein !! { // dürfte auch zusammengesetzt sein // Case-Marker mit möglichen Alternativen case 1: case 3 : case 5 : case 7 : case 8 : case 10 : case 12 : days = 31; // wenn Alternative zutrifft, wird Anweisung ausgeführt break ; Auch negative ganzzahlige Werte erlaubt // wichtig: damit switch verlassen wird ! // sonst : folgende Anweisungen auch ausgeführt ! case 4 : case 6 : case 9 : case 11 : days = 30; break ; case 2 : Lesbarkeit: case gegenüber switchAnweisung eingerückt. Zugehörige Befehle werden nochmals eingerückt days = 28; break ; default : // optionaler Default-Zweig - ausgeführt, wenn kein anderer zutrifft IO.writeln( "Kein gültiger Wert!" ) ; } © H.Neuendorf Es kann nur höchstens einen default-Zweig geben Vergleich mit if-Sequenzen : Wenn kein Fall zutrifft und kein default-Fall angegeben, bleibt switch-Anweisung ohne Wirkung Lesbarkeit + Effizienz ! switch –Anweisung : switch ( <Ausdruck> ) { case <Konstante> : <Anweisung> // ................. <Anweisung> break ; case <Konstante> : <Anweisung> // ................. <Anweisung> break ; default : <Anweisung> // ................. <Anweisung> } © H.Neuendorf Ausdruck : Mit ganzzahligem Wert vom Typ int, short, byte oder char - nicht aber Typ long ! (56) Case-Kombinationen : Mehrere Alternativen gemeinsam behandelbar durch Fall-Aufzählung char m = 'B' ; switch( m ) { // Variable vom Typ char case 'A' : IO.writeln( "Das A wars" ); break; case 'B' : IO.writeln( "Das B wars" ); break; default : IO.writeln( "Sonst was" ); } Sonderformen : final int c1 = 10; final int c2 = 20; int n = 10; switch( n ) { // Benamte Konstanten als Case-Werte case c1 : IO.writeln( "Die 10 wars" ); break; case c2 : IO.writeln( "Die 20 wars" ); break; default : IO.writeln( "Sonst was" ); } public int getValue( int n ) { switch( n ) { // Verlassen mit return-Anweisung in Methoden case 1 : return 100 ; case 2 : return 200 ; In jeden case sind auch weitere Fallunterscheidungen oder default : return 500 ; Schleifen einbaubar. } Wird rasch unübersichtlich ! } B) Schleifen Wiederholte Ausführung von Anweisungsfolgen while - Schleife Kontrollstrukturen Abweisschleife : while ( <Bedingung> ) { <Anweisungen> } Prüfung Fortsetzungs-Bedingung vor jedem Schleifendurchlauf. Bedingung ist Ausdruck, der Booleschen Wert liefert ( true oder false ) ⇒ Eventuell Schleife nie betreten / durchlaufen, wenn Bedingung nicht zutrifft ! Zusammenfassung mehrerer Anweisungen in einen Anweisungs-Block { ... } int i = 1 ; int sum = 0 ; while( i <= n ) { int n = 5 ; // Abbruch: wenn i größer als n ⇒ Schleife verlassen // wird vor jedem neuen Durchlauf mit aktuellen Werten geprüft sum = sum + i ; i++ ; // wenn i = 6 erreicht, wird Schleife nicht mehr durchlaufen! } IO.writeln( sum ) ; // hier wird Programm nach Verlassen der Schleife fortgesetzt Vermeiden von Endlosschleifen : Schleife muss terminieren ⇒ Abbruchbedingung checken ! © H.Neuendorf (57) Kontrollstrukturen do-while - Schleife Durchlaufschleife, nicht abweisend : do { <Anweisungen> } while ( Bedingung ) Prüfung der Fortsetzungsbedingung erst am Ende jedes Schleifendurchlaufs ⇒ Schleifenrumpf wird somit stets mindestens einmal betreten und ausgeführt ! int i = 1 ; int sum = 0 ; int n = 5 ; do { sum = sum + 1 ; // Anweisungen werden mindestens einmal ausgeführt ! i ++ ; } while( i < n ) ; IO.writeln( sum ) ; // wenn i = 5 wird Schleife verlassen // hier wird Programm nach Verlassen der Schleife fortgesetzt Anm: do{ anweisungen } while( !bedingung ) entspricht dem Konstrukt do ... until anderer Sprachen © H.Neuendorf (58) Kontrollstrukturen for - Schleife (59) Zählschleife Anzahl Durchläufe steht fest. Anzahl Durchläufe durch Laufvariable gezählt. Dreiteiliger Schleifen-Kopf : for ( <In>; <Fb>; <V> ) a) Initialisierungsteil : { <Anweisungen> } legt Startwert der Laufvariablen fest b) Fortsetzungsbedingung : wird jedesmal vor Betreten der Schleife geprüft c) Veränderungssteil : int sum = 0 ; wie while-Schleife ! wird nach jedem Durchlauf ausgeführt → verändert Laufvariablenwert int n = 6 ; for( int i=1; i <= n; i++ ) { sum = sum + 1 ; IO.writeln( "Durchlauf Nr. " + i ) ; // chars in UC-Tabelle angeordnet : for( char n='A'; n <='Z'; n++ ) { IO.writeln( "Hier : " + n ) ; } } IO.writeln( sum ) ; // Programm fortgesetzt Initialisierung + Veränderungsteil können aus mehreren Anweisungen bestehen - mit Komma trennen Komplexerer Schleifenkopf : for ( int i = 1, sum2 = 0 ; © H.Neuendorf i <= n ; i++ , sum2 = sum2 + 2 ) { /* ... */ } Verschieden typisierte Laufvariablen müssten außerhalb for-Schleife deklariert werden (60) Kontrollstrukturen for - Schleife Schleife kann durch entsprechende Anweisungen auch dekrementiert werden : for ( int i = 10 ; i > 3 ; i = i - 3 ) { ............................ } // i nimmt Werte 10, 7, 4, 1 an Alle Schleifen können geschachtelt werden : // Multiplikationstabelle : Rumpf einer Schleife kann wieder Schleife enthalten Jeder Teil des for-Schleifenkopfs darf weggelassen werden : int n = 10 ; int mult ; for( int i = 1; i <= n; i++ ) { for( int j = 1; j <= n; j++ ) { mult = i * j ; IO.write( mult ) ; } IO.writeln( ); Fehlende Initialisierung oder Veränderung ⇒ quasi Übergang zur while-Scheife : int i = 1; int n = 10 ; for( ; i<=n; ) { IO.writeln( i++ ) ; } Keine Fortsetzungsbedingung ⇒ true ⇒ Endlosschleife : for( int i=1 ; ; i++ ) { IO.writeln( i ) ; } // i = 100 ; j = 50 ; Fehler !! Endlosschleifen : Die im Schleifenkopf deklarierte Laufvariable ist nur innerhalb Schleife gültig © H.Neuendorf for ( ; ; ) {....} while (true) {.....} } Kontrollstrukturen break (61) + continue Strukturierte Schleifenabbrüche durch break-Anweisung : Kein goto Abbruch des aktuellen Durchlaufs + aller folgenden Schleifendurchläufe Sinn : Abfangen von Situationen, die weitere Ausführung sinnlos machen aber : Schlechter Programmierstil → Programmverlauf unübersichtlicher ⇒ vermeiden ! → Verstreuung Programmlogik ⇒ Fortsetzungsbedingung im Schleifenkopf ! int sum = 0 ; Sprünge aus while-, do-, for-Schleifen : int z = 100 ; break → direkt hinter Schleifenende while( true ) { sum = sum + z ; continue → Abbruch aktueller Schleifendurchgang + Sprung zur Wiederholungsbedingung der Schleife + Fortsetzung mit nächstem Durchgang IO.writeln( sum ) ; if ( sum > 1000 ) break ; } Verlassen äußerer Schleifen durch Bezug auf Label : für break und continue L1: for ( ; ; ) { ............ // Label an äußerer Schleife for (; ; ) { .... if (...) break ; /* exit inner loop */ if( … ) break L1 ; /* exit outer loop */ } } // Verwendung continue : for( int n=-10; n<=10; n++ ) { if( n == 0 ) { continue ; } © H.Neuendorf IO.writeln( "Wert = " + 1.0 / n ) ; } (62) Schleifen - wann welche verwenden ? 1. Anzahl der Wiederholungen steht von Anfang an fest oder kann berechnet werden : ⇒ for-Schleife verwenden : Bsp: int n = IO.promptAndReadInt ( "Anzahl Durchläufe : " ) ; for( int i=1; i<=n; i++ ) { /* etwas tun */ } 2. Erst während Durchläufen wird über weitere Fortsetzung entschieden : ⇒ while- oder do ... while-Schleife verwenden : Bsp: char c = IO.promptAndReadChar( "Aktion ausführen? (j/n) " ) ; while( c == ' j ' ) { // etwas tun ...... c = IO.promptAndReadChar( "Nochmal (j/n) ? " ) ; } 3. Aber: Prinzipiell jede for-Schleife auch durch while-Schleife ausdrückbar ! Bsp: int n = IO.promptAndReadInt( "Anzahl Durchläufe : " ) ; while( i <= n ) { /* etwas tun */ ....... i++ ; int i = 1 ; } Jedoch for-Schleife besser verständlich, da alle nötigen Infos im Kopf der for-Schleife zusammengefasst sind - und nicht über den Schleifenkörper verstreut sind ! © H.Neuendorf Java-Methoden (63) Methodendeklaration : class Groesstes { public static void printMax( int x, int y ){ // Methodenkopf if ( x >y ) IO.writeln(x) ; else Methodenkopf { Methodenrumpf } // Methodenrumpf Methoden können Parameter-Werte entgegennehmen oder parameterlos sein IO.writeln(y) ; printGruss( ) ; } public static void printGruss( ) { // Methodenkopf IO.writeln( "Hallo DH" ) ; // Methodenrumpf) Parameterübergabe : Kopie des aktuellen Parameters an formalen Parameter ⇒ Typkompatibilität gefordert ! } public static void main( String[ ] args ) { int a = 10 ; int b = 20 ; printMax( a , b ) ; // Methodenaufruf ... //.. mit Parametern a und b printGruss( ) ; Klassen können beliebig viele Methoden enthalten - Reihenfolge egal ! // Methodenaufruf ohne Parameter } Methoden können im Rumpf andere Methoden aufrufen → } Formale Parameter : in Deklaration angegeben Aktuelle Parameter : bei Aufruf konkret übergeben Terme erlaubt : printMax( 10, 2*b-1 ) ; © H.Neuendorf Methoden printMax() und printGruss() stehen auf gleicher Ebene wie die spezielle Methode main( ) "Hauptprogramm" Dabei wird Ausführung der aufgerufenen Methode an Aufrufstelle eingeschoben Signatur von Methoden (64) Methodenkopf public void printMax( int x, int y ) Methode = Kopf (header) + Rumpf (body) ↑ ↑ ↑ ↑ Methodenrumpf : Anweisungsfolge 1. 2. 3. 4. Methodenkopf : Signatur = Schnittstelle ⇒ Aufrufsyntax → Kenntnis Methodensignatur befähigt, Methode aufzurufen 1. Sichtbarkeit : legt fest, aus welchem Kontext Methode aufrufbar ist a) public : jedes "Programmteil" darf die Methode aufrufen b) private : Methode von "außen" nicht aufrufbar; nur aus Methoden der selben Klasse aufrufbar 2. Typ Rückgabewert, den Methode bei Aufruf zurückliefert a) void ("leer") : Methode liefert nichts zurück b) int, long, float, char, .... : Methode liefert Wert zurück 3. Methodenname : dadurch verschiedene Methoden einer Klasse unterschieden 4. Parameterliste : Typ + Name der Parameter, die bei Methoden-Aufruf zu initialisieren sind a) Methoden können mehr als einen Parameter tragen b) Auch parameterlose Methoden → (mittes Komma aufzählen) Aufruf mit leerer Parameterliste printGruss( ) ; Aufruf von Methoden : Durch Angabe Namen + Mitgeben korrekter Parameterwerte © H.Neuendorf printMax( 51, 10 ) ; Methoden mit Wertrückgabe class Groesstes { (65) Prozeduren geben keinen Wert zurück ⇒ Rückgabetyp void (kein return nötig) public static int calcMax( int x, int y ) { IO.writeln( "Berechnung läuft " ) ; if( x >y ) return x ; // Rückgabe durch return else Funktionen geben einen Ergebniswert zurück ⇒ Rückgabetyp ist im Kopf anzugeben ! return y ; } public static void main( String[ ] args ) { int a = 10 ; int b = 20 ; int z ; // Aufruf + Zuweisung des Rückgabewerts an z z = calcMax( a , b ) ; calcMax( 2 , 3 ) ; // Verwerfen des Ergebnisses gruesse( 't' ) ; } Müssen mit return-Anweisung abschließen. Ausführung der return-Anweisung beendet Methodenaufruf. Compiler prüft Vorhandensein korrekter returnAnweisung ! Nur ein einziger Rückgabewert möglich Aber : Auch ganze Objekte ! Funktionen auch quasi prozedural aufrufbar ohne Rückgabewert zu verwenden public static void gruesse( char c ) { if ( c == 'a' ) return ; return-Anweisung in Prozeduren : IO.writeln( "Hallo" ) ; Verwendbar, um vorzeitig abzubrechen } } © H.Neuendorf Anweisung return ; (ohne Rückgabewert !) Methoden - Wertrückgabe + Schachtelung class Groesstes { public static int calcMax( int x, int y ) { if( x >y ) return x ; else // Rückgabe return y ; } public static void gibAus( int a ) { (66) Als Eingabewert zum Aufruf einer Methode kann direkt ein anderer Methodenaufruf eingesetzt werden, der den korrekten Rückgabetyp hat ! Umweg über entsprechend typisierte Variable ist nicht nötig IO.writeln( "Wert beträgt: " + a ) ; ⇓ } public static void main( String[ ] args ) { Methodenaufrufe können geschachtelt werden ! gibAus( 56 ) ; Keine direkte Trennung von Deklaration und Implementierung möglich ! ( → Interfaces ) int a = 10 ; int b = 20 ; int z = calcMax( a , b ) ; gibAus( z ) ; a = 45 ; b = 21 ; gibAus( calcMax( a, b ) ) ; } } © H.Neuendorf Bsp: Methode gibAus() muss mit Integerwert aufgerufen werden : a) direkte Übergabe des Werts b) Übergabe Variable, die Wert trägt c) Aufruf einer Methode, die Wert zurückliefert (67) Finale Methodenparameter class Kubisch { public static double hochDrei( final double x ) { // x = 10.0 ; Fehler !! double fx = x * x * x ; // ok! return fx ; // oder: return x * x * x ; } public static void main( String[ ] args ) { double y = hochDrei( 2.75 ) ; IO.writeln( "Wert = " + y ) ; } } Methoden sollten ..... Finale Methodenparamter dürfen in Methode verwendet aber nicht im Wert verändert werden ! Selbstkontrolle der Entwicklerintention durch Compiler ……. Leitlinien für "gute" Methoden 9 eine einzige klar definierte Aufgabe gut erledigen 9 übersichtlich bleiben – dh nicht zu viele Parameter und nicht zu viel Coding aufweisen 9 nützlich und wiederverwendbar sein – dh nicht zu wenig Coding enthalten 9 ausgewogenen Kompromis zwischen Allgemeinheit und Einfachheit darstellen 9 alle nötigen Daten als Parameter erhalten und nicht von globalen Variablen abhängen 9 Ergebnisse als Rückgabewerte zurückliefern - und Seiteneffekte möglichst vermeiden 9 gut dokumentiert werden © H.Neuendorf Rekursion ⇒ Wiederholung ohne Schleifen ! Methoden-Selbstaufruf ↔ Schleifen : Iteration (68) Rekursion : Selbstaufuf FAKULTÄT n! = 1*2*3* ....*(n-1)*n iterativ ⇒ n! = (n - 1)! * n rekursiv Basisfall → n=0: 0! = 1 public static long fak( int n ) { if ( n==0 ) { return 1; } // Basisfall else { return n * fak( n-1 ); } // Rekursion auf ( n-1)! } Problem auf sich selbst zurückgeführt + vereinfacht bis zum trivialen Basisfall 6 Interner Ablauf fak(3) fak( 2 ) aufrufen Rückweg 3 * fak(2) Wert 3 zwischenspeichern Zurückgelieferte Werte mit gespeicherten Werten multiliziert 2 fak(2) Rekursionen : 1. Methode muss Basisfall enthalten 2. Rekursion muss sich auf Basisfall zubewegen 3. Basisfall muss erreicht werden 2 * fak(1) Andernfalls Stack-Overflow 1 else-Zweige Voraussetzungen : fak(1) Jeder rekursive Aufruf von fak hält einen eigenen temporären Wert im Speicher 1 * fak(0) 1 fak(0) Basisfall : if-Zweig © H.Neuendorf 1 Vorteil : Eleganz Problem : Speicherbedarf Daten aller nichtabgeschlossenen Aufrufe (incl. Rücksprungadressen) müssen auf Laufzeitstack zwischengespeichert werden ! Rekursion (69) Beispiele Rekursive Modulo-Definition : public static int mod( int a, int b ) { falls a < b ⎧a mod(a , b) = ⎨ ⎩mod(a − b, b) falls a ≥ b if( a < b ) { return a } // Basisfall else { return ( mod( a-b, b ) ) ; } // Rekursion } Rekursive Exponential-Definition für ganzzahlige Exponenten : public static double power( double x, int y) { x y = x ⋅ x y −1 if( y == 0 ) { return 1 } // Basisfall else { return ( x * power( x, y-1 ) ) ; } // Rekursion } ⎛ n −1 ⎞ ∑ i = ⎜⎜ ∑ i ⎟⎟ + n ⎝ i =0 ⎠ i =0 n Rekursive Summation von ganzen Zahlen : (statt for-Schleife) public static int sum( int n ) { if( n > 0 ) { return (n + sum( n-1 ) ) ; } // Rekursion else { return 0 ; } // Basisfall } © H.Neuendorf (70) Rekursion Probleme n Hoher Speicherbedarf + zahlreiche Selbstaufrufe FIBONACCI-Folge → jedes Glied ist Summe der beiden Vorgänger erste beiden Glieder Wert 1 = Basisfall public static long fibonacci( int n ) { if ( n==1 || n == 2) { return 1 ; } // Basisfall else { return fibonacci( n-1 ) + fibonacci( n-2 ) ; } // Rekursion } fibonacci(5) Es gibt aber auch intelligentere rekursive Lösungen … fibonacci(n) 1 1 2 1 3 2 4 3 5 5 6 8 7 13 8 21 9 34 .................... fibonacci(4) fibonacci(3) fibonacci(3) fibonacci(2) fibonacci(2) fibonacci(1) Durchführung zeigt Ineffektivität : fibonacci(2) fibonacci(1) - Hohe Zahl von Selbstaufrufen - Zahlreiche Zwischenergebnisse mehrfach berechnet ⇒ Simple Rekursion nicht das geeignete Mittel ! © H.Neuendorf Lokale + Globale Variablen - Sichtbarkeiten + Lebensdauer Gültigkeitsbereiche : class Beispiel { public static int a = 5 ; // "global" public static final double PI = 3.14 ; public static void tuWas( int x ) { int y ; // lokale Variable y y=a +x; double d = 1.7 * PI ; } public static void main( String[] args ) { int y = 22 ; // lokal double d = 2.3 * PI ; a = 10 ; tuWas( 7 ) ; } } (71) Lokale Variablen : Auf Methodenebene deklariert Nur dort verwendbar ⇒ Außerhalb Methode nicht "sichtbar" / nicht verwendbar / nicht existent ! Speicherplatz jedesmal bei Methodenaufruf neu angelegt. Bei Methodenende Speicherplatz freigegeben + lokaler Variablenwert gelöscht ⇒ Lokale Variablen "leben" nur während der Ausführung ihrer Methode Globale Variablen : Auf Klassenebene deklariert Gültig + verwendbar in allen Methoden der Klasse Speicherplatz angelegt, wenn Programm startet. Erst bei Programmende Speicherplatz freigegeben. Bis dahin behält globale Variable ihren Wert ⇒ Globale Variablen der Klasse "leben" In Java gibt es keine echten globalen Variablen – es handelt sich um (statische) Attribute der Klasse © H.Neuendorf während gesamter Programmausführung (72) Blockkonzept : Zugriffsbereiche class Bloecke { public static double a = 1.0 ; // global public static void tuWas( ) { for( int i = 1 ; i < 10 ; i++ ) { double d = 9.0 ; // lokal d = d + a ; // OK ! for( int j = 1 ; j < 5 ; j++ ) { // OK : Tiefergeschachtelter Block ! d = d + 1.0 ; } IO.writeln( " Wert = " + d ) ; // OK ! } // Fehler: Zugriff außerhalb Block ! d = d + 2.0 ; IO.writeln( " Wert = " + d ) ; } public static void main( String[] args ) { tuWas( ) ; } } © H.Neuendorf Lokale Variablen : In Methoden-Blöcken deklariert bzw. in Unterblöcken von Methoden ! Nur innerhalb Block zugreifbar, in dem sie deklariert wurden + in darin enthaltenen tiefergeschachtelten Unter-Blöcken Lokale Variablen sollten "so spät und eng" wie möglich angelegt werden - dh nur dort verfügbar sein, wo sie benötigt werden : Minimal Scope Variable d "lebt" im Block der zu forSchleife - in der d deklariert wurde. Auch in darin enthaltenen Unter-Blöcken angesprechbar. Jedoch außerhalb "ihres" Blocks nicht ansprechbar / verwendbar ! (73) Namensräume : Methoden repräsentieren eigenen Namensraum class Beispiel { Namensraum 3 Trotz Namensgleichheit kein Namenskonflikt : Namensraum 1 public static int tuWas( int para ) { int x = 10 ; x = para ; return x ; } Namensraum 2 public static int tuDas( int para ) { int x = 20 ; x = para ; return x ; } public static int x = 30 ; } Jeder Methode verfügt über eigene Variable x In Methode: Lokale Variable → Nur sichtbar + benutzbar in Methode → Außerhalb Methode verborgen Die gleichnamigen Variablen leben in verschiedenen Namensräumen Namen in einem Namensraum unabhängig von Namen in anderem Namensraum wählbar! Unzulässig sind Variablen mit gleichem Namen im selben Namensraum ! Compiler-Fehler ! Block allein genügt nicht : int x = 1 ; Regel : Wird mehrfach vergebener Variablenname benutzt, wird immer die Variable angesprochen, in deren Namensraum man sich befindet © H.Neuendorf { int x = 3 ; } // Fehler ! OK in C++ ! Lokale + Globale Variablen - Sichtbarkeit + Lebensdauer class Beispiel { public static int x = 10 ; public static int y = 20 ; Sichtbarkeit der globalen Variablen x wird unterbrochen durch Sichtbarkeitsbereich der lokalen gleichnamigen Variablen x public void m ( int par ) { int x ; x = par ; } Das lokale x verdeckt das gleichnamige globale x // ................. } Sichtbarkeit = Programmbereich, in dem auf Variable zugegriffen werden kann : Von: Deklaration der Variablen Bis: Ende des Blocks, in dem Variable deklariert wurde Variablen innerer Blöcke verdecken gleichnamige Variablen äußerer Blöcke Gutes Miko-Design : Minimized Scope → Lokale Variable sollte nur in den Blöcken sichtbar sein, in dem sie auch wirklich benötigt wird Beim Deklarieren möglichst auch sinnvoll initialisieren © H.Neuendorf ⇒ Laufvariable der for-Schleife lebt nur in dieser ⇒ Besser als while-Schleife mit stets externer Variablendeklaration (74) (75) Namenskonventionen in Java-Programmen Keine Vorschrift - jedoch allgemein üblich ! Einheitlichkeit + Konsistenz erhöht deutlich Lesbarkeit der Programme ! Klassennamen beginnen mit Großbuchstaben IO, String, Konto In Klassennamen aus mehreren Worten beginnt jedes Wort mit Großbuchstaben ZinsUndRatenRechner Methoden nach Verben benennen Methodennamen beginnen mit Kleinbuchstaben writeln() CamelCase für zusammengesetzte Namen In zusammengesetzten Methodennamen schreibt man Wortanfänge groß promptAndReadDouble() Variablennamen beginnen mit Kleinbuchstaben wert Konstanten bestehen aus Großbuchstaben MINIMUM = 50 Konstantennamen aus mehreren Worten durch Unterstriche zusammengesetzt MAX_VALUE = 100 Hilfsvariablen (Schleifenzähler etc) mit kurzen Namen belegen i , j Methoden die boolean zurückliefern sollten entspr. Frage formen isOpen(), hasNext() Keine Umlaute (ä,ü,ö,) oder Sonderzeichen (ß, ...) verwenden (trotz UC .....) © H.Neuendorf