Guten Tag, meine Damen und Herren.
Jetzt darf ich die jetzt noch Gekommenen bitten, Platz zu nehmen.
Gut, heute zweite Vorlesung Programmieren.
Wir sind noch dabei, uns mit den Grundbegriffen der Programmierung zu
beschäftigen.
Heute Thema nochmal Typen und Variablen.
Wir hatten in der letzten Vorlesung ja mit zwei anderen sehr
grundlegenden Begriffen der objektorientierten Programmierung
angefangen.
Das sind Objekte gewesen und Klassen.
Und ich möchte jetzt zum Einstieg nochmal kurz als Wiederholung
bitten, sich nochmal zu überlegen, was denn ein Objekt war und was
eine Klasse war.
Also was sind so die typischen Dinge?
Ja, es ist noch etwas unruhig.
Ich würde diejenigen, die jetzt erst kommen, bitten, möglichst schnell
Platz zu nehmen, damit sie ein bisschen mehr Ruhe reinbekommen.
Gut, dann darf ich Sie nochmal fragen, was ist ein Objekt?
Oder was sind so die typischen Eigenschaften eines Objekts?
Was sind so die Charakteristika, die ein Objekt ausmachen?
Sie dürfen sich gar nicht melden per Handzeichen.
Sie vorne sind zu halb dabei, schon zu stricken.
Was würden Sie sagen, was macht ein Objekt aus?
Ja,
das habe ich jetzt akustisch noch nicht verstanden.
Sagen Sie es bitte nochmal.
Ja doch, das ist gut, genau.
Also Felder und Methoden war die Antwort.
Also was sind Felder?
Die heißen auch Attribute.
Wozu dienen die?
Ja, wenn ich vielleicht Sie mal fragen darf, vorne mit den Brillen.
Wozu man Felder verwendet, weiß jemand anders?
Also Felder, oder wie gesagt, ich glaube der Begriff, den wir
verwendet hatten, war Attribut.
Also wir hatten, denken Sie nochmal dran an das Beispiel mit dem
Billiardspiel, Billiardtisch, Kugeln.
Was waren denn Attribute, die wir zum Beispiel für den Billiardtisch
verwendet hatten?
Ja,
ist es noch etwas zu laut oder zu leise im Vergleich dazu?
Sagen Sie es bitte nochmal.
Länge und Breite des Billiardtisches zum Beispiel.
Also das waren Eigenschaften der Objekte, mit denen wir in unserem
Programm arbeiten wollen.
Also die Idee der objektorientierten Programmierung ist ja, dass wir
eine Programmrepräsentation von Gegenständen der realen Welt haben,
also zum Beispiel von einem Billiardtisch, und in den Attributen
werden dann Kenngrößen zu diesen Objekten, die wir eben in unserem
Programm benötigen, abgelegt.
Also es sind solche Speicherstellen oder die Möglichkeit,
Informationen über unsere Objekte abzulegen.
In Attributen passiert das.
Sie hat noch eine zweite Sache genannt, Methoden.
Was sind Methoden oder wozu dienen Methoden?
Um Felder zum Beispiel zu verändern, ja das wäre eine Möglichkeit,
aber man kann das noch allgemeiner fassen.
Um mit anderen Objekten zu kommunizieren, genau, auch eine
Möglichkeit.
Sie nochmal.
Genau, Sie sagen, die beschreiben das Verhalten der Objekte.
Genau, und das ist wahrscheinlich das Allgemeinste von den drei
Sachen, die wir jetzt gehört haben.
Also in Methoden, da steckt praktisch dann Ihr Programmcode drin, also
das Eigentliche, was Sie programmieren, wie das Verhalten von den
Objekten sein soll, also wie werden bestimmte Dinge berechnet.
Sie wollen zum Beispiel eben von diesen Billiardkugeln berechnen, wie
die sich auf dem Tisch bewegen, und dann wird das Verhalten, was
passiert zum Beispiel in einem Simulationsschritt, wie wird dann die
neue Position der Kugel berechnet, diese Berechnung, die findet dann
in den Methoden statt.
Also Methoden, Funktionalität und den Zustand des Objekts, oder die
aktuellen Eigenschaften, die werden in den Attributen oder Felder,
hatten wir auch gehört als Begriff, werden die festgehalten.
Okay, also Objekte dienen dazu, Dinge aus der realen Welt auf dem
Computer darzustellen, und was ist dann eine Klasse?
Ja, ich glaube, Sie wissen es genau, nehmen wir vielleicht jemand
Neues, weil Sie hatten auch schon was gesagt.
Ja, genau, Sie haben gesagt, eine allgemeine Beschreibung eines
Objektes, eine Vorlage, haben Sie gesagt, genau.
Also, ja, hatten wir auch gesagt, ein Bauplan für Objekte.
Also, wenn wir ähnliche Objekte haben, die sich irgendwie gleich
verhalten, gleiche Eigenschaften haben, die werden dann mit so einer
Klassenbeschreibung zusammengefasst.
Also, wenn wir mehrere Objekte auf unserem Billardtisch haben, dann
ist es in so einem Fall sinnvoll, eben eine Klasse für so einen
Billardball, eine Billardkugel anzulegen, in der man dann eben diese
ganzen verschiedenen Bälle, die im Spieljagdspiel auftauchen,
modellieren kann.
Also das, was Sie programmieren, was Sie hinschreiben, ist eine
Klasse.
Und wie erzeugen Sie jetzt aus so einer Klasse ein Objekt?
Das hatten wir auch letztes Mal schon.
Da gibt es ein Java-Schlüsselwort.
Das ist relativ einfach.
Wie erzeugen Sie ein neues Objekt für eine Klasse?
Ja, Sie bitte.
Schlüsselwort ist new, genau.
Also Sie schreiben pin, zum Beispiel, p gleich new table.
Und dann wird eben so ein neues Table oder Tischobjekt angelegt und
das bekommt den Namen p und über den Namen p können Sie dann darauf
zugreifen auf dieses Objekt und auf diesem Objekt Methoden aufrufen
oder Attribute verändern.
Gut, also soweit zur Wiederholung nochmal zu Objekten und Klassen.
Wir hatten uns dann noch diese beiden anderen Begriffe, Typen und
Variablen, angeschaut.
Und vielleicht auch hier nochmal kurz eine Wiederholung.
Also zu Variablen hatten wir letztes Mal eigentlich noch gar nichts
gemacht.
Typen hatten wir schon ein bisschen was.
Was würden Sie sagen, was ist so das Charakteristische von einem Typ
jetzt?
Oder was wird durch einen Typ festgelegt?
Genau, welche Klasse einem Objekt angehört.
Also wenn Sie jetzt wieder denken, Sie haben zum Beispiel
Billardkugeln und den Tisch und Sie haben jetzt eine Variable oder ein
Objekt p und um eben zu sagen, welche Art von Gegenstand dieses p
repräsentiert, ob es jetzt ein Ball ist oder ein Tisch oder irgendwas
ganz anderes, dazu dient der Typ.
Und wir hatten dann auch noch primitive Datentypen festgestellt.
Also die eine Möglichkeit ist Klassen, dass der Typ die Klasse anzeigt
von einem Objekt und dann gibt die zweite Möglichkeit ist, dass ein
primitiver Typ aussagt, wozu so eine Variable gehört oder welche Werte
in der Variable aufgenommen werden können.
Und bei den primitiven Datentypen, da hatten wir zum Beispiel
ganzzahlige Datentypen oder Wahrheitswerte, Booleans, True, False,
Variablen für einzelne Zeichen, für Charakter.
Also Sie legen über den Typ fest, welche Werte in der Variable
gespeichert werden können, wenn man es allgemein sagen möchte.
Also entweder, wenn Sie eine Ganzzahl haben, dann legen Sie fest, das
ist in einem bestimmten Zahlenbereich, also für Integer, dann eben in
so einem Bereich, der 2 hoch 32 Werte umfasst.
Oder wenn Sie ein Objekt oder eine Klasse als Typ angeben, dann sagen
Sie eben damit, dass in dieser Variablen solche Objekte repräsentiert
werden.
Okay, und an der Stelle machen wir jetzt weiter mit der Vorlesung.
Also hier die Datentypen hatten wir uns jetzt mal angeschaut, die
primitiven, die haben wir hier nochmal dastehen.
Dann hatten wir die Präzedenzen und Operatoren.
Das heißt, jeder Datentyp ist auch mit Operatoren verknüpft, wie Sie
eben auf diesem Datum berechnen können.
Also auf Ganzzahlen macht es zum Beispiel Sinn, solche Dinge wie
arithmetische und Vergleichsoperatoren zu haben.
Das ist vielleicht auf Objekten jetzt nicht so sinnvoll, wenn man da
diese Operatoren hat.
Also zu jedem Typ gehören gleichzeitig immer noch Operatoren und die
sind mit so einer Präzedenz verbunden, die eben praktisch die
Klammerungsregeln beschreiben.
Gut, und hier an der Stelle geht es jetzt weiter mit neuem Stoff.
Was wir uns als erstes hier anschauen, ist einer dieser primitiven
Datentypen, den es in Java gibt.
Und das ist eigentlich der einfachste, weil der nur zwei Werte
umfasst.
Also in diesem Datentyp Boolean für Wahrheitswerte können nur zwei
Werte dargestellt werden.
Und das ist eben einmal wahr, einmal falsch.
Die werden in Java mit True und False gekennzeichnet.
Und ich habe sie hier jetzt mal, weil es einfach kürzer ist, mit 0 und
1 bezeichnet.
Wobei jetzt eben 0 für False stehen soll und 1 für True.
In Java, wenn Sie programmieren, müssen Sie aber immer True und False
verwenden.
Also da können Sie nicht die 0 und die 1 verwenden.
Und auf diesem Datentyp der Wahrheitswerte haben wir jetzt Operationen
definiert.
Hier zum Beispiel diese logische Unverknüpfung oder die logische Oder
-Verknüpfung.
Hier das logische XOR oder die Negation.
Schauen wir uns mal an, wie die arbeiten.
Also die Tabelle steht so spaltenweise zu lesen.
Also wenn A falsch ist und B falsch ist, dann ist A und B auch falsch.
Wenig überraschend.
Das ist praktisch so die Standarddefinition der Aussagenlogik.
Und damit A und B wahr wird, dass wir hier eine 1 stehen haben, muss
sowohl A als auch B wahr sein.
Okay, das sehen wir hier.
Also nur wenn A wahr ist, B wahr ist, dann ist auch A und B wahr.
In allen anderen Fällen ist A und B falsch.
Dann schauen wir uns das Oder an.
Bei dem Oder ist es so, dass wir hier, wenn A falsch ist und B falsch
ist, dann ist auch A oder B falsch.
Auch nicht so überraschend.
Wenn B wahr ist und A falsch ist, ist es wahr, genauso wie bei
vertauschten Fällen.
Was vielleicht jetzt ein bisschen interessanter ist, ist noch dieser
Fall hier, wenn wir beide Eingaben 1 haben.
Wenn A 1 ist und B 1 ist, also beide wahr, dann ist dieses Oder auch
wahr.
Und das ist vielleicht, oder das ist ja einfach eine Konvention, dass
man in der Butschon-Logik den Oder-Operator so definiert, dass der
eben auch in dem Fall, wenn beides wahr ist, auch eine 1
zurückliefert.
Also das entspricht nicht immer so genau dem umgangssprachlichen
Verwenden, der Verwendung von Oder.
Also wenn Sie sagen, es regnet oder die Sonne scheint, dann sind das
häufig so ausschließende Dinge, die Sie damit verknüpfen und dass Sie
da nicht beides gleichzeitig wahrhaben wollen.
Also das wäre das ausschließende Oder, was man in der Umgangssprache
häufig verwendet, aber hier ist es dieses einschließende Oder, wo
eben, wenn beide Dinge wahr sind, auch das Ergebnis von dem Oder wahr
ist.
Also das ist sowas wie ein Und-Oder-Oder.
Also das sind beide Fälle abgedeckt.
Das ist jetzt dann im Gegensatz zu dem exklusiven Oder, das hat ein
bisschen ein komisches Zeichen, das ist einfach dieses Hochzeichen,
was verwendet wird für das exklusive Oder.
Und das entspricht jetzt eben ein bisschen eher dem
umgangssprachlichen Oder, wo jetzt eben wenn beide wahr sind, das
exklusive Oder falsch wird.
Also das ist wirklich sowas, genau eines von diesen beiden muss wahr
sein, damit das exklusive Oder wahr wird.
Also ausschließendes Oder.
Und Negation ist eigentlich wieder klar, also dann müssen wir uns nur
diese erste Spalte hier betrachten, da wird einfach eben der
Wahrheitswert umgedreht.
Also aus 1 wird 0, also aus wahr wird falsch und aus falsch wird wahr.
Okay, also das ist der Datentyp oder der primitive Typ Boolean, der
nur diese beiden Werte kennt hier.
Relativ einfach, aber der wird in Vergleichen relativ häufig
verwendet, wenn sich ihr Programm eben je nachdem, wie ihre
Eingabewerte sind, unterschiedlich verhalten soll.
Dann müssen sie da so einen Vergleich reinprogrammieren und da können
eben dann auch solche logischen Verknüpfungen drin auftreten.
Gut, jetzt kommen wir zum nächsten Datentyp, der schon ein bisschen
interessanter ist.
Also jetzt haben wir bei Boolean nur zwei Werte, die gespeichert
werden können in so einer Boolean-Variable oder die dieser Typ
umfasst.
Bei den ganzen Zahlen, da braucht man natürlich mehr Werte.
Und weil der Speicherplatz auf dem Computer begrenzt ist für so einen
ganzzahligen Datentyp, ist es auch so, dass man bei diesem Typ angeben
muss, wie groß die Ganzzahlen sein sollen, die man eben in diesem Typ
behandeln kann.
Also da gibt es verschiedene Größen, byte ist das kleinste, das ist
ein 8-Bit-Datentyp, short 16-Bit, int 32 und long ist eine 64-Bit
-Darstellung, also da passen dann die größten Zahlen rein.
Und im Normalfall überlegen Sie sich, wenn Sie Ihr Programm schreiben,
was ist der beste Datentyp, was reicht für die Werte, die ich
darstellen möchte.
Im Normalfall nimmt man eigentlich häufig int, wenn man keine
zusätzlichen Informationen hat und weil das auch der Standard-Datentyp
der Maschine ist.
Also Ihr Rechner, der arbeitet praktisch im Normalfall mit Integern
und kann darauf auch sehr schnell arbeiten.
Auf diesen kleineren Datentypen wie byte und short ist er gar nicht
schneller und deshalb verwendet man eigentlich meistens Integer
direkt.
Okay, also das sind ganzzahlige Datentypen, in denen Sie so etwas
Ähnliches für die ganzen Zahlen, wie Sie es aus der Mathematik kennen,
aber eben beschränkt auf einen bestimmten Zahlenbereich in Ihrem
Programm verwenden können.
Und auf diesen Zahl-Datentypen haben Sie auch die üblichen
Operationen, die Sie von der Mathematik her vermuten würden.
Also Sie haben Addition, Subtraktion, Multiplikation und
Vergleichsoperatoren.
Also kleiner, kleiner, gleich, größer, größer, gleich.
Gleich und ungleich gibt es auch noch.
Was vielleicht ein bisschen interessanter ist, sind dieser
Divisionsoperator und dieser Prozentoperator.
Wissen Sie noch, was der Prozentoperator macht?
Wissen Sie noch?
Ja, hier oben.
Richtig, genau Modulo-Operator berechnet den Rest in der Division.
Das sieht man auch hier unten.
Also wenn wir 17 durch 5 teilen, dann kommt hier, eben weil es eine
ganzzahlige Division ist, kommt hier keine Kommazahl raus, sondern es
kommt eine Ganzzahl raus.
Also 17 durch 5 ergibt dann eben, wie oft passt es rein?
Drei Mal.
Also wird praktisch abgerundet, das Ergebnis hier auf drei.
Und hier dieser Modulo-Operator liefert dann eben den Rest.
Also wenn Sie 17 durch 5 teilen, wieder ganzzahlig, dann haben wir ja
schon gesehen, ist das 3 und der Rest bei der Division ist 2.
So weit ist das eigentlich relativ naheliegend.
Was man sich vielleicht noch überlegen könnte, ist, was passiert im
negativen Fall?
Also was würde rauskommen, wenn Sie hier minus 17 durch 5 teilen?
Können Sie mal entweder raten oder Sie wissen, was rauskommt?
Also minus 17 durch 5...
Wird nichts angezeigt, ist Ihre Vermutung, wenn Sie minus 17 durch 5
teilen?
Also ein Mathematiker würde sagen, ja ich rechne es halt aus, minus 17
durch 5, da kommt dann irgendwas raus mit minus 3, irgendwas.
Aber Sie sagen, der Computer bringt einen Fehler, was vermuten Sie,
was noch rauskommen könnte?
Also im Prinzip kann man es ja schon ausrechnen, minus 17 durch 5.
Das Problem ist eben bloß, wie hier bei den positiven Zahlen, dass man
irgendwie runden muss.
Ja, was wäre Ihre Vermutung?
Minus 3, sagen Sie?
Ja, wäre eine Möglichkeit.
Minus 4 haben wir hier noch.
Genau, also jetzt haben wir drei Möglichkeiten, minus 3, minus 4 oder
gar nichts.
Und das stimmt, er rechnet wirklich hier was aus.
Und die Frage, ob das Ergebnis jetzt minus 3 oder minus 4 ist, das
hängt vom Runden ab.
Also rundet der bei negativen Zahlen auch zur Null hin, also rundet er
da auf praktisch, oder rundet er ab bei negativen Zahlen.
Und es ist so, dass die eigentlich auf allen mir bekannten Computern
so ist, dass bei negativen Zahlen abgerundet wird.
Das heißt, da wird jetzt nicht zur Null hin gerundet, sondern es wird
zu einer kleineren negativen Zahl gerundet.
Das heißt, das Ergebnis wäre hier, wenn man minus 17 durch 5 teilt,
wäre minus 4.
Wenn Sie mal ausprobieren, genau.
Das ist mehr oder weniger Konvention.
Das entspricht zum Teil nicht ganz dem, was in der Mathematik üblich
ist, aber die Rundung zur...
Oh, ich glaube, ich sage es falsch.
Oder weiß es jemand gerade?
Ich bin mir gerade nicht mehr ganz sicher.
Es kann auch sein, dass er zur Null immer rundet.
Er schneidet ab, genau.
Das heißt, dann kommt hier doch als exaktes Ergebnis von minus 17
durch 5 würde minus 3, irgendwas rauskommen.
Und abgeschnitten wäre es dann einfach minus 3.
Genau, Sie haben recht.
In der Mathematik würde man eher minus 4 möglicherweise definieren,
damit der Rest positiv bleibt.
Aber hier ist es so, dass minus 3, danke für den Hinweis, rauskommt.
Das hat dann eben unter anderem auch die Konsequenz, dass wenn Sie
dann minus 17 modulo 5 rechnen, dass dann auch ein negativer Rest
rauskommen kann.
Also wenn Sie minus 17 durch 5 teilen, wäre dann hier der Rest
negativ.
Der wäre dann hier minus 2.
Okay, also das sind vielleicht so ein bisschen so Randfälle, die Sie
da mit berücksichtigen müssen, wenn Sie mit negativen Zahlen zu tun
haben.
Aber der häufigere Fall, denke ich, ist, dass man mit positiven Zahlen
hier sowieso zu tun hat.
Gut, aber ich denke, das Wichtige an dieser Stelle ist, dass eben hier
ganzzahlig gerechnet wird.
Und wenn Sie einfach hinschreiben 17 durch 5, dann bekommen Sie eben
nicht den exakten Wert 3, irgendwas, sondern Sie bekommen einfach den
gerundeten Wert.
Gut, ein anderes Problem gibt es auch noch.
Dadurch, dass wir eben jetzt nicht mit den mathematischen Zahlen
rechnen, wenn ganzzahlen, die eben unbeschränkt groß oder unbeschränkt
klein im negativen Bereich werden können, weil wir eben hier nur einen
festgegebenen Zahlenbereich haben, also zum Beispiel von minus 32.000
bis 32.000 und so ein paar zerquetschte bei short.
Also was passieren kann, ist, dass das Ergebnis Ihrer Operation nicht
mehr eben darstellbar ist in dem Zahlentyp.
Also wenn Sie mit Integern rechnen, dann war ja die Grenze, wie groß
die dargestellt werden können, so ein bisschen überhalb von 2
Milliarden.
Wenn Sie jetzt hier rechnen, 2 Milliarden plus 2 Milliarden, dann ist
eben das wirkliche Ergebnis 4 Milliarden zu groß, um noch dargestellt
werden zu können in dem Datentyp Integer.
Also das überschreitet diese Grenze, die eben in dem Zahlenbereich in
dem Typ vorhanden ist.
Und dann kommen eben etwas unerwartete Ergebnisse raus, wie zum
Beispiel hier dieses minus 294 Millionen.
Aber gut, das ist jetzt natürlich keine Zufasszahl, die der Computer
Ihnen hier ausgibt, sondern was er macht, ist, dass er intern ja im
Binärsystem rechnet und er hat da einfach eben eine bestimmte Anzahl
von Bits, die für diese Operation zur Verfügung stehen.
Und hier wäre es jetzt so, dass eben diese 32 Bit von dem Integer
-Datentyp nicht ausreichen für die Addition, da bräuchte man noch ein
33.
Bit dazu.
Und dieses 33.
Bit wird einfach abgeschnitten und fällt weg.
Und die verbleibenden 32 Bit, die werden dann wieder interpretiert,
einfach als eine Darstellung von einer Zahl.
Und da kommen dann eben solche Ergebnisse raus, wie hier.
Also Sie können das, wenn Sie einen Taschenrechner haben oder ein
Programm auf Ihrem Computer, was die Binärdarstellung Ihnen anzeigen
kann von diesen Zahlen, das auch mal ausrechnen.
Und dann sehen Sie, dass Sie hier einfach dieses oberste Bit
abgeschnitten haben, beim Ergebnis.
Und hier nochmal ein ähnliches Beispiel, also dieser Wert hier, diese
zwei Milliarden irgendwas, das ist der kleinste darstellbare Wert in
dem Integer-Datentyp.
Also da gibt es keine negativ noch kleineren Werte, die in dem Typ
gespeichert werden können.
Und was passiert dann, wenn Sie eins abziehen, dann sind Sie auch
wieder aus dem darstellbaren Bereich rausgelaufen, auf der unteren
Seite praktisch.
Und was dann auch wieder passiert ist, dass dann eben dieses
zusätzliche Bit, was Sie noch benötigen würden, um den Wert
darzustellen, einfach abgeschnitten wird.
Und Sie kommen dann praktisch an der anderen Seite wieder raus, bei
dem größten positiv darstellbaren Wert.
Also das sagt man im Englischen auch manchmal dazu, das ist so ein
wraparound behavior, oder so ein Umlaufverhalten, also wenn Sie oben
aus Ihrem Datentyp rauslaufen, indem Sie eins dazuzählen, dann kommen
Sie ganz unten wieder in Ihren Datentyp rein.
Und genauso, wenn Sie eben jetzt nach unten aus Ihrem Zahlentyp
rauslaufen, indem Sie vom kleinsten Wert wie hier zum Beispiel eins
abziehen, dann kommen Sie wieder ganz oben bei der größten
darstellbaren Zahl in Ihren Datentyp rein.
Also das ist dieses wraparound Verhalten, was Sie hier haben.
Das ist auch etwas, was in Java so definiert ist im Sprachstandard,
also das heißt, dieses Verhalten, dass diese Ergebnisse hier
rauskommen, ist in Java eindeutig so definiert.
Also da kann nichts anderes rauskommen an der Stelle.
Das ist in anderen Programmiersprachen wie zum Beispiel C nicht der
Fall.
Da wäre sowas wie hier undefiniert.
Und in so einem undefinierten Fall darf dann Ihr C-Compiler in dem
Fall praktisch machen, was er will.
Also er kann dann beliebige Ergebnisse hier zurückgeben, weil das ein
undefiniertes Verhalten ist.
Und das ist manchmal nicht ganz so schön, weil sich dann die Programme
eben zum Teil unerwartet verhalten können.
Aber der Vorteil ist, warum man das auch in C macht, ist, dass es den
Compilern erlaubt, weitere Optimierungen vorzunehmen und schnelleren
Code zu generieren.
Das ist vielleicht jetzt nicht ganz so ersichtlich, woran das liegt,
aber das ist so, dass die heutigen stark optimierenden Compiler sehr
stark auch solche undefinierten Effekte verwenden können.
Aber wie gesagt, in Java gibt es das nicht.
Da haben Sie den Vorteil, dass sich Ihr Programm auch in solchen
Randfällen wie beim Überlauf ein bisschen vorhersagbarer verhält und
wirklich immer genau so ein Ergebnis wie hier rauskommt.
Über- und Unterläufe müssen Sie beachten.
Das sind auch so typische Fehlerquellen, wo Ihr Programm, wenn es eben
sehr große Eingaben bekommt, auch sich fehlerhaft verhalten kann, weil
Sie zum Beispiel an so einen Über- oder Unterlauf nicht gedacht haben.
In sicherheitskritischer Software ist es deshalb so, dass man solche
Operationen dann auch praktisch von Hand mit solchen zusätzlichen
Prüfungen versieht, ob ein Überlauf eingetreten ist oder nicht oder ob
der eintritt bei einer bestimmten Operation.
Und die Fälle werden dann abgefangen speziell.
Gut, also Über- und Unterlauf ist ein gewisses Problem bei
ganzzahligen Datentypen.
Und es gibt noch eine zweite Sache, die manchmal ein bisschen
überraschend ist.
Und zwar ist es so, dass intern der Computer eigentlich, habe ich
vorhin schon kurz angeredet, eigentlich fast immer mit dem Datentyp
Integer rechnet.
Also mit diesen 32-Bit-Zahlenwerten.
Und das hat dann auch die Konsequenz, wenn Sie jetzt zum Beispiel eine
Variable von dem Typ Byte haben, die ja nur 8-Bit darstellen kann,
also nur Zahlenwerte von minus 128 bis 127, dann wird mit den Werten
intern nicht wie mit Bytes gerechnet, sondern wie mit diesem größeren
Datentyp Integer.
Und bei dem Short, dem 16-Bit-Datentyp, ist es auch so, dass mit dem
auch intern wie Integer gerechnet wird.
Das heißt, wenn Sie jetzt zwei Byte-Werte addieren, dann ist das
Ergebnis kein Byte-Wert wieder, sondern es ist ein Integer-Wert.
Und das ist vielleicht, also einerseits ist es wichtig zu wissen, wenn
Sie eben dann den Rückgabewert wieder als Byte verwenden wollen, dann
bekommen Sie einen Fehler von Ihrem Compiler schon, dass der
Rückgabewert eben nicht Byte ist, sondern Integer.
Und der andere Effekt ist, der taucht im Zusammenhang mit diesem Üben-
und Unterlauf auf.
Das heißt, Sie haben bei den Datentypen Byte und Short manchmal keine
Überläufe, obwohl Sie es vermuten würden.
Also überlegen wir uns mal an einem Beispiel kurz noch.
Also wir haben gesagt, Byte geht von minus 128 bis 127.
Und dann schauen wir uns mal an, was wird passieren, wenn wir jetzt so
einen analogen Fall wie hier uns anschauen.
Also wir würden sagen, wir rechnen minus 128, und das wäre ein Byte
-Typ, minus 1.
Was ist dann das Verhalten, wenn Sie da auf einen Byte-Typ minus 128,
minus 1 rechnen?
Man könnte vermuten, dass es dann eben auch wieder so einen Unterlauf
gibt, weil dieses minus 128, minus 1 sich eben nicht mehr darstellen
lässt in einem Byte, und dass Sie dann 127 als Ergebnis bekommen.
Das ist aber nicht der Fall, weil intern werden Ihre Byte-Werte
erstmal in Integer-Werte übersetzt, dann wird mit Integer gerechnet,
und das Ergebnis ist auch wieder Integer.
Das heißt, wenn Sie hier minus 128 haben in Ihrer Byte-Variable, dann
wird die erst nach Integer konvertiert, dann haben Sie wieder genug
Platz, um da minus 1 von abzuziehen.
Das heißt, das Ergebnis wäre dann minus 129, und es tritt kein
Überlauf an der Stelle auf.
Wichtige Sachen hier nochmal.
Also hier Division, Modulo, Ganzzahlig, Über- und Unterlauf beachten.
Und bei diesen kleineren, ganzzahligen Datentypen, die werden immer
nach Integer konvertiert.
Also auch das beachten.
Bei Long ist das nicht der Fall.
Bei Long ist es so, dass das Ergebnis von einer Long-Addition oder
Multiplikation auch wieder Long ist.
Das wäre ein gewisser Nachteil, oder man könnte sonst mit diesen Long
-Datentypen nicht so sinnvoll arbeiten, weil eben diese ganze große
Bitbreite, die man zur Verfügung hat, wird dann bei jeder Operation,
Addition, Multiplikation keinen besonderen Wert haben, wenn das
Ergebnis wieder in den kleineren Wertebereich reinpassen müsste.
Also bei Long ist das Ergebnis auch Long, nur bei den kleineren
Datentypen ist das Ergebnis Int.
Und habe ich vorhin auch schon gesagt, das hat dann häufig die
Konsequenz, dass man eigentlich selten Byte- und Short-Datentypen
sieht in Programmen, sondern dass man meistens Integer direkt
verwendet, es sei denn, es kommt wirklich auf Speicherplatz an.
Also wenn Sie sehr große Mengen von Integern in Ihrem Programm
verwenden, dann kann es wirklich sinnvoll sein, um Speicherplatz zu
sparen, dass Sie dann doch solche Byte-Werte nur verwenden.
Gut, also was haben wir jetzt an Datentypen gehabt?
Jetzt haben wir diese Wahrheitswerte, wir haben die ganzzahligen
Datentypen.
Was kann man sonst noch so brauchen?
Was sind so die Dinge, mit denen man sonst noch so rechnet?
Also ganzzahlig ist vielleicht noch ein bisschen wenig, manchmal
möchte man auch mit irgendwelchen Kommazahlen arbeiten.
Das ist eine Folie, die habe ich noch relativ kurzfristig reingemacht
für das Aufgabenblatt, das Sie gerade bearbeiten.
Ich wollte da noch zwei zusätzliche Daten, zwei Operatoren Ihnen noch
zeigen, über die Sie sich vielleicht schon auf dem Aufgabenblatt
gewundert haben.
Es gibt noch diesen Plus-Plus-Operator und den Minus-Minus-Operator.
Und dieser erste hier nennt sich auch Inkrement-Operator, der zweite
Minus -Minus-Dekrement-Operator.
Und die hört sich auf einen relativ einfach an, was die machen.
Die erniedrigen oder erhöhen eine ganzzahlige Variable um eins.
Also Plus-Plus erhöht die Variable um eins, Minus-Minus verringert die
Variable um eins.
Jetzt gibt es aber noch eine Besonderheit bei diesen Operatoren, und
zwar kann man die in zwei Schreibweisen verwenden.
Man kann die entweder als Präfix-Operator verwenden oder als Postfix
-Operator.
Und was bedeutet Präfix und Postfix in dem Zusammenhang?
Oder Sie kennen es vielleicht schon.
Wissen Sie, was Präfix-Notation und Postfix-Notation von Operatoren
ist?
Genau, als Präfix-Operator schreibt man vor das Objekt, auf das man es
anwendet, und Postfix-Operator danach.
Aus der Mathematik kennen Sie von Funktionen, die Präfix-Schreibweise
eigentlich mit Klammern, also da schreiben Sie Hinhalt f von x und
Klammern, das heißt das f wird da vorgeschrieben, der Operator.
Sie kennen auch die Postfix-Notation.
Haben Sie da was im Kopf, wo Sie Postfix-Notation in der Mathematik
schon mal gesehen haben?
Also wo Sie den Operator dahinter schreiben, der irgendwas mit ihrer,
in dem Fall auch eine Ganzzahl macht.
Hat jemand eine Idee für einen Postfix-Operator?
Ja, hier oben.
Genau, Fakultätsfunktion, also dieses Ausrufezeichen, was Sie dahinter
schreiben, 5 Fakultät, schreiben Sie als 5 Ausrufezeichen, also für
diese Multiplikation, 5 mal 4 mal 3 mal 2 mal 1.
Und das wäre ein Beispiel für eine Postfix-Notation, aber ist
ansonsten nicht so gebräuchlich.
Okay, aber das gibt es auch in der Mathematik beides.
Also auch hier, plus plus und minus minus kann man in beiden Varianten
verwenden.
Das heißt, Sie können sowas hinschreiben, plus plus x oder minus minus
y, das wäre dann die Präfix-Notation.
Oder Sie können es dahinter schreiben, x plus plus, y minus minus, das
wäre dann die Postfix-Notation.
Und jetzt kann man sich auch vielleicht noch fragen, ja, wozu braucht
man denn diese beiden Varianten?
In beiden Fällen wird ja das x hier zum Beispiel um 1 erhöht und hier
auch um 1 erhöht.
Aber Sie haben ein bisschen einen unterschiedlichen Effekt, eine
unterschiedliche Semantik.
Und die hängt damit zusammen, in welcher Reihenfolge einerseits der
Wert bestimmt wird und diese Erhöhung um 1 vorgenommen wird.
Also schauen wir uns das mal hier an.
Also wenn Sie sowas hinschreiben, y gleich plus plus x in Ihrem
Programm.
Also da greife ich schon ein bisschen vor, aber ich denke, das kann
man auch so verstehen, das kommt noch später mehr in der Vorlesung.
y gleich plus plus x, das entspricht dem, dass dieser Plusplus
-Operator, weil er eben auch davor steht, als erstes ausgeführt werden
soll.
Also als erstes erhöhen Sie die Variable um 1.
Das wäre dieses hier, das wäre so eine andere Notation dafür, x um 1
erhöhen, x einzeln zuzählen, wieder x zuweisen.
Und dann der Wert, den das y bekommt, ist dann dieser erhöhte Wert.
Also erst einzeln zuzählen, dann zuweisen.
Wenn Sie diese untere Notation verwenden, diese Postfix-Notation, dann
wird das Erhöhen des Wertes nachgemacht, also nach der Zuweisung.
Sie weisen erst y, den Wert von x zu und erhöhen dann x um 1.
Also der Unterschied hier ist die Reihenfolge, wann eben diese
Erhöhung oder Erniedrigung durchgeführt wird.
Vor der Zuweisung oder nach der Zuweisung.
Bei Präfix-Notation findet es zuerst statt, bei Postfix-Notation erst
danach die Erhöhung.
Und das gleiche gilt auch für den Dekrement-Operator, also für das
Minusminus, also auch da dieselbe Semantik mit der vorgeschriebenen
und der nachgeschriebenen.
Also soweit der Einwurf, der für das Verständnis vom aktuellen
Übungsblatt noch ein bisschen hilft.
Gut, und ich hatte ja auch schon gesagt, man möchte nicht immer nur
mit Ganzzahlen rechnen.
Sie haben ja, gerade wenn Sie auf Simulation, Physik zum Beispiel dran
denken, haben Sie eben sehr viele Werte, die nicht ganzzahlig sind.
Also die Beschleunigung durch die Erdanziehung, 9,81 ungefähr, das
wäre eben so eine Zahl.
Oder hier haben wir ein anderes Beispiel, wenn Sie Pi darstellen
wollen, dann ist das eben auch keine ganzzahlige Zahl.
Und deshalb möchten Sie auf Ihrem Rechner auch einen Datentyp haben,
mit dem Sie eben solche Kommazahlen darstellen können.
Und die Kommazahlen auf dem Computer, die nennen sich
Fließkommazahlen, weil Sie haben eben auch wieder einen bestimmten
eingeschränkten Zahlenbereich, der dargestellt werden kann.
Und an welcher Stelle das Komma in Ihrer Zahl steht, ist jetzt nichts,
was durch den Datentyp fix vorgegeben ist.
Also dass der Datentyp Ihnen sagt, es müssen immer zehn Stellen vor
dem Komma stehen und zwölf Stellen nach dem Komma.
Sondern das Komma kann an beliebigen Stellen, je nachdem, welche Zahl
Sie darstellen wollen, und wo es eben am sinnvollsten das Komma jetzt
gesetzt werden kann, können Sie dieses Komma setzen.
Also da hat der Begriff Fließkommazahlen.
Das Komma ist nicht an eine bestimmte Stelle festgelegt.
Also es muss nicht immer nach der zehnten Ziffer das Komma stehen,
sondern kann an beliebigen variablen Stellen stehen.
Und für diese Fließkommazahlen gibt es auch einen Standard, diesen
IEEE 754 Standard.
Dieses IEEE, das ist so eine US-Ingenieursvereinigung, die so ein
bisschen VDI bei uns entspricht.
Und die legen eben Standards fest, so ein bisschen wie bei uns
vielleicht die DIN-Normen können eben auch Standards festgelegt
werden.
Und ein Standard von IEEE betrifft eben diese Darstellung von
Fließkommazahlen.
Genau, und das sehen wir gleich hier unten, wie der da festgelegt ist.
Also erstmal allgemein kann man sich das vorstellen als Teilmenge der
reellen Zahlen.
Und natürlich können wir auch hier wieder nur endlich viele Zahlen
darstellen, also können wir nicht jede beliebige reelle Zahl
darstellen.
Da gibt es ja sogar noch mehr als von den Ganzzahlen.
Also die reellen Zahlen sind auch in der Unendlichkeit nochmal eine
Stufe höher als das, was wir bei Ganzzahlen haben.
Und deshalb können wir eben nur eine Teilmenge darstellen.
Die Notation in Ihrem Programm ist so, wie Sie es kennen, also einfach
mit einem Dezimalpunkt.
Man muss vielleicht aufpassen, das ist hier die US-Notation, also Sie
können kein Komma schreiben, wie im Deutschen üblich, sondern Sie
müssen den Dezimalpunkt verwenden und damit können Sie dann eben
solche Kommazahlen darstellen.
Alternativ dazu können Sie auch diese wissenschaftliche Notation
verwenden, die Sie wahrscheinlich von Ihrem Taschenrechner her kennen,
dass Sie da dieses e für ein Exponent eingeben können.
Also Sie können auch so schreiben, 6,022e23 und das steht dann eben
für die Zehnerpotenz, mit der man diesen Wert hier multiplizieren
muss.
Das heißt, dieses 6,022 soll mit 10 hoch 23 multipliziert werden, um
eben zu dem dargestellten Wert zu kommen.
Ziemlich schön bekannt, diese e-Notation, oder?
Nehme ich an.
Oder hat die jemand noch gar nicht gesehen?
Hier oben, ja, Sie kennen sie nicht, oder?
Also so schwierig ist es auch nicht, genau, Sie müssen einfach mal
gesehen haben, dann geht das.
Gut, jetzt kann man sich überlegen, wie stellt der Computer jetzt
solche Kommazahlen dar?
Und so ein bisschen so eine Art, wie sie eigentlich hier verwendet
wird, ist auch, wie sie der Computer dann darstellt.
Das heißt, wir haben hier auch so eine Kommazahl hier, die
multipliziert wird mit irgendeinem Wert, der die Größenordnung mehr
oder weniger angibt.
Aber nachdem der Computer eben im Binärsystem rechnet, ist es auch
hier so, dass auch hier eben keine Dezimaldarstellung, sondern eine
Binärdarstellung verwendet wird.
Das heißt, also diese Basis von der Exponentiation ist nicht 10, wie
es eben bei Menschen üblich ist, sondern der Computer verwendet hier 2
als Basis.
Das heißt, wir haben hier was Ähnliches, also erst gibt es hier so ein
Vorzeichen, da sage ich gleich noch was dazu, warum das so komisch
geschrieben ist.
Dann multipliziert mit dieser Mantisse, also das entspricht so einem
Wert wie hier, dieses 6,022, mal 2 Hochexponent.
Das heißt, so werden die Zahlen intern dargestellt.
Das heißt, wir brauchen dann irgendeine Darstellung von diesem
Exponent und der Mantisse und das Vorzeichen.
Und das Vorzeichen ist typischerweise, da reicht ein Bit, um das
darzustellen.
Das heißt, das ist 0 oder 1.
Und dann kann man es mal anschauen, wenn dieses Vorzeichen-Bit 0 ist,
dann wird der Wert mit minus 1 hoch 0.
Minus 1 hoch 0 ist 1, also dann ist es eine positive Zahl, wenn dieses
Vorzeichen -Bit hier 0 ist.
Wenn das Vorzeichen-Bit 1 ist, dann wird minus 1 hoch 1 gleich minus 1
mit minus 1 diese Zahl hier multipliziert.
Also dieses Vorzeichen-Bit, wenn es auf 1 gesetzt ist, zeigt es an,
die Zahl ist negativ.
Wenn es 0 ist, zeigt es an, dass es positiv ist.
Okay, aber schauen wir uns mal hier ein Beispiel an.
Also wenn wir zum Beispiel hätten Vorzeichen-Bit 1, der Exponent ist
minus 1, die Mantisse 5.
Dann wäre die Zahl, die eben dadurch dargestellt wird, wäre minus 1
hoch 1 mal 5 mal 2 hoch minus 1.
So mal ausrechnen, also das hier ist minus 1 wieder, also minus 1 mal
5 minus 5 mal 2 hoch minus 1.
2 hoch minus 1 ist 1 halb, also 5 mal 1 halb minus 2,5.
Okay, soweit so gut.
Und jetzt können wir uns noch überlegen, diese Zahlen hier, also das
Vorzeichen -Bit, da reicht 1 Bit aus, um das darzustellen.
Und für den Exponent und für die Mantisse, da gibt es eben
verschiedene Möglichkeiten, wie groß wir die wählen können.
Und was steuern wir damit?
Also mit dem Exponenten hier, mit diesem Wert, steuern wir praktisch,
wie groß die Zahlen sind, die dargestellt werden können.
Wenn Sie hier große Werte für den Exponent zulassen, dann können Sie
auch betragsmäßig große Zahlen hier darstellen, in dieser Fließkomma
-Darstellung.
Andererseits, wenn Sie hier den Exponent klein wählen, dann wird eben
auch der Zahlenbereich hier wieder absolut, vom kleinsten bis zum
größten Wert, relativ klein.
Und was steuern Sie mit der Mantisse?
Die Mantisse gibt Ihnen praktisch die Präzision an, wie genau Sie
jetzt einzelne Kommazahlen praktisch annähern können.
Je größer Sie diese Mantisse wählen, je mehr Bits Sie zur Verfügung
stellen, desto präziser lässt sich eine Fließkommazahl darstellen.
Gut, und da gibt es jetzt eben verschiedene Standardwerte, für wie
groß jetzt diese Exponenten und Mantissen werden können.
Und eigentlich gibt es zwei solche Standarddatentypen, Float und
Double, die in fast allen Programmiersprachen auftauchen, die auch
durch den IEEE-Standard festgelegt sind.
Diese Float-Variablen, die sind insgesamt 32 Bit groß.
Das heißt, wir müssen irgendwie diese 32 Bit uns jetzt einteilen in
Exponent, Mantisse und Vorzeichen.
Also ein Bit fällt schon mal weg fürs Vorzeichen, dann haben wir noch
31 Bit übrig.
Und die muss man jetzt irgendwie aufteilen in Exponent, um den
Zahlenbereich, wie groß kann jetzt so eine Zahl maximal werden,
festzulegen und die Präzision in der Mantisse.
Und bei den Float ist es eben so, dass man 8 als Exponent gewählt hat
und 23 als Mantisse.
Das heißt, das ist praktisch ein Bytewert, dieser Exponent, der kann
von minus 128 bis plus 127 gehen.
Und in der Mantisse haben wir eben 23 Bit, mit dem wir so einen Wert
wie hier oben, diesen 6,022 angeben können.
Also das reicht in vielen Fällen schon aus, in manchen Fällen braucht
man aber ein bisschen mehr.
Weiß jemand von Ihnen so grob im Kopf, wie groß die größte Zahl, also
wenn man es in so einer wissenschaftlichen Notation wieder anschauen
möchte, wie groß die größte Zahl ist, die sich mit Float darstellen
lässt.
Also irgendwas mal 10 hoch und die Zahl 10 hoch, irgendwas würde ich
von Ihnen gerne wissen, was das so ungefähr ist.
So größenordnungsmäßig.
Wenigstens 160 ist die Vermutung hier.
Sie haben es gerade ausprobiert oder nachgeschlagen, 38 ist der Wert,
der hier dargestellt werden kann.
Es gibt eigentlich auch noch einen netten Trick, wie man so
Exponentiationen vom 10er-System ins 2er-System umrechnen kann.
Man schaut sich die 1.000 an.
Die 1.000 lässt sich im 2er-System und im 10er-System relativ gut als
2 hoch irgendwas oder 10 hoch irgendwas darstellen.
Also im 10er-System haben wir die 1.000 wäre 10 hoch 3 und im 2er
-System, 1.000 passt nicht ganz genau, aber 1.024 wäre 2 hoch 10.
Das wollten Sie auch gerade sagen, nehme ich an.
Also 1.000, das sieht man dann, man hat einmal eben 3 als Exponent,
einmal 10.
Das heißt, so 3,33 ist der Faktor, mit dem man eben multiplizieren
muss, um von der einen zur anderen Darstellung zu kommen.
Also wenn Sie hier jetzt irgendwie...
Es gibt dann noch diesen zweiten Datentyp für Fließkummerzahlen, der
eben eine größere Genauigkeit und auch größere Werte erlaubt.
Insgesamt würden hier 64 Bit verwendet, also praktisch die doppelte
Anzahl von Bits.
Man hat bei dem Exponent 11 Bit, bei der Mantisse 52 Bit und auch
wieder 1 Bit für das Vorzeichen.
Also beim Exponent ist es gar nicht so viel mehr, aber bei der
Mantisse haben Sie fast über die doppelte Präzision, die Sie damit
eben darstellen können.
Vielleicht auch da hinten, Sie haben vorher schon gegoogelt, was ist
denn hier jetzt der größte Wert bei Double, der sich darstellen lässt.
Also was ist da wieder der Exponent dann?
308, genau, habe ich auch so im Kopf.
Anstatt genau, können Sie eben praktisch nochmal hoch 10 mehr
darstellen als mit den Fließkummerzahlen, also bis 308 ungefähr.
Das reicht eigentlich für sehr viele Anwendungen aus.
Trotzdem ist natürlich die Frage, was passiert jetzt hier, wenn Sie
noch größere Werte darstellen.
Und dann, ja, Sie strecken es.
Wir müssen leider nochmal sagen, ich habe es noch nicht gehört, jetzt
geht es ein bisschen lauter noch.
Richtig, auch das stimmt.
Wenn man mit noch größeren Zahlen zu tun hat, dann gibt es zum
Beispiel in Java oder auch in anderen Programmiersprachen spezielle
Langzahlbibliotheken oder Big Integer heißen die zum Teil auch, mit
denen man eben noch größere Zahlen darstellen kann und da sind die
Zahlenbereiche dann auch häufig im Voraus nicht beschränkt, sondern es
hängt dann nur vom Speicherplatz Ihres Computers ab, wie groß die
Zahlen werden können, die Sie darstellen wollen.
Und das gibt es sowohl bei Ganzzahlen als auch bei Fließkummerzahlen.
Ja, die Frage war, ob man eine Zahl auf mehrere Zahlen aufteilen kann.
Das heißt, Sie wollen, wenn sie zu groß wird, irgendwie die aufteilen
dann.
Letztendlich ist das die Idee, was diese Langzahlbibliotheken machen,
dass sie eine eigene, sehr große Basis einführen, dass sie in so einem
2 hoch 30 System arbeiten und jeder Integer dann eine Ziffer in diesem
2 hoch 30 System ist bei Ganzzahlen zum Beispiel.
Und das ergibt dann auch so eine Aufteilung auf mehrere Integerwerte
zum Beispiel.
Genau, das ist das, was dann diese Langzahlbibliotheken machen.
Aber im Normalfall braucht man die eigentlich nicht, sondern da
reichen die Eingeboten-Datentypen.
Gut, wie gesagt, jetzt gibt es noch ein paar Besonderheiten, wie eben
was passiert, wenn die Zahlen zu groß werden.
Und für diese Besonderheiten, da hat dieser IEEE-Standard auch nochmal
spezielle Werte festgelegt, die es eben als Zahlenwerte für
Fließkummer und für Double noch gibt.
Und zwar zum einen gibt es den Wert NAN, der steht für Not a Number.
Dann gibt es Positive Infinity, Negative Infinity und bei der Null
gibt es auch zwei Darstellungen.
Da gibt es die positive Null und die negative Null.
Das sind jetzt vielleicht so ein bisschen überraschende Dinge, aber
gehen wir mal der Reihe nach durch.
Oder fangen wir vielleicht hier oben rechts mal an, Negative Infinity.
Das taugt auf, wenn eben bei einer Operation der Wert so groß ist,
dass er nicht mehr dargestellt werden kann in dem Zahlenbereich von
Float oder Double.
Und dann wird eben nicht ein Fehler generiert oder sowas wie bei den
Integern, dass man so einen Überlauf, so einen Wraparound-Effekt hat,
sondern in Fließkummerzahlen wird dann ein spezieller Wert, Negative
Infinity oder Positive Infinity, eben für den Unendlichwert verwendet.
Not a Number, können Sie sich vorstellen, wo sowas rauskommt?
Division durch Null wäre eine Möglichkeit, wo das rauskommen könnte,
genau, weil das was ist, was ja eben nicht definiert ist.
Ich muss sagen, ich bin mir jetzt nicht hundertprozentig sicher, wie
das ist, vielleicht weiß das jemand von Ihnen.
Ich habe im Kopf, dass in dem IEEE-Standard das so festgelegt ist,
dass wenn ich zum Beispiel drei durch Null teile, dass dann Positive
Infinity als Ergebnis rauskommt.
Also da teilt man praktisch nicht durch Null, sondern durch eine ganz
kleine Zahl.
Und wenn man durch diese ganz kleine Zahl teilt, kommt eben was sehr
Großes raus.
Weiß das von Ihnen gerade jemand, ob das so ist?
Ich bin mir nicht sicher, was passiert, wenn man Null durch Null
teilt, ob dann Not a Number rauskommt oder auch Positive Infinity.
Können Sie vielleicht auch, wenn Sie da hinten gerade im Google sind,
das mal prüfen.
Genau, aber Not a Number, wo könnte es sonst noch rauskommen?
Also bei Division durch Null, das würde ich auch sagen, das wäre so
ein potenzieller Kandidat, wo man das eigentlich vermuten könnte, aber
die haben sich eben für eine andere Lösung hier entschieden.
Null mal unendlich?
Ja, ich glaube, das ist sogar so, genau.
Also es wäre zu sagen, die Alternativen wären, dass man dem auch
wieder Null oder unendlich wieder zuweist.
Aber bei Null mal unendlich ist das schwierig und ich glaube, dass das
wirklich Not a Number dann das Ergebnis ist.
Sonst ist es auch so, dass bestimmte Funktionen, die ihr Computer kann
oder die auch in Java zur Verfügung stehen, Mathematikfunktionen sowas
generieren können, also wenn Sie Wurzel von Minus 2 zum Beispiel
berechnen, oder Wurzel im Allgemeinen von einer negativen Zahl, das
ist ja in reellen Zahlen auch nicht definiert, was das Ergebnis da
sein soll und da liefert dann so eine Wurzelfunktion oder
Wurzelmethode auch Not a Number zurück.
Und wenn Plus 0,0 und Minus 0,0, das wird eben auch um so, ja, so ein
bisschen kann man es mit der Mathematik vergleichen, wenn man solche
Folgen hat, die sich zum Beispiel von Null annähern, ob die jetzt von
positiv oder von negativ sich zu Null annähern, man kann da eben so
kleine Dinge nochmal unterscheiden, ob ein Wert, der eigentlich in
einer präzisen Darstellung gar nicht genau Null wäre, sondern nur eine
ganz kleine Zahl, die aber noch positiv ist, die würde man dann eben
mit Plus 0,0 darstellen, wenn es eine sehr kleine negative Zahl ist,
die aus dem Bereich rausläuft, weil sie zu klein ist, würde man Minus
0,0 darstellen.
Oh, das ist eine gute Frage.
Ich glaube sogar, dass das geht.
Also die Frage war, kann man eine Fließkomma-Zahl mit Not a Number
oder mit Infinity vergleichen, ob das die Programmiersprachen
zulassen.
Ich bin mir nicht hundertprozentig sicher, aber ich glaube, es gibt
schon Not a Number auch als Sprachkonstrukt in Java, sodass das
hinschreiben können.
Ja, aber es ist auch nicht so üblich, dass man sowas jetzt macht, dass
man praktisch erstmal einen Fehler generiert und dann vergleicht auf
Not a Number, sondern im Normalfall wird man eben versuchen, schon
davor diese Fälle, wo Not a Number rauskommen kann, irgendwie
auszuschließen und dass das eben gar nicht dazukommt.
Ok, also hier ist es so, dass wir eben die reellen Zahlen über
abzubauen, endlich viele sind und wir hier nur endlich viele Werte
haben, müssen wir irgendwie zusätzlich auch noch die Werte runden.
Also wir haben nicht nur die Größeneinschränkungen, sondern wir haben
auch das Rundungsproblem noch zusätzlich hier.
Also man kann sich das vielleicht, wenn Sie sich so ein Zahlenstrahl
vorstellen, mit den reellen Zahlen drauf, dann ist es so, dass es eben
einen bestimmten Bereich gibt, so was ich bei float von 10 hoch 38 mal
irgendwas bis minus 10 hoch 38 mal irgendwas, das sind beispielsweise
die maximal und minimalen Werte, in denen eine Zahl dargestellt werden
kann.
Alles, was noch größer ist, wird dann eben positiv oder negativ
Infinity geben.
Dann gibt es zwischen den einzelnen Zahlen auf dem Zahlenstrahl gibt
es Lücken, weil eben nicht jede reelle Zahl dargestellt werden kann.
Und dann gibt es nochmal um die Null rum einen Bereich, wo eben auch
in einer gewissen Breite keine Zahlen dargestellt werden können, weil
die zu nah an der Null sind und dann diese negativen Exponenten, die
man eben zur Verfügung hat, um kleine Zahlen darzustellen, auch eben
irgendwann mal ausgehen.
Also das heißt, man hat bei großen Zahlen betragsmäßig eine Lücke, man
hat bei Zahlen, die sehr nah an der Null sind, eine Lücke und eben
zwischen den einzelnen Zahlen durch die Rundung auch Lücken.
Gut, und durch diese ganze Rundung hat man auch das Problem, dass
Gleichheit und Ungleichheit häufig nicht so wirklich präzise arbeiten,
wie man sich das erwünscht.
Also statt x gleich y sollte man deshalb besser einen aufwendigeren
Test machen, der versucht eben auch so kleine Abweichungen von den
Zahlen noch als Gleichheit zu betrachten.
Also statt Gleichheit sollten Sie besser sowas schreiben, hier also
der Absolutbetrag zwischen den beiden Zahlen soll kleiner sein als
eine vorgegebene Konstante zum Beispiel.
Bei Ungleichheit entsprechend, dass der Absolutbetrag der Differenz
größer sein soll als eine bestimmte Konstante.
Also dass die Zahlen eben nicht ganz genau gleich sein müssen, sondern
auch wenn sie noch ein kleines bisschen nebeneinander liegen, dass sie
dann zum Beispiel auch schon als gleich betrachtet werden.
Was vielleicht noch besser ist, ist so ein bisschen so eine Notlösung,
was gut funktioniert, sind einfach Vergleiche auf kleiner oder größer.
Also wenn Sie nicht direkt auf den exakten Wert abprüfen, ob die ganz
exakt übereinstimmen, sondern wenn Sie einfach nur wissen wollen, ist
die eine größer als die andere, das ist unproblematisch.
Gut, was ich jetzt hier noch übersprungen habe, ist, dass Java auch
noch bestimmte typischen Operationen auf Fließkommazahlen zur
Verfügung stellt.
Dazu gibt es eine eingebaute Klasse, die heißt Math, die Sie verwenden
können.
Und da gibt es eben bestimmte Fließkommazahlen schon drin
vordefiniert, wie zum Beispiel dieses Abs, Sinus, Cosinus, also Abs
für Absolutbetrag, oder so eine Wurzelfunktion, Square Root.
Okay, und hier unten sieht man auch schon mal, wie das verwendet wird.
Also hier wird man dann, das ist auch eine Notation, die Sie noch
nicht kennen bisher, Klasse, Punkt, diese Mathematikfunktion oder
Methode aufrufen, Math, Punkt, Sinus, Cosinus, Square Root oder
Absolutbetrag, und dann können Sie eben die eingebauten
Mathematikfunktionen verwenden.
Okay, soviel zu Fließkommawerten.
Aus diesen waren jetzt die Zahlenwerte, wir haben die logischen Werte
jetzt gehabt, die Zahlenwerte.
Was auch noch sehr gebräuchlich ist auf Computern, ist, dass Sie eben
Zeichen ausgeben, also irgendwelche Symbole, die eben auf dem
Bildschirm erscheinen sollen, also wie hier diese Buchstaben, die Sie
hier sehen.
Und für diese Buchstaben gibt es eben auch Codierungen, die werden
auch wieder auf Zahlen abgebildet, mit denen ist meistens nicht so
sonderlich sinnvoll zu rechnen, aber Sie brauchen eben bestimmte, also
einen Wertebereich dafür und bestimmte Konstanten.
Und es gibt zum Beispiel einen Zeichenkonstanten für so ein Groß-A,
für 3, für Prozent, eigentlich für alle Zeichen, die Sie über die
Tastatur eingeben können.
Und es gibt auch noch besondere Zeichen, die Sie über die Tastatur
manchmal nicht so direkt eingeben können.
Also hier dieses Backslash-N steht für einen Zeilenumbruch, dann
dieses Backslash 007.
Wenn Sie hier mit einer Null anfangen, dann wird das als Oktalzahl
gewertet, also in dem Fall auch das gleiche wie Dezimalzahl.
Und 007, das gibt einfach einen Ton aus.
Das ist etwas, was man vielleicht heute nicht mehr so häufig erlebt.
Wenn Sie im Terminal arbeiten bei Linux oder auf dem Apple-Gerät und
Sie löschen irgendwie zu viel Zeichen oder haben sonst irgendwie so
einen Fehlerzustand, dann wird manchmal so ein Piepston ausgegeben.
Und das können Sie in Ihrem Programm machen, indem Sie einfach so
dieses 007 rausschreiben, dann wird so ein Piepston ausgegeben.
Hier noch andere Konstante, das ist diese Unicode-Notation, wo Sie
eben noch mehr Zahlen darstellen können.
Das sind dann 64.000 oder 65.000, mehr als 65.000 Werte, die
unterschrieben werden können.
Okay, also das sind die Zeichen, die Sie meistens eben so wie hier
oben verwenden, indem Sie da eben diese einfache Anführungszeichen
schreiben.
Andererseits haben die aber immer auch einen Zahlenwert zugeordnet,
weil die Computer ja mit Zahlen nur rechnen können.
Und das führt auch dazu, dass Sie mit diesen Zeichenwerten wie mit
Ganzzahlen rechnen können.
Und die Werte entsprechen diesen sogenannten ASCII- oder Unicode
-Nummern.
Also das heißt, dieses a hat zum Beispiel ein Wert 97 in so einer
Standard -Codierung, und wenn Sie die einzelnen zuzählen, kommt dann
eben 98 raus.
Wenn Sie das dann wieder als Zeichen interpretieren, wäre das dann
auch ein klein b zum Beispiel.
Das ist manchmal auch sinnvoll, wenn man eben so, also häufig wird es
in Vergleichen eingesetzt, wenn man prüfen möchte, ist ein Zeichen,
was der Benutzer zum Beispiel eingibt, ist das zwischen 0 und 9, also
ist das eine Ziffer.
Dann können Sie da eben wirklich einfach diese Codierungswerte
vergleichen, also sind die Zifferwerte zwischen den Codierungswerten
von 0 und 9.
Was man eigentlich fast nie sieht, ist, dass man damit rechnet, mit
der Multiplikation, mit solchen Werten, aber auch das wäre möglich.
Lassen wir hier noch kurz was dazu sagen.
Also diese ASCII-Codierung, das ist eine relativ alte Ziffern
-Codierung, die ursprünglich eigentlich nur 7 Bit beinhaltet hat.
Heute ist das so eine sehr gebräuchige Darstellung, die normalerweise
mit 8 Bit verwendet wird, also ein Byte, ein ASCII-Zeichen.
Und das steht für American Standard Code for Information Interchange,
wenn ich das richtig in Erinnerung habe.
Also das sind praktisch so die Standardzeichen, die Sie auf Ihrer
Tastatur haben, und mit Unicode, das ist später eingeführt worden, das
dient dazu, auch internationale Zeichen darstellen zu können, wie
Umlaute im Deutschen oder Zeichen mit AXA im Französischen oder auch
mathematische Sonderzeichen, wie Pfeile, Integral oder chinesische
Schriftzeichen oder ähnliche Dinge.
Gut, häufig ist es so, dass Sie diese Zeichen nicht alleine als ein
einzelnes Zeichen behandeln wollen, sondern Zeichenketten sind auch
etwas, was sehr gebräuchlich ist, also ein Wort, ein ganzer Satz, ein
Text.
Und dazu gibt es einen Datentyp, der in Java eingebaut ist, der heißt
String.
Das ist jetzt im Gegensatz zu den anderen kein primitiver Datentyp
mehr, das heißt, das ist eine Klasse, die Java aber schon eingebaut
hat, die dazu dient, mit solchen Dingen wie Zeichenketten umgehen zu
können.
Und Sie können das so verwenden, dass Sie eben so eine Zeichenkette
hinschreiben, in doppelten Anführungszeichen, und diese Zeichenketten
zum Beispiel dann einer Variable zuweisen.
Hier auch nochmal, dann hier wäre noch ein drittes Beispiel, wo wir so
ein Zeilenvorschubzeichen auch nochmal mit drin haben, dass wir da
einen Zeilenumbruch haben an der Stelle.
Und es gibt auch bestimmte Operationen drauf.
Eigentlich der wichtigste ist wahrscheinlich die Verknüpfung.
Wenn Sie zwei Zeichenketten einfach hintereinander hängen wollen, oder
die Verkettung, oder auch Konkatenation genannt, dann schreiben Sie
diesen Plus-Operator dazwischen, und dann werden die Zeichenketten
hintereinander gehängt.
Also wenn Sie hier so greeting and address gleich greeting plus
address schreiben, dann wird eben dieses Hallo zuerst genommen und
dahinter dann dieser Text, dieser String address gehängt und in dem
String, der sich daraus ergibt, ist dann eben beides hintereinander
abgelegt.
Gut, dann noch ein letzter Datentyp, der nennt sich Enumeration und
ist mit Enum abgekürzt.
Und wozu können wir den brauchen?
Also bisher haben wir ja jetzt diese Zahlendatentypen, wir haben diese
Zeichendatentypen, auch Zeichenketten.
Manchmal möchte man aber sich einen eigenen Datentyp definieren, wo
die Werte als konstante Werte fest von Ihnen vorgegeben werden.
Also wenn man einen relativ kleinen Bereich von möglichen Werten hat,
denen aber schöne Namen geben möchte und nicht zum Beispiel einfach
durch Zahlendarstellungen, die er im Programm ausdrücken möchte, dann
kann man dazu diesen Enumeration, also Aufzählungsdatentyp verwenden.
Und in diesem Aufzählungsdatentyp werden eben die möglichen Werte
direkt angegeben, also man definiert den Typ selber und gibt dabei bei
der Definition des Typs an, welche Werte er speichern können soll.
Also hier zum Beispiel, wenn wir die Himmelsrichtungen speichern
wollen, dann können wir da eben so einen neuen Datentyp definieren,
gehen mit dem Schlüsselwort Enum, für Enumeration, dann geben wir dem
einen Namen, hier zum Beispiel Compass Direction, und dann geben wir
in geschweiften Klammern an die möglichen Werte, die in diesem
Datentyp gespeichert werden können.
Also hier zum Beispiel Norden, Osten, Süden, Westen, wir könnten für
Wochentage sowas haben, auch wieder Schlüsselwort Enum, dann der Name
für diesen Datentyp und in geschweiften Klammern die möglichen Werte,
also von Montag bis Sonntag.
Oder Farben, Rot, Grün, Blau zum Beispiel.
Und im Allgemeinen ist es so, dass man Enum, Name, und in geschweiften
Klammern dann die Liste der Werte.
Und Operationen sind normalerweise gar keine drauf definiert, das
heißt, Sie können nur diese Dinge einer Variable zuweisen, Sie können
prüfen auf Gleichheit, aber weitere Dinge sind nicht möglich.
Wenn Sie da noch mehr haben wollen, dann müssen Sie das von Hand
programmieren und wie das geht, dazu kommen wir später noch.
Das ist relativ ähnlich wie bei Klassen.
Gut, jetzt habe ich zum Abschluss von diesem Teil über Typen noch ein
paar Fragen an Sie.
Heute verwende ich mal nicht dieses elektronische System, aber ich
möchte es nächste Woche gerne nochmal probieren, dieses elektronische
Feedback -System wieder einzusetzen.
Es gab letzte Woche ja etwas technische Probleme, dass sich nicht alle
von Ihnen einloggen konnten.
Deshalb wäre jetzt meine Frage, wer von Ihnen kann sich denn über das
normale Mobilfunknetz einloggen?
Also Sie müssen da keine Uni-Seiten zugreifen, sondern die sind von
überall her verfügbar.
Das heißt, Sie können auch ganz normal über Ihren Mobilfunkzugang auf
diese Seite zugreifen.
Wer von Ihnen hätte dazu die Möglichkeit?
Okay, das sind schon mal einige.
Das heißt, bei denen, die dazu nicht die Möglichkeit haben, wer von
den Verbleibenden hätte denn ein Gerät dabei, mit dem er sich
einloggen würde, wenn das hier im WLAN gehen würde?
Okay, das ist auch ein kleiner Teil.
Und bei dem Rest nehme ich an, dass Sie kein Gerät dabei haben, mit
dem Sie darauf zugreifen könnten?
Oder wer hätte keine Möglichkeit, auf so ein System zuzugreifen?
Sagen Sie nochmal bitte?
Also irgendwie fehlen glaube ich ein paar noch.
Es war vielleicht ein größerer Teil von Ihnen, der gestrickt hat, der
hätte wohl Zugriff drauf.
Ich versuche nochmal zu klären, ob man den Zugriff hier über das WLAN
beschleunigen kann, dass sich mehr von Ihnen schneller einloggen
können.
Ansonsten gäbe es eben dann noch die Möglichkeit, direkt über
Mobilfunk auf die Seiten zuzugreifen.
Aber wie gesagt, das versuchen wir nächstes Mal nochmal.
Heute gibt es die Fragen in der altmodischen Weise, ohne
elektronisches Feedback-System.
Gut, die erste Frage an Sie.
Warum ist der Wert von 10 hoch 6 nicht 1 Million, sondern 12?
Also wenn Sie sowas hinschreiben, ja?
Richtig, genau, das war die Antwort.
Ich weiß nicht, ob Sie alle gehört haben, weil dieses Hochzeichen hier
ist nicht das mathematische Hoch, sondern das ist das Exor, das
exklusive Oder.
Und das bedeutet, hier wird von diesen beiden Werten 10 und 6 in der
Binärdarstellung bitweise ein exklusiv Oder gebildet und da ist eben
das Ergebnis 12.
Das heißt, was müssten Sie machen, wenn Sie da wirklich 10 hoch 6
ausrechnen wollten?
Ja,
genau, man könnte es hintereinander schreiben, 10 mal 10 mal 10, eben
6 mal hintereinander.
Das geht bei 6 noch, wenn Sie 10 hoch 60 ausrechnen wollen, dann wird
es schon ein bisschen umständlicher.
Dann müssen Sie eben entweder so eine Funktion selber oder so eine
Methode programmieren, die Ihnen x hoch y berechnet, oder Sie schauen
nach, ob es die in der Bibliothek vielleicht schon gibt.
Ja,
ich bin mir nicht sicher, ob ich Sie verstanden habe.
Ich habe irgendwas gehört mit 10d und 6d.
Es gibt eine Methode in dieser Mathematikklasse, die ich erwähnt
hatte, Power for Power, ich bin mir nicht sicher, ob die auch auf
Ganzzahlen rechnet oder ob die nur mit Fließkummerzahlen rechnet.
Müssen Sie nachschauen, vielleicht gibt es auch eine Integer-Variante,
also eine ganzzahlige Variante und im Zweifelsfall, wenn Sie nichts
finden, müssen Sie es eben selber programmieren, dass Sie sich da so
eine Exponentiationsfunktion selber schreiben.
Okay, aber hier ist es das XOR.
Nächste Frage, kann man hier so etwas hinschreiben wie a kleiner b
kleiner c, um damit zu prüfen, ob die Zahlen der Größe nach geordnet
sind?
Genau geht nicht, den zweiten habe ich nicht verstanden, weil a
kleiner b...
Genau, also a kleiner b gibt einen Boolschenwert, einen Wahrheitswert
zurück und diesen Boolschenwert, den kann man nicht dann mit c
vergleichen, oder könnte man vergleichen, aber das ist nicht das, was
sinnvoll ist dann.
Was man dann machen muss, ist was?
Man muss es aufteilen in zwei Tests, also man muss hinschreiben a
kleiner b und b kleiner c.
Und dazu wird man dieses Doppel-und verwenden.
Aber das heißt, man kann sowas hier nicht hinschreiben, sondern muss
es aufspalten in zwei Vergleiche.
Okay, dann kann man die Vergleichsoperatoren kleiner und größer
verwenden, um Stringvariablen zu vergleichen.
Das sind jetzt zum Teil Dinge, die schon ein bisschen über die
Vorlesung rausgehen, die Sie vielleicht, wenn Sie schon mal
programmiert haben, schon mal gehört haben.
Sie sagen ja, das geht, Sie können hinschreiben String kleiner, einen
anderen String, ja.
Und was ist das Ergebnis dann?
Also Stringlänge würden Sie sagen, wäre das was verglichen wird dann
hier?
Wäre eine Möglichkeit, was sagen die anderen dazu?
Nein, gibt es noch eine Antwort?
Was sagen Sie?
Genau, also diese kleiner und größer sind auf Strings nicht definiert
oder zumindest nicht sinnvoll.
Das heißt, man muss dann eine eigene Methode verwenden, die in der
Stringklasse aber schon vorhanden ist.
Und die heißt CompareTo, mit der man eben ein String mit dem anderen
vergleichen kann.
Und die liefert dann Minus 1, 0 oder 1 zurück, je nachdem ob ein
String größer oder kleiner ist.
Und der Vergleich, der ist praktisch sowas wie im Telefonbuch, wie da
die Namen angeordnet sind.
Das nennt sich lexikografische Ordnung.
Und nach dem werden dann mit diesem CompareTo die Strings in Java
verglichen.
Aber dieses kleiner und größer funktioniert auf Strings so nicht.
Gut, was ist das Ergebnis von 1 durch 0 und 1,0 durch 0,0 in Java?
Sie sind ja in der dritten Reihe, bitte.
Also mit dem ersten sagen Sie, 1 durch 0 gibt einen Fehler.
Sie haben gesagt Compilerfehler.
Was heißt Compilerfehler?
Genau, also während Sie Ihr Programm ablaufen lassen und es kommt
irgendwann mal auf so eine Berechnung 1 durch 0, dann würde Ihr
Programm an dieser Stelle abbrechen mit einem Fehler, das liefert
Ihnen auch noch eine Fehlermeldung, das ist die sogenannte Exception,
aber bricht dann an der Stelle ab.
Das heißt, das 1 durch 0, das kann nicht berechnet werden.
Und wir hatten gerade auch noch den Begriff Compilerfehler oder ein
Fehler, der zur Kompilierzeit auftritt oder zur Laufzeit.
Das wird häufig auch noch unterschieden.
Also das erste Fehler zur Kompilzeit, das sind die, die der Compiler
schon herausfinden kann während der Übersetzung.
Das heißt, der liefert Ihnen dann irgendeine Fehlermeldung und Ihr
Programm konnte gar nicht übersetzt werden.
Und das zweite ist eben ein Laufzeitfehler, der tritt erst, wo Sie Ihr
Programm zwar kompilieren konnten, und das sieht alles soweit mal ganz
gut aus, aber während das Programm läuft, kann eben ein Fehler
passieren.
Das ist ein sogenannter Laufzeitfehler.
Okay, also hier bei dem 1 durch 0 dritten Laufzeitfehler auf.
In dem zweiten Fall, 1,0 durch 0,0, hatten Sie glaube ich auch schon
gesagt, was passiert da?
Genau, da kommt das Ergebnis Infinity raus, also das Plus-Infinity.
Wie gesagt, das ist eben in diesem IEEE-Standard so festgelegt, dass
man bei den Fließkummerzahlen durch 0 teilen darf.
Das kommt ein bisschen daher, dass man sagt, dieser Wert 0,0, der
könnte ja praktisch bloß so ein Rundungswert sein, der eigentlich für
eine Zahl, die zwar sehr klein ist, steht, aber eben nicht genau 0
ist, und deshalb lässt man hier diese Division durch 0 zu.
Okay, gut, also soweit zu den Datentypen.
Jetzt kommen wir in der zweiten Hälfte der Vorlesung oder im letzten
Drittel oder Viertel noch zu Variablen.
Also den Begriff der Variablen wollen wir noch ein bisschen präziser
fassen.
Den haben wir bisher schon ein bisschen informell immer verwendet
gehabt, aber um das nochmal ein bisschen genauer zu klären, was eine
Variable in Java ist, wollen wir uns jetzt noch etwas anschauen.
Also generell ist eine Variable erstmal ein Platzhalter für die Werte
eines Datentyps.
Das heißt, so eine Variable hat immer auch einen Typ, so wie Integer,
Float oder was wir eben gerade gesehen hatten, und ist ein
Platzhalter, in dem man eben einen dieser Werte des Datentyps
reinstecken kann.
Und welche Art von Werten Sie in eine Variable reinstecken können, das
müssen Sie in Ihrem Programm dem Computer vorher mitteilen.
Und zwar machen Sie das so, indem Sie zu einer Variable immer den Typ
angeben müssen, bevor Sie diese verwenden.
Das heißt, Sie müssen in Ihrem Java-Programm, wenn Sie eine Variable
verwenden wollen, erstmal vor der Verwendung angeben, welchen Typ soll
diese Variable haben.
Und dieses Angeben des Typs, das nennt sich auch Variablen
-Deklaration.
Und das sieht so aus in Java, dass Sie einfach eben hinschreiben, erst
den Typ und dann den Namen, den Ihre Variable haben soll.
Also wenn Sie in Ihrem Programm eine Variable a verwenden wollen, die
solche Fließkomma-Werte enthalten soll, müssen Sie als erstes in Ihrem
Programm so eine Deklaration hinschreiben, Float a, mit der Sie dem
Compiler oder Ihrem Programm bekannt machen, dass eben im Folgenden
die Variable a verwendet werden soll und die den Typ Fließkomma-Zahl
hat.
Und da können Sie auch mehrere Variablen gleichzeitig deklarieren,
indem Sie die in einer Komma getrennten Liste hintereinander
schreiben.
Also hier auch wieder Typ und eine Liste von Variablen würde bedeuten,
dass Sie danach drei Variablen x, y, z in Ihrem Programm verwenden
können, die alle den Typ Integer-Ganzzahl haben.
Gut, dann was machen wir mit den Variablen?
Also man möchte mit denen irgendwie rechnen können oder denen Werte
zuweisen.
Wir haben ja gesagt, die Variablen sind praktisch solche Container
oder Platzhalter, in denen man einen Wert reinstecken kann.
Und dann ist die Frage, wie kriegen wir einen Wert in eine Variable?
Oder man könnte auch sagen, wie setzen wir eine Variable auf einen
bestimmten Wert?
Und das geht ganz einfach, man schreibt den Variablen-Namen links hin
und rechts den Wert, den die Variable bekommen soll.
Also man könnte hinschreiben zum Beispiel hier, also erst einmal float
a und dann a gleich 3,25.
Also float a wird man erst einmal deklarieren, wir haben hier eine
Variable, Fließkommatyp, die wollen wir in Zukunft verwenden.
Und dann können wir hier mit dieser Zuweisung der einen Wert geben.
Und weil das relativ häufig ist, dass man Variablen nach der
Deklaration gleich einen Wert gibt, kann man das auch in einem
Ausdruck machen.
Man kann diese Zeilen praktisch verschmelzen und schreiben float a
gleich 2,5.
Das entspricht genau diesem Vorderen.
Also eine Variable a vom Typ float wird deklariert und gleichzeitig
wird ihr der Wert 2,5 zugewiesen.
Also allgemein schreibt man hin, in dieser Kurzschreibweise, ersten
Typ, dann der Name der Variable, den wir deklarieren wollen, die wir
in Zukunft also verwenden und dann mit gleich Wert geben wir der den
Startwert.
Den können wir nachher natürlich noch verändern, aber das ist dann
erst einmal dieser Startwert für die Variable.
Gut, und wo hat man Variablen bisher gesehen?
Zum einen in diesen Ausdrücken, in denen wir so ein bisschen gerechnet
hatten, also wie x plus y.
Und ganz am Anfang in der letzten Vorlesung hatten wir Variablen auch
als Attribute in Klassen gesehen.
Also wenn Sie bei dem Billardtisch noch mal denken, da hatten wir
breite Höhe, wir hatten dann auch so ein Vektordatentyp für
zweidimensionale Koordinaten, da hatten wir x und y, das sind
Variablen.
Also die Attribute in den Klassen sind auch Variablen.
Gut, und was vielleicht jetzt nicht so überraschend ist, die Variablen
heißen ja schon Variable, können ihren Wert ändern.
Und damit kann man zum Beispiel so eine Zustandsänderung in einem
Objekt modellieren.
Und die Variablenänderung funktioniert einfach über diesen
Zuweisungsoperator.
Also Sie schreiben sowas hin, x gleich y, damit ändern Sie dann den
Wert der Variablen x auf den Wert, den die Variable y an dieser Stelle
hat.
Und deshalb ist auch dieser Gleichheitsoperator etwas anderes als die
mathematische Gleichheit, sondern es ist eben ein Zuweisungsoperator
und kein Vergleichsoperator.
Und das sieht man sehr deutlich hier an dieser Stelle, wenn Sie
schreiben x gleich x plus 1, das bedeutet dann was?
Sie nehmen den aktuellen Wert, der in der Variable x drinsteckt,
zählen 1 dazu und schreiben den dann als neuen Wert wieder in die
Variable x zurück.
Aber es ist eben nicht diese falsche mathematische Gleichheit, dass x
gleich x plus 1 wäre, was ja falsch wäre.
Es ist eine Zuweisung, das ist was wichtiges hier.
Also wenn man ein Gleichheitszeichen schreibt, ist es die Zuweisung.
Also schauen wir uns das nochmal in einem kleinen Beispiel an, wie die
Zuweisung funktioniert.
Nehmen wir an, wir haben zwei Variablen, zwei Grenzzahlvariablen x und
y und wir nehmen an, die haben initial den Wert 10 bzw.
6.
Jetzt führen wir so eine Operation durch, wie x gleich x plus 1.
Was passiert dann?
Wir nehmen den Wert der Variable x, also 10, zählen 1 dazu, 11 und
schreiben das wieder zurück nach x.
Also haben wir nachher hier 11 in unserer Variable drinstehen.
Ich würde gerne mal ein bisschen mehr Ruhe bitten, das ist doch etwas
unruhig.
Ich weiß genau, eine Viertelstunde haben Sie schon durchgehalten, aber
die 10 Minuten, die Restlichen, die schaffen wir auch noch.
Also 1 dazugezählt, was passiert mit der Variable y?
Die wird in dieser Zuweisung nicht erwähnt auf der linken Seite, also
ändert sie auch ihren Wert nicht, die bleibt dann bei 6.
Wenn wir jetzt schreiben x gleich y, nehmen wir auch wieder den Wert
von y, den er hier hat, also den Wert 6, und dieser Wert 6 wird dann
in die Variable x geschrieben.
Also hat dann nach dieser Zuweisung x auch den Wert 6.
Und die Variable y bleibt unverändert, weil die eben nicht hier
geschrieben wird.
Das, was wir auf der linken Seite haben, wird geschrieben, das heißt,
die behält ihren Wert 6.
Wenn wir jetzt y weiter berechnen als 2 mal x plus 1, dann wird eben
der aktuelle Wert der Variable x genommen, mit 2 multipliziert 1
dazugezählt.
Dann haben wir 13, das wird in y abgespeichert.
x bleibt unverändert und wir haben diesen Zustand hier.
Also nochmal, das hat nichts mit der mathematischen Gleichheit zu tun,
und die Zuweisung ist auch nicht symmetrisch.
Das heißt, es macht einen Unterschied, ob Sie hinschreiben x gleich y
oder y gleich x.
Immer der Wert der Variable auf der linken Seite wird geändert.
Also hier oben wird x verändert, hier unten wird y verändert.
Danach haben sie dann schon den gleichen Wert.
Nach so einer Zuweisung hat x und y den selben Wert.
Dann wären sie auch mathematisch gleich, aber das ist eben so eine
Zuweisung, die nicht symmetrisch einen Variablenwert ändert.
Gut, ich hatte auch schon gesagt, dass diese Variablen auch als
Attribute auftreten.
Und da gibt es noch eine spezielle Notation, wie Sie solche
Attributwerte in der Klasse, also für ein Objekt, ändern können.
Also in der Klassendeklaration geben Sie an, welche Attribute in Ihrem
Objekt vorhanden sind, in Ihren Objekten verwendet werden sollen.
Und geändert werden die dann, oder können Sie auch, mit einer
speziellen Notation, und zwar werden die mit Objektname,
Punktattributname angesprochen.
Schauen wir uns das in einem Beispiel mal an.
Wir hatten in der letzten Vorlesung schon diese Klasse Vector2D für
zweidimensionale karthesische Koordinaten uns angeschaut gehabt.
Und hier mit diesem U legen wir jetzt ein neues Objekt dieser Klasse
an.
Das bekommt den Namen P, dieses neue Objekt.
Und wenn wir jetzt die Attribute x bzw.
y von diesem Objekt P verändern wollen, dann geht es eben mit diesem
Punktoperator hier.
Also wir schreiben hin, Objektname, das P war hier unser Name,
Punktattributname, das ist das x, also die x-Koordinate, soll auf 4,0
gesetzt werden.
P.y setzt uns den y-Koordinate auf diesen Wert.
Also wichtig hier diese Notation, Schreibweise, Objektname,
Punktattributname, um Attribute innerhalb von einem Objekt zu
verändern.
Also hier nochmal ein Beispiel.
Oder etwas genauer anschaut, was im Speicher passiert.
Wir haben hier mit dieser new Instruktion ein Objekt angelegt.
x und y haben hier jetzt mal die Werte 0 zum Start.
Und wenn wir jetzt P.x auf 4 setzen, also diese Zuweisung durchführen,
ändert sich eben hier unten der Wert von x in dem Objekt auf 4.
Und durch diese y ändert sich der Wert von y entsprechend.
Wenn wir jetzt noch ein zweites Objekt hätten, einen Vektor q, legen
wir den auch wieder mit new an und setzen die Attribute entsprechend,
dann wird dieses Objekt so aussehen.
Also mit new Objekt anlegen, mit Objektname, Punktattributname, können
die Attribute modifiziert werden von außen von so einem Objekt.
Das wird man später sehen, dass das zum Teil nicht das beste
Programmierspiel ist, solche Attribute direkt änderbar zu machen,
gerade bei großen Klassen, sondern da definiert man in einem besseren
Fall eigentlich solche Zugriffsfunktionen oder Methoden, über die
diese Werte verändert werden.
Aber dazu kommen wir dann später noch.
Und für jetzt ist das, denke ich mal, auch in Ordnung, wenn Sie sowas
programmieren.
Jetzt ist es so, dass die Variablen, wenn man sich Objekte anschaut,
noch eine nicht ganz einfache Eigenschaft haben.
Und dazu möchte ich jetzt nochmal ein bisschen in die Tiefe gehen, was
denn so eine Variable genau ist.
Das ist ein relativ wichtiger Aspekt jetzt noch, den wir hier in den
letzten 5 Minuten machen.
Deshalb möchte ich die Aufmerksamkeit nochmal erhöhen in den letzten
Minuten.
Also es fängt relativ einfach an.
Eine Variable ist erstmal, praktisch kann man sich vorstellen wie ein
Name für einen Speicherplatz.
Also das heißt, hier haben wir so einen Container oder so einen
Speicherplatz, wo irgendwie irgendwas drin gespeichert werden kann.
Der Name der Variable ist x, also hier ist mein Speicherplatz, den man
sich wie so ein Gefäß denken kann.
Dann ist da ein Inhalt drin, der Wert 0 ist hier gespeichert in der
Variable, die hat einen Typ, Integer und einen Namen, x.
Also mit x können wir das Ding ansprechen.
Das hat einen Wert und einen Typ.
Und mit einer Zuweisung ist es so, dass einfach ein neuer Wert in
diese Variable geschrieben wird, praktisch in diesem Container oder in
diesem Behälter abgelegt und der alte Wert wird dadurch eben
überschrieben.
Also wenn wir hier am Anfang die Variable x haben, also diesen
Container x, in dem der Wert 0 gespeichert ist und wir haben die
Zuweisung x gleich 4295, dann wird hier in dieser Speicherstelle eben
dieser Wert hier ausgetauscht, durch den Wert, den wir jetzt neu
reinlegen und der alte ist nicht mehr vorhanden.
Ihr Rechner hat jetzt eben für verschiedene Variablen so eine ganze
Anreihung von solchen Gefäßen oder Speicherstellen.
Die habe ich jetzt hier mal mit solchen Kästchen abgebildet.
Also nehmen wir an, Sie haben die Variable x, y, z, a, b, c und so
weiter.
Dann sind die in Ihrem Speicher abgelegt, also irgendwie so angeordnet
und diese Nummerierung, die drunter steht, die gibt an, wo die Dinge
abgelegt sind.
Ihr Hauptspeicher besteht aus sogenannten Speicherzellen und jede
Speicherzelle kann im einen Wert aufnehmen.
Und die Speicherzellen sind durchnummeriert.
Die werden zum Teil auch Adressen genannt.
Okay, und das ist ein bisschen eine idealisierte Sicht, aber da
springe ich jetzt ein bisschen drüber.
Und genau, das ist jetzt eine interessante Sache, wie jetzt Objekte im
Speicher abgelegt werden.
Bei solchen Ganzvariablen zum Beispiel ist es relativ einfach, die
passen eben genau in so ein Gefäß rein.
Bei Objekten ist es so, dass wenn die verschiedene Attribute haben,
müsste es an verschiedenen Speicherstellen abgelegt werden.
Also schauen wir uns mal an, hier zum Beispiel so ein Vector2D-Objekt
hatte zwei Attribute, x und y, und die werden dann irgendwo im
Speicher abgelegt, wenn Sie so ein Objekt anlegen, an
aufeinanderfolgenden Adressen.
Also hier zum Beispiel wäre an der Adresse 17 das x-Attribut abgelegt
und an der Adresse 18 das y-Attribut.
Und wenn Sie jetzt eine Variable haben von dem Typ Vector2D, dann ist
es eine sogenannte Referenz auf so eine Objektidentität.
Also die Objektidentität ist das, was im Hautspeicher abgelegt wird
und wo Ihre Attribute bestimmte Werte haben.
Also hier diese beiden Speicherstellen, wo x und y drinsteht, das ist
die Objektidentität.
Ihre Objektvariable, also wenn Sie sowas hinschreiben wie p gleich new
Vector2D, dann enthält es p nicht diese Objektidentität, sondern eine
sogenannte Referenz auf diese Objektidentitäten.
Und was heißt das eine Referenz?
Das ist praktisch sowas wie ein indirekter Hinweis, wo im Speicher die
abgelegt sind.
Und das wird so gemacht, dass man hier die Nummer speichert, an der
die Objektidentität abgelegt ist.
Das heißt, wenn Sie eine Variable pos des Typs Vector2D haben, zum
Beispiel pos gleich new Vector2D schreiben, dann wird ein
Speicherplatz verwendet, in dem Ihre Pos-Variable, diese Referenz,
abgelegt ist.
Und in dieser Referenz stehen nicht direkt die Attribute, sondern hier
ist praktisch ein Zeiger oder eine Referenz abgelegt, an welcher
Speicherstelle dann die eigentlichen Attribute abgelegt sind.
Okay, also Sie haben immer diese Referenz hier oder diese Indirektion,
dass Sie erst in einem Schritt an die Werte kommen, indem Sie eben
hier von dieser Speicherstelle, wo Ihre Objektvariable abgelegt ist,
eben einmal noch nachschauen müssen, wo liegt das Eigentliche, und da
liegen dann die Attributwerte.
Also hier 17 zeigt Ihnen an, an dieser Stelle müssen Sie schauen, da
sind dann der Reihe nach die Attributwerte abgelegt.
Okay, schauen wir uns das jetzt nochmal an, wenn nicht, dann haben wir
diese Programmsequenz hier, also neuer Vector2D, den wir P nennen,
dann haben wir hier noch so eine Konstruktion, die ich noch nicht
erklärt habe bisher, also der Wert x soll 2,0 sein, der y Wert 3,5,
das ist ein sogenannter Konstruktor, also wird das p hier angelegt, da
steht drin, an 17 liegt der eigentliche Inhalt, also die
Attributwerte, schauen wir nach, an 17, da steht hier x 2,0, y 3,5,
okay, dann, wenn Sie ein neues Objekt q anlegen, kriegen Sie hier
wieder irgendwo im Speicherplatz so eine Referenz für Ihr Objekt q,
also Ihr eigenes q liegt hier, aber das zeigt dann auf die Attribute
so indirekt, indem es sagt, ja, die liegen hier an der Stelle 20 und
folgen die Adressen.
Und wenn Sie dann hier noch was hinschreiben wie r gleich p, dann ist
die Frage, was das bedeutet.
Sie weisen hier einer Objektvariable r, also so eine Variable vom Typ
Vector2D, den Wert p zu.
Und das, was bei dieser Zuweisung passiert ist eben, dass nicht jetzt
irgendwie hier diese Objektidentität kopiert wird, sondern es wird nur
dieser Referenzwert kopiert.
Also hier in r legen Sie den Wert ab, der in p steht.
In p hat man den Wert 17, der eben auf dieses Ding hier zeigt, und das
heißt in r kriegen Sie dann auch denselben Wert 17.
Also nur die Referenzen werden bei so einer Zuweisung verändert, aber
nicht die Attribute.
Und es wird auch keine neue Kopie angelegt, durch so eine Zuweisung
von einem zusätzlichen Objekt.
Eine Sekunde bitte noch.
Also wenn wir jetzt so eine Zuweisung hätten, dann noch p gleich q,
dann wird auch hier einfach nur p verändert und es kriegt den Wert,
der hier in q drin steht, also die 20 wird hier reingeschrieben, aber
an diesen p selber, also an der Objektidentität von p, x, y, ändert
sich gar nichts und auch nicht an q.
Wir sehen auch, hier können mehrere Referenzen auf ein Objekt zeigen.
Also hier haben wir zwei solche Variablen, oder zwei Objekte, die
beide an diese 20 zeigen und das kann dann auch interessante Effekte
haben, dass man dadurch Änderungen von einem auf den anderen
durchschreibt.
Also schauen wir uns das hier nochmal an, also hier Objektreferenzen,
hier nochmal eine andere Darstellung, p, q und r, also hier die
Referenznummern, dann hier der eigentliche Inhalt, auf einander
folgenden Adressen abgelegt.
Wenn wir jetzt sowas haben wie p gleich q, dann wird hier dieser Wert
von p abgeändert auf den von q, also auf 20 und dieser blaue Pfeil
zeigt dann eben hier runter.
Also es werden nur die Objektreferenzen verändert.
Gut, an der Stelle denke ich, machen wir mal Schluss, da wiederholen
wir nochmal ganz kurz nächstes Mal, was das für weitere Auswirkungen
hat und sehen uns nächsten Montag dann wieder.