Zum Inhalt springen

Kurs:Wirtschaftsinformatik WS08 09 PROGRAMMIERUNG/Lernskript

Aus Wikiversity

Kurs:Programmierung

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){
}