Zusammenfassung: Imperative Programmierung
2 Wichtige Begriffe Syntax: Regeln nach denen ein beliebiger Satz Teil einer Sprache ist Compiler/Interpreter versteht nur syntaktisch korrekte Sätze Semantik: Bedeutung/Aussage eines Satzes der Sprache Compiler/Interpeter kann den Sinn eines Satzes nicht feststellen Token: Ein Baustein eines Satzes Separator : Trennzeichen, welche die Bausteine eines Satzes separieren Es kann bestimmte Zeichen zur Auszeichnung eines Satzendes geben Es gibt Trennzeichen mit Semantik (z.b. Klammern in math. Ausdrücken) Satz: Eine syntaktisch korrekte, ausführbare und alleinstehende Anweisung in der gewählten Sprache Ausdruck: Teil eines Satzes, der nicht allein stehen kann und zu bestimmten Wert auswertet Codeblock: Aggregation mehrerer Sätze, die eine semantische Einheit bilden
3 Datentypen Programme basieren immer auf der Manipulation von bestimmten Daten Daten werden beschrieben durch konkrete Werte 27: Eine natürliche ganze Zahl größer Null 'A': Ein Buchstabe "Hello World": Eine Buchstabenkette Je nach Wert ändert sich die Semantik eines Satzes/Ausdruckes: 999 + 1 ==? 'A' + 'B' ==? Programmiersprachen erwarten das Werte kategorisierbar sind Ein Datentyp beschreibt eine Menge von Werten der gleichen Art Der Datentyp legt fest, was als Operation auf seinen Werten erlaubt ist Nur so kann festgestellt werden, ob ein Satz syntaktisch korrekt ist
4 Datentypen Primitiv byte: {-128...127 (8 Bit) short: {- 32768... 32767 (16 Bit) int: {-2.147.483.648... 2.147.483.647 (32 Bit) long: {-9223372036854775808... 9223372036854775807 (64 Bit) (notiert mit L, z.b. 27L) float: {-1,4E-45... 3,4E+38 (32 Bit) (notiert mit f, z.b. 27.5f) double: {-4,9E-324... -1,7E+308 (64 Bit boolean: {true, false char: Unicode-Zeichen (16 Bit) Zusammengesetzt (aktuell) Array: Endliche Menge aus Werten eines bestimmten Typs String: Menge aller Buchstabenkette A { a a T 31 String { c1... cn c char, n int( 2 1)
5 Coercion/Cast Der Compiler wandelt automatische Werte eines kleineren Datentyps in einen größeren (genannt: implizite Typumwandlung, Coercion) Typ (Wert) Mögliche implizite Umwandlung (Variablentyp) byte short char int long float short, int, long, float, double int, long, float, double int, long, float, double long, float, double float, double double Es gibt keine Coersion bei zusammengesetzten Datentypen! Umgekehrt kann es zum Genauigkeitsverlust kommen, was den Compiler daran hindert, die Umwandlung automatisch durchzuführen In diesem Fall, muss der Typ explizit umgewandelt werden (genannt Cast) ( DATENTYP ) WERT AUSDRUCK BEZEICHNER
6 Variablen/Deklaration Deklaration einer Variablen: Falls DATENTYP!= Array DATENTYP Bezeichner ; Falls Array BASISTYP [] Bezeichner ; Zuweisung Falls DATENTYP primitive Bezeichner = WERT AUSDRUCK DATENTYP ; Falls DATENTYP == Array BASISTYP BASISTYP Falls DATENTYP == String [] Bezeichner = { WERT, WERT, ; [] Bezeichner = new BASISTYP [ SIZE ] ; String Bezeichner = " STRING " ;
7 Scope Jede Variable hat einen Gültigkeitsbereich, genannt Scope In Java bilden Codeblöcke Gültigkeitsbereiche, die schachtelbar sind BLOCK = PROLOG { SATZ; BLOCK Der Scope einer Variablen ist der Codeblock, in dem sie definiert wurde { int number = 27; { int secondnumber = number * number ; float number = 37f; float secondnumber = 3.14159265359f; Java ist statisch typisiert // Error! // Ok! SCOPE II SCOPE I Innerhalb eines Scopes kann es keine Deklaration zweier Variablen mit gleichen Namen geben Wenn eine Variable für Werte eines bestimmten Typs deklariert wurde, kann sie innerhalb ihres Scopes keinen Wert eines anderen Typs annehmen
8 Heap vs. Stack Ein Programm läuft im flüchtigen Speicher des Zielsystems Der zugeteilte Programmspeicher ist aufgeteilt in drei Bereiche Programmspeicher: Enthält den Programmcode Stack: Speicher, der linear mit dem Programm auf- und abgebaut wird Enthält die Werte der Variablen des aktuellen Scopes Heap: Random-Access-Speicher Management des Speichers erfolgt durch das Programm Enthält Daten losgelöst vom Scope einer Variablen
9 Pointer Variablen sind Platzhalter für bestimmte Werte Sobald ein Programm eine Variable stößt, wird diese ausgewertet Bei primitiven Variablen, wird diese durch den aktuellen Wert ersetzt Werte zusammengesetzter Datentypen werden immer mit new erzeugt Die Operator new veranlasst, das genügend Platz zum Speichern des Wertes auf dem Heap reserviert wird Eine Variable eines zusammengesetzten Datentyps verweist auf den Speicherbereich, der den Wert enthält Eine solche Variable wird Pointer genannt und ihr Wert ist eine Referenz Erst durch den Dereferenzierung-Operator erhält man Zugriff auf den Wert des zusammengesetzten Datentyps Bezeichner. Attribute Bezeichner. Methode ( Wert Ausdruck, ) Der spezielle Wert null kann nur für Variablen zusammengesetzter Datentypen verwendet werden und bedeutet noch kein Speicher referenziert
10 Kontrollstrukturen Kontrollstrukturen bestimmen den Programmverlauf Bedingte Ausführung if ( BEDINGUNG ) { switch ( AUSDRUCK ) { SATZ; BLOCK case LITERAL 1 : { SATZ; BLOCK else { SATZ; BLOCK optional case break : optional default : { SATZ; BLOCK break optional
11 Kontrollstrukturen Wiederholte Ausführung while ( BEDINGUNG ) { do { SATZ; BLOCK SATZ; BLOCK while ( BEDINGUNG ) ; for ( ZUWEISUNG ; BEDINGUNG ; UPDATE ) { SATZ; BLOCK for ( DATENTYP BEZEICHNER : ARRAY ) { SATZ; BLOCK
12 Methoden Methoden dienen im imperativen Sinne zur Strukturierung des Codes Wiederkehrende und/oder logisch zusammenhänge Codeabschnitte werden zu Methoden zusammengefasst MODIFIKATOREN optional RÜCKGABETYP BEZEICHNER ( DATENTYP PARAM-, ) BEZEICHNER optional { SATZ; BLOCK return AUSDRUCK ; Parameter MODIFIKATOREN SICHTBARKEIT optional SICHTBARKEIT static final public protected private AUSDRUCK RÜCKGABETYP In der imperativen Programmierung: MODIFIKATOREN == static
13 Methoden II Die umschließende Klasse ist der Scope einer Methode class Program { Methoden Signatur einer Methode: RÜCKGABETYP BEZEICHNER ( DATENTYP, DATENTYP, ) In einem Scope kann es keine zwei Methoden mit gleicher Signatur geben Bei Methoden gleichen Namens aber mit unterschiedlichen Parameter spricht man von Überladung einer Methode Aufruf einer Methode BEZEICHNER ( WERT AUSDURCK, ) Argumente Anzahl und Typ der Argumente muss mit Anzahl und Typ der Parameter übereinstimmen
14 Methoden III Methodenrückgabe (Rückgabetype!= void) Methode liefert als Ergebnis ein Wert des angegebenen Rückgabetyps Der letzte ausführbare Satz in einer Methode muss mit return beginnen Auf return folgt ein Ausdruck bzw. Wert des Rückgabetyps Überall dort, wo ein Ausdruck erwartet wird, kann auch ein Aufruf einer Methode erfolgen, die einen Wert des erwarteten Typs liefert static int modulo(int n, int mod) { if (n <= 0 mod <= 0) return -1; if (n == mod) return 0; static int mult(int a, int b) { int result = 0; int temp = 0; while ((temp + mod) <= n) { temp += mod; log("temp", temp); return n - temp; for (int i = 1; i <= a; i++) { result += b; return result; int somevalue = modulo(mult(27, 37), 5);
15 Methoden IV Der spezielle Rückgabetyp void: Methode ohne Rückgabe (meist zum Ausführen von Nebeneffekten ) Es gibt keinen Wert vom Typ void Void-Methoden können daher auch keinen Wert liefern und man kann sie damit auch niemals stellvertretend für Werte benutzen Insb. können sie nie auf der rechten Seite einer Zuweisung aufgerufen werden! Innerhalb der Methode kann man die weitere Ausführung mittels return ohne Wertangabe abbrechen static void dosomething( ) { boolean test = ; if (!test) { System.out.println("Error"); return ;
16 Wiederholung: Call-By-Semantik Bei einem Methodenaufruf wird ein Speicher-Frame auf dem Stack erzeugt Alle Parameterwerte werden an diesen Frame übergeben: Primitive Parameter: Der Aufrufwert wird kopiert und dem entsprechenden Methodenparameter zugewiesen Änderungen haben außerhalb der Methode habe keine Auswirkung Call-By-Value Pointer-Parameter: Die Referenz hinter dem Aufruf-Pointer wird kopiert und dem entsprechenden Methodenparameter zugewiesen Dereferenzierung führt zum gleichen Speicherbereich! Änderungen an referenzierten Daten bleiben bestehen! Call-By-Reference
17 Arrays Definition eines Arrays in Java: DATENTYP [] Bezeichner ; int[] someintarray; Ein Array wird immer in Abhängigkeit zu einem Basistyp definiert Nur Elemente dieses Basistyps können im Array gespeichert werden Statische Initialisierung eines Arrays: DATENTYP [] Bezeichner = { WERT, WERT, ; int[] numbers = {0, 1, 1, 2, 3, 5, 8; Dynamische Initialisierung eines Arrays: DATENTYP [] Bezeichner = new DATENTYP [ SIZE ] ; int[] numbers = new int[7]; Achtung: Die statische Initialisierung ist nur bei der Definition möglich int[] numbers = new int[7]; numbers = new int[] {-1, -2, -3, -5, -8;
18 Arrays II Jedem Element in einem Array ist ein eindeutiger Index zugewiesen Über seinen Index kann auf ein Element zugegriffen oder das zu einem Index gehörende Element überschrieben werden Java kennt nur konsekutive Arrays: Die Array-Elemente sind durchnummeriert I int {0...2147483647 Der Index-Operator [] dient zum Zugriff auf bzw. Setzen von Werten VARIABLE [ INDEX ] int[] numbers = {0, 1, 1, 2, 3, 5, 8; numbers[0] 0 numbers[3] 2 numbers[6] 8 VARIABLE [ INDEX ] = WERT AUSDRUCK DATENTYP ; numbers[0] = 10; numbers[0] == 10 numbers[3] = 999; numbers[3] == 999 numbers[6] = numbers[4]; numbers[6] == 3
19 Strings String ist in Java ein zusammengesetzter Datentyp Variablen vom Typ String sind demnach Pointer Bevorzugt: implizite Erzeugung ohne new: String Bezeichner = " STRING " ; String mystring = "Hello World"; Der + Operator ist für String überladen: System.out.println("Hello" + " " + "World"); Hello World Sobald ein Operand String ist, werden alle automatisch umgewandelt int year = 2014; String somestring = "This is the year " + year; Ok Strings sollten mit equals nicht mit == verglichen werden string1.equals(string2)
20 Projekt: Heap Ziel ist eine interaktive Anwendung, die auf der Konsole Eingaben entgegen nimmt und diese als Operationen auf einem künstlichen Heap interpretiert
21 Netbeans (Neues Projekt) File New Project Categories: Java Projects: Java Application Browse: Project-Name: Vorlesung_6 Nutzerverzeichnis U: Create Main Class: Uncheck
22 Netbeans (Neues Projekt) Netbeans legt im gewählten Verzeichnis folgende Ordnerhierarchie an Die meisten Dateien sind Netbeans spezifisch und nicht weiter interessant Neue Klassen-Dateien werden im src-verzeichnis abgelegt Die Musterlösung befindet sich hier: http://www.unimuenster.de/ziv/lehre/java/index.html Die Java-Datei Vorlesung_6 muss in das src- Verzeichnis kopiert werden Nach dem ersten Ausführen der main-methode findet man den compilierten Code (.class) unter build/classes
23 Read-Eval-Print-Schleife Das Programm Vorlesung_6 ist eine klassische Consolen-Anwendung Read: Einlesen einer Nutzeranweisung Eval: Auswerten der Eingabe Print: Ausgeben des Ergebnisses Zum Einlesen von Eingaben benötigt es einer Hilfsmethode static String readln() { System.out.println("HEAP => "); return new java.util.scanner(system.in).nextline(); Java.util.Scanner ist eine Klasse, von der bei jedem Aufruf von readln() per new ein neues Objekt erzeugt wird Als Eingabe bei der Erzeugung wird System.in verwendet Der Aufruf von nextline lässt die Anwendung solange warten, bis der Nutzer eine Eingabe mit Enter abgeschlossen hat und liefert diese Der Code ist vereinfach, produktiv würde man es anders umsetzen
24 Read-Eval-Print-Schleife (II) public static void main(string[] args) { // Anlegen des Heap-Arrays String[] heap = initarray("", new String[30]); // Hilfsvariablen // Die Variablen hätten auch innerhalb der do-schleife definiert werden können // aber da sie nach einer Iteration erneut zugwiesen werden, kann die Variable // sozusagen wiederverwendet werden, ansonsten würden n-neue Variablen erzeugt! String line; String[] tokens; do { // read line = readln(); // eval + print tokens = parse(line); // parsen der Eingabe zu Tokens heap = interpret ( // interpretieren der Tokens, wobei tokens[0].touppercase(), // das erste Token immer als Befehl shiftarray(tokens), // und die restlichen als Argumente heap // verstanden werden ); while (heap!= null); // Falls die Heap-Variable nach dem letzten // interpret auf null gesetzt wurde => abbrechen System.out.println("Shutdown complete");
25 Debugging Debugging: Analyse des Programmverlaufs zum Aufspüren von Fehlern Bisher: Logging: Ausgabe aktueller Variablen bzw. Zustände auf die Konsole Nachteile: Code wird durch Logging aufgebläht Logging-Befehle müssen irgendwann wieder entfernt werden, da jeder Aufruf unnötig Zeit kostet Keine Liveanalyse : Das Programm muss laufen, den gesuchten Fehler produzieren und erst dann werden die Ausgaben analysiert und das Logging ggf. angepasst Die meisten IDEs bieten Tools zur Liveanalyse Achtung: Logging zur Analyse eines laufenden System ist durchaus legitim, als Debugging-Tool sollte man allerdings nicht verwenden
26 Debugging in Netbeans Der Button (oben neben ) startet die Liveanalyse (ab jetzt: Debugging) Oben erscheinen neue Symbole Stopp die aktuelle Ausführung Pausiert die aktuelle Ausführung Führt das Programm fort, falls es zuvor pausiert wurde Damit gewinnt man allerdings noch nicht viel Per Klick auf eine Zeilennummer im Editor setzt man einen Breakpoint
27 Breakpoints Das Programm hält an, sobald die markierte Zeile ausgeführt werden soll Falls nicht vorhanden sollte man nun das Variablen Fenster aktivieren Man erhält folgende Übersicht, über alle aktuelle aktiven Variablen
28 Breakpoints (II) Neben den Steuerungspunkten oben erscheinen neue Symbole Zur nächsten Zeile springen Zum nächsten Ausdruck springen In den Methodenaufruf springen Zum Aufruf der Methode zurückspringen