Kurs:Wirtschaftsinformatik WS08 09 PROGRAMMIERUNG/Lernskript
Grundbegriffe der Informatik
[Bearbeiten]Maschinen der Informatik
[Bearbeiten]Methoden der Informatik
[Bearbeiten]Kernaussage: Die Methoden der Informatik basieren auf Modellen
Beispiel: Modellbildung
Die Struktur der Software ist ebenfalls hierarisch aufgebaut …
- Problem: Finde den kürzesten Weg von
Dortmund nach Essen
- Was ist ein Weg?
- Was heißt kurz?
- …
- Modell: gerichteter Graph
Die Lösung ist damit auf ein sog. Graph-Problem zurückgeführt worden. Gegeben seien ein ungerichteter Graph mit den Knoten Start und Ziel sowie die gewünschte Abfahrtszeit t. Es sei K die Menge der Kantenzüge vom Knoten Start zum Knoten Ziel. Es sei e die minimale Gesamtlänge aller Kantenzüge aus K. Die Teilmenge T ⊂ K enthalte alle Kantenzüge, deren Gesamtlänge höchstens 20% über e liegt. Wähle den Kantenzug aus T, der die geringste Gesamtzeit für die Fahrt und erwartete Staudauern aufweist.
Eine softwarebasierte Lösung ist ein Programm, das dieses Modell realisiert
- Suche eine Repräsentation von Graphen in einer gewählten Programmiersprache
- Suche ein geeignetes Verfahren die kürzesten Wege zu finden
- ...
Beschreibung der Disziplin und Abgrenzung der Informatik von anderen Disziplinen
[Bearbeiten]Auch in anderen Disziplinen werden Modelle verwandt.
Unterschied zu
- Mathematik
- Naturwissenschaften (Physik …)
- Ingenieurwissenschaften
- Informatik:
- Aufgabenstellung → Abstraktion
- Modellbildung → Analyse und Synthese
- Problemlösung → Programm / Software
Die zentrale Idee, die zusätzlich dazu kommt ist der des Algorithmus. Hier liegt der entscheidende Unterschied.
Algorithmus
[Bearbeiten]Die zentrale Idee für die Realisierung softwarebasierter Lösungen ist der Begriff des Algorithmus
- Algorithmen sind abstrakte Verfahrensvorschriften
- Ein Algorithmus allein macht ein Programm noch nicht aus
- Informations- und Algorithmusstrukturen sind die wesentliche „Zutat“ zu einem guten Programm
- Trotzdem: Der Algorithmusbegriff ist ein sinnvoller Startpunkt für die Überlegungen zur
Programmierung
Charakterisierung des Algorithmus
[Bearbeiten]Einige wichtige Punkte bestimmen, was ein Algorithmus ist
- A1 Ein Algorithmus beschreibt eine Relation über das Kreuzprodukt einer Eingabe- und einer Ausgabemenge. Dadurch werden für jede Eingabe die zulässigen Ausgaben festgelegt.
- A2 Ein Algorithmus setzt sich aus wohldefinierten Elementaroperationen zusammen, die auf einer geeigneten Maschine ausführbar sind.
- A3 Ein Algorithmus legt die Abfolge der Schritte fest, wobei jeder Schritt genau eine Elementaroperation umfasst.
- A4 Ein Algorithmus ist eine Beschreibung endlicher Länge.
- A5 Ein Algorithmus benutzt nur endlich viele Speicherplätze zur Ablage von Zwischenergebnissen.
- A6 Terminierung: Für jede (!) Eingabe endet die Ausführung des Algorithmus nach endlich vielen Schritten.
- A7 Begrenzte Schrittanzahl: Für jede (!) Eingabe wird ie zugehörige Ausgabe spätestens nach Ausführung einer vorgegebenen Schrittanzahl n geliefert. Wenn ein Rechensystem für jeden Schritt höchstens die Zeit s benötigt, dann wird die Ausgabe spätestens nach Verstreichen der
begrenzten Antwortzeit t = s ⋅ n geliefert.
- A8 Determiniertheit: Die Eingabe-Ausgabe-Relation (siehe A1) ist rechtseindeutig. Dies bedeutet, dass jeder Eingabe genau eine Ausgabe zugeordnet wird.
- A9 Determinismus: In jedem Zustand, der bei Ausführung des Algorithmus erreicht wird, ist jeweils nur ein einziger Folgeschritt als nächster ausführbar.
- Anmerkung: Die Forderung A9 impliziert A8. Gelegentlich werden die Forderungen A6 bzw. A7 auf einzelne Programmabschnitte beschränkt.
- A10 Allgemeinheit: Ein Algorithmus löst nicht nur ein einziges Problem, sondern eine Klasse von Problemen.
- A11 Änderbarkeit: Ein Algorithmus soll sich leicht modifizieren lassen, um ihn an eine veränderte Aufgabenstellung anzupassen.
- A12 Effizienz: Für eine gegebene Eingabe soll die Anzahl der benötigten Schritte möglichst gering sein.
- A13 Robustheit: Der Algorithmus soll sich möglichst auch dann wohldefiniert verhalten, wenn eine unzulässige Eingabe (die nicht Element der Eingabemenge ist)vorliegt oder eine sonstige unvorhergesehene Situation auftritt.
Die Forderungen A10 bis A13 sind nicht immer leicht zu erfüllen und müssen gegeneinander abgewogen werden
Beispiel: A10 - A13, weiche Forderungen
[Bearbeiten]Der erste Algorithmus erfüllt Forderung A11 besser,weil er kürzer und übersichtlicher formuliert ist. Für Forderung A13 ist der zweite Algorithmus die günstigere Variante, da große Eingabewerte nicht so schnell Probleme der Rechnerarithmetik aufwerfen.
Wichtig: die „Verstehbarkeit“ von Software.
Berechnung des arithmetischen Mittels nach der Formel (x + y)/2
- Schritt 1: lies Eingaben x und y weiter mit Schritt 2
- Schritt 2: Berechne a = x + y weiter mit Schritt 3
- Schritt 3: Berechne b = a / 2 weiter mit Schritt 4
- Schritt 4: Schreibe Ausgabe b beende Ausführung
Berechnung des arithmetischen Mittels nach der Formel x/2 + y/2
- Schritt 1: lies Eingaben x und y weiter mit Schritt 2
- Schritt 2: Berechne a = 0,5 * x weiter mit Schritt 3
- Schritt 3: Berechne b = 0,5 * y weiter mit Schritt 4
- Schritt 4: Berechne c = a + b weiter mit Schritt 5
- Schritt 5: Schreibe Ausgabe c beende Ausführung
Determinisitsch und Indeterministische Algorithmen
[Bearbeiten]Bei der Festlegung der Reihenfolge der Elementaroperationen gibt es noch Freiheiten. Es muss nur feststehen, dass irgendeine Elementaroperation ausgeführt werden kann (A3)
Beispiel: Addition ist unabhängig von der Reihenfolge
[Bearbeiten]- Die Addition (2+2) oder die Berechnung der Einkommensteuer sollten determiniert sein.
Achtung: Die konkrete Abfolge der Schritte ist damit nicht festgelegt!
- Flugsitzreservierungen von verschiedenen Buchungsterminals aus sind in der Regel
nicht determiniert.
Softwaresysteme, welche die Arbeit mehrerer Rechner involvieren, sind in der Regel indeterministisch und müssen mit großem Aufwand zu determinierten Verfahren gemacht werden.
Illustrativer Pseudocode: Determinismus und Indeterminismus
[Bearbeiten]nicht deterministisch:
[Bearbeiten]- Schritt 1: lies Eingaben x und y weiter mit Schritt 2 oder 3
- Schritt 2: Berechne a = y – x weiter mit Schritt 4
- Schritt 3: Berechne a = x – y weiter mit Schritt 4
- Schritt 4: Falls a ≥ 0: weiter mit Schritt 5
- Falls a < 0: weiter mit Schritt 6
- Schritt 5: Setze b = a weiter mit Schritt 7
- Schritt 6: Berechne b = – a weiter mit Schritt 7
- Schritt 7: Schreibe Ausgabe b beende Ausführung
ist indeterministisch, aber trotzdem determiniert !
deterministisch:
[Bearbeiten]- Schritt 1: lies Eingaben x und y weiter mit Schritt 2
- Schritt 2: Falls x ≤ y: weiter mit Schritt 3
- Falls x > y: weiter mit Schritt 4
- Schritt 3: Berechne a = y – x weiter mit Schritt 5
- Schritt 4: Berechne a = x – y weiter mit Schritt 5
- Schritt 5: Schreibe Ausgabe a beende Ausführung
Programm
[Bearbeiten]- Gute Software ist kein Kinderspiel
- Der Schwerpunkt liegt auf der Konstruktion von Programmen und Programmbausteinen
Programme sind „reine Konstruktion“
- Maschinen können Programme ausführen
- Maschinen können sehr unterschiedlich realisiert sein (Hardware oder Software)
- Systeme sind sinnvollerweise hierarchisch strukturiert
Die Struktur der Software ist ebenfalls hierarisch aufgebaut …
HIER IN JAVA
Java-Programm = Folge von Klassendefinitionen
- Java-Programm:
- Folge von Klassendefinitionen, die u.a. eine
- Folge von Methodendefinitionen enthalten
- Komplexe Programmstrukturen (Abläufe ...)
- Komplexe Daten-/Informationsstrukturen
- Die klare Strukturierung in Klassen, Methoden ... hilft, auch bei umfangreichen Programmsystemen den Überblick zu behalten
- Die zusätzlichen Informationen über Struktur und Typen nutzen Compiler und andere Werkzeuge
aus, einfache Fehler schnell aufzufinden
Programmiersprachen
[Bearbeiten]Formale Sprachen
[Bearbeiten]Die Formulierung von Algorithmen wird in künstlichen Sprachen abgefasst
Wegen der Eindeutigkeit der Interpretation: Formale Sprachen.
Begriffe: Syntax und Semantik
Für diese Sprachen gilt (in etwa):
- Syntax bezeichnet die Regeln des formalen Aufbaus einer Sprache.
- Semantik bezeichnet die Regeln für die Interpretation eines Wortes / Satzes / Textes, um seine Bedeutung
zu definieren.
Hochsprachen
[Bearbeiten]- Hier nun kurz die Konzepte heutiger so genannter „höherer“ Programmiersprachen („Hochsprachen“)
- Elemente höherer Programmiersprachen:
- relativ mächtige Elementaroperationen
- Strukturierung von Daten
- Die vorhandenen Maschinen liefern aber (noch) nicht die notwendige Basis zur Ausführung dieser Elementaroperationen
Elemente von Hochsprachen
[Bearbeiten]- Einfache Anweisungen
- Zusammenfassung von mehreren Anweisungen zu einer komplexen Methode (auch Unterprogramm genannt)
- Einfache Datentypen
- Komplexe Datentypen oder auch Datenstrukturen
- Objekte: Vereinigung von Algorithmus und Datenstrukturen
- Die Verallgemeinerung und Abstraktion: Klassen von Objekten
- Einfache Anweisungen
- Addition, Subtraktion, ... von Werten
- Transfer von Werten zwischen
(Zwischen-) Speichern
- Kontrollfluss ...
- Ausführung nacheinander
(eine Anweisung nach der anderen)
- Alternativen
- Wiederholung
Komplexe Anweisungen
[Bearbeiten]- Zusammenfassung von mehreren Anweisungen zu einer einzigen Anweisung
- math. Funktionen wie sin, cos, ..., ggT, kgV, ...
- Wichtige Punkte:
- Namensgebung
- Zusammenfassung von mehreren
Anweisungen
- Abstraktion ...z.B. ggT(20, 45) versus ggT(x, y)
Primitive Datentypen
[Bearbeiten]- Einzelne Werte werden selten betrachtet. Besser: ganze Wertemengen
- In der Regel sind einfache (primitive) Wertemengen vorgesehen:
- ein Abschnitt der ganzen Zahlen
- eine Teilmenge der rationalen Zahlen
- einfache Zeichen und Zeichenketten
- Wahrheitswerte (wahr, falsch)
- Dazu werden die üblichen Operationen auf diesen Mengen zur Verfügung gestellt
→ Wertemengen + Operationen = Datentyp
Komplexe Datentypen
[Bearbeiten]- Primitive Wertemengen werden als Basis für komplexe Strukturen benutzt
- Vorschriften, wie einzelne einfache Daten zu komplexen Gebilden geformt werden
→ komplexe Datenstrukturen
- Wichtigste Konstruktion:
Zusammenfassung von bestehenden Datentypen. Die Daten einer Person bestehen aus
- Person = { Vorname, Nachname, Adresse,Geburtsdatum }
- Adresse = { Straße, PLZ, Stadt }
Objekte
[Bearbeiten]- Die Trennung von Verfahrenvorschrift (Algorithmus) und
Datenstrukturen hat sich als hinderlich erwiesen.
- Daher: Besonders wichtig für das Verständnis von
Programmen ist die Zusammenfassung der zulässigen Verfahren und der dazugehörigen Datenstrukturen
- Beispiel:
- Person = {Vorname, Nachname, Adresse, ... }
- sowie {Ändere_Nachname(…), Ändere_Adresse(…)}
- Damit können auf einzelne Objekte zugeschnittene Verfahren formuliert werden, die spezifische Eigenschaften eines Objektes sicherstellen
Klassen von Objekten
[Bearbeiten]- In der Regel mühsam: alle Objekte einzeln beschreiben.
Man ist nicht an einer einzelnen Person, sondern an allen Personen interessiert
- Daher: Zusammenfassung aller gleichartigen Objekte zu einer Klasse von Objekten
- Üblicherweise werden dann in Programmen nur die Definition von Klassen und Verfahren angegeben, wie einzelne Exemplare (Objekte, Instanzen) geschaffen werden können
- Beispiel:
- class Person = {Vorname, ...} sowie {Ändere ( ...) }
- Person Michael_Goedicke
Grundelemente der Programmierung
[Bearbeiten]Erste Schritte
[Bearbeiten]Java-Programm = Folge von Klassendefinitionen
- Java-Programm:
- Folge von Klassendefinitionen, die u.a. eine
- Folge von Methodendefinitionen enthalten
Beispielprogramm:
public class einfacheRechnung
{ public static void main (String [ ] Aufrufparameter)
{ int x, y;
x = 10;
y = 23 * 33 + 3 * 7 * (5 + 6);
System.out.print (”Das Resultat lautet”);
System.out.print (x + y);
System.out.println (”.”);
}
}
Im Beispiel:
- Definition der Klasse einfacheRechnung
- darin:
- Definition der Methode main
- main spielt eine besondere Rolle: Methode ist, die automatisch als erste aufgerufen wird, wenn man das Programm startet
- Einige Punkte des obigen Programms müssen zur Zeit so hingenommen werden (public, static)
geschweifte Klammern { }
[Bearbeiten]Die syntaktische Struktur von (Java-) Programmen ist durch Schachteln von Klammern gegeben
- Generell bestehen solche Schachteln aus einem Kopf und einem Rumpf
public class einfacheRechnung
{ ...
}
public static void main ( ... )
{ ...
}
Variablen in Programmen
[Bearbeiten]In Klassen- und Methodendefinitionen: Variablen
- im Beispiel oben: int x, y;
Variablenbegriff in der Informatik: Variablen sind Name-Wert Paare
- Variablen sind Speicher, deren Inhalt gelesen und verändert werden kann.
- Variablen (Speicher) haben
- einen Namen (s.o. x und y)
- einen Ort (der ist in der Regel dem Programmierer nicht zugänglich)
- einen Typ ... D.h. ein Speicher kann nur eine bestimmte Art von Werten enthalten (int)
Die Zuweisung = ist eine andere Opration als der Test auf Gleichheit == in Java. Entsprechend spricht man in einer Zuweisungsanweisung Variablenname = Wert auch von den Ausdrücken L-Wert und R-Wert, wie man ihn aus der Programmiersprache C kennt. Es soll nochmals hervorgehoben werden, dass die Zuweisungsanweisung keine Äquivalenzrelation ist. Die Zuweisungsanweisung ist nicht symmetrisch, reflexiv und transitiv wie die Gleichheit.
Beispiel:
Im Rumpf einer Methodendefinition: Anweisungen, die den Algorithmus darstellen
- Im Beispiel:
- x = 10;
- y = 23*33 + 3*7*(5 + 6);
- Schrittnummern (wie in den ersten Algorithmen) werden nicht benötigt:
Semikolon ; bedeutet Hintereinanderausführung
- Die beiden Anweisungen oben sind Zuweisungen an Variablen. Die durch sie repräsentierten
Speicherplätze haben am Ende der Ausführung einen neuen Wert.
- Der alte Wert ist unwiederbringlich verloren!
- Zuweisungen verändern den Speicherinhalt.
Besonderheit von Java
Die Bedeutung der Operationszeichen in Ausdrücken hängt von den Typen der beteiligten Variablen ab:
Der Operator '+' macht etwas anderes im ersten Beispiel als im zweiten Beispiel. Java leitet seine Interrpretation des Quelltextes aus dem Kontext der verwendeten Datentypen ab.
int x;
x = 10 + 5;
Verkettung zweier Zeichenketten, wobei die zweite aus der Umwandlung eines int-Wertes (x)
gewonnen wird
System.out.print(”Wert von x: ” + x);
Variablen in Programmen haben einen großen Unterschied zum Variablen-Begriff in der
Mathematik!
- In der Mathematik:
Variablen repräsentieren beliebigen, aber festen Wert, der ggfs. auch noch unbekannt sein kann.
- In Programmen:
Variablen repräsentieren Speicherplätze, die je nach Zuweisung ihren Wert ändern. Werte müssen den Variablen explizit zugewiesen werden (keine impliziten oder unbekannten Werte von Variablen).
Anweisungen
[Bearbeiten]Zuweisung
[Bearbeiten]Zuweisung: Ein errechneter Wert wird in einer Variablen abgelegt
- Der grundsätzliche Aufbau: Variablenname = Ausdruck
- b = 5 * 27;
- Die Verwendung des gespeicherten Wertes geschieht durch die Angabe des Variablennames
in einem Ausdruck
- a = b * 8;
- Beachte: Die Verwendung von Variablennamen auf der linken und der rechten Seite eines
Ausdrucks hat unterschiedliche Bedeutung!
Der Ablauf der Zuweisung besteht aus drei Schritten
1. Die linke Seite der Zuweisung wird ausgewertet (hier der Variablenname, kann aber komplexer sein) 2. Die rechte Seite (Ausdruck) wird ausgewertet (Regeln zu Operatorreihenfolgen etc. werden beachtet) 3. Falls der Wert der rechten Seite typkompatibel zu der Variablen ist oder automatisch (d.h. implizit) angepasst werden kann, erfolgt die Zuweisung
- linkeSeite = rechteSeite;
Die drei Schritte laufen nur ungestört ab, wenn keine Ausnahmen auftreten
- Die genannte Variable könnte nicht vorhanden sein
- Die Auswertung der rechten Seite liefert einen Fehler
- Typ der Variablen und des Ergebnisses sind nicht typkompatibel
- linkeSeite = rechteSeite;
linkeSeite2 = (linkeSeite1 = rechteSeite1) op rechteSeite2 ;
Eine Zuweisung ist auch ein Ausdruck!
- Der zugewiesene Wert einer Zuweisung ist der
„Wert“ einer Zuweisung als Ausdruck betrachtet
- int a = 1, b = 2, c, d;
- a = 3 + 2 * (a + b);
- b = 7 – a/2;
- c = 3 * (d = a + b + 1);
- Mehrere Zuweisungen in einem Ausdruck werden von rechts nach links abgearbeitet:
- a = b = c = d = 5;
- a = (b = (c = (d = 5)));
Es gibt einige Kurzversionen für häufig vorkommende Zuweisungen
- Z.B.: Erhöhung einer Variable um einen Wert
- a = a + 5; kann geschrieben werden a += 5;
a += 10 | a = a + 10 | |
a /= 2 + b | a = a / (2 + b) | |
a *= 3 / 2 | a = a * (3 / 2) ist verschieden von a = a * 3 / 2 |
Weitere spezielle Formen der Zuweisung: Auto-Inkremente und -Dekremente
a++ oder ++a | a += 1 , a = a + 1 | |
a–– oder ––a | a –= 1 , a = a – 1 |
- Der nachgestellte Operator: Erhöhung bzw. Erniedrigung nach Verwendung
- Der vorangestellte Operator: Erhöhung bzw. Erniedrigung vor Verwendung
Schon einfache Programme können durch die Verwendung der Kurzformen unübersichtlich werden
Was passiert hier?
- int a = 2, b = 3;
- a *= b++;
- b += 19 / ––a;
Programme können durch die Kurzformen unverständlich werden
- Zum Beispiel: a + 2*a++ + a
- Daher die Verwendung dieser Formen nur in übersichtlichen Fällen!
Methodenaufruf
[Bearbeiten]System.out.print (”Das Resultat lautet ”);
System.out.print ( x + y );
System.out.println (”.”);
- Ein weiterer Typ von Werten: String.
Die Konstanten werden in ” ... ” gesetzt, um sie von den Konstanten des Typs int oder Namen von Variablen eindeutig unterscheiden zu können.
- In System.out.print ist eine Ortsangabe für die Methode print enthalten.
Beispiel: Methode Eingabe zum Einlesen einer ganzen Zahl
static int Eingabe () // JDK 1.2 kompatibel
...
{ String s = ""; int f = 0;
java.io.BufferedReader d =
new java.io.BufferedReader(
new java.io.InputStreamReader(System.in));
try { s = d.readLine();}
catch (java.io.IOException e) { }
try { f = Integer.valueOf(s).intValue(); }
catch (NumberFormatException e)
{ System.out.println(
"String Conversion to int failed" + e); }
return f;
}
Beispiel: Neuheit in Java 1.5: formatierte Eingabe
- Ersetzt die Version der vorangegangenen Seite
- Erleichtert das Programmier-Leben
import java.util.Scanner;
Scanner s = new Scanner(System.in);
String param = s.next();
int value = s.nextInt();
s.close();
- Algorithmen werden mit Java in Form von Methoden-Rümpfen formuliert
- Ein Methodenrumpf besteht aus Vereinbarungen (=Variablendeklarationen) und Anweisungen
- Anweisungen und Vereinbarungen werden nacheinander aufgeschrieben und durch Semikolon getrennt
void abc (.....)
{ int b;
b = 5;
}
Zusammenfassung:
[Bearbeiten]Wir haben kennengelernt:
- Variablen (Speicher)
- Anweisungen ...
- Zuweisung ( x = ... )
- Aufruf von Operationen (d.h. Methoden)
Primitive Datentypen
[Bearbeiten]Datentypen
- Datentypen fassen die Wertemenge und die zulässigen Operationen auf den Werten zu einem
Begriff zusammen (z.B. macht es wenig Sinn in dem Ausdruck 13 * x + x/2 für die Variable x den Wert ”Oktoberfest” einzusetzen).
- In Programmiersprachen wie Java muss daher alles zusammenpassen:
- Typ einer Variable,
- der Wert, der dort gespeichert ist, und
- die Operationen, die auf diesem Wert angewendet werden
Objektorientierte Programmiersprachen wie Java bieten benutzerdefinierte Typen
- Die Idee der Klasse eines Objektes: Zusammenfassung der objektspezifischen Daten
und Operationen.
- Bibliotheken bieten einigen Komfort in Form häufig vorkommender Datentypen (Grafik, ...)
- Außerdem gibt es die so genannten primitiven Datentypen, die aus verschiedenen Gründen fest
in Java eingebaut sind (können daher auch nicht verändert werden).
Primitiver Datentyp: Ganze Zahlen
- Ganze Zahlen .... vier verschiedene Datentypen:
- byte Wertebereich [ –128 ... 127 ]
- short Wertebereich [ –215 ... 215 –1]
- int Wertebereich [ –231 ... 231 –1]
- long Wertebereich [ –263 ... 263 –1]
- Interne Darstellung hier zunächst nicht wichtig
- Meist reicht int aus
- Wichtig: Den zulässigen Zahlenbereich nicht
verlassen ⇒ sonst falsche Ergebnisse möglich!
- Operationen: +, –, *, / (ganzzahlig), % (modulo)
Details zu Operationen auf ganzen Zahlen
- Die ganzzahlige Division und die Modulobildung ist auch auf negative Zahlen anwendbar, wie die folgenden Beispiele zeigen:
- (– 14) / 3 liefert – 4
- 14 / (– 3) liefert – 4
- (– 14) / (– 3) liefert 4
- (– 14) % 3 liefert –2
- 14 % (– 3) liefert 2 (– 14) % (– 3) liefert –2
- Unzulässig sind die Division durch null (z.B. a / 0) sowie die entsprechende Restbildung (z.B. a % 0)
- Konstanten des Datentyps byte, short, int, long werden wie übliche Zahlen aufgeschrieben.
Primitiver Datentyp: Gleitkommazahlen (Teilmenge der rationalen Zahlen)
- Interne Darstellung in der Form Vorzeichen ⋅ Mantisse ⋅ 2Exponent
- z.B. + 0,625 ⋅ 22 = 2,5
- Salopp: der Exponent definiert, wo der Dezimalpunkt (besser Dual-Punkt) ist
- Schreibweisen für Vorz.⋅ Mantisse ⋅ 10Exponent
- -1.5
- 100.
- 1.e12f
- .6
- +17.208E-3d
Besonderheiten bei Gleitkommazahlen
Die rationalen Zahlen in den Intervallen
- float: [–3,4028235·1038 ... 3,4028235·1038]
- double: [–1,797693·10308 .... 1,797693·10308]
werden nur approximiert. D.h. nur ca. 232 bei float und 264 rationale Zahlen bei double werden exakt dargestellt ...
Die kleinste von 0.0 verschiedene Zahl lautet bei
- float 1,4012984·10 -45
- double 4,9406564·10 -324
Die üblichen Operationen stehen zur Verfügung: +, –, *, /
Achtung: Es müssen immer Rundungsabweichungen beachtet werden, die sich zu beachtlichen Fehlern aufschaukeln können.
Beispiel einer Rundungsabweichung:
public class raetselhaft
{
public static void main (String args [ ])
{ float a = 300.1f, b = 400.1f,
c = 200.1f, d = 600.1f,
e = 10.0f, y;
y = b *(a*b + e – c*d)*d + (a – c)/(e*e);
System.out.println ("y = " + y);
}
}
Das erwartete Ergebnis lautet ... 1 . Das Programm rechnet falsch 1876,78 !?
Das Problem ist die Darstellung von 0,1 im Dual-System. Klammer: (120 070,01 + 10 – 120 080,01) = 0 0,0078125
Ein sehr wichtiger primitiver Datentyp sind die Wahrheitswerte true und false
Beispiele für Variablen des Datentyps:
boolean angemeldet, bezahlt, storniert;
angemeldet = true;
bezahlt = false;
Außerdem gibt es die Operationen <, >, <=, >=, ==, !=, die auf den numerischen Datentypen definiert sind und Werte aus dem Datentyp boolean liefern.
Operationen auf Werten des Datentyps boolean sind
- oder ||,
- und &&,
- nicht !
public class booleanBeispiel
{ public static void main (String Parameter[ ])
{ float gemessenerDruck = 5.0f,
Maximaldruck = 18.8f;
int ZuflussStufe = 2 ,
AbflussStufe = 3 ;
boolean Ueberdruck,
unkritisch;
Ueberdruck = gemessenerDruck > Maximaldruck;
unkritisch = Ueberdruck
&& gemessenerDruck < 1.2f * Maximaldruck
&& ZuflussStufe <= AbflussStufe;
System.out.println("Überdruck: " + Ueberdruck +
" unkritisch: " + unkritisch );
}
}
Im Beispiel (Prog 2-9) werden boolsche und arithmetische Ausdrücke gemischt! Das wirft die Frage nach der Prioritätsregelung auf.
Eindeutige Prioritätsregelung:
1. Höchste Priorität genießen die unären Operatoren positives (+) und negatives (–) Vorzeichen sowie das boolsche Komplement (!). 2. Multiplikative Operationen (Punktrechnungen * / %) 3. Additive Operationen (Strichrechnungen + –) 4. Vergleiche (== != < > <= >=) 5. Und-Verknüpfung (&&) 6. Niedrigste Priorität besitzt schließlich die Oder-Verknüpfung (||).
Es hilft Klammern zu setzen!
Die Zeichen in Zeichenketten sind in dem primitiven Datentyp char definiert (1)
- Der Wertebereich umfasst die Groß- und Kleinbuchstaben, Sonderzeichen und
Spezialzeichen (Steuerzeichen, z.B. Leerzeichen )
- Konstanten werden in einfache Hochkomma ’ gesetzt:
’a’ ’Ä’ ’?’ ....
- Die Zeichen werden alle in einer Tabelle (Unicode) mit Nummern versehen ...
- Steuerzeichen werden in einer Spezialnotation angegeben (sog. Escape-Sequenzen \ ):
- ’\n’ Zeilenvorschub
- ’\t’ Tabulator,
- ’\’’ für ’
- ’\"’ für " ,
- ’\\’ für \
- Die Vergleichsoperationen sind für char auf der Basis der Unicode-Tabelle definiert
- char rund = ‘(‘, eckig = ‘[‘;
- boolean x = rund < eckig ;
Die Bereich der Buchstaben und Ziffern sind zusammenhängend.
Strings
[Bearbeiten]Zeichenketten: Datentyp String ist nicht primitiv in Java
- In der Sprache Java spielen Werte (=Objekte)
vom Typ String eine Sonderrolle
- Konstanten können direkt angegeben werden:
"Der Mond scheint blau \n"
- Zeichenketten können mittels + verkettet werden
- Beliebige Datentypen können in Strings umgewandelt werden, wenn sie als Parameter von
println auftauchen
- Als Vergleichsoperationen sind nur == und != zugelassen.
Vorsicht beim Vergleich von Objekten vom Typ String
public class StringVergleich
{ public static void main (String args [])
{ String a = "merkwürdig",
b = "merkwürdig",
c = new String ("merkwürdig"),
d = new String ("merkwürdig");
System.out.println (a + " " + (a == b) + " "
+ c + " " + (c == d) );
}
}
Im nächsten Beispiel sind die letzten beiden Strings c und d sind unterschiedliche Objekte. Daher liefert == zwischen ihnen false!
public class StringVergleich
{ public static void main(String args[ ])
{ String a = "merkwürdig",
b = "merkwürdig",
c = new String ("merkwürdig"),
d = new String ("merkwürdig");
Situation der Variablen a, b, c und d
new erzeugt neue separate Objekte, in diesem Fall mit dem gleichen Inhalt, während die Deklaration von a und b aus Ersparnisgründen auf ein und dasselbe Objekt verweisen.
Folie 42
Arrays
[Bearbeiten]Datentypen, Typcasting
[Bearbeiten]Das gesamte System von Datentypen in Java wird als streng bezeichnet
- Jede Variable in Java muss mit einem Typ deklariert
werden, der festlegt
- welche Werte die Variable aufnehmen kann
- welche Operationen auf diesen Werten
anwendbar sind
- Hilft bei der Überprüfung vieler einfacher Fehler!
- Ist gelegentlich hinderlich, wenn man offensichtliche Gemeinsamkeiten von Datentypen ausnutzen möchte (z.B. Werte des Typs short und int verknüpfen)
Daher wird der Begriff der Typkompatibilität eingeführt
- Man wird also 3.5 * x als
- typkompatibel bezeichnen, wenn 3.5 (float) und x double ist
- nicht typkompatibel bezeichnen wenn x boolean ist
- Des Weiteren betrachte den Operator +
- Bezeichnet Addition zwischen den numerischen Typen
- String-Verkettung zwischen Strings
Implizite Typanpassung
[Bearbeiten]- nicht zwischen beliebigen Typen möglich
- Die Umwandlung erfolgt nur in Richtung des allgemeineren Typs:
- int und boolean sind nicht kompatibel,
- int → float hingegen schon.
Insgesamt gilt folgende Regelung:
byte short int long float double
- char int
- boolean string
- double string
Besonderheit bzgl. des roten Pfeils → String
- Bereits in den ersten Beispielprogrammen:
System.out.println("Wert: " + a);
- Hier wird keine implizite Datentyp-Umwandlung vorgenommen, sondern eine explizite mit Hilfe der Methode toString.
- toString ist im Java-System definiert und kann vom Programmierer verändert werden.
- Der Java-Compiler sorgt dafür, dass in Ausdrücken der Form xxx + stringstring
für xxx zunächst das zugehörige toString aufgerufen wird.
In Ausdrücken ist die Reihenfolge der Operatoren zu beachten
Beispiel: System.out.print(17 + ” und ” + 4)liefert: 17 und 4
Aber:
System.out.print(17 + 4 + “ und“) liefert 21 und
- Ansonsten arbeiten die Umwandlungsregeln wie erwartet
- Gelegentlich sind auch Umwandlungen 5.4 → 5 sinnvoll ...
Umwandlungen zum „gröberen“ Datentyp können Ungenauigkeit und Verlust an Information hervorrufen
- Ist möglich, aber nur auf explizite Anweisung hin
- Erfolgt durch Voranstellen des gewünschten Ziel-Typs in Klammern:
- float x = 82.2f;
- int n = (int) x;
- Nachkomma-Teil wird abgeschnitten
Die Umwandlungsregeln beziehen sich auf sinnvolle Beziehungen zwischen den verschiedenen Datentypen
- Zahlen und Zeichen (auf der Basis der Unicode-Tabelle)
- Nicht alles, was sinnvoll wäre, geht auch in Java:
(int) "345" geht nicht, muss durch Integer.parseInt("345") erledigt werden
- Falls eine Konversion nicht existiert: Exception
Informationsverlust droht u.a. bei Überschreitung des Wertebereichs
- Leider entdeckt Java dieses Problem nicht automatisch:
(short) 100000 liefert –31072
Gerichtete Graphen
[Bearbeiten]class Knoten {
String Bez; Knoten Nf; Kante Kopf, Fuss;
Knoten(String Bez) {
this.Bez = Bez; Nf = null; Kopf = Fuss = null;
}
}
class Kante {
String Bez; Kante Nf; Knoten Kante;
Kante (String Bez, Knoten Kante) {
this.Bez = Bez; Nf=null; this.Kante = Kante;
}
}
<syntaxhighlight lang="java">
class Graph
{ Knoten Kopf, Fuss;
Graph (){
Kopf = Fuss = null;
}
Knoten suche_Knoten (String Bez) {
Knoten k = Kopf;
while ( k != null && ! k.Bez.equals(Bez) {
k = k.Nf;
}
return k;
}
Kante suche_Kante(String von, String nach) {
Knoten vonK = suche_Knoten(von), nachK = suche_Knoten(nach);
if (vonK != null) {
Kante ka = vonK.Kopf;
while ( ka != null && ! ka.Kante != nachK) {
ka = ka.Nf;
return ka;
}
else return null;
}
void durchlaufe() {
System.out.println("Graph:" ) ; durchlaufe(Kopf);
}
void durchlaufe (Knoten k) {
if ( k != null ) {
System.out.println(k.Bez + "->" ); durchlaufe (k.Kopf);
System.out.println() ; durchlaufe (k.Nf);
}
}
void durchlaufe (Kante ka) {
if ( ka != null ) {
if ( ka.Bez.length() > 0 } {
System.out.print("+ ka.Bez + " + ka.Kante.Bez);
durchlaufe(Kopf);
}
else {
System.out.print(ka.Kante.Bez);
durchlaufe(ka.Nf);
}
}
}
void erzeuge_Knoten(String Bez) {
if (Kopf == null) {
Kopf = Fuss = new Knoten(Bez);
}
else (
Fuss = Fuss.Nf = new Knoten(Bez);
}
}
void erzeuge_Kanten(String von, String nach, String Bez) {
Knoten vonK = suche_Knoten(von) , nachK = suche_Knoten(nach);
if ( vonK != null && nachK != null ) {
if ( vonK.Kopf == null) {
{
vonK.Kopf = vonK.Fuss = new Kante(Bezn nachK);
}
else {
vonK.Fuss = vonK.Fuss.Nf = new Kante(Bez, nachK);
}
}
void entferne_Knoten(String Bez) {
}
void entferne_Knoten(String Bez, Knoten k) {
if ( k !=null ) {
if ( k.Bez.equals(Bez)) {
entferne_hinf(Kopf k) ; return k.Nf;
}
else {
k.Nf = entferne_Knoten(Bez, k.Nf) ; return k;
}
else {
return null;
}
}
void entferne_hinf(Knoten vonK, Knoten nachK) {
}
void entferne_Kante(String von, String nach) {
}
Kante entferne_Kante(Kante ka, Knoten nachK){
}