Herzlich willkommen zur fünften Vorlesung SE2. Bevor ich einsteige, nochmal
einen Hinweis zu den Übungen. Üblicherweise ist es so,
dass ich das hier eigentlich nicht sagen muss,
weil die Leute in die Vorlesung gehen,
sind meistens auch nicht die, die Probleme in den Übungen haben.
Ab Freitag wird das Blatt 5 bearbeitet in den Übungen.
Und ihr habt dafür nur eine Woche Zeit.
Das haben wir extra so angekündigt, dass ihr auch,
wenn ihr noch Abnahmen habt vom Blatt 4,
da konntet ihr ja jetzt in der Wiederholungsblattphase gut das aufholen.
Das heißt, bitte denkt dran,
dass ihr für Blatt 5 jetzt nur eine Woche Zeit
haben werdet
Das heißt also, bis zum Freitag der nächsten Woche
sollte es abgenommen sein, in den ganzen Terminen,
weil am Freitag nächster Woche dann die Laborphase 2 beginnt mit den Vierergruppen.
Also bitte Gast geben, wenn ihr noch nicht so weit seid. Okay,
heute haben wir zwei Themen, ein Thema vor der Pause,
dass jetzt hier Fehlerbehandlung mit Exceptions,
ein relativ spannendes Thema im Bereich Programmiersprachen und ein weiteres Programmiersprachenthema
dann anschließend Modularisierung
Wie steht es denn um Modularisierung? Was bedeutet Modularisierung?
So ein bisschen. Ich werde heute relativ wenig drüber sprechen,
warum man modularisiert, sondern eher sagen,
wie man technisch die Unterstützung bekommt in einer Programmiersprache wie Java.
Gut, vor der Pause jetzt also erstmal Fehlerbehandlung.
Und was ich machen will, ist, ich will versuchen,
gehen Fehlerbegriff vom letzten Mal so ein bisschen wieder reinzuholen,
den ich vor zwei oder drei Wochen vorgestellt habe,
dem dann so ein orthogonales Begriff, Begriffsgebäude gegenüberzustellen.
Und dann sehen wir uns an,
wie man mit Fehlern traditionell umgeht,
wie man das besser machen kann mit Exceptions in Objekten
in Themensprachen
Am Ende gehen wir noch so ein bisschen auf Subtyping und Exceptions ein.
Gut, erstmal nochmal zur Wiederholung. Es geht jetzt hier
um Fehlerbehandlung. Da müssen wir erstmal wieder uns klar machen,
was überhaupt ein Fehler ist. Da habe ich ja,
wie gesagt, vor zwei Wochen ein bisschen mehr zu gesagt.
Grob gesagt, hier auf der Folie zusammengefasst,
das Ganze hat immer so den Ursprung in irgendeiner Fehlhandlung bei der Entwicklung.
Das heißt, Menschen machen irgendwelche Fehler.
Das haben wir auch Programmierfehler genannt, Englisch Error.
Wenn so eine Fehlhandlung dann sich irgendwie niederschlägt im Quelltext,
dann ist es
ein Fehlerzustand
Und wenn es statisch im Programmtext ist, ist es
ein Programmfehler oder ein Effekt. Wenn so ein Fehler,
der im Programmtext ja stehen kann, ohne dass sie je ausgeführt wird,
aber wenn er dann ausgeführt wird, dann wird er ein Laufzeitfehler.
Führt zu einer Verfälschung des Speichers. Das heißt noch nicht,
dass dann auch dieser Fehler entdeckt wird,
sondern das passiert erst, wenn eine Fehlerwirkung auftritt.
Das heißt also, wenn irgendwie jemand wahrnimmt, oh Mensch,
da passt was nicht. Das habe ich ja so ein bisschen erläutert,
dass da durchaus Zeit vergehen kann von dem Zeitpunkt,
wo ein Fehler wirkt, bis zum Zeitpunkt,
wo er entdeckt wird und dass er zwischendurch noch ganz viel
anderes kaputt machen kann
Da haben wir halt unterschieden. Wie gesagt, die Ursache,
Fehlhandlung, dann der Effekt, dass es einen Fehlerzustand gibt,
statisch im Programmtext oder zur Laufzeit, wenn es ausgeführt wird.
Und es gibt dann irgendwie eine Fehlerwirkung.
Und wie kommt man dagegen an? Naja, Fehlerwirkung muss man
halt testen. Also Software testen ist die beste Möglichkeit,
um rauszufinden, an welchen Stellen Fehlerwirkungen auftreten.
Wenn man feststellen will, wo denn der Fehler herkommt,
also wenn man eine Fehlerwirkung jetzt gesehen hat,
jetzt herausfinden will, woran lag es denn?
Da muss man zum Fehlerzustand kommen und letztendlich eigentlich zur Fehlhandlung
Das heißt, man macht das mit Debugging, Hilfsmitteln,
um rauszufinden, wo es denn herkommt.
Und am Ende möchte man eigentlich schon vermeiden,
dass überhaupt Fehler in die Software reinkommen. Das heißt,
was man braucht, sind Standards, Normen,
auch Ausbildung, gute Programmierer, machen üblicherweise weniger Fehler.
Von daher ist halt die Programmierausbildung für Software schon sehr wichtig.
Okay, das nur erstmal nochmal zu dieser Begriffsdifferenzierung.
Ach ja, das ist der Programmabbruch, Felia.
So, jetzt haben wir diese tollen Begriffe,
haben jetzt sozusagen kategorisiert, wie wir die Dinge nennen.
Es ist immer sehr schön,
wenn man sowas hat
Stabiles Begriffsgebäude. Und was hilft uns das jetzt,
wenn wir tatsächlich Fehler beheben wollen? Was hilft uns das,
wenn wir Situationen haben, wie hier auf der Folie?
Ich habe sowas mal hier zusammengestellt,
so mehr oder weniger bunten Strauß an möglichen Fehlersituationen,
also eine Implementation erfüllt nicht die Anforderung ihrer Spezifikation.
Was macht man? Tja, bei einem schreibenden Zugriff,
eine Datei hat ein Programm, keine Schreibrechner für die Datei.
Okay. Ein Klient hält sich bei der Anforderung einer Dienstleistung
nicht an die Vorbedingungen. Kann er machen,
kann passieren, wenn man das Vertragsmodell einsetzt,
dann kann sowas natürlich passieren und dann ist das ziemlich deutlich
ein Fehler,
was man da jetzt genau tut
Für den Zugriff auf einer Webseite hat der Browser keinen Zugang zum Internet.
Total normal. Kann häufig passieren. Beim Raid-Zugriffe dann ungültiger Index benutzt,
das habt ihr wahrscheinlich in SE1 diverse Male selbst erlebt,
dass sowas passieren kann. Und letztes Beispiel durch konkurrierenden,
also gleichzeitigen Zugriff zweier Prozesse auf ein Objekt,
gerät dieses Objekt in einen inkonsistenten Zustand. So,
das sind jetzt ziemlich unterschiedliche Fehlersituationen, mit denen wir erstmal,
glaube ich, ein bisschen überfordert sind,
wenn wir jetzt gefragt würden, wie gehen wir damit systematisch um?
Das ist auch tatsächlich nicht so ganz einfach,
aber was wir hier über die Jahre entwickelt haben,
ist eine Unterscheidung von Fehlern, unserer Meinung nach sehr hilfreich ist,
um systematisch her mit Fehlern umzugehen.
Und die will ich jetzt hier einfach mal vorstellen.
Die Idee ist ganz einfach, wenn wir über Softwareentwicklung reden,
und das ist ja eine Veranstaltung über Softwareentwicklung,
dann geht es um das Erstellen, neue Software oder Bearbeiten von bestehender Software.
Das heißt, dass hier mal so angedeutet,
das soll die Software sein, mit der wir irgendwie umgehen.
Dann gibt es das Entwicklungsteam. Das sind die Leute, die den Quelltext bearbeiten.
Also wir jetzt als Entwickler, wir bearbeiten die Software.
Den Quelltext kompilieren, Pesten und so weiter. So,
und dann kommt aber noch dazu, dass ja die Software,
wenn sie dann irgendwann mal ausgeliefert wird,
also irgendwo in einem Kontext laufen soll,
also wirklich real etwas tun soll,
dann läuft sie in einer Umgebung, nennt sich auch gerne Produktivumgebung.
Da, wo die Software wirklich etwas produktiv tun soll.
Da stehen da lauter Dinge in dieser Umgebung, auf die
wir als Entwicklungsteam erstmal keinen Einfluss haben. Das heißt,
wir haben den Fokus, wie das hier angedeutet ist,
auf Softwareentwicklung und müssen aber damit umgehen, dass es noch eine Umgebung gibt.
Wir müssen schon damit rechnen,
dass es sowas gibt wie eine Datenbank, wie Dateisysteme,
Sensoren, Bibliotheken und auch natürlich die Benutzer des Systems,
wenn das System später läuft, dann haben wir Benutzer,
da haben wir keinen Einfluss drauf.
Wir können die nicht irgendwie steuern oder manipulieren oder programmieren
Okay, und wenn man das jetzt sich weiter überlegt,
dann kann man eben halt aus Sicht des Entwicklungsteams,
das den Quelltext manipuliert, also die Software baut,
zwei Einflussebene unterscheiden, das einfach jetzt mal hier so vorgenommen.
Wir sagen unmittelbar einen verändernden Einfluss hat das Team nur auf den Quelltext.
Da haben wir wirklich Kontrolle. Wir wissen,
wie viele Klassen es gibt, wir können das für stimmen,
welche Konventionen werden eingehalten, das wird vom Team festgelegt,
wird das Vertragsmodell eingesetzt, welche Algorithmen werden genutzt und ähnliches.
Und kaum Einfluss, also das ist dann genau das,
wo wir sagen, ja,
da können wir halt nur mit umgehen,
wir müssen sozusagen damit rechnen,
dass da zur Laufzeit
irgendwas passiert
Das ist der Einflussbereich, wo wir praktisch keinen Einfluss haben,
nämlich Bibliotheken, wenn wir die benutzen,
die können wir nicht einfach selbst verändern.
Manchmal kann man das, wenn es Public Domain sind,
aber meistens ist das auch nicht so eine gute Idee, Bibliotheken zu patchen.
Datenbanken stehen oft nicht unter der Kontrolle der Softwareentwicklung.
Da hat man nicht so den direkten Einfluss drauf.
Dateisystem, da gibt es Administratoren im Produktivkontext. Und die legen
die Rechte fest. Und so weiter und so weiter. So,
und die Idee unserer Systematisierung von Fehlerbehandlung ist jetzt,
dass wir diese beiden Einflussbereiche unterscheiden, in dem Sinne,
dass wir sagen, naja, in beiden, Ebenen können Fehler auftreten.
Und dann nennen wir die einen, die direkt bei der Softwareentwicklung passieren,
die wir also selbst machen, die wir als Entwickler machen,
mehrere Entwickler, der eine macht einen Fehler,
der andere, der muss ihn ausmerzen und so weiter.
Die nennen wir Entwicklungsfehler. Die passieren bei der Entwicklung,
bei der Softwareentwicklung. Und alles andere nennen wir Umgebungsfehler.
Also die Fehler, die außerhalb unseres Einflussbereichs liegen.
Dass es hier nochmal so ein bisschen angedeutet, also nochmal das Softwaresystem,
benutzt Bibliotheken, das Internet, möglicherweise möglicherweise Sensoren,
das Dateisystem, der Benutzer interagiert mit dem System,
das kann es alles geben.
Und dann ist es so, dass wir die Software selbst,
die produzieren wir, da sind wir in Kontrolle und sorgen dafür,
dass alles stimmt.
Und die Umgebung können wir nur konsumieren.
Die müssen wir sozusagen mehr oder weniger passiv hinnehmen.
Und da kommt eben halt dann genau diese Idee her,
interne Fehler, Entwicklungsfehler, alles, was außerhalb passiert, Umgebungsfehler.
So, ich hoffe, das ist erstmal als Begriffsgebäude für euch nachvollziehbar.
Was kann man jetzt nicht noch überlegen, was jetzt gilt?
Das ist ganz, also warum ist diese Unterstadtung jetzt hilfreich?
Das erste ist, Entwicklungsfehler sind prinzipiell vermeidbar.
Wir wollen ja keine Fehler machen. Aber das ist halt
menschlich, dass wir auch Fehler machen
Der entscheidende Punkt ist, wenn ein,
wenn innerhalb des Teams jemand einen Fehler entdeckt,
einen Entwicklungsfehler, dann wird er behoben.
Also entweder ist er dann behoben oder er ist nicht behoben.
Dann gibt es so eine Fehler-Datenbank, Baczilla oder was weiß ich,
was man da benutzen kann. Da sind die Fehler irgendwie
festgehalten, aber die sind entweder da oder nicht da.
Irgendwann sind sie behoben und dann sind sie weg.
Das ist ganz anders bei den Umgebungsfehlern. Bei Umgebungsfehlern ist es so,
der kann halt immer wieder auftreten. Es kann sein,
dass in der einen Umgebung, wie die Software läuft,
das Dateisystem andere Schreibrechte in einem anderen Kontext.
Das kann ich also, das kann durchaus wackeln,
das kann durchaus mal so und mal so sein.
Also bei Entwicklungsfehlern, wenn ein Fehler entdeckt wird,
wird er
sofort behoben
Und das ist auch so, dass man bei Entwicklungsfehlern üblicherweise
nicht irgendwas tut in der Software, um ihn zu beheben.
Also sozusagen, um damit umzugehen. Also wenn zum Beispiel ein Dienstleister
von einem Klienten benutzt wird und jetzt stellt der Dienstleister fest,
oh, jetzt übergibt er mir eigentlich eine Referenz.
Jetzt könnte ich ja, wenn er mir eine Null-Referenz übergibt,
dann kann ich ja nicht viel machen.
Aber ich könnte ja vielleicht mir noch woanders eine Referenz besorgen.
Irgendwie sowas. Alles Blödsinn. In dem Moment,
wo der den Fehler hier macht und man entdeckt das,
wird der Fehler behoben und fertig.
Dann macht man nicht irgendwie, schreibt man nicht Code,
der damit umgeht. Beim Gebingsfehlern ist es genau andersrum.
Da muss ich halt eigentlich Code schreiben, um damit umzugehen,
dass eventuell ich keinen Zugriff habe aufs Dateisystem oder aufs Netz
Wenn ein Browser kein Netzzugang hat, dann soll er zumindest ein Fenster aufmachen.
Hallo lieber Benutzer, schalt mal deine Internetverbindung an.
Sonst komme ich hier nicht weiter. Das sind also tatsächlich
unterschiedliche Arten, wie man mit diesen Fehlern umgeht.
Okay, wenn diese Unterscheidung erstmal klar ist, ist die
jetzt einigermaßen nachvollziehbar gewesen? War diese Unterscheidung jetzt nachvollziehbar?
Gibt es irgendwie Fragen dazu oder das sieht erstmal nicht so aus?
Scheint also relativ einfach zu sein. Ist auch meine Erfahrung
der letzten Jahre, dass wir immer hier diese Unterscheidung
ganz gut dass die ganz gut verstanden wird.
Okay, jetzt können wir uns die Beispiele nochmal angucken.
Also eine Implementation erfüllt nicht die Anforderungen ihrer Spezifikation.
Was würdet ihr sagen? Ist das ein Entwicklungsfehler oder ist
es ein Umgebungsfehler? Wer ist für Entwicklungsfehler? Einmal im
Handzeichen? Erst dafür, dass es ein Umgebungsfehler ist.
Großartig. Sehr schön. Bei einem schreibenden Zugriff
eine Datei hat ein Programm keine Schreibrechte für die Datei.
Gut, habe ich jetzt eben erzählt, brauche ich euch nicht fragen.
Das ist natürlich ein Umgebungsfehler. Wie ist es jetzt mit dem Vertragsmodell?
Ein Klient hält sich bei der Anforderung einer Dienstleistung nicht an die Vorbedingungen.
Ist das Entwicklungsfehler oder ist es ein Umgebungsfehler?
Wer ist für Entwicklungsfehler? Wer ist für Umgebungsfehler
Ja, da sieht man schon, das ist eher gespalten,
das ist nicht so ganz selbstverständlich. Ich denke,
so wie wir es hier momentan handhaben, in der SE2,
dass wir sagen, innerhalb unserer Softwareentwicklung benutzen wir das Vertragsmodell,
was möglicherweise die anderen gedacht haben, ist,
wenn man jetzt nach außen,
also das Vertragsmodell auch nach draußen trägt und jetzt sozusagen der Benutzer sich nicht an Vorbedingungen hält,
aber üblicherweise wird man da kein Vertragsmodell formulieren,
dem interaktiven Benutzer gegenüber.
Also von daher ist das eher, wenn man jetzt
das Vertragsmodell bei der Softwareentwicklung einsetzt, eine Entwicklungsumfehler.
Für den Zugriff eine Webseite der Browser keinen Zugang zum Internet
habe ich auch schon mehr oder weniger erläutert, das ist ein Umgebungsfehler.
Bei einem Raid-Zugriff, ein ungültiger Index benutzt,
was ist das?
Ein Entwicklungsfehler,
wer ist dafür
Ein Umgebungsfehler? Ah, fast niemand, natürlich.
Ist ja klar, da hat irgendein Programmierer Mist gebaut.
Also hat irgendwas einen falschen Index benutzt, falsch berechnet,
wie auch immer. Durch konkurrierenden, gleichzeitigen Zugriff, Zweier,
Prozesse auf ein Objekt, gerät dieses in einen inkonsistenten Zustand.
Das ist eine gemeine Frage, weil ihr mit Nebenläufigkeit hier
eigentlich gar nichts zu tun habt in SE1, in SE2.
Aber was würdet ihr sagen, ist das eher ein Entwicklungsfehler?
Wer ist für Entwicklungsfehler? Ja, wer ist für Umgebungsfehler
? Ja, das sind dann deutlich weniger.
Es ist tatsächlich ein Entwicklungsfehler, weil dieses Objekt,
das für Zugriff von mehreren Prozessen geschrieben ist,
man kann Software so schreiben, dass das funktioniert.
Das muss also nicht passieren.
Das ist also auch ein Programmierfehler
Gut, und ein Benutzer tippt in ein Eingabefeld an der
grafischen Oberfläche, Daten im falschen Format.
Also wird irgendwie gesagt, hier, gib doch mal das Datum ein.
Kennt ihr wahrscheinlich schon von Webseiten, wo ihr mal was eingekauft habt.
Was ist das? Ist das ein Entwicklungsfehler? Wer ist
für Entwicklungsfehler? Wer ist für Umgebungsfehler? Toll. Ihr
habt alle das wunderbar verstanden. Das ist natürlich ein Umgebungsfehler,
weil wir keinen Einfluss darauf haben, was der Benutzer alles macht.
Wer kann machen, was er will. Was ein Entwicklungsfehler wäre,
wäre, wenn dann unsere Software abstürzt,
bloß weil er das falsche Datum da eingetragen hat.
Aber wir müssen damit umgehen, dass da irgendwo etwas in
der Umgebung nicht nach unserem Rusto passiert.
Soweit, so Hut. Jetzt ist der nächste Schritt,
wir haben jetzt eine wahnsinnig nützliche Unterteilung vorgenommen zwischen Entwicklungsfehlern und Umgebungsfehlern.
Jetzt kommt die nächste Frage, was machen wir denn jetzt,
wenn wir tatsächlich mit Acceptions konkret umgehen wollen, mit Acceptions, mit Fehlersituationen?
Da kann man auch wieder zwei Situationen unterscheiden.
Ich habe hier mal wieder Blue Jay irgendwo mitgebracht.
Blue Jay starten hier. Das ist so eine archätypische Situation. Wir sind ja jetzt in der SE2,
ihr müsst also ganz viel Abstraktion, Akzeptionsvermögen entwickeln, es ist die Situation,
wie wir sie in jedem objektnotierten System ständig und immer und überall haben,
ein Klient benutzt einen Dienstleister, Dienstleister,
das ist sehr generisch hier, sehr allgemein.
Wir haben immer hier diesen Benutztfall, der sagt,
naja, der Klient macht wohl irgendwas mit dem Dienstleister.
So, weil das eben so zentral ist,
kann man eben halt an diesem zentralen Zusammenhang auch sehr schön sich überlegen,
wie gehen wir denn jetzt mit Fehlersituationen um?
Also zum einen, wie erkennt oder signalisiert ein Dienstleister einen Fehler,
wenn einer auftritt und wie geht ein Klient damit um,
wenn so ein Fehler gemeldet wird
Also wenn der Dienstleister zum Beispiel, da kann man sich fragen,
wie oft soll denn ein Dienstleister prüfen, ob er korrekt verwendet wird?
Das hatte ich eben schon angedeutet, wenn eine Methode irgendwie
eine Referenz bekommt, dann soll die ja nicht Nall sein.
Dann wäre es natürlich schlau, um ein Vertragsmodell zu überprüfen,
ob die wirklich nicht Nall ist,
weil je eher man das prüft, desto besser,
bevor man da irgendwie noch andere Sachen macht in der Methode.
Aber was macht der Dienstleister jetzt? Was macht der Dienstleister
, wenn er so einen Fehler erkennt? Das stellt er
irgendwie fest, oder Klient hat mich falsch benutzt,
was macht er jetzt?
Wenn wir uns gleich mal
ein bisschen angucken
Genauso, wenn dann der Dienstleister jetzt irgendetwas tut,
irgendeinen Fehler signalisiert oder eine Acception wirft, was wir gleich sehen werden,
dann stellt sich für den Klienten wieder die Frage,
naja, wie kann denn der sich jetzt darauf einstellen?
Was macht denn der Klient in dem Fall? Ist er dafür zuständig,
dass da irgendwas jetzt passiert oder kann er das ignorieren oder was vielleicht auch ganz spannend ist,
ist, kann der je nach Fehlerart vielleicht differenzieren?
Also ich benutze einen Dienstleister, jetzt kommt eine Fehlermeldung,
jetzt kriege ich hier irgendwie so eine Fehlermeldung und jetzt,
oh, das ist eine Fehlermedäre,
kann ich was damit anfangen oder ist eine Fehlermedäre,
da kann ich nichts anfangen, je nachdem,
kann ich halt in dem einen Fall was tun,
im anderen Fall nicht.
Also es wäre schon schön,
wenn man auch nach Fehlerarten differenzieren könnte.
All das geht natürlich mit Java,
aber bevor wir uns das angucken, würde ich gerne ein bisschen,
sagen wir, ausholen, wie allgemein Fehlerbehandlungen gemacht wurde,
bevor man sowas systematisch in Sprachen integriert ist, wie Exceptions hatte.
Hier sind mal so vier verschiedene Möglichkeiten aufgezählt, die ihr
auch immer noch viel in der Softwareentwicklung live da draußen antreffen werdet.
Also es ist jetzt hier nicht irgendwie aus den Fingern gesogen,
sondern es ist tatsächlich so, eine Möglichkeit ist,
allererste Möglichkeit, das Programm wird einfach aus der Fehlhaftmethode auch abgebrochen.
Also der Dienstleister stellt fest, falscher Parameter übergeben.
Mensch, das ist blöd. In Java gibt es system.exit.
Damit kann man die Virtual Machine anhalten. Kann man machen
. Wir werden sehen, wie schlau das ist.
Die fehlhafte Methode liefert einen Return-Wert, der vom Aufrufer als Fehlerkohl interpretiert werden soll.
Das findet ihr ganz viel in Unix-Systemen, in c-basierten Systemen.
Da gibt es immer so diese Idee,
dass jede Funktion in zehn, sie heißen die Funktionen,
die Methoden, also mehr oder weniger,
jede Funktion immer einen Ergebniswert liefert und wenn der Null ist,
hat sie alles ordnungsgemäß ausgeführt und wenn da irgendein anderer
Wert zurückkommt,
ist halt irgendwas schiefgegangen
Ist ein bisschen blöd, wenn man, na gut,
da komme ich gleich drauf.
Die fehlerhafte Methode setzt eine global lesbare Variable auf den eingetretenen Fehlerschatuss.
Also Dienstleister stellt Fest, Fehler. Ich setze irgendwo
zentrale an der Stelle einen Fehlerstatus, sage ich.
Hier jetzt global lesbare Variable. Ups, Fehlerstatus, Ups
. Jetzt ist es soweit. Und vierte Möglichkeit,
die hier mal so dargestellt ist, die fehlerhafte Methode ruft
eine sogenannte Callback-Operation auf, die ihr mit übergeben wurde.
Also Dienstleister kriegt nicht nur die Parameter für die eigentliche Aufgabe,
sondern auch noch so eine Referenz oder irgendwas auf ein Objekt,
das im Falle eines Fehlers aufzurufen ist, wo man sagen kann,
hier übrigens ist jetzt was passiert und jetzt kümmere du dich mal drum,
was da jetzt passiert, Abbruch oder sonst irgendwas.
Alles nicht so tolle Ansätze. Der Abbruch, das habe ich
schon hoffentlich einigermaßen angedeutet, also das ist natürlich keine Lösung.
Wenn in einem größeren Softwaresystem ein kleines Zahnrad sich ungerecht behandelt fühlt, so irgendwie,
oh, ich habe meinen Parameter falsch bekommen und dann sagt,
jetzt mache ich hier mal alles, alles Ende, Exit, das geht natürlich nicht.
Das weiß dieses Zahnrad ja gar nicht,
ob das jetzt angemessen oder nicht.
Und das ist auch bei sicherheitskritischen Systemen nicht
die allerbeste Idee
Logisch. Okay, das heißt also, das könnt ihr schon mal vergessen.
System-Exit bitte einfach aus eurem Vokabular streichen. Return-Wert ist auf
mehreren Ebenen schwierig, weil wenn das richtige Funktionen sind,
die man da programmiert, die also wirklich richtige Werte zurückliefern,
wie will man da den Fehlercode noch mit rein kodieren in diese Ergebniswerte?
Ziemlich blöd. Dann muss man außerdem,
wie man damit umgeht, muss man eigentlich,
bei jedem Aufruf muss man immer prüfen, oh,
wie hat der Aufruf, jetzt hat er jetzt für einen Wert geliefert,
wie reagiere ich da drauf und so weiter.
Also das ist, das führt einfach zu einem inakzeptalen Aufwand
Würde euer Quelltext nur noch vollstehen mit solchen Prüfungen,
also in so einem C-Programm irgendwie nach jedem Methodenaufruf oder nach
jedem Prozedur oder Funktionsaufruf, Fehlercode überprüfen, Fehlercode überprüfen.
Das ist irgendwie nicht das Wahre. Gut,
globale Fehlervariable hat auch das Problem,
wenn dann diese globale Fehlervariable einfach ignoriert wird,
hat es nicht so den tollen Effekt.
Das heißt, der Dienstleister sagt nur, ich setze diese globale Fehlervariable.
Und weil ich mache jetzt mal meinen Job weiter,
so irgendwie, kann ja auch nicht richtig sein.
Also scheinbar normal läuft das weiter, ist nicht so gut.
Callbacks, das ist eigentlich das,
was noch am ehesten so in die richtige Richtung geht
Führt aber dazu, dass natürlich die Schnittstellen aufgeblasen werden.
Also wenn jeder Dienstleister immer noch so einen zusätzlichen Parameter bekommt,
könnt ihr euch vorstellen, dann werden eure Schnittstellen relativ dick,
dass da einfach zu viel ständig hin und her übergeben wird.
Was eigentlich ja nur für die Fehlerbehandlung wichtig ist.
Das ist ja nicht für den Normalfall interessant. Das ist auch
mal ganz wichtig bei Fehlerbehandlung. Man will ja eigentlich,
man hofft ja immer, dass keine Fehler auftreten.
Und natürlich muss man sich mit Fehlern auseinandersetzen,
aber man möchte auch nicht den Quelltext jetzt komplett mit Fehlerbehandlungscode zudecken,
sodass man den eigentlichen Code gar nicht mehr sieht,
weil der muss ja auch wartbar sein.
Man muss ja wissen, was da jetzt konkret passiert,
damit man bei Änderungen auch weiß,
an welchen Stellen man
was tun sollte
Gut. Also, das ist natürlich schon vor etlichen Jahrzehnten diskutiert worden und
deswegen wurden dann in Programmiersprachen, das wurden Ausnahmen eingeführt.
Exceptions. Die gibt es schon ziemlich lange. Eine der
ersten Sprachen war CLU. Das war Anfang der 70er Jahre.
Von Barbara Haliskraft entwickelt worden. Spannende Sprache. Die erste
weiterververbreitete Sprache mit Acception, mit einem Acception-Konzept war ADA.
ADA ist die Sprache vom amerikanischen Department of Defense.
Es ist in den 70er Jahren ein Auftrag
gegeben worden
Ende der 70er, 79 ist die Sprache dann endlich fertig, definiert worden.
Das war ein Riesenkomitee. Die Sprache war nie so richtig erfolgreich,
weil sie eben halt sehr stark vom Militär durchgedrückt wurde.
Und dann kam Eifel, habe ich ja schon öfter mal erwähnt,
eine der wichtigen objektorientierten Programmiersprachen, Mitte der 80er Jahre.
Und das war die erste OO-Sprache, die Acceptions eingebaut hat.
Eifel war explizit angetreten als eine Software-Engineering-Sprache.
Und wenn man sich mit Softwareentwicklung systematisch beschäftigt,
dann muss man irgendeinen Mechanismus auch haben für Fehlerbehandlung.
So, und ja, warum macht man jetzt überhaupt so
ein eigenes Sprachkonzept
Das ist ja erstmal, sagen wir, die Macht,
die Sprache komplizierter, bringt das wirklich so viel.
Also hier sind mal so drei Ziele genannt,
die man immer im Blick behalten sollte,
wenn man sowas sich überlegt, wie man so eine Sprachunterstützung baut.
Das erste Fehler sollten nicht einfach ignoriert werden können.
Es muss also irgendwie durch die Sprache garantiert sein,
dass wenn ein Fehler auftritt, ist dann auch wirklich irgendwas passiert.
Der Quelltext zur Fehlerbehandlung soll die normal, fand ich, unnötig verschütten.
Das habe ich eben schon angedeutet. Das ist einfach unschön,
wenn der Quelltext nicht mehr lesbar ist,
weil er nur noch mit Fehlerbehandlungscode zugeschüttet ist.
Und was eben halt auch Angenehmes,
beziehungsweise was ich immer wieder herausgestellt habe bei systematischer Softwareentwicklung.
Man möchte schon, je nach Fehlerart unterschiedlich reagieren können.
Das heißt, man möchte irgendwie die Fehler typisieren.
Man möchte also wissen, in dem Fall kann ich
das machen, im anderen Fall kann ich nichts machen.
Das geht halt nur, wenn man die Fehler irgendwie typisiert.
Ja, und dann ist es auch schon relativ leicht,
jetzt den Schritt zu Java zu machen.
Auch Java hat ein Acception-Konzept, inspiriert von so diesen
ganzen Sprachen wie CLU und Eifel und ähnlich.
Und Ich habe eben gesagt, man möchte Exceptions typisieren.
Da habe ich da sozusagen, dass sie einen Typ haben,
damit man abhängig vom Typ unterschiedlich reagieren kann.
Naja, ihr wisst ja, Klassen in Java sind Typen, definieren einfach Typen.
Das heißt, was man in Java einfach gemacht hat,
hat gesagt, naja, Exceptions sind Exemplare von Exception-Klassen.
Und davon gibt es eine ganze Menge vordefinierte Exception-Klassen.
Es gibt aber auch die Möglichkeit, eigene Exception-Klassen zu definieren.
Das werden wir uns vielleicht ein bisschen noch angucken,
auch in den Übungen.
Da kann man tatsächlich dann zusätzliche Informationen noch mit reinstecken
in die Fehlermeldung oder ähnliches
Aber das ist erstmal ja ganz wichtig.
Acceptions sind Objekte in Java. Das heißt,
ich kann tatsächlich mir hier mal so eine Acception erzeugen.
New, illegal, argument, acception. Zack,
da habe ich sie schon. Unglaublich. Man kann sich
einfach eine Acception erzeugen. Es ist nicht weiter schwierig,
weil illegal Argument Acception, das ist so eine Acception,
die man gerne schmeißt, wenn man einen ungültigen Parameter übergeben bekommt.
Also insbesondere bei so APIs, bei so Bibliotheksroutinen,
wenn man da was übergeben muss.
Und der Parameter ist nicht gültig, dann kann die API
dann die API-Methode dann reagieren mit Elegal Argument Acception.
Das ist das Ding, kann ich
mir hier drüber legen
Habe also jetzt hier mal eine illegal Argument Acception.
Und die kann ich auch inspizieren, ja. Die hat
hier irgendwie verschiedene Throwable, Cores, Deck, Trays,
Data, Detail Message, da ist jetzt nicht viel drin.
Ja, und ich kann tatsächlich an so einem Exemplar auch wieder alles Mögliche aufrufen.
Also hier ist nix, hier ist nix von Throwble,
da gibt es aber so ein paar Sachen, so Get Cores,
Supress, Stack Trace Element, was auch immer.
Also da kann man einiges machen. Also einiges an Informationen
, an diesem Fehlerobjekt abfragen. Okay,
wenn ich diese Exception Die habe ich jetzt erzeugt,
aber das ist noch keine Exception in dem Sinne,
dass sie jetzt wirklich aufgetreten ist.
Erst wenn ich so eine Acception werfe,
dann passiert irgendwas mit dem System, mit der Virtual Machine.
Also ich kann tatsächlich jetzt hier einfach sagen, Throw EA.
So, ja, dann schmeiße ich das Ding hier durch die Gegend und klack.
Throw. Arror. Das ist ja bitter. Hm,
das kann ja wohl nicht wahr sein.
So, ach, na gut. Simikono haben vergessen.
Okay. Und dann kommt tatsächlich hier diese Acception und Haut
voll durch und sagt hier Java-Lang, illegal argument acception.
Okay, die konnte ich hier jetzt auch unqualifiziert weil Illegal Argument Acception zum Sprachkern gehört.
Okay. Soweit, so gut. Was jetzt etwas spannender wird,
und das ist jetzt, glaube ich,
das zeige ich jetzt einfach mal live an diesem Beispiel hier,
also ich habe hier ein bisschen Beispielcode.
Und der ist wirklich sehr einfach gehalten, damit ihr hier
nicht jetzt wieder abgelenkt werdet durch tausend andere Sachen.
Das ist sehr abstrakt. Also zuerst mal haben wir hier
einen Dienstleister. Und dieser Dienstleister ist sehr einfach.
Klaas Dienstleister hat eine Operation, sehr generisch,
Operation 1. Und macht hier nichts weiter als Throne-New-Erlegal-Argument.
Ob das, was ich gerade gemacht habe
Okay, also wenn ich das jetzt aufrufe, dann fliegt mir deine Exception entgegen, würde ich mal vermuten. Okay,
der Klient sieht so aus, auch relativ einfach,
wir haben eine Main-Methode,
da wird hier ein Exemplar der eigenen Klasse erzeugt,
also es ist eine statische Methode, wisst ihr ja,
hier wird ein Exemplar erzeugt und dann kann ich an diesem Exemplar jetzt,
benutze Dienstleister aufrufen und hier benutze Dienstleister, ist hier eine Methode,
die macht nichts weiter als ein Dienstleister-Objekt zu erzeugen,
also von der anderen Klasse und dann die Operation 1 aufzurufen.
Sehr einfach. Ja. Gut, wenn ich jetzt hier das Main aufrufe,
dann Sack kommt hier genau dieser Fall, dass an der Exception auftritt.
Hier ist also diese Exception geworfen worden an dieser Stelle und entsprechend ist das dann durchgeschlagen.
Wenn man sich jetzt hier das anguckt, was hier passiert,
dann sieht man irgendwie hier at Client Main, Dienstleister-Operation,
da sieht man richtig den Aufruf, die Aufrufreihenfolge, welche Methode,
welche aufgerufen hat und an welchen Stellen es dann gekracht hat.
Da sieht man also hier, Operation 1,
benutze Dienstleister Mains, also einmal hier so durchgeschlagen.
Okay, man sieht hier,
ich kann eine Exception einfach erstmal werfen,
also ein Exemplar erzeugen,
ist das eine
Und dann das Ding durch die Gegend werfen, ist das andere.
Das ist also viel gefährlicher, so ein Ding zu werfen,
als es nur einfach zu erzeugen.
Ich kann jetzt an dieser Stelle mal was anderes machen.
Ich schreibe jetzt hier mal Throw, New Java,
IO, IO Exception. Okay. Und hier oben mache
ich jetzt einfach mal eben, kommentiere ich mal aus.
So, Java IO, IO Exception ist auch eine Acception.
Auch vordefiniert, allerdings nicht im Sprachkern,
sondern in dem Paket Java IO
Aber die kann ich ja hier einfach so erzeugen.
Jetzt habe ich aber ein Problem, wenn ich das jetzt übersetze,
dann kommt hier eine Meldung, unreported Exception, Jabba,
euer Goal Exception, must be cord,
or declaired to be throne.
Aha. Wir haben also anscheinend zwei unterschiedliche Arten von Exceptions in Java.
Diese hier oben, das war eine Uncheck-Acception. Dieses ist
aber eine Check-Acception. Wir werden uns gleich genauer angucken,
was das bedeutet. Aber solche Check-Acceptions haben eine Eigenschaft,
sie erzwingen, dass man sie behandelt. Das heißt,
diese IO-Exception, wenn ich die hier werfe,
Dann kann ich das nicht einfach tun,
sondern es ist ja klar, wenn jetzt zum Beispiel,
nehmen wir mal an, diese Operation 1 greift auf Dateisystem zu.
Sie liest irgendwelche Daten aus einer Datei.
Jetzt kann sie aber auf die Datei nicht zugreifen.
Dann kommt halt so eine IO-Exception. Okay. Jetzt kann es aber ja sein,
dass der Klient, der mich hier benutzt, der muss ja wissen,
was auf ihn zukommt, dass eventuell dieser Dienstleister aufs Dateisystem zugreift.
Also was muss man tun? Man muss hier oben,
also entweder kann ich jetzt hier,
ich jetzt nicht selbst das Ding werfen würde,
sondern vielleicht eine andere Methode noch aufrufen würde,
die den Code, diese Acception werfen könnte,
kann ich einen Try-Block drumherum setzen, wenn wir uns gleich ansehen.
Aber ich kann auch erst mal jetzt hier sagen, einfache Lösung,
ich deklariere einfach mal, dass ich die Acception hier werfe.
Java, IO, IO, Acception. So,
dann lässt sich der hier übersetzen. Dann ist der Compiler
zufrieden. Hier fliegt zwar eine Checkd Acception,
aber die Operation sagt ihrem Klienten, hallo,
wer immer mich benutzt, das kann dir passieren,
dass du so eine Acception um die Ohren
geknallt kriegst
Damit ist sozusagen hier der Dienstleister fein raus. Der hat
jetzt gesagt, der mich benutzt, da kann es knallen.
Gut. Sieht gut aus. Dann haben wir hier jetzt
aber gesehen, weil wir den Dienstleister übersetzt haben,
ist der Klient jetzt auch wieder nicht übersetzt.
Wenn ich das jetzt übersetze, knallt es hier.
Und jetzt haben wir die gleiche Situation wie eben nur etwas anders.
Jetzt sagt der hier, Dienstleister, Operation 1,
der hat gesagt, es kann eine iO-Rezeption kommen.
Eine Check-Exception, der hier macht jetzt nichts damit.
Der ruft die einfach auf und ist hilflos,
was er da tun soll.
Ja, wir sind jetzt mal ganz kreativ.
Wir schreiben hierhin Throws.
Aber, I o, I o, Acception.
Weil wir auch hier an dieser, ups, an dieser Stelle immer toll,
an dieser Stelle nicht wischen, nicht wissen,
wir machen es mal ein bisschen breiter, beziehungsweise Engrid.
So. Auch an dieser Stelle wissen wir nicht, was wir tun sollen.
Wir wissen nicht, wie können wir jetzt damit umgehen?
I o Exception, was soll ich damit anfangen?
Ich gebe es mal an meinen eigenen Klienten weiter.
Lässt sich natürlich jetzt übersetzen die Methode,
aber jetzt haben wir das Problem da oben.
Ja, weil der wieder sagt, oho, so,
und jetzt könnten wir hier einfach die Acception auch noch weiterreichen.
Das würde heißen, wenn wir das Main-Programm aufrufen,
kann es sein, dass eine Acception um die Ohren fliegt
Wir können aber auch jetzt mal was tun an dieser Stelle.
Ich würde ja auch demonstrieren, die man mit solchen Exceptions umgeht.
Das eine ist jetzt, wir haben gesehen, wie kann man das signalisieren?
Wie kann man Exceptions werfen? Naja, man kann eben
halt checken und Uncheckt-Exceptions werfen. Und wenn es eine Checkt-Exception ist,
dann muss halt jeder Klient das Ding irgendwie behandeln.
Also entweder muss das weiterreichen an deine eigenen Klienten oder muss es behandeln.
Also kann man jetzt hier sowas schreiben wie Try?
Dann wird hier tatsächlich dieses ausgeführt. Und dann sage ich
hier, Catch, Ja, Java, IO.
Hi, O Exception. So. Und dann kann ich jetzt hier
irgendetwas tun. Ich mache jetzt hier mal irgendwas Sinnloses,
nur damit ihr es mal seht,
dass überhaupt irgendwas passiert, System R, Printeln,
also zumindest auf den Error-Stream schreibe ich jetzt raus, das ging schief.
Toll. So, funktioniert erstmal so, was ich jetzt also mache,
ist, ich versuche diesen Klienten zu benutzen, diesen Dienstleister zu benutzen.
Und wenn es schief geht, fange ich hier,
genau, dann weiß ich, ich weiß ja,
dass diese Acception kommen kann, weil das ja hier deklariert ist.
Also muss ich hier damit umgehen und wenn ich das
so schreibe, dann ist es auch übersetzbar.
Jetzt ist der Compiler wieder zufrieden, weil er weiß,
naja, der Programmierer hat sich da jetzt irgendwie Gedanken gemacht, dass da was passieren muss.
Das heißt, wenn ich jetzt meine Main-Methode ausführe,
würde ich hoffen, dass jetzt tatsächlich hier eine Meldung rauskommt,
wenn ich das jetzt tue, Client Main, Ah,
zack, dann kommt ja einfach nur die Meldung raus, das ging schief.
Logisch, weil die Exception ja immer noch geworfen wird.
Aber sie ist jetzt behandelt worden. Also keine Acception mehr,
die das Ganze beendet, sondern das ging schief.
Ja, soweit so gut. Was man aber auch machen kann,
ich habe jetzt hier gezeigt, dass ich eine Check-Exception behandeln muss.
Aber ich könnte ja auch hier mit was anderem rechnen.
Zum Beispiel, ich weiß zufällig,
dass in dem Dienstleister irgendwas mit so eine Taschenrechner-Applikation,
stellen wir uns einfach jetzt mal vor.
Und diese Taschenrechner-Applikation, da kann der Benutzer auch eine Division durch Null machen.
Das führt zu einer Aerosmatic Exception. Also ich kann hier
schreiben, Harris metic exception. Ai. Und dann kommt
hier auch erst mal wieder irgendeine Meldung raus, was weiß ich.
Dann nehmen wir jetzt hier mal was anderes.
Da sagen wir Division durch Null. So, nehmen wir
das mal an. Egal, das lässt sie sich jetzt
hoffentlich auch übersetzen. Ja, ich kann also tatsächlich auch Exceptions,
die nicht gecheckt sind, also die ich nicht behandeln muss.
Ich kann die trotzdem behandeln
Die sind, wenn sie erst mal stattfinden,
wenn sie erst mal geworfen sind, sind sie eigentlich gleich.
Also dieses Checkt und Uncheck ist einfach eine statische Sache beim Quelltextschreiben.
Zur Laufzeit werden die genau gleich behandelt, sind einfach Exceptions,
die da irgendwie auch behandelt werden können.
Ich kann das tatsächlich sowas auch hinschreiben hier.
Okay, damit sind wir schon ziemlich weit gekommen.
Also Exception ist ein Exemplar in der Acception-Klasse. Wir haben
auch gesehen, dass man hier jetzt den Typ abfragen kann.
Ich kann also für jeden Typ einer Exception,
kann ich einen eigenen Catch-Blog schreiben.
Und was wir jetzt gesehen haben, es gibt diese geprüfte
und die ungeprüften Exceptions
Und was ich versucht habe, jetzt mit meiner,
mit meiner, mit meinem Aufgalopp zu anfangen, euch darzustellen,
ist, diese verschiedenen Arten von Acceptions, diese beiden Kategorien,
die passen ziemlich gut zu unserer Unterscheidung zwischen Umgebungsfehlern und Entwicklungsfehlern.
Also hier, die geprüften Exceptions, die müssen ja deklariert werden.
Da musste man also damit umgehen. Das sind also
primär Umgebungsfehler. Sowas wie Server not active Acception,
IO Exception, Timeout Acception, das sind alles so Dinge,
wo man schon so am Namen sieht.
Und es geht irgendwie darum, irgendwo woanders ist was,
wo ich nicht die Kontrolle drüber habe,
IO Exception, Dateisystem, Timeout,
irgendwo im Netz oder so
Die Ungeprüften müssen nicht deklariert werden. Die Ungeprüften,
das sind genau die, die man gut nehmen kann für Entwicklungsfehler.
Also Array Index out of bounds,
man wird nicht bei jedem Raid-Zugriff einen Try drum schreiben.
Try greift auf Array zu, wenn es schief geht,
catch mal die Exception, dann würde man durchdrehen.
Macht keinen Mensch. Da ist eine Frage. Du hast
eine Frage. Oh, da ist der Ponter, ja.
Ja, klar. Ich setz dich schon raus, einfach
die Zeichnung jetzt aus. Warum? Denn ich wohl,
der Array-Ponter, das ist das Ding
Ah, okay. Also er fragt, warum heißen die Exception?
Warum heißen die nicht Fallt oder Error? Ja,
also das ist, das ist eine interessante Frage.
Warum heißen die Exception? Es sind halt Ausnahmen vom Normalfall
. Das ist das, was da gemeint ist.
Es müssen nicht unbedingt Fehler sein. Es können auch einfach nur,
ja, Abweichungen von einem Normalfall sein,
wo man trotzdem mit einer alternativen Behandlung etwas tun kann.
Das heißt also, das müssen nicht unbedingt alles Fehler sein.
Auch wenn wir jetzt hier von Umgebungsfehlern reden,
dann kann es ja auch sein, dass ein etwas,
was in der Umgebung nicht stimmig ist,
in unserer Software vielleicht behandelt werden kann
Und die haben halt das, ich vermute mal,
einfach allgemeiner gewählt, diesen Begriff im Sinne von Acception.
Es ist halt eine Ausnahme von dem Normalfall.
So ist es vermutlich gemeint. Sonst noch Fragen. Nicht,
dass ich irgendwas übersehe, aber ich glaube,
du sitzt ein bisschen im toten Winkel hier.
Das tut mir leid. Du musst dich weiter da reinsetzen
. Okay, also das sind hier diese Acceptions,
die man eben halt nicht ständig irgendwie überprüfen will.
Aber da ganz wichtig, geprüft und ungeprüft ist statisch zu verstehen.
Also nur zur Übersetzungszeit relevant. Zur Laufzeit sind die alle,
führen die dazu, dass in der Virtual Machine ganz viel Spezialbehandlung stattfindet.
Okay, das Ganze mache ich jetzt noch mal kurz zusammengefasst
Mit Throw schmeiße ich eine Acception. Ja,
haben wir gesehen. Ich kann eine Acception erzeugen mit New
und dann kann ich die einfach werfen. Throws,
damit deklariere ich an meiner Schnittstelle, dass bei mir was passieren könnte,
damit ein Klient von mir weiß, muss ich darauf aufpassen.
Try ist dieses Einleiten eines Blogs,
in dem das Auftreten in der Acception erwartet wird.
Catch ist dann das Einleiten des Blogs, in dem das behandelt wird.
Wird auch gerne Exception-Händler genannt, dieser Blog.
Davon kann es mehrere geben, haben wir gesehen.
Also Trial, Catch, Catch, Catch.
Und dann gibt es noch einen Finaly-Blog in Java.
Das ist ein Blog, der immer ausgegeführt wird
Wenn der Tri-Block einfach nochmal durchläuft oder wenn einer der Catchblöcke ausgeführt wird,
in beiden Fällen soll, wird alles das ausgeführt, was im Finaly-Block steht.
Also wenn man zum Beispiel auf eine Datei zugreift,
dann will man das in jedem Fall, diese Datei wieder geschlossen wird,
bevor das System dann irgendwie seine Fehlermeldung ausgibt.
Da ist eine Frage dazu. Ob man was machen kann?
Vielen Dank. Ob man was machen kann, dann will ich nichts sagen. Achso,
ja, also er sagt, kann man das nicht einfach so machen,
dass man das hinter den letzten Catchblocks schreibt, also Try,
Catch, Catch, Catch und dann am Ende steht,
ja, da haben sie halt einfach gesagt, na,
wir benennen es auch richtig,
dann steht da eben halt das Finally.
Ja, das ist einfach nur ein bisschen expliziter.
Kann man darum streiten, ob das jetzt irgendwie elegant ist.
Da war noch eine Frage, glaube ich, oder?
Da hinten, ja. Ich glaube, das sind drei und zwei,
glaube ich. Das war das Date, das war das.
Ich verstehe nicht ganz schlecht. Kannst du ein bisschen lauter sprechen? Also,
wenn man jetzt den Zweifel für die Fahranreiter fahren,
wo wir da hingen, der Name,
diesen ganzen Köpfchen hinausgeführt werden und der Feindung,
wenn wir trotzdem hingen, oder sonst was,
der immer ausgeführt wird.
Deswegen hat er das
Sehr richtige, sehr richtige Einwand. Also der Kommitone sagt,
es ist ja so, dass in dem Trial-Block auch ein Return-Statement stehen kann.
Und wenn dann das Return ausgeführt wird,
muss natürlich erst noch der Finaly-Block ausgeführt werden,
bevor dann wirklich das Return ausgeführt wird.
Also das ist ein weiterer Grund, warum der Finaly-Block schon
wichtig ist und auch explizit markiert ist als etwas,
was in jedem Fall passieren sollte.
Okay, dann haben wir das alles eigentlich schon gesehen.
Grundsätzlich ist es zum Auslösen einer Acception,
eine Ausnahme ist ein Signal, dass irgendwas schiefgelaufen ist.
Eine Ausnahme auslösen bedeutet,
eine Ausnahmebedingung zu signalisieren
Und das führt dazu, dass abgebrochen wird in der
auslösenden Methode und zurückgesprungen wird zur aufrufenden Methode.
Das wird weitergemacht, also weiter propagiert, bis sie abgefangen wird.
Bis zu dem Punkt, wo irgendwo tatsächlich ein Catch steht. Also Abfang heißt,
sie zu behandeln, dann kann man irgendwas machen,
kann man vielleicht wieder den Zustand herstellen,
der vorher war und wenn wir uns nochmal weitermachen.
Man kann aber auch einfach organisiert abbrechen, mit Aufräumen arbeiten.
Wenn man nichts tut in keiner Methode,
dann bricht sich das so seinen Weg bis zur Main-Methode.
Die Main-Methode ist immer der Einstieg eines Java-Programms. Okay,
wenn sie da durchfällt, dann gibt es halt da einfach
einen Abbruch
Wann löst man eine Exception aus? Na gut,
wenn Methode ihre Aufgabe nicht erfüllen kann,
also bestimmte Bedingungen sind nicht erfüllt, Klient hat seine Vorbedingungen
nicht erfüllt, dann kann eine Exception ausgelöst werden.
Bevor man die Exception auslöst, muss man erst zum Objekt erzeugen.
Und dann erst durch das Werfen wird wirklich irgendwas gemacht.
Dann haben wir gesehen, die geprüften Exceptions,
die muss man im Kopf deklarieren.
Also wenn die irgendwie auftreten können, dann muss man das auch sagen.
Wenn eine Methode A eine andere Methode B benutzt,
die eine geprüfte Exception deklariert,
dann müssen in der Methode A Vorkehrung für den Fall
getroffen werden,
dass die Exception wirklich auftritt
So, jetzt gibt es zwei Möglichkeiten für die Methode,
A, sie kann dies entweder behandeln oder das weiter propagieren.
Das ist nochmal die allgemeine Struktur, Try, Anweisungen,
die irgendwas auslösen könnten, Catch,
davon kann es mehrere geben und dann das Finaly,
die Anweisungen, die in jedem Fall ausgeführt werden sollen.
So, jetzt gibt es aber noch was. Dieses Differenzieren von
verschiedenen Außennahmen, das kann man noch ein bisschen genauer betrachten.
Das ist noch eine Frage erstmal. Ja, Sport,
Interview, aber da würden wir noch ein paar Einstellungen zu Feuer bauen.
Ich glaube, es ist verpflichtend, mindestens einen Catchbock zu
schreiben bei I'm a Try. Da bin ich jetzt aber
nicht 100% sicher, aber ich bin mir aber ziemlich sicher.
Wir können es gleich mal ausprobieren
Okay. Die Sache, mit dem differenziert behandelt. Ich habe jetzt gesagt,
es gibt verschiedene Arten von Excessions und man kann verschiedene Catchblöcke schreiben.
Und für jeden Catchblock wird der Typ,
der Exception spezifiziert, die er behandeln soll.
Wichtig ist aber, es wird immer maximal ein Exception-Händler wirklich ausgeführt.
Wenn man mehrere hat, dann wird der erste passende gewählt.
Wie kann das sein, dass man mehrere passende haben kann?
Naja, ein Catchblock passt zu einer Exception.
Wenn der Typ der erzeugten Exception gleich oder ein Subtyp ist.
Aha. Das heißt also, wenn wir uns jetzt das Thema angucken,
es gibt die auf der nächsten Folge kommt die gleich.
Es gibt eine Exception-Hierarchie und ich kann tatsächlich so überschreiben,
wie Catch, Exception, E. Und dann mache ich hier
auch mal erst mal wieder nichts besonders Schlaues.
Ich probiere das mal eben hier so runter.
So. Jetzt ist es so, eine IO-Exception ist eine Acception.
Auch eine Arismatic Exception ist eine Acception. Das sind Subtypen. Das heißt,
jetzt ist es so, wenn es eine iOxception ist,
dann wird von oben nach unten einfach geguckt, welches ist der Speziellste.
Also wenn es eine iOxception ist, wird dieser Exception-Händler genommen.
Bei einer Aeros-Metting-Exception wird dieser genommen. Und wenn es
irgendeine andere Exception ist, dann wird dieser Exception-Händler genommen.
Aber es passen ja tatsächlich mehrere. Also wenn tatsächlich jetzt
eine iOxception kommt, könnte der genommen werden oder der hier.
Und das lässt sich erst mal übersetzen, wir können das mal eben zeigen.
Und jetzt kann ich aber auch mal was anderes zeigen,
wenn nämlich man das andersrum schreibt,
also ich setze mal nach oben diesen Exception hier,
dann wird da oben, wenn alle Exceptions weggesaugt,
und da sagt der Compiler, jetzt ist doch Schwachsinn,
our exception has already been court.
Ja, weil Io-Exception ist ein Subtyp von Exception
und dann sagt er, das ist ja sinnlos.
Das ist verschatteter Code quasi. Also würde man das tun,
legst wieder nach unten schreiben im Sinne von die allgemeineren Exception-Händler
sind natürlich weiter unten und die speziellen sind weiter oben.
Jetzt können wir auch mal was anderes ausprobieren. Wir können mal
hier die Dinge auskommentieren. Mal sehen, was passiert,
wenn wir das jetzt wegnehmen, lässt sich das übersetzen.
Try without catch. Also das geht nicht.
Offensichtlich nicht. Gut
Ja, was ihr selber nachlesen solltet, ist,
in Java 7 gibt es jetzt einige Verbesserungen beim Exception-Handling.
Das will ich hier jetzt nicht einführen. Es gibt sowas
wie ein Multi-Catch. Ich kann tatsächlich in einem Catch mehrere
Exceptions angeben. Ich kann Exception, die ich gefangen habe,
sozusagen bearbeitet habe, kann ich erneut werfen.
Und es gibt einen Try with Resources,
das werdet ihr auch in den Übungen kennenlernen,
wenn ihr da ein bisschen euch mit den Exceptions beschäftigt.
Hier ist nochmal diese Exception-Herarchie. Und hier sieht man jetzt,
diese Klassen sind vorgegeben, im Collections-Framework.
Quatsch, in der Java-Bibliothek allgemein. Und dann ist es so,
wenn ich eine eigene ungeprüfte Exception deklarieren will,
eigene Klasse, dann leite ich die von Run-Time-Acception ab.
Wenn ich aber eine geprüfte Exception erzeugen will, dann leite ich die von Exception ab.
Alle Subtypen von Error und alle Subtypen von Run-Time Error sind Run-Time-Acception,
sind ungeprüfte Exceptions. Nur die direkt abgeleitet von Exception sind, das sind dann Check-Exceptions. Okay,
hier ist nochmal so ein Beispiel,
wie meine eigene Exception-Klasse definieren kann,
also zum Beispiel so eine Range-Acception, irgendwie,
ich habe so eine Sammlung und will irgendwie,
da ist ein falscher Bereich angegeben worden,
das möchte ich irgendwie konsequent melden können mit einer eigenen Exception,
dann kann ich tatsächlich die von Run-Time-Acception erben lassen,
dann ist es
auch eine Uncheck-Exception
Gut, das hier nochmal so als Übersichtsinformation.
Was ich schon anfangs gesagt habe, man möchte eigentlich,
dass die Fehlerbehandlung nicht alles zuschüttelt. Und das kann man
tatsächlich hier auch sehen, dass das jetzt hier wunderbar funktioniert.
Wenn man jetzt hier sieht, Try, das ist das Eigentliche,
was gemacht wird und der ganze Fehlerbehandlungscode ist jetzt ausgelagert nach unten.
Das ist üblicherweise nicht in dem Verhältnis. Meistens hat man irgendwie
ein bisschen mehr Code und dann am Ende so ein bisschen Fehlerbehandlungscode,
aber es ist zumindest klar getrennt.
Das heißt, der Fehlerbehandlungscode wird zu einer,
zu einer Fußnote. Das ist eigentlich sehr viel angenehmer für
die Softwareentwicklung
Okay, das war jetzt schon alles,
was ich zur Fehlerbehandlung sagen wollte und Exceptions.
Also sehr unterschiedliche Fehlerursachen. Was wir jetzt hier speziell machen,
das ist sozusagen so ein Spezialbegriffsbildung in SE2,
wie werdet ihr sonst in keinem Lehrbuch finden,
ist diese Unterscheidung in Umgebungs- und Entwicklungsfehler.
Wir glauben, dass diese Unterscheidung nützlich ist. Insbesondere glauben wir,
dass die gut dazu passt, dass man für Entwicklungsfehler eher
Uncheck Exceptions nehmen sollte und für Umgebungsfehler eher die Check Exceptions.
Das kann man eigentlich weitgehend so beobachten,
dass das immer passt
Da gibt es eben halt dann dieses Konzept von Exceptions in Programmiersprachen.
Und in Java sind halt die Sections einfach Exemplare von Exception-Klassen.
Ja, und man kann für jede geworfene Exception kann man
da tatsächlich einen Acception-Händler programmieren, der dann die Acception abfängt.
So, gibt es noch Fragen zu Acceptions soweit?
Das sieht nicht so aus, dann machen wir fünf Minuten Pause.
So, jetzt kommen wir zu einem ganz anderen Thema.
Now, for something completely different. Jetzt geht es
um Modularisierung
Und wir werden uns erst mal nur technisch ansehen,
welche Unterstützung für Modularisierung bietet denn Java oder auch Programmiersprachen
allgemein und werden dann auch in der Vorlesung über Klassenentwurf,
die wird so in vier, fünf Wochen sein,
werden wir das auch noch mal ein bisschen genauer uns ansehen,
das Thema Modularisierung.
Hier geht es jetzt erst mal nur darum, was ist das überhaupt?
Was ist überhaupt ein Modul? Das ist eigentlich ein Begriff,
der aus der klassischen imperativen Programmierung kommt. Das heißt,
früher hatte man auch schon das Problem,
dass man große Softwaresysteme hatte Zu meiner Zeit,
als ich zum Beispiel doch studiert habe,
da habe ich an einem größeren Compiler geschraubt, als ständiger Hilfskraft.
Und der war tatsächlich so groß und die Übersetzung hat so lange gedauert,
dass man immer, wenn die Übersetzung angestoßen wurde,
erstmal einen Kaffee in Getränken gehen konnte,
ungefähr eine Viertelstunde lief da die Übersetzung.
Also das dauerte einfach so lange, bis das übersetzt war.
Das ist für heute für euch heute wahrscheinlich völlig unvorstellbar,
dass man irgendwie so lange warten muss,
aber ganz früher war es ja noch mit Lochkarten viel schlimmer,
aber das kenne ich selbst nicht mehr.
Also dann diese Notwehr, da hat man gesagt,
na gut, wir müssen ja nicht eigentlich alles neu übersetzen,
Bloß weil wir jetzt eine Änderung an einer Stelle gemacht haben.
Deswegen wäre es schlau, wenn wir irgendwie hier den Quelltext in Übersetzungseinheiten zerlegen könnten.
Das heißt, einer der ersten Motivationen für Modularisierung war,
ich möchte Teile getrennt übersetzen können, ohne alles neu übersetzen zu müssen.
Dann war es aber auch so,
dass man sehr stark darüber diskutiert hat,
wie zerlegen wir denn größere Software in kleinere Bestandteile?
Das war so in den 70er Jahren schon eine Diskussion.
Und so an 60er, 70er Jahre war so eine
relativ vorherrschende Meinung, wenn wir Software entwickeln,
dann machen wir
das top down
Wir machen einen Top-Down-Entwurf. Ja, und dann machen wir
Step-wise-Refinement, also schrittweise Verfeinerung. Wir haben also unser Programm,
im Groben, dann zeigen wir das in zwei Unterteile und dann,
und dann verfeinern wir immer mehr, bis wir irgendwann den Code aufschreiben können.
Das war eine sehr idealisierte, sagen wir, Sicht auf Softwareentwicklung,
das passiert fast nie. Was auch damals diskutiert wurde,
war, dass man das entlang des Kontrollflusses macht. Also,
wenn mein Programm dies und dies und dies und das tut,
dann macht es zuerst das, dann das,
dann das, dann sind das auch die Module.
Ja, er ist der Schritt, dann der Verarbeitungsschritt,
dann der Verarbeitungsschritt.
Solta sollte man
den Quelltext auch zerlegen
Also Entlang des Kontrollflusses. Das ist heutzutage, sagen wir,
ein bisschen anders. Heutzutage macht man Modularisierung primär im Sinne des Geheimnistprinzips.
Das werden wir uns noch ansehen, was das genau bedeutet.
Aber letztendlich wollen wir mit dem Geheimnisprinzip Entwurfsentscheidungen kapseln.
Und zwar so kapseln, dass ein Klient nicht davon betroffen
ist, wenn wir diese Entscheidung ändern.
Wir wollen also flexibel bleiben und deswegen kapseln wir Entwurfsentscheidungen,
verstecken sie hinter einer Schnittstelle und haben dadurch mehr Flexibilität
Und das ist auch das, was in der Objektontierung relativ,
sagen wir, etabliert ist. Ihr kennt es schon im Kleinen für Klassen,
dass wir euch immer sagen, bitte macht eure Exemplarvariablen im Private,
damit man nicht direkt auf sie zugreifen kann als Klient,
sondern dass man immer über die fachlichen Methoden,
Operationen auf die Daten zugreift, weil man dadurch mehr Kontrolle hat,
was da passiert und dann auch mehr Möglichkeiten hat,
Entwurfsentscheidungen zu ändern.
Im Kleinen ist das oft nicht so gut nachvollziehbar.
Ihr müsst es einfach erst mal akzeptieren,
dass das möglicherweise gut ist und ihr werdet das im Laufe
eures Softwareentwicklungsdaseins dann an sehr vielen Stellen noch zu schätzen wissen
Gut, ganz einfach erstmal die Grundidee, was ist ein Modul? Modul ist eine statische Einheit des Quellentextes,
also das ist relativ wichtig, dass man nicht jetzt Module mit Objekten gleichsetzt, sondern Module sind statische Einheiten des Quellentextes. Und so ähnlich wie in der Klasse,
ja auch, wir haben ja gesagt,
eine Klasse hat eine Schnittstelle und eine Implementation,
ist das bei Modulen auch, dass man das unterscheidet,
aber die Module haben es eigentlich zuerst getan, quasi,
die Klassen kamen dann später und hier ist das so ein bisschen angedeutet,
so blau ist immer so die Schnittstelle und mittendrin,
dieses Geld ist so der Kern, dass die Implementation,
die ist so ein bisschen gekapselt und man sieht hier,
dass Module sich gegenseitig benutzen, man sieht zum Beispiel immer,
dass die Pfeile starten in dem gelben Teil,
weil natürlich da steht der Code drin, echter Code,
Rümpfe von Methoden,
die dann andere Module benutzen und die anderen Module werden immer nur,
die Pfeile gehen immer nur bis an diese blaue Kante ran.
Das heißt, es wird immer nur die Schnittstelle eines anderen Moduls benutzt und
da drin wird dann wieder möglicherweise eine weitere Schnittstelle benutzt.
Also Module benutzen sich gegenseitig ausschließlich über ihre Schnittstellen.
Die werden wir gleich noch ein bisschen differenzieren.
Weiteres Konzept ist aber auch, Module können Module enthalten.
Das heißt, ich kann das auf mehreren Schichten, auf mehreren Ebenen schachteln
Das ist bei großen Systemen fast unumgänglich, dass man das macht,
weil man sonst einfach das nicht handhaben kann.
Die schiere Menge, also so große Java-Systeme, haben locker mehrere tausend Klassen.
Und deswegen gibt es dann sowas wie Schichten, Subsysteme,
Pakete in Java, werden wir uns noch ansehen heute.
Und die können immer aneinander enthalten sein.
Und wenn Module wieder in Modulen enthalten sein können,
dann gibt es so Überlegungen, wie, was bedeutet das denn,
dass das Modul da drin ist?
Also zum Beispiel hier sehen wir,
sind so zwei kleine Module auf einer Ebene tiefer möglicherweise in einem größeren Modul geschachtelt.
Und die sind hier tatsächlich privat innerhalb dieses Moduls.
Die sind von außen
gar nicht einsehbar
Sieht man aber, hier sind so Module,
die sind irgendwie an der Schnittstelle definiert.
Also anscheinend muss ein Klient, der das benutzt,
auch vielleicht diese Typen hier oder diese Module kennen.
Okay. Soweit so gut, also diese Schachtelung und
dann kommt man nämlich genauso zu Benennungsfragen und zu Schutzfragen
und die werden wir uns jetzt ein bisschen ansehen.
Aber ich muss erst noch mal differenzieren. Ich hatte gesagt,
Schnittstelle kennt ihr, was ihr bisher nur kennt,
ist eigentlich die Exportschnittstelle. Das heißt, Dienstleistungen,
die ein Dienstleister seinen Klienten zur Verfügung stellen will, die werden an der Exportschnitte bekannt gemacht. Also ihr kennt das von den Operationen,
Also man kann auch Typen exportieren, man kann vielleicht sogar auch Konstanten definieren,
das kennt ihr aus Interfaces in Java und alles,
was gekapselt ist, also modulweite globale Variablen,
lokale Prozeduren, lokale Typen und die Implementationen der exportierten Operationen,
das alles ist gekapselt, gehört zum Implementation,
zum internen Teil und an der Exportschelle wird eben halt das bekannt gegeben,
was nach außen sichtbar ist.
Was nicht so verbreitet ist als Verständnis ist,
aber genauso wichtig ist, dass es sowas geben muss wie eine Importstückstelle,
also wenn ein Modul eine bestimmte Schnittstelle hat,
also Dienstleistung seinen Klienten anbietet, kann es durchaus auch sein,
dass es dann aber auch sagt,
damit ich das kann, damit ich meine Dienstleistungen erfüllen kann,
brauche ich selber das und das und das.
Das ist die sogenannte Importenstelle. Also das,
was ein Modul braucht, um funktionieren zu können.
So klassisches Beispiel Stereo-Anlagen. Früher waren die so in mehreren Komponenten,
gibt es heute immer noch so Verstärker, CD-Spiele, DVD-Spiele, was weiß ich.
Tuner, da ist es auch so ein CD-Spieler,
der hat irgendwie als Schnittstelle, man kann ihn aufmachen,
kann eine CD einlegen, wieder zumachen,
dann kann er mit der Fernbedienung bedienen.
Aber das ist sozusagen die Schnittstelle, die er anbietet.
Aber er selber braucht natürlich auch ein Incoming-Interface. Er braucht also
irgendwie möglicherweise Strom. Das ist schon mal das Einfachste,
wo du auch einen Verstärker, der muss ja erstmal irgendwie was bekommen,
damit er was verstärken kann.
Also braucht er ein Signal. Das sind also diese Sachen,
das ist die sogenannte Import, den Chat.
Die kann man genauso deklarieren. Was brauche ich,
um funktionieren zu können? So, was wir jetzt uns
ansehen wollen für auch jetzt konkret abgebildet auf Java,
ist, was leistet dieses Modulkonzept denn überhaupt?
Naja, zum einen führt es einen Namensraum ein, also Namespace.
Das findet ihr in vielen Programmiersprachen, das Konzept von sogenannten Namespaces.
Das ermöglicht mir Programmeinheiten zu gliedern und zu benennen.
Und zwar auch eindeutig zu benennen
Große Mengen von Klassen und Methoden hat,
dann wäre es blöd, wenn alles eindeutig benannt sein müsste.
Genau Module liefern auch Namensräume im Sinne von,
ich kann Dinge genauso benennen wie in einem anderen Modul,
ohne dass es eine Kollision gibt.
Und außerdem ist es auch ganz wichtig für den Zugriffsschutz,
also ein Modul hat immer Elemente verborgen,
also etwas ist im Modul gekapselt und gezielt kann ich dann
bestimmte Dinge exportieren.
Also Zugriffsschutz und diese beiden Aspekte sehen wir uns jetzt mal
an und zwar zuerst mal für Java für Klassen
Klassen and Java, aber auch erstmal Klassen-Allgemeinklassen von Objekten mit denen
Programmiersprachen. Kann man auch als Module ansehen. Ihr habt
ja Klassen schon ausführlich kennengelernt. Also wisst ihr,
Klassen sind logische Einheiten des statischen Quellentextes. Meistens ist es
in Java so, dass eine Übersetzungseinheit auch eine Klasse enthält.
Damit kann ich eben halt meine großen Systeme in kleinere Einheiten zerlegen.
Kann die auch einzeln übersetzbar machen. Lassen sich mal als einzeln
getrennt übersetzen. Sie definieren eine Schnittstelle mit den Dienstleistungen,
die sie ihren Klienten anbieten. Sie definieren auch einen Namensraum
für ihre Dienstleistung. Ich kann ja in der einen Klasse
die Methode genauso nennen wie in einer anderen Klasse.
Das ist okay, weil das sozusagen ein eigener Namensraum ist,
innerhalb der Klasse. Und sie können eben Teile ihrer Implementation
verbergen. Das heißt, es gibt Möglichkeiten des Zugriffsschutzes
Alles, was Private ist, ist für Klienten nicht zugreifbar.
Das ist schon mal sehr ähnlich. Also Klassen sind in dem Sinne auch Module.
Aber Klassen sind auch mehr, deutlich mehr als Module.
Also wir haben ja schon mitbekommen, sie definieren auch Typen mit Exemplaren.
Also von einer Klasse kann ich Exemplare erzeugen.
Die kann ich dann wieder Polymore verwenden. Das heißt,
man kann in Subtyp-Beziehungen miteinander stehen. Und was außerdem noch
wichtig ist, was ich eben auch schon gesagt habe,
so Module können geschachtelt werden, auch Klassen,
können ineinander geschachtelt werden.
Das ist was, was wir bisher noch gar nicht uns angesehen haben.
Deswegen werde ich jetzt mal ein paar Worte sagen,
zu geschachtelten Klassen in Java.
Die können tatsächlich
beliebig oft ineinander geschachtelt werden
Eine Klasse, die in einer anderen Klasse enthalten ist,
heißt geschachtelte Klasse. Und die heißen tatsächlich,
müssen wir jetzt das genaue Wort auch verwenden, Nested Class.
A Nested Class ist Anyclass, who's declaration occourse,
within the body of another class or interface.
Wenn wir jetzt also wissen, Klassen können in Klassen geschachtelt sein,
dann gibt es natürlich irgendwo die Klassen,
die nicht irgendwo drin geschachtelt sind.
Und das sind die Top-Level-Klassen. Also gibt es entweder Top-Level-Klassen oder
geschachtelte Klassen. Warum hat man überhaupt sowas wie geschachtelten Klassen?
Wer will denn sowas? Wer braucht denn sowas?
Naja, einmal zu besseren Strukturierungen, Verstecken von Details,
werden wir uns gleich ansehen
Aber auch bequemeres Programmieren, das sind dann die anonymen innere Classes.
Das sind dann für Greep-Programmierung interessant. Kommt auch in ein
paar Wochen hier in dieser Vorlesung, wie wir Googies programmieren.
Noch genauer reingezoomt, die geschachtelten Klassen in Java,
die werden unterschieden in vier Arten von geschachtelten Klassen.
Eine davon sind die Statting-Member-Classes und die anderen drei sind sogenannte innere Klassen.
Also Inner-Classes sind nur drei verschiedene und Nested-Classes ist sozusagen der Oberbegriff
für alle. Also eine, hier Statting, Memberclasses,
das sind keine inneren Klassen, sondern das sind nur geschachtelte Klassen.
Diese hier sind ein bisschen speziell, die werden wir uns
noch mal ansehen. Jetzt geht es erst mal darum,
uns, dass wir uns mal einfach das einfachste Beispiel ansehen,
nämlich das klassisch statisch ineinander geschachtelt sind.
Statisch geschachtelte Klassen, deklariert mit dem Schlüsselboard Static im Rumpf
einer umgebenden Klasse. Also ich kann schreiben, Klasse A,
dann habe ich eine Operation, dann kann ich hier schreiben,
Private Static, Class, My Static Member Class.
Ich zeige das mal an etwas konkreteren Beispielen.
Wir nehmen mal hier SR1 Tunes mal wieder.
Ihr kennt das Projekt
Das ist unser Beispiel wieder nur die eine Seite.
Wir haben unsere Titelliste für Musiktitel. Da haben wir hier
diese klasse Titel. Dann gibt es eine Implementation der Titelliste.
Das ist die Linked-Titelliste. Und die verwendet einen doppelt verketteten Knoten.
Das kennt ihr alles. Und wenn ich jetzt hier mal
die Tests ausführe, dann klappen die auch hoffentlich alle.
Das sieht gut aus. Was ich jetzt aber,
wenn ich mir das hier genau angucke,
also ich als Klient jetzt drauf schaue,
was will ich denn wissen von meinem System?
Ich weiß, wenn ich dieses Interface-Titelliste verwende,
da muss ich damit umgehen, die Interfaces sind immer für Klienten gedacht,
dann brauche ich sowas wie einen Titel, brauche auch vielleicht die Titelbibliothek.
Und ich brauche eine Implementation von dem Interface.
Aber als Klient muss ich nicht wissen,
dass die Link-Titelliste intern mit einer doppelt verketteten Liste implementiert ist.
Das interessiert mich als Klienten eigentlich gar nicht.
Und deswegen können wir jetzt einfach mal Folgendes machen.
Ich nehme jetzt mal hier den Quelltext, kopiere den einfach
hier raus und schreibe den hier in diese Klasse rein.
Gehe einfach mal ganz nach unten, stelle mich hier so ans Ende
und sage hier jetzt irgendwie, füge das mal ein.
Und dann habe ich jetzt hier gerade den ganzen Rumpf einer Klasse,
Und da haben wir ihn, den ganzen Rumpf einer Klasse,
den rücke ich mal ein bisschen ein.
So, dann ist der hier richtig hübsch,
dann sieht man auch, dir ist der grüne Kontext.
Ah, Moment, das ist irgendwie,
jetzt habe ich wohl einen zu weit eingerückt oder eine Klasse.
Und lass dir das übersetzen? Nee, ne?
Genau, ich habe hier offensichtlich eine Klammer nicht gesetzt.
So, jetzt sieht man auch wieder,
es gibt hier einen grünen Rahmen hier außen.
Das ist der Rumpf der ganzen Klasse.
Und da drin geschafft ist ein weiterer grüner Rumpf,
nämlich eine Klasse hier,
Doppel-Ding-Knoten.
Die ist jetzt
hier reingeschachtelt
Und ich mache das jetzt, wie es auf der Folie
steht, also auf der Folie steht irgendwie Static Class.
Okay. Also schreibe ich immer Static Class und schreibe ich
auch mal Private davor. So, jetzt habe ich,
also wie eine private Exemplar war, oder wie eine private Methode,
habe ich jetzt hier eine Klasse innerhalb dieser Klasse definiert.
Das kann ich hoffentlich übersetzen. Ja, kann ich.
Jetzt kann ich tatsächlich diese Klasse hier wegschmeißen. Löschen,
entfernen, weg damit, zack, lässt sich trotzdem übersetzen,
weil die Linktitelliste jetzt ja die verketteten Knoten,
diese Klasse, die ist da drin geschachtelt,
die braucht ja auch nur diese Klasse selber
Es ist also viel schöner, viel sauberer,
dass das jetzt da reingeschachtelt ist, einfach weil es dazugehört.
Es ist ein Detail der Implementation. Vor zwei Jahren
kam dann in der Erfundung die Frage, naja,
aber so ein doppelt verketteter Knoten, der ist doch total nützlich,
der muss doch nicht nur in der Klasse verwendet werden,
den kann man doch noch woanders verwenden.
Ja, das stimmt. Könnte man so sehen. Dann
ist es wieder eine Frage, wie das System gebaut ist,
wie wiederverwendbar etwas sein soll. Ich kann ja Wiederverwendung auch
explizit vorsehen, was ich jetzt nicht machen kann.
Ich kann jetzt hier an dieser Stelle nicht schreiben, New Linked Titel
Liste Punkt. Doppel-Link-Knoten. Das kann ich nicht machen.
Dann kommt nämlich eine Fehlermeldung. Error, Link-Titel,
das ist eine Doppel-Link-Knoten. Hass private Access in Link-Titel-Liste.
Das heißt, das ist ein privates Ding.
Aber der Compiler kann die Meldung trotzdem rausspucken.
Also er weiß, was ich tun will.
Also es scheint nicht so abwegig zu sein. Ich kann
tatsächlich jetzt an dieser Klasse statt Private Public davor schreiben.
Und dann kann irgendein Klient, so wie zum Beispiel ich
hier, mir ein solches Exemplar erzeugen
Ich habe tatsächlich jetzt, das heißt also auch der Name
von dieser geschachtelten Klasse ist, steht zur Verfügung.
Also er ist zugreifbar, weil er public ist.
Und er ist aber auch im Namensraum dieser Klasse eingeordnet.
Das heißt also, der komplette,
qualifizierte Name dieser Klasse ist dann irgendwie Link-Titelliste.doppeling-Knoten.
Und damit kann ich sie natürlich auch dann für andere wieder
verfügbar machen. Okay, soviel zu statisch geschachteten Klassen.
Prima, prima. Gibt es da Fragen zu?
Das ist eigentlich relativ überschaubar. Klassen kann man ineinander schachteln
. Ja, da ist eine Frage. Das heißt,
wenn ich das im ersten Glas finde,
die Elternklasse
Er fragt, wenn man die geschachtelten Klassen, geschachtelte Klassen nennt,
ob es dann einen Begriff gibt für die Elternklassen.
Ja, hatte ich hier eine Folie, das sind die Top-Level-Klassen.
Also die, die nicht geschachtelt sind, die heißen Top-Level-Klassen.
Das ist die Top-Level-Klasse. Einige Schachtelklasse eigentlich von der Tür. Ob die,
wie man angibt, das ist eine Top-Level-Klasse, eine andere,
andere, andere Klasse enthält, da muss man nichts angeben.
Die ist einfach darin gescheitert. Genauso wie du,
wenn du eine Exemplar-Variable hast,
die schreibst du einfach rein in den Quelltext der Klassen,
des Klassenrumfess genauso, kannst du auch eine Klasse da reinschachteln,
ja?
Das ist noch eine Frage. Ich meine,
die Klasse ist ja eigentlich
unnötig kompliziert
Also das ist jetzt keine Frage der Großfahrt. Okay,
da ist also so die Meinung, die Klasse wird unnötig komplizierter,
weil die Klasse jetzt da reingeschachtelt ist.
Zum Beispiel Flugberuf, klar, weil ich das kann ich nicht nennen.
Aber gerade auch die Käufer, das ist mir jetzt ganz so getrieben sein.
Ja, also er sagt, er sieht nicht so
den großen Sinn da drin, dass man Klassenschachteln kann.
Ja, es ist tatsächlich eine Geschmacksfrage, was man angenehmer findet,
ob das Klassendiagramm leichter zu lesen ist.
In diesem Fall ist das Klassendiagramm jetzt ein bisschen übersichtlicher.
Das kann schon auch ein Vorteil sein
Also man möchte einfach so auf verschiedenen Ebenen einfach auch Details verbergen können.
Manche finden das gut, manche eher nicht.
Aber es ist eine technische Möglichkeit. Erstmal ist es ja
jetzt hier so die technische Möglichkeit, die wir betrachten.
Okay, da ist noch eine Frage. Sie fragt,
wie man das in der UML zeichnen kann.
Das ist eine Klasse in einer Klasse geschachtet ist.
Oha. Das ist mal eine Frage. Ich weiß gar nicht,
ob das in der UML überhaupt vorgesehen ist,
weil das eben halt auch eher eine Geschmacksfrage ist,
weil die Klasse, wo sie dann genau steht, ist eigentlich wurscht.
In beiden Fällen hat man
die Klasse
Das ist dann eher eine Benennungsfrage. Ich würde mal vermuten,
dass es das nicht gibt, die Möglichkeit.
Aber ich lege mich nicht hundertprozentig fest. Du kannst es
vielleicht mal recherchieren und mir dann per E-Mail schicken, die Antwort.
Gut, sonst noch Fragen jetzt zu geschachtelten Klassen.
Gut, dann kommen wir zu Paketen. Pakete kennt ihr
schon ein bisschen. Ja, also oberhalb der Klassen,
also die nächst höhere Granulationsegen-Granulierungsebene, oberhalb der Klassen,
also die Klassen enthalten ja Methoden und Exemplarvariablen.
Und diese Klassen wiederum sind enthalten in Paketen
Ihr kennt das in dem Sinne,
dass ihr schon Dinge aus anderen Paketen importiert habt.
Ihr habt also gesagt, ich möchte etwas aus einem anderen Paket benutzen.
Die liegen also irgendwo in den Paketen und manchmal muss man
die erst importieren, um sie leicht benutzen zu können.
Ein Paket liegt allgemein einen gemeinsamen Namensraum fest für die enthaltenen Klasseninterfaces.
Und ein Paket liegt aber auch einen Zugriffsschutz fest.
Das heißt, ich kann tatsächlich in einem Paket dafür sorgen,
dass Klassen für Klienten des Paketes nicht sichtbar sind.
Also wenn wir hier zum Beispiel diesen Klasse D haben,
dieses außerhalb des Paketes ABC,
dann kann D nur auf die Klasse A zugreifen,
deshalb,
weil die Klasse A
public ist
Das B und Interface C, da steht nicht Public davor.
Damit sind sie private und damit nur innerhalb des Paketes zugreifbar.
Klaas D kommt da nicht ran. Also das ist schon mal ganz nützlich.
Zuerst mal diese Betrachtung des Namensraumes. Das ist immer nützlich,
sowas zu haben, verschiedene Namensräume, um Dinge,
die häufig gebraucht werden als Namen auch mehrfach benutzen zu können.
Also sowas wie List zum Beispiel. Es gibt Java AWT-List
und Java YouTill List. Das eine ist ein Interface,
das andere ist eine Klasse, damit man die
in einem begrenzten Kontext eben halt ohne Konflikt verwenden kann.
Ich kann also in verschiedenen Paketen gleiche Klassennamen haben,
kein Problem
Damit eine Klasse in einem Paket drin ist,
also die Zugehörigkeit zu einem Paket, die muss deklariert werden.
Also man schreibt üblicherweise in die erste Zeile dann zum Beispiel
Package Graphics. Damit sagt man in der Übersetzungseinheit,
alles, was jetzt kommt, gehört zum Paket Graphics.
Dann haben alle Klassen und Interfaces dieses Pakets das als Präfix.
Das heißt, der vollqualifizierte Name setzt sich,
wenn ich jetzt in eine Klasse Punkt definiere in diesem Paket,
dann hat diese Klasse den Namen Graphics.punkt.
Jede Klasse kann nur zu genau einem Package gehören
Und damit kann man eben halt auch Operationen eindeutig über vollqualifizierte Bezeichner ansprechen.
Also graphics.ursprung kann ich tatsächlich dann hier ansprechen. Es ist ein bisschen unglücklich,
dass sie auch den Punkt genommen haben für die Schachtelung von Paketen.
Also das ist eine Klasse in einem Paket geschattelt ist,
dass dann wieder eine Methode in einem, in einer Klasse,
Bestandteil einer Klasse ist oder auch das Klassen wieder in Klassen
geschachtelt sein können.
Alles mit Punkten, das macht es für den Parser
sehr schwierig mit den Compilern,
nicht für euch unbedingt
Okay, Package Deklaration am Anfang der Besetzungseinheit, Dateien ohne explizite Deklaration,
das ist das, was ihr bisher kennengelernt habt.
Wir haben bisher eigentlich immer mit dem sogenannten Unnamed Package gearbeitet.
Das heißt, mit dem Default-Paket,
wenn man keine Package Deklaration oben hinschreibt,
dann ist es alles in einem Unnamed Package in Java.
So, dann verfügt Java über den Importmechanismus.
Ihr habt ja schon ein paar Mal gesehen,
man kann oben hinschreiben im Port Java YouTill Stern.
Kann man machen, hat aber wenig mit der klassischen Importstelle
zu tun
Bei einer Important-Schelle sage ich,
was ich importiere und nur das kann ich dann auch benutzen.
Das ist in Java nicht so. Ich kann in Java,
das habt ihr ja auch schon ein paar Mal gesehen,
ich habe es heute auch gemacht.
Ich habe zum Beispiel Java.io, io Exception benutzt.
Ich hätte oben hinschreiben können, Import Javaio.stern.
Dann hätte ich io Exception einfach unqualifiziert benutzen können.
Aber ich kann es auch vollqualifiziert benutzen und es ist das Gleiche.
Also das heißt, das ist einfach nur eine syntaktische Vereinfachung.
Dieses Importstatement in Java ist nicht ein echtes Import im Sinne von Modularisierung,
sondern eher so eine, naja, wie gesagt,
eine Vereinfachung,
um sich Schreibaufwand zu sparen
Dann haben wir hier diese Importanweisung in zwei Formen,
also einmal dieses Import Some Package Some Class. Dann ist,
also entweder mache ich das mit dem voll qualifizierten Namen einer
bestimmten Klasse oder ich importiere einfach alles aus dem Paket
und dann kann ich das auch entsprechend benutzen.
Und das ist dann anschließend in verkürzter Form für nutzbar,
das haben wir ja schon gesehen.
Ohne Package Import könnte ich das genauso hinschreiben mit dem Graphics
Punkt, Punkt. Es ist immer ganz schlau,
da auch wirklich nur die Dinge zu importieren,
die man wirklich braucht.
Das verdeutlicht dann zumindest
die konkreten Abhängigkeiten im Quelltext
Die namens Vergabe für Pakete in Java ist relativ klar geregelt.
Es ist einfach diese Idee, dass man die übers Internet verwenden will.
Deswegen werden die einfach sozusagen so umgekehrte Domänennamen häufig verwendet.
Also sowas wie hier.de, Uni Hamburg, Informatik,
SWT, Graphics und dann wäre das jetzt das Paket,
wo dann unsere konkreten Klassen drinstehen.
Und der Top-Level-Zeichner bezeichnet Java, also ganz vorne,
der ist reserviert für die Programmiersprache Java.
Also das ist einfach, das ist eigentlich alles.
Man kann Pakete ineinander schachteln,
um solche Namen zu erzeugen
Es gibt keinen weiteren Nutzen davon. Also die Pakete,
geschachtelte Pakete führen nicht dazu,
dass ein geschachteltes Paket irgendwie vor dem Zugriff geschützt werden kann.
Was wir jetzt auch schon gesehen haben, ist,
wie man mit Modifikatoren den Zugriff ändern kann.
Ich kann also diese Sichtbarkeit oder Zugreifbarkeit steuern. Ihr kennt es
für Bestandteile einer Klasse, bei Bestandteilen einer Klasse. Wisst ihr,
wenn ich es private, wenn ich ein Exemplar variable private deklariere,
dann ist sie nur in der Big Klasse zugreifbar.
Bei Paketen, Ist das auch möglich, nämlich Sichtbarkeit einer Sichtbarkeit
einer Klasse oder eines Interfaces selbst, außerhalb des Packages?
Ein Paket selbst ist immer zugreifbar. Das heißt,
es gibt, wie gesagt, keinen Schutzmechanismus zwischen Paketen.
Auch wenn Pakete ineinander geschachtelt sind, nützt das nichts.
Das heißt, hier ist ein Beispiel, ein Paket,
mein Paket und dann ist da drin geschachtelt, mein Paket, Unterpaket.
Alles, was hier public ist, ist überall public.
Ich kann also nicht jetzt in dem Paket sagen,
ich möchte, dass das hier privat bleibt, das Paket.
Geht einfach nicht. Das ist eine echte Designschwäche von Java
Da haben sie einfach nicht zu Ende entworfen, als sie
die Sprache gebaut haben. Und das ist einfach schade,
weil das ganz viel unschönes Zeug bewirkt. Also werdet ihr
in der Praxis mit Java auch noch an vielen Stellen bemerken.
Also keine Schachtelung, keine Einschränkung der Zugreifbarkeit. Elemente von Paketen,
also die Klassen und auch die Interfaces und auch die E-Nums und überhaupt alles,
was man so haben kann, sind ja alles Typen in Java.
Könnte man allgemeiner sagen, die Typen innerhalb eines Paketes sind für andere Typen,
je nach Zugriffsrecht zugreifbar. Es gibt den Standardzugriff,
wenn ich nichts davor schreibe, also einfach nur Class A,
Dann ist diese Klasse nur innerhalb des Paketes zugreifbar.
Also Paket privat. Und etwas, was zur Schnittstelle
des Paketes gehört, das muss ich mit Public declarieren.
Also hier Public Class A, Public Interface B,
die gehören zur Schnittstelle dieses Paketes. Aber C und D
sind Paket privat. Weil das Public davor nicht steht.
Ganz wichtig, bei Klassen und auch bei Interfaces gibt es
nicht noch irgendwie protected und irgendwie andere Sachen, private.
Es gibt nur Public oder nix. Okay,
wenn wir jetzt an der Stelle sind, dann können wir
jetzt endlich auch die ganzen Modifikatoren von Java einmal kurz durchgehen.
Da ist eine Frage
Nicht, weil du nach jedem fahren würdest, lieber,
wenn wir jetzt über, da geht der eine für die Föderalbum.
Ja, da freut er den, die Pakete,
dann würde das weg, anderen, wir sprechen.
Also er fragt, wie wäre es denn,
wenn es doch sowas gäbe in Jara, sowas wie Pakete,
die man miteinander ineinander schachteln kann und diese Schachtelung bewirkt auch was,
also einen Zugriffsschutz, da komme ich noch drauf,
da kommt gleich eine Folie dazu.
Also das ist in Diskussion,
sollte eigentlich in Java 7 drin sein, ist es nicht,
jetzt ist es vielleicht in Java 8 geplant,
weiß man aber auch nicht so genau und da gibt es
ein bisschen was.
Also privat kennt ihr, also jetzt, wenn wir auf
eine Klasse gucken, was kann eine Klasse für Member haben?
Sie kann Methoden haben, kann Klassen haben und kann ein Exemplarvariablen haben.
So, und die können halt entweder Private sein,
nur innerhalb der Klasse zugreifbar.
Public kennt ihr auch. Ist für alle Klienten zugreifbar.
Protected kennt ihr auch. Ist für alle erbenden Klassen zugreifbar.
Jetzt gibt es noch eine weitere, nämlich den Standardzugriff.
Wenn ich nichts hinschreibe. Und ein Element ohne deklarierten Zugriffsschutz
ist zugreifbar für alle Klassen im selben Paket.
Das ist hier mal so ein bisschen verdeutlicht.
Ich habe eine Klasse A, die ist in diesem Paket
Public deklariert. Das soll hier so eine Package-Grenze sein,
also habe ich hier zwei Pakete.
Ich habe also hier immer irgendwie vier Klassen.
Und diese Klasse A hat jetzt eine private Methode,
ah, dann ist private, diese Methode A,
nur innerhalb dieser Klasse zugreifbar.
Sonst nirgends. Zweiter Fall, es ist der Default,
also nichts ist angegeben. Dann kann natürlich die Klasse selbst
drauf zugreifen und alle Klassen im Paket können auch B sehen.
Also hier farblich hervorgehoben, grün die, die auf B
zugreifen können. Hier, wenn es protected ist,
dann sehen wir auch eine Erbendeklasse in einem anderen Paket.
Hauptsache, sie erbt,
die kann auch auf C zugreifen
Aber, und das ist jetzt überraschend,
das überrascht zumindest viele, die ich so kenne,
das ist jetzt hier auch, bedeutet automatisch,
dass alle Klassen innerhalb des Paketes auch auf C zugreifen können.
Also auch die Klassen, die nicht haben, aber im selben Paket liegen,
können auf C zugreifen, wenn es protected ist.
Ist Geschmackssache, da ist halt der Gedanke,
ein Paket ist schon eine sehr enge Bündelung, da sind die Dinge,
die zusammengehören und dann sollten die auch darauf zugreifen können.
Das ist Geschmackssache. Das ist ganz nützlich zum Testen zum Beispiel,
wenn Testklassen im Paket mit drinstehen,
dann können die da auf Dinge zugreifen und auch vielleicht
Whitebox-Tests machen
Gut, und dann hier, wenn natürlich in einer öffentlichen Klasse,
einer Methode öffentlich ist, dann kann die von allen Klassen im System zugegriffen werden. Dann ist das so,
hier, das ist nochmal die Folie mit denen Super Packages,
also tatsächlich diese Möglichkeit, Module in andere Module zu schachteln,
dass man also sagt, hier ist eine Super Package und dann kann ich exportieren,
ganz bewusst, ich sage Export, dieses eine Modul,
dieses eine Paket, das andere Paket und dann werden die Dinge,
die ich nicht exportiert habe, eben halt bleiben privat,
die gehören nur mir.
Das ist der JSA 294, wenn ihr euch damit beschäftigen wollt,
könnt ihr immer reinlesen,
Java Specification Request
Super Packages auf dem Weg in die nächste Version von Jabba.
Nein, ist nicht abzusehen. Es ist momentan wieder eingeschlafen,
2, 9, 4. Ich habe heute nochmal geguckt,
das ist also so ein bisschen komisch.
Aber da gibt es auch durchaus Meinungen dazu, wie nützlich das ist,
vielleicht nochmal auf die andere Frage hin, ob das schlimm wäre.
Nein, es wäre sehr, sehr nützlich,
wenn man sowas einführen würde, aber es ist tatsächlich,
da hängt eine ganze Menge noch anderes Zeug mit dran.
Also auch für die Laufzeit muss man dann bestimmte Dinge klarstellen,
wie das dann ist mit Überprüfungen für Reflection und so weiter.
Also es ist ein bisschen Fegelinsch,
aber es ist eigentlich komisch,
dass es
noch nicht drin ist in Java
Gut, das ist die Zusammenfassung und wir haben noch etwas
Zeit und ich habe natürlich ein paar Storesi-Fragen für euch,
damit es noch mal so ein bisschen interessant wird.
Nur noch mal kurz die Zusammenfassung.
Klassen sind wie Module, Einheiten, der Softwarearchitektur.
Große Systeme setzen sich aus Modulen zusammen, sind üblicherweise auch Übersetzungseinheiten.
Bilden Namensräume, kann man also auch vom Zugriff schützen.
Aber Klassen sind im Gegensatz zu klassischen Modulen eben halt auch Typen.
Das heißt, man kann Exemplare erzeugen. Module besitzen verbindliche
Exporte und Import hinstellen, das ist in Java ein bisschen anders.
Da ist alles irgendwie nur so, so einigermaßen explizit ist es so,
dass man in Java explizit sagt, was exportiere ich von meinen Membern?
Oder ein Paket sagt, welche Typen, welche Klassen sind public?
Das ist explizit, aber man muss sie sich trotzdem noch aufsammeln.
Die Import-Schelle ist einfach total impliziert. Wenn ein Quellentext irgendwas benutzt wird,
dann muss man den ganzen Quellentext lesen, um zu wissen,
dass das Ding jetzt etwas anderes benutzt.
Gut, Verständnisfrage. Wir gehen mal auf Sturesi und starten
mal eine neue Abstimmung.
Bei welchem der folgenden Fehler ist am wenigsten klar,
ob es sich um einen Entwicklungsfehler oder um einen Umgebungsfehler handelt?
Ich starte schon mal hier die Abstimmung. Also,
Entwicklungsfehler, Umgebungsfehler. Jetzt habe ich hier vier Möglichkeiten.
Verschärfung einer Vorbedingung in einer Unterklasse? Exception bei einer Typ
, bei einer Typ-Zusicherung, also so ein Downcast möglicherweise.
Versuch, eine Datei zu lesen, die nicht existiert.
Oder D, ein Mail-Programm, kriegt keine Verbindung zu einem
gültigen Mail-Host. Wo es am wenigsten klar,
ob es ein Entwicklungsfehler oder ein Umgebungsfehler ist
Also ich finde, es ist bei dreien
klar und bei einer möglicherweise nicht so klar.
Und das können wir ja mal versuchen,
ob das hier für euch auch nachvollziehbar ist.
Ja, das ist fließend zäh, die Stimmen.
13, oha. Da ist eine Frage.
Oh. Habt ihr Smartphone oder Clicker
Smartphone. Ja, auf dem Klicker wird es sowieso nicht angezeigt.
Ja, blöde Frage. Doof. Doof doof doof.
Eigentlich habe ich dann, ach nee,
ja, oh, oh, oh, oh.
Ja, herrlich. Ich habe ja eben,
bin ich aus dem Netz geflogen. Mal gucken,
ob es jetzt geht. Ja, herrlich. Das Netz
funktioniert mal wieder nicht
Ja, das macht die Technik so wunderbar zu benutzen.
Also wir müssen jetzt hier mit den Klickern arbeiten,
also sind sozusagen nur die Klicker beteiligt.
Ich beende das mal kurz. Tut mir leid.
Wir gucken mal, wie die Verteilung war.
Okay. Ah, es sieht ganz gut aus.
Also versuch, eine Datei zu lesen, die nicht existiert.
Das haben immerhin 42 Prozent, 42 Prozent, gut gefunden.
Das ist auch das, was ich als richtig ansehe.
Weil Verschärfung einer Fummel in einer Unterklasse ist ein Fehler,
ist immer ein Fehler, das ist ein Programmierfehler.
Es muss ein Entwicklungsfehler sein
Wenn ich bei einer Typzusicherung was falsch mache, ist es
auch ein Programmierfehler. Mail-Programm kriegt keine Verbindung zum gültigen Mailhost,
das ist ein Umgebungsfehler. Versuch, eine Datei zu lesen,
die nicht existiert, das kann daran liegen,
dass in der Produktivumgebung die Datei einfach gelöscht wurde.
Oder ich kann als Programmierer einen Fehler gemacht haben,
dass ich das Ding einfach falsch benannt habe.
Aber den falschen Namen gewählt, dann ist es ein Programmierfehler,
also ein Entwicklungsfehler. Deswegen, das finde ich eher diskutabel.
Gut, ich habe noch eine. Eine habe ich noch
. Aber bevor ihr die verstehen könnt,
muss ich ja erstmal ihr die Folie zeigen.
Hier ist eine Klasse. Public Class A,
die ist in einem Paket Paket
Drin. Und die hat jetzt hier mehrere Methoden, M1,
M2, M3 und M4. Und ich habe hier ein zweites Paket,
Paket 2, da wird jetzt hier, wird gesagt,
ich importiere mal alles aus Paket 1,
dann sagen wir in Klaas
B, Extense, aha, und haben eine Methode,
du, oh du, darf ich gar nicht schreiben.
Das ist nicht übersetzbar. Nehm mal an, die würde nicht du heißen.
Das ist nicht der Fehler, den ich suche.
So, welche Ms von A kann ich jetzt aufrufen?
Also tatsächlich Ja, die Frage, welche Methoden der Klasse B,
der Klasse A, kann B in der Methode Du aufrufen.
Gar keine M2 und M4, M3 und M4,
M2,
M3 und
So, 14, 15 Stimmen. Entscheidet euch jetzt.
Die Zeit ist nämlich ungleich. Ich halte an. So
, wir schauen mal. Oha. 50 Prozent sagen M2
und M4. Und auch immerhin 344 Prozent sagen, M2,
M3 und M4. Ja, die richtige Antwort ist natürlich M2 und M4. Warum ist das so?
Weil M1 privat ist, ist klar, kann keiner
benutzen.
M3 ist Package privat
Das heißt, nur die Klassen im Paket können darauf zugreifen.
M2 ist protected, ist im Package-Zugriff, aber auch vererbende Klassen.
Und B erbt ja von A und Public-Klassen sind sowieso aufrufrufbar.
Also ist M2 und M4 richtig.
Okay, das war es für heute mit Sturesi.
Vielen Dank. Tschüss