Okay wunderbar, dann können wir jetzt auch richtig starten.
Ich möchte Sie alle auch heute wieder ganz herzlich begrüßen zur
Programmieren -Vorlesung.
Wir hatten gerade schon eine kleine Fragerunde vorab, weil ich schon
ein bisschen früher da war.
Das war auch ganz gut, weil dann hat sich gleich was ergeben, was
wahrscheinlich für einige von Ihnen interessant ist.
Heute in der Vorlesung soll es um das Thema Typen und Variablen geben.
Da haben Sie beim letzten Mal schon ein paar Beispiele dafür gesehen,
weil wir ja dieses einfache Programm und so diese Grundkonzepte oder
Grundbausteine eines Programms uns schon angeschaut haben.
Aber erstmal sozusagen nur ein Überblick und in den folgenden
Vorlesungen heute und auch später nochmal werden wir quasi einzelne
Elemente, die wir da gesehen haben, eben Typen, Variablen und dann
später auch Methodenaufrufe noch ein bisschen genauer anschauen.
Also Sie haben jetzt bisher erstmal nur einen Überblick bekommen.
Sie müssen das noch nicht alles, was wir beim letzten Mal gesehen
haben, alles schon im Detail verstanden haben, können aber natürlich
gerne auch schon ein bisschen damit herumspielen und sich so ein
Programm selber mal anlegen, selbst kompilieren, ausführen und ein
bisschen ändern und gucken, was passiert sozusagen.
Genau, das sind die beiden Themen für heute, Typen und Variablen, aber
vorher erst nochmal wieder ein bisschen was Organisatorisches zum
Praktomat.
Wie ich beim letzten Mal schon erläutert habe, funktioniert unser
Übungsbetrieb ja so, dass Sie Ihre Abgaben für den Übungsschein
elektronisch abgeben.
Das erste Übungsblatt wird jetzt, was ist heute?
Nächste Woche, erscheinen.
Sie können das bei uns auf der Programmieren-Wiki-Seite zum Beispiel
herunterladen und sollen dann eben entsprechend Java-Programme
schreiben als Abgabe und diese Abgabe der Lösung dann über dieses
Praktomat -System hochladen.
Und dafür müssen Sie beim Praktomat angemeldet und freigeschaltet
sein.
Und um das wiederum hinzubekommen, das habe ich beim letzten Mal ja
auch schon erläutert, benötigen wir von Ihnen diese
Einverständniserklärung.
Details dazu gibt es auf den Orga-Folien vom letzten Mal.
Und zwar bis zum 4.11.
um 12 Uhr mit Unterschrift und Datum.
Also dass wir wirklich wissen, Sie eigenhändig versichern uns, dass
Sie eben mit diesem Betrieb so einverstanden sind.
Die Abgabe per Einwurf in den Briefkasten, wie es hier steht.
Und hier auch nochmal der Link, wo Sie eben dieses PDF, diese Vorlage
für diese Einverständniserklärung finden.
Dann das zweite Wichtige, was Sie machen müssten, ist, sich auch
einmalig bei dem Praktomat-System anmelden.
Eben, das wissen Sie ja schon, aus dem KIT-Netz, mit Ihrem KIT
-Account.
Hier ist nochmal der Link.
Wenn Sie dann den Disclaimer schon abgegeben haben und das System
Ihnen trotzdem noch zeigt, bitte Disclaimer abgeben oder bitte
Einverständniserklärung abgeben, dann warten Sie ruhig bitte noch ab,
weil wir werden die ganzen Einverständniserklärungen, die wir sammeln,
eben in den Briefkasten dann erst relativ kurz vorher oder eigentlich
erst nach dieser Abgabefrist am 4.11.
dann alle ins System eintragen.
Also werden Sie nicht nervös, weil Sie immer noch auf
Einverständniserklärung fehlt stehen, sondern warten Sie dann auch
nach dem 4.11.
bitte ein paar Tage ab.
Wenn dann am zum Beispiel 10.11.
da immer noch eine Fehlermeldung im Praktomat steht, dass die
Einverständniserklärung noch fehlt, dann melden Sie sich bitte bei uns
und spätestens dann, wenn der Praktomat dann ab dem 11.11., das steht
jetzt hier nicht drauf, aber ab dem 11.11.
dann für Ihre Abgaben freigeschaltet ist und es dann immer noch nicht
bei Ihnen funktioniert, dann melden Sie sich entsprechend bei
programmierenvorlesung.itpd und so weiter, also der E-Mail-Adresse,
die ich Ihnen das letzte Mal mitgegeben habe.
Erstmal nochmal zum Rekapitulieren.
Was haben wir letzte Woche gemacht und was kennen Sie jetzt im Prinzip
schon?
Das sind nochmal die Lernziele der letzten Woche und jetzt ergänzt so
ein bisschen um Elemente aus dem, was Sie gleich auf der nächsten
Folie sehen, den sogenannten Advanced Organizer, der Ihnen die Inhalte
der ganzen Vorlesung nochmal ein bisschen grafisch darstellen soll und
hier jetzt im Prinzip schon mal die Elemente, die Sie jetzt schon
kennen.
Beim letzten Mal haben wir, mal gucken, ob ich hier bin, uns ein
einfaches Programm angeschaut mit dem Lernziel, dass Sie ein einfaches
Programm eben schreiben, kompilieren und ausführen können, wie das auf
der Konsole funktioniert, was Sie da aufrufen müssen und so weiter und
Sie haben auch die Grundelemente eines Java-Programms kennengelernt
und das ist dann gleich in unserem Advanced Organizer im Bereich
Methodik, eben diese Grundlagen zum Ausführen und auch zur Ein- und
Ausgabe mit diesem System.println, was Sie da gesehen haben.
Dann haben wir letztes Mal über Objekte und Klassen gesprochen.
Da war das Lernziel, dass Sie für ein Gegebnisproblem Objekte und
Klassen identifizieren können und auch Klassen beschreiben können
durch Attribute und Methoden, dass Sie auch Klassen in Java ausdrücken
können, eben dieser .java-Datei eine Klasse spezifizieren können und
dass Sie neue Objekte einer Klasse erzeugen können, indem Sie eben ein
Konstruktor verwenden und mit diesem new Operator eine neue Klasse
erzeugen.
Und das ist eben schon eine ganz grundsätzliche Sache in der
objektorientierten Programmierung, eben dieser Unterschied zwischen
einerseits Klassen, die Schablonen sind für Objekte, die dann zur
Laufzeit des Programms erzeugt werden und die Objekte sind andersrum
Instanzen von Klassen.
Die Klassen sind das, was Sie schreiben, wenn Sie programmieren und
zur Laufzeit werden dann eben Objekte dieser Klassen erzeugt und rufen
sich gegenseitig auf und realisieren dann im Prinzip ihr Programm zur
Laufzeit des Programms.
Also von daher ganz zentrale Geschichte schon.
Ein paar Details wie hier, das ist ja eine Methode, haben wir noch
nicht besprochen, aber dieses grobe Schema haben wir dann eben an der
Stelle schon besprochen.
Und das wie gesagt ist ein ganz zentraler Punkt von den Inhalten
insgesamt dieser Vorlesung und hier ist eben dieser sogenannte
Advanced Organizer, der Sie verschiedenen Themen der Vorlesung für Sie
nochmal grafisch darstellen soll.
Hier finden Sie eben die Klassen und die Objekte.
Sie finden auch, dass Klassen eine Art von Datentypen in Java sind,
die andere sind die elementaren Datentypen, die wir uns heute
anschauen werden.
Und Objekte sind eben Instanzen dieser Klasse, das steht ja hier von,
im Allgemeinen Instanzen von Datentypen sind Werte und wir werden
heute auch noch über Variablen, das sind Behälter oder
Speicherbereiche für diese Werte uns unterhalten und auch dann schon
über das Thema wie Variablen für Objekte funktionieren.
Jetzt mal kurz beschrieben, das hat was mit Objektidentität und
Referenzen zu tun.
Im Bereich Methode kamen wir auch noch ein bisschen mehr.
Hier hatten wir Grundlagen ausführen, Ein- und Ausgabe schon.
Es wird später in der Vorlesung auch mal ums Fehler finden gehen, um
Qualitätsstandards, um Best Practices, wie man elegant und schön
programmiert.
Es wird ein bisschen was zu Java und Java-Bibliotheken, die Sie
verwenden können, geben.
Und Designprinzipien, Best Practices, habe ich gerade schon gesagt.
Die Fehler finden Vorlesung kommt erst relativ am Ende, aber falls Sie
da schon zwischendurch mal Luft und Zeit haben in Ihrem
Studienverlauf, was vielleicht nicht der Fall ist, ist das vielleicht
auch was, was Sie sich im Voraus schon mal anschauen können, weil es
auch beim Bearbeiten der Übungsaufgaben der Späteren helfen kann,
kommt bei uns halt ein bisschen später dann erst dran, weil vieles
helfen könnte.
Zustand, Verhalten kommt dann auch irgendwann später.
Das ist dann noch so die Ergänzung, was Klassen alles so enthalten.
Und dann werden wir weiter darauf aufbauen, aus diesen Grundbausteinen
komplexere Systeme zu bauen, bis hin dann zu den Abschlussaufgaben.
Da sind dann so Themen wie Felder, wo viele Daten mit verwaltet werden
können und auch Listen, wo auch viele Daten mit verwaltet werden
können.
Ein Thema, das Prinzip der Datenkapselung, was dann zu sozusagen
fortgeschrittenen Konzepten führt, wie abstrakte Datentypen und
sogenannte Interfaces.
Das sind Schnittstellen, um ein Programm gut modular aufbauen zu
können.
Und in der objektorientierten Programmierung auch noch ganz wichtig,
das Prinzip der Vererbung, dass Klassen spezialisiert werden können.
Zum Beispiel aus einer Klasse Ball, wie wir sie hier noch hatten, kann
man spezialisiert dann auch die Klassen Billardball und Fußball zum
Beispiel ableiten.
Das sind Bälle, aber man kann eben an einigen Stellen sie als Bälle
behandeln, an anderen Stellen als die spezielleren Klassen.
Das ist ein sehr mächtiges Werkzeug bei der Programmierung.
Das ist also so der Überblick und wie gesagt, heute geht es um die
primitiven oder elementaren Datentypen und um Variablen, Objekte,
Identität und Referenzen.
Auf der nächsten Folie, wenn Sie da nochmal drauf gucken wollen, auch
nochmal der jetzt aktualisierte Semesterplan.
Hier im hinteren Bereich haben wir ein bisschen was verschoben, damit
Sie eben diese Kriterien, die nachher relevant sind für die
Bearbeitung der Abschlussaufgaben auch schon früh genug mitbekommen
und quasi in den Übungsaufgaben auch schon einüben können.
Deswegen hat sich hier hinten die Reihenfolge im Vergleich zur letzten
Vorlesung ein bisschen geändert, aber hier vorne eigentlich alles
gleich.
Gut, nochmal anknüpfend an das, was wir letzte Woche gemacht haben.
Wir haben ja letzte Woche diese Grundelemente eines Java-Programms,
also dieses Bild hier schon uns angeschaut, haben gesagt, aha, das ist
ein Beispiel für eine Klasse.
Klasse hat einen Namen.
Hier gibt es die Main-Methode zum Beispiel.
Heute eben in dem Bereich zu Typen und Variablen gibt es jetzt Details
zu den Dingen, die wir beim letzten Mal schon kurz genannt haben.
Nämlich, dass hier so was wie Int oder auch wie String ein Datentyp
ist.
Also Datentypen werden wir uns heute anschauen.
Und dass dann diese Dinge wie N oder Summe hier, also Sum Variablen
sind und was das ist und wie die funktionieren.
Was eine Variablen-Deklaration ist.
Werte sind eben etwas, was wir im Bereich Datentypen schon besprechen
werden und was eine Variablen-Initialisierung sind.
Das sind jetzt Themen, um die wir uns heute kümmern werden.
Die Lernziele entsprechend heute sind, dass Sie im Bereich Typen die
acht elementaren Datentypen oder auf Englisch auch primitiven
Datentypen kennen in Java und können für Ihr Problem, was Sie gerade
lösen wollen in den Übungsaufgaben und dann auch später natürlich im
echten Leben sozusagen, dafür können Sie einen geeigneten elementaren
Datentyp auswählen, der zu dem passt, was Sie da lösen wollen als
Problem.
Und das zweite Lernziel an der Stelle, dass Sie für die elementaren
Datentypen, die wir heute behandeln werden, auch die relevanten
Operationen kennen und diese auch anwenden können.
Operation ist sowas wie Addition, aber auch sowas wie Zeichenketten
zusammenfügen.
Das wären zwei Beispiele für Operationen und Operatoren.
Im Bereich Variablen sollen Sie heute lernen, dass Variablen Namen für
Speicherplätze sind, in denen Werte zwischenzeitlich abgelegt werden
können während der Berechnung.
Sie sollen dann Variablen deklarieren können und ihren Wert durch
Zuweisung verändern.
Das schauen wir uns dann natürlich genauer an.
Und Sie wissen auch, wie das bei den Objektvariablen funktioniert.
Das ist da nämlich ein bisschen anders.
Dazu müssen Sie wissen, wie Objektvariablen im Speicher abgelegt
werden und dann auch unterscheiden können zwischen der Objektidentität
und einer Referenz auf ein Objekt.
Das ist eben das, was hier in diesem Advanced Organizer durch diesen
Teil hier dargestellt werden soll und wo Sie dann am Ende dieser
Vorlesung hoffentlich genauer wissen, was das Bild da bedeutet.
Fangen wir zunächst bei den Datentypen an.
Hier erstmal eine Definition, wie wir das an der Uni immer ganz gerne
tun.
Ein Datentyp oder auch einfach Typ, manchmal im folgenden, bezeichnet
eine Menge irgendwie gleichartiger Werte.
Das ist ganz analog zu auch den Klassen.
Da haben wir auch gesagt, eine Klasse beschreibt eine Menge irgendwie
gleichartiger Objekte und hier ist es eben allgemeiner jetzt so, ein
Datentyp beschreibt eine Menge gleichartiger Werte.
Und Datentypen, das ist der nächste Punkt, legen die möglichen Werte
von Variablen, Attributen und Methoden fest.
In Java ist es eben so, dass man schon während man ein Programm
schreibt, explizit angibt, was für ein Datentyp etwas ist und sein
wird und damit festlegt, welche Werte an dieser Stelle im Programm
sozusagen überhaupt infrage kommen für die Berechnung.
Und Datentypen weiterhin bestimmen auch, welche Operationen auf den
Werten möglich ist.
Dass zum Beispiel zwei Zahlen addiert werden können und zwei
Zeichenketten nicht zum Beispiel.
Genau, in Java gibt es, wie eben schon gesagt, acht elementare
Datentypen, die wir uns im folgenden anschauen werden.
Auf Englisch eben primitive datatypes, auf Deutsch sagt man auch
manchmal primitive Datentypen.
Und wenn man das jetzt mal ein bisschen gruppieren will, so was wie
Zahlen, und da kann man noch unterscheiden zwischen ganzen Zahlen und
Fließkommazahlen, also quasi reelle Zahlen, aber ein bisschen
eingeschränkt.
Dann Wahrheitswerte, nämlich einfach ein Datentyp, der nur die Werte
wahr und falsch enthält und sonst nichts.
Und auch Zeichen, also wirklich so was wie A, B, C sind Zeichen.
Zusätzlich zu diesen elementaren Datentypen ist in Java auch jede
Klasse ein Datentyp.
Also eine Klasse beschreibt ja sozusagen die möglichen Objekte und
beschreibt damit auch eine Menge von Werten, bloß dass die Werte eben
nicht elementare, einzelne sozusagen Werte sind, sondern ganze
Objekte.
Und damit kann ich eben immer beim Programmieren diese elementaren
Datentypen hier oben erweitern, um eigene benutzerdefinierte und quasi
meine eigenen Datentypen, die ich benötige in meiner Verarbeitung,
definieren, wie zum Beispiel, das hatten wir in der letzten Vorlesung
schon gesehen, diese Vector2D-Klasse, um einen neuen Datentyp für das
Behandeln von Vektoren einzuführen.
Wie gesagt, eben im Advanced Organizer sind wir dann an dieser Stelle.
Klassen kennen sie schon und jetzt gucken wir uns auch die primitiven
Datentypen an und damit kennen sie die Datentypen.
In unserem einfachen Programm sind Datentypen eben an diesen drei
Stellen hier schon zu sehen.
Hier steht, dass diese eigentlich Parameter-Variable vom Typ String
ist.
Das dahinter bedeutet nochmal, dass es ein Array ist, ein sogenanntes
Feld.
Das kommt ein bisschen später, aber der Datentyp String, da reden wir
heute drüber.
Und dass diese beiden Variablen n und sum, dass die vom Typ Integer
sind.
Und was das ist, wie gesagt, schauen wir uns im Folgenden an.
Hier einmal eine Übersicht über diese elementaren Datentypen in Java.
Wir haben also jetzt so von einfach nach kompliziert sortiert diese
acht Datentypen.
Der erste ist der Boolean.
Das sind einfach Wahrheitswerte und der Wertebereich enthält nur zwei
mögliche Werte, nämlich wahr und falsch.
Dann haben wir K für Character.
Das sind Zeichen, wie zum Beispiel ein A.
Das ist ein Zeilenumbruch, das ist auch ein Zeichen.
Oder hier ein Symbol, was man in dem Editor nicht so leicht ausdrücken
kann.
Ich glaube, das ist jetzt das Aleph, ich bin mir aber nicht ganz
sicher.
Das hebräische Symbol, was auch nur ein Zeichen ist, aber dann auch
über so eine escaped Sequenz ausgedrückt werden kann.
Aber im Prinzip Character für einzelne Zeichen, die man dann in
Anführungszeichen schreibt, um zu sagen, das ist jetzt nur das
einzelne Zeichen, was man an der Stelle meint.
Dann gibt es eine Menge von ganzen Zahlen, also Datentypen für ganze
Zahlen.
Es gibt Byte als Datentyp für sozusagen recht kleine ganze Zahlen.
Man benutzt nur 8-Bit, um diese Zahlen abzuspeichern.
Man kann damit nur einen kleineren Wertebereich beschreiben.
Es gibt ein bisschen länger Short.
Noch länger, das ist eigentlich das Typische, was man in Java oft
benutzt.
Den Datentyp Int für Integer, Ganzzahl auf Englisch.
Und den Datentyp Long für große, ganze Zahlen.
So ist es in Java gedacht.
Und Beispiele für diese ganzen Zahlen wären jeweils einfach 12, sowohl
für Byte, Short als auch Integer.
Wenn man Long schreiben möchte in Java, kann man das noch tun, indem
man ein L dahinter hängt, um dem Compiler zu sagen, dass man jetzt ein
Long -Wert an der Stelle meint.
Also eine große Ganzzahl oder eine potenziell große Ganzzahl.
Und dann gibt es noch zwei Fließkommazahlen oder Gleitpunktzahlen oder
zwei Datentypen für Gleitpunktzahlen.
Nämlich einmal Float und einmal Double, die unterschiedlich große
Wertebereiche bezeichnen, wie wir das gleich nochmal sehen würden.
Und einmal 32 und einmal 64 Bit zum Abspeichern dieser Werte
enthalten.
Beispiel wäre da zum Beispiel hier 9,81 F.
Das wäre dann ein Float.
Also das F gibt an, dass man jetzt einen Wert vom Datentyp Float hier
bezeichnen möchte.
Bei den Doubles kann man dieses F weglassen.
Das ist sozusagen der Standard in Java.
Mit 9,81 bezeichne ich eben ein Fließkomma-Wert vom Typ Double.
Hier stehen jetzt schon so ein bisschen in Speichergrößen die
Wertebereiche angedeutet.
Auf der nächsten Folie jetzt nochmal ein paar Details dazu, wie diese
Wertebereiche ganz konkret aussehen.
Beim Character ist es so, dass die Zeichen eigentlich auch intern als
Zahlen kodiert werden und man hat dafür vier Bits.
Man kann die Zahlen als Bit repräsentiert 0,0,0, was dann auch die
Ziffer sozusagen 0 ist, bis... jetzt ist die gute Frage, wofür das
hier eigentlich tatsächlich steht, zumindest bis auch zu einem Wert
FFF jetzt als hexadezimal kodiert ausdrücken, was, wenn man es als
Zahl interpretieren würde, die 65.000 noch was wäre, aber als
Character kann es eben auch als Zeichen interpretiert werden.
Bei Byte geht der Wertebereich von minus 128 bis plus 127.
Diese Zahlen sind in diesem Wertebereich enthalten und mit diesen
Zahlen kann ich rechnen in diesem Intervall, wenn ich mit Variablen
dann vom Typ Byte rechne.
Bei Short ist es schon ein bisschen mehr, 32.000 minus 32.000 bis plus
32.000, paar wie noch.
Integer sehen Sie ist schon größer, 2 Milliarden, also insgesamt 4
Milliarden mögliche Werte.
Beim Long habe ich dann insgesamt von minus 9 Trillionen waren es
glaube ich, genau, bis plus 9 Trillionen die ganzen ganzzahligen
Zahlen im Wertebereich von Long, also insgesamt 18 Trillionen mögliche
Zahlen.
Genau, das sind eben diese Wertebereiche für die ganzen Zahlen.
Bei den Floats und Doubles, also bei den Fließkommazahlen habe ich
Werte, also Teilmengen der reellen Zahlen.
Da ist es jetzt also nicht so, dass ich beliebige reelle Zahlen
zwischen dieser Zahl minus 3 mal 10 hoch 38 und plus 3 mal 10 hoch 38
ausdrücken kann, sondern ich kann nur in diesem Bereich, kann ich
Zahlen ausdrücken, aber wie gesagt nicht jede beliebige Zahl, das
wären ja unendlich viele, sondern eine ausgewählte Menge, die eben in
der Art und Weise wie Fließkommazahlen im Rechner codiert werden,
bestimmt wird, da kommen wir gleich auch nochmal zu.
Und bei Doubles sind es entsprechend nochmal viel mehr, weil es
doppelt so viele Bits sind, entsprechend kann man viel mehr Zahlen
hier ausdrücken.
Diese Werte, wir Menschen sind ja dran gewöhnt, Werte im Dezimalsystem
darzustellen, also wir zählen von 0 bis 9 und dann verwenden wir 1, 0,
um 10 zu repräsentieren und zählen dann entsprechend weiter im
Dezimalsystem.
Im Zusammenhang mit der Programmierung gibt es auch noch zwei andere
Systeme, die man recht häufig verwendet, einmal das Oktalsystem, wo
man nur die Ziffern bis 7 verwendet und dann quasi anstatt einer 8,
dann schreibt 1 0 oder das Hexadezimalsystem, wo man zusätzlich zu den
Ziffern von 0 bis 9 auch noch A bis F verwendet, um quasi bis 16 zu
zählen und dann erst wieder eine 1 davorzunehmen, das sind hier
entsprechend die Beispiele und in Java kann ich auch in meinem
Quellcode direkt auch so meine Zahlen schreiben, indem ich eine 0
davor setze, dann wird es als Oktalzahl interpretiert oder indem ich
0x davor setze, dann wird es als Hexadezimalzahl interpretiert, was
ich da hingeschrieben habe.
Oder ganz wichtig natürlich der Hinweis, der Rechner selber rechnet
weder im Dezimalsystem, noch im Oktalsystem, noch im
Hexadezimalsystem, sondern binär.
Also der Rechner kennt Strom ein oder Strom aus, wer drin im Speicher
oder nicht drin.
Also wie gesagt, der Rechner kennt nur 1 und 0, das heißt der Rechner
stellt intern alle Zahlen im Binärsystem dar, kennt also nur die
Ziffern 1 und 0.
Entsprechend das Beispiel, was wir hier haben, 18 im Dezimalsystem,
wäre im Rechner eben dargestellt als 1 0 0 1 0.
Oder diese Zahlen, 65.000 und noch was, eben als so eine lange Kette
von Einsen.
Gut, wir kommen da gleich nochmal ein bisschen darauf zurück, auf
diese binäre Darstellung, weil das nämlich, oder insgesamt quasi die
Anzahl der Stellen, die ich habe, um eine Zahl abzuspeichern, für so
eine Zahl braucht der Rechner nämlich auch entsprechend viele
Speicherzellen sozusagen, entsprechend viele Bits.
Dann eine Rolle spielt, wenn wir verstehen wollen, was eigentlich
passiert, wenn ich größere Zahlen berechnen will, als meine
Wertebereiche erlauben.
Gut, bevor wir aber dahin kommen, zu den einzelnen Zahlen und was
genau die Wertebereiche sind und was man da auch beim Rechnen beachten
muss, kommen wir erst nochmal zu den Operatoren, was eben für alle
elementaren Datentypen an dieser Stelle gilt.
Es gibt in Java diese Menge von Operatoren, hier ein bisschen
gruppiert, es gibt arithmetische und Vergleichsoperatoren, das sind
Dinge, die Sie auch so aus der Mathematik kennen.
Man kann überhaupt Plus und Minus eine Variable davor setzen und quasi
damit aus einem Wert Minus 2 den Wert 2 machen und umgekehrt.
Wir haben die ganz normalen Operationen Multiplikation, Division und
Modular eben durch diese Zeichen Sternchen, Slash und Prozentzeichen
dargestellt.
Sie haben auch ganz bekannt und funktioniert auch wie typisch Addition
und Substraktion.
Sie haben Größenvergleiche und auch Prüfungen auf Gleichheit und
Ungleichheit.
Und bei den Operatoren muss man immer noch beachten, was als Ergebnis
von einem solchen Ausdruck herauskommt.
Bei den ersten 3 ist es so, dass das Ergebnis von einem Ausdruck, wie
er hier steht, auch wieder eine Zahl ist.
Macht ja Sinn, sowas wie x plus y ergibt eine Zahl, wenn x 5 ist und y
3, kommt da zum Beispiel 8 heraus.
Bei den anderen beiden hier unten, Größenvergleiche und Gleichheit,
ist das Ergebnis eines solchen Operators ein Wahrheitswert.
Also wenn da steht x kleiner y, wenn x dann 3 ist und y 5, würde
dieser Ausdruck, was habe ich gerade gesagt, true ergeben, also wahr.
Da würde ein wahr, ein Bolscher-Wert herauskommen.
Ebenso bei gleich, x gleich gleich y prüft auf Gleichheit, ob die
Werte x und y gleich sind.
Mit einem doppelten Gleichheitszeichen, das ist wichtig, und x
Ausrufezeichen gleich y prüft auf Ungleichheit.
Das Ausrufezeichen ist also wie so ein durchgestrichenes
Gleichheitszeichen hier zu interpretieren, was man ja in einem
Texteditor nicht zur Verfügung hat.
Dann gibt es, das sind eben diese arithmetischen und
Vergleichsoperatoren, die funktionieren auf Zahlen, also sowohl auf
den ganzen Zahlen als auch auf den Fließkomma-Zahlen.
Dann gibt es noch ein paar weitere Operatoren auf ganzen Zahlen.
Die haben etwas mit dieser Darstellung im Rechner als Nullen und
Einsen zu tun.
Hier nur kurz am Rande erwähnt, es gibt mehr Infos dazu im Anhang, die
Sie sich anschauen können.
Das sind hier diese Tilde x, das sogenannte bitweise Kompliment, wo in
der Binärdarstellung immer von 1 zu 0 getauscht wird sozusagen.
Also aus einer 1 wird eine 0 und aus einer 0 wird eine 1, über die
ganze Zahl hinweg.
Da kommt also dann quasi eine ganz andere Zahl dabei raus.
Und es gibt ähnlich Operatoren auf jeweils zwei Zahlen.
Es gibt das bitweise und, das bitweise entweder oder oder das und und,
das bitweise oder, die jeweils zwei Zahlen in ihrer
Binärrepräsentation nehmen, also als Nullen und Einsen, die sozusagen
übereinander schreiben und dann immer für die Stellen, die an der
gleichen Stelle stehen, halt ein und oder ein oder ein logisches
ausführen.
Wie das und und oder funktioniert, sehen wir gleich bei den
Boolchenwerten.
Und das wird dann quasi über die ganze Zahlenkette hinweg für jede
einzelne Zahl, deswegen bitweise ausgeführt.
Wie gesagt, mehr Infos dazu im Anhang.
Dann gibt es die Operatoren auf Wahrheitswerten.
Das sind dann eben so ähnlich nicht als Ausrufezeichen, um einen Wert
zu negieren.
Hier das doppelte Kaufmanns und als sogenannte sequentielles und und
das doppelte Pipe-Symbol hier, also dieser senkrechte Strich, als
sequentielles oder.
Man kann bei Wahrheitswerten genauso gut auch das bitweise und und
bitweise oder verwenden.
Das Spezielle an diesem doppelten und und doppelten oder ist die
sogenannte, das steht hier unten, Kurzschlussauswertung.
Wenn ich in meinem Programm zum Beispiel X oder Y so hingeschrieben
habe und der Rechner bei der Berechnung des Ganzen findet jetzt schon
raus, dass X wahr ist, dann wird Y gar nicht mehr angeguckt, weil wahr
oder irgendwas anderes wird als Ergebnis immer wahr ergeben.
Und dann kann man sich quasi diese Auswertung von Y, was ja auch was
anderes sein kann, an der Stelle noch sparen.
Und das nennt man Kurzschlussauswertung oder Short-Circuit Evaluation,
was bei und und und diesem, also bei diesem doppeltgeschriebenen und
und dem doppeltgeschriebenen oder, also dem sequentiellen und und dem
sequentiellen oder eben von Java gemacht wird.
Muss man wissen, weil sonst vielleicht der eine oder andere
überraschende Effekt eintreten könnte, wenn man das nicht weiß, wenn
man diese Operatoren benutzt.
Und dann gibt es auch noch Operatoren auf den ganzen Zahlen, wieder
welche, die auf dieser, sag ich mal, Bit-Repräsentation, also der
Binär -Repräsentation arbeiten, den sogenannten Links-Shift, Rechts
-Shift mit und ohne Vorzeichen, wo diese Einsen und Nullen-Codierung
der Zahlen um eins nach rechts oder links verschoben wird und dann
entsprechend auch eine andere Zahl dabei herauskommt, auch dazu dann
die Informationen im Anhang.
Aber damit haben Sie dann hier die komplette Liste der Operatoren, die
eben in Java möglich sind, die Sie so in Java hinschreiben können.
Einen Aspekt dieser Tabelle habe ich jetzt noch nicht erklärt, nämlich
hier die erste Spalte, die sogenannte Präzedenz.
Das ist die Angabe, die einfach in Java in der Sprache festgelegt ist,
welcher Operator stärker bindet.
Und zwar je kleiner der Wert hier ist, desto stärker bindet der
Operator.
Was das genau bedeutet, sehen wir auf der nächsten Folie nochmal.
Diese Präzedenzen der Operatoren vermeiden oder helfen, unnötige
Klammern zu vermeiden.
Wenn wir jetzt also einen Ausdruck hätten wie hier, das können Sie
jetzt mit Ihrem Wissen aus der Mathematik oder so einfach versuchen
ein bisschen zu interpretieren.
Hier steht x kleiner 2 mal y plus 1 und nicht, Klammer auf, x kleiner
gleich y Modulo, das ist jetzt der Modulooperator, z Klammer zu.
Und durch diese Präzedenz, wie Sie das auch in der Mathematik
sicherlich schon kennen, wird eben festgelegt, dass man nicht einfach
hier nur anfängt, das von links nach rechts auszuwerten, sondern es
wird festgelegt, wozu sagen implizit die Klammern sind.
Es ist zum Beispiel so, dass, das kann man jetzt eben in der
Präzedenztabelle der Operatoren nachgucken, dass das Mal, die
Multiplikation stärker bindet als die Addition.
Das heißt, hier wird erst 2 mal y gerechnet und auf das Ergebnis wird
plus 1 gerechnet.
Das ist jetzt auch dem wahrscheinlich, was Sie erwarten würden.
Dieses Ganze bindet aber stärker als der Vergleich zum Beispiel.
Das heißt, es wird erst 2 mal y plus 1 gerechnet und dann wird
verglichen, ob das Ergebnis größer oder nicht wie x ist.
Das wird dann zu einem Wahrheitswert ausgewertet.
Das und bindet von diesen Beispielen hier am wenigsten stark.
Das heißt, hier wird auch erstmal der gesamte andere Ausdruck
ausgewertet.
Auch hier das Modulo zuerst, dann der Vergleich, dann das und, weil
das ja explizit mit Klammern außen vorgeschrieben ist und am Schluss
quasi, wenn hier ein Wahrheitswert rausgekommen ist und hier ein
Wahrheitswert rausgekommen ist, dann wird erst mit und geguckt, ob die
beiden gleich sind.
Also wenn beide gleich sind, ist das Ergebnis wahr und wenn nicht,
falsch.
Also damit spart man sich einfach so eine Klammerschreibweise, sondern
kann sich ein bisschen auf diese Präzedenz der Operatoren an der
Stelle verlassen.
Nochmal, je kleiner der Wert ist, desto stärker bindet der Operator,
was vielleicht ein bisschen unintuitiv ist, aber in Java halt so
festgelegt.
Auch noch ein Hinweis, Leerzeichen spielen an dieser Stelle keine
Rolle, also ob ich jetzt hier ein Leerzeichen zwischensetze zwischen x
und kleiner oder nicht, das ist dem Compiler völlig egal.
Es gilt also nur diese Präzedenz und gegebenenfalls Klammern, die ich
explizit setze, da wird natürlich das in den Klammern zuerst
ausgewertet.
Soviel als Überblick zu den gesamten Datentypen, zu den Operatoren,
die es insgesamt so gibt.
Und auf den folgenden Folien schauen wir uns jetzt die einzelnen
Datentypen, über die wir gerade gesprochen haben, nochmal ein bisschen
genauer an.
Der erste wäre eben die Wahrheitswerte, Boolean.
Das ist eben ein Datentyp, der nur zwei verschiedene Werte kennt,
nämlich wahr und falsch.
In Java, auf Englisch dann geschrieben als true und das ist auch ein
Schlüsselwort dann in der Sprache, was Sie so hinschreiben können und
false.
Beim Arbeiten mit solchen Wahrheitswerten übersetzt man sich das auch
oder nutzt man auch Abkürzungen und nutzt die 1 auch als Abkürzung für
true, also für wahr und die 0 als Abkürzung für false, also falsch.
Das entspricht jetzt wieder diesem Strom an sozusagen oder Strom nicht
an, was im Computer dann auch verwendet wird.
Und auf diesen Wahrheitswerten kann ich eben auch einige Operatoren
anwenden, auch noch die jeweils in der einzelnen Schreibweise, aber
hier jetzt mal schon die Doppelte verwendet.
Diese Tabelle ist jetzt als Wahrheitstabelle so zu lesen, wenn ich
einen Wert 0 habe und noch einen Wert 0 in den beiden Variablen A und
B, kommen wir gleich nochmal genau zu, was das bedeutet, dann ergibt
so ein Ausdruck A und B auch den Wert 0.
Also so können Sie so eine Tabelle immer lesen.
Hier ist angegeben die verschiedenen Kombinationen, die der vordere
erste Operant und der zweite Operant annehmen können und hier steht
dann drin, was ist das Ergebnis eines und Operators bei der
entsprechenden Belegung der beiden Operanten.
Was ist das Ergebnis eines oder Operators, was ist das Ergebnis des
xor, also entweder oder Operators und was ist das Ergebnis des nicht
Operators, jetzt nur für A.
Wir können es ein bisschen anschauen, es ist also so, wie man es auch
alleine natürlich sprachlich erwarten würde, dieses Kaufmannsund
bedeutet und, das heißt das Ergebnis von A und B ist nur dann 1, wenn
beide 1 sind.
Also 1 und 1 und jetzt nicht als Addition gerechnet, sondern war und
war ergibt wieder war, war und falsch ergibt falsch, falsch und falsch
sowieso.
Oder ist genau umgekehrt, es wird nur zu falsch ausgewertet, wenn eben
beide Eingaben sozusagen, beide Operanten 0 sind, ansonsten wird es zu
1 ausgewertet.
Das entweder oder ist ein bisschen interessanter vielleicht, es wird
als 1 ausgewertet nur, wenn der eine Wert 0 und der andere 1 ist oder
andersrum.
Wenn beide Werte war sind, also 1 ergibt das entweder oder
entsprechend auch wieder 0, weil es sagt eben nicht nur oder, sondern
entweder oder.
Und die Negation ist eben hier die Umkehrung der ersten Spalte, aus 0
wird 1 und aus 1 wird 0.
Soviel auch schon zu den Wahrheitswerten, die sind noch relativ
einfach, gibt ja auch nur zwei verschiedene, True und False.
Hier auf der nächsten Folie oder auf den nächsten Folien kommen wir zu
den ganzen Zahlen, die sind ein wichtiger Datentyp, wir wollen ja
meistens mit dem Rechner irgendwas Komplizierteres berechnen, als nur
war und falsch.
Wie gesagt sind die Datentypen Byte, Short, Integer und Long die
Datentypen, die ganze Zahlen in Java repräsentieren können.
Und ich habe eine Menge von Operatoren und gleich auf der nächsten
Folie noch ein paar mehr, die ich auf diesen ganzen Zahlen oder die
ich verwenden kann, um mit diesen ganzen Zahlen zu rechnen.
Das sind zum einen diese arithmetischen Operatoren, Plus, Minus, Mal,
die so funktionieren, wie sie das aus der Mathematik kennen.
Auch Vergleichsoperatoren, außer dass man in Speicherüberläufe kommen
kann, kommen wir gleich zu, funktionieren so wie in der Mathematik.
Bei Division, also dem Slash hier und dem Modula, muss man dazu
wissen, dass das ganzzahlige Operationen sind.
Und das ist schon das erste, wo das hier in Java ein bisschen
unintuitiv wird.
Wenn ich in Java hinschreibe 17 durch 5, kommt da 3 raus.
Obwohl es ja eigentlich 3, irgendwas wäre, wenn wir das jetzt im
Matheunterricht gemacht hätten.
Aber in Java wird eben die Division ganzzahlig gemacht und der Rest,
wenn ich halt diesen Slash benutze für die Division, einfach fallen
gelassen.
Modulo kennen Sie wahrscheinlich auch genauso aus dem
Mathematikunterricht.
Wenn ich jetzt sage 17 Modulo 5, kommt da 2 raus, weil eben 17 durch 5
eben nicht ohne Rest geteilt werden kann, sondern der Rest, wenn ich
17 durch 5 rechne, wäre dann eben 2.
Weil 3 mal 5 nur 15 sind und ich dann entsprechend diese 2-Differenz
noch habe.
Genau, das sind die Operatoren.
Wie gesagt, insbesondere die Divisionsoperatoren, mit denen muss man
ein bisschen aufpassen.
Die anderen sind so, wie man es erwarten würde.
Was aber zusätzlich noch speziell ist zu bedenken bei den ganzen
Zahlen in Java und dann auch in allen Programmiersprachen, ist, dass
sie ja eben nur einen bestimmten Wertebereich haben.
Und dieser Wertebereich ergibt sich eben aus der Menge von Bits, die
ich verwende, um diese Zahlen abzuspeichern.
Also der Menge der Stellen in der Binärrepräsentation, die ich im
Speicher vorgesehen habe, um diese Zahlen abzuspeichern.
Und das Schwierige dann beim Verwenden ist, dass wenn ich jetzt zum
Beispiel mit dem kleinen Datentypen hier byte arbeite, aber auch bei
den großen kann das passieren, wenn ich zum Beispiel zwei Zahlen
addiere und das Ergebnis einer solchen Operation dann nicht im
Wertebereich mehr ist, dann werden einfach die höchstwertigen Bits
ignoriert und ich bekomme was völlig Unerwartetes heraus.
Zum Beispiel, wenn ich den Typ Integer habe, der eben diesen
Wertebereich hat von minus 2 Milliarden bis plus 2 Milliarden, und ich
rechne im Typ Integer 2 Milliarden plus 2 Milliarden, dann würde mir
der Rechner das Ergebnis minus 294 Millionen ausgeben.
Das liegt eben daran, dass in dieser Binärdarstellung des Wertes dann
einfach die Einsen und Nullen addiert werden.
Man bekommt immer einen Übertrag, aber der Übertrag sozusagen hinten
irgendwo runterfällt oder noch schlimmer, als negatives Vorzeichen
interpretiert wird und man dann ansprechend sogar eine negative Zahl
als Ergebnis herausbekommt.
Also man kann sich vorstellen, wenn man addiert und über den größeren
Wert hinausläuft, dann fängt man einfach hier unten wieder an und
rechnet dann wieder von unten sozusagen weiter.
Ähnlich ist es, wenn ich jetzt von dem kleinstmöglichen Integerwert,
also dem vom Betrag her größten negativen Wert, eins abziehe, also
hier aus dem Wertebereich herauslaufe, dann bekomme ich als Ergebnis
den größten möglichen Wert wieder heraus.
Das muss man einfach wissen, weil, also man muss dazu in Java eben
wissen, dass es dieses Problem gibt und dass es entsprechend sehr
wichtig ist, immer für das eigene Programm abzuschätzen, wie groß
werden die Zahlen, mit denen ich rechne und dann auch den
entsprechenden Datentyp dazu zu verwenden.
Also nicht den Datentyp bytes zu verwenden, wenn ich zum Beispiel mit
Zahlen um die Millionengröße rechnen möchte.
Kleiner Hinweis noch, weil das auch verwirrend sein kann, diese
arithmetischen Operationen funktionieren auf allen diesen Datentypen,
ist in Java jetzt aber so speziell, dass wenn ich zwei Bytewerte
addiere, wird das Ergebnis ein Integer sein, einfach womöglich, um
diese Überläufe dann schon einigermaßen zu vermeiden.
Das wird von Java automatisch so gemacht.
Gut, das waren die wichtigen oder auch typischen Operatoren, die wir
eben gesehen haben.
Es gibt noch zwei weitere relevante Operatoren, wichtige Operatoren
auf den ganzen Zahlen.
Das sind der Inkrement- und der Dekrement-Operator.
Und das sind Operationen, die einfach nur vor eine Variable
geschrieben werden und dann den Wert der Variable um 1 erhöhen oder um
1 verringern.
Also plus plus rechnet 1 drauf, ist eine Kurzschreibweise sozusagen,
um plus 1 zu sagen und minus minus verringert den Betrag um 1.
Diese Operatoren kann ich entweder vor eine Variable schreiben, das
nennt sich dann Präfixnotation, oder nach eine Variable, also ich kann
schreiben plus plus x und das bedeutet, der Wert, der in x
abgespeichert wird, wird um 1 erhöht.
Oder minus minus y zum Beispiel.
Präfix oder ich kann es auch als Postfix machen, ich habe x plus plus
oder y plus plus.
Das macht einen Unterschied, wenn man so ein Konstrukt mit Operator in
einem weiteren Ausdruck, wo zum Beispiel auf Gleichheit oder
Ungleichheit geprüft wird, verwendet.
Die Semantik ist sowas wie y gleich plus plus x, bedeutet man rechnet
erst x gleich x plus 1, also man erhöht erst x um 1.
Kommen wir gleich bei den Variablen nochmal zu, warum das so komisch
aussieht.
Und setzt danach den Wert von y gleich den Wert von x.
Und wenn ich es andersrum schreibe, y gleich x plus plus, habe ich
erst diese Zuweisung an die Variable y und dann die Inkrementierung
des Wertes von x.
Und analog das Ganze für minus.
Gut, soviel auch schon zu den ganzen Zahlen.
Wir haben es also gesehen, die haben einen bestimmten Wertebereich,
man muss aufpassen, dass man nicht oben drüber läuft oder unten
drunter rausläuft aus dem Wertebereich.
Und wir haben die entsprechenden Operatoren für diese Zahlen gesehen.
Was noch fehlt, sind zum einen die Zeichen und Zeichenketten und zum
anderen noch die Fließkommazahlen.
Das sind in Java wie gesagt die Datentypen float und double.
Das ist in Java eine Implementierung eines Standards, der auch in
anderen Programmiersprachen gilt.
IEEE 754 Standard, wenn Sie es mal nachgucken wollen.
Und hier geht es eben darum, Fließkommazahlen darzustellen, also eine
Teilmenge der reellen Zahlen darzustellen.
Man kann wie gesagt nicht alle reellen Zahlen darzustellen, weil das
wären ja unendlich viele und das passt in den Speicher sozusagen nicht
rein.
Sondern man hat jetzt in diesem IEEE Standard festgelegt, welche
Zahlen genau dargestellt werden können.
Also Teilmenge der reellen Zahlen.
Sie werden dargestellt, wie Sie es kennen, beziehungsweise nicht ganz
wie Sie es kennen, mit einem Dezimalpunkt.
Sie kennen Komma, aber hier wird, es ist ja eine amerikanische
Sprache, ein Punkt verwendet.
Oder Sie können auch die sogenannte wissenschaftliche Darstellung
verwenden.
So etwas wie 6,022e23 nehmen, das kennen Sie vom Taschenrechner, was
dann eben steht für 6,022 mal 10 hoch 23.
Also wir haben, wie gesagt, in diesen Fließkommazahlen eine Teilmenge
der reellen Zahlen.
Es sind eben nicht nur die ganzen Zahlen, sondern wir haben auch Teile
nach dem Komma.
Und jetzt kann man sich fragen, wie kriegt man das denn jetzt in diese
binäre Präsentation hinein.
Also der Rechner kennt nur 1 und 0, der kennt keine Kommazahlen erst
mal.
Also muss man auch Fließkommazahlen intern darstellen als...
Ach, danke schön.
Aber ich glaube, ich versuche es erst mal so.
Das war der Lutschbonbon hier an der Stelle.
Für den Hals.
Sehr gut.
Beim nächsten Mal probiere ich es aus.
Wie wir diese Kommazahlen hineinbekommen in die interne Darstellung
des Rechners.
Und das wird eben bei diesen Fließkommazahlen im IEEE-Standard
festgelegt.
Mit drei verschiedenen, sage ich mal, Arten von Bits gemacht.
Oder drei verschiedenen Feldern.
Eines um das Vorzeichen zu kodieren, soll es jetzt eine negative oder
eine positive Zahl sein.
Eins um den Exponenten zu kodieren und eins um die sogenannte Mantisse
zu kodieren.
Man geht immer von der Basis 2 aus und der Wert einer solchen
Gleitkommazahl ergibt sich durch diese Formel, dass man nämlich sagt,
man hat minus 1 hoch das Vorzeichen.
Also wenn das Vorzeichen Bit 1 ist, ist die Zahl negativ.
Man nimmt die Zahl, die in der Mantisse als ganze Zahl kodiert ist.
Und rechnet das Ganze mal 2 hoch die Zahl, die im Exponenten als ganze
Zahl kodiert ist.
Beispiel, wenn das Vorzeichen den Wert 1 hat, Exponent den Wert minus
1 hat und Mantisse den Wert 5 hat, dann ergibt sich minus 1 hoch 1 mal
5 mal 2 hoch minus 1.
Also der Wert, die Gleitkommazahl 2,5.
Der Rechner hat aber sozusagen nur ganze Zahlen verwendet, um diese
Zahl zu kodieren.
Jetzt geht es wieder, glaube ich.
Wir sehen auf der nächsten Folie nochmal ein bisschen genauer, wie das
aussieht.
Aber hier erstmal noch ein bisschen Dinge, die ich damit machen kann.
Es gibt bei Float und Doubles auch noch ein paar besondere Werte,
nämlich sozusagen Platzhalter, die als NaN, not in number,
interpretiert werden.
Als positive infinity, also positiv unendlich oder negativ unendlich,
negative infinity.
Und es gibt, ein bisschen witzigerweise, auch zwei verschiedene
Zahlen, plus 0 und minus 0, was ich aus dieser Kodierung, die wir
gleich nochmal sehen werden.
Mathematische Operationen, die ich mache und die als Ergebnis eine
Zahl haben, die nicht direkt dargestellt werden kann als
Fließkommazahl, weil ich ja eben nur eine Teilmenge der realen Zahlen
darstellen kann, werden dann gerundet auf eine passende drumherum.
Und ich habe in Java viele mathematische Operationen, viele Fließkomma
-Operationen auch schon vorgefertigt in der Klasse Math, was Sie auch
verwenden können, wie zum Beispiel den Betrag von etwas oder Quatsch,
den absolute, also die ganze Zahl von etwas, Sinus, Cosinus oder eine
Wurzel, Square Root.
Auch noch wichtig bei dem Umgang mit Fließkommazahlen, ist der Umgang
mit Gleichheit oder Ungleichheit.
Man sollte ja eigentlich meinen, man könnte so etwas hinschreiben wie
ist die Zahl X gleich die Zahl Y?
Das ist nur selten sinnvoll nutzbar, weil es eben leicht zu
Ungenauigkeiten beim Rechnen mit Gleitkommazahlen kommen kann.
Deswegen sollte man in seinem Programmcode bei Floats und Doubles
lieber nicht vergleichen, ist die eine Zahl gleich der anderen Zahl,
sondern lieber sagen, ist der Betrag von der Differenz dieser beiden
Zahlen kleiner als ein bestimmter, sehr kleiner Wert.
Also in der Mathematik würde man Epsilon sagen, in Java nimmt man halt
eine kleine Zahl und prüft, ob die Zahlen nah genug beieinander sind,
sodass sie wahrscheinlich als gleich gemeint waren und man vielleicht
nur ein paar Rundungsfehler dabei hatte.
Ebenso, wenn man es mit Ungleich prüfen möchte, guckt man lieber, ob
die Differenz groß genug ist zwischen zwei Zahlen, dann sind sie
wahrscheinlich als ungleich anzusehen.
Aber da muss man eben beim Rechnen mit solchen Werten sehr aufpassen
und das kann auch zu wirklich schwerwiegenden Fehlern führen, wenn man
viel rechnet mit diesen doch einigermaßen ungenauen Zahlen und davon
dann kritische Dinge abhängen, wie Trajektorien von Raketen oder
sowas.
Das sollte man lieber nicht machen.
Noch wie gesagt ein bisschen Detail zu den Gleitkommazahlen.
Das muss man beim Arbeiten mit diesen Zahlen nicht unbedingt wissen.
Was man eben wissen muss, ist die Konsequenz daraus, dass sich
Rundungsfehler einschleichen können und dass sich die Vergleiche, wie
ich sie eben gezeigt habe, eben so machen sollte über den Betrag und
nicht direkt mit gleich, gleich oder Ausrufezeichen gleich für
ungleich.
Aber hier eben nochmal ein bisschen Detail zu dem, was ich gerade
gesagt habe, Exponent, Vorzeichen und Mantisse.
Das sind eben diese Darstellungen der Gleitkommazahlen im Rechner, wie
sie von dem IEEE-Standard vorgegeben werden.
Einmal sogenannte Single Precision in 32 Bit kodiert, der Datentyp
float und Double Precision nutzt dann doppelt so viele Bits, um eine
Gleitkommazahl zu kodieren und deswegen heißt es eben auch Double.
Das kann man sich dann also merken, warum es also doppelt so viele
Bits wie ein normaler Float, eine normale Floating Point Number, eine
normale Gleitkommazahl.
So ein Float nutzt eben 1 Bit für das Vorzeichen, 8 Bit für den
Exponenten und 23 Bit für die Mantisse und Double ist ein bisschen
anders aufgeteilt.
1 Bit Vorzeichen, 11 Bit Exponent und entsprechend mehr für die
Mantisse.
Und das haben wir jetzt eben auch schon gesehen.
Ich kann eben jede Gleitkommazahl wie in der Formel vorhin schreiben.
Ich kann sagen, die Zahl ist Vorzeichen, Mantisse mal Basis, das ist
eben die Basis 2 fest bei binären Zahlen, hoch Exponent, bei binären
Zahlen immer Basis 2 und dann kann ich eben Kommazahlen durch diese
Kombination von ganzen Zahlen, die im Rechner nur gespeichert werden
können, abbilden.
Es ist jetzt in Java und in dem IEEE-Standard noch ein paar
Spezialitäten.
Details dazu gibt es im Anhang, dass ich nämlich, wenn Sie das jetzt
wirklich mal machen würden, in der Mantisse nur Werte von 1,0 bis echt
kleiner 2 speichere und deswegen die 1 eigentlich auch weglassen kann,
wenn ich ohnehin nur diesen kleinen Wertebereich anschaue und
eigentlich sozusagen erst ab dem Komma dahinter dann die Werte
abspeichere.
Also eigentlich eine ganze Zahl speichere, aber die wird dann
interpretiert als irgendwas, was hinter 1,0 rangehängt wird bei der
Berechnung.
Hier das Vorzeichen ist eben, wenn es 0 ist, ist es eine positive
Zahl, wenn es 1 ist, ist es eine negative Zahl und es gibt dann auch
noch die Spezialität, dass der Exponent immer positiv sein muss.
Also da das erste Bit nicht als Vorzeichen interpretiert wird, sondern
man immer 127 auf den Exponenten nochmal draufrechnet beziehungsweise
vom Exponenten abzieht, wenn man sowas interpretiert, das ist dafür
gut, dass man leichter vergleichen kann, ob eine Zahl groß oder klein
ist.
Also noch so ein paar Rechentricks sozusagen, um die Darstellung hier
auch gut und effizient im Rechner berechenbar zu machen.
Können Sie sich anschauen, wenn es interessiert ist zur Verwendung.
Jetzt nicht unbedingt wichtig, wie gesagt, was zur Verwendung wichtig
ist, ist die Ungenauigkeit, also sich über die Ungenauigkeit im Klaren
zu sein.
Die Nachteile davon, und da kommen wir jetzt nochmal zu der
Ungenauigkeit, weil es so wichtig ist, dass zum einen, nicht ganz so
entscheidend, es diese beiden Nullen gibt, Plus Null und Minus Null,
das ist einfach dieser Codierung geschuldet, man hat eigentlich
sozusagen eine Zahl damit verschwendet, die man im Wertebereich auch
noch für etwas anderes hätte nutzen können.
Aber problematischer eben als Nachteil von diesen Guide-Komma-Zahlen
sind die Rundungsfehler und das können Sie auch direkt selber mal in
Ihrem einfachen Programm, in unserem Simple Program ausprobieren.
Wenn man jetzt bei Floats, weil die ja ein bisschen weniger mögliche
Werte haben, einfach die Rechnung 1,05 als Float minus 0,05 als Float
rechnet, dann bekommt es Java, also der Rechner, schon nicht hin, das
ganz auszurechnen, sondern man bekommt als Ergebnis 0,999 und so
weiter 4 heraus.
Also selbst bei so kleinen Zahlen, also die sehen ja eigentlich noch
so aus, als könnte man das mal gebrauchen, wenn man vielleicht Euro
-Beträge berechnet oder sowas, kann es aber eben hier schon zu
Rundungsfehlern bei der Berechnung führen.
Das ist jetzt natürlich gerade als Beispiel rausgepickt, wo es schön
funktioniert, aber trotzdem selbst bei gar nicht zu großen Zahlen kann
man dieses Problem haben.
Anderes Beispiel ist, wenn ich eine entsprechende größere Floatzahl
plus 1 rechne, bekomme ich immer noch die gleiche Zahl heraus, weil es
einfach keine andere Darstellung jetzt intern gibt, um die, jetzt muss
ich eben hier Punkte zwischen machen müssen, das sind 100 Millionen,
um 100 Millionen und 1 auszudrücken in Float, deswegen wird es wieder
auf 100 Millionen gerechnet.
Genau, weitere Hintergründe dazu im Anhang.
Man kann sich das so darstellen und hier ist es jetzt ein bisschen
quasi mit noch weniger Werten dargestellt, jetzt nicht genau für Float
und Double.
Man kann es sich so darstellen, dass diese Gleitkommazahlen eben nur
eine Teilmenge der reellen Zahlen sind und auch, dass die Zahlen nicht
gleichmäßig dicht dargestellt sind.
Wenn wir jetzt also mal eine andere Art, Gleitkommazahlen zu kodieren,
hernehmen, also nur mit Exponenten von minus 3 bis 1, also für die
Bezahlung, die Matissenlänge habe ich jetzt hier leider nicht
hingeschrieben, aber relativ wenig, also kleiner als in Float, dann
kodiere ich damit Zahlen, sage ich mal relativ viele hier im Bereich
0, aber ich kann zum Beispiel mit dieser gewählten Repräsentation
schon 3,01 nicht darstellen, sondern es gibt hier nur 3 und dann noch
3 Werte zwischen 3 und 4.
Also es ist hier relativ dicht bei den kleinen Zahlen, 0,001 zum
Beispiel, aber je weiter ich an die großen Zahlen komme, desto gröber
ist mein Raster der darstellbaren Zahlen und einige dazwischen kann
ich dann eben nicht mehr darstellen, wie zum Beispiel hier die 100,001
im Float.
Das wäre dann eben auch so ein Bereich, wo die Zahlen entsprechend
dünn gestreut sind durch diese Kodierung, die wir gewählt haben.
So etwas wie Viertel, Achtel, Sechzehntel sind gut darstellbar, aber
bei anderen Brüchen bekomme ich dann entsprechend die Rundungsfehler.
Gut, kommen wir nun noch zu den Zeichen.
Ich hatte ja schon gesagt, Character oder kurz Car, wie man es
vielleicht sagen möchte, steht in Java für Character.
Das heißt Zeichen, so etwas wie A, auch die Ziffer 3 sozusagen kann
auch als Zeichen geschrieben werden oder so etwas wie Prozent.
Und ich kann in diesem Datentyp auch Escape-Sequenzen benutzen, um
Zeichen auszudrücken, die nicht Teil des Zeichensatzes meines Editors
sind.
Also wenn ich jetzt einen normalen Texteditor benutze, dann kann der
oft nur ASCII-Zeichen.
Das sind dann nicht so viele, also auch schon kein Ä oder Ö.
Und da kann ich mittlerweile eigentlich schon, je nachdem, welchen
Editor ich benutze, dann kann ich aber eben auch diese Escape
-Sequenzen verwenden, um auch Zeichen, die nicht in meinem Editor zur
Verfügung stehen, zu benutzen.
Zum Beispiel hier, Slash 007.
Oktal ausgedrückt wäre das das sogenannte Bell-Symbol.
Oder hier dieses Beispiel hexadezimal interpretiert 05D0, das wäre
dann ein Aleph, also ein hebräisches Zeichen, wenn ich mich richtig
sinne.
Genau, Operatoren, dadurch, dass diese Zeichen intern alle als Zahlen
nur kodiert werden, und das wird im Prinzip hier auch durch diese vier
Ziffern an der Stelle oder durch diese vier Zahlen dargestellt, kann
ich mit den Characters auch rechnen.
Also ich kann zum Beispiel sagen, der Character A rechne ich plus 1
und dann kommt das Ergebnis als Ergebnis 98 raus, wo ich jetzt
ableiten kann, dass die Kodierung für A intern eben 97 ist.
Oder ich kann auch sagen, ich multipliziere A mal B, bekomme dann 9500
raus.
Ob das für irgendwas praktisch ist, weiß ich nicht, aber man kann eben
diese normalen arithmetischen Operationen darauf auch anwenden.
Die Ergebnisse sind dann vom Typ Integer wie bei den kleinen ganzen
Zahlen und bei den Integers selber eben auch.
So ein einzelnes Zeichen ist meistens noch nicht besonders praktisch,
wenn wir programmieren wollen, also manchmal schon, aber oftmals
wollen wir ja ein bisschen mehr Text darstellen, und dafür gibt es
dann noch in Java den vorbereiteten Datentyp String.
Das ist dann kein elementarer Datentyp mehr und war deswegen in der
vorherigen Liste nicht drin, ist aber gleichzeitig einer, den wir so
häufig brauchen, dass ich ihn jetzt an der Stelle auch schon mal
gleich mit aufgeführt habe.
Und er ist ein bisschen ähnlich, weil er nämlich auch mit dem Plus
-Operator funktioniert.
Also String, hier jetzt auch groß geschrieben im Vergleich zu den
anderen, deswegen, weil es nicht mehr ein elementarer Datentyp ist,
sondern in Java eben durch eine Klasse realisiert wird, also ebenso
wie Klassen, die Sie selber schreiben, eben eine Klasse ist und kein
elementarer Datentyp für Zeichenketten, und das heißt auf Englisch
eben String.
Man kann zum Beispiel sowas hinschreiben, man kann sagen, ein String
Text, das ist jetzt eine Variable Text vom Typ String, soll den Wert
annehmen, doppeltes Anführungszeichen, das ist ein Text, und dann
wieder doppeltes Anführungszeichen.
Und das, was eben zwischen diesen beiden Anführungszeichen steht, das
ist die Zeichenkette, die quasi Teil dieses Datentyps Strings ist.
Ähnlich hier, String Greeting ist Hallo, ich kann also auch sowas wie
Sonderzeichen wie ein Ausrufezeichen benutzen, oder ich kann String
Address am Fasangarten 5, neue Zeile 761376131 Karlsruhe schreiben.
Ich kann also auch hier Zeichen escapen und zum Beispiel sowas wie
einen Zeilenumbruch alternativ mit Slash N darstellen.
Von den Operatoren, die wir bis jetzt gesehen haben, ist nur einer
direkt anwendbar, das ist wie gesagt der Plus-Operator, und der
ermöglicht mir die sogenannte Konkatenation, also Verkettung von zwei
Zeichenketten.
Ich kann also schreiben, eine Zeichenkette plus eine andere
Zeichenkette und bekomme dann quasi als Ergebnis eine lange
Zeichenkette, in der steht, dies ist eine Konkatenation.
Hier oben hatten wir Greeting und Address schon definiert.
Ich könnte jetzt also auch sagen, ich will eine neue Zeichenkette
Greeting Address haben, und die soll sich ergeben aus Greeting plus
Address.
Das fügt dann eben diese beiden Strings aneinander zu einem neuen
längeren String.
Wenn man das Plus benutzen möchte, muss man natürlich aufpassen, dass
es nicht innerhalb des Strings selber steht.
Also wenn ich sowas hier aufschreibe, dies ist plus keine
Konkatenation, dann ist das Plus einfach interpretiert als Teil der
Zeichenkette, als ein Zeichen unter vielen.
Nur wenn das Plus sozusagen außerhalb der Anführungszeichen steht,
dann wird es auch tatsächlich als Plus-Operator, als
Verkettungsoperator hier interpretiert.
Das ist der eine Operator, der geht.
Die anderen Operatoren der Tabelle auf Folie 12, das war diese Tabelle
mit den Operatoren, die funktionieren auf den Strings dann
entsprechend nicht.
Gut, dann haben wir noch einen weiteren Datentyp, der auch nicht
elementarer Datentyp ist, aber besonders von Java unterstützt wird,
nämlich der Enum.
Das ist ein Datentyp, also eine Möglichkeit, wie ich einen relativ
einfachen Datentyp auch selbst definieren kann.
Also eigentlich so eine Mischung aus vielleicht klasse und elementar
könnte man sagen.
Intern in Java ist es auch wieder eine Klasse, aber es ist so eine Art
und Weise, wie man relativ kurz wenige Werte oder mittelfiele Werte
selbst definieren kann.
So ein Enum ermöglicht es einfach, einen eigenen Datentyp zu
definieren mit benutzerdefinierten Werten.
Und ich gebe eine Liste der möglichen Werte dieses Typs explizit an.
Also zum Beispiel kann ich sagen, ich möchte ein Enum haben, Richtung
auf dem Kompass, also Kompass Direction, der die Werte haben kann,
North, East, South oder West.
Das ist eben so, Kompassrichtung, also zumindest die groben sind eine
dieser vier.
Und jetzt kann ich, anstatt das in ganze Zahlen zum Beispiel zu
kodieren und zu sagen, eins steht für Norden, zwei steht für Osten und
so weiter, kann ich besser ein Enum definieren, der wirklich auch
sagt, aha, der erste Wert hier bedeutet Norden, der zweite Osten und
so weiter.
Ähnlich könnte ich ein Enum Farbe definieren, wo ich sage, Rot, Grün
und Blau sind die drei verschiedenen Werte, die dieser jetzt selbst
definierte Datentyp Color annehmen kann.
Wie funktioniert das in Java?
Es ist analog zur Klassendeklaration.
Ich kann das Schlüsselwort Enum hinschreiben, mir einen Namen selber
ausdenken und dann in geschweiften Klammern mit Komma getrennt eine
Liste der Werte angeben und dann ist das jetzt mein neuer Datentyp,
den ich definiert habe.
Operationen funktionieren darauf erstmal nicht, also nicht out of the
box sozusagen, sondern ich muss als Benutzer, als Programmierer selbst
angeben, welche Operation ich gerne hätte.
Das ist dann analog zu Klassenmethoden, was wir uns dann später
nochmal anschauen werden.
Gut, soviel auch schon zu den Typen.
Jetzt eine kleine Denkaufgabe für Sie, um zu gucken, wie weit haben
Sie das nachvollzogen, wie weit haben Sie das vielleicht jetzt sogar
schon verinnerlicht.
Und zwar ein paar Übungsaufgaben.
Ich würde, vielleicht schon mal der Hinweis, die hinteren beiden sind,
sage ich mal, fortgeschritten und vielleicht auch ein bisschen Knobel-
oder auch Berateraufgabe regelrecht, aber die ersten drei müssten Sie
anhand der Dinge, die wir jetzt schon besprochen haben, auch für sich
lösen können.
Ich würde Ihnen ein paar Minuten Zeit geben, dass Sie zunächst einmal
selbst überlegen können und dann, wenn Sie zu einem Ergebnis gekommen
sind, auch gern mit Ihrem Nachbarn kurz absprechen können, was Sie
gegenseitig meinen, was da wohl jeweils die richtige Antwort ist.
Also hier A, B oder C oder hier Ja oder Nein.
Und dann besprechen wir das gleich in der großen Runde zusammen oder
ich bespreche es entsprechend.
So, dann gleich die erste Frage und ich würde Sie dann gleich bitten,
jeweils mit Handzeichen zu sagen, ob Sie A, B oder C meinen.
Die erste Frage ist, eigentlich zwei Unterfragen, welches Ergebnis
liefern die folgenden Berechnungen?
Das erste ist 1F, also ein Float, plus 2, F sage ich jetzt mal nicht
mit, also 1 plus 2 durch 3.
Wer meint, dass da 1 rauskommt?
Wer meint, dass 42 rauskommt?
Das sind diejenigen, die gerne per Anhalter durch die Galaxis lesen,
genau.
Wer meint, dass 1,66666, also quasi ein, zwei Drittel rauskommt?
Genau und die Mehrheit mag bestimmt auch per Anhalter durch die
Galaxis, aber noch lieber die richtige Antwort hier geben.
Es ist eben so, dass die Division hier stärker bindet als das Plus,
das heißt man rechnet erst 2 durch 3.
Es sind hier ja Floats, also kommt sozusagen ein Drittel raus, also
eigentlich 0,6667 und man rechnet dann plus 1.
Auf der anderen Seite noch ein bisschen ähnlich, hier steht 4 plus 2
durch 3.
Wer denkt, dass da 2 herauskommt?
Wer denkt, dass 4 rauskommt?
Und wer denkt, dass 1,66, also ein, zwei Drittel rauskommt?
Das ist auch richtig, da kommt 4 heraus, weil eben 2 durch 3 eine
Integer -Division ist und da dann erst mal 0 rauskommt, weil 2 durch 3
ist eben 0, Rest 2.
Okay, kommen wir hier zur zweiten Aufgabe.
Kann man mit a kleiner b kleiner c testen, ob diese drei Zahlen
geordnet sind?
Das ist auch nicht ganz so einfach, weil wir direkt über sowas noch
nicht gesprochen haben, also viele haben jetzt glaube ich weder noch
gesagt, was natürlich auch völlig okay ist.
Aber es stimmt, was diejenigen, die nein gesagt haben, gesagt haben,
weil man kann es zwar prinzipiell irgendwo hinschreiben, aber würde
trotzdem einen Compile-Fehler bekommen, weil a kleiner b eben ein
Vergleich ist und das Ergebnis dieses Vergleiches ist ein
Wahrheitswert.
Und damit, den könnte ich dann noch mit c vielleicht vergleichen oder
nicht, aber zumindest kann ich damit dann nicht rausfinden, ob diese
Zahlen geordnet sind, weil eben das Ergebnis von einem a kleiner b
entweder true oder false ergibt.
Und das dritte, kann ich mit den Operatoren kleiner und größer auch
Strings vergleichen?
Wer meint ja?
Genau, das ist richtig, hatte ich ja gesagt, für die Strings
funktioniert nur das Plus.
Für Characters, wenn ich das jetzt gefragt hätte, wäre es richtig
gewesen.
Characters sind einzelne Zahlen und die kann ich auch auf größer und
kleiner vergleichen, aber für Strings eben nicht, weil das ganze
Zeichenketten an der Stelle sind.
Kommen wir jetzt zu der vierten Aufgabe.
Was ist das Ergebnis von 1 durch 0 in Java?
Das ist jetzt was, worüber wir noch nicht gesprochen haben, aber Sie
können einfach mal überlegen, was könnte dabei rauskommen?
Wer tippt auf A?
Okay, wer sagt B und wer sagt C?
Im Prinzip könnte irgendwie auch alles sein, aber tatsächlich ist es
so in Java, dass das festgelegt wird, ist als Arithmetic Exception,
also wenn ich versuche, Integerwerten als durch 0 zu teilen, dann gibt
mir Java zur Laufzeit eine Fehlermeldung, das geht aber nicht.
Also durch 0 teilen ist ja eben etwas, was wir so nicht machen können.
Kommen wir zum zweiten, zum letzten Beispiel.
Was ist das Ergebnis von 1,0 durch 0,0 in Java?
Also wenn ich das ganze als Gleitkommazahlen darstelle, also als
Doubles hier in dem Beispiel, wer meint A, dass es eine Arithmetic
Exception auch gibt?
Wer meint B, 0,0?
Und wer meint C, Infinity?
Genau, ich glaube, es ist eine kleine Mehrheit für Infinity und das
ist auch richtig.
Das ist in Java eben, ich meine, bei den ganzen Zahlen habe ich ja gar
keinen Wert Infinity, bei den Doubles habe ich eben dieses positive
oder negative Infinity, was verwendet werden kann und da haben sich,
wurde das im Prinzip verwendet an der Stelle als Konvention, einfach
als Festlegung in Java, dass das Infinity ergibt, obwohl es jetzt
mathematisch nicht ganz so, nicht notwendigerweise sinnvoll ist, weil
eigentlich die Division durch 0 nicht geht.
Soviel zu dem Teil der Datentypen.
Was waren jetzt also die Lernziele?
Sie kennen diese acht elementaren Datentypen, können dadurch, dass Sie
sie kennen, auch entscheiden, was für ein Problem ein geeigneter
Datentyp ist.
Will ich eben eine Zeichenkette oder eine Zahl darstellen.
Und Sie kennen auch die mit den elementaren Datentypen verbundenen
Operationen und können diese anwenden plus Modulo, plus bei Strings
und so weiter.
Also dieser Bereich hier aus dem Advanced Organizer.
Das war aber für heute noch nicht alles und zeitmäßig gucken wir mal,
wie weit wir noch kommen.
Nochmal zusammenfassend, was wir jetzt also aus unserem einfachen
Programm kennengelernt haben, sind diese Datentypen Integer und
String.
Und natürlich auch nicht zu vergessen, die ganze Klasse Simple Program
ist ja auch ein Datentyp, den ich jetzt hier, indem ich diese Klasse
definiert habe, auch neu benutzerdefiniert habe.
Im folgenden werden wir uns die Variablen angucken, die sind noch
essentiell, um sozusagen richtig programmieren zu können.
Und das war was, was wir auf einer vorherigen Folie schon mal gesehen
haben.
Variablen, Typ, Varien, Namen und so weiter.
Aber was ist denn jetzt eigentlich so eine Variable?
Sie kennen das aus der Mathematik ja schon, dass man rechnen kann, A
ist 5 und B ist irgendwas und dann lösen Sie vielleicht ein
Gleichungssystem oder sowas.
Es ist ähnlich bei der Programmierung, aber nicht ganz genauso.
Eine Variable ist ein Platzhalter in der Berechnung für Werte, mit
denen eben der Computer, wenn er dann tatsächlich das Programm
ausführt, rechnen soll.
Und Platzhalter deswegen, dass ich zum Beispiel verschiedene
Eingabewerte reingeben kann in mein Programm, die dem Wert x zum
Beispiel oder einer Variable x zuweisen kann und ich dann eben ein
Programm hinschreiben kann, was mit verschiedenen Eingabewerten dann
rechnen kann, zum Beispiel addieren, was eigentlich noch relativ
straightforward ist.
Zum Beispiel kann ich sowas hinschreiben, eine Variable x plus y ist
das größere als 4, dann als Abfrage, was insgesamt einen Wahrheitswert
ergibt, aber eben abhängt davon, welche Werte zu diesem Zeitpunkt in
den Variablen x und y abgelegt sind.
Man kann sich das am besten vorstellen, wirklich als Gefäß, als
Speicherplatz und das ist ja tatsächlich im Rechner auch irgendwo
gespeichert, der einen Wert enthält, hier die 0, für das Beispiel,
dass x den Wert 0 hat, einen Namen hat, hier den Namen x und das ist
noch wichtig in Programmiersprachen, das kommt in der Mathematik nicht
vor, diese Gefäße haben auch immer einen Typ.
Also das Gefäß legt fest, hier das Beispiel Integer, dass in dieser
Variable, in diesem Gefäß nur Integer-Werte liegen dürfen, keine
Doubles und andere Dinge, sondern Integer, das ist also typesicher,
sagt man dann auch.
Man muss dann, um so ein Gefäß zu erzeugen, eine Variablen-Deklaration
machen, bevor man eine Variable verwendet.
Man muss also erstmal in Java festlegen, was ist der Name und was ist
der Typ meiner neuen Variable.
Das funktioniert so, dass man zum Beispiel schreibt float a, oder
Integer x, man könnte jetzt schreiben Integer x, oder man kann
schreiben Integer x, y, z, um gleich drei Variablen auf einmal zu
deklarieren.
Und damit erzeuge ich eben hier erstmal bei dem a ein neues Gefäß vom
Datentyp float und hier erzeuge ich drei neue Gefäße namens, das erste
heißt x, das zweite y und das dritte z, jeweils vom Typ Integer, die
also nur Integer-Werte sozusagen aufnehmen können.
Und das bedeutet dann eben, dass auch vom Rechner tatsächlich dann so
ein Speicherplatz reserviert wird für diese Variable, ein solches
Gefäß sozusagen reserviert wird.
Nur das Reservieren, nur das Deklarieren bringt uns natürlich noch
irgendwie gar nichts, da können wir nichts mit machen, sondern wir
brauchen auch das nächste, das sind Zuweisungen.
Ich möchte mit diesen Gefäßen, mit den Variablen ja auch rechnen
können.
Und das ist jetzt gerade der wichtige Schritt, der auch nicht ganz
einfach notwendigerweise zu verstehen ist, wenn man noch nicht
programmiert hat.
Variablen können nämlich ihren Wert ändern, also ich kann durch
Zuweisung, so nennt sich das, den Zustand eines Objektes zum Beispiel
verändern oder auch einfach, wenn ich eine Integer-Variable halt habe,
den Wert, der in dieser Speicherzelle stehen soll, ändern.
Und das schreibt man in Java wie folgt, Zuweisung hat das Schema Name,
also Variablen-Name gleich Wert und dieses Gleich ist eben der
Zuweisungsoperator.
Es ist ein bisschen gefährlich, den Gleich zu nennen, weil es nämlich
gerade kein Gleichheitszeichen ist, also nicht prüft, ob das eine
gleich dem anderen ist, sondern eben eine Zuweisung von dem, was links
steht, auf das, was rechts steht.
Und wenn man so eine Zuweisung ausführt, wird dann der neue Wert, der
eigentlich auf der rechten Seite steht, in der Variablen gespeichert
und der alte wird überschrieben.
Wenn ich also vorher, in meinem vorherigen Programmzustand, x den Wert
0 hat, die Integer-Variable x den Wert 0 hat und ich führe die
Anweisung, x soll zugewiesen bekommen, den Wert 4295 aus, dann hat
danach, nach der Ausführung dieser Anweisung oder Zuweisung, x eben
den Wert 4295.
Hier noch ein paar mehr Beispiele für solche Zuweisungen, also ganz im
Allgemeinen nochmal.
x-Zuweisungsoperator y ändert den Wert der Variablen x und setzt ihn
auf den Wert der Variablen y.
Und die Reihenfolge ist eben hier wichtig.
Die linke Variable bekommt den Wert, der auf der rechten Seite steht.
Ein anderes Beispiel, ich kann schreiben x-Zuweisungsoperator x plus
1.
Und das würde jetzt ja mathematisch überhaupt gar keinen Sinn machen,
wenn man sich sowas anschauen würde, weil das kann ja nicht als
mathematische Gleichheit interpretiert kann das so nicht sein.
Aber das ist eben keine mathematische Gleichheit, sondern der
Zuweisungsoperator.
Das heißt, hier steht, der alte Wert von x wird um 1 erhöht und wird
jetzt neu, wieder als neuer Wert von x in die Speicherzelle
geschrieben.
Also man nimmt den bisherigen Wert von x, addiert 1 und setzt die
Variable x auf den neuen, 1 größeren Wert.
Hier nochmal ein Beispielablauf, wenn ich also diese Kette von
Anweisungen hätte, die ich nacheinander ausführe und ich nehme an,
initial haben meine Variablen die Werte x gleich 10 und y 6.
Wenn ich dann diese Anweisung ausführe, x bekommt die Zuweisung x plus
1, so sag ich es jetzt mal kompliziert, dann hat danach x den Wert 11.
Ich habe also 10 plus 1 gerechnet, y bleibt unverändert.
Wenn ich schreibe, x bekommt den Wert zugewiesen von y, dann ist
danach der Wert x auch 6, weil y vorher 6 war.
Und wenn ich dann hinschreibe, also als nächste Anweisung ausführe, y
bekommt den Wert zugewiesen von 2 mal x plus 1, dann bekommt eben y
einen neuen Wert und zwar 2 mal 6, das wäre 12 plus 1 sind 13.
Also von oben nach unten abgearbeitet und das auf keinen Fall als
lineares Gleichungssystem verstehen, sondern einfach nacheinander sind
das Anweisungen, was der Rechner ausführen soll und das ist eben keine
Gleichheit, sondern eine Zuweisung.
Das nochmal auf den Folien auch, es ist keine Gleichheit, sondern eine
Zuweisung.
Interessant zu sehen, dass in anderen Programmiersprachen hier nicht
dieses Gleichheitszeichen verwendet wird, sondern wenn man das in
anderen Programmiersprachen wie Simula oder Eiffel explizit macht,
dass man Doppelpunkt gleich schreibt.
Und das ist eigentlich auch viel besser, bloß dass es halt ein Tipper
mehr sozusagen ist und deswegen hat man sich in Java wahrscheinlich
für die kürzere Schreibweise entschieden, die aber am Anfang
verwirrend ist.
Und auch noch entsprechend Konsequenz davon, das Ganze ist keine
symmetrische Operation, x Zuweisungsoperator y ändert den Wert von x,
y Zuweisungsoperator x ändert den Wert von y.
Also es wird jeweils der Wert, der sich hier ergibt, sei es jetzt
einfach aus der Variable oder durch einen komplizierteren Ausdruck, in
die Variable, die auf der linken Seite steht, sozusagen
hineingeschrieben.
Und wenn Sie sich die Variable eben als Behälter vorstellen,
Behältnis, ist es ein bisschen einfacher, sich das vorzustellen.
Man kann jetzt entweder sagen, man macht eine Deklaration einer
Variable und danach eine Zuweisung.
Es gibt in Java auch eine Kurzschreibweise dafür, das nennt man
Initialisierung.
Eine Kombination aus Deklaration und Zuweisung, das man direkt
schreibt, zum Beispiel float a, also eine neue Variable vom Typ a,
soll gleich am Anfang den Wert 2,5 bekommen.
Dann hat man sich eine Zeile oder ein bisschen Schreibaufwand gespart.
Das ist das entsprechende Schema dafür.
Und jetzt hatte ich eigentlich noch zum einen eine zerbrochene
Animation, aber auch noch eine kleine Übungsaufgabe für Sie.
Aber dadurch, dass wir es jetzt nur noch 10 Minuten haben, ist es
leider ein bisschen knapp dafür.
Es ist also etwas, was ich Sie einladen würde, sich das zu Hause zu
überlegen, was entsprechend hier die Ausgaben sind.
Und dann können wir mal gucken, ob wir das am Anfang beim nächsten Mal
nochmal kurz besprechen.
Oder Sie sich vielleicht auch in Ihren Lerngruppen, die Sie
hoffentlich schon gefunden haben, für sich besprechen können.
Oder vielleicht noch besser in dem kleinen Simple Program mal
ausprobieren, was denn tatsächlich rauskommt.
Also erst überlegen, was rauskommt und dann in Ihrem eigenen Programm
ausprobieren, was rauskommt.
Das können Sie entsprechend aus den Folien da hinein kopieren und
ausprobieren.
Gut, was nämlich auch noch ein wichtiger Teil ist, wo wir die letzten
10 Minuten noch darüber sprechen müssen, ist, wie Zuweisungen und
Variablen im Zusammenhang mit Objekten funktionieren.
Wir haben jetzt gerade nur Variablen gesehen, die als Typ einen
elementaren Datentyp hatten.
Aber jetzt schauen wir uns mal eben an, wie Variablen aussehen oder
wie die zu verwenden sind, wenn ich Objekte als sozusagen Werte habe,
mit denen ich arbeiten will.
Da gibt es zwei verschiedene Aspekte sozusagen.
Das eine ist erstmal, dass wir Variablen auch schon gesehen haben als
Attribute in Klassen.
Attribute, eben letzte Vorlesung, sind eigentlich auch nur Variablen,
die auf Klassenebene deklariert sind.
Also wir hatten hier in unserem Vektor 2D die Variable x oder das
Attribut x, das Attribut y und x und y sind eben auch Variablen,
funktionieren genauso, wie wir das gerade besprochen haben.
Wenn ich jetzt also ein Beispiel habe, ich erzeuge ein neues Objekt p
mit new Vektor 2D, dann kann ich mit p.x auf das Attribut, also die
Variable x in diesem Vektor zugreifen und zum Beispiel den Wert 4
zuweisen.
Das ist also eine Zuweisung, wie wir es gerade besprochen haben, an
dieses Attribut x im Objekt p.
p.x ist die sogenannte Dereferenzierung an der Stelle.
Oder ein anderes Beispiel, ich mache einen neuen Vektor 2D q und setze
x auf 1,2 mal 10 hoch 5 und das y von q hier auf minus 0,36.
Das wäre dann, wenn ich diese Dinge hier ausführe, wäre also das hier
der Ergebniszustand, den ich in meinem Rechner, in meinem Programm
dann habe.
Das ist der Fall, wie ich über Objektvariablen auf Attribute, die eben
auch nur Variablen sind, zugreife.
Noch interessanter und auch ein bisschen komplizierter wird es, wenn
ich mir jetzt diese Variable hier mal anschaue.
Das ist ein Vektor 2D p, das ist also eine Variable vom Typ Vektor 2D,
vom Typ einer Klasse.
Und da ist es ein bisschen anders, als wie mit diesem einfachen Bild
des Speichers, sondern wie folgt.
Ich muss dazu wissen, dass im Speicher oder mein Hauptspeicher, also
das, wo mein Programm zwischendurch Werte ablegt, aus einer Reihung
von Speicherplätzen aufgebaut ist.
Und diese Speicherplätze haben Adressen, mit denen mein Programm
tatsächlich auf Maschinencode-Ebene auf diese Speicherplätze zugreifen
kann.
Also das sieht zum Beispiel so aus, das sind sozusagen die Gefäße, die
wir eben hatten, alle so aufgereiht.
Hier an Speicherstelle Nummer 1 liegt ein Integer mit dem Wert 0, an
Speicherstelle Nummer 2 ein weiterer Integer mit dem Wert 3, den ich
als Variable X bezeichnet habe und so weiter.
Also wichtig ist, diese Speicherplätze sind durchnummeriert, das sind
die Adressen dieser Speicher.
Das ist, Anmerkung, eigentlich ein bisschen komplizierter, aber für
unsere Zwecke an dieser Stelle jetzt ausreichend.
Können Sie sich dann noch anschauen, wenn Sie es weiter interessiert.
Wie werden jetzt Objekte im Speicher abgelegt?
Das ist jetzt der tricky part sozusagen.
Objekte werden irgendwo im Speicher, sage ich jetzt mal, abgelegt und
zwar so, dass die Attribute meines Objektes an aufeinanderfolgenden
Speicheradressen abgelegt wird.
Also wenn ich jetzt dieses Vector2D Objekt habe, was nur die beiden
Attribute X und Y hat, dann habe ich irgendwo im Speicher so ein Paar
aus X und Y und das hier ist sozusagen mein Objekt.
Das ist meine Objektidentität, dieses Paar X und Y im Speicher.
Objektvariablen, wie zum Beispiel Position oder wir hatten eben
einfach nur P, sind dann Referenzen auf diese Objektidentität.
Das heißt, in dieser Variable, anders als wir das jetzt hier mit dem
Behälter hatten, wird jetzt nicht irgendwie das ganze Objekt
reingepackt oder so, sondern in der Variable steht nur die Adresse, wo
das Objekt zu finden ist, eine sogenannte Referenz drin.
Also in der Variable steht jetzt in diesem Fall der Wert 17, weil die
Objektidentität eben an Speicherstelle 17 liegt.
Genauer gesagt, die Adresse des ersten Attributs, aber gut, okay.
Hier also, wenn ich einen Vektor2D-Position erzeuge, enthält die
Variable Position eigentlich nur die Adresse, die zeigt auf die Stelle
im Speicher, wo die Objektidentität, wo das Objekt liegt.
Hier nochmal ein Beispiel, wie das auch mit Zuweisungen dann
funktioniert.
Betrachten wir die folgende Situation, wir haben neue Objektvariable
vom Typ Vektor2D, das steht da jetzt nicht, müsste eigentlich hier
noch davor stehen.
Wir erzeugen einen neuen Vektor2D, das heißt, obwohl das ist
eigentlich das Ergebnis von aller, wir erzeugen ein neues Vektor2D
-Objekt.
Wir weisen danach dem Attribut X den Wert 2 zu und dem Attribut Y den
Wert 3,5.
Dann führt das dazu, dass nach der Ausführung dieser drei Anweisungen
im Speicher das so aussieht, irgendwo liegt dieses Objekt mit den
Attributen XY und ich habe weiterhin diese Variable P, die eben auf
die Adresse 17 zeigt und damit auch das Objekt zeigt oder
referenziert, kann man auch sagen.
Wenn ich dann die Zweitzeile ausführe, also erstmal den Anfang, neues
Objekt nochmal Q mit den X 1,2 und Y 9,4, dann wird hier ein neues
Objekt angelegt und Q, diese Variable Q, enthält eben nur die Referenz
auf Speicherstelle 20.
Wenn ich dann auch noch R gleich P ausführe als Anweisung, bekomme ich
eine neue Variable, auch vom Typ Vektor2D und die bekommt einfach den
Wert von P. Also die bekommt dann auch die Adresse 17, ohne dass ich
irgendwie das Objekt nochmal kopiere oder so.
Also es enthalten dann einfach beide Variablen eine Referenz auf das
gleiche Objekt.
Wenn ich jetzt nochmal die Zuweisung P, bekommt zugewiesen den Wert
von Q, mache, führt das dazu, dass einfach nur hier der Variablen P,
die neue Adresse 20, zugewiesen wird.
P und Q jetzt also auf das gleiche Objekt hier hinten zeigen und die
Objektvariable P eben nicht mehr dieses Objekt hier referenziert,
sondern das andere.
Also die Identität dieser Objekte bleibt gleich, aber die
Objektvariable zeigt nicht mehr auf das gleiche Objekt wie vorher.
Was hier also wichtig ist zu wissen, ich kann mehrere Referenzen
haben, die auf das gleiche Objekt oder die gleiche Objektidentität
verweisen.
Und die Objektidentität von dem Objekt, was wir zuerst angelegt haben,
was ursprünglich von P referenziert wurde, wird durch diese Zuweisung
überhaupt nicht verändert.
Und das kann ein bisschen unerwartet sein, wenn man es eben nicht
kennt, von daher muss man das gut verstanden haben, auch ein bisschen
ausprobieren, um gut in Java arbeiten zu können.
Das Ganze noch mal ein bisschen alternativ dargestellt, wenn wir jetzt
mal von dieser Reihung im Speicher ein bisschen abstrahieren.
Das gleiche jetzt also nochmal, ich habe hier meine Variablen P, Q und
R, die zeigen jeweils auf eine Adresse im Speicher, so wie wir es
gerade initialisiert haben.
Also P zeigt auf dieses erste Objekt, Q auf das zweite Objekt und R
wieder auf das erste Objekt.
Wenn ich dann in Java die Zuweisung bei diesem Zustand hier, die
Zuweisung P soll zugewiesen bekommen, den Wert von Q ausführe, dann
wird sozusagen nur dieser Zeiger hier eben nur die Adresse verändert
und nicht der Inhalt der Objekte selber.
Anders sieht es jetzt aus, wenn ich jetzt nochmal zurück auf den alten
Zustand, die Anweisung oder Zuweisung P Punkt X soll zugewiesen
bekommen, den Wert 7,5 mache.
Dann wird nämlich hier dereferenziert durch den Punkt, also ich gehe
erst auf P und dann durch das Punkt X gehe ich hinüber in die
Objektidentität und ändere diesen Wert X von 2,0 auf 7,0.
Und das gilt dann auch für R Punkt X.
Also danach, wenn ich jetzt irgendwo hinschreiben würde R Punkt X,
würde ich auch das Ergebnis 7,5 bekommen, weil R und P eben auf das
gleiche identische Objekt zeigen und die Änderung sich da entsprechend
auch auswirkt.
Aliasing nennt sich das, wenn ich eben mehrere Referenzen auf das
gleiche Objekt zeigen lasse.
Gut, noch zwei kurze Dinge in zwei Minuten, mal gucken.
Was aber noch wichtig ist zu wissen, Objektvariablen können auch den
speziellen Wert 0 annehmen, zum Beispiel kann ich sowas hier
hinschreiben, Vector2DP ist 0 sozusagen, soll zugewiesen bekommen, den
Wert 0.
Das heißt dann, dass in dieser Objektvariable eben kein Objekt
referenziert wird, sondern eben diese spezielle Adresse 0, die
sozusagen bedeutet keins.
Die Objektidentität wird eben nicht durch diesen Teil hier erzeugt,
sondern Objektidentität, ein neues Objekt wird immer nur erzeugt durch
den Aufruf oder des New Operators, auch sogenannter Konstruktor, wo
wir dann glaube ich beim nächsten oder übernächsten Mal dazu kommen.
Also das hier erzeugt ein Objekt, das hier oben erzeugt kein Objekt.
Schließlich eine der wenigen letzten Folien, wir haben jetzt über
Variablen gesprochen und der Sinn bei Variablen ist, dass sie sich
verändern können.
Man kann in Java aber auch Konstanten definieren, indem man vor eine
Variable noch das Schlüsselwort Final setzt und damit sagt, dass
dieses hier, Data, nicht verändert werden soll im weiteren
Programmverlauf, sondern eigentlich fest immer den Wert Hello World
enthalten soll.
Das macht insbesondere auch Sinn, wenn man in Klassen solche
Konstanten wie P festmachen will, das soll ja nicht nachher noch
geändert werden.
Hier entsprechend das Schema, können Sie sich anschauen.
Noch eine Sache muss ich sagen, zu der Überprüfung der Datentypen.
Ich hatte eben schon gesagt, von daher ist es vielleicht auch schon
klar, dass diese Speicherzellen, also diese Speicherbehälter immer
auch einen Typ haben.
Und das ist in Java eben anders als zum Beispiel in Python speziell,
dass alle Variablen einen Datentyp haben müssen.
Der Datentyp muss zur Compile-Zeit bekannt sein und wird auch
überprüft beim Kompilieren.
Also Sie können Kompilierfehler bekommen, wenn Sie falsche Datentypen
sozusagen verwendet haben und auch welche Operatoren dann möglich
sind, weiß schon der Compiler.
Hier können Sie was nachlesen und die Zusammenfassung, gut, hatte ich
am Anfang schon gesagt, lasse ich jetzt vielleicht an dieser Stelle
weg.
Genau, dann sehen wir uns nächste Woche mit dem nächsten Thema, wo es
dann hier weitergeht.