Grundlagen der Programmierung –Teil1 Einheit 1I - 15. Okt. 2010 GDP DDr. Karl D. Fritscher basierend auf der Vorlesung “Grundlagen der Programmierung” von DI Dr. Bernhard Pfeifer Variablen • Variablen dienen dazu, Daten an einer bestimmten Stelle im Hauptspeicher eines Programms abzulegen und gegebenenfalls zu lesen oder zu verändern. Eine Variable hat 4 Kennzeichen: • Variablenname: (mehr oder weniger) beliebiger Name (Tipps zur Benennung von Variablen beachten!) • Datentyp: Legt fest, welche Operationen auf einer Variable möglich sind und wie die Darstellung der Variablen im Speicher erfolgt (mehr dazu auf den nächsten Folien) • Wert: Der Wert muss der Variablen in der Regel explizit zugewiesen werden. Es ist wichtig, dass einer Variable vor Ihrer ersten Verwendung ein Wert zugewiesen wird (Initialisierung). Wird dies nicht gemacht ist Wert der Variablen zufällig und kann Fehlfunktionen des Programms führen. (siehe auch Folie „Definite Assignment“ • Adresse: Nummerierte Speicherzelle im Arbeitsspeicher welche von der Variable belegt wird. Eine Variable kann natürlich auch mehrere Speicherzellen belegen Variablen • Bevor Variablen ein Wert zugewiesen werden kann, muss die Variable definiert werden. Die Definition einer Variablen setzt sich aus Deklaration und Reservierung des Speicherplatzes zusammen. Deklaration: Hier wird dem Compiler mitgeteilt, mit welchem Typ (und welchem Typmodifikator – mehr dazu später) er einen Namen verbinden muss Bei einfachen Datentypen (siehe nächste Folie) sieht das so beispielsweise so aus: int i; Dabei wir automatisch auch im Hauptspeicher Speicherplatz für die Variable reserviert Variablen vom selben Typ können auch in einer einzigen Vereinbarung definiert werden: int a,b,c;+ • Variablen können auf zwei unterschiedliche Arten verändert werden durch eine Zuweisung : i=5; durch einen Inkrement- oder Dekrement-Operator: i++; bzw. i--; Datentypen • • • In Java kann man die Datentypen wie folgt klassifizieren: Java kennt acht elementare Datentypen, die gemäß Sprachspezifikation als primitive Datentypen bezeichnet werden. Daneben gibt es die Möglichkeit, Array-Typen zu definieren, und als objektorientierte Sprache erlaubt Java die Definition von Objekttypen: Interface(Schnittstellen)-Typ und Klassen Typ. Zusammen werden diese drei Typen als Referenztypen bezeichnet. Primitive Datentypen Gleitkommatypen import java.io.*; public class Sample { public static void main (String[] argument) throws IOException { Ergebnis ?? System.out.println (1/10); } } • Wenn wir das Programm übersetzen und ausführen, so würden wir erwarten, dass als Ergebnis 0.1 am Bildschirm ausgegeben wird. • Zu unserer Überraschung wird jedoch 0 ausgegeben. Das ist aber vermeintlich falsch! • Aber Achtung! Wir haben im obigen Programm mit Ganzzahlen gearbeitet. Der Divisionsoperator ist in Java jedoch so definiert, dass die Division zweier ganzer Zahlen wiederum eine ganze Zahl ist. Und somit stimmt die Division natürlich: 1 / 10 = 0 mit 1 Rest Gleitkommatypen • Java kennt die beiden Fließkommatypen float (einfache Genauigkeit) und double (doppelte Genauigkeit). Die Länge beträgt 4 Byte für float und 8 Byte für double. • Fließkommaliterale werden immer in Dezimalnotation aufgeschrieben. Sie bestehen aus einem Vorkommateil, einem Dezimalpunkt, einem Nachkommateil, einem Exponenten und einem Suffix. Um ein Fließkommaliteral von einem integralen Literal unterscheiden zu können, muss mindestens der Dezimalpunkt, der Exponent oder der Suffix vorhanden sein. Entweder der Vorkomma- oder der Nachkommateil darf ausgelassen werden, aber nicht beide. 47 Gleitkommatypen • Vorkommateil und Exponent können wahlweise durch das Vorzeichen + oder - eingeleitet werden. • Der Exponent, der durch ein e oder E eingeleitet wird, ist optional. • Auch der Suffix kann weggelassen werden, wenn durch die anderen Merkmale klar ist, dass es sich um eine Fließkommazahl handelt. Der Suffix kann entweder f oder F sein, um anzuzeigen, dass es sich um ein float handelt, oder d oder D, um ein double anzuzeigen. Fehlt er, so ist das Literal (unabhängig von seiner Größe) vom Typ double. Gültige Beispiele: ????? 3.14 2d 1e6 .5f 6. 47 Gleitkommatypen • Neben diesen numerischen Literalen gibt es noch einige symbolische Literale in den Klassen Float und Double des Pakets java.lang. NaN („not a number“) entsteht beispielsweise bei der Division durch 0, POSITIVE_INFINITY bzw. NEGATIVE_INFINITY sind Zahlen, die größer bzw. kleiner als der darstellbare Bereich sind. MAX_VALUE und MIN_VALUE sind Konstanten welche die größte bzw. kleinste darstellbare Zahl des jeweiligen Datentype repräsentieren (definiert in „Wrapper-Klassen“ (später mehr dazu)) Gleitkommatypen import java.io.*; public class Sample2 { public static void IOException { System.out.println } } Ergebnis ?? main (String[] (1d/10.); argument) throws Datentyp char • Will man mit einzelnen Zeichen arbeiten, so steht der Datentyp char zur Verfügung (2 Byte, UTF -16) • Literalkonstanten werden dabei unter ein einfaches Hochkomma gestellt • Daten von diesem Datentyp werden intern mit 16 Bit dargestellt (unicode) import java.io.*; public class Buchstabe { public static void IOException { main (String[] argument) char ersterBuchstabe = 'A'; System.out.print("Der erste Buchstabe des Alphabets ist ein"); System.out.println(ersterBuchstabe); } } throws Datentyp char • char wird innerhalb der Java Virtual Machine als Datentyp int betrachtet: Datentyp char • char wird innerhalb der Java Virtual Machine als Datentyp int betrachtet: import java.io.*; public class AddBuchstabe { public static void IOException { main (String[] argument) char ersterBuchstabe = 'A'; char zweiterBuchstabe = 'B'; System.out.println(„Die Addition von erstem und zweitem Buchtaben ergibt"); System.out.println(ersterBuchstabe+zweiterBuchstabe); } } Ergebnis ?? throws Datentyp boolean • Beim Programmieren steht man des öfteren vor dem Problem, dass man Werte miteinander vergleichen muss. Das Ergebnis des Vergleichs liefert dann WAHR oder FALSCH. • In Java existieren die Wahrheitswerte true und false • Die Auswertung von logischen Ausdrücken liefert als Ergebnis Werte des Typs boolean Siehe auch Folie „Logische Operatoren“ ImpliziteTypumwandlung • Es kann vorkommen, dass man einen gewissen Datentyp für eine Operation benötigt, jedoch einen anderen vorliegen hat .Will man z.B.: eine 64 Bit Zahl mit einer 32 Bit Zahl addieren, so steht man vor dem Problem, dass die Addition nur für denselben Typ definiert ist. In einem solchen fall benötigt man also eine Typumwandlung, damit die Operation überhaupt durchgeführt werden kann. • Bei der oben beschriebenen Problemstellung muss man gar nichts tun. Denn der Compiler erkennt, dass die eine Zahl einen Zahlenbereich hat, der den der anderen Zahl umschliesst. Aus diesem Grund wird einfach die int Zahl in eine long Zahl umgewandet, und die Addition kann ausgeführt werden. • Diese automatische Vorgehensweise nennt man implizite Typkonvertierung (implicit typecast) • Implizite Typkonvertierungen treten immer dann auf, wenn ein kleinerer Zahlenbereich in einen größeren Zahlenbereich abgebildet wird. byte short int long float double ImpliziteTypumwandlung 2 Eine automatische Konvertierung wird vom Compiler in folgenden Fällen vorgenommen Bei einer Zuweisung, wenn der Typ der Variablen und des zugewiesenen Ausdrucks nicht identisch ist. Bei der Auswertung eines arithmetischen Ausdrucks, wenn Operanden unterschiedlich typisiert sind: int a; float b,c; c=a+b; 54 ImpliziteTypumwandlung 3 Eine automatische Konvertierung wird vom Compiler in folgenden Fällen vorgenommen (Fortsetzung) Beim Aufruf einer Methode, falls die Typen der aktuellen Parameter nicht mit denen der formalen Parameter übereinstimmen (dazu wieder einmal später mehr) public double doSomething (double a, double b) { .... } float x,y,z; Z=doSomething(x,y); 54 Implizite/Explizite Typumwandlung • Will man beispielsweise eine Variable vom typ double in eine Variable vom Typ int konvertieren, so kann man das nicht so ohne weiteres tun, denn es geht ja durch diese einschränkende Konvertierung Information verloren! Beispiel: int a=1; double b=3.5; a=b; • Daher nimmt der Compiler an, dass es sich um einen Fehler handelt und gibt eine Fehlermeldung aus: possible loss of precision found: double required: int •55 Implizite/Explizite Typumwandlung • Der Grund für dieses Verhalten liegt darin begründet, dass es sich bei einer solchen Vorgangsweise häufig um einen Programmierfehler handelt. Um jedoch eine Konvertierung vornehmen zu können, muss man den Compiler dazu mit Hilfe des Cast-Operators () dazu „zwingen“, es zu tun. Diese Vorgangsweise nennt man explizite Typkonvertierung (explicit typecast): int a=1; double b=3.5; a=(int)b; • Eine Umwandlung von boolean in einen anderen Datentyp ist nicht möglich! •55 Definite Assignment • • In Java gibt es ein Konzept, das sich Definite Assignment nennt. Gemeint ist damit die Tatsache, dass jede lokale Variable vor ihrer ersten Verwendung definitiv initialisiert sein muss. Dazu muss im Quelltext eine Datenflussanalyse durchgeführt werden, die jeden möglichen Ausführungspfad von der Deklaration einer Variablen bis zu ihrer Verwendung ermittelt und sicherstellt, dass kein Weg existiert, der eine Initialisierung auslassen würde. import java.io.*; public class Test { public static void main (String[] argument) throws IOException { int a; System.out.println(a); } } PROBLEM? Ausdrücke und Operatoren • Obwohl wir Ausdrücke schon in unseren ersten kleinen Programmen kennengelernt haben, wollen wir diese jetzt noch einmal etwas genauer unter die Lupe nehmen. Merke: Ein Ausdruck ist die kleinste ausführbare Einheit eines Programms. Ein Ausdruck besteht immer aus mindestens einem Operator und einem oder mehreren Operanden, auf die der Operator angewendet wird. Nach den Typen der Operatoren unterscheidet man zwischen arithmetischen / nummerischen, relationalen , logischen , (bitweise) Zuweisungs , und sonstigen Operatoren (oft als Methode implementiert zB instanceof) Arithmetische Operatoren • Es existiert die Addition, Subtraktion, Multiplikation, Division und der Restwertoperator • Zusätzlich gibt es den einstelligen Operator für ein positives oder negatives Vorzeichen • Arithmetische Operatoren erwarten numerische Operanden und liefern einen numerischen Rückgabewert. • Haben die Operanden unterschiedliche Datentypen, so wird ein automatischer Type-Cast durchgeführt. + + * / % ++ -- positives Vorzeichen negatives Vorzeichen Summe Differenz Multiplikation Quotient Restwert Prä/Postinkement Prä/Postdekrement +n gleichbedeutend mit n -n kehrt das Vorzeichen um a+b ergibt Summe von a,b a-b a*b a/b a modulo b a=a+1 a=a-1 Relationale Operatoren • Relationale Operatoren dienen dazu, Ausdrücke miteinander zu vergleichen und in Abhängigkeit davon einen logischen Rückgabewert zu produzieren == Gleich != Ungleich a!=b => true wenn a ungleich b < Kleiner a<b => true wenn a kleiner b <= Kleiner gleich > >= Größer Größer gleich a==b => true wenn a=b a<=b => true wenn a kleiner oder gleich b a>b a>=b 59 Logische Operatoren • Logische Operatoren dienen dazu, boolsche Werte miteinander zu verknüpfen. Im Gegensatz zu relationalen Operatoren, die durch Vergleiche einen boolschen Wert produzieren, werden logische Operatoren zur Weiterverarbeitung von Wahrheitswerten verwendet. • Es gibt die Grundoperationen UND, ODER und NICHT. UND und ODER werden in zwei Varianten zur Verfügung gestellt: Short-Circuit-Evaluation (SCE) && bzw ||: Bei der Aneinanderreihung mehrerer logische Ausdrücke wird ein logischer Ausdruck (weiter rechts) wird nur dann ausgewertet, wenn er für das Ergebnis noch von Bedeutung ist boolean wert1=false, wert2=true; if(wert1 == true && wird nicht ausgewertet wert2 == false) wert1=wert2; keine Short-Circuit-Evaluation & bzw | boolean wert1=false, wert2=true; if(wert1 == true & wird ausgewertet wert2 == false) wert1=wert2; Logische Operatoren ! logisches NICHT && UND mit SCE || ODER mit SCE & UND ohne SCE | ODER ohne SCE ^ Exklusiv-Oder !a => false, wenn a true und vice versa a&&b => true wenn a und b true sonst false a||b => true, wenn mind. einer der Ausdrücke true ist, sonst false a^b => true, wenn beide Ausdrücke einen unterschiedlichen Wert haben Bitweise Operatoren • Mit diesen Operatoren kann auf die Binärdarstellung von numerischen Operanden zugegriffen werden. • Ein numerischer Datentyp wird dabei binär (dual) repräsentiert, und es können die einzelnen Bits direkt manipuliert werden. ~ Einerkomplement & bitweises UND | bitweises ODER ^ bitweises XOR >> rechtsschieben mit Vorzeichen >>> rechtsschieben ohne Vorzeichen << linksschieben ~a entsteht aus a, indem alle Bits von a invertiert werden a&b ergibt den Wert, der entsteht, wenn die korrespondierenden Bits von a und b miteinander UND verknüpft werden a|b ergibt den Wert, der entsteht, wenn die korrespondierenden Bits von a und b miteinander ODER verknüpft werden a^b ergibt den Wert, der entsteht, wenn die korrespondierenden Bits von a und b miteinander Exklusiv-ODER verknüpft werden a>>b schiebt um b bits in a nach rechts entspricht Division von a durch 2n a>>b schiebt um b bits in a nach rechts Es werden stets Nullen von links eingefügt a>>b schiebt um b bits in a nach links Bitweise Operatoren Beispiele: 14 & 1 14 | 1 14 ^ 1 1110 & 0001 = 1111 = 15 1110 | 0001 = 0000 1110 ^ 0001 = 1111 = 15 8 >>3 00000000 00000000 00000000 00001000 00000000 00000000 00000000 00000001 -7 >> 3 11111111 11111111 11111111 11111001 11111111 11111111 11111111 11111111 -7 >>> 3 11111111 11111111 11111111 11111001 00011111 11111111 11111111 11111111 = abschneidende Ganzahldivision durch 8 (=23) Zuweisungsoperatoren = += -= *= /= %= &= |= ^= <<= >>= Einfache Zuweisung Additionszuweisung a+=3; entspricht a=a+3; Subtraktionszuweisung Multiplikationszuweisung Divisionszuweisung Modulozuweisunng UND Zuweisung a&=b weist a den Wert zu von a&b ODER Zuweisung XOR Zuweisung Linksschiebungszuweisung Rechtsschiebungszuweisung Sonstige Operatoren Sonstige Operatoren für primitive Datentypen: • Type-Cast Operator (): wird für explizite Typumwandlung verwendet int c = (int) 4.2; • Fragezeichenoperator ? : mehr dazu in der nächsten Vorlesung Sonstige Operatoren für Referenztypen: • InstanceOf – Operator instanceof: mehr dazu in der übernächstenVorlesung 63 Stelligkeit von Operatoren Neben dem Typ ist auch die Stelligkeit eines Operators von zentraler Bedeutung: • monadische Operatoren: benötigen nur einen Operanden. Bsp.: ++ -- • dyadische Operatoren: verknüpfen zwei Operanden und werden absolut am häufigsten verwendet. Bsp.: + - == • triadische/ternäre Operatoren: verknüpfen drei Operanden. Als einziges Beispiel in Java ist der Fragezeichen-Operator ? zu nennen Priorität von Operatoren • Operatoren sind in Java mit so genannten Prioritäten versehen, wodurch gewisse Reihenfolgen bei der Auswertung (=operator ranking) eingehalten werden • Darüber hinaus gibt es folgende Regeln zur Auswertung eines Ausdrucks in JAVA: 1. Als erstes werden Teilausdrücke in Klammern ausgewertet. 2. Danach werden Ausdrücke mit unären Operatoren ausgewertet 3. Abschließend werden Teilausdrücke mit mehrstelligen Operatoren ausgewertet (Fortsetzung auf der nächsten Folie !) Priorität von Operatoren 2 • Regeln zur Auswertung (Fortsetzung) 1. Als erstes werden Teilausdrücke in Klammern ausgewertet. 2. Danach werden Ausdrücke mit unären Operatoren ausgewertet 3. Abschließend werden Teilausdrücke mit mehrstelligen Operatoren ausgewertet 4. Bei Operatoren mit gleicher Priorität (siehe auch Tabelle) wird die Reihenfolge der Auswertung anhand Ihrer Assoziativität bestimmt Bsp: a – b + c ……. Es wird zuerst die Operation a-b durchgeführt bevor die Addition mit c erfolgt -> linksassoziativ Beachte: Die Reihenfolge der Auswertung kann durch Verwendung von Klammern beeinflusst werden: a-(b+c) (siehe auch Regel 1) • In Java sind Zuweisungsoperatoren, der Bedingungsoperator und unäre Operatoren rechtsassoziativ. Alle anderen Operatoren sind linksassoziativ. Priorität von Operatoren • Wie bereits erwähnt sind Operatoren sind in Java mit so genannten Prioritäten versehen, wodurch gewisse Reihenfolgen bei der Auswertung eingehalten werden (Punkt vor Strich) Übungsfragenzu Datentypen und Operatoren 1. Was versteht man unter Typumwandlung? 2. Wie kann es bei einer Typumwandlung zu unerwünschten Veränderungen im Wert kommen? 3. Was versteht man unter expliziter bzw. impliziter Typumwandlung? Nennen sie jeweils ein Beispiel. 4. Was sind Operatoren? In welche Klassen können Operationen eingeteilt werden? 5. Was versteht man unter der Priorität eines Operators? In diesem Zusammenhang fällt auch manchmal der Begriff „operator ranking“ . Was versteht man darunter ? Ausdrücke & Anweisungen In Java unterscheidet man zwischen Ausdrücken und Anweisungen: • Ausdruck: Ein Ausdruck ist in Java im einfachsten Fall eine Konstante oder eine Variable. Ausdrücke haben immer einen Wert, der auch teilweise als Rückgabewert bezeichnet wird Durch Verknüpfung von Operanden durch Operatoren entsteht ein komplexer Ausdruck Ausdrücke & Anweisungen Anweisungen: Im Gegensatz zu Ausdrücken haben Anweisungen keinen Rückgabewert. Mann kann folgende Anweisungen unterscheiden: Leere Anweisung Anweisungen zur Deklaration von Variablen Ausdrucksanweisung (Ausdruck;) Selektionsanweisung (if, switch) Iterationsanweisung (while, do, for) Sprunganweisungen (break, continue) Try - Anweisung Throw - Anweisung Synchronized - Anweisung Anweisungen • Eine Reihe von Anweisungen kann in einem Block zusammengefasst werden { Anweisung1; Anweisung2; } • Im Prinzip erlaubt die Blockanweisung eine Zusammenfassung von vielen Anweisungen zu einer einzigen geblockten (komplexen) Einzelanweisung. Anweisungen • Die leere Anweisung ; ist die einfachste Anweisung und hat auf das laufende Programm keinerlei Effekt • Variablendeklaration Syntax: Typname VariablenName; oder Typname VariablenName = Initialwert; die Deklaration einer lokalen Variable gilt in Java als eine Anweisung. Sie darf überall erfolgen, wo auch eine andere Anweisung stehen dürfte. Anweisungen • Ausdrucksanweisungen Syntax Ausdruck; Ausdruckanweisungen dienen dazu, Ausdrücke in einem Anweisungskontext auszuführen. Sie werden erzeugt, indem man an den Ausdruck ein Semikolon anhängt. In Java können Anwesiungen nicht aus allen Ausdrücken erzeugt werden. In Java zulässige Ausdrucksanweisungen sind: Zuweisung a+=b; Inkrement oder Dekrement c++; Methodenaufruf System.out.println(„Hello“); Instanzerzeugung Auto bmw = new Auto(); Anweisungen • Verzweigungen erst aufgrund von Verzweigungen ist es möglich komplexe Programme zu erstellen. Die Verzweigung ist eine der wichtigsten Anweisungen überhaupt, da Entscheidungen gefällt und verschiedene Codezweige ausgeführt werden können. In Java existiert die if und if-else sowie die switch Anweisung Hunger ? nein GDP lernen ja Essen Die if-Anweisung If-Anweisung Syntax if(ausdruck) anweisung; if(ausdruck) anweisung; else anweisung; Zuerst wird der Ausdruck (ausdruck) ausgewertet. Die Anweisung (anweisung) wird genau dann ausgewertet , wenn der Ausdruck (ausdruck) true zurückgeliefert hat. Liefer (ausruck) hingegen false zurück, so wird nichts gemacht oder wie in der zweiten Anweisung der else Zweig ausgeführt 79 Dangling Else • In fast allen blockorientierten Programmiersprachen können durch die elseAnweisung Mehrdeutigkeiten entstehen. Dann ist es oft schwer möglich zu sagen zu welchem if das else gehört (dangling else) : if (a) if (b) dothis (); else dothat (); Zu welchem if gehört nun der else Zweig? Lösung: Der Else Zweig gehört immer zu der innersten Verzweigung (die noch nicht geschlossen wurde). Hätte man den Code richtig eingerückt, so wäre dies auch gleich klar ersichtlich: if (a) if (b) dothis else dothat (); (); 80 Ergänzung zu ?-Operatoren Fragezeichenoperator ? der einzige dreistellige (=ternäre) Operator in Java - er erwartet einen logischen Ausdruck und zwei weitere, die beide entweder numerisch, vom Typ boolean oder ein von einem Referenztyp) sein müssen b = (a > 10) ? 1 : a ; //ist a >10, dann wird b=1, ansonsten b=c gesetzt Obige Zeile kann auch mit Hilfe von if...else.. formuliert werden: if (a > 10) { b = 1 ; } else { b = a; } 80