Vorlesung Informatik 1

Werbung
Vorlesung Informatik 1
Fachhochschule für Technik Esslingen
Studiengang Wirtschaftsinformatik
Teil 2: Programmelemente
Dr. rer. nat. Andreas Rau
http://www.hs-esslingen.de/~rau
[email protected]
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#1
Grundbegriffe & Werkzeuge
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#2
Komplexität
Pessimisten meinen
Ein Computer hilft uns, Probleme zu lösen, die wir ohne ihn nicht hätten...
und tatsächlich geht mit Software so manches schief. Allerdings liegt das nicht
an der Software an sich sondern an ihrer Komplexität und der Schwierigkeit der
beteiligten Menschen, mit dieser Komplexität fertig zu werden.
Im allgemeinen bekämpft man Komplexität durch Abstraktion. Allerdings muß
man bei der Softwareentwicklung irgendwann wieder konkret werden. Deshalb
verwendet man die Methode der hierarchischen Zerlegung bzw. des top-down
Entwurfs um ein Problem zu lösen. Man zoomt gewissermaßen von außen in
das Problem hinein, indem man zunächst die Grobstruktur des Problems bzw.
der Lösung analysiert und sich erst hinterher um die Details kümmert. Hierbei
wird das Problem oft in Teilprobleme zerlegt. Das zugehörige Schlagwort hierzu
lautet
divide and conquer (teilen und herrschen)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#3
Funktion und Daten
Laut Niklaus Wirth besteht Softwareentwicklung aus der schrittweisen
Verfeinerung von Funktionen und Daten. Es ist wichtig, beide Aspekte im Auge
zu behalten. Die Vernachlässigung eines der beiden kann fatale Folgen haben
Funktionen können abstrahiert werden über
● Anweisungen
● Funktionen
● Module
Daten können abstrahiert werden über
● Einzelne Werte
● Felder (Arrays) und Listen gleichartiger Werte
● Strukturen aus zusammengehörigen Werten verschiedenster Art
Wie wir wissen, liegt der Vorteil der Objektorientierung in der Verbindung von
Funktionen und Daten zu einem geschlossenen Objekt. Die dadurch erreichte
Kapselung stellt sicher, dass nur gültige Operationen auf den Daten möglich
sind. Zunächst wollen wir uns jedoch mit den Grundlagen vertraut machen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#4
Verfeinerung von Programm und Daten
Sowohl Programm als auch Daten werden schrittweise verfeinert. Dabei
Vermeidet man Redundanz
● Kleine, wiederverwendbare Funktionen mit klarem Zweck
● Alles nur einmal speichern (z.B. nicht Geburtsdatum und Alter)
● Bündelt man, was zusammengehört (Kohesion)
● Funktionen in Modulen
● Daten in Datensätzen
● Minimiert man Abhängigkeiten verschiedener Teile (Kopplung)
● Möglichst geschlossene Funktionbereiche
● Vollständige Datensätze
●
Letzteres erreicht man durch möglichst minimale, wohldefinierte Schnittstellen.
Grundsätzlich gilt, daß Daten nur dort direkt sichtbar sein sollten, wo Sie
unbedingt gebraucht werden (Information Hiding). Ansonsten besteht die Gefahr
von inkonsistenten Daten aufgrund unkontrollierter Änderungen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#5
Aufbau und Inhalte
Bei den Inhalten eines Programms unterscheidet man Syntax und Sematik:
Syntax
Struktureller Aufbau des Programms (Satzbau)
Beispiel: Fussball gerne Hans spielt?
➔Kann vom Compiler/Interpreter geprüft werden (vollständig)
Semantik
Bedeutung des Programms
Beispiel: Der Hund hat in der Drogerie gewonnen.
➔ Muss vom Mensch geprüft werden (unvollständig)
Mit anderen Worten: Die Syntax legt fest, wie ein Kochrezept aufgebaut ist. Von
der Semantik hängt es ab, ob das Essen schmeckt (Anders als ein menschlicher
Koch macht der Computer keine Fehler wenn das Rezept stimmt).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#6
Detaillierungsgrad
Die Anforderungen an Syntaktische und Semantische Korrektheit sind bei
Programmen viel höher als bei natürlicher Sprache: Ein Mensch der mitdenkt,
kann noch so manche unklare Anweisung sinnvoll umsetzen. Ein Computer
dagegen streikt schon, wenn eine Klammer fehlt oder macht wieder und immer
wieder zuverlässig den vorgeschriebenen Fehler.
Als Folge davon muss man beim Programmieren den Arbeitsablauf haarklein
und Schritt für Schritt beschreiben. Möglich ist dies u.a. deswegen, weil
Programmiersprachen viel exakter definiert sind als natürliche Sprachen (es gibt
keine Ausnahmen oder unregelmässige Verben).
Mit anderen Worten: Der Rechner erscheint manchmal als zickiger und
unflexibler Sklave, der Dienst nach Vorschrift macht ohne mitzudenken.
Andererseits ist er zumindest bei syntaktischen Fehlern so freundlich, mit
durchaus verständlichen Fehlermeldungen auf das Problem hinzuweisen. Wer
(englisch) lesen kann, ist also bei der Fehlersuche klar im Vorteil...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#7
Elemente
Jedes Programm besteht irgendwie aus Funktionen und Daten, konkret aus
Kontrollstrukturen / Anweisungen (Statements) ⇒ Ablauf steuern
● Sequenz
● Verzweigung
● Auswahl
● Schleife
Ausdrücken / Berechnungen (Expressions) ⇒ Werte erzeugen/verändern
● Werte (Variablen und Literale)
● Operatoren
Gültigkeitsbereichen (Scopes) ⇒ Sichtbarkeit und Lebensdauer festlegen
● Pakete
● Klassen
● Methoden
● Blöcke
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#8
Programmierwerkzeuge(1)
Zur Programmierung werden verschiedene Werkzeuge benötigt
Primäre Werkzeuge (Programmierung im Kleinen und im Großen)
●Ein allgemeiner oder spezieller Editor zur Programmerstellung (z.B. Notepad)
●Ein Compiler oder Interpreter zur Programmübersetzung und -Ausführung
●Ein Debugger zur Fehlersuche
Sekundäre Werkzeuge (Programmierung im Großen, Projektverwaltung)
● Ein Dokumentationswerkzeug (z.B. OpenOffice)
● Ein Entwurfs- und Modellierungswerkzeug (z.B. ArgoUML)
● Eine Versionsverwaltung (z.B. CVS, SVN)
● Ein Projektplanungswerkzeug (z.B. OpenOffice)
Moderne Entwicklungsumgebungen kombinieren viele dieser Werkzeuge unter
einer Oberfläche für mehr Komfort und Produktivität.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#9
Programmierwerkzeuge(2)
Eigenschaften von Compilern und Interpretern
Vorteile von Compilern:
Optimierung für bestimmten Prozessor und bestimmtes Betriebssystem; sehr
hohe Ausführungsgeschwindigkeit des resultierenden Programms
Nachteile von Compilern
Zusätzliche Schritte bei der Programmerstellung, insbesondere beim Testen
Vorteile von Interpretern:
Erhöhte Produktivität durch einfachen Programmtest und interaktives arbeiten
Nachteile von Interpretern:
Geringe Ausführungsgeschwindigkeit (ständige Interpretation)
Wie man sieht, sind die Nachteile des Einen die Vorteile des Anderen...
Compiler werden eingesetzt, wenn es auf die Geschwindigkeit bei der
Programmausführung ankommt (Programmiersprachen), Interpreter wenn es auf
die Geschwindigkeit bei der Programmerstellung ankommt (Skriptsprachen).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#10
Programmierwerkzeuge(3)
Ob ein Compiler oder Interpreter verwendet wird richtet sich nach der jeweiligen
Programmiersprache (vgl. HTML, "weder Fisch noch Fleisch")
Compiler:
Quellcode
z.B. *.pas, *.c
Interpreter:
Quellcode
z.B. *.bat
Objectcode
z.B. *.o
Compiler
Linker
Executable
z.B. *.exe
Interpreter
Java kombiniert die Prinzipien von Compiler und Interpreter zur Erreichung der
Plattform-Unabhängigkeit. Nur die virtuelle Maschine muss portiert werden.
Quellcode
Compiler
*.java
javac.exe
Aufruf:
X:>javac <Quelldatei(en)>
Bytecode
*.class
Virtuelle Maschine
java.exe
X:>java <Klassenname>
Java kennt keine exe-Dateien; jar-Dateien sind jedoch so ähnlich (später mehr).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#11
Programmierwerkzeuge(4)
Im Java Development Kit (JDK) bzw. Java 2 Software Development Kit (J2SDK)
wie es heute heisst befinden sich noch eine Reihe weiterer
Programmierwerkzeuge. Eines der wichtigsten davon ist der javadoc Compiler.
Mit seiner Hilfe ist es möglich, aus speziellen Kommentaren im Quelltext und
den (Struktur)Informationen die im Programm selbst enthalten sind automatisch
die Programmdokumentation zu generieren.
Die generierte Dokumentation entspricht genau dem Aufbau der Online-Hilfe
zum Java 2 Application Programming Interface (API). Dies ist kein Zufall, wird
doch auch die Dokumentation zum API aus dem Quelltext der Bibliothek
generiert. Es ist jedoch möglich über zahlreiche Parameter und Vorlagen (die
man unmöglich hier alle beschreiben kann) das aussehen gezielt zu steuern.
Quellcode
*.java
Doku-Generator
javadoc.exe
Dokumentation
*.html
Browser
Aufruf
X:>javadoc -d<Zielverzeichnis> <Quelldatei(en)>
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#12
Elementare Bestandteile
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#13
Rahmenwerk für Beispielprogramme
Ab jetzt wirds Ernst, d.h. Sie müssen die Theorie auch praktisch umsetzen.
Kleine Sünden strafft der Compiler sofort... Aber keine Angst: Das wird schon.
In Java findet alles innerhalb von Klassen und Objekten statt. Daher benötigen
wir bereits für unsere ersten Beispielprogramme und Experimente eine echte
Klasse als Rahmen. Dabei werden einige Dinge verwendet und
vorweggenommen, die Sie jetzt noch nicht verstehen. Für den Augenblick gilt
dabei: einfach hinnehmen und machen*. Die Erleuchtung folgt später. Garantiert.
// Beispielprogramm; die fett gedruckte Teile sind immer gleich
public class KlassenName { // entspricht vorläufig dem Name des Programms
// Die (Klassen)Methode mit der jedes Programm beginnt (C läßt grüssen)
public static void main( String[] args) {
// Hier spielt sich alles ab, u.a. kann man hier Variablen erzeugen
// und Berechnungen durchführen. Später werden wir hier auch Objekte
// erzeugen und Methoden aufrufen. Aber alles zu seiner Zeit...
}
}
*Kinder lernen genauso
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#14
Zeichenvorrat von Java(1)
Wie jede andere Sprache basiert auch eine Programmiersprache auf einem
Alphabet, d.h. einer Menge von gültigen Zeichen. Das Alphabet von Java basiert
auf dem Unicode Zeichensatz (vgl. www.unicode.org).
Im Gegensatz zu den Zeichensätzen ASCII und ISO-Latin-1, die pro Zeichen 1
Byte verwenden, besteht ein Unicode Zeichen aus 2 Bytes. Aus
Kompatibilitätsgründen sind jedoch die ASCII Zeichen mit identischer Kodierung
im Unicode enthalten. Ziel von Unicode ist die einheitliche Kodierung der
Zeichen aller Sprachen.
Die meisten Zeichen können direkt über die Tastatur eingegeben werden. Für
Sonderzeichen oder Symbole einer Fremdsprache (insbesondere innerhalb von
Zeichenketten) besteht die Möglichkeit, den Code anzugeben. Dazu werden sog.
Fluchtsequenzen (Escape Sequences) verwendet. Diese können die Form
\o1o2o3 (ASCii-Code 0-255) oder \ux1x2x3x4 (Unicode) haben..
Beispiel: (Erklärung zu Zahlensystemen folgt später)
\101 (ascii, oktal) = 65 (dezimal) = a
\u0041 (unicode, hex) = 65 (dezimal) = a
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#15
Zeichenvorrat von Java(2)
Konkret umfasst das Alphabet von Java (=Baumaterial!) folgende Zeichen
Buchstaben (inkl. Unterstrich und Dollarzeichen) und Ziffern für Bezeichner
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z _ $
0 1 2 3 4 5 6 7 8 9
● Sonderzeichen für Trenner (engl. separators)
() {} [] ; , .
● Sonderzeichen für Operatoren
= > < ! ? : & | + - * / ^ %
● Sonderzeichen für Ersatzdarstellungen wie \\, \n, \t
\ (engl. Backslash)
● unsichtbare(!) Zeichen (engl. Whitespace*) zur Formatierung des Quelltexts
das Leerzeichen (engl. Blank), das Zeilenendezeichen (engl. linefeed), den horizontalen
Tabulator (engl. tab) und den Seitenvorschub (engl. form feed)
● Begrenzer für Zeichenkonstanten und konstante Zeichenketten (vgl. Literale)
das einfache Anführungszeichen '. Eselsbrücke: ein (Anführungs)zeichen
das doppelte Anführungszeichen ". Eselsbrücke: mehrere (Anführungs)zeichen
●
*im Internet finden sich satirische Beschreibungen von Programmiersprachen, die nur solche Zeichen verwenden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#16
Zeichenvorrat von Java(3)
Aus diesem Zeichenvorrat wird der Quellcode und dabei die
vorkommenden Literale, reservierten Wörter, und Bezeichner aufgebaut.
darin
Literale
Sind "Werte, die man einfach hinschreiben kann" (siehe spezielle Folie).
Reservierte Wörter
Sind vordefinierte Namen für Elemente der Programmiersprache, z.B.
Kontrollstrukturen und spezielle Operatoren (siehe Buch) . Sie dürfen nicht als
Bezeichner verwendet werden.
Bezeichner
Sind die Namen von Klassen, Methoden und Variablen. Sie werden vom
Programmierer vergeben und müssen mit einem Buchstaben oder $/_ beginnen.
Java ist case-sensitiv, unterscheidet also zwischen Groß- und Kleinschreibung, .
Bezeichner, die sich nur dadurch unterscheiden sind keine gute Idee!!!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#17
Kommentare(1)
Als Kommentar bezeichnet man Text, der (in der Regel) vom Rechner ignoriert
wird aber dem Menschen (neben gut gewählten Bezeichnern) hilft, das
Programm zu verstehen. In Java gibt es mehrere Arten von Kommentaren:
Kommentare für einzelne Zeilen (alles nach // wird ignoriert)
System.out.println( "Hello, World!"); // gibt "Hello, World!" am Bildschirm aus
Kommentare für Zeilenbereiche (alles zwischen /* und */ wird ignoriert)
/*
* Der Stern vor dieser Zeile ist optional, macht das Ganze aber hübscher
*/
Neben ihrer Verwendung zur Dokumentation können Kommentare auch bei der
Fehlersuche nützlich sein. Hier werden sie genutzt, um Programmteile
"auszukommentieren", d.h. durch Kommentarzeichen zu deaktivieren und so als
möglichen Übeltäter auszuschließen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#18
Kommentare(2)
Daneben gibt es in Java aber auch noch Kommentare, die sowohl für den
Menschen als auch den Computer gedacht sind:
Kommentare zur Generierung von Dokumentation (später mehr!)
/**
* Diese Klasse implementiert das berühmte "Hello, World!" Beispiel
*/
public class HelloWorld {
// ...
}
Sie können an bestimmten Stellen verwendet werden, um Text für die spätere
Generierung der Programmdokumentation direkt im Programm zu hinterlegen
und sind an dem einleitenden /** (mit 2 Sternchen) erkennbar.
Der Vorteil dieser Vorgehensweise liegt darin, daß man Programm und
zugehörige Dokumentation stets gemeinsam vorliegen hat und konsistent
pflegen kann. So soll verhindert werden, dass Programm und Dokumentation
inhaltlich auseinander laufen (weitere Details später).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#19
Konventionen für Bezeichner
Bezeichner sind wichtig. Daher gibt es für Bezeichner etablierte Stilvorgaben
Bezeichnertyp
Klassennamen
Methodennamen
Datenfeldnamen
Variablennamen
symbolische Konstanten
Konvention
1. Buchstaben groß, Rest klein
Kleinbuchstaben
Kleinbuchstaben
Kleinbuchstaben
alle Buchstaben groß
Beispiel
Datum
heute()
monat
termin
FEBRUAR
Bei aus mehreren Wörtern zusammengesetzte Namen wird ab dem zweiten
Wort jeweils der erste Buchstabe eines Wortes groß geschrieben. Für das erste
Wort gelten die obigen Konventionen (Ausnahme: Konstanten).
Beispiele:
verschiebeTermin()
neuerTermin
MONATE_PRO_JAHR
Methode
Variable
Konstante
Daneben gilt: Ein Name sollte etwas aussagen, und das möglichst eindeutig!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#20
Übung: Richtige und Falsche Bezeichner
Welche Bezeichner sind syntaktisch richtig? Wofür stehen sie per Konvention?
firstName
M31N_N1CKNAM3
§3
2terVorname
ALTERSGRENZE
_internalValue
telefon#2
zins%
Konto
schuhgröße
Kontostand
hAaRfaRbe
x
a1
b4712
haus-nr
Buchung
$1
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#21
Vorschau: Bestandteile einer Klasse
public class BeispielKlasse {
Legende
static int klassenVariable;
static void klassenMethode( int formalerParameter) {
// ...
}
int instanzVariable;
int instanzMethode( int formalerParameter) {
// ...
}
public static void main( String[] args) {
int lokaleVariable;
klassenMethode( lokaleVariable);
BeispielKlasse objektReferenz;
objektReferenz = new BeispielKlasse();
lokaleVariable = objektReferenz.instanzMethode( lokaleVariable);
reservierte Wörter:
Sind durch die jeweilige
Programmiersprache
vorgegeben (müssen
genau so lauten) und
dürfen nicht für eigene
Bezeichner verwendet
werden.
eigene Bezeichner:
Werden vom
Programmierer
vergeben (könnten
auch anders lauten).
}
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#22
Vorschau: Bestandteile einer Klasse
public class BeispielKlasse {
public static void main( String[] args) {
Liefert kein
Ergebnis
}
Für Parameter aus
dem DOS-Fenster
Statisch, stets vorhanden
(auch ohne Objekte)
gehört zur Klasse
}
Öffentlich, für
jedermann zugänglich
© Andreas Rau, 19.11.10
Bezeichner sind normal frei wählbar.
Dieser aber ist fest vereinbart!
(„Rose im Knopfloch“)
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#23
Datentypen
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#24
Datentypen
Datentypen
Elementare Datentypen
boolean
Referenztypen = null
Numerische Datentypen
Ganzzahltypen
Gleitkommatypen
char
float = 0.0
byte = 0
double = 0.0
short = 0
int = 0
Default Typ
long = 0
für Literale
Default Wert
für Variablen
© Andreas Rau, 19.11.10
Default Wert
für Datenfelder
Klassentyp Arrays Schnittstellentyp
z.B. String
Strings und Arrays sind "magisch“
Sie werden durch eigene Konstrukte
in der Sprache besonders unterstützt,
z.B. Literale und spezielle Operatoren
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#25
Wertebereiche der Datentypen
Jeder Elementare Datentyp in Java hat einen bestimmten Wertebereich:
Typ
boolean
char
byte
short
int
long
float
double
© Andreas Rau, 19.11.10
Inhalt
Wahrheitswert
16 Bit-Zeichen (Unicode)
auch als Zahl interpretierbar
8 Bit-Ganzzahl
mit Vorzeichen (Zweierkomplement)
16 Bit-Ganzzahl
mit Vorzeichen (Zweierkomplement)
32 Bit-Ganzzahl
mit Vorzeichen (Zweierkomplement)
64 Bit-Ganzzahl
mit Vorzeichen (Zweierkomplement)
32 Bit-Gleitkommazahl (IEEE 754)
mit Vorzeichen, Mantisse, Exponent
64 Bit-Gleitkommazahl (IEEE 754)
mit Vorzeichen, Mantisse, Exponent
Wertebereich
true und false
0 bis 65535 (alle Unicode-Zeichen)
7
bis +2 -1
-2
15
bis +2 -1
-2
31
bis +2 -1
-2
63
bis +2 -1
-2
7
15
31
63
-3.4*1038 bis +3.4*1038
-1.7*10308 bis +1.7*10308
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#26
Wertebereiche der Datentypen
Vergleich der Wertebereiche
in logarithmischer(!) Darstellung
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#27
Analogie – Datentypen als Eimer
feiner
größer
byte
© Andreas Rau, 19.11.10
short
int
long
float
double
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#28
Einschub: Speicherorganisation
Elementare Bezeichnungen
1 Bit
1 Byte
eine einzelne Binärziffer
eine einzelne Speicherzelle (8 Bit)
1 Nibble
1 Word
eine einzelne Hexziffer (4 Bit oder ein halbes Byte)
Registergröße des Prozessors (heute: 32 Bit oder 4 Byte)
Dimensionen
1 KByte (KB)
1 MByte (MB)
1 GByte (GB)
1 TByte (TB)
2^10 Byte =
1 024 Byte
2^20 Byte =
1 048 576 Byte
2^30 Byte = 1 073 741 824 Byte
2^40 Byte =
... Byte
(Wechseldatenträger)
(Festplatten)
(Datenbanken)
Offizielle Abgrenzung der Binäreinheiten von den physikalischen SI-Einheiten:
Kilobyte (kByte) = 1000 Byte
Kikibyte (KiByte) = 1024 Byte
Wird aber nicht konsequent durchgehalten und führt häufig zu Verwirrung.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#29
Einschub: Speicherbereiche(3)
Wie der Stack funktioniert (Last-In, First-Out bzw. First-In, Last-Out)
Stack heisst Stapel, und dass kann man durchaus wörtlich nehmen: Was als
letztes oben draufgelegt wurde, wird als erstes wieder weggenommen, und was
als erstes (also ganz unten) kommt, geht als letztes.
Dieser Ablauf ist besonders dazu geeignet, um sich zu merken "wo man
herkommt" – z.B. die Rücksprungaddresse beim Aufruf von Unterprogrammen.
Das geht natürlich auch über mehrere Schritte. Zusätzlich kann man sich dann
noch ein paar Notizen zum Kontext machen. Das sind dann die lokalen
Variablen.
Exkurs: Sicherheitslücken durch Buffer-Overflow (an der Tafel)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#30
Literale
Literale sind Werte, die man „einfach so hinschreiben“ kann, z.B. um Variablen
zu initialisieren. Dazu sind für gängige Datentypen feste Schreibweisen definiert.
Datentyp
boolean
char
int
long
float
double
String*
Array
Literal
true, false
'a', 'b', '\o1o2o3' (ASCII Code oktal), '\ux1x2x3x4' (Unicode)
154 , 012 (Oktal), 0x1a (Hexadezimal)
154L, 012L (Oktal), 0x1aL (Hexadezimal)
1.0f, 12.5e+5f (Expotentialdarstellung = 1250000)
1.0 , 12.5e+5 (Expotentialdarstellung = 1250000)
"das ist eine Zeichenkette"
(vgl. API Doku)
{1, 6, 3, 9} (int-Array mit 4 Elementen)
(vgl. API Doku)
Für die übrigen Datentypen gibt es keine Literale! Ihre Werte müssen entweder
durch Konvertierung der vorhandenen Literale gebildet (elementare Datentypen)
oder mit dem new-Operator (kommt noch) erzeugt und initialisiert werden.
*identische Stringliterale werden zu einem einzigen String-Objekt zusammengefasst
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#31
Einschub: Zahlensysteme(1)
Normale Menschen verwenden im Alltag zur Darstellung von Zahlen das
Dezimalsystem (das hat wohl etwas mit unseren Fingern zu tun...).
Programmierer verwenden jedoch daneben noch andere Zahlensysteme, v.a.
das Dualsystem und das Hexadezimalsystem sowie manchmal das
Oktalsystem. Dies hängt mit der Speicherorganisation eines Rechners
zusammen. In den genannten Zahlensystem läßt sich der Zustand der Bits
leichter ablesen. Dies ist wichtig, weil sich vieles in der Informatik platzsparend
in einzelnen Bits kodieren läßt – vor allem in der Steuerungstechnik.
Alle genannten Zahlensysteme sind sog. Stellenwertsysteme. Ein Stellenwert
weißt jeder Ziffer abhängig von ihrer Position eine Wertigkeit zu, die sich aus
dem Exponenten der Basis des Zahlensystems und der Position ergibt (dabei
fangen Informatiker wie immer bei Null an zu zählen). Der Ziffernvorrat für jede
Stelle ergibt sich aus Basis des Zahlensystems.
Der Vorteil von Stellenwertsystemen gegenüber primitiven Zahlensystemen (z.B.
1 = |, 2 = ||, 3 = |||): Man braucht viel weniger Platz für größere Zahlen!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#32
Einschub: Zahlensysteme(2)
Dezimalsystem (Dezimal = 10)
Ziffern
...
Exponent
...
10^3 10^2 10^1 10^0 10^-1 10^-2 10^-3
...
Wertigkeit
...
1000
...
Ziffernvorrat
Z3
Z2
100
Z1
Z0
10
1
Z-1
Z-2
1/10 1/100
Z-3
1/1000
...
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Die Zahl 6421.32 bedeutet also in Wirklichkeit
6*1000 + 4*100 + 2*10 + 1 + 3*1/10 + 2*1/100 = 6421.32
Beim Dezimalsystem erscheint dies ziemlich
Zahlensysteme funktionieren aber genauso...
© Andreas Rau, 19.11.10
witzlos.
Die
anderen
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#33
Einschub: Zahlensysteme(3)
Dualsystem (Dual = 2)
Ziffern
...
Z3
Z2
Z1
Z0
Z-1
Z-2
Z-3
...
Exponent
...
2^3
2^2
2^1
2^0
2^-1
2^-2
2^-3
...
Wertigkeit
...
8
4
2
1
½
¼
1/8
...
Ziffernvorrat
0, 1
Die Zahl 1101.01 bedeutet also in Wirklichkeit
1*8 + 1*4 + 0*2 + 1*1 + 0*1/2 + 1*1/4 = 13,25
Damit kann man also vom Dualsystem ins Dezimalsystem umrechnen...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#34
Einschub: Zahlensysteme(4a)
Oktalsystem (Oktopuss = 8 Arme)
Ziffern
...
Z3
Z2
Z1
Z0
Z-1
Z-2
Z-3
...
Exponent
...
8^3
8^2
8^1
8^0
8^-1
8^-2
2^-3
...
Wertigkeit
...
512
64
8
1
1/8
1/64
1/512
...
Ziffernvorrat
0, 1, 2, 3, 4, 5, 6, 7
Die Zahl 742.36 bedeutet also in Wirklichkeit
7*64 + 4*8 + 2*1 + 3*1/8 + 6*1/64 = 482,46875
Damit kann man also vom Oktalsystem ins Dezimalsystem umrechnen...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#35
Einschub: Zahlensysteme(4b)
Hexadezimalsystem (Hexa = 6, Dezimal = 10 ergo Hexadezimal = 16)
Ziffern
...
Exponent
...
16^3 16^2 16^1 16^0 16^-1 16^-2 16^-3
Wertigkeit
...
4096
Ziffernvorrat
Z3
Z2
256
Z1
16
Z0
1
Z-1
Z-2
1/16 1/256
Z-3
1/4096
...
...
...
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A(10), B(11), C(12), D(13), E(14), F(15)
Die Zahl 2E1A.0F bedeutet also in Wirklichkeit
2*4096 + 14*256 + 1*16 + 10*1 + 0*1/16 + 15*1/256 = 11802,059
Damit kann man also vom Hexadezimalsystem ins Dezimalsystem umrechnen...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#36
Einschub: Zahlensysteme(5)
Für die Rückwärtsrichtung, d.h. vom Dezimalsystem in ein anderes Zahlensystem,
verwendet man den sog. Restwertalgorithmus. Bei diesem Algorithmus teilt man
solange durch die Basis des Zahlensystems, bis alle Ziffern ermittelt sind:
Beispiel: 112 ins Dualsystem wandeln
112
56
28
14
7
3
1
:
:
:
:
:
:
:
2
2
2
2
2
2
2
Also 112
= 56 Rest 0
= 28 Rest 0
= 14 Rest 0
= 7 Rest 0
= 3 Rest 1
= 1 Rest 1
= 0 Rest 1
=
1110000
=
64 +
32 +
16
=
1*2^6 + 1*2^5 + 1*2^4 + 0*2^3 + 0*2^2 + 0*2^1+ 0*2^0
= (((((((1*2) + 1)*2 + 1)*2 + 0) *2 + 0)* 2 + 0)*2 + 0
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#37
Einschub: Zahlensysteme(6)
Der Restwertalgorithmus funktioniert zwar für alle Zahlensysteme, für Oktal und
Hexadezimal ist er aber etwas mühsam. Alternativ kann man ausnutzen, dass
Hexziffern bzw. Oktalziffern jeweils 4 bzw. 3 Dualziffern entsprechen.
Hex
0xF
0xE
0xD
0xC
0xB
0xA
0x9
0x8
Oktal
-
Dual
1111
1110
1101
1100
1011
1010
1001
1000
Hex
0x7
0x6
0x5
0x4
0x3
0x2
0x1
0x0
Oktal
07
06
05
04
03
02
01
00
Dual
0111
0110
0101
0100
0011
0010
0001
0000
Wichtig: Gruppierung immer von hinten her; Wertigkeit pro Block stets 8, 4, 2, 1
1
6
D
101101101 = (000)1 0110 1101 = 0x16D
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#38
Einschub: Zahlensysteme(7)
Restwert
Restwert
Dezimal
=
27
27:8 = 3R3
27:16 = 1R11
3:8 = 1R3
1:16 = 0R 1
Stellenwerte
Stellenwerte
3*8+3*1
1*16+11*1
Oktal = 033
Restwert
Stellenwerte
27:2 = 13R1 1*16+1*8+0*4
13:2 = 6R1 +1*2+1*1
6:2 = 3R0
3:2 = 1R1
1:2 = 0R1
3 Bits pro Ziffer
Hex = 0x1b
4 Bits pro Ziffer
Binär = 11011
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#39
Einschub: Darstellung von negativen Ganzzahlen
Intern werden alle Werte als „Bits und Bytes“ kodiert gespeichert. Speziell bei Ganzzahlen
stellt sich dabei die Frage, wie negative Zahlen dargestellt werden sollen um sie einfach
verarbeiten zu können. Gesucht ist also eine Darstellung für -5 damit 5-5 = 0
möglichst einfach berechnet werden kann.
Der naive Ansatz, ein eigenes Bit für das Vorzeichen zu spendieren erfordert eine
explizite Unterscheidung der Kombinationen ++, +-, -+ und --. Mit Hilfe der Darstellung im
sog. Zweierkomplement ist diese Fallunterscheidung nicht mehr nötig. Dabei wird die
Tatsache ausgenutzt, dass binär 1+1 = 0(+Überlauf) ergibt.
Das Zweierkomplement (ZK) besteht aus zwei Schritten:
1. Bildung des Komplements durch kippen aller* Bits (damit es bei Addition lauter 1er gibt)
2. Addition von 1 (um den Überlauf zu erzwingen – "Domino Effekt / Klippe", "Tachorad")
Bsp. Kodierung von -5
5 = 0101
1010 nach Komplement (Hinweis: 0101+1010=1111)
1011 nach Addition von (000)1 (Hinweis: 0101+1011=(1)0000)
Das dies wirklich so ist kann man sogar mit einem Programm sichtbar machen (später).
*je nach Datentyp gibt es unterschiedlich viele führende Nullen!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#40
Darstellungsgenauigkeit in verschiedenen Zahlensystemen(1)
Nicht jede Zahl ist in jedem Zahlensystem gleich gut darstellbar. So ist zum Beispiel im
Dezimalsystem die rationale Zahl (d.h. der Bruch) 1/3 nicht exakt darstellbar:
1 / 3 = 0.33...
10
9
10
9
…
Ein Dezimalcomputer, der Fließkommazahlen nur mit 5-stelliger Genauigkeit darstellen
kann, könnte also folgende Berechnung nicht exakt durchführen
3 * (1/3) = 3 * 0.33333 = 0.99999
Das kann man im Einzelfall durch Rundung oder Umstellung der Berechnung korrigieren:
1 * (3/3) = 1
Im Allgemeinen addieren sich solche Fehler jedoch über die Zeit auf!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#41
Darstellungsgenauigkeit in verschiedenen Zahlensystemen(2)
Ähnlich „ungeschickte“ Zahlen gibt es auch im Dualsystem, z.B. 0.1
0.1 = 1/10 (dezimal) = 1 / 1010 (dual)
1 / 1010 = 0.00011001...
10
100
1000
10000
11010
11
01100
1010
1
100
1000
10000
1010
...
© Andreas Rau, 19.11.10
Probe mit Tabellenkalkulation:
1,0000000000000000000000000
0,5000000000000000000000000
0,2500000000000000000000000
0,1250000000000000000000000
0,0625000000000000000000000
0,0312500000000000000000000
0,0156250000000000000000000
0,0078125000000000000000000
0,0039062500000000000000000
0,0019531250000000000000000
0,0009765625000000000000000
0,0004882812500000000000000
0,0002441406250000000000000
0,0001220703125000000000000
0,0000610351562500000000000
0,0000305175781250000000000
0,0000152587890625000000000
0,0000076293945312500000000
0,0000038146972656250000000
0
0
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
Summe
0,0000000000000000000000000
0,0000000000000000000000000
0,0000000000000000000000000
0,0000000000000000000000000
0,0625000000000000000000000
0,0312500000000000000000000
0,0000000000000000000000000
0,0000000000000000000000000
0,0039062500000000000000000
0,0019531250000000000000000
0,0000000000000000000000000
0,0000000000000000000000000
0,0002441406250000000000000
0,0001220703125000000000000
0,0000000000000000000000000
0,0000000000000000000000000
0,0000152587890625000000000
0,0000076293945312500000000
0,0000000000000000000000000
0,0999984741210938000000000
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#42
Darstellungsgenauigkeit in verschiedenen Zahlensystemen(3)
Programmbeispiel: Countdown mit Ganzzahlen (OK)
public class Countdown1 {
}
public static void main( String[] args) {
int i = 5;
System.out.println( i);
i = i-1;
System.out.println( i);
i = i-1;
System.out.println( i);
i = i-1;
System.out.println( i);
i = i-1;
System.out.println( i);
i = i-1;
if (i==0) System.out.println( "Boom!");
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#43
Darstellungsgenauigkeit in verschiedenen Zahlensystemen(4)
Programmbeispiel: Countdown mit Kommazahlen (Fehlfunktion!)
public class Countdown2 {
}
public static void main( String[] args) {
double d = 0.5;
System.out.println( d);
d = d - 0.1;
System.out.println( d);
d = d - 0.1;
System.out.println( d);
d = d - 0.1;
System.out.println( d);
d = d - 0.1;
System.out.println( d);
d = d - 0.1;
if (d==0) System.out.println( "Boom!"); // besser: d < 0.01
}
Folgerung: Exakte Berechnungen (z.B. bzgl. Geld) immer mit Ganzzahlen durchführen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#44
Variablen
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#45
Variablen(1a)
Maschinen verarbeiten Material, Programme verarbeiten Daten. Mit anderen
Worten: Daten sind der Rohstoff mit dem Computer arbeiten (daher auch die
Bezeichnung elektronische Datenverarbeitungsanlage, kurz EDV).
Während man Materialien über Eigenschaften wie Härte oder Dichte
charakterisiert, ist bei Daten insbesondere der Datentyp von Bedeutung. Formal
umfasst die Beschreibung eines (abstrakten) Datentyps
1) Eine Menge von Werten (z.B. Zahlen)
2) Eine Menge von Operationen auf diesen Werten (z.B. Rechenoperationen)
Daten, die mit einem Rechner verarbeitet werden sollen, müssen also auf einen
geeigneten Datentyp abgebildet werden. Während dies für Zahlen direkt möglich
ist, müssen andere Daten i.d.R. in geeigneter Form als Zahlen kodiert werden.
So können z.B. Farben in die Grundfarben RGB zerlegt und deren Anteile
numerisch repräsentiert werden. Für andere Daten wie z.B. Wochentage kann
eine geeignete Kodierung selbst definiert werden, z.B. Sonntag=0, Montag=1, ...
Dabei sind Konstanten bzw. die später vorgestellten Enums nützlich.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#46
Variablen(1b)
Seit der imperativen Programmierung werden Daten in Variablen gespeichert
und können immer wieder gelesen oder auch verändert werden. Die Verwaltung
von Variablen erfolgt bei der Objektorientierung statt über Module über Klassen.
Der Name einer Variablen soll nicht nur zur einen Speicherplatz eindeutig
identifizieren sondern sollte auch aussagen, was der darin gespeicherte Wert
bedeutet. Mühe für gute selbsterklärende Namen erspart Mühe für Kommentare!
Neben dem (offensichtlich notwendigen) Namen ist der Datentyp ist die
wichtigste Eigenschaft einer Variablen. Er legt fest für welche Art von Werten die
Variable stehen kann und was man mit diesen Werten machen kann.
Weitere wichtige Eigenschaften von Variablen sind:
Lebensdauer (zu welchen Zeitpunkten ist die Variable generell verfügbar)
● Sichtbarkeit / Gültigkeit (wo ist der Name der Variablen bekannt)
● Zugriffsschutz (wo darf die Variable tatsächlich verwendet werden)
●
Diese ergeben sich in Java implizit aus dem Ort der Deklaration (siehe Folie
"Bestandteile einer Klasse"), der Zugriffsschutz (später) explizit über Modifier.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#47
Variablen(2)
In Java müssen alle Variablen vor der ersten Verwendung bekanntgemacht bzw.
deklariert werden. Dies ist nicht in allen Programmiersprachen so. Bei der
Deklaration wird dem Namen der Variablen ein Datentyp fest zugeordnet.
Grundsätzliche Syntax der Deklaration (Erweiterungen später)
<Datentyp> <Variablenname>;
Beispiel
int i;
Die Sichtbarkeit und Lebensdauer der Variablen ergibt sich aus dem Ort der
Deklaration (innerhalb einer Funktion, innerhalb einer Klasse, ...).
Im Unterschied zu älteren Programmiersprachen können Variablen in Java
überall im Code deklariert werden. Dies erleichtert das Verständnis von
Programmen, da auf diese Art und Weise Deklaration und Verwendung einer
Variablen näher beieinander liegen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#48
Variablen(3)
Variablen müssen vor ihrer ersten Verwendung zuerst initialisiert werden, d.h.
man muss Ihnen einen Wert zuweisen. Die Verwendung von nicht initialisierten
Variablen entspricht dem Griff in eine leeren bzw. nicht aufgeräumten Schublade
(mit Ungeziefer drin). Dies war eine häufige Fehlerquelle in anderen
Programmiersprachen. Um dem vorzubeugen, prüft der Java Compiler vor der
Verwendung einer Variablen nach, ob eine Initialisierung erfolgt ist. Ist dies nicht
der Fall generiert der Java Compiler einen Fehler.
Die Initialisierung kann bereits im Rahmen der Deklaration geschehen:
<Datentyp> <Variablenname> = <Startwert>;
Die Initialisierung kann aber auch in einem separaten Schritt, d.h. durch
Zuweisung eines Werts in einer eigenen Zeile erfolgen.
<Datentyp> <Variablenname>;
<Variablenname> = <Wert>;
Zur Vereinfachung des Initialisierungsaufwands, inbesondere bei Objekten und
deren Datenfeldern, gibt es für elementare Datentypen Defaultwerte. Der
explizite Initialisierungszwang gilt also nur für lokale Variablen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#49
Beispiele: Verwendung von Variablen
Beispiel
public class Variables {
public static void main(String[] args) {
int i; // Eine Variable deklarieren
i = 1; // Variable initialisieren
char c1, c2; // Zwei Variablen deklarieren
c1 = 'a'; // Variable initialisieren mit festem Wert
c2 = c1; // Variable initialisieren mit anderer Variablen
}
int j = 1; // Variable deklarieren und sofort initialisieren
j = i; // Wert der Variablen überschreiben
}
Variablen können mehrfach beschrieben (Zuweisung) und gelesen/verwendet
(Ausdrücke, kommt als nächstes) werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#50
Einschub: Speicherbereiche(1)
In Java gibt es unterschiedliche Speicherbereiche:
Programmspeicher (text segment/code segment)
Inhalt: Programmcode (Methoden), Klassenvariablen (insb. Konstanten)
Lebensdauer: dauerhaft; gesamte Programmlaufzeit
Heap
Inhalt: dynamisch (mit new) erzeugte Objekte inkl. deren Instanzvariablen
Lebensdauer: variabel; von der Erzeuger bis zum „Wegwerfen“ der Referenz
Stack
Inhalt: Funktionsparameter und lokale Variablen, Rücksprungadressen
Lebensdauer: begrenzt; bis zum Verlassen der Funktion
Anders als in anderen Programmiersprachen muss sich der Programmierer in
Java praktischerweise nicht um die Speicherverwaltung kümmern und kann
beliebig Objekte anlegen - aufgeräumt werden Sie automatisch!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#51
Stack
Funktionsparameter
und lokale Variablen;
begrenzte Lebensdauer;
„viele“ Änderungen
Programm
Daten
Einschub: Speicherbereiche(2)
© Andreas Rau, 19.11.10
Heap
Objekte inkl. Datenfelder;
dynamische Lebensdauer;
„wenig“ Änderungen
Textsegment
Programmcode,
und Klassenvariablen;
unbegrenzte Lebensdauer;
keine Änderungen
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#52
Kopieren von Variablen(1)
Kopieren von Variablen mit elementarem Datentyp:
3
int x, y; // Deklaration von 2 int-Variablen
x = 1;
y = x;
y = 3;
// Initialisierung von x mit dem Wert 1
// Kopieren von x in y (dannach y = 1)
// Änderung von y (dannach y = 3)
System.out.println(x); // liefert 1
System.out.println(y); // liefert 3
Der Wert von x wurde echt kopiert. Beide Variablen können
unabhängig voneinander verändert werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#53
Kopieren von Variablen(2)
Kopieren von Variablen mit Referenztyp
17.6.1961
Datum d1, d2;
d1 = new Datum(17, 6, 1961); // neues Objekt
d2 = d1; // Objektreferenz kopieren
d2.setzeTag(18); // Objekt durch d2 manipulieren
System.out.println(d1); // liefert 18.06.1961
System.out.println(d2); // liefert 18.06.1961
Das Objekt wurde nicht kopiert, sondern nur die Referenz.
Manipulationen betreffen beide Variablen – sie zeigen auf
dasselbe Objekt.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#54
Kopieren von Variablen(3)
Das Kopieren von Variablen ist also analog dem Kopieren von
Schubladeninhalten: Bei Variablen mit elementarem Datentyp sind
die Werte selbst in der Schublade hinterlegt. Bei Variablen mit
Referenztyp ist der Wert auf dem Heap – die Schublade enthält nur
eine Referenz. Nach dem Kopieren hat man also kein neues
Objekt, sondern nur zwei Referenzen auf das ursprüngliche Objekt.
X
1
D1
Y
3
D2
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#55
Analogie – Variablen und Datentypen
Schubladen-Analogie:
Werte = Teile mit einer bestimmten Form
Variablen = Fächer mit einer bestimmten Form
Je nach Form kann man verschiedene Dinge mit einem Teil machen (drehen,
stapeln, ...). Die Teile passen aber nur in bestimmte Fächer.
Manche Teile sind so groß, dass Sie gar nicht in ein Fach passen. In diesem Fall
legt man die Teile an einem andern Ort ab und deponiert nur einen Zettel mit
einem Verweis im Fach.
Schubladendenken kann auch hilfreich sein!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#56
Analogien – Speicherorganisation
Analogien stellen Bezüge zwischen der immateriellen Welt der Software und der
realen Welt her. Allerdings sollte man sie nicht überbelasten... Nichtsdestotrotz
können solche Bilder dem Gehirn helfen, sich besser mit der neuen Materie
zurechtzufinden. Nach einiger Zeit braucht man sie dann nicht mehr.
In der Vorlesung werden immer wieder Analogien zu einigen wichtigen
Konzepten der Programmierung verwendet. Sie werden sicher nicht allen Lesern
gleichgut gefallen, und es steht jedermann frei, sich eigene Eselsbrücken zu
bauen (die funktionieren meist ohnehin besser als die von anderen Leuten).
Wenn wir uns einen Schrank mit Schubladen vorstellen, dann
ist eine Variable eine Schublade mit einem Namen und bestimmtem Typ
● ist ein Array eine feste Gruppe numerierter Schubladen mit gleichem Typ
● ist ein Rekord eine feste Gruppe benannter Schubladen mit gemischtem Typ
●
In Java gibt es übrigends keine Rekords. Sie sind ein Konzept der imperativen
Programmierung. Die Objekte in Java sind eine Erweiterung dieses Konzepts.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#57
Analogien - Sichtbarkeit von Variablen
(FIXME)
Wir stellen uns eine Welt mit vielen Hotels und Zimmern vor...
Eine lokale Variable (in Java: Variable in einer Funktion oder Methode) ist eine
Schublade in einem Zimmer. Sie ist nur im Zimmer zugänglich und wenn ein
neuer Gast das Zimmer betritt hat das Zimmermädchen den Inhalt weggeräumt.
Eine statische lokale Variable (in Java: Nachbildung über eine Instanzvariable)
ist eine Schublade in einem Zimmer für Stammgäste. Jedesmal wenn der Gast
wieder in sein Zimmer kommt hat Sie wieder ihren alten Inhalt.
Eine modulglobale Variable (in Java: Klasse oder Datenelement ohne public)
ist eine Schublade an der Rezeption des Hotels. Sie ist nur für Gäste des Hotels
bzw. der Hotelkette und deren Tochterfirmen zugänglich und behält ihren Inhalt.
Eine absolut globale Variable (in Java: Klasse oder Datenelement mit public)
in einem Programm ist eine öffentliche Schublade in der Eingangshalle eines
Hotels und für alle Gäste und Besucher zugänglich. Auch sie behält ihren Inhalt.
Je besser eine Variable zugänglich ist, desto besser muss koordiniert werden!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#58
Konstanten
Konstanten sind Variablen die man nicht ändern kann. Was für einen Sinn hat
das, wenn ein Programm auf Zustandstransformationen auf Variablen als
Grundprinzip basieren soll?
Da Konstanten im Gegensatz zu Werten bzw. Literalen einen Namen haben
können sie die Lesbarkeit eines Programms erhöhen: Den Bezeichner
MAX_NUMBER_OF_USERS versteht man besser als den Wert 5 (no magic!).
Außerdem erhöhen sie die Wartbarkeit eines Programms dadurch, daß man sie
an mehreren Stellen referenzieren kann, d.h. man verwendet den Namen anstatt
den Wert zu duplizieren. Steht die Konstante für einen einstellbaren Parameter
muss man diesen also nur an einer Stelle ändern!
Wie oben erwähnt sind Konstanten spezielle Variablen und haben all deren
Eigenschaften, insbesondere Name und Datentyp, sind aber durch den Modifier
final als Konstanten gekennzeichnet und müssen sofort initialisiert werden.
Beispiel
final double PI = 3.14;
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#59
Enums(1)
In der Praxis braucht man oft Variablen mit endlich vielen Ausprägungen. Enums
(Aufzählungstypen) wurden in Java 5.0 eingeführt, um diese endlich symbolisch
statt numerisch kodieren zu können (wie true und false) und so die
Lesbarkeit und Typsicherheit von Programmen zu verbessern. Hintergrund ist
1. Lesbarkeit (läßt sich durch symbolische Konstanten lösen )
Kein Mensch kann sich merken, dass Sonntag = 0 ist.
2. Typsicherheit (läßt sich nicht durch symbolische Konstanten lösen)
Ausdrücke wie "Sonntag + Sommer" eigentlich keinen Sinn machen. Wenn aber
Sonntag = 0 und Sommer = 2 ist das für den Computer total in Ordnung!
Berechnungen wie Sonntag + 2 erscheinen zwar ganz praktisch (das Resultat
wäre in diesem Fall der Dienstag), was aber passiert bei Sonntag + 9?
Bei der Definition eines Enums werden nicht nur symbolische Namen festgelegt
sondern gleichzeitig ein neuer Datentyp definiert. Dadurch können z.B.
Wochentage von Jahreszeiten sauber unterschieden werden. Die Elemente des
Enums sind die Literale dieses Datentyps!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#60
Enums(2)
Die Definition eines Enums funktioniert in Java folgendermaßen:
public enum Season { // in der Datei Season.java
Spring, Summer, Autumn, Winter;
}
Anschließend kann man Variablen mit dem Aufzählungstyp deklarieren
Season current = Season.Autumn; // ggf. in einer anderen Datei
Die Kodierung der Konstanten im Hintergrund ist zwar immer noch numerisch,
jedoch für den Programmierer nicht mehr direkt sichtbar bzw. relevant. Äpfel und
Birnen mischen geht jetzt nicht mehr:
current = current + 1; // das gibt was auf die Finger!
Wie wir später sehen werden, haben Enums auch Ähnlichkeiten mit Klassen.
Diese können ausgenutzt werden, um Berechnungen wie auf der
vorhergehenden Folie wieder zu ermöglichen – und zwar sicher!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#61
Übung: Kodierung von Daten
Welchen Datentyp würden Sie wählen, um folgende Daten zu kodieren?
Das Alter einer Person
● Das Baujahr eines Autos
● Die Gültigkeit eines Tickets
● Das Gehalt eines Mitarbeiters
● Das Alter des Universums
● Den Namen einer Firma
● Eine Telefonnummer
● Die Farbe(n) einer Ampel
● Die Farbe eines Pixels auf dem Bildschirm
● Die Lottozahlen
● Das Geburtsdatum eines Studenten
●
Hinweis: Neben den in diesem Abschnitt vorgestellten Datentypen enthält die
Laufzeitbibliothek eine Reihe von nützlichen Klassen zur Repräsentation von
häufig vorkommenden Daten.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#62
Nachtrag: Arrays(1)
Arrays gibt es als festen Bestandteil in vielen Programmiersprachen. Auch Java
unterstützt Arrays direkt durch bestimmte syntaktische Konstrukte wie ArrayLiterale und den in diesem Kapitel eingeführten Zugriffsoperator.
Obwohl Sie sich Arrays in Java bei näherer Betrachtung als waschechte Objekte
entpuppen sind sie genau wie Strings „magisch“ in dem Sinne, dass der Java
Compiler uns durch spezielle Konstrukte einen einfacheren Umgang mit Ihnen
erlaubt. Man merkt also quasi kaum, dass es Objekte sind.
Durch die Realisierung von Arrays (wie auch Strings) als Objekte kann man
● typische Fehler (wie Zugriffe jenseits der Array-Grenzen) vermeiden
● eine Reihe von Hilfsfunktionen als Methoden anbieten (vgl. API-Referenz)
So findet bei jedem Arrayzugriff (zur Laufzeit) eine Überprüfung anhand der
Array-Grenzen statt. Die Größe des Arrays ist über eine öffentliche Konstante
abfragbar. Dadurch kann man in Java sicherer Programmieren als z.B. in C.
Der Index gibt die Anzahl der übersprungenen Elemente an (zählt ab 0...).
Der letzte Index ist Arraygröße-1.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#63
Nachtrag: Arrays(2)
Eindimensionale Arrays
ia
Deklaration
int[] ia; // Der Typ von ia ist int-Array
Int #0
Int #1
Int #2
Int #3
Initialisierung
a) mit Literal
int[] ia = {1, 6, 9}; // Länge implizit über Anzahl der Werte
b) mit neuem Objekt
int[] ia = new int[7]; // explizite Längenangabe
Zugriff
int i = ia[0]; // auslesen des 1-ten Elements (Index 0)
ia[2] = 3; // befüllen des 3-ten Elements (Index 2)
Die Felder eines Arrays sind Speicherplätze, also auch LValues!
Arrayelemente werden je nach Typ mit den jeweiligen Default-Werten initialisiert.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#64
Nachtrag: Arrays(3)
Mehrdimensionale Arrays
iaa
Deklaration
int[][] iaa; // ein int-Array-Array
Int[] #0
Int[] #1
Int #0 = 1
Int[] #2
Int #1 = 6
Initialisierung
a) mit Literal
Int #0 = 9
Int #1 = 2
int[][] iaa = { {1, 6}, {9, 2, 1}, {1}};
b) mit neuem Objekt
Int #2 = 1
Int #0 = 1
int[][] iaa = new int[7][]; // explizite Längenangabe, hinten leer
Zugriff
int i = iaa[0][1]; // 2-tes Element aus 1-tem Array lesen
iaa[2][3] = 3; // 4-tes Element aus 3-tem Array befüllen
Durch Darstellung von mehrdimensionalen Arrays durch Verschachtelung von
eindimensionalen Arrays müssen diese nicht quadratisch sein (Baum statt
Tabelle). Die Größe untergeordneter Arrays kann ggf. später festgelegt werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#65
Nachtrag: Arrays(4)
Das gedankliche Modell hinter einem einfachen Array ist Liste/Tabelle. Die
Implementierung erfolgt aber als Baum. Das kann man so oder so interpretieren:
1. Zeilenweise (1. Index = Zeile, üblich)
0,0
0,1
0
1,0
1,1
1
0
1
2
0
1
2
0
1
2
2
3
2. Spaltenweise (1. Index = Spalte)
0,0
1,0
0,1
1,1
0
1
2
3
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#66
Zugriff auf Arrays
Arrayzugriff
Mit Hilfe des Operators [ ] kann man also "in Arrays hineinschauen", entweder
um einen Wert zu lesen oder zu schreiben. Dabei gibt die Zahl innerhalb der
eckigen Klammern "die Nummer der Schublade" an (weitere Details folgen).
Beispiel:
// vorgefülltes Array aus Array-Literal erzeugen
int[] lottotipp = { 2, 5, 17, 26, 31, 35};
// neues leeres Array mit new Operator erzeugen (nächste Folie)
int[] lottozahlen = new int[6];
// Erste Lottozahl erzeugen
lottozahlen[0] = Math.random() * (49-1+1) + 1;
// Erste Lottozahl ausgeben
System.println( "1. Lottozahl=" + lottozahlen[0]);
Um die generierten Lottozahlen geschickt mit unserem Tipp vergleichen können
fehlt uns an dieser Stelle leider noch das notwendige Handwerkszeug...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#67
Ausdrücke
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#68
Ausdrücke
Ausdrücke dienen zur Berechnung von Werten (umgangssprachlich würde man
sie wohl als Formeln bezeichnen). Sie werden einerseits in Zuweisungen an
Variablen (Zustandsänderungen) und andererseits als Bedingungen in
Kontrollstrukturen (Ablaufsteuerung) verwendet.
Ausdrücke sind aus 2 Bestandteilen aufgebaut:
Operanden
● Operatoren
●
Die Operanden sind die Werte in einem Ausdruck, die Operatoren legen fest,
was mit diesen Werten passieren soll. Oder andersrum: der Operator operiert
auf dem Operand.
Die meisten Ausdrücke lassen sich relativ intuitiv hinschreiben bzw. lesen, da die
verwendeten Operatoren „alte Bekannte“ (aus der Schulmathematik) sind. Dies
gilt insbesondere für arithmetische und logische Berechnungen sowie
Vergleiche. Es gibt jedoch auch ein paar Besonderheiten...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#69
Operanden
Operanden sind die Werte in einer Berechnung. In Java können dies
Literale (feste Werte, z.B. 5)
● Variablen und Konstanten (gespeicherte Werte, z.B. a, Math.PI)
● Funktionen (berechnete Werte, z.B. Math.sin(Math.PI/2)).
●
sein. Diese können an beliebiger Stelle in einem Ausdruck stehen.
Operanden haben wie Variablen (die ja auch Operanden sind) einen Datentyp.
Dieser legt fest, welche Operatoren zu ihrer Verknüpfung gültig sind. So kann
man mit Zeichenketten z.B. nicht rechnen.
Eine ungültigen Kombination von Operanden und Operatoren quittiert der
Compiler mit einer Fehlermeldung die angibt, was er eigentlich erwartet
(expected) hat, und was er tatsächlich vorgefunden (found) hat.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#70
Operatoren(1) - Überblick
Operatoren stehen für eine Operation, d.h. die Art und Weise in der die
Operanden verknüpft werden, um ein Ergebnis zu erhalten. Formal betrachtet
sind Operatoren nur eine andere Schreibweise für Funktionen. Operanden
entsprechen also Funktionsparametern. Damit könnte man also statt dem
gewohnten 2+3 (symbolische Infix Notation) also auch 2 add 3 (Infix Notation)
oder add(2,3) (Präfix Notation) oder (2,3,add) (Suffix Notation) schreiben. Die
Infix Notation ist kürzer und für Menschen leichter lesbar. Für die maschinelle
Auswertung ist jedoch die Präfix oder Suffix Notation praktischer (vgl. UPN
Taschenrechner). Tatsächlich kommt jedoch jede dieser Schreibweisen in
irgendeiner exotischen Programmiersprache vor...
Bei der Betrachtung von Operatoren unterscheidet man folgende Eigenschaften:
Stelligkeit (Anzahl der Operanden ~ Parameter)
● Priorität (Auswertungsreihenfolge)
● Assoziativität (Bindungsrichtung)
●
Daneben ist es hilfreich, Operatoren nach ihrer Art zu gruppieren (vgl. Buch).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#71
Operatoren(2) - Eigenschaften
Stelligkeit
Gibt die Anzahl der Operanden an. Es gibt
● einstellige (unäre) Operatoren, z.B. Vorzeichen, logische Negation
● zweistellige (binäre) Operatoren, z.B. Addition (+), Multiplikation (*), ...
● dreistellige (ternäre) Operatoren, z.B. Bedingungsoperator (?:)
Funktionen mit 4 und mehr Parametern schreibt man besser als Funktion!
Priorität (siehe Tabelle)
Bestimmt die Auswertungsreichenfolge (ähnlich wie „Punkt vor Strich“)
Assoziativität (~Assoziativgesetz)
Bestimmt die Auswertungsrichtung bei gleichwertigen Operanden (siehe Buch)
● Von links nach rechts, z.B. a+b+c ~ ((a+b)+c)
● Von rechts nach links, z.B. a = 2+3 ~ a = (2+3)
Art (siehe Tabelle)
Gibt die Art des Operators an, z.B. Arithmetisch, Logisch, ...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#72
Operatoren(3) – Übersicht und Demo
Aufruf
Zugriff
Unär
[]
()
.
++
-+ ~
!
(type)
new
* / %
Arithmetik + +
<<
Schieben >>
>>>
Arrayzugriff
Methodenaufruf
Komponentenzugriff
Prä- oder Postinkrement
Prä- oder Postdekrement
Vorzeichen (unär)
Bitweises Komplement
Logische Negation
Typ-Umwandlung (cast)
Objekterzeugung
Multiplikation, Division, Rest
Addition, Subtraktion
Stringverkettung
Linkshift
Rechtsshift (mit VZ)
Rechtsshift (ohne VZ)
< <=
> >=
Vergleich kleiner/kleiner gleich
Vergleich größer/größer gleich
Vergleich instanceof Typprüfung für Objekte
==
Gleichheit
!=
Ungleichheit
&
Bitweises AND
&
Logisches AND (Complete)
^
Bitweises XOR
^
Logisches XOR
Logik
|
Bitweises OR
|
Logisches OR (Complete)
&&
Logisches AND (Shortcut)
||
Logisches OR (Shortcut)
?:
Bedingung
Bedingte Auswertung
=
Einfache Wertzuweisung
Zuweisung
<op>= Kombinierte Zuweisung
Die Prioritäten sind so sinnvoll gewählt, dass es bei komplexen gemischten Ausdrücken keine
unliebsamen Überraschungen gibt. Mit anderen Worten: An vielen Stellen sind keine Klammern nötig.
Klammern erhöhen aber oftmals die Lesbarkeit und sollten deshalb ggf. trotzdem verwendet werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#73
Der Zuweisungsoperator
Der Zuweisungsoperator hat die niedrigste Priorität (Werte müssen vor der
Zuweisung berechnet werden) und ist der einzige Operator, der von rechts nach
links ausgewertet wird (wichtig für Kettenzuweisungen). Alle anderen Operatoren
werden von links nach rechts, d.h. in Leserichtung ausgewertet.
Beispiele
int
i =
i =
i =
j =
i, j, k;
5; // einfache Zuweisung
5*2; // zuerst Berechnung, dann Zuweisung
j = k = 2; // Kettenzuweisung
4 * (k = 2);
Wie das letzte Beispiel zeigt, kann man Zuweisungen auch als Teil einer
Berechnung vornehmen. Dies ist jedoch der Lesbarkeit nicht unbedingt
zuträglich und passiert in der Praxis öfter versehentlich als absichtlich!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#74
Kombinierter Zuweisungsoperator
Um weniger schreiben zu müssen, kann der Zuweisungsoperator mit einem
binären Operator (z.B. +, *, /, %, &, |, <<, >>, ...) kombiniert werden:
op1 <op>= op2
entspricht
op1 = op1 <op> op2
Beispiele
int a
a +=
a *=
a %=
a |=
a &=
a <<=
= 1;
2; //
8; //
5; //
8; //
8; //
2; //
a
a
a
a
a
a
=
=
=
=
=
=
a +
a *
a %
a |
a &
a <<
2
8
5
8
8
2
==>
==>
==>
==>
==>
==>
a
a
a
a
a
a
= 3
= 24
= 4
= 12
= 8
= 32;
Aus Gründen der Lesbarkeit ist es allerdings oftmals besser, die ausführliche
Schreibweise zu verwenden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#75
Inkrementoperator (und Dekrementoperator)
In Java gibt es verschiedene Möglichkeiten zum Hochzählen einer Variablen:
a = a + 1; // ~ a_neu = a_alt + 1, d.h. erhöht den Wert von a
a += 1;
// Abkürzung als kombinierte Zuweisung
a++;
// Abkürzung mit Inkrement-Operator (nur 1er Schritte)
Den Inkrementoperator gibt es in zwei verschiedenen Formen:
Präinkrement ( Wert (anders) = neuer Wert; Effekt (gleich) = Erhöhung)
int a,b;
a = 1;
b = ++a; // erst (prä) a=a+1 dann b=a, d.h. b ist 2, a ist 2
Postinkrement ( Wert (anders) = alter Wert; Effekt (gleich) = Erhöhung)
int a,b;
a = 1;
b = a++; // erst b=a dann (post) a=a+1, d.h. b ist 1, a ist 2
Beide haben neben ihrem direkten Ergebnis (neuer bzw. alter Wert von a) noch
einen weiteren Effekt (Veränderung von a) und gehen daher nur für LValues!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#76
Inkrementoperator (und Dekrementoperator)
Beispiel
int a = 5;
int b = 7;
// a erhöhen
++a; // a=6, b=7
// b = a_alt, a erhöhen
b = a++; // a=7, b=6
// b erhöhen, a=b_neu - 2
a = ++b – 2; // a=5, b=7
// b = a_alt*3, a erhöhen
b = a++ * 3; // a=6, b=15
// a erhöhen, c=a_neu + b_alt, b erhöhen
int c = (++a) + (b++); // a=7, b=16, c=22
// b erhöhen, c = a_alt + b_neu, a erhöhen
c = (a++) + (++b); // a=8, b=17, c=24
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#77
Inkrementoperator (und Dekrementoperator)
Beispiel
i = 5;
System.out.println( "Postinkrement");
System.out.println( 4 * i++);
// 4 * 5
System.out.println( i);
i = 5;
System.out.println( "Präinkrement");
System.out.println( 4 * ++i);
// 4 * 6
System.out.println( i);
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#78
Inkrementoperator (und Dekrementoperator)
Beispiel
// schwer verständliche Fälle
i = 6;
System.out.println( 4 * i++ * i); // 4 * 6 * 7 = 168
System.out.println( i);
i= 6;
System.out.println( 4 * i * i++); // 4 * 6 * 6 = 144
System.out.println( i);
i = 6;
System.out.println(
System.out.println(
i = 6;
System.out.println(
System.out.println(
4 * ++i * i); // 4 * 7 * 7 = 196
i);
4 * i * ++i); // 4 * 6 * 7 = 168
i);
Fazit: In der Praxis lieber in mehreren Schritten (nicht überoptimieren)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#79
Einschub: Zuweisungen – RValues und LValues
Zuweisungen sind streng genommen keine Ausdrücke sondern Anweisungen
(passen aber ganz gut hierher). Bei Ihnen muss man unterscheiden zwischen
LValue = Wert der auf der links vorkommen kann (mit Speicherplatz)
● RValue = Wert, der auf der rechts vorkommen kann (ohne Speicherplatz)
●
Eigentlich logisch, denn zum zuweisen(=speichern) braucht man eben einen
Speicherplatz. In Java können nur Variablen (inkl. Arrays!) als LValues auftreten.
Im rechten Teil einer Zuweisung sind dagegen alle Arten von Operanden erlaubt.
Damit gilt für die Syntax von Zuweisungen:
<Variable> = <Ausdruck>;
Beispiele:
a = 5;
x = a + 2;
v = sin(a);
lottozahlen[1] = 49; // !!!
© Andreas Rau, 19.11.10
Operanden
RValues
Literale
Funktionen
LValues
Variablen
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#80
Der Bedingungsoperator ?:
Der Bedingungsoperator ist in mehrerer Hinsicht speziell: Er ist der einzige
Operator mit mehr als 2 Operanden und besteht aus 2 getrennten
Operationsymbolen. Sein Aufbau ist
(<Bedingung> ? <Wert1> : <Wert2>)
Die Bedingung muss ein Ausdruck vom Typ boolean sein, die Typen von Wert1
und Wert2 sind beliebig, müssen aber gleich sein. Die Klammern sind kein
Bestandteil des Bedingungsoperators selbst, sind aber oft notwendig um die
Operanden des Bedingungsoperators vor anderen Operatoren zu schützen – er
hat nämlich mit die niedrigste Priorität
Der Wert eines bedingten Ausdrucks ist
Wert1 wenn die Bedingung wahr ist
● Wert2 wenn die Bedingung falsch ist
●
Beispiel
int abs = ( z >= 0 ? z : -z); // berechnet den Betrag von z
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#81
Arithmetische Operatoren
Bei den arithmetischen Operatoren finden wir in erster Linie alte Bekannte aus
der Mathematik:
Addition +
● Subtraktion ● Division /
● Multiplikation *
●
Zu beachten ist jedoch, daß der Divisionsoperator je nach Datentyp eine andere
Bedeutung hat: Bei Fließkommazahlen (z.B. double) wird eine echte Division
durchgeführt, bei Ganzzahlen (z.B. int) wird nur das Ganzahlergebnis
berechnet. Zur Ermittlung des Teilungsrests gibt es einen neuen Operator
●
Modulo %
Beispiele
5.0 / 2 = 2.5; // Gleitkomma Divisionsoperator
5 / 2 = 2;
// Ganzzahl Divisionsoperator (DIV)
5 % 2 = 1;
// Ganzzahl Modulo Operator (MOD)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#82
Arithmetische Operatoren und Typkonvertierungen(1)
Beim Rechnen mit verschiedenen numerischen Datentypen wird automatisch mit
der höchsten Genauigkeit gerechnet, d.h. der genaueste Datentyp in einem
Ausdruck bestimmt den Datentyp des Resultats.
double > float > long > int > byte > char
Alle anderen Werte werden automatisch in diesen Datentyp konvertiert. Diese
automatische Typkonvertierung funktioniert nur „von unten nach oben“ und wird
auch Beförderung (Promotion) genannt. In Gegenrichtung würde dagegen ein
Informationsverlust auftreten. Deswegen funktioniert zwar die Zuweisung von int
nach long aber nicht umgekehrt. Hier ist eine manuelle Konvertierung notwendig.
long x = 34; // geht
int y = 34L; // geht nicht (possible loss of precision)
Der Datentyp auf der linken Seite einer Zuweisung wird jedoch bei der
automatischen Typkonvertierung bei der Berechnung noch nicht berücksichtigt.
float f = 2/3; // rechts nur int, also rechnen mit int. Ergebnis ist gleich Null!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#83
Arithmetische Operatoren und Typkonvertierungen(2)
Bei einer manuellen Typkonvertierung muss der Zieldatentyp explizit mit Hilfe
des cast-Operators angegeben werden. Der Compiler unterstellt dem
Programmierer dann, dass er weiss was er tut, und erspart sich
Fehlermeldungen wegen möglichem Informationsverlust.
Beispiel:
int i = (int)5L; // expliziter cast von long nach int
Später werden wir sehen, dass der cast-Operator auch zum konvertieren
(genauer: uminterpretieren) von Objektreferenzen verwendet werden kann. So
kann man z.B. ein Auto als Fahrzeug interpretieren.
Ein solcher upcast (in der Vererbungshierarchie nach oben) ist streng
genommen nicht nötig („gelingt immer und klebt nicht“). Anders sieht das bei
downcasts aus (in der Vererbungshierarchie). Wenn wir versuchen ein Objekt
das wir momentan „durch die Fahrzeug Brille“ betrachten plötzlich als Auto zu
interpretieren, es sich in wirklichkeit aber um ein Boot handelt, gibt das einen
Laufzeitfehler. Diese Richtung ist also mit Risiken behaftet. Das ist im Prinzip
genauso wie bei den Zahlen (up = zum allgemeineren, d.h. größeren Typ).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#84
Arithmetische Operatoren und Typkonvertierungen(3)
Beispiele bzgl. „loss of precision“ (Zuweisung „groß an zu klein“)
// Überzählige Bits werden abgeschnitten
int i = 256;
byte b = (byte)i;
System.out.println( “b=“ + b); // ergibt 0
00000000 00000000 00000001 00000000 int (4 Byte)
00000000 byte (1 Byte)
Nach dem Abschneiden bleibt
nichts übrig
// Achtung: Das übrige Bit wird als Vorzeichenindikator uminterpretiert
int i = 128;
byte b = (byte)i;
Vorzeichen-Indikator wird 1;
Deutung nach 2erKomplement
System.out.println( “b=“ + b); // ergibt -128
00000000 00000000 00000000 10000000 int (4 Byte)
10000000 byte (1 Byte)
© Andreas Rau, 19.11.10
10000000 = negativ
01111111 Komplement
+1 +1
10000000 = 128
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#85
Relationale Operatoren
Relationale Operatoren vergleichen zwei Werte und setzen sie somit in
Beziehung (Relation). Das Ergebnis ist dabei stets ein Wahrheitswert.
Die verwendeten Operatorsymbole unterscheiden sich etwas von den aus der
Mathematik gewohnten, sind aber trotzdem einigermaßen verständlich:
Beispiele (alle true)
int a = 2;
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
a
a
a
a
2
5
<
3);
<= 2);
>
0);
>= 1);
== a);
!= a);
//
//
//
//
//
//
kleiner
kleiner gleich
größer
größer gleich
gleich (Schubladeninhalt!)
ungleich ('!' = Negation)
Hinweis: Das Symbol '=' hat bereits die Bedeutung 'Zuweisung'. Daher wird zum
direkten Vergleich '==' verwendet. Durch die Umkehr der Operandenreihenfolge
kommt es bei Schreibfehlern nicht zu einer unerwünschten Zuweisung.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#86
Logische Operatoren
Die logischen Operationen AND und OR stehen in Java in zwei Ausprägungen
zur Verfügung: Einer "effizienten" und einer "gründlichen".
Die "effiziente" Variante (&& bzw. ||) bricht die Berechnung ab, sobald das
Ergebnis feststeht. Dies ist dann der Fall, sobald in einem AND Ausdruck der
Wert false bzw. in einem OR Ausdruck der Wert true auftaucht. Weitere
Operanden werden dann nicht mehr betrachtet und so unnötiger
Rechenaufwand, z.B. zur Auswertung von Teilausdrücken, vermieden.
Die "gründliche" Variante (& bzw. |) wertet stets alle Operanden aus. Dieses
scheinbar sinnlose Vorgehen ist immer dann wichtig, wenn die Auswertung
eines Operanden neben dem Rechenergebnis noch weitere Effekte hat. Diese
werden als Seiteneffekte bezeichnet. Solche Seiteneffekte sollten zwar generell
vermieden werden, dies ist jedoch nicht immer möglich. Typische Beispiele für
solche Seiteneffekte sind Änderungen von Variablen oder Ausgaben auf dem
Bildschirm oder in Dateien / Datenbanken.
Die Operatoren NOT (!) und XOR (^) gibt es nur in einer Ausprägung (Warum?).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#87
Sinnvolle Prioritäten?
Um gemischte Ausdrücke leichter verstehen und manuell berechnen zu können,
merke man sich folgende Auswertungsreihenfolge:
Arithmetische Operatoren (Rechnen) vor
● Relationalen Operatoren (Vergleichen) vor
● Logischen Operatoren (Verknüpfen)
●
Variablen werden vor Beginn der Auswertung eingesetzt, Funktionen werden
berechnet, wenn alle ihre Parameter berechnet sind(=verfügbar sind),
Zuweisungen erfolgen erst nach kompletter Auswertung des Ausdrucks.
Beispiel (mit a=1, b=2, c=3, d=Math.PI)
bool b = a+b<5 && c != 0
//
= 1+2<5 && 3 != 0
//
=
3<5 && 3 != 0
//
= true && true
//
=
true
//
b =
true
© Andreas Rau, 19.11.10
&&
&&
&&
&&
Math.sin(d/2) > 0;
Math.sin(Math.PI/2) > 0
1 > 0
true
Einsetzen
Rechnen
Vergleichen
Verknüpfen
Zuweisen
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#88
Bitoperatoren
Bitoperationen operieren auf den Bits einer (Ganz)zahl. Um zu verstehen, was eine
Bitoperation mit einer Zahl macht muss man sich daher auf die Bitebene begeben.
Beispiele
17 & 4 = 10001 & 00100 = 00000 = 0
12 | 13 = 1100 | 1101 = 1101 = 13
(neutrales Element = 1)
(neturales Element = 0)
Bitoperationen verhalten sich also im Prinzip genauso wie die logischen Operatoren. Statt
true und false verknüpfen sie 1 und 0 – und zwar mehrere auf einmal (wieviele hängt
wieder von der Anzahl der Bits im Datentyp ab). Dabei bezeichnet man den zweiten
Operanden oft als (Bit)maske. Diese wird zur besseren Klarheit i.d.R. in Hex angegeben.
Mit Hilfe der Bitoperationen kann man insbesondere einzelne Bits gezielt ein- (ODER mit
1er Maske) und ausschalten (UND mit 0er Maske). Letzteres kann auch dazu verwenden,
gezielt einzelne Bits zu testen (Bit war gesetzt, wenn Ergebnis ungleich 0).
Beispiele
13 & 0x03 = 1101 & 0100 = 0100 = 4 // Bit 2 (2^2=4) testen
13 | 0x02 = 1101 | 0010 = 1111 = 15 // Bit 1 (2^1=2) setzen
13 & ~0x08 = 1101 | 0111 = 0101 = 5 // Bit 3 (2^3=8) löschen
Zum veranschaulichen und üben ist auf der Homepage der BitManipulator verfügbar.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#89
Schiebeoperatoren
Schiebeoperatoren arbeiten auf der Binärdarstellung von Ganzzahlen. Aufgrund
des Stellenwertsystems werden beim Verschieben nach links bzw. rechts alle
Stelle um eine Potenz auf- bzw. abgewertet, d.h. beim Bitschieben gilt
● linksschieben ~ *2
● rechtsschieben ~ /2
Doch warum gibt es beim Rechtsschieben zwei verschiedene Operatoren? Bei
der Anwendung des Zweierkomplements fällt auf, dass für negative Zahlen das
MSB (most significant bit – das höchstwertige Bit ganz links) stets 1 ist. Damit ist
es geeignet, das Vorzeichen anzuzeigen (ist aber kein Vorzeichenbit!).
Um das Vorzeichen zu erhalten, muss das jeweils richtige Bit reingeschoben
werden (beim vorzeichenlosen shiften wird links wie rechts 0 reingeschoben).
Beispiel
+5>>1 == 0101>>1 == 0010 == +2 (Ganzzahldivision durch 2)
-5>>1 == 1011>>1 == 1101 == -3 (Mit VZ 1101, ZK: 0010, 0011 = 3!)
-5>>>1== 1011>>1 == 0101 == +5 (Ohne VZ, 1011 als 11 interpretiert)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#90
Mathematische Funktionen(1)
Zum Rechnen sind neben Operatoren hin und wieder auch mathematische
Grundfunktionen wie sin(), cos() oder auch Konstanten wie PI oder e notwendig.
Diese stehen auch in Java zur Verfügung. Allerdings sind kein Bestandteil der
Sprachsyntax von Java sondern "ganz normal" über eine Klasse implementiert.
Diese Klasse heißt Math und ist Teil der Java Bibliothek (vgl. API Doku). Alle in
dieser Klasse enthaltenen mathematischen Funktionen und Konstanten sind als
Klassenmethoden bzw. Klassenvariabeln realisiert. Der Zugriff darauf ist direkt
über die Klasse möglich.
Beispiele
Math.PI
Math.sqrt(2)
Das "Math." ist immer notwendig, es gibt keine "freischwebenden" Funktionen!
Weitere Funktionen und Konstanten kann man in der Online-Hilfe (die werden
wir ab sofort noch öfters brauchen. Kann sich ja kein Mensch alles merken...)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#91
Mathematische Funktionen(2)
Erzeugung von Zufallszahlen
Zur Erzeugung von Zufallszahlen existiert in der Klasse Math die Funktion
random(). Diese liefert eine Gleitkomma Zufallszahl zwischen 0 inkl. und 1 exkl.
Um ganzzahlige Zufallszahlen in einem anderen Bereich zu erzeugen, z.B.
zwischen 5 und 10, sind weitere Rechenoperationen notwenig:
Grundfunktion
Math.random()
liefert
[0...1[
Intervall vergrößern (aufblasen)
Math.random()*6
liefert
[0...6[
Intervall verschieben
Math.random()*6 + 5
liefert
[5...11[
Ganzzahlige Werte ausschneiden
(int)(Math.random()*6 + 5)
liefert
[5, 6, 7, 8, 9, 10]
Dabei ist 6 = Obergrenze – Untergrenze + 1 und 5 = Untergrenze (Warum nur?)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#92
Erzeugung und Zugriff auf Objekte
new-Operator
Der new-Operator dient zur Erzeugung und Initialisierung von Objekten. Dazu
sind neben dem Operator mindestens der Klassenname nötig (später mehr).
Punktoperator
Mit Hilfe dieses Operators kann man "in Objekte hineinschauen". Dabei sind
zwei Dinge zu beachten:
●
●
Objekte sind wie Rekords, d.h. ihre Elemente haben Namen. Also muss
hinter dem Punkt ein Name stehen (und davor ein Objekt. Immer!)
Objekte können nicht nur Daten sondern auch Methoden enthalten. Um sie
verwenden zu können wird zusätzlich der Funktionsaufruf-Operator benötigt.
Beispiele
Person p = new Person(); // Objekt vom Typ Person erzeugen
p.name = "Mustermann"; // name der Person setzen
p.vorname = "Max"; // vorname der Person setzen
Natürlich geht das in der Praxis meist anders (nämlich über Zugriffsmethoden),
da ein direkter Zugriff ja das Prinzip der Kapselung verletzen würde.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#93
Aufruf von Funktionen
Funktionsaufruf-Operator
Dieser Operator dient, nunja, zum Aufruf von Funktionen. Diese sind in Java
grundsätzlich in einem Objekt oder einer Klasse enthalten und werden als
Methoden bezeichnet. Daher tritt der Funktionsaufruf meist in Verbindung mit
dem Punktoperator auf (Ausnahmeregeln lernen wir später kennen).
Beispiel
System.out.println( Integer.toString()); // Integer ausgeben
Dieses Beispiel demonstriert außerdem die Möglichkeit, Objektzugriffe mit dem
Punktoperator zu verketten. Dabei gilt weiterhin, dass links vom Punkt immer
ein Objekt stehen muss. Dies bedeutet, dass das Datenfeld der Klasse System
System.out
ein Objekt sein muss, welches eine Methode namens println() enthält. Dies
wird durch einen Blick in die API Doku bestätigt (wird sowieso Zeit, dass wir die
kennenlernen – siehe Klassen System und PrintStream).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#94
Vorschau: Kopieren von Werten bei Funktionsaufrufen(1)
Ablauf eines Funktionsaufrufs
public class Doppler {
public static int verdoppeln( int wert) {
int dasDoppelte = wert * 2;
return dasDoppelte;
}
}
public static void main(String[] args) {
int x, y;
x = 1;
1. wert = x (aktueller Parameter)
y = verdoppeln( x);
2. Funktion ausführen
3. y = dasDoppelte (Rückgabewert)
System.out.println( y);
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#95
Vorschau: Kopieren von Werten bei Funktionsaufrufen(2)
Für das Kopieren der Parameter und des Rückgabewerts beim Funktionsaufruf
gelten dieselben Regeln wie für das Kopieren von Variablen:
Primitive Datentypen
werden als Wert übergeben. Änderungen des Wertes innerhalb der Funktion
haben also außerhalb der Funktion keine Auswirkungen.
Referenztypen
werden als Referenz übergeben (Call-by-Reference). Änderungen des Objekts
innerhalb der Funktion verändern das Originalobjekt, d.h. sie wirken sich auch
außerhalb der Funktion aus (die Referenz bleibt jedoch unverändert!).
Um unabsichtliche Fehler zu vermeiden ist es wichtig, diesen Unterschied zu
kennen. Ungeachtet dessen haben beide Aufrufvarianten ihren Sinn: Kopien von
primitiven Datentypen sind "billig", Objekte zu kopieren wäre i.d.R. zu teuer.
Daher werden primitive Werte als Kopie übergeben (und ggf. nach Veränderung
explizit zurückgegeben) während Objekte direkt verändert werden können.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#96
Nachtrag: Wrapperklassen
Auch beim Umgang mit elementaren Datentypen benötigt man gelegentlich die
eine oder andere zusätzliche Funktion. Da man diese nicht in den elementaren
Typen selbst unterbringen kann, gibt es zu jedem elementaren Datentypen eine
zugehörige sog. Wrapperklasse die diese Methoden enthält (analog zu Math).
Beispiele (für int / Integer, weitere Wrapper Klassen siehe Buch)
int max = Integer.MAX_VALUE; // größten Wert des Typs abfragen
int i = Integer.parseInt( "17"); // Zeichenkette "als int deuten"
String s = Integer.toString( i); // Zahl in Zeichenkette wandeln
Der Name Wrapperklasse kommt daher, dass die Objekte dieser Klassen auch
dazu verwendet werden können, um Werte des zugehörigen Typs "als Objekt zu
verpacken" - rechnen kann man damit Objekten dann allerdings nicht mehr.
Beispiele
Integer i = new Integer( 5); // einpacken
int j = i.intValue(); // auspacken
Ab Java 5.0 erfolgt dieses Ein- und Auspacken automagisch (Autoboxing).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#97
Nachtrag: Wrapperklassen(2)
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
toHexString()
x
x
x
x
x
x
x
toOctalString()
x
x
x
x
x
x
x
x
x
toBinaryString()
x
toString()
valueOf()
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
decode()
doubleValue()
floatValue()
longValue()
intValue()
shortValue()
byteValue()
charValue()
booleanValue()
parseDouble()
parseFloat()
parseLong()
parseInt()
parseShort()
parseByte()
Wrapperklasse
Boolean
x
Character
Byte
Short
Integer
Long
Float
Double
parseChar()
Datentyp
boolean
char
byte
short
int
long
float
double
parseBoolean()
Übersicht über alle Wrapperklassen und deren wichtigste Eigenschaften:
x
x
x
Boolean und Character sind speziell; die Wrapperklassen für Zahlen (Numbers)
haben viele gemeinsame Eigenschaften. Integer bietet spezielle Unterstützung
für verschiedene Zahlensysteme.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#98
Nachtrag: Die Hilfsklasse Arrays
Ähnlich wie die Wrapperklassen für primitive Datentypen bietet die Hilfsklasse
Arrays einige nützliche Funktionen für den Umgang mit Arrays, z.B. zum
durchsuchen und zum sortieren.
Einige ausgewählte Funktionen (weitere siehe API Doku)
import java.util.Arrays;
public class ArrayFunctions {
}
public static void main( String[] args) {
int[] ia = { 1, 6, 3, 8, 4, 9};
System.out.println( Arrays.toString( ia));
Arrays.sort( ia); // sortiert das Array
System.out.println( Arrays.toString( ia));
int i = Arrays.binarySearch( ia, 3); // liefert Index 1(!)
System.out.println( "Found 3 at index " + i);
Arrays.fill( ia, 0); // füllt das Array mit 0
System.out.println( Arrays.toString( ia));
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp
#99
Nachtrag: Strings
Stringfunktionen kurz vorgestellt
Zeichenketten werden in Java als Objekte der Klasse String repräsentiert (und
nicht etwa als Array von Zeichen – der Grund dafür ist die Kapselung). Mit
diesen Objekten kann man zwar nicht "rechnen" aber doch einige interessante
Dinge tun. Die zugehörigen Operationen sind als Funktionen implementiert die in
der Klasse String zu finden sind (hier wird der Vorteil der Zusammenfassung von
Daten und Methoden zu Klassen deutlich).
Einige praktische Stringfunktionen (weitere siehe API Doku)
String s = "das ist eine Zeichenkette";
s.length(); // Länge der Zeichenkette ermitteln
s.charAt(1); // einzelne Zeichen lesen (schreiben ist nicht!)
s.indexOf( 'e'); // Zeichen suchen vorwärts (liefert 8)
s.lastIndexOf( 'e'); // Zeichen suchern rückwärts (liefert 24)
s.substring(4,7); // Teilstring, hier "ist"
s.equals( "eine andere Zeichenkette"); // Vergleich, hier false
Anmerkungen: Strings in Java sind nicht veränderbar (immutable). Daher kann
der Compiler gefahrlos identische Stringliterale im Speicher zusammenfassen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #100
Beispielprogramme(1)
Mit Ausdrücken kann man bereits eine Menge anfangen, z.B.
Zahlen konvertieren (Konverter1.java)
● Zahlen konvertieren (Konverter2.java)
● Zahlen konvertieren (Konverter3.java)
● Zahlen konvertieren (Konverter4.java)
● Zahlen konvertieren (Konverter5.java)
●
Man kann natürlich auch noch andere Dinge tun ;-). Aber das Beispiel
passt zu dem vorher behandelten Lernstoff über Zahlensysteme
● demonstriert die Anwendung verschiedener Operatoren
● zeigt eindrucksvoll: "there's more than one way to do it"*
●
Die obigen Beispielprogramme verwenden zur Eingabe von der Tastatur ein
Objekt einer Hilfsklasse namens "CommandLine". Diese Klasse kapselt
Techniken, die wir erst später kennenlernen werden.
*dieser Slogan gehört eigentlich zu einer anderen Programmiersprache, der Skriptsprache Perl
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #101
Kontrollstrukturen
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #102
Kontrollstrukturen(0)
Wie wir wissen sind Computer universelle oder zumindest sehr flexible
Maschinen. Diese Flexibilität rührt daher, dass sie programmierbar sind, hat also
etwas mit den Programmen zu tun. Dies hat im wesentlichen 3 Gründe
1. Man kann verschiedene Programme ablaufen lassen
Dies führt offensichtlich zu Flexibilität, weil verschiedene Programme natürlich
auch verschiedene Dinge tun können.
2. Ein Programm kann benutzerdefinierte Eingaben verarbeiten
Hierzu kann man ein Programm als spezielle Formel interpretieren (die wir mit
Hilfe von Ausdrücken implementieren können). Durch verschiedene Eingaben
kann diese Formel auch unterschiedliche Ausgaben produzieren.
3. Ein Programm kann verschiedene Berechnungspfade enthalten
Anders als die mathematischen Formeln die wir kennen, kann ein Programm
Varianten enthalten, z.B. um Sonderfälle zu behandeln. Man kann also ähnliche
Formeln in einem Programm zusammenfassen. Die dazu notwendigen
Konstrukte werden auf den folgenden Seiten beschrieben.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #103
Kontrollstrukturen(1)
Kontrollstrukturen dienen der Ablaufsteuerung. Während Ausdrücke festlegen,
was passiert, legen Kontrollstrukturen fest wann bzw. in welcher Reihenfolge.
In der Theorie kennt man 3 elementare Arten von Kontrollstrukturen. Dies sind:
Die Sequenz – Schritte aufzählen (Blöcke)
● Die Verzweigung - Alternativen auswählen (if-else, switch-case)
● Die Wiederholung – Dinge mehrmals tun (while, for, do-while)
●
Praktisch werden diese in Java durch mehr als 3 Konstrukte abgedeckt.
Hintergrund dieser elementaren Kontrollstrukturen ist die sog. strukturierte
Programmierung. Dabei soll der Begriff strukturiert (das Gegenteil von
unstrukturiert) andeuten, dass dem resultierenden Programm eine Struktur
zugrundeliegen soll die durchdacht, erkennbar und verständlich ist.
Im Gegensatz dazu spricht man von sog. Spaghetti-Code, wenn die
Programmstruktur nicht nachvollziehbar ist – ein Nebeneffekt des
hemmungslosen Einsatzes von Sprunganweisungen (goto). In diesem
Zusammenhang gibt es auch den Begriff des "Kündigungsschutzprogramms"...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #104
Kontrollstrukturen(2)
Sequenz / Blöcke
Eine Sequenz ist eine Folge von Anweisungen die – wie ein Kochrezept – Schritt
für Schritt und ohne wenn und aber abgearbeitet werden sollen und werden in
Java werden durch Blöcke realisiert.
Eigenschaften von Blöcken
● Blöcke bündeln mehrere Anweisungen zu einer Sequenz und können überall
dort stehen, wo eine einzelne Anweisung erwartet wird. Der Inhalt eines Blocks
wird dabei durch die beiden geschweiften Klammern begrenzt. Am häufigsten
werden sie verwendet um mehrere Anweisungen im Inneren anderer
Kontrollstrukturen unterzubringen.
●
Es ist möglich, innerhalb eines Blocks Variablen zu deklarieren, die außerhalb
des Blocks nicht mehr gültig sind. Blöcke bilden also einen eigenen
Gültigkeitsbereich. Somit kann man Hilfsvariablen dort definieren, wo man sie
braucht, ohne Platz im Speicher oder im Namensraum zu verschwenden.
Gleichzeitig verbessert sich die Lesbarkeit, wenn Deklaration und
Verwendungsort von Variablen nahe beeinander liegen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #105
Kontrollstrukturen(3)
Noch: Eigenschaften von Blöcken
● Blöcke können verschachtelt werden. Die resultierenden Gültigkeitsbereiche
sind dann ebenfalls geschachtelt. Dabei gilt, die inneren Blöcke "sehen" die
äußeren Variablen, aber nicht umgekehrt.
public static void main(String[] args) {
int i;
{
int j;
{
int k;
// hier sind i, j und k sichtbar
}
// hier sind nur i und j sichtbar
}
// hier ist nur i sichtbar
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #106
Kontrollstrukturen(4)
Verzweigung / if-else, switch-case
Eine Verzweigung ist gewissermaßen eine Weggabelung. Sowas kommt in
Kochrezepten eher selten vor. In Programmen erlaubt uns dies, auf
verschiedene Gegebenheiten zu reagieren bzw. verschiedene Fälle zu
unterscheiden. Verzweigungen werden in Java werden durch if-else- und switchcase-Konstrukte realisiert.
Eigenschaften von if-else
● Ein
if-else-Konstrukt besteht aus einer Bedingung und einem oder zwei
Anweisungszweigen, wobei der zweite optional ist. Ist die Anweisung wahr,
wird der erste Zweig durchlaufen, andernfalls der zweite. Mit if-else lassen sich
also entweder-oder Entscheidungen darstellen.
●
Es ist möglich, if-else-Konstrukte zu verschachteln, indem innerhalb des if oder
else-Zweigs weitere if-else-Konstrukte untergebracht werden. Auf diese Weise
lassen sich Entscheidungsketten bildet. Damit kann man mehr als zwei Fälle
unterscheiden bzw. sich mit immer spezielleren Fragen seinem Ziel nähern.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #107
Kontrollstrukturen(5)
public static void main(String[] args) {
int i = (int)((Math.random() * 10) + 1);
if ( (i%2)==0) {
System.out.println("Gerade!");
} else {
System.out.println("Ungerade!");
}
}
Es empfielt sich, den if- und den else-Teil grundsätzlich als Block zu klammern.
Dadurch verbessert man die Lesbarkeit und vermeidet außerdem Fehler beim
"hinzufügen" von Anweisungen (sog. "dangling else"-Problem)
if ( (i%2)==0)
System.out.println("Gerade!");
else
System.out.println("Ungerade!");
i=0; // das passiert immer, nicht nur bei else!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #108
Kontrollstrukturen(6)
Beispiel für beidseitig geschachtelte if- else- Anweisungen.
Damit kann man sich einer Diagnose Schritt für Schritt nähern oder einen Ablauf
steuern, z. B. einen Benutzerdialog. Hat gegenüber komplexeren Bedingungen
den Vorteil der leichteren Verständlichkeit, kommt im Zeitalter der grafischen
Oberflächen aber in der Praxis dennoch eher selten vor.
Frage 1
ja
ja
Frage 2a
Aktion 3aa
© Andreas Rau, 19.11.10
nein
nein
ja
Frage 2b
Aktion 3ab Aktion 3ba
nein
Aktion 3bb
if (<Frage1>) {
if (<Frage2a>) {
<Aktion3aa>;
} else {
<Aktion3ab>;
}
} else {
if (<Frage2b>) {
<Aktion3ba>;
} else {
<Aktion3bb>;
}
}
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #109
Kontrollstrukturen(7)
Beispiel für einseitig geschachtelte if- else- Anweisungen
Damit kann man nahezu beliebige Fallunterscheidungen darstellen. Aktion 4
repräsentiert hierbei den sonst- Fall (keiner der vorigen Fälle traf zu). Oft als
Ersatz für switch-case! Theoretisch ist eine einseitige Verschachtelung auch im
if- Zweig möglich, diese Version kommt aber in der Praxis sehr selten vor.
ja
Frage 1
Aktion 1
ja
Aktion 2
nein
Frage 2
ja
Aktion 3
© Andreas Rau, 19.11.10
nein
Frage 3
nein
if (<Frage1>) {
<Aktion1>;
} else if (<Frage2>) {
<Aktion2>;
} else if (<Frage3>) {
<Aktion3>;
} else {
<Aktion4>;
}
Aktion 4
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #110
Kontrollstrukturen(8)
Eigenschaften von switch-case
● Ein
switch-case-Konstrukt besteht aus einem Ausdruck, der einen
elementaren Datentyp(!) ergibt, und mehreren Fällen, die durch konstante
Werte(!) dieses Datentyps charakterisiert sind. Es wird der Fall ausgeführt,
dessen Wert mit dem Wert des Ausdrucks übereinstimmt. Daneben gibt es
noch einen (optionalen) default-Fall der dann ausgeführt wird, wenn kein
anderer passt. Mit switch-case lassen sich also Fallunterscheidungen
darstellen. Der Ausdruck ist dabei oftmals der Name einer Variablen, die Werte
Literale. Das muss aber nicht so sein.
●
●
Nach der Abarbeitung eines Falles wird die Ausführung mit dem nächsten Fall
fortgesetzt (sog. "fall through") – es sei denn, der erste Fall endet mit einer
break-Anweisung. Praktisch braucht das nahezu niemand und man sollte sich
angewöhnen, die break-Anweisung immer hinzuschreiben. Braucht man sie
ausnahmsweise mal nicht, ist das mit Sicherheit einen Kommentar wert...
Es ist theoretisch möglich, switch-case-Konstrukte zu verschachteln. Allerdings
macht das praktisch kein Mensch, weil da niemand mehr durchblickt.
Fallunterscheidungen sollten eindimensional sein!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #111
Kontrollstrukturen(9)
switch (i-j) {
case 0:
System.out.println( "Volltreffer");
break;
case -1: // fall-through
case -2: // fall-through
case -3:
System.out.println( "Knapp drunter");
break;
case +1: // fall-through
case +2: // fall-through
case +3:
System.out.println( "Knapp drüber");
break;
default:
System.out.println( "Voll daneben");
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #112
Kontrollstrukturen(10)
Vergleich von if-else und switch-case
Das switch-case-Konstrukt ist ein Sonderfall einer if-else-Kette. Mit seiner Hilfe
lassen sich bestimmte Fallunterscheidungen, nämlich solche die durch exakte
Werte eines elementaren Datentyps charakterisiert sind, einfach und
übersichtlich darstellen.
Im Gegensatz dazu kann man mit Hilfe einer if-else-Kette beliebige
Bedingungen mit beliebigen Datentypen und damit beliebige Fälle
darstellen, z.B. Bereiche von Zahlen oder Vergleiche von Zeichenketten. Das ifelse-Konstrukt ist also mächtiger als switch-case.
Zusätzlich kann man mit einer if-else-Kette durch die Reihenfolge der
Bedingungen eine Priorisierung der Fälle ausdrücken. Der häufigste Fall
kommt zuerst und wird dann auch am schnellsten abgearbeitet. Zwar kann man
auch bei switch-case die Reihenfolge bewußt wählen, einen direkten Einfluß auf
die Abarbeitungsreihenfolge hat man aber damit nicht.
In beiden Konstrukten kann der else-Zweig bzw. default-Fall verwendet werden,
um vergessene Fälle zu entdecken. Auch wenn man glaubt, dass ein solcher
Fall nie eintritt, lohnt es sich manchmal, seinen Glauben durch einen else-Zweig
mit einem entsprechenden Kommentar der Welt mitzuteilen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #113
Kontrollstrukturen(11)
Fallunterscheidung mit einzelnen Werten
Fallunterscheidung mit Wertebereichen
if (i==0) {
System.out.println("Null");
} else if (i==1) {
System.out.println("Eins");
} else {
System.out.println("???");
}
if (0<=i && i<10) {
// klein
} else if (10<=i && i<100) {
// mittel
} else if (100<=i && i<1000) {
// gross
} else {
// wow!
}
switch (i) {
case 0:
System.out.println("Null");
break;
case 1:
System.out.println("Eins");
break;
default:
System.out.println("???");
}
© Andreas Rau, 19.11.10
// mit switch nicht möglich
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #114
Kontrollstrukturen(12)
Schleifen / while, for, do- while
Eine Schleife weist den Rechner an, Dinge mehrmals zu tun und erspart es dem
Programmierer, diese Dinge mehrfach hinschreiben zu müssen. Erkauft wird
diese Bequemlichkeit durch den Aufwand für die Schleifenverwaltung. In der
Echtzeitprogrammierung werden Schleifen deshalb manchmal wieder
"ausgerollt" (loop unrolling), d. h. man schreibt doch wieder alles mehrmals hin.
Man unterscheidet zwischen fußgesteuerten Schleifen (erst machen, dann
prüfen) und kopfgesteuerten Schleifen (erst prüfen, dann machen).
Fußgesteuerte Schleifen werden stets mindestens einmal durchlaufen. Bei
kopfgesteuerten Schleifen kann es auch vorkommen, dass Sie nie durchlaufen
werden. Deswegen werden sie auch abweisende Schleifen genannt.
Das abweisende Verhalten von kopfgesteuerten Schleifen ist z. B. dann wichtig,
wenn in der Schleife Referenzen benutzt werden, die auch null sein können. Die
Bedingung einer abweisenden Schleife dient also oftmals auch zum Schutz vor
Fehlern, die im Schleifenrumpf passieren könnten (sog. guard condition).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #115
Kontrollstrukturen(13)
Gemeinsame Eigenschaften von Schleifen
Eine Schleife besteht im aus drei Bestandteilen:
Die Initialisierung stellt den Anfangszustand für die (Variablen in der)
Abbruchbedingung her. Dies sollte kurz vor der Schleife passieren, andernfalls
ist das Verständnis der Schleife erschwert.
Der Schleifenrumpf arbeitet auf das Ziel hin. Er muss die Abbruchbedingung
beeinflussen, sonst terminiert die Schleife nie (Endlosschleife). Eine notwendige
Bedingung dafür ist, dass der Schleifenrumpf mindestens eine der Variablen
verändert, die in der Abbruchbedingung vorkommen.
Eine Schleife verfolgt i. d. R. ein bestimmtes Ziel und wird solange ausgeführt,
bis dieses Ziel erreicht ist. Dieses Ziel wird durch den Zustand von bestimmten
Variablen ausgedrückt und in der Abbruchbedingung überprüft. Bei einer
kopfgesteuerten Schleife erfolgt diese Prüfung vor dem Schleifenrumpf, bei einer
fußgesteuerten dannach. In Java wird das negierte Ziel angegeben ("weiter
solange Ziel nicht erreicht").
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #116
Kontrollstrukturen(14)
Eigenschaften von while
Die while- Schleife ist eine kopfgesteuerte Schleife.
Beispiel
// Suche einen Wert in einem Array,
// gebe den index aus wenn gefunden, -1 sonst
int[] values = { 1,6,3,8,9,4,2};
int value = 2;
int result;
int index = 0;
while (index < values.length && values[ index]!= value) {
++ index;
}
result = (index < values.length ? index : -1);
System.out.println( result);
In diesem Beispiel stellt der erste Teil der Abbruchbedingung sicher, dass die
Suche bei einem leeren Array gar nicht erst begonnen wird.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #117
Kontrollstrukturen(16)
Eigenschaften von do- while
Die do- while- Schleife ist eine fußgesteuerte Schleife. Sie wird also mindestens
einmal durchlaufen.
Beispiel
// Ersten Wert suchen wie auf voriger Folie
if ( index<values.length) { // Wenn ein Wert gefunden wurde
// Suche den nächsten Wert in einem Array ab position idx
// liefere den index zurück wenn gefunden, -1 sonst
do {
++ index;
} while (index < values.length && values[ index]!= value);
result = (index < values.length ? Index : -1);
}
Unter der Annahme, dass idx am Anfang den Index des zuletzt gefunden Werts
enthält, stellt diese Schleife sicher, dass mindestens eine Position
weitergegangen wird um nicht an diesem Wert "kleben zu bleiben".
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #118
Kontrollstrukturen(15)
Eigenschaften von for
Die for- Schleife ist ein Sonderfall einer while- Schleife, also kopfgesteuert, und
wird hauptsächlich für Zählschleifen eingesetzt:
// zähle von start bis end- 1
public void counter( int start, int end) {
for ( int i= start; i< end; i++) {
System. out. println( i);
}
}
Die äquivalende while- Schleife verdeutlicht die Funktion der for- Schleife:
{
}
int i= start;
while ( i< end) {
System. out. println( i);
i++;
}
Hinweis: Mit anderen Worten, jede for-Schleife läßt sich als while-Schleife schreiben (aber nicht umgekehrt). Ein kleiner Unterschied besteht bzgl der Sichtbarkeit
von i. In der for-Schleife ist das i nur innerhalb des Schleifenrumpfs definiert, für die while-Schleife muss i außerhalb des Schleifenrumpfs definiert werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #119
Kontrollstrukturen(17)
Wie alle Kontrollstrukturen können auch Schleifen geschachtelt werden. Dabei
erhöht sich die Anzahl der Durchläufe und damit die Rechenzeit geometrisch:
Wird die äußere Schleife n- mal durchlaufen und die innere Schleife m- mal,
finden insgesamt n*m Durchläufe statt. Daher ist es ratsam, mit der
Verschachtelung von Schleifen vorsichtig umzugehen.
for (int i=0; i<100; i++)
for (int j=0; j<100; j++) {
System.out.println( (i*100+j+1) + "ter Durchlauf");
}
}
Durch die Verschachtelung der beiden Schleifen mit je 100 Durchläufen werden
insgesamt 10.000 Ausgaben erzeugt! Man spricht in diesem Zusammenhang
auch von quadratischer Laufzeit. Das ist bei Algorithmen immer schlecht...
Bei Zählschleifen werden die Zählvariablen aus alter Fortran Tradition meist mit
i, j, k benannt (mehr als 3 Schleifen sollte man aus Gründen der Verständlichkeit
und der Rechenzeit nicht schachteln – siehe oben).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #120
Kontrollstrukturen(18)
Stellt man in einer Schleife fest, dass das Ziel (vorzeitig) erreicht wurde, kann
man die (innerste) Schleife mit Hilfe der break- Anweisung verlassen (wie bei
switch- case) oder mit Hilfe der continue- Anweisung den Rest des
Schleifenrumpfs
zu
überspringen
und
fortzufahren
(sofern
die
Abbruchbedingung noch nicht erfüllt ist). Die break-Anweisung wird manchmal
auch in speziellen „Endlosschleifen“ (Event Loop) zur Ereignisverarbeitung
eingesetzt (Hinweis: Schleifenabbruch geht nicht mit dem break in switch!):
while (true) {
if (<Ereignis1> {
<Aktion1>
} else if (<Ereignis2>) {
<Aktion2>
} else if (<Ereignis3>) {
<Aktion3>
} else {
break; // Abbruch bei ungültigem Ereignis
}
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #121
Kontrollstrukturen(19)
Kontrollstrukturen und Datenstrukturen
Kontrollstrukturen sollen Daten verarbeiten. Dazu muessen auch die
Datenstrukturen geeignet gewählt werden. Dies gilt besonders für Schleifen.
Neben irgendwelchen Such- oder Rechenfunktionen, die bestimmte Variablen
auf ein Ziel hin optimieren, geht es bei Schleifen oft darum, eine Menge von
Daten auf eine bestimmte Art und Weise zu verarbeiten. Aber wie?
Die Lösung ist im Grunde naheliegend: Zählschleifen verwenden eine
numerische Zählvariable, Arrays können über einen numerischen Index
adressiert werden. Damit sind Arrays und Zählschleifen wie füreinander
geschaffen.
Beispiel
// alle Zahlen in einem Array verdoppeln
for (int i=0; i<numbers.length; ++i) {
numbers[i] = numbers[i]*2;
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #122
Kontrollstrukturen(10)
Der enge Zusammenhang von for-Schleifen und Arrays wird in Java 5.0 durch
die Einführung einer speziellen Form der for-Schleife unterstrichen mit deren
Hilfe sich die Verarbeitung aller Elemente eines Arrays besonders einfach
formulieren läßt.
Beispiel
// alle Zahlen in einem Array ausgeben
for (int n : numbers) { // "für jedes n im numbers Array"
System.out.println( n);
}
In anderen Programmiersprachen wird diese Form der Schleife oft als for-each
Schleife bezeichnet. Dies hätte jedoch ein neues Schlüsselwort erfordert.
Anmerkung:
Diese neuartige for-Schleife funktioniert auch mit (den später eingeführten)
Collections.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #123
Wie man eine Schleife entwickelt
Schleifen sind eigentlich nicht schwer, ehrlich! Aber wie entwickelt man sie? Am
einfachsten geht das am Anfang, indem man sie ausschreibt, also z.B.
Ziel: 1 2 3 ... n ausgeben
Schleifendurchläufe ausschreiben
System.out.print( 1 + " ");
System.out.print( 2 + " ");
System.out.print( 3 + " ");
...
System.out.print( n + " ");
Wiederkehrende Teile eindampfen und verallgemeinern
System.out.print( i + " ");
Schleife außenrum ( Initialisierung? Abbruchbedingung? Fortschritt?)
for ( int i=1; i<=n; ++i) {
System.out.print( i + " ");
}
Fertig!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #124
Anwendungsbeispiele
Natürlich kommen in realen Programmen die Kontrollstrukturen immer in
Kombination vor, so z.B. beim Suchen nach einem Wert in einem Array
for ( int i : numbers) {
if ( i == searchValue) {
// Wert verarbeiten
break; // wenn keine weiteren Exemplare gesucht
}
}
Eine kleine Abwandlung davon ist die Prüfung, ob ein Wert vorhanden ist
boolean found = false;
for ( int i : numbers) {
if ( i == searchValue) {
found = true;
break;
}
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #125
Struktogramme
Mit Hilfe von Struktogrammen (auch: Nassi-Shneidermann Diagramme) kann der
gesamte Kontrollfluss eines (Teil)Programms übersichtlich dargestellt werden.
Dies kann hilfreich sein, um Teilalgorithmen zu skizzieren oder auch um wichtige
Programmteile nachträglich zu dokumentieren.
Dabei fällt auf, dass sich die Kontrollstrukturen nur hintereinander setzen oder
sauber ineinander verschachteln lassen. Es gibt also keine Sprünge über Kreuz.
Insbesondere Schleifen sind stets ineinander verschachtelt und niemals
verschränkt.
(Bilder siehe Buch)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #126
Anwendung: Einordnung der Kontrollstrukturen
Jetzt kennen Sie die Kontrollstrukturen. Schön, und was machen wir nun damit?
Die Kontrollstrukturen sind LEGO-Bausteine zum Bau einer Folge von Schritten
zur Lösung eines Problems. Jeder Baustein hat einen bestimmten Zweck:
Sequenz – Mehrere Schritte nacheinander ausführen
● if-else – Eine ja/nein Entscheidung treffen bzw. Fallunterscheidung vornehmen
● switch-case – Eine Auswahl aus vielen Alternativen bzw. Fällen treffen
● for-Schleife – Zählschleife zur Erzeugung/Bearbeitung von Werten, z.B. Arrays
● for-each-Schleife – Bearbeitungsschleife für Arrays oder Collections
● while-Schleife – Durchführung von Befehlen nach Prüfung einer Bedingung
● do-while-Schleife – Durchführung von Befehlen vor Prüfung einer Bedingung
●
Bevor man die Bausteine jedoch zusammensetzt, muss man (a) das Problem
verstanden und sich (b) die Lösung ausgedacht haben.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #127
Anwendung: Wie man sich ein Programm ausdenkt
Jetzt kennen Sie bereits die wesentlichen Bestandteile eines Programms (alles
weitere dient nur der Gliederung) und können womöglich verschiedene
Beispielprogramme lesen und verstehen. Aber: Wären Sie auch selber
draufgekommen? Wie schreibt man überhaupt ein Programm?
Ein Programm ist die Lösung zu einem Problem. Und wie jede Lösung setzt
auch ein Programm eine Lösungsidee voraus. Diese Idee muss nicht in der
Programmiersprache formuliert sein. Im Gegenteil: Leichter fällt es meistens, die
Lösungsidee zunächst in anschaulichen Begriffen oder sog. Pseudocode zu
formulieren und erst dannach (Schritt für Schritt) in die Zielsprache zu
übersetzen (erinnern Sie sich an das Bild von der Kamera und den Bits?).
Hilfreiche Bilder dazu könnten sein (jeder nimmt das, was ihm hilft)
● Daten/Variablen = Schubladen
● If/Else = Schalter, Fallunterscheidung
● Schleifenindex = Zeigefinger
● ...
Die Anwendung dieser Bilder kann man auch anhand einiger Beispiele zeigen...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #128
Anwendung: Rezeptvorschläge für if-else
Die Kontrollstruktur if-else kann z.B. eingesetzt werden
Um Sonderfälle abzuprüfen (z.B. VIP Kunde)
● Um Fehlerfälle abzuprüfen (z.B. Nenner auf 0 prüfen vor Division)
● Um Sachverhalte schrittweise einzugrenzen (Verschachtelung if-else-if)
● Um Fallunterscheidungen vorzunehmen (z.B. Note <= 4,0 sonst durchgefallen)
●
Die Bedingung kann dabei einfach oder komplex sein
Beispiele für einfache Bedingungen (relationale Operatoren)
a==0, b>0, c%2==0
Beispiele für komplexe Bedingungen (logische Operatoren)
('a'<=c && c<='z'), ('a'<=c && c<='z') || ('A'<=c && c<='Z'), a<0 || Math.sqrt(a)>10
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #129
Anwendung: Rezeptvorschläge für switch-case
Aufgrund der Datentyp-Einschränkung dient switch-case i.d.R.
Auswahl aus einer Anzahl von Fällen, z.B. Menüpunkte
● Auswahl aus einer Anzahl von Zuständen (über eine Zustandsvariable)
●
Damit hat switch-case im Unterschied zu if-else weniger eine Prüffunktion und
viel deutlicher eine Auswahlfunktion.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #130
Anwendung: Rezeptvorschläge für for-Schleifen
Die for-Schleife kann z.B. eingesetzt werden
Eine Anweisungsfolge n-Mal zu wiederholen
for ( int i=0; i<n; ++i) { ... }
● Eine Zahlenfolge mit bestimmter Schrittweite zu generieren*
for ( int i=1; i<n; i+=3) { ... }
● Elemente in einem Array zu erzeugen, zu bearbeiten oder auszulesen
for ( int i=0; i<a.length; ++i) { ... a[i] ... }
●
Dabei entspricht eine for-Schleife der Bearbeitung einer Dimension. Durch
Verschachtelung von Schleifen können mehrere Dimensionen bearbeitet
werden, z.B. mehrdimensionale Arrays. Dabei ist es wichtig, verschiedene
Indexvariablen für jede Schleife zu verwenden und diese nicht zu verwechseln.
Es wird empfohlen, Indexvariablen grundsätzlich nur einmal zu verwenden um
auf das aktuelle Element zuzugreifen und dieses in einer Variablen zu speichern.
*man kann auch andere Daten aus Zahlen generieren, z.B. Zeichen via Ascii
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #131
Anwendung: Rezeptvorschläge für for-each-Schleifen
Die for-each-Schleife kann eigentlich nur für einen Zweck eingesetzt werden
●
Elemente in einem Array oder einer Collection auslesen
for ( String name : namen) { ... name ...}
Die for-each-Schleife wurde speziel für diesen Zweck eingeführt, um den
Schreibaufwand für eine normale for-Schleife einzusparen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #132
Anwendung: Rezeptvorschläge für while-Schleifen
Die while-Schleife kann eigentlich alles, wird aber am Besten eingesetzt für
Unbestimmte Anzahl von Wiederholungen bis zur Ereichung eines Resultats
while ( !fertig) { ... }
● Wiederholung von Schritten wie oben aber mit Vorbehalt
while ( kindersicher && !fertig) { ... }
●
Bei der while-Schleife kann es also vorkommen, dass sie komplett übersprungen
wird weil es nichts zu tun gibt oder nichts getan werden kann, z.B. weil in einem
leeren Array nichts gefunden werden kann oder ein nicht vorhandenes Array
(null) nicht durchsucht werden kann.
Die while-Schleife ist die universellste aller Schleifen (die anderen dienen quasi
nur der Bequemlichkeit). Daher gilt: bei Unsicherheit einfach while verwenden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #133
Anwendung: Rezeptvorschläge für do-while-Schleifen
Die do-while-Schleife ist die "unsichere Schwester" der while-Schleife für
●
mindestens einmalige Wiederholungen bis zur Ereichung eines Resultats
do { ... } while ( !fertig)
Der Inhalt der do-while Schleife wird also mindestens einmal durchlaufen. Ist
dann (noch) nicht das gewünschte Ergebnis erreicht ggf. nochmal. Beispiel:
Ziehung einer Lottozahl und Wiederholung falls bereits vorhanden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #134
Unterprogramme
(Funktionen)
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #135
Unterprogramme und Schnittstellen(1)
Als Unterprogramm bezeichnet man einen Programmteil mit klarem
abgegrenztem Zweck, der an mehreren Stellen im Programm aufgerufen und
somit wiederverwendet werden kann.
1. Aufruf
2. Aufruf
println( ... )
println( ... )
Der Begriff Unterprogramm stammt aus einer Zeit, als Unterprogramme oft nicht
viel mehr waren als ein paar Zeilen Code, die man mit Anweisungen wie gosub
und return anspringen und wieder verlassen konnte. Der Datenaustausch mit
diesen Programmschnipseln war über globale Variablen organisiert.
Dies war zwar besser als die Verwendung von goto (ein Sprung ohne
Wiederkehr), aber die Verwendung von globalen Variablen verschleierte die
Schnittstelle des Unterprogramms und führte aufgrund der gemeinsamen
Verwendung dieser Variablen an vielen Stellen im Programm zu Problemen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #136
Unterprogramme und Schnittstellen(2)
Zweck von Unterprogrammen
Unterprogramme sind aus vielen Gründen sehr nützlich. Die wichtigsten sind:
●
●
Erhöhte Lesbarkeit eines Programms. Die Aufteilung eines Programms in
Module und Unterprogramme gleicht der Einteilung eines Buchs in Kapitel. Der
Name eines Unterprogramms entspricht der Kapitelüberschrift und verdeutlicht
die Bedeutung des darin enthaltenen Codes.
Wiederverwendung - sowohl innerhalb eines Programms als auch durch
Bildung von Bibliotheken. Wiederverwendung dient nicht nur der Einsparung
von Tipparbeit sondern hilft auch, Fehler zu vermeiden (Verwendung von
erprobtem Code) und die Wartbarkeit zu verbessern (Änderungen sind nur an
einer Stelle nötig).
Um diese Ziele zu erreichen ist Sorgfalt beim Programmentwurf notwendig. Ein
Unterprogramm sollte immer nur einen Zweck verfolgen und entsprechend
benannt sein (keine eierlegende Wollmilchsau). Solche Unterprogramme sind
kurz und passen idealerweise auf eine Bildschirmseite (Verständlichkeit).
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #137
Unterprogramme und Schnittstellen(3)
Die moderne Bezeichnung für ein Unterprogramm lautet Prozedur oder
Funktion. In Java spricht man von Methoden, aber die Begriffe Prozedur und
Funktion sind dennoch nützlich zur Charakterisierung dieser Methoden.
Eine Prozedur
verarbeitet ihre Parameter in einer Aktion ohne direkten Rückgabewert. Sie
„macht etwas“ und hat i.d.R. Seiteneffekte, z.B. eine Ausgabe am Bildschirm
oder die Veränderung globaler Variablen. In Java verändern solche Methoden
typischerweise den inneren Zustand des aufgerufenen Objekts. Dies trifft
speziell auf set-Methoden zu, die den Wert einer Zustandsvariablen auf einen
bestimmten Wert setzen.
Eine Funktion
verarbeitet ihre Parameter in einer Berechnung zu einem Ergebnis. Sie
„berechnet etwas“ und kann zusätzlich Seiteneffekte haben. Der trivialste
Vertreter dieser Art von Methoden in Java sind get-Methoden, die den Wert einer
Zustandsvariablen zurückliefern.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #138
Unterprogramme und Schnittstellen(4)
Methoden werden charakterisiert durch ihren Namen und ihre Schnittstelle.
Diese bezeichnet man zusammen als Signatur (=Fingerabdruck) der Methode.
Der Name der Methode soll möglichst treffend beschreiben, was die Methode
tut. Dazu werden oft Wortkombinationen verwendet, die mit einem Verb
beginnen, z.B. getName() oder readLine().
Die Schnittstelle beschreibt den Typ des Rückgabewerts sowie die Anzahl,
Positionen und Typen der formalen Parameter. Ihre Namen dienen als
Platzhalter zur Formulierung des Methodenrumpfs. Ihre Werte werden beim
Aufruf der Funktion durch die Werte der aktuellen Parameter ersetzt.
In vielen Sprachen ist eine Funktion bereits durch ihren Namen eindeutig
charakterisiert. In Java kann es dagegen mehrere gleichnamige Methoden mit
unterschiedlicher Signatur innerhalb eines Gültigkeitsbereichs geben (Beispiel:
die Methode println()). Dies nennt man überladen (overloading). Es
ermöglicht es, Methoden die im Prinzip das gleiche tun (hier: Ausgabe von
Werten) – nur eben mit verschiedenen Daten – auch gleich zu benennen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #139
Kopieren von Werten bei Funktionsaufrufen(1)
Ablauf eines Methodenaufrufs
public class Doppler {
Typ des Rückgabewerts
Name
+
Formale Parameter = Signatur
public static int verdoppeln( int wert) {
int dasDoppelte = wert * 2;
return dasDoppelte;
}
}
public static void main(String[] args) {
int x, y;
x = 1;
1. wert = x (aktueller Parameter)
y = verdoppeln( x);
2. Funktion ausführen
3. y = dasDoppelte (Rückgabewert)
System.out.println( y);
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #140
Kopieren von Werten bei Funktionsaufrufen(2)
Für das Kopieren der Parameter und des Rückgabewerts beim Methodenaufruf
gelten dieselben Regeln wie für das Kopieren von Variablen:
Elementare Werte werden als echte Kopie übergeben. Änderungen des Wertes
innerhalb der Funktion haben außerhalb der Funktion keine Auswirkungen. Dies
wird Call-by-Value genannt.
Objekte werden dagegen als Referenz übergeben. Änderungen des Objekts
innerhalb der Funktion verändern das Originalobjekt, d.h. sie wirken sich auch
außerhalb der Funktion aus. Dies wird Call-by-Reference* genannt.
Zum Schutz vor unabsichtlichen Fehlern ist es wichtig, diesen Unterschied zu
kennen. Beide Aufrufvarianten haben ihren Sinn! Für primitive Datentypen ist
Call-by-Value schnell und billig. Bei der Übergabe von Objekten liegt der Vorteil
des Call-by-Reference neben der Durchgriffsmöglichkeit auf den Originalwert
auch in einer effizienteren Übergabe, besonders bei großen Objekten.
*Bei spitzfindiger Betrachtung stimmt diese Aussage für Java nicht ganz: beim "echten" Call-by-Reference, z.B. in
C, kann die ursprüngliche Variable (d.h. der Schubladeninhalt) verändert werden. In Java wäre dies der Verweis
auf das Objekt. Tatsächlich kann jedoch nur das Objekt selbst, nicht aber der Verweis geändert werden.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #141
Iteration und Rekursion(1)
Es gibt zwei Wege um Dinge mehrfach zu tun: Iteration und Rekursion.
Bei der Iteration werden Schleifen verwendet. Diese Variante haben wir bereits
kennengelernt. Sie entspricht anschaulich der Wiederholung von Arbeitschritten.
Bei der Rekursion ruft sich eine Funktion wieder selbst auf. Diese Variante ist
neu. Sie kommt aus der Mathematik und erscheint auf den ersten Blick
unverständlich, erlaubt es allerdings die Lösung vieler Probleme elegant
darzustellen. Dazu sind wie bei der vollständigen Induktion zwei Schritte nötig:
(1) man führt das Problem auf ein kleineres Teilproblem zurück und (2) man löst
das kleinste Teilproblem um die Kette der Aufrufe abzubrechen.
Die Mächtigkeit von Iteration und Rekursion ist gleich, d.h. jeder iterative
Algorithmus kann theoretisch in einen entsprechenden rekursiven Algorithmus
umgewandelt werden und umgekehrt (die Lesbarkeit ist dabei ein anderes
Thema). Da jedoch die Rekursion mit erheblichem Zusatzaufwand für die vielen
Aufrufe verbunden sein kann, hängt die Wahl zwischen Iteration und Rekursion
in der Praxis oft von einer Abwägung zwischen Lesbarkeit und Effizienz ab.
Dabei gibt es leider keine feste (Daumen)regel...
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #142
Iteration und Rekursion(2)
Beispiel: Berechnung der Fakultät (DER Klassiker ;-) )
public class FakExample {
// iterative Lösung
public static int iterFak( int value) {
int result = 1;
while (value > 1) {
result *= value--; // Achtung: Seiteneffekt!
}
return result;
}
}
// rekursive Lösung
public static int rekFak( int value) {
if (value > 1) {
return value * rekFak( value-1); // Teilproblem
} else {
return 1; // Anfangsproblem
}
}
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #143
Iteration und Rekursion(3)
Aufruf: iterFak( 5);
value
5
4
3
2
1
result
1
5
20
60
120
Aktion
*5
*4
*3
*2
*1
Aufruf: rekFak( 5);
value
5
4
3
2
1
© Andreas Rau, 19.11.10
Aktion
5 * rekFak(4)
4 * rekFak(3)
3 * rekFak(2)
2 * rekFak(1)
1
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #144
Iteration und Rekursion(4)
Die rekursive Lösung entspricht der math. Definition der Fakultätsfunktion
n*(n-1)! falls n > 1
n! =
1 sonst
Anderes Beispiel für solche mehrteiligen Definitionen
x falls x >= 0
|x| =
-x sonst
Neben math. rekursiven Funktionen gibt es auch verschiedene rekursive
Datenstrukturen bei denen Teilstrukturen der Gesamtstruktur ähneln, z.B.
Bäume. Diese lassen sich sehr elegant mit rekursiven Algorithmen bearbeiten.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #145
Zusammenfassung: Ausdrücke und Funktionen
Es besteht eine gewisse Ähnlichkeit zwischen Ausdrücken und Anweisungen
einerseits und Funktionen und Prozeduren andererseits:
Ausdrücke und Funktionen berechnen etwas, d.h. liefern einen Wert zurück.
● Anweisungen und Prozeduren machen etwas, d.h. liefern einen Effekt.
●
Demnach kann man Funktionen als eine Abstraktion für (Teil-)ausdrücke und
Prozeduren als eine Abstraktion für eine Folge von Anweisungen betrachten.
Trotzdem darf natürlich eine Funktion Anweisungen enthalten, z.B. für
abschnittsweise definierte Funktionen, und eine Prozedur kann Ausdrücke
verwenden. Entscheidend ist, „was hinten rauskommt“ (oder auch nicht).
Um einen (Teil)ausdruck als Methode zu implementieren, muss (1) ihr
Rückgabewert den Typ des Ergebnisses haben und (2) die Schnittstelle soviele
Parameter haben, wie Variablen in dem Teilausdruck vorkommen.
Eine Anweisungsfolge als Methode zu implementieren ist viel einfacher – man
packt sie einfach rein und übergibt die benötigten Daten als Parameter.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #146
Fazit
Die in diesem Teil behandelten Konzepte sind allgemeingültig und lassen sich –
mit leichten syntaktischen Abwandlungen – auf fast alle gängigen
Programmiersprachen übertragen. Sie stellen gewissermaße die Materialkunde
(Datentypen)
und
handwerkliche
Grundausbildung
(Ausdrücke
und
Kontrollstrukturen) zum Bau von Häusern (Programmen) dar.
Die unterschiedlichen Arten von Programmiersprachen stellen in diesem Sinne
verschiedene Stilrichtungen der Architektur dar. Die Objektorientierung, auf der
Java basiert, ist ein solcher Baustil und wird im nächsten Teil behandelt.
Schlussbemerkung:
Mit den Inhalten aus diesem Abschnitt der Vorlesung sind alle Grundwerkzeuge
bekannt. Für Ihre Beherrschung – insbesondere im Zusammenspiel – ist jedoch
noch viel Übung notwendig. Wer Pinsel, Farben und Staffelei kennt, ist noch
lange kein Picasso – aber auch der hat schließlich als Kind mal mit
Strichzeichnungen angefangen.
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #147
Blick über den Tellerrand
Das folgende Programm realisiert eine Schleife in JavaScript. Da sowohl Java
als auch JavaScript die Kontrollstrukturen von C "geerbt" haben dürfte dieses
Programm leicht verständlich sein.
<html>
<body>
<script type="text/javascript">
i = "Das ist ein Test";
document.writeln( i);
for (i=0; i<10; i++) {
document.writeln( i);
}
</script>
</body>
</html>
Auffällig ist jedoch, dass die Variable i offenbar keinen (eindeutigen) Datentyp
hat. Dies ist für Skriptsprachen typisch aber auch tückisch!
© Andreas Rau, 19.11.10
D:\home\ar\fhte\vorlesungen\informatik1\folien\informatik1-theorie-programmelemente.odp #148
Zugehörige Unterlagen
Herunterladen