Softwaretechnik, Mitschrift 2. Semester Inhaltsverzeichnis Seite 1. EINFÜHRUNG .....................................................................................2 1.1 Eigenschaften von Java ................................................................................. 2 1.2 Erste Beispiele ................................................................................................ 2 2. VARIABLEN, TYPEN UND OPERATOREN ....................................... 7 2.1 Variablen.......................................................................................................... 7 2.2 Der Typ boolean .............................................................................................. 7 2.3 Der Typ byte .................................................................................................... 8 2.4 Besondere Operationen ................................................................................. 9 2.5 Gleitkommatypen .......................................................................................... 11 2.6 Typumwandlung............................................................................................ 12 2.6 String.............................................................................................................. 14 2.7 Arrays............................................................................................................. 15 3. ELEMENTARE JAVA - ANWEISUNGEN.......................................... 18 3.1 Allgemeines................................................................................................... 18 3.2 Methoden ....................................................................................................... 18 3.3 Statements..................................................................................................... 19 3.4 IF – Anweisungen.......................................................................................... 19 3.5 Switch – Anweisung ..................................................................................... 21 3.6 Schleifen ........................................................................................................ 22 3.7 break- und continue – Anweisungen........................................................... 24 3.8 return – Anweisung....................................................................................... 24 4. KLASSEN UND OBJEKTE IN JAVA ................................................ 25 4.1 Objekte........................................................................................................... 25 4.2 Klassen .......................................................................................................... 25 4.3 Deklarationen ................................................................................................ 28 4.4 Methoden ....................................................................................................... 30 4.5 Konstruktoren ............................................................................................... 32 4.6 Schlüsselwort this ........................................................................................ 33 4.7 Gültigkeitsbereich......................................................................................... 34 4.7 Methodenaufrufe und Parameterübergabe................................................. 36 4.8 Objektfreigabe ............................................................................................... 36 © S. Meißner & A.Vogl Seite 1 Softwaretechnik, Mitschrift 2. Semester 1. Einführung Java wurde entwickelt von James Gosling und Bill Joy. 1995 wurde die Software von Sun vorgestellt. 1.1 Eigenschaften von Java Allgemeine Eigenschaften von Java: • • • • • • • objektorientiert Begriffe: Klassen, Objekte, Methoden, Attribute, Vererbung, Polymorphie usw. Plattformunabhängig Sicher Robust Exception Handling (Ausnahmebehandlung) Erweiterbar Multithreaded - quasi-parallele Abläufe Man erstellt einen Java-Bytecode. Zur Interpretation wird die Java virtuelle Maschine (JVM) verwendet. Die Datei Cbeispiel.java wird vom Java-Compiler in Cbeispiel.class gewandelt und vom Java Interpreter ausgelesen. Java besteht aus: • • • der Sprache selbst (Gegenstand der Vorlesung) Java virtuelle Maschine Klassenbibliotheken - sehr umfangreich - mit jar -xvf src.jar die java-Klassen entpacken 1.2 Erste Beispiele Erstellung einer Beispieldatei „Hello World“. a) Eine Klasse mit Notepad schreiben. Kompilieren unter DOS (javac CHello.java). Ausführen unter DOS (java CHello.class). b) Mit JBuilder. Schritt 1: Projekt anlegen. Schritt 2: Neue Klasse zum Projekt hinzufügen. Schritt 3: Klasse schreiben. Schritt 4: übersetzen und ausführen © S. Meißner & A.Vogl Seite 2 Softwaretechnik, Mitschrift 2. Semester © S. Meißner & A.Vogl Seite 3 Softwaretechnik, Mitschrift 2. Semester Projekt Editor Komponenten baum Wir geben nun folgenden Code in den Editor ein: // Paketname package helloword; // Importieren aller Klassen von java.lang import java.lang.*; // Object als Urahn aller Klassen public class CHelloWorld extends Object { public static void main(String args[]) { // Ausgabe auf das Standardausgabegerät System.out.println("Hello World!"); } } © S. Meißner & A.Vogl Seite 4 Softwaretechnik, Mitschrift 2. Semester Einige wichtige Schlüsselwörter: • • • • • • • • Wortzwischenraumzeichen: Leerzeichen, Zeilenbegrenzer (newline) Begrenzer: ( ) [ ] ; . { } Kommentare Anweisungen import-Anweisung Klassendeklaration Methode main Objekt „out“ Kommentare werden im C++ - Stil eingefügt: beginnt mit //. C-Stil beginnt mit /* und endet mit */. Schachtelung nicht erlaubt. Java-doc-Kommentare /** HTML bzw. XML-Text */. © S. Meißner & A.Vogl Seite 5 Softwaretechnik, Mitschrift 2. Semester Generell gilt: Kommentare sind für die Dokumentation und die Lesbarkeit des Programms. Es soll gut dokumentiert sein (20-30% der Zeilen für Kommentare). Kommentare werden durch den Compiler entfernt. Aus Java-doc-Kommentaren werden HTML-Seiten erstellt. Betrachten wir nun näher public static void main(String[] args): public: static: void: main: String[]: args: Zugriffsmodifikator. Alle dürfen diese Methode aufrufen Klassenzugehörigkeit Rückgabewert Methodenname „Typ“ des Parameters Name des Parameters © S. Meißner & A.Vogl Seite 6 Softwaretechnik, Mitschrift 2. Semester 2. Variablen, Typen und Operatoren 2.1 Variablen Es gibt Variablen für unterschiedliche Typen. Wir unterscheiden dabei grundsätzlich zwischen Referenztypen und primitiven Datentypen. Zu diesen gehören Typen gehören byte (8 bit), short (16 bit), char (16 bit), int (32 bit) und long (64 bit), die Gleitkommatypen float und double sowie boolean. Im Allgemeinen werden Variablen wie folgt beschrieben: Typ Typ int int Variablenname; Variablenname = Wert; array [ ] = null; [ ] array = null; // // // // normale Variablendefinition Variablendefinition mit Wertzuweisung Array – Deklaration andere Schreibweise für Arrays Der Zuweisungsoperator ist das = . Dabei gilt folgendes zu beachten: Var = Wert; Var = ´´Referenz´´; String s; s = ´´abc´´; int i = 3; byte b; int i; b = i; // Folgendes geht nicht: // int ist zu groß für byte. Beachte: // linke Seite muss Typkompatibel sein! 2.2 Der Typ boolean Wir können mit boolean numerische Vergleiche durchführen mitteln ==, !=, >, <, >=, <=. Sie liefern stets boolesche Werte true oder false. Es sind folgende Operatoren mit boolean gebräuchlich: &, && ! UND NEGATION |, || ^ ODER EXKLUSIVES ODER Die Operatoren == und != liefern den booleschen Wert als Ergebnis. Bei primitiven Typen wird deren Inhalt verglichen. Bei Referenztypen wird die Adresse verglichen. Beispiel: String a = ´´abc´´; String b = ´´abc´´; if (a == b) { ... } String c = ´´ab´´; c += ´´c´´; if (a == c) { ... } © S. Meißner & A.Vogl // gibt Fehler da Adresse a != b // c = ´´abc´´, aber dennoch Fehler! Seite 7 Softwaretechnik, Mitschrift 2. Semester Wir betrachten folgende Wahrheitstabelle zur Verdeutlichung: A B A && B A || B A ^ B f f t t f t f t f f f t f t t t f t t f Folgendes gilt beim Programmieren zu beachten: if ((args[0] != null) & (args[0].length() != 1)) /** oben werden beide Seiten ausgewertet, untenstehender Syntax ist typisch: */ if ((args[0] != null) && (args[0].length() != 1)) Man kann die folgenden Operatoren auch anders schreiben: if if if if (A || B) {do action ( );} // entspricht: (A) {do action ( );} else if (B) {do action ( );} (A ^ B) { ... } // entspricht: (A) != (B) { ... } 2.3 Der Typ byte byte verwaltet Ganzzahlen von –128 bis 127. Es wird mit 8 bit dargestellt. Wir können uns das folgendermaßen vorstellen: Vorzeichen 0 1 1 0 1 0 0 1 = + 64 + 32 + 8 + 1 = +105 Beim Arbeiten mit Variablen vom Typ byte gibt es allerhand zu beachten: byte a = 5; byte b = 3; byte c = a + b; byte d = 0x80; // Fehler: Implizite Typerweiterung // Fehler: entspricht 128 > byte Java arbeitet mit der impliziten Typerweiterung. Man spricht dabei von Integralpromotion. Das Ergebnis ist vom Typ long, wenn einer der beiden Operanden ebenfalls vom Typ long ist. Sonst ist das Ergebnis int. Daher gibt es oben eine Fehlermeldung. Soll das Ergebnis in einen bestimmten Typ umgewandelt werden, muss gecastet werden. Casting: byte c = (byte) (a + b); byte d = (byte) (0x80); // Fehler: entspricht 128 > byte Es gibt die unären Operatoren +, -, ~ (bitweises Komplement). byte a = ...; byte b = - a; byte c = + b; © S. Meißner & A.Vogl // Fehler: Auch hier findet eine // implizite Typerweiterung statt. Seite 8 Softwaretechnik, Mitschrift 2. Semester byte d = ~ c; // Es muss gecastet werden! Es gibt auch Operationen der Form ++a / --a (Präfix) und a++ / a-- (Postfix). Es findet dabei erst die Inkrementierung statt, dann der Vergleich bzw. analog umgekehrt: int x = 3; int y = 4; int z1 = ++x; int z2 = y++; // z1 = 4, x = 4; // z2 = 4, y = 5; Bei Operationen werden häufig Rechnungen in der Form variable = variable operator wert; benutzt. Dies geht leichter in der Form variable operator= wert; Hierbei wird automatisch gecastet, d. h. die Form steht für variable = (typ) (variable operator wert); Beispiele: for (byte b = 0; b<10; b++) { ... } for (byte b = 0; b<10; b= b+1) { ... } for (byte b = 0; b<10; b += 1) { ... } // Funktioniert! // Fehler: Casting fehlt! // Funktioniert! 2.4 Besondere Operationen Im Folgenden werden wir uns zunächst die Schiebeoperatoren >>, << und >>> ansehen. Danach betrachten wir die Bitoperationen &, |, ^ genauer. Schiebeoperator << x; schiebt alle Bits um x – Stellen nach links. Also: int a = 2; a = a << 1; // a erhält den Wert 4 Man kann sich das folgendermaßen vorstellen: 0 0 0 0 0 0 1 0 << 1 0 0 0 0 0 1 0 0 Was passiert im folgenden Code? int a = 1; a = a << 32; Integer hat 32 Bit, wobei die erste Stelle fürs Vorzeichen reserviert ist. Es kann also nur um 0...32 Bit (bei long: 0...63 Bit) geschoben werden. Die Ausgabe ist demnach 1. Es findet eine Quasi – Modulo – Operation statt: a = a << i ⇔ a = a << (i % 32); bzw. für long: a = a << i ⇔ a = a << (i % 64); Wir schieben nun in byte: byte b = 1; System.out.println (b << 10); die Variable b wird in int konvertiert und erst dann werden die Bit geschoben. Das Ergebnis ist also ein int! Man beachte außerdem: byte b = 1; b = b <<1; © S. Meißner & A.Vogl // gibt Compilerfehler! Seite 9 Softwaretechnik, Mitschrift 2. Semester Schiebeoperator >> x; schiebt das Vorzeichenbit ein um x – Stellen nach rechts, wobei das Vorzeichen erhalten bleibt. Also: int a = -1; a = a >> 1; Man kann sich das folgendermaßen vorstellen: 1 0 0 0 0 ... >> 2 1 1 1 0 0 ... Schiebeoperator >>> x; schiebt x 0 – Stellen von links aus ein und verrückt auch das Vorzeichenbit. Also: int a = 1; a = a >>> 1; Man kann sich das folgendermaßen vorstellen: 1 0 0 0 0 ... >>> 2 0 0 1 0 0 ... Zur impliziten Typerweiterung für byte, short, char: b = (byte) ((b & 0xFF) >> 1); führt leicht zu Fehlern, daher arbeitet man besser mit Methoden. Etwa: b = CshiftUtil.shiftRight (b,1); public class CshiftUtil { public static byte shiftRight (byte b, int n) { return (byte) ((b & 0xFF) >> n); } public static short shiftRight (short s, int n) { return (short) ((s & 0xFF) >> n); } } Wir haben bereits die Vergleichsoperatoren &&, ||, ^ kennengelernt, die den Typ boolean ergeben. Nun betrachten wir die Bitoperatoren &, | genauer. Diese sind ähnlich den Vergleichsoperatoren, nur dass hier bitweise verglichen wird. Zunächst einmal die grundsätzliche Verwendung. Mit & maskiert man und kann Bits ausblenden. Häufig wird dies in Verbindung als >> bzw. >>> - Kombinationen verwendet. Um Bits zu setzen, gebraucht man |, wobei häufig erst ausgeblendet / gelöscht wird und dann gesetzt. Mit ^ wird die Differenz gefunden. Im folgenden Programmbeispiel wird eine Zahl vom Typ byte eingelesen und anschließend wird die binäre Darstellung ausgegeben. Dies geschieht mit Hilfe der Bitoperatoren: public class CandDemo { public static void convert (byte b) { for (int i = 0; i <= 8; i++) { if (b & sMaskArray [i] != 0) System.out.print (1); else System.out.print (0); } } public static final byte sMaskArray [] = {(byte) 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01} © S. Meißner & A.Vogl Seite 10 Softwaretechnik, Mitschrift 2. Semester Was geschieht hier? Zunächst erstellen wir ein Array mit 8 Byte, die sMaskArray: 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 ⇔ (byte)0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01 Jetzt sei b = -3. Dies entspricht dem binären Code 11111101. Jetzt maskieren wir den Code mit der Stelle i im Array entsprechend: i = 0 => & 10000000 Das Ergebnis von 11111101 & 10000000 ist 10000000. Da dies ungleich 0 ist, wird eine 1 ausgegeben. Die Maskierung mit i = 6 wäre 11111101 & 00000010 und ergibt 00000000. Da dies Ergebnis gleich 0 ist, wird eine 0 ausgegeben. Auf diese Weise erhalten wir schließlich den richtigen Binärcode für –3, nämlich 11111101. Wir haben gesehen, wie der Computer „addiert“. Sehen wir uns nun also noch einmal zusammengefasst an, wie der Computer bitweise vergleicht: a b a a a = = & | ^ 01000101; 10010011; b = 00000001; b = 11010111; b = 11010110; // Vor dem Ergebnis stehen noch 24 Einsen, // da in 32 int – Bit kobnvertiert wurde. Zum Abschluss noch zu EXOR ein paar Beispiele: int int a = b = a = a b a b a = = ^ ^ ^ 1; 2; b; a; b; // // // // // entspricht entspricht entspricht entspricht entspricht 0...01 0...10 0...11 0...01, b ist also = 1 0...10, a ist also = 2 2.5 Gleitkommatypen Zu den Gleitkommatypen gehören float (32bit, 7stellig) und double (64bit, 15stellig). Sie werden für Vergleichsoperatoren genutzt. Um diese Typen umzuwandeln, müssen folgende Methoden beachtet werden: String valueOf (float) String valueOf (double) Man benutzt unäre Operatoren +, - oder arithmetische Operatoren +, -, *, /. Auch Inkrementrechnungen ++, --, auch als Post- und Präfix, sind möglich. Hinzu kommen die speziellen Werte +0.0, -0.0, +∞, -∞ (+ Infinity, - Infinity), sowie NaN (not a number, z. B. – 1, da dies keine reelle Zahl ist!): Double.isNaN (double d) Float.isNaN (float f) © S. Meißner & A.Vogl // Ausgabe boolean Seite 11 Softwaretechnik, Mitschrift 2. Semester Beispiele: ... double d = -1.0; d = Math.sqrt (d); // squareroot (Quadratwurzel) if (Double.isNaN (d)) { System.out.println (“Wurzel ziehen nicht möglich”); } ... System.out.println (1/0); // java.lang.ArithmeticException System.out.println (1.0/0); // Ausgabe: Infinity Bei Ganzzahlen kommt es bei einer „durch 0” – Operation zu einer Exception. Bei Gleitkommazahlen ist dies möglich als Infinity. ... double a = 1.1; if (3 * a == 3.3) System.out.println (´´gleich´´); else System.out.println (´´ungleich´´); Als Ausgabe wird ungleich erscheinen. Der Grund ist, dass der PC 1.1 + 1.1 + 1.1 rechnet und dabei einen Rundungsfehler macht: 3.3000000000000003 und das ist ungleich 3.3! Auch hier rechnet man besser mit Math – Methoden: if ( 3 * a == 3.3) { ... } // Fehler! Besser mit Methode: public static final double EPSILON = 1.0e – 20; if (Math.abs (x-y) < EPSILON) // quasi gleich { ... } 2.6 Typumwandlung Beispiele: int i = System.in.read ( ); char c = (char) i; // -1 für Dateiende // explizite Typumwandlung Implizite Typumwandlung: evtl. Informationsverlust byte short int long float double char Beispiel: byte b=1; b += 1.6; © S. Meißner & A.Vogl // entspricht b = (byte) (b + 1.6) Seite 12 Softwaretechnik, Mitschrift 2. Semester Byte b wird in double umgewandelt, erst dann werden 1.6 addiert und durch abschneiden zurück gecastet. Jeder Ausdruck hat einen Wert. Was, wenn Typ S erwartet wird aber Typ T kommt? Entweder Kompilierfehler: int a = 1; if (a) {...} // boolean – Bedingung, aber a = int! oder implizite Typumwandlung (elementare Typerweiterung, auch bei Referenzen): a op = b; b <<= 1; String s = ´´abc´´; Object o = s; Typverkleinerungen „cast”. Beispiel: (Typ)(Ausdruck); int i; byte b = (byte) i; b = (byte) i+1; b = (byte)(i+1); // hier Kompilierfehler // richtig Was geschieht bei der Umwandlung nach String? int i = 5; String s; s = i; s = String.valueOf (i); s = ´´´´ + i; // // // // Fehler: i erst in Objekt umwandeln! richtig richtig, da String + i also interner Aufruf von valueOf String t; Double d = new Double (1.0); System.out.println (d); // interner Aufruf toString t = d; // Fehler da unterschiedliche Typen t = d.toString (); // richtig Konversionskontexte: bei Zuweisungen (implizit): • elementare Typvergrößerung • „Vergröberung / Vergrößerung bei Referenzen” elementare Typverkleinerungen wenn • Ausdruck konstant • Variablen nur vom Typ byte, short, char • Wert des Ausdrucks liegt im Wertebereich der Variablen (daher byte b = 1; möglich!) => „Zuweisungskompatibel“ bei Methodenaufrufen: © S. Meißner & A.Vogl Seite 13 Softwaretechnik, Mitschrift 2. Semester • nur Vergrößerung: public static void f (byte b) {…} public static void f (float f) {…} f (5); // in int f (0.0); // double => Compilerfehler f (1.0F); // in float 2.6 String Wir erzeugen einen String über Zeichenliterale String s = ´´Java``; oder über Konstruktoren: char [] charArray = {´J´,´a´,´v´,´a´}; String s = new String (charArray); Wir wollen nun Strings miteinander vergleichen. byte b [] = {1, 2, 3, 4}; String t = new String (b); String v = new String (t); if (v == t) { ... }; // ASCII – Unicode! // Vergleich möglich, da v, t auf das // gleiche Objekt zeigen Nun sehen wir uns einige String – Methoden an: char charAt (int index); int length (); boolean equals (String other); int compareTo (String other); // >0 wenn alphabetisch größer // ==0 wenn gleich // <0 sonst public static String valueOf (byte) { ... } int indexOf (char c) // sucht Zeichen, // -1 falls nicht vorhanden, // sonst Ausgabe des 1. Index Wir wollen nun überprüfen, ob eine Eingabe eine Zahl ist: public static boolean isDigit (char c) { if (c >= ´0´&& c <= ´9´) return true; else return false; } // oder: { return (´´0123456789´´.indexOf ( c ) != -1); } Wie fügt man zwei Variablen des Typs Strings zusammen? String.concat (String other); String a = ´´abc``; String b = ´´def´´; String c = a.concat (b); // oder kurz: a + b; void trim (); // entfernt „überflüssige“ Zeichen, wie Leerstellen © S. Meißner & A.Vogl Seite 14 Softwaretechnik, Mitschrift 2. Semester Im nächsten Beispiel soll eine Zeichenkette rückwärts wieder ausgegeben werden. public static String createReverse (String s) { String result = new String (); for (int i = s.length()-1; i >= 0; i--) result +=(s.charAt(i)); } Hier werden jedoch viele Objekte angelegt. Daher ist folgende Version ein besserer Syntax: char tmpArray [] = new char [s.length()]; for (int i = s.length() –1, j = 0; i >= 0; i--, j++) { tmpArray [j] = s.charAt (i); } return (new String(tmpArray)); Damit sind wir bei den Arrays angelangt, die im folgenden Kapitel genauer untersucht werden sollen. 2.7 Arrays Arrays sind Java – Objekte. Sie werden dynamisch erzeugt und haben eine Referenz. Wir definieren zunächst eindimensionale Arrays: Type [] identifier; Type identifier []; int [] intArray; System.out.println (intArray [0]); // Fehler: Nullpointer – Exception identifier = new Type [size]; int Array = new int [3]; // reserviert drei Speicherplätze Ein Array kann auch folgendermaßen deklariert werden: int c []; c = new int [] {2, 3, 4]; Bei Arrays mit Strings müssen zusätzliche Dinge beachtet werden: String sArray [] = new String [3]; System.out.println (sArray [0]); // gibt null aus sArray [i]; sArray [0] = ´´a´´; sArray [1] = ´´b´´; sArray [2] = ´´c´´; int aArray [] = {1, 2, 3}; byte bArray [] = {0, 1}; String sArray [] = {´´abc´´, ´´def´´, new string (bArray)}; sArray.length; // als Attribut, Eigenschaft Für den Index byte, short, char und int (nicht long!) gelten alle aufgeführten Eigenschaften für Arrays. Bei Zuweisungen gilt folgendes: int c [] = aArray; c [0] = 3; // ändert nun auch aArray! System.out.println (aArray [0]); final int aArray [] = {1, 2}; © S. Meißner & A.Vogl Seite 15 Softwaretechnik, Mitschrift 2. Semester aArray [0] = 5; // final schützt nur Arrayname! aArray = x; // geht nicht wegen final! // es geht auch: int aArray [] = {1, 2, 3}; int bArray [] = {0, 1}; bArray = aArray; Längenangaben sind immer ≥ 0. Man kann also auch Arrays der Länge 0 erstellen: double d [] = new double [0]; Zugriff auf Arrays erlangt man mit dem Attribut length oder einem Indexoperator. Dabei gilt, für einen illegalen Index int die ArrayIndexOutOfBoundsException. int a [] = {1, 2, 3}; int b []; b = a; b [0] = 5; System.out.println (a[0]); // Ausgabe „5“ Nach dieser Methode zeigt die Variable b auf die gleiche Speicheradresse wie a. Wir wollen nun aber ein Array kopieren, d. h. zwei Variablen zeigen auf zwei Speicheradressen gleichen Inhalts. Dafür gibt es drei verschiedene Verfahren: int a [] = {1, 2, 3}; // Möglichkeit 1: Lösung mit einer for - Schleife b = new int [a.length]; for (int i = 0; i <= a.length; i++) b [i] = a [i]; ... // Möglichkeit 2: Arrays sind Objekte und haben daher auch Methoden b = (int[]) a.clone(); ... // Möglichkeit 3: die schnellste Methode vom Typ: System.arraycopy (vonArray, vonIndex, nachArray, nachIndex, Anzahl); // also b = new int [a.length]; System.arraycopy (a, 0, b, 0, a.length); Betrachten wir nun mehrdimensionale Arrays. Diese sind von folgendem Typ: int a [] []; a = new int [2] [3]; Ein zweidimensionales Array kann man sich wie folgt vorstellen: a Typ von a [0] ist int [], also z. B. a [0] [1];. Weitere Beispiele für mehrdimensionale Arrays: © S. Meißner & A.Vogl Seite 16 Softwaretechnik, Mitschrift 2. Semester double b [] double b [] b [0] = new b [1] = new int c [] [] int c [] [] [] = new double [2] []; [] = new double [] [3]; // Fehler! double [3]; double [10]; = { {1, 2, 3}, {4, 5}, {} }; = { {1, 2, 3}, {4, 5}, new int [20] }; Im letzten Beispiel zeigt das Array Integer c auf das Array der 1. Dimension, das auf drei andere Arrays in der 2. Dimension zeigt, mit den jeweils entsprechenden Inhalten. Betrachten wir ein Beispiel für das Arbeiten mit Arrays: int a [] [] = new int [2] []; for (int i = 0; i < a.length; i++) { a [i] = new int [i + 1] } int b [] []; b = (int [] []) a.clone; // wir haben nun das 1. Array geklont, es zeigt aber auf die gleiche // Speicheradresse wie das Array a. Deswegen: for (int i = 0; i <= a.length; i++) { b [i] = (int []) a[i].clone []} ... Im folgenden Beispiel geben wir eine Wahrheitstabelle aus: public static void inc (boolean [] b) { boolean c = true; for (int i = b.length-1; i >=0 && c; i--) { if (b [i] == true) { b [i] = false;} else { b [i] = true; c [i] = false; } } } ... while b [0] == false { print (b) inc (b); } ... © S. Meißner & A.Vogl Seite 17 Softwaretechnik, Mitschrift 2. Semester 3. Elementare Java - Anweisungen 3.1 Allgemeines Die leere Anweisung besteht nur aus einem Semikolon: for (int i = 1; i < 10; i++); i = 1; System.out.println (´´hello´´); for (int i = 1; i < 10; i++) { ; } // entspricht: // Anwendung als Warteschleife: while (b) {;} Wir haben schon diverse Ausdruck – Anweisungen kennen gelernt: a = s; i++; --i; methode (param1, ...) { ... } klassenname.methode (...); // // // // // Zuweisung Inkrement (Präfix) Dekrement (Postfix) Methodenaufruf mit Wertübergabe z. B. System.out.println (...); Mit new erzeugen wir ein neues Objekt. Beispiele: new String (); String a = new String (´´abc´´); String b = new String (a); Häufig stehen Anweisungen als Blockanweisung in geschweiften Klammern. Sehen wir uns dazu die if – Anweisung an: if (Bedingung) Anweisung; if (Bedingung) { Anweisung }; if (Bedingung1) { Anweisung1 }; else { Anweisung2 }; 3.2 Methoden Java – Programme bestehen “weitgehend” aus Methoden. Methode ist eine Folge von Anweisungen (Befehle). Anweisungen sind mit Semikolon abzuschließen (Ausnahme: Blockanweisungen { ... } ). © S. Meißner & A.Vogl Seite 18 Softwaretechnik, Mitschrift 2. Semester 3.3 Statements Man unterscheidet den empty statement ; oder { } und den block statement: { } block statement Genauer betrachtet sieht ein block statement wie folgt aus: lokale Var, Deklaration, Statement statement Zu den Ausdrucksanweisungen (expression statements) gehören: • Zuweisungen (assignments) • De- / Inkrementierung (++ / --) • Methodenaufrufe (method inrocation) => doIt (); • Erzeugung von Objekten (class instance creation ) => new String (´´abc´´); 3.4 IF – Anweisungen Struktur: if (Bedingung) { Anweisung 1 } else if (Bedingung) { Anweisung 2 } else { Anweisung 3 } Man kann einen Algorithmus wie folgt notieren: PAP (Programm – Ablauf – Plan) Struktogramm Ausdruck Ausdruck Anw. 1 © S. Meißner & A.Vogl true Anw. 2 false Anw. 1 Anw. 2 Seite 19 Softwaretechnik, Mitschrift 2. Semester Beispiel 1: Berechne aus 2 gegebenen Gleichungen x und y. Also: Wenn ax + by = c und für dx + ey = f, dann ist x = (c * e – b * f) / nenner; y = (a * f – c * d) / nenner. public static final double Espilon = 1.0 E –15; public static double [] calc (double a, double b, double c, double d, double e, double f) { double nenner = a * e – b * d; if (Math.abs(nenner) > Espilon) { double sol [] = new double [2]; sol [0] = (c * e – b * f) / nenner; sol [1] = (a * f – c * d) / nenner; return sol; } else return null; } Beispiel 2: Berechne den Absolut – Betrag einer Zahl x. public static double abs (double x) { if (x >= 0) return x; else return –x; } Beispiel 3: Erstelle eine Punkte – Note – Liste. if (points > 80) grade = 1; else if (points > 60) grade = 2; else if (points > 40) grade = 3; else if (points > 20) grade = 4; else grade = 5; Beispiel 4: Berechne die Fakultät der Zahl n!. public static long fakultaet (int n) { if (n == 0) return 1; else return x * fakultaet (n – 1); } Das obere Beispiel ist eine rekursive Funktion. Die Berechnung für z. B. fakultaet (4) ist 4 * fak (3) = 4 * 3 * fak (2) = 4 * 3 * 2 * fak (1) = 4 * 3 * 2 * 1 * fak (0) = 4 * 3 * 2 * 1* 1 © S. Meißner & A.Vogl Seite 20 Softwaretechnik, Mitschrift 2. Semester 3.5 Switch – Anweisung Betrachten wir die Struktur der Switch – Anweisung als PAP: Auswahl Anw1 Anw1 Anw1 Anw1 Die Switch – Anweisung funktioniert nur mit byte, short, char und int: switch (Ausdruck) { case fall1: Anw1; case fall2: Anw2; ... default: Defaultanweisung; } Die Konstanten fall1, fall2, ... müssen zuweisungskompatibel zum Ausdruck sein. Keine der zwei case – Markierer dürfen den gleichen Wert haben. Default darf auch nur einmal vorkommen! int a; switch (a) case case ... case { 0.0: ...; 1: ...; // nicht erlaubt! 1: ...; // nicht erlaubt! } Beispiel: Wir erstellen eine Methode für ein Kalenderprogramm. public static int calcDays (int theMonth, boolean isLeapYear) { int days = 31; switch (theMonth) { case 4: case 6: case 9: case 11: days = 30; break; case 2: days = (isLeapYear ? 29 : 28); break; default: days = 31; } return days; } © S. Meißner & A.Vogl Seite 21 Softwaretechnik, Mitschrift 2. Semester Beispiel: Für eine Zahl geben wir einen Text aus. String txt; switch (zahl) case 0: ... case 9: default } { txt = ´´null´´; break; txt = ´´nein´´; break; txt = ´´Fehler!´´; Besser zu handhaben ist in diesem Fall eine IF – Array – Kombination: final String txtArray [] = {´´null´´, ´´eins´´, ...}; if (zahl >= 0 && zahl <= 0) txt = txtArray [zahl]; else txt = ´´fehler´´; 3.6 Schleifen Zunächst betrachten wir die do – while – bzw. while – Schleife. Diese beiden Schleifentypen unterscheiden sich insofern, als dass die do – while – schleife auf jeden Fall einmal ausgeführt wird. Syntax: while (Bed) { Anweisung (dabei wird Bed. geändert) } do { Anweisung } while (Bed); ... while (x < 10) x++; while (x < 10) {x++} // identisch! ... int querSumme = 0; while (x != 0) { querSumme += (x % 10); x /= 10; } Sehen wir uns nun die for – schleife an. for ( ForInit ; Expr ; ForUpdate ) Statement Die „leere“ for – Schleife entspricht einer Endlosschleife while (true) { ... }: for ( ; ; ) { ... } Wir sehen uns nun noch genauer die ForInit an: , StatementExpression © S. Meißner & A.Vogl Seite 22 Softwaretechnik, Mitschrift 2. Semester LocalVariableDeclaration Beispiele: for (int i = 0, j, float=0.0; ; ) { ... } for (a+b; x <= 10; x++) // oben: Fehler => keine Ausdrucksanweisung! int b; for (int a = 5, b = 3; ; ) { ... } // oben: Fehler => Deklaration und Ausdruck nicht erlaubt! for ( ; x != 0; x /= 10) { querSumme += (x % 10); } Wir sehen uns nun die einzelnen Notationen an. while do – while for Schleife Ausdruck Schleife Ausdruck AW=...SW=...EW=... Anweisung Anweisung Anweisung Ende Schleife Ausdruck, Ende Schleife Ende Schleife Ausdruck Anweisung Anweisung Schleife for (start,end,schritt) Ausdruck Anweisung Beispiel: static final double Epsilon = 1.0 e 10; public static double sqrt (double x) { double links = 0; double rechts = x; while (rechts – links > Epsilon) { double tmp = (rechts + links) / 2; if (tmp * tmp > x) rechts = tmp; else links = tmp; } return links; } © S. Meißner & A.Vogl Seite 23 Softwaretechnik, Mitschrift 2. Semester 3.7 break- und continue – Anweisungen Wir sehen uns die Struktur an: break ; identifier continue ; identifier Der „identifier“ wird auch als Sprungmarke bezeichnet. Eine mit Label versehene Anweisung sollte vermieden werden, weshalb hier auch nicht näher darauf eingegangen werden sollte. 3.8 return – Anweisung return ; Expression © S. Meißner & A.Vogl Seite 24 Softwaretechnik, Mitschrift 2. Semester 4. Klassen und Objekte in Java 4.1 Objekte Ein Objekt wird beschrieben durch Zustand und Verhalten. Der Zustand wird bestimmt durch Daten / Attribute. Verhalten wird bestimmt durch Methoden. public class CPoint { private int mx; private int my; public CPrint () { mx = my = 0; } public void setx (int thex) { mx = thex; } public void sety (int they) { my = they; } public int get x () { return mx; } public int gety () { return my; } public void mor (int dx, int dy) { mx += dx; my += dy; } public String toString () { return ´´(´´ + mx + ´´,´´ + my + ´´)´´; } } Objekte besitzen also Daten / Attribute / Zustände, ein Verhalten sowie eine Identität. 4.2 Klassen Klassen beschreiben Objekte. Sie werden auch „abstrakte Datentypen“ genannt, da mit ihnen Objekte erzeugt werden. Wir wollen zunächst ein Rechteck konstruieren. Dafür sind zwei Objekte ausreichend, da wir nur den linken oberen und den rechten unteren Punkt mithilfe zweier Variablen erzeugen müssen. Der Syntax könnte also wie folgt aussehen: public class CRectangle { private CPoint mUpperLeft; private CPoint mLowerRight; public CRectangle () { mUpperLeft = new CPoint (); mLowerRight = new CPoint (); } } Wir wollen in einem großen Beispiel das Zusammenspiel von Klassen und Objekten verstehen. Dazu werden wir eine Klasse für rationale Zahlen erstellen: • wir benötigen int zaehler; und int nenner; • ein Objekt „erzeugt / initiallisiert“ wird benötigt • Addition, Multiplikation, Subtraktion und Division sollen möglich sein • Vergleiche von rationalen Zahlen werden durchgeführt • Ausgabe mittels der Methode toString © S. Meißner & A.Vogl Seite 25 Softwaretechnik, Mitschrift 2. Semester public class CRational extends java.lang.Object // 1 private int zaehler; private int nenner; public CRational () { // 2 zaehler = 0; nenner = 1; } public CRational (int z, int nenner) { // 3 zaehler = z; this.nenner = nenner; // 4 normalize (); // 5 } public String toString () { return zaehler + ´´/´´+ nenner; } private void normalize () { boolean isNegative; if (zaehler < 0) { isNegative = true; zaehler = - zahler; } if (nenner < 0) { isNegative = !isNegative nenner = - nenner; } int ggT = ggT (zaehler, nenner); zaehler /= ggT; nenner /= ggT; if (isNegative) zaehler = - zaehler; } public static int ggT (int a, int b) { // 6 int tmp; while (b != 0) { tmp = a % b; a = b; b = tmp; } return a; } public void add (CRational other) { // 7 this = add (this other); } public static CRational add (CRational r, CRational s) { // 8 int z = r.zaehler * s.nenner + r.nenner * s.zaehler; int n = r.nenner * s.nenner; return new CRational (z, n); } ... // 9 public int compareTo (Crational other) { return sub (this, other).zaehler; // 10 } public boolean equals (Object other) { if ((other instanceOf (CRational)) == false) return false; return compareTo ((CRational).other) == 0; // 11 } } public class CtestRational { public static void main (String args []) { ... } } © S. Meißner & A.Vogl // 12 Seite 26 Softwaretechnik, Mitschrift 2. Semester Bemerkungen: 1. Diese Klasse einzubinden ist überflüssig, da sie quasi die „Mutterklasse“ aller Klassen ist. 2. Dieser Konstruktor hat den gleichen Namen wie die Klasse, da dies die Methode ist, mit der wir das Objekt erzeugen. Sie wird aufgerufen, wenn keine Variablen übergeben werden, vom Typ: CRational r = new CRational (); 3. Hier gilt das gleiche wie bei 2). Nur wird dieser Konstruktor aufgerufen, wenn die Syntax vom Typ ist: r = new CRational (-5, 7); 4. this deutet Java an, dass es sich um die Variable nenner aus dem Objekt private nenner; handelt. 5. Diese Methode werden wir noch schreiben, um den ggT für die rationale Zahl zu finden. 6. Es handelt sich hier um eine „statische“ Methode, d. h. sie ist an ein Objekt gebunden, da sie nur hier „interessant“ bzw. relevant ist. 7. Der Aufruf muss vom Typ sein: r.add (t); 8. Dieser Aufruf ist vom Typ: u = add (r, s); 9. Analog werden Methoden für Subtraktion, Multiplikation und Addition erstellt. 10. Es handelt sich bei sub (this, other) um ein „anonymes Objekt“. 11. Diese ungewöhnlich wirkende Form ist lediglich ein cast – Operator in CRational, welches sich ebenso verhält wie beim Casting nach int. 12. Um unsere Klasse zu überprüfen, ob sie so funktioniert, wie wir es uns wünschen, schreiben wir eine Testklasse, die lediglich eine main – Methode enthält und auf die Klasse zugreift und mit ihr arbeitet. © S. Meißner & A.Vogl Seite 27 Softwaretechnik, Mitschrift 2. Semester 4.3 Deklarationen Nun wollen wir uns mit Klassen näher beschäftigen. Klassen können entsprechend ihres Nutzens modifiziert werden: public class private • • • • protected final abstract Identifier Body public ist eine öffentliche Klasse private wird nur in einer Klasse verwendet protected wird nur in einer Klasse verwendet final kann nicht vererbt werden Sind Klassen in einem package zusammengefasst, sind die einzelnen Klassen für alle immer sichtbar. Der Body steht in geschweiften Klammern. Diese können entweder leer stehen oder mit Inhalten besetzt sein: { } Method Declaration Field Declaration class Declaration Stratic Initializer Für die Variablen gelten verschiedene Zugriffsmodifikationen. Insgesamt sind es die acht Methoden public, private, protected, final, static, volatile, tranisent oder ohne. Außerdem gibt es noch native, abstract und synchronized. Wir wollen nun ein Stack für Integer anlegen: CStack • • int mStack [ ] int mTopIndex << constructor >> push (int) • • • © S. Meißner & A.Vogl pop ( ): int peek ( ): int isEmpty ( ): boolean Seite 28 Softwaretechnik, Mitschrift 2. Semester public class CStack { private int mStack []; private int mTopIndex; public CStack (int theSize) { mStack = new int [theSize]; mTopIndex = -1; } public void push (int val) { mTopIndex ++; mStack [mTopIndex] = val; } public int pop () { int val = mStack [mTopIndex]; mTopIndex --; return val; } public int peek () { return mStack [mTopIndex]; } public boolean isEmpty () { return (mTopIndex == -1); } } public static String reverse (String txt) { CStack stack = new CStack (txt.length); for (int i = 0; i < txt.length(); i++) stack push (txt.charAt (i)); String buffer b = new String Buffer (); while (stack.isEmpty() == false) { char d = (char) stack.pop (); b.append (d); } return b.toString (); } Variablen können also auch nicht nur verschiedenen Typen haben, sondern zusätzlich deklariert sein: Type Var Declarations ; private public protected final static volatile © S. Meißner & A.Vogl private int a; int b = 5; public int c [] = new int [6]; private long a [] = {1, 2, 3}; private JButton buttonArray [] { new JButton (´´button1´´); new Jbutton (´´button2´´); } Seite 29 Softwaretechnik, Mitschrift 2. Semester 4.4 Methoden Eine Methode hat einen Block für die Implementierung und eine Signatur (bestehend aus Name der Methode und Anzahl und Typen der Parameter (eventuell auch mit Typ des Rückgabewerts)): Typ ( identifier ) Formal Parameter List void public protected private static final ; } abstract { throws Block Statements synchronized native • • • • void wird benutzt, wenn kein Rückgabetyp erwartet wird. Die Klammer () kann sowohl leer bleiben als auch mit einem FormalParameterList besetzt werden. static signalisiert eine Methode für eine Klasse, nicht für ein Objekt. native bedeutet, dass das Programm nicht in Java geschrieben ist. Sehen wir uns nun throws genauer an: throws ClassType , © S. Meißner & A.Vogl Seite 30 Softwaretechnik, Mitschrift 2. Semester Methoden können überladen werden, d. h. verschiedene Methoden haben gleiche Namen. Ein Beispiel dafür: public class CRectangel { public boolean contains (CPoint p) { ... } public boolean contain (float x, float y) { ... } } Im Folgenden Beispiel entsteht ein Fehler: void move (int x, int y) { ... } static void move (int x, int y) { ... } // Methode für ein Objekt // Methode für eine Klasse Der Compiler kann die Signaturen nicht unterscheiden. Auch nimmt der Compiler keine Rücksicht auf den Rückgabewert, weshalb auch im Folgenden ein Fehler entsteht: int move (int x, int y) { ... } Das folgende Programm enthält ebenfalls einige Fehler: public class CTest { void doIt (byte a, int b) { ... } void doIt (int a, byte b) { ... } public static void main (String args []) { byte a, b; ... doIt (a, b) // Fehler, da kein Objekt vorhanden ist CTest obj = new Ctest (); obj.doIt (a, b); // Fehler, da nicht eindeutig obj.doIt (5, 6); // impl. Typerweiterung auf 2 Integer obj.doIt (1, b); // Aufruf 2. Methode, da int, byte © S. Meißner & A.Vogl Seite 31 Softwaretechnik, Mitschrift 2. Semester 4.5 Konstruktoren Konstrukturen werden benötigt, um Objekte zu initialisieren. Der Name des Konstruktors ist identisch zum Klassennamen und hat keinen Rückgabetyp. Wenn kein Kontrsuktor deklariert wurde, fügt der Java – Compiler einen „Standardkonstruktor“ hinzu. Im Folgenden ein Vergleich ein und desselben Quellcodes – vor und nach der Compilerbearbeitung: public class Ccircle { private double mX; private double mY; } public String toString () { return mX, my; } import java.lang.*; public class Ccircle extends java.lang.Object { private double mX; private double mY; public CCircle () { super (); } public String toString () { return mX, my; } } Im Übrigen ist hier die Ausgabe 0.0 0.0. Ein weiteres Beispiel: public class CCircle { private double mX, mY; public void CCircle () { // Fehler, da wegen void Methode mX = mY = 0; } public CCircle CCircle () { mX += 1; mY += 1; return this; } public String toString () { return mX, mY; } public CCircle (double x, double y) { mX = x; mY = y; } public static void main (String args []) { CCircle c = new CCircle (); System.out.println (new CCircle().CCircle()); } } Für den Konstruktor wird new benötigt. Dieses legt Speicher an, füllt diesen zunächst mit „0“ und ruft den Konstruktor auf. Man kann es daher auch „Initialisierer“ nennen: public class CTest { private JButton mButton = new JButton (´´hello´´); public CTest ( ... ) { mButton.setBackground (Color.red); } } © S. Meißner & A.Vogl Seite 32 Softwaretechnik, Mitschrift 2. Semester 4.6 Schlüsselwort this Objekte haben auch „Identitäten“ (also Vergleichsreferenzen, Adressen, u. ä.). Doch manchmal ist das Problem, welches Objekt vorliegt. private int mX; public void set (int mX) { this.mX = mX; } public void otherMethod () { ... this.set (5); ... } this ist nur bei Konstruktoren erlaubt. Es ist quasi eine Referenz zu dem Objekt selbst. Noch ein Beispiel: public class CTest { public CTest () { ... this (0, 0); ... } public CTest (int a, int b) { ... this (); ... } Die beiden Methoden rufen sich gegenseitig auf und erzeugen somit eine Endlosschleife! © S. Meißner & A.Vogl Seite 33 Softwaretechnik, Mitschrift 2. Semester 4.7 Gültigkeitsbereich Der Gültigkeitsbereich legt fest, in welchem Codeabschnitt die Variablen verwendet werden. Dabei unterscheidet man: 1. formaler Parameter: Die Variable wird nur in der entsprechenden Methode benutzt. void doIt (int a) { ... } 2. lokale Variable: Die Variable ist nur in dem Block gültig, wo sie definiert wurde. 3. Attribute: Attribute haben ihre Gültigkeit in der Klasse mit Ausnahmen von Klassenmethoden. Dazu zwei Beispiel: public class CClass { int x, y; public static void main (String args []) { x = 1; // Fehler! CClass c; c.x = 1; // Funktioniert! }} public class CScopeDemo { static int sGlobal = 0; int mVar = 10; void print1 () { int mVar = 20; int sGlobal = 1; System.out.println (mVar + sGlobal + this.mVar + CscopeDemo.sGlobal); } void print2 () { System.out.println (mVar + sGlobal); } } Im letzten Beispiel wäre die Ausgabe für print1 20 1 10 0 und für print2 10 0. Auf der nächsten Seite folgt ein letzten Beispiel. Es besteht aus zwei Teilen, wobei der zweite Teil identisch mit dem ersten ist, nur wurden die Variablen umbenannt, was, wie man sieht, deutlich der Übersichtlichkeit dient. © S. Meißner & A.Vogl Seite 34 Softwaretechnik, Mitschrift 2. Semester class Point { int x, y; } class Test { static Point Point (int x, int y) { Point p = new Point (); p.x = x; p.y = y; return p; } public static void main (String args []){ int Point; Point pa [] = new Point [2]; for (Point = 0; Point < 2; Point ++) { pa [Point] = new Point (); pa [Point].x = pa [Point].y = Point; } System.out.println (pa [0].x + ´´ ´´ + pa [0].y); System.out.println (pa [1].x + ´´ ´´ + pa [1].y); Point p = Point (3, 4); System.out.println (p.x + ´´ ´´ + p.y); } } Die Ausgabe ist in der ersten Zeile 0 0, in der zweiten Zeile 1 1 und schließlich 3 4. Warum das so ist wird deutlich, wenn wir die Variablen umbenennen: class Point { int x, y; } class Test { static Point f (int x, int y) { Point p = new Point (); p.x = x; p.y = y; return p; } public static void main (String args []){ int c; Point pa [] = new Point [2]; for (i = 0; i < 2; i ++) { pa [i] = new Point (); pa [i].x = pa [i].y = i; } System.out.println (pa [0].x + ´´ ´´ + pa [0].y); System.out.println (pa [1].x + ´´ ´´ + pa [1].y); Point p = f (3, 4); System.out.println (p.x + ´´ ´´ + p.y); } } © S. Meißner & A.Vogl Seite 35 Softwaretechnik, Mitschrift 2. Semester 4.7 Methodenaufrufe und Parameterübergabe Wir sehen uns die Parameterübergabe an einigen Beispiel an. Im ersten Beispiel wird die Variable int a; unerlaubterweise überdeckt: void print (int a, String s) { int a; ... } ... print (5, ´´abc´´); ... Im zweiten Beispiel werden die Variablen in der Methode swap vertauscht, wegen des Parameters sind sie im Anschluss jedoch wieder „normal“: int a, b; public void swap (int a, int b) { int tmp = a; a = b; b = tmp; } ... a = 5; b = 7; swap (a, b); ... Nun legen wir Werte in die Onjekte b1 und b2 ab – die Vertauschung findet dauerhaft statt. public void swap (JButton b1, JButton b2) { JButton tmp = b1; b1 = b2; b2 = tmp; } 4.8 Objektfreigabe Wir legen ein Objekt an und weisen diesem keinen Wert (null) zu: JButton b1 = new JButton(); ... b1 = null; Hier wird der sogenannte Garbage Collector aktiv, der „überflüssige“ Daten löscht. protected void finalize () throws Throwable = null; System.gc (); // Hinweis für den Garbage Collector! Die finalize – Methode wird aufgerufen, bevor das Objekt freigegeben wird. Nach dem beenden des Programms werden alle Ressourcen freigegeben. Nach dem Aufruf von finalize wird erneut überprüft, ob das Objekt referenziert wird. Falls nicht, wird es freigegeben. Achtung: Der Garbage Collector ist vorsichtig einzusetzen! © S. Meißner & A.Vogl Seite 36