[SOLVED] CS计算机代考程序代写 Java jvm ER compiler Programmierung

30 $

File Name: CS计算机代考程序代写_Java_jvm_ER_compiler_Programmierung.zip
File Size: 612.3 KB

SKU: 2115029929 Category: Tags: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Or Upload Your Assignment Here:


Programmierung
Michael Goedicke
[email protected] auf der Basis von Folien von V Gruhn

Ausnahmen (Exceptions)
▪ im Programmablauf können Ausnahmesituationen auftreten ▪ z.B. Division durch Null, Datei nicht vorhanden,
Übergabe eines ungültigen Parameters an eine Methode…
▪ allgemein: Zustand, der das Programm daran hindert, im normalen Ablauf
fortzufahren
▪ Werden solche Situationen nicht vom Programmierer vorhergesehen,
gerät das Programm evtl. in nicht definierte Zustände, bricht ab o.ä.
▪ Idealerweise werden solche Ausnahmesituationen durch umsichtige Programmierung von vornherein vermieden.
▪ Manchmal ist es aber unmöglich (insb. bei externen Ereignissen) oder sehr umständlich, Ausnahmesituationen zu vermeiden.
▪ U.u. ist es dann einfacher, Ausnahmesituationen eintreten zu lassen und sie ordentlich zu behandeln, als sie unbedingt zu vermeiden.
➢ „Exception Handling“
M. Goedicke – Programmierung WiSe 2020/2021 2

Ausnahmebehandlung: Motivation
▪ Naives Vorgehen zur Ausnahmesignalisierung in einer Methode: bestimmter Rückgabewert, der als “Fehlersignal” vereinbart wird
▪ Bsp.: Wir wollen vermeiden, dass bei Initialisierung eines Buch-Objekts das Erscheinungsjahr vor Erfindung des Buchdrucks gesetzt wird:
public class Buch {
private int jahr; // Erscheinungsjahr

public boolean setJahr (int jahr) {
if (jahr > 1452) {
this.jahr = jahr;
return true; // Signal “gültiger Parameter”
} else {
return false; // Signal “ungültiger Parameter” }
}
▪ Problem: Im aufrufenden Programmteil ist eine umständliche Prüfung des Erfolgs erforderlich.
M. Goedicke – Programmierung WiSe 2020/2021 3

Ausnahmebehandlung: Motivation
▪ Besonders problematisch, wenn eine Methode eigentlich kein Erfolgs- / Fehlersignal liefern sollte, sondern einen fachlichen Rückgabewert:
public class Bibliothek { …
public int zahlDerNeuerscheinungen (int jahr) { if (jahr > 1452) {
… // zaehle in jahr erschienene Bücher
return gefundeneAnzahl; }
else {
return -1; // Signal “ungültiger Parameter”
} }
▪ mögliche Rückgabewerte der Methode
▪ 0…n Zahl der gefundenen Bücher (bei gültigem Parameter) ▪ -1 Fehlersignal (bei ungültigem Parameter)
M. Goedicke – Programmierung WiSe 2020/2021 4

Ausnahmebehandlung: Motivation
▪ Umständliche Überprüfung des Rückgabewerts der Methode auf das vereinbarte Fehlersignal in der aufrufenden Methode:
int anzahl = bib.zahlDerNeuerscheinungen(suchJahr); switch (anzahl) {
case –1:
System.out.println (“Ungültiges Suchjahr.”); break;
case 0:
System.out.println (“Keine Buecher gefunden.”); break;
default:
System.out.println (anzahl + ” Bücher gefunden.”); System.out.println (“Buchliste anzeigen?”);

}
M. Goedicke – Programmierung WiSe 2020/2021 5

Ausnahmebehandlung: Motivation
▪ Fehlersignalisierung über den Methoden-Rückgabewert insb. dann praktisch unmöglich, wenn der komplette Rückgabe-Wertebereich fachlich potentiell erreichbar ist, also kein „Randbereich“ für die Fehlersignalisierung bleibt.
▪ Beispiel: Die Methode soll den Kontostand zum Ende des übergebenen Jahrs zurückliefern:
public double getAbschlussSaldo(int jahr) { … }
▪ Problem: Beliebige positive und negative Werte sowie 0 sind als gültige Rückgabewerte möglich. Wie signalisieren wir dann die Ausnahmesituation, dass das Konto im angefragten Jahr noch gar nicht existierte?
M. Goedicke – Programmierung WiSe 2020/2021 6

Ausnahmebehandlung: Motivation
▪ Probleme bei der Ausnahmebehandlung über „besondere“ Rückgabewerte:
▪ Fehler werden auf dem gleichen Weg zurückgegeben wie reguläre Rückgabewerte
▪ Rückgabewert von Methoden ist aber meistens fachlich bestimmt und nicht gut zur Fehlersignalisierung geeignet
➢ Programmierer versuchen, Fehlersignale in Randbereichen des fachlichen Wertebereichs unterzubringen
▪ Nicht immer ist ein Randbereich dafür verfügbar
▪ Bedeutung so codierter Fehlersignale muss vereinbart werden ▪ wenn überhaupt, passiert das nur informell in Kommentaren
▪ anfällig für Missverständnisse bei der Entwicklung im Team
➢Gefahr von falsch oder gar nicht behandelten Fehlercodes ➢Seiteneffekte, Programmabbrüche, …
M. Goedicke – Programmierung WiSe 2020/2021 7

Ausnahmebehandlung: Exceptions als Lösung
▪ eigener Kanal für Signalisierung von Fehlern
➢ Entkopplung der Signale vom regulären Rückgabewert
▪ eigene Datenstrukturen für Fehlersignale ➢ differenzierte, genaue Fehlermeldungen
▪ formelle Deklaration möglicher Fehlersignale
➢ keine Gefahr von Missverständnissen bei der Signalvereinbarung
▪ erzwungene Behandlung von Fehlersignalen
➢ keine vernachlässigten oder übersehenen Fehler
➢ Robustere, lesbarere Programme
M. Goedicke – Programmierung WiSe 2020/2021 8

Exceptions
▪ Fehlersignale in Java: Exceptions
▪ von Exception abgeleitete Klassen
▪ enthalten Methoden zur Beschreibung der Fehlersituation
▪ von Java verwendete Exceptions (kleiner Auszug)
▪ ArithmeticException, ArrayIndexOutOfBoundsException,
IllegalArgumentException, NullPointerException, NegativeArraySizeException, FileNotFoundException, MalformedURLException, UnknownHostException u.v.m.
▪ selbstdefinierte Unterklassen von Exception
▪ zur Signalisierung von Fehlersituationen in der eigenen Anwendung, z.B.
UngueltigesJahrException, BuchBereitsVerliehenException, …
▪ Konvention: Exceptions werden mit Suffix Exception benannt
M. Goedicke – Programmierung WiSe 2020/2021 9

Eigene Exception-Klassen
▪ Ableitung eigener Exceptions von Oberklasse Exception
▪ Deklaration eines leeren Konstruktors und eines Konstruktors mit String-
Parameter, um Exceptions beim Werfen erzeugen zu können
public class UngueltigesJahrException extends Exception { public UngueltigesJahrException() {
super(); }
public UngueltigesJahrException(String message) { super(message);
} }
▪ Erinnerung: Konstruktoren
▪ Bei der Ableitung werden Konstruktoren nicht mitvererbt, darum müssen
wir sie selbst deklarieren
▪ Wenn wir einen parametrisierten Konstruktor deklarieren (hier: zur
Übergabe einer Fehlererläuterung), erzeugt Java keinen impliziten leeren Konstruktor, wir müssen ihn darum selbst deklarieren
M. Goedicke – Programmierung WiSe 2020/2021 10

Deklarieren und Werfen von Exceptions
▪ Statt Rückgabe eines speziell zu vereinbarenden Rückgabewerts: Signalisieren des Fehlers durch “Werfen” einer Exception mit throw
public class Bibliothek { …
public int zahlDerNeuerscheinungen (int jahr)
throws UngueltigesJahrException {
if (jahr > 1452) {
… // in jahr erschienene Bücher zaehlen return gefundeneAnzahl; // Wert zurückgeben
}
else {
throw new UngueltigesJahrException();
}
}
… }
▪ Statt informeller Dokumentation des Fehlersignals z.B. in einem Kommentar, der vergessen/übersehen werden kann:
Formale Deklaration der Exceptions, die die Methode im Fehlerfall werfen kann, mit throws im Methodenkopf
M. Goedicke – Programmierung WiSe 2020/2021 11

Fangen von Exceptions: Deklaration
▪ In der aufrufenden Methode: Eingrenzen des Programmteils, von dem Exceptions geworfen werden können: try-Block
… try {
int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0)
System.out.println (“Keine Buecher gefunden.”);
else {
System.out.println (anzahlNeue + ” Buecher gefunden.”); System.out.println (“Buchliste anzeigen?”);

} }
catch (UngueltigesJahrException e) { System.out.println (“Ungültiges Suchjahr.”);
}

▪ Abfangen und Behandeln der Exception: catch-Block
M. Goedicke – Programmierung WiSe 2020/2021 12

Fangen von Exceptions: Ablauf
▪ wenn Exception auftritt, wird die Bearbeitung des try-Blocks abgebrochen und mit catch-Block fortgefahren
… try {
int anzahlNeue = uniBib.zahlDerNeuerscheinungen(suchJahr); if (anzahlNeue == 0)
System.out.println (“Keine Buecher gefunden.”);
else {
System.out.println (anzahlNeue + ” Buecher gefunden.”); System.out.println (“Buchliste anzeigen?”);

} }
catch (UngueltigesJahrException e) { System.out.println (“Ungültiges Suchjahr.”);
}

▪ wenn keine Exception, wird catch-Block übersprungen
M. Goedicke – Programmierung WiSe 2020/2021 13

Behandlungsvarianten
▪ mit catch fangen und behandeln
▪ häufigste Form, wie gerade gezeigt
▪ mit catch fangen und eine andere Ausnahme werfen
▪ z.B., um technische Fehlersignale in fachliche zu verwandeln
und sie auf höherer Ebene behandeln zu lassen
▪ nicht fangen, sondern “weiterfliegen” lassen ▪ Fehlersignal nicht selbst behandeln,
sondern von höherer Ebene behandeln lassen
▪ aber niemals: fangen und nichts tun
▪ catch (Exception e) {} // niemals verwenden!
▪ auftretende Fehler würden ignoriert und versteckt
▪ zumindest den Fehler in Logdatei o.ä. protokollieren!
M. Goedicke – Programmierung WiSe 2020/2021 14

Fangen und andere Exception werfen
▪ z.B. um technische Fehlersignale in fachliche zu verwandeln
public class Bibliothek {
private Buch[] buchBestand;
private int index = 0;
public Buch naechstesBuch () throws ListenEndeException
{
try {
index++;
return buchBestand[index]; }
catch (ArrayIndexOutOfBoundsException e) { throw new ListenEndeException();
} }
}
▪ Aufrufer von naechstesBuch muss sich zwar um
ListenEndeException kümmern, muss aber nicht wissen, dass der Buchbestand als Array implementiert ist
M. Goedicke – Programmierung WiSe 2020/2021 15

Exception “weiterfliegen” lassen
▪ …um sie von höherer Ebene behandeln zu lassen public class Bibliothek {
private Buch[] buchBestand;
private int index = 0;
public Buch naechstesBuch ()
throws ArrayIndexOutOfBoundsException {
index++;
return buchBestand[index]; }
}
▪ Arrayzugriff kann ArrayIndexOutOfBoundsException erzeugen,
die aber hier nicht gefangen wird
▪ nicht gefangene Exceptions werden an aufrufende Methode
weitergegeben
➢ Deklaration im throws-Teil des Methodenkopfes
M. Goedicke – Programmierung WiSe 2020/2021 16

Exception-Behandlung durch JVM
▪ wenn eine Exception nirgends gefangen, sondern immer weiter nach oben „durchgereicht“ wird, „behandelt“ die Java Virtual Machine sie schließlich durch Programmabbruch und Fehlerausgabe:
Exception in thread “main”
java.lang.ArrayIndexOutOfBoundsException: 5
at Bibliothek.naechstesBuch(Bibliothek.java:8) at Bibliothek.main(Bibliothek.java:13)
▪ Ausgabe der Exception mit Stack Trace ▪ Thread-Name
▪ Name der Exception
▪ ggf. Informationen zum Grund des Fehlers
▪ Call Stack (Aufrufhierarchie der Methode, die Exception erzeugte)
mit Zeilennummern
▪ Hilfreich für den Entwickler zur zielgerichteten Eingrenzung des
Fehlers, aber fatal für den Anwender (Programmabbruch!)
M. Goedicke – Programmierung WiSe 2020/2021 17

Optional: finally-Block
▪ Gelegentlich “Aufräumarbeiten” wie Schließen von Dateien nötig, unabhängig davon, ob Exception auftrat oder nicht
▪ Deklaration in finally-Block nach letztem catch-Block
▪ Wird grundsätzlich nach try- bzw. catch-Block ausgeführt
▪ Auch, wenn im try/catch-Block return oder throw o.ä. steht, d.h.
der Programmcode danach eigentlich gar nicht mehr erreicht würde
try {
… // Anweisungen
}
catch (ArrayIndexOutOfBoundsExceptione) {
… // Ausnahmebehandlung
throw new ListenEndeException(); }
finally {
… // Aufräumarbeiten
}
aufrufender Programmteil
M. Goedicke – Programmierung WiSe 2020/2021
18

Exceptions in Konstruktoren
Beispiel: Initialisierung von Buch-Objekten im Konstruktor:
public class Buch {
public Buch (String autor, String titel,
String verlag, int jahr)
{ … } }
▪ Motivation:
▪ Ein Konstruktor hat keinen deklarierten Rückgabewert (bzw. sein
Rückgabewert ist immer eine Instanz der jeweiligen Klasse), d.h. Exceptions sind unsere einzige Möglichkeit, Fehlersignale an den aufrufen Programmteil zu melden.
▪ Wenn ein Konstruktor normal beendet wird, gibt er immer eine Instanz der jeweiligen Klasse zurück. Das Werfen einer Exception ist die einzige Möglichkeit, ein Konstruktur ohne Anlegen einer Instanz zu beenden (z.B. wenn es aufgrund fehlerhafter Paramter nicht möglich ist, ein sinnvoll initialisiertes Objekt zu erzeugen).
M. Goedicke – Programmierung WiSe 2020/2021 19

Werfen mehrerer Exceptions
▪ komma-getrennte Deklaration möglicher Exceptions im Kopf (in beliebigen Methoden möglich, nicht nur in Konstruktoren)
public class Buch { …
public Buch (String autor, String titel, String verlag, int jahr)
throws KeinTitelException, UngueltigesJahrException
{
this.autor = autor; if (titel != “”)
this.titel = titel; else
throw new KeinTitelException();
this.verlag = verlag;
if (jahr > 1452)
this.jahr = jahr; else
throw new UngueltigesJahrException(); }
… }
M. Goedicke – Programmierung WiSe 2020/2021 20

Fangen mehrerer Exceptions
▪ mehrere catch-Blöcke hintereinander
… try {
neuesBuch = new Buch (neuAutoren, neuTitel, neuVerlag, neuJahr);
}
catch (KeinTitelException e1) {
System.out.println (“Kein Titel angegeben.”);
}
catch (UngueltigesJahrException e2) { System.out.println (“Ungültiges Erscheinungsjahr.”);
} …
▪ wenn Exception auftritt, wird der passende catch-Block gesucht und ausgeführt
▪ sonst fliegt die Exception weiter an übergeordnete Methode
M. Goedicke – Programmierung WiSe 2020/2021 21

Klassenhierarchie bei Exceptions
▪ hierarchische Deklaration von Exceptions üblich ▪ z.B. Exception ⊳━ RuntimeException ⊳━
IndexOutOfBoundsException ⊳━ ArrayIndexOutOfBoundsException
▪ Vorsicht: catch-Blöcke für Ober-Exceptions fangen auch Unter-
Exceptions!
try {…}
catch (Exception e1) {
// faengt alles
System.out.println (“Ausnahmesituation”);
}
catch (ArrayIndexOutOfBoundsException e2) {
// nicht erreichbar!
System.out.println (“Arraygrenze ueberschritten”);
}
➢ catch-Reihenfolge von speziellen zu generischen Exceptions ordnen M. Goedicke – Programmierung WiSe 2020/2021 22

Nachrichten in Exceptions speichern
▪ Übergabe von Informationen über Fehlerursache im Konstruktor der Exception möglich
public class Buch { …
public Buch (String autor, String titel, String verlag, int jahr)
throws UngueltigeEingabeException { this.autor = autor;
if (titel != “”)
this.titel = titel;
else
throw new UngueltigeEingabeException(“Kein Titel”); this.verlag = verlag;
if (jahr > 1452)
this.jahr = jahr;
else
throw new UngueltigeEingabeException(“Jahr ungültig”);
}
… }
M. Goedicke – Programmierung WiSe 2020/2021 23

Nachrichten aus Exceptions lesen
▪ beim Fangen wird die gerade erzeugte Exception-Instanz der Variable im catch-Ausdruck zugewiesen
▪ Zugriff auf die Methoden der Exception wie gewohnt per Punktnotation
… try {
neuesBuch = new Buch (neuAutoren, “”, neuVerlag, neuJahr);
}
catch (UngueltigeEingabeException e) {
System.out.println (e.getMessage()); // „Kein Titel“ }

▪ Methoden (z.B. getMessage, toString…) werden von Oberklasse Exception geerbt
M. Goedicke – Programmierung WiSe 2020/2021 24

Checked und unchecked Exceptions
▪ selbstdefinierte Exceptions müssen im throws-Teil des
Methodenkopfs deklariert werden
➢ reguläres Methodenverhalten aus der Signatur ablesbar
➢ Ausnahmeverhalten der Methode aus dem throws-Teil ablesbar
▪ sowohl für den Programmierer als auch den Compiler
▪ Compiler fordert, dass deklarierte Exceptions vom aufrufenden
Programmteil abgefangen werden (“checked Exceptions”)
▪ einige von Java generierte Exceptions müssen nicht im throws-Teil
deklariert werden
▪ konkret: alle von RuntimeException abgeleitete Exceptions
▪ signalisieren Laufzeitfehler, die bei umsichtiger Programmierung
eigentlich nicht auftreten dürfen
▪ könnten aber überall auftreten (z.B. NullPointerException), sodass
es umständlich wäre, sie überall zu deklarieren und zu prüfen
▪ Abfangen wird daher vom Compiler nicht überprüft (“unchecked”)
M. Goedicke – Programmierung WiSe 2020/2021 25

Checked und unchecked Exceptions
▪ Von RuntimeException abgeleitete Exceptions, deren Deklaration und Behandlung der Compiler nicht einfordert, sind u.a. ArithmeticException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException, NullPointerException, uva.
▪ Sie signalisieren „technische“ Fehler, die prinzipiell überall auftreten können, bei sauberer Programmierung aber vermeidbar sind.
▪ Nach dem Motto „Manchmal ist es einfacher, Fehler zuzulassen und sie zu behandeln, als sie zu vermeiden“, können Entwickler solche Exceptions aber auch selber werfen und fangen (z.B. die IllegalArgumentException).
▪ Müssen im Methodenkopf nicht deklariert werden.
▪ Checked Exceptions signalisieren hingegen Fehler, die eher „fachlicher“ Natur sind (also z.B. von Nutzereingaben oder –aktionen herrühren) und somit vom Entwickler nicht verhinderbar sind, und i.d.R. nur an bestimmten Stellen auftreten können.
▪ z.B. IOException: Auf Probleme beim Dateizugriff muss ein Programm vorbereitet sein, daher erfordert der Compiler ihre Behandlung.
M. Goedicke – Programmierung WiSe 2020/2021 26

Leitlinien für den Exception-Einsatz
▪ Beim Deklarieren individueller Checked Exceptions nicht übertreiben
– zu umständliche erzwungene Handhabung führt dazu, dass
Entwickler des aufrufenden Programmteils Abkürzungen nehmen
▪ Todsünde: catch (Exception e) { }
▪ Niemals einen catch-Block leer lassen! Zumindest System.out.println(e)!
▪ Checked Exceptions (die ja irgendeine Behandlung erzwingen) nur einsetzen, wenn vom aufrufenden Programmteil auch tatsächlich eine sinnvolle Behandlung erwartet werden kann
▪ Wenn vorauszusehen ist, dass der aufrufende Programmteil im Angesicht des betreffenden Fehlers ebenfalls machtlos sein könnte, lieber eine Unchecked Exception verwenden – dann kann der aufrufende Entwickler entscheiden, ob er sich sinnvoll um den Fehler kümmern kann oder die Exception nach oben weiterfliegen lässt.
▪ Für diese Entscheidung notwendig: Alle Exceptions, die man in einer Methode u.U. wirft, sorgfältig kommentieren – insb. unchecked Ex.
M. Goedicke – Programmierung WiSe 2020/2021 27

Leitlinien für den Exception-Einsatz
▪ Insb. in unchecked Exceptions, die ggf. bis zur JVM hochfliegen und auf der Konsole ausgegeben werden, möglichst viel Information zur Eingrenzung des Fehlers speichern:
▪ Beim Fangen einer Low-Level-Exception und Weiterwerfen einer High- Level-Exception: Ursprüngliche Exception der neuen Exception mitgeben
▪ Beim Erzeugen einer eigenen Exception: Aussagekräftige Fehlermeldung incl. der fehlerverursachenden Variablenwerte
▪ Auch beim Werfen von Exceptions das betroffene Objekt nach Möglichkeit in konsistentem, gültigem Zustand zurücklassen.
▪ Entweder Änderungen erst vornehmen, wenn sichergestellt ist, dass alle Vorbedingungen erfüllt sind…
▪ …oder Änderungen zurücknehmen, wenn sich herausstellt, dass nicht alle voneinander abhängigen Änderungen ausgeführt werden können.
▪ Lässt sich nicht immer erreichen – bevor übertriebene „Klimmzüge“ zu diesem Zweck gemacht werden, besser im Kommentar den aufrufenden Programmteil darauf hinweisen.
M. Goedicke – Programmierung WiSe 2020/2021 28

Objektkonsistenz trotz Fehlersituation sicherstellen
▪ Reaktion auf Fehler (Meldung oder Korrektur)
▪ möglichst früh – idealerweise vor Zustandsänderung des Objekts
▪ Objekt könnte sonst inkonsistenten Zustand annehmen – Beispiel: ▪ public Date ausleihen (Benutzer nutzer) {
leihfrist = …; // Attribut leihfrist setzen if (nutzer != null)
ausleiher = nutzer // Attr. ausleiher setzen else throw new IllegalArgumentException(); …
}
▪ In diesem Fall kann es passieren, dass das Attribut leihfrist zwar bereits verändert wird, dann aber kein ausleiher mehr eingetragen wird
➢ Das Objekt würde sich in einem inkonsistenten Zustand befinden!
M. Goedicke – Programmierung WiSe 2020/2021 29

Objektkonsistenz trotz Fehlersituation sicherstellen
▪ Besser: Prüfung vor Zustandsänderung
▪ public Date ausleihen (Benutzer nutzer) { if (nutzer == null)
throw new IllegalArgumentException();
ausleiher = nutzer; // Attribut ausleiher setzen leihfrist = new Date(…); // Attr.leihfrist setzen return leihfrist;
}
➢ Entweder werden alle Attribute konsistent zueinander verändert oder keines.
M. Goedicke – Programmierung WiSe 2020/2021 30

Zusammenfassung: Exceptions werfen und fangen
▪ Deklarieren und Werfen von Exceptions
typ methode (…)
throws Exceptionklasse1, Exceptionklasse2 {

throw new Exceptionklasse1(); // oder throw new Exceptionklasse2(Nachricht); …
}
▪ Fangen und Behandeln von Exceptions

try {…}
catch (Exceptionklasse1 e) {…} catch (Exceptionklasse2 e) {…} finally {…}

M. Goedicke – Programmierung WiSe 2020/2021 31

Zusammenfassung: Behandlungsvarianten
▪ Exception mit catch fangen und behandeln
▪ Fehler dort behandeln, wo er signalisiert worden ist
▪ Exception mit catch fangen und andere Exception werfen ▪ z.B., um technische Fehlersignale in fachliche zu verwandeln
und sie auf höherer Ebene behandeln zu lassen
▪ Exception nicht fangen, sondern „weiterfliegen“ lassen ▪ Fehlersignal nicht selbst behandeln,
sondern von höherer Ebene behandeln lassen
M. Goedicke – Programmierung WiSe 2020/2021 32

Reviews

There are no reviews yet.

Only logged in customers who have purchased this product may leave a review.

Shopping Cart
[SOLVED] CS计算机代考程序代写 Java jvm ER compiler Programmierung
30 $