Skript: Grundlagen der Programmierung
Skript: Grundlagen der ProgrammierungKursname: Programmierung I (W3WI_108)
Modulbeschreibung International Management for Business and Information Technology
- Studiengang: International Management for Business and Information Technology Mannheim (Bachelor Wirtschaftsinformatik)
- 1. Semester
- 5 ECTS Punkte Arbeitsleistung: 150h, davon 60h Kontaktstudium.
Fachkompetenz: Die Studierenden kennen die grundlegenden Elemente, Strukturen und Konzepte einer objektorientierten Programmiersprache. Sie besitzen grundlegende Kenntnisse der Programmiermethodik, kennen Werkzeuge zur Implementierung und sind in der Lage, elementare Algorithmen in der Programmiersprache abzubilden. Methodenkompetenz: Die Studierenden können die Grundprinzipien der Programmierung und die Konzepte der Objektorientierung anwenden und autonom kleine bis mittlere lauffähige Programme in einer gängigen Entwicklungsumgebung implementieren und testen. Personale und Soziale Kompetenz: Die Studierenden können eigenständig passende Lösungen erarbeiten. Sie können stichhaltig und sachangemessen über Konzepte und eigene Implementierungen und damit verbundene Probleme argumentieren, eigene Umsetzungen plausibel darstellen und eventuelle Fehler nachvollziehbar gegenüber anderen begründen. Übergreifende Handlungskompetenz: Die Studierenden können unter Einsatz der Programmiersprache einfache praktische Probleme modellieren, algorithmisch behandeln und anwenderfreundlich lösen. |
Lehrinhalte
Prinzipien der Programmerstellung
Aufbau der Programmiersprache
|
Prozedurales und modulares Programmieren:
Grundprinzipien der objektorientierten Programmierung mit
|
- 46679 views
1. Einführung
1. EinführungDieser Abschnitt gibt...
- einen allgemeinen Überblick über Java.
- stellt die wichtigsten Programme des JDK vor
- erklärt das Konzept von Variablen und Zuweisungen.
- 20372 views
1.1 Buchreferenzen
1.1 Buchreferenzen
|
Kostenlose E-Books
- Gallileo Computing <openbook>
- Java 7 - Mehr als eine Insel
- Java ist auch eine Insel
- 9031 views
1.2 Algorithmen
1.2 Algorithmen
Im Diagramm links wird das typische Vorgehensmodell gezeigt welches von einer Problemstellung zur einer ausführbaren Anwendung führt.
Üblicherweise treten in jeder Phase Fehler auf, die ein Zurückgehen und Verbessern der Ergebnisse einer früheren Phase erfordern. |
Algorithmus
- Vorgehensbeschreibung, Handlungsanweisung
- Alltagsbeispiele: Kochrezept, Bastelanleitung, Partitur
- Mathematische Beispiele:
- Euklids ggT-Bestimmung, geometrische Konstruktionen
- Name abgeleitet von Muhammed al-Chwarizmis Buch „Über das Rechnen mit indischen Ziffern“ (um 825)
- Erster Computeralgorithmus: 1843 von Ada Lovelace für Charles Babbages Analytical Engine (Maschine wurde nicht vollendet)
Anforderungen an einen gültigen Algorithmus
- Er verlangt gültige Eingaben und liefert definierte Ausgaben, durch die seine Korrektheit überprüft werden kann
- Er umfasst endlich viele Einzelschritte und besteht aus hinreichend elementaren Anweisungen
- Er ist in realistischer Zeit ausführbar
- Er terminiert garantiert: d.h. ist nach endlich vielen Schritten fertig abgearbeitet, liefert ein Ergebnis und „hält an“
Algorithmus |
---|
|
- 7804 views
Beispiel
BeispielDas Problem des Findens einer Kubikwurzel ist ein Beispiel für das Entwickeln einer Lösungsidee und der Entwicklung eines Algorithmus.
Die Anwendung ist relativ klein und wahrscheinlich in der oberen linke Ecke des Bildschirms zu finden:
Herunterladen der jar Datei mit Demoprogramm. Starten:
Quellcode der Anwendung auf Github. |
ProblemBestimmen von Kubikwurzeln deren Ergebnis eine ganze Zahl zwischen Null und Hundert ist. Das Demoprogramm links erlaubt Ihnen Ihre Fähigkeiten zu testen. |
- 5214 views
Lösungsidee
LösungsideeLösungsidee 1: "Educated Guess"
Man kennt ja so einige Kubikwurzeln...
- Kubikwurzel von 1 : 1
- Kubikwurzel von 8 : 2
- Kubikwurzel von 27 : 3
- Kubikwurzel von 64 : 4
- Kubikwurzel von 125 : 5
- Kubikwurzel von 1000: 10
Lösungsidee 2: Taschenrechner mit Möglichkeit zur dritten Wurzel ziehen benutzen
Zum Kopfrechnen wenig geeignet...
Lösungsidee 3: Taschenrechner mit Potenzfunktion nutzen
Kubikwurzel x = x1/3
Zum Kopfrechnen auch weniger geeignet
Lösungsidee 4: Primzahlzerlegung!
8 = 2 * 2 * 2. Die Kubikwurzel von 8 ist 2!
Was tun bei größeren Zahlen?
Beispiel: 216 = 2 * 2 * 2 * 3 * 3 * 3
Lösung: 6 = 2 * 3
Vorgehen
Merke Dir jeden Primfaktor der dreimal vorkommt. Die Lösung ist das Produkt der Primfaktoren die 3 mal vorkommen.
Dieses Verfahren ist prinzipiell im Kopf zu meistern. Divisionen durch kleine Zahlen sind nicht so schwer.
- 4718 views
Algorithmus
AlgorithmusVorbedingung
Die vorgebene Zahl k soll eine Kubikwurzel aus den natürlichen Zahlen kleiner, gleich 100 besitzen
Der Algorithmus
Verbale Beschreibung | UML Diagramm |
---|---|
|
Wir gehen davon aus, dass Merken, Dividieren, Multiplizieren elementare Anweisungen sind die nicht weiter verfeinert werden müssen.
Der Algorithmus wird immer terminieren, da die Division durch den letzten Primfaktor eins ergeben wird.
Das Teilproblem Primfaktor von k bestimmen ist aber kein elementares Problem. Zur Lösung dieses Teilproblems wird eine neue Lösungsidee und ein neuer Algorithmus benötigt.
Teilalgorithmus Primfaktor einer Zahl k
Verbale Beschreibung | UML Diagramm |
---|---|
Wie kann man diesen Algorithmus schneller machen?
|
- 4042 views
Implementierung in Java
Implementierung in JavaImplementierung in Java
/** * Berechnet die Kubikwurzel einer positiven Zahl * * @param k Zahl von der die Kubikwurzel berechnet werden soll. * @return Ergebnis die Kubikwurzel */ public static int kubikwurzelVon(int k) { int ergebnis=1; do { int f = primfaktor(k); ergebnis = ergebnis*f; k = k / (f*f*f); } while (k>1); return ergebnis; } /** * Diese Methode berechnet einen Primfaktor des Werts k * Es wird 1 zurückgegeben wenn es keine anderen Primfaktoren * gibt * @param k Die Zahl von der ein Primfaktor berechnet wird * @return f ein Primfaktor der Zahl oder 1 falls keiner existiert */ public static int primfaktor(int k) { int f = 1; do { f++; } while ((k/f*f!=k)&&(f<k)); if (f==k) f=1; return f; }
Der Algorithmus zum Bestimmen eines Primfaktors ist sehr naiv...
- 4406 views
1.3 Grundlegende Konzepte
1.3 Grundlegende KonzepteIm Javaumfeld muss man zwei grundlegende Konzepte unterscheiden:
- Die Programmiersprache Java: Mit ihr werden die zu programmierenden Algorithmen beschrieben
- Das Laufzeitsystem Java zu dem man auch die Entwicklungswerkzeuge (JDK) zählt.
- Mit Hilfe der Entwicklungswerkzeuge werden die (menschlich) lesbaren Java-Quellcodeprogramme in ein maschinenlesbares Format übersetzt und auf diverse Fehler geprüft
- Das Laufzeitsystem kann dann diese übersetzten Programme ausführen.
- 5162 views
1.3.1 Grundkonzepte von Programmiersprachen
1.3.1 Grundkonzepte von ProgrammiersprachenProgrammiersprachen sind eine Ebene über den Maschinensprachen angesiedelt. Sie sind ein Mittel der Abstraktion und Strukturierung.
Die wichtigsten Komponenten einer Programmiersprache (Neuendorf S.40) sind:
- Datentypen: (primitive und strukturierte)
- Modellierung verschiedener Wertebereiche (Größen); Sicherheit durch Typprüfung
- Variablen und Konstanten
- Verwendung von Namen anstelle von Speicheradressen für Behälter die Daten verwalten
- Ausdrücke
- Verknüpfung von Variablen und Konstanten zu Termen die Werte berechnen
- Zuweisungen
- Speicherung von Ausdruckswerten in Variablen
- Ablaufsteuerungsanweisungen: Verzweigungen und Schleifen
- Manipulation des Programmverlaufs. Sie erlauben die Steuerung des Kontrollflusses
- Unterprogramme: Prozeduren, Funktionen, Methoden
- Kapselung von Programmteilen zur Wiederverwendung und übersichtlichen Strukturierung
Die oben aufgeführten Komponenten sind in allen gängigen Programmiersprachen zu finden.
In Java werden diese Komponenten zu Klassen zusammengeführt:
- Sie sind die Zusammenfassung von Daten und Methoden in eigenständigen gekapselten Einheiten
Der Zusammenhang zwischen diesen Komponenten wird im folgenden UML Diagramm vereinfacht gezeigt:
- 6056 views
Tippfehler?
Hallo!
Ich denke Sie haben einen kleinen Tippfehler bei den Komponenten gemacht. Es steht dort Unterprogrogramme anstelle von Unterprogramme. Da ich mir nicht sicher war, ob die nun wirklich so heißen, wollte ich nur einmal nachfragen, ob das so stimmt.
MFG
- Log in to post comments
1.3.2 Klassen und Objekte
1.3.2 Klassen und ObjekteBevor man ein System in einer Programmiersprache implementiert, verwendet man objektorientierte Techniken um mit dem Kunden den Problembereich (Anwendungsbereich) im Rahmen einer Systemanalyse zu modellieren.
Anschließend werden die modellierten Objekte und ihre Datentypen in eine Programmiersprache abgebildet.
Im Rahmen des Kurses werden Diagramme in UML (Unified Modeling Language) zur Beschreibung von Javaklassen verwendet. In Java sowohl als UML werden die folgenden Begriffe verwendet:
- Klasse: Ein Typ eines Gegenstandes in der realen Welt. Mit Klassen werden nicht nur Typen der realen Welt beschrieben sondern auch abstrakte Konzepte wie zum Beispiel ein Vertrag
- Attribute: Datenfelder einer Klasse. Sie haben einen Namen zur Unterscheidung und einen Typ
- Methoden: Programme die auf alle Instanzen einer Klasse angewendet werden können.
- Methoden können keinen, einen oder mehrere Eingabewerte haben
- Methoden können keinen oder einen Rückgabewert haben
- Methoden können die Datenfelder der Objekte verändern
- Objekte:
- Ausprägungen von Klassen
- Entitäten mit gemeinsamen Eigenschaften
- Instanzen von Klassen
Objekte haben einen Lebenszyklus. Sie können erzeugt, modifiziert und dereferenziert werden. Dereferenzierte Objekte in Java sind nicht mehr erreichbar und daher nutzlos. Sie sind defacto gelöscht.
Modellierung in UML
Im Folgenden ist eine Klasse Flugzeug in UML und zwei Objekte der Klasse a380 und jumbo modelliert.
Implementierung in Java
Die Modellierung der Klasse Flugzeug in Java ist nachfolgend dargestellt.
Die Erzeugung der beiden Objekte a380 und jumbo erfolgt in der Methode main(). Hierzu muss ein Javaprogramm ausgeführt werden.
Die Methode main() wird als Javaprogramm durch das Kommando java Flugzeug aufgerufen.
public class Flugzeug { String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette int leerGewicht; // Ein Attribut vom Type einer Ganzzahl /** * Eine Methode zum Drucken der Attributbelegung des Objekts * Die Methode erfordert keine Eingaben. Sie erzeugt keine * Aufgaben */ public void drucken() { System.out.println("Kennzeichen " + kennzeichen + " " + leerGewicht + "kg"); } /** * Die Methode main() wird zum Starten des Programms benutzt * @param args Übergabe von Konsolenparameter. Hier nicht genutzt */ public static void main(String[] args) { // Erzeugen zweier Objekte Flugzeug jumbo = new Flugzeug(); Flugzeug a380 = new Flugzeug(); // Belegen der Attribute des ersten Objekts mit Werten jumbo.kennzeichen = "D-ABYT"; jumbo.leerGewicht = 191000; // Belegen der Attribute des zweiten Objekts mit Werten a380.kennzeichen = "D-AIMD"; a380.leerGewicht = 286000; // Drucken der beiden Objekte auf der Konsole jumbo.drucken(); a380.drucken(); } }
Das obige, ausführbare Programm hat die folgende Grundstruktur die in allen Javaklassen zufinden ist.
Javaklassen können noch mehr Bestandteile haben. Für die Einführung sind die folgenden Bestandteile die wichtigsten:
- Eine Javaklasse sollte in einer Datei mit dem gleichen Namen und der Extension .java gespeichert werden
- Das Schlüsselwort class steht unmittelbar vor dem gewählten Namen der Klasse
- Vor dem Schlüsselwort class können noch weitere Schlüsselworte stehen, die z.Bsp. die Sichbarkeit der Klasse bestimmen
- Nach dem Klassennamen können noch Oberklassen oder Implementierungen angegeben werden
- Die Implementierung der Klasse steht zwischen dem folgenden geschweiften Klammerpaar
- Attribute einer Klasse haben einen Typ und einen Namen. Sie werden immer mit einem Semikolon beendet.
- Methoden einer Klasse besitzen
- optionale Schlüsselworte um die Methodeneigenschaften zu spezifizieren
- Rückgabetypen: Der Typ des Ergebnis der Berechnung
- einen Namen
- eine Parameterliste die mit runden Klammern beschrieben wird. Sie kann leer sein.
- Einen Rumpf mit der Implementierung der Methode. Der Rumpf wird mit geschweiften Klammern implementiert. Die Implementierung des Rumpfs steht zwischen den geschweiften Klammern
- 6306 views
1.4 Java Abgrenzung, Begriffsbestimmung
1.4 Java Abgrenzung, BegriffsbestimmungJava ist:
- eine der vier großen Sundainseln der Republik Indonesien
- im amerikanischen benutztes Synonym für: (guten) Kaffee
- alles was ein Softwareentwickler braucht ist ausreichend Kaffee
- eine Programmiersprache und das damit verbundene Laufzeitsystem
Java in der Softwareentwicklung
- Markenzeichen (Trademark) der Firma Oracle (ehemals Sun Microsystems)
- nur von Oracle zertifizierte und/oder lizensierte Produkte dürfen das Warenzeichen führen
- Programmiersprache
- C/C++ ähnliche Syntax
- 1995 veröffentlicht
- konservative Weiterentwicklung der Sprache um Fragmentierung zu vermeiden
- standardisierter Bytecode
- wird aus Javaquellcode generiert
- plattform- und betriebssystemunabhängig
- Virtuelle Maschine (VM: Laufzeitumgebung)
- führt (neutralen) Bytecode aus
- verfügbar von unterschiedlichen Anbietern für viele Betriebsysteme
- Laufzeitbibliotheken (Java API)
- standardisiert
- optionale, sowie verpflichtende Komponenten
- reichhaltiger Funktionsvorrat der alle wesentlichen Funktionen eines Betriebssystem portabel für die Anwendung zur Verfügung stellt
- Graphik
- Netzwerkommunikation
- Mathematik
- Verschlüsselung
- Dateizugriff
- Datenbankzugriff
- etc.
- standardisiert
- Java "Editionen" (Editions)
- Funktionalitätsbündel für unterschiedliche Anwendungszwecke
- gleiche Sprache, unterschiedliche VM, unterschiedlicher Bibliotheksumfang
- z.Zt. existierende Editionen
- SE: Standard Edition (Gegenstand des Kurses!)
- Desktopanwendungen, fundament für Serveranwendungen, zunehmend auch Smartphones
- EE: "Enterprise Edition"
- Middleware für Java-Applikationsserver
- Fokus: Mehrbenutzeranwendungen, Webfrontends, komplexe betriebswirtschaftliche Anwendungen
- ME: "Micro Edition"
- Mobile Endgeräte: Mobiltelefone, PDAs, Smartphone
- Fokus: Animation auf Mobiltelefonen, Nutzung der Mobiltelefoninfrastruktur
- unterschideliche "Profile und Konfigurationen" für unterschiedliche Geräteklassen
- wird nicht mehr wirklich verwendet...
- JavaCard
- Javalaufzeitumgebung für intelligente EC und Kreditkarten mit Chip
- weitere Varianten
- JavaFX : Laufzeitumgebung für multimediale Benutzeroberflächen
- Ist Seit Java 7.0 update 6 Teil der Javalaufzeitumgebung von Oracle
- Randbemerkung: JavaScript: Netscape führte im Frühjahr 1996 die Browserscriptsprache JavaScript ein. Netscape war zum damaligen Zeitpunkt ein sehr früher Kooperationspartner von Sun Microsystems, der als einer der ersten Partner eine Portierung von Java im Browser auslieferte. Sun Microsystems gewährte Netscape zum damaligen Zeitpunkt das Recht das Markenzeichen "Javascript" zu verwenden. Die Marke gehört noch heute Oracle (als Nachfolger von Sun Microsystems)
- Interessant: JavaScript hat technologisch gesehen nichts mit der Programmiersprache Java gemein.
- JavaFX : Laufzeitumgebung für multimediale Benutzeroberflächen
- SE: Standard Edition (Gegenstand des Kurses!)
- 6207 views
1.4.1 Java Standard Edition (SE)
1.4.1 Java Standard Edition (SE)Standalone Java-Programme
Java-Programme können lokal wie ein gewöhnliches Programm ausgeführt werden.
Java-Applikationsserver die selbst keine direkte Benutzeroberfläche haben und im Hintergrund laufen sind technisch gesehen Programme der Java Standardedition.
Java-Programme die selbst eine graphische Benutzerschnittstelle mit Hilfe der Java Swing/AWT Bibliotheken exponieren nennt man auch "Fat" oder "Rich Clients".
Das Starten eines Javaprogramms geschieht mit dem Kommando java welches die Javalaufzeitumgebung startet:
$ java HelloWorld Hello World!
Softwarepakte der Java Standard Edition
Hier wird der Stand der Entwicklung basierend auf Java 7 update 25 dokumentiert.
JRE: Java Runtime Environment
Dieses Softwarebündel enthält alle Komponenten die man zum Ausführen von Javaprogrammen benötigt. Seit Java 7u21 kann man sie in zwei Varianten installieren
JRE (für Desktops): Download
- Javalaufzeitumgebung
- Plugins und Bibliotheken für Browsereinbettung
- Sicherheitskonsole
- Kommentar: Dieses Javapaket sollten Sie installiert haben um die Applets des Kurses auf dieser Webseite zusehen
JRE for Server: Download
- Javalaufzeitumgebung
- Zusatzprogramme zum Monitoren von Serveranwendungen
- das Paket enthält keine Browserplugins
- Kommentar: Dieses Paket wird nicht für den Kurs benötigt
JDK: Java Development Kit (Download)
Dieses Softwarepaket wird für den Kurs verwendet. Es enhält
- Javalaufzeitumgebung
- Übersetzer
- Analyse- und Testprogrogramme
- Programme zum Erstellen von Javadokumentation
Hinweis: Sie müssen für den Kurs JRE und JDK installieren. Die Softwarepakete werden an unterschiedlichen Stellen installiert.
- 6175 views
1.4.1. JRE
JRE steht doch für Java Runtime Environment, statt für Java Runtime Edition, oder?
In Wikipedia z.B. steht auch, dass es Environment heißt.
lG
- Log in to post comments
1.4.2 Java Enterprise Edition
1.4.2 Java Enterprise EditionDie Java Enterprise Edition (JEE) ist eine Applikationservertechnologie die auf der Java Standard Edition aufbaut.
JEE Applikaktionsserver betreiben in der Regel Mehrbenutzeranwendungen die Webseiten für die Benutzer generieren. Die Enterprise Edition wird typischerweise in zwei Varianten eingesetzt.
- Webcontainer: Um die Klasse Servlet entwickelte Technologien zum Erzeugen von html Seiten
- EJB Container (Enterprise Java Beans): Ein auf EJB Klassen basierendes Framework um Anwendungslogik zu implementieren die unabhängig vom Benutzer ist (z. Bsp. Datenbank, Persistenz, Messaging, Sperren etc.)
Hinweis: Java EE ist nicht Gegenstand dieses Kurses.
- 4889 views
1.5 Java Historie
1.5 Java HistorieFreigabe | Neue Eigenschaften | ||
---|---|---|---|
JDK Beta | 1995 | AWT | |
JDK 1.0 | Jan. 1996 | ||
JDK 1.1 | Feb. 1997 | Innere Klassen | |
J2SE 1.2 | Dec. 1998 | JFC (Swing), Collections | |
J2SE 1.3 | May. 2000 | ||
J2SE 1.4 | Feb. 2002 | 64 Bit Unterstützung, Assertions | |
J2SE 5.0 | Sept. 2004 | Generics, Concurrency, Autoboxing, Enumerations | Anforderungen des Kurrikulums der DHBW... |
Java SE 6 | Dez. 2006 | updates Concurrency | |
Java SE 7 | Jul. 2011 | updates Concurrency, Unterstützung für dynamische Sprachen | |
Java SE 8 | März 2014 | Long Term Support | |
Java SE 9 | Sept. 2017 | Modularisierung (Project Jigsaw) | |
Java SE 10 | März 2018 | ||
Java SE 11 | Sept. 2018 | Long Term Support | |
Java SE 12 | März 2019 | ||
Java SE 13 | Sept. 2019 | Switch Expressions (Preview) | |
Java SE 14 | März 2020 | Switch Expressions (Standard) | |
Java SE 15 | Sept. 2020 | ||
Java SE 16 | März 2021 | ||
Java SE 17 | planned Sept. 2021 | planned: Long Term Support |
Referenz: Wikipedia
Bei den neuen Eigenschaften werden nur Eigenschaften genannt, die einen Bezug zur Vorlesung haben!
- 400 views
1.6 Programme des Java JDK
1.6 Programme des Java JDKJava Standard Edition (SE) in seiner Distribution von Oracle kann in zwei Varianten benutzt werden:
- JRE: Java Runtime Environment
- JDK: Java Development Kit (JDK Oracle Download Seite)
Das JDK ist eine echte Obermenge des JRE und enhält die folgenden Komponenten die man zur Entwicklung und Ausführung von Java Programmen im Rahmen des Kurses benötigt:
Program | Bedeutung | Kommentar |
---|---|---|
java | Startet Java Programme | Laufzeitsystem. Interpretiert Javabytecode. Übersetzt häufig verwendeten Javabytecode in Maschinencode |
javac | Übersetzer | Übersetzt Javaquellcode in Bytecode |
javadoc | Generieren von Dokumentation | Erzeugt Dokumentation durch Übersetzen von speziellen Kommentaren im Javaquellcode |
jar | Java Archiver | Bündelt Javabytecode und Dateien zu jar Dateien |
javap | Java Class File Disassembler | Extrahiert Schnittstelleninformation aus class Dateien und erzeugt öffentliche Schnittstellen |
jdb | Java Debugger | Kommandozeilendebugger |
jps | Anzeige der Prozess Ids von Java Programmen | Hilfswerkzeug für jconsole |
jconsole | Monitoren von Java Prozessen | graphisches Werkzeug zum monitoren von Java Anwendungen |
jinfo | Auslesen der Konfiguration eines laufenden Javaprozess | Konsolenausgabe der laufenden Prozessdaten |
Dies ergibt die folgenden Zusammenhänge zwischen den verschiedenen Dateien die bei der Entwicklung beteiligt sind:
Referenzen
- 6993 views
1.6.1 Das erste Javaprogramm (Benutzung der Kommandos java und javac)
1.6.1 Das erste Javaprogramm (Benutzung der Kommandos java und javac)Überblick
Das Übersetzen von Javaquellprogrammen in interpretierbare Bytecodedateien erfolgt mit dem Java-Übersetzer javac. Das Ausführen der übersetzten Dateien erfolgt mit dem Kommando java, dem Javalaufzeitsystem, welches Javaprogramme startet.
Javaübersetzer: javac
Der Javaübersetzer übersetzt lesbare Quelldateien mit der Dateiextension *.java in eine oder mehrere Bytecodedateien mit der Dateiextension *.class.
Beispiel
$ javac HelloWorld.java
Dieser Befehl erzeugt eine Datei mit dem Namen HelloWorld.class im gleichen Verzeichnis
Tipp: DerJavaübersetzer erwartet den vollen Dateinamen inklusive der Dateiextension .java
Javalaufzeitsystem: java
Das Kommando java erlaubt das Starten von Java-Programmen.
Beispiel:
$ java HelloWorld
Javaprogramme müssen als Bytecodedateien (Dateiextension *.class) oder als zu Java-Archiven (Dateiextension *.jar) gebündelte Bytecodedateien vorliegen.
Tipp: Das Kommando java akzeptiert nur den Namen der auszuführenden Klasse. Es akzeptiert nicht den Namen der gleichnamigen Datei mit dem Bytecode!
Das Kammando java sucht dann standardmässig im aktuellen Verzeichnis nach einer Bytecodedatei (Extension *.class) die den Interpretercode für die gewünschte Klasse enthalten.
Hiermit ergibt sich die Abfolge der Kommandos für ein einfaches Testprogrann HelloWorld.java:
1. Testen der Java Laufzeitumgebung
Überprüfen sie ob eine Javalaufzeitumgebung vorhanden ist. Dies geschieht mit der Option -version des Programms java:
$ java -version openjdk version "11.0.11" 2021-04-20 LTS OpenJDK Runtime Environment Corretto-11.0.11.9.1 (build 11.0.11+9-LTS) OpenJDK 64-Bit Server VM Corretto-11.0.11.9.1 (build 11.0.11+9-LTS, mixed mode)
2. Erstellen des Java-Quellcodes
Erstellen sie eine Textdatei mit dem Namen HelloWorld.java mit dem folgenden Inhalt:
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } }
Zum Beispiel mit gedit:
3. Übersetzen des Quellcodes
Das Programm javac (javac.exe in Windows) übersetzt den Quellcode.
$ javac HelloWorld.java
Das Ergebnis ist eine Datei mit dem Namen HelloWorld.class. Diese Datei enthält den interpretierbaren Bytecode des Programms.
4. Ausführen des Programmes
Der generierte Bytecode der Datei HelloWorld.class wird dann mit Hilfe des Programms java ausgeführt. Die extension .class wird nicht mit angegeben:
$ java HelloWorld Hello World!
- 8772 views
javac
javacDie (vereinfachte) Syntax von javac ist:
$ javac [Optionen] [Quelldateien]
Die wichtigsten Optionen des Javaübersetzers javac sind:
- -classpath -cp classpath : Verzeichnisse in denen nach .class Dateien gesucht werden soll
- -d directory : Verzeichnis in dem die erzeugten .class Dateien abgelegt werden. Das Verzeichnis muss bereits existieren. Fehlt diese Option, so werden die erzeugten .class Dateien im aktuellen Verzeichnis abgelegt
- - help: druckt alle Standardoptionen auf der Konsole
- -source release: Erlaubt das Parsen der Javaquelldateien nach alten Sprachstandards [1.5,5,1.4,1.3].
- -sourcepath sourcepath: Suchen von Quelldateien in den angegebenen Verzeichnissen und jar Archiven
- -X : Anzeige der nicht Standardoptionen
- 5520 views
1.6.2 Generieren von Schnittstelleninformation
1.6.2 Generieren von SchnittstelleninformationSchnittstellengenerierung mit javap
Der Schnittstellengenerator javap wird im gleichen Verzeichnis aufgerufen in dem sich die Datei HelloWorld.class befindet. Rufen Sie ihn mit dem Befehl "javap HelloWorld" auf. Die Dateiendung .class wird nicht benötigt. javap benutzt nicht die Quelldatei. javap benutzt die Binärdatei und sucht sie im vorgegebenen Suchpfad für Binärdateien
$ javap HelloWorld Compiled from "HelloWorld.java" class HelloWorld extends java.lang.Object{ HelloWorld(); public static void main(java.lang.String[]); }
Es werden die Informationen über alle öffentlichen Eigenschaften generiert. Es werden nicht Informationen über die Implementierung und private Attribute und Methoden generiert.
- 4960 views
1.6.3 Generieren von Javadokumentation
1.6.3 Generieren von Javadokumentation1. Editieren Sie die Datei HelloWorld.java
/** * * @author Ihr Name */ public class HelloWorld { /** * @param args the command line arguments */ public static void main(String[] args) { System.out.println("HelloWorld"); } }
Datei sichern...
2. Rufen Sie javadoc auf
sschneid@scalingbits:~/l1$ javadoc HelloWorld.java Loading source file HelloWorld.java... Constructing Javadoc information... Standard Doclet version 1.6.0_18 Building tree for all the packages and classes... Generating HelloWorld.html... Generating package-frame.html... Generating package-summary.html... Generating package-tree.html... Generating constant-values.html... Building index for all the packages and classes... Generating overview-tree.html... Generating index-all.html... Generating deprecated-list.html
3. Kontrolle Ergebnis: Öffnen Sie mit Ihrem Browser die Datei index.html
Beispiel (Screenshot)
- 5882 views
1.6.4 Javaprozess-Monitoring mit jconsole, jinfo, jps
1.6.4 Javaprozess-Monitoring mit jconsole, jinfo, jpsMonitoring mit jconsole
jconsole ist eine grafische Javaanwendung die es erlaubt die Konfiguration eines Javapozess' zur Laufzeit zu beobachten. Einige der Eigenschaften die beobachtet werden können sind:
- Speicherverbrauch
- Parameter mit denen die VM konfiguriert ist
- genauer Typ der VM
- CPU-Verbrauch
- Anzahl der Threads
- Anzahl geladene Klassen
- ...
Die Verwendung von jconsole geschieht wie folgt:
1. Starten eines Javaprogramm
Starten eines (länger) laufenden Javaprogramm
Starten des Programm DemoFrame
java DemoFrame
2. Bestimmen der Pozess-Id des laufenden Javaprogramms
Jeder Prozess des Betriebssystems hat eine eindeutige Nummer den "Process Identifier". Das Kommando jps listet unabhängig vom Betriebssystem alle Javaprozesse.
jps 254 16964 Jps 16959 DemoFrame
3. Starten von jconsole
jconsole 16959
Wichtig: Das Javaprogramm darf zum Zeitpunkt an dem jps und jconsole aufgerufen werden noch nicht beendet sein!
Laufende jconsole Anwendung:
Monitoring mit jinfo
jinfo liest ebenfalls die wichtigsten Kenndaten eines laufenden Prozesses aus und gibt sie auf der der Konsole aus.
Das erfassen der ProzessId geschieht auch mit dem Hilfsprogramm jps:
1. Starten des Javaprogramms
Starten (länger) laufenden Javaprogramm
Starten des Programm DemoFrame
java DemoFrame
2. Bestimmen der Pozess-Id des laufenden Javaprogramms
Pegasus:bin sschneid$ jconsole 16959 Pegasus:bin sschneid$ jps 254 17168 Jps 17166 DemoFrame
3. Starten von jinfo
Pegasus:bin sschneid$ jinfo 17166 Attaching to process ID 17166, please wait... Debugger attached successfully. Client compiler detected. JVM version is 16.3-b01-279 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment sun.boot.library.path = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Libraries java.vm.version = 16.3-b01-279 awt.nativeDoubleBuffering = true gopherProxySet = false mrj.build = 10M3065 java.vm.vendor = Apple Inc. java.vendor.url = http://www.apple.com/ path.separator = : java.vm.name = Java HotSpot(TM) Client VM file.encoding.pkg = sun.io sun.java.launcher = SUN_STANDARD user.country = DE sun.os.patch.level = unknown java.vm.specification.name = Java Virtual Machine Specification user.dir = /Users/sschneid/Documents/JavaKurs/beispiele/l1/HelloWorld java.runtime.version = 1.6.0_20-b02-279-10M3065 java.awt.graphicsenv = apple.awt.CGraphicsEnvironment java.endorsed.dirs = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/endorsed os.arch = i386 apple.awt.graphics.UseOpenGL = false java.io.tmpdir = /var/folders/UO/UOnPVFsvGEO5k3UJnjadeE+++TI/-Tmp-/ line.separator = java.vm.specification.vendor = Sun Microsystems Inc. os.name = Mac OS X sun.jnu.encoding = MacRoman java.library.path = .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java java.specification.name = Java Platform API Specification java.class.version = 50.0 sun.management.compiler = HotSpot Client Compiler os.version = 10.6.4 http.nonProxyHosts = local|*.local|169.254/16|*.169.254/16 user.home = /Users/sschneid user.timezone = java.awt.printerjob = apple.awt.CPrinterJob file.encoding = MacRoman java.specification.version = 1.6 java.class.path = . user.name = sschneid apple.awt.graphics.UseQuartz = false java.vm.specification.version = 1.0 java.home = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home sun.arch.data.model = 32 user.language = de java.specification.vendor = Sun Microsystems Inc. awt.toolkit = apple.awt.CToolkit java.vm.info = mixed mode java.version = 1.6.0_20 java.ext.dirs = /Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/ext sun.boot.class.path = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jsfd.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/Resources/Java/JavaRuntimeSupport.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/ui.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/laf.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/sunrsasign.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jsse.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/charsets.jar java.vendor = Apple Inc. file.separator = / java.vendor.url.bug = http://bugreport.apple.com/ sun.io.unicode.encoding = UnicodeLittle sun.cpu.endian = little mrj.version = 1060.1.6.0_20-279 socksNonProxyHosts = local|*.local|169.254/16|*.169.254/16 ftp.nonProxyHosts = local|*.local|169.254/16|*.169.254/16 sun.awt.exception.handler = apple.awt.CToolkit$EventQueueExceptionHandler sun.cpu.isalist = VM Flags:
- 6194 views
1.7 Variablen und Zuweisungen
1.7 Variablen und ZuweisungenZur Verwaltung der Objekte(Daten) in einem Programm dienen Datenbehälter die Variable oder Konstante genannt werden.
Datenbehälter | Bedeutung | Beispiel |
---|---|---|
Konstante |
Ein Datenbehälter dem über die gesamte Lebensdauer eines Programms genau ein Wert zugewiesen wird. Hinweis: In Java ist eine Konstante eine Variable die beim Deklarieren mit dem Schlüsselwort final versehen wurde. Eine Konstante ist in Java eine Variable die nur einmal mit einem Wert belegt werden kann. |
pi = 3.14; |
Variable | Datenbehälter der mit einem bestimmten Wert initialisiert wird: Ihm können im Laufe der Programmausführung unterschiedliche Werte zugewiesen werden |
x = 0; x = 1; x = x +1; |
Variablen müssen in Java immer deklariert sein bevor man sie benutzen kann. Dies erfolgt mit einer Deklaration in der folgenden Syntax:
Typ der Variablen: bestimmt den Wertebereich und Operationen auf einer Variablen. Er ist fest für die Lebensdauer einer Variablen.
Variablennamen: Ein beliebiger Bezeichner zum Benennen der Variable. Der Variablenname muß innerhalb eines gegebenen Kontexts eindeutig sein. Man kann einen Namen nicht zweimal in einem bestimmten Kontext verwenden.
Beispiele:
int x; float y,z;
Variablen erhalten mit Zuweisungen eine neue Belegung. Hierfür können Konstanten, Variablen oder Audrücke benutzt werden deren Ergebnis auf eine Variable zugewiesen wird.
Die Syntax einer Zuweisung ist in Java immer:
|
Beispiele:
int x = 1; float y = 3.2* 4.1;
Die Bedeutung in Java ist:
Zuweisungsoperator Java ( Variable = Ausdruck;) |
---|
Nimm das Ergebnis der Berechnung der rechten Seite des Operators und weise der Variablen auf der linken Seite das Ergebnis zu. |
Java führt immer zuerst die Berechnung des Ausdrucks auf der rechten Seite aus. Erst dann weist Java das Ergebnis der Variablen zu.
Der Ausdruck selbst muss nicht unbedingt eine komplexe Berechnung sein. Er kann auch eine Variable oder Konstante sein.
Tipps zum Verstehen einer Zuweisung
- Identifizieren sie die 4 Bestandteile einer Zuweisung
- Zuweisungsoperator =
- Semikolon
- Variable (links vom Zuweisungsoperator)
- Term (rechts vom Zuweisungsoperator, links vom Semikolon
- Lesen Sie eine eine Zuweisung immer von rechts nach links. Zuerst wird der Term auf der rechten Seite berechnet. Dann wird das Ergebnis der linken Seite zugewiesen
Ein Beispiel sei ein einfaches Programm welches die Eingaben in den Variablen x und y nutzt um durch fortgesetzte Addition zu Multiplizieren.
Prozessorschritte | x | y | z |
---|---|---|---|
Initialisierung x=2; y=3; z=0; x = x -1; z = z + y; x = x - 1; z = z + y; |
2 1 1 0 |
3 3 3 3 |
0 0 3 6 |
Ergebnis in z | 0 | 3 | 6 |
Beispiele
Zuweisung einer Konstanten auf eine Variable. Um genau zu sein: Es wird ein Literal zugewiesen.
pi = 3.14;
Zuweisung einer Variablen auf eine andere Variable:
d = pi;
Zuweisung eines mathematischen Ausdrucks(Term) auf eine Variable. Der Term wird vor der Zuweisung berechnet.
d = 2*pi*r;
Zuweisung eines Terms der die Variable selbst enthält. Der Term wird vor der Zuweisung mit dem alten Wert der Variablen berechnet. Erst nach der Berechnung wird der neue Wert zugewiesen.
d = 2* d;
- 7258 views
1.8 Übungen
1.8 Übungen
1.7.1 Das erste Java Programm
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } } |
- Übersetzen Sie die Datei HelloWorld.java mit Hilfe des Kommandos javac in eine Bytecode-Datei mit dem Namen HelloWorld.class.
- Führen Sie das Programm mit dem Kommando java aus.
1.7.2 Eine einfache grafische Oberfläche
|
1.7.3 Swing Rechner
Diese Übung ist eine Gruppenübung. Sie ist wahrscheinlich zu schwer für das Selbststudium.
1. Quellcode erzeugen
Erzeugen Sie eine Datei mit dem Namen SwingRechner.java und dem folgendem Inhalt:
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; public class SwingRechner extends javax.swing.JFrame { private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JPanel jPanel1; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; private javax.swing.JTextField jTextFieldOut; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; private void initComponents() { jTextField1 = new javax.swing.JTextField(); jTextField2 = new javax.swing.JTextField(); jTextFieldOut = new javax.swing.JTextField(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("Swing Rechner"); jTextField1.setText("0"); jTextField1.setColumns(6); jTextField2.setText("0"); jTextField2.setColumns(6); jTextFieldOut.setText("0"); jTextFieldOut.setEditable(false); jButton1.setText("XXXX"); jButton2.setText("YYYY"); jButton3.setText("ZZZZ"); JPanel radioPanel = new JPanel(new GridLayout(1, 0)); radioPanel.add(jButton1); radioPanel.add(jButton2); radioPanel.add(jButton3); jButton1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton1) jTextFieldOut.setText( executeOperation1(jTextField1.getText(), jTextField2.getText())); } } ); jButton2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton2) jTextFieldOut.setText( executeOperation2(jTextField1.getText(), jTextField2.getText())); } } ); jButton3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton3) jTextFieldOut.setText( executeOperation3(jTextField1.getText(), jTextField2.getText())); } } ); this.setBounds(300, 300, 200, 30); setMinimumSize(new Dimension(200,30)); getContentPane().add(jTextField1, BorderLayout.WEST); getContentPane().add(jTextField2, BorderLayout.EAST); getContentPane().add(radioPanel, BorderLayout.NORTH); getContentPane().add(jTextFieldOut, BorderLayout.SOUTH); pack(); } public SwingRechner() { initComponents(); } public static void main(String[] args) { SwingRechner f1 = new SwingRechner(); f1.setVisible(true); } public String executeOperation1(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); // Add Application logic here: int resultInt = 0; return (Integer.toString(resultInt)) ; } public String executeOperation2(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); // Add Application logic here: int resultInt = 1; return (Integer.toString(resultInt)) ; } public String executeOperation3(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); int resultInt = 2; return (Integer.toString(resultInt)) ; } }
2. Erzeugen Sie die Javabytecodedatei SwingRechner.class
javac SwingRechner.java
3. Führen sie das Programm aus
java SwingRechner
Das erscheinende Programmfenster sollte so aussehen
Programmvorlage | Gelöste Aufgabe |
---|---|
4. Programmanpassung
Passen Sie das Programm an um einen Rechner für drei Grundrechenarten zu erhalten. Ersetzen sie in SwingRechner.java die folgenden Texte
- XXXX durch ein Symbol einer Rechenart
- YYYY durch eine Symbol einer Rechenart
- ZZZZ durch ein Symbol einer Rechenart
Implementieren Sie die drei entsprechenden Grundrechenarten in den Methoden executeOperation1(), executeOperation2(), executeOperation3().
Ändern Sie hierfür die drei Zuweisungen sinngemäss und ersetzen sie die Zuweisungen von 0, 1, 2 durch den passenden mathematischen Term mit op1 und op2.
... int resultInt = 0; ... int resultInt = 1; ... int resultInt = 2; ...
Das Programm soll anschliesend die Operationen passend zu den Buttonbeschriftungen ausführen
5. Speichern, Übersetzen und Ausführen des Programms
1.7.4 Übersetzen und Ausführen eines Programms mit Konsoleneingaben
Erzeugen Sie eine Datei mit dem entsprechenden Namen. Setzen Sie den unten aufgeführten Quellcode ein. Übersetzen Sie ihn. Führen Sie ihn aus.
import java.util.Scanner; public class GGT { public static void main(String[] args) { Scanner eingabe = new Scanner(System.in); System.out.print("Geben Sie die erste Zahl ein: "); int zahl1 = eingabe.nextInt(); System.out.print("Geben Sie die zweite Zahl ein: "); int zahl2 = eingabe.nextInt(); int ergebnis = ggT(zahl1, zahl2); System.out.println("Ergebnis: " + ergebnis); } /** * Euklidscher Algorithmus als Java-Methode */ public static int ggT(int x, int y) { while (x != y) { System.out.println("Zwischenbelegung, x= " + x + ", y=" + y); if (x > y) { x = x - y; } else { y = y - x; } } return x; } }
1.7.8 Airline in Block 1
Warnung: Alle verwendeten Namen in Java sind sensitiv auf Klein- und Großschreibung! Dies bedeutet, dass für Java ein Name der wie in der deutschen Sprache mit einem Großbuchstaben beginnt, etwas anderes ist als ein Name mit der mit einem Kleinbuchstaben beginnt.
Legen Sie ein Projekt "javakurs1" für die erste Vorlesung an.
Benutzen Sie in Netbeans den Menüpunkt File->New Project
Sie erhalten dann einen modalen Dialog. Wählen Sie "Java Application" dann den Button "next".
Der Name des Projekts ist prinzipiell frei wählbar und hat keinen weiteren Einfluß für die Vorlesung (Sie müssen dieses Projekt nur wieder finden und sie sollten hier keine Klassen benutzen die noch in der Vorlesung kommen werden).
- Tragen Sie nur im ersten Feld "Project Name" den Wert "javakurs1" ein.
- Entfernen Sie in der Checkbox "Create Main Class" den Haken.
- Clicken Sie auf "Finish"
Empfehlung: Ändern Sie in diesem keines der anderen Felder und einen wirklich guten Grund
2. Anlegen eines Pakets "Airline"
Pakete dienen zum Trennen und Gruppieren von Klassen in Java.
Bewegen Sie die Maus über das Projektsymbol (Kaffeetasse) mit dem Namen "javakurs1" und lassen Sie sich mit einem rechte Mausklick mit Halten das folgende Auswahlmenü anzeigen.
Wählen Sie hier "New"->"Java Package" aus:
Es erscheint der folgende modale Dialog:
Tragen Sie bei "Package Name" den Wert "Airline" ein un beenden Sie den Dialog mit dem "Finish" Button.
3. Erzeugen eines Unterpakets "Block1" im Paket "Airline"
In jeder Vorlesungsstunde(Block) wird in der Regel ein neues Paket verwenden.
Diese Aufgabe müssen Sie in jeder Vorlesungsstunde sinngemäß wiederholen.
In der linken Spalte kann man das Projekt "javakurs1" sehen. Diese Projekt hat eine Unterkategorie "Source packages". Hier sollte es jetzt einen Eintrag für ein Paket mit dem Namen "Airline" geben.
Man kann Unterpakte erzeugen, in dem man die Maus auf das Symbol von "Airline" bewegt. Führen Sie auf diesem Symbol einen Rechtsklick aus und halten Sie ihn.
Wählen Sie hier, wie zuvor "New"->"Java Package" aus
Es erscheint ein modaler Dialog. Tragen Sie hier im Feld "Package Name" den Wert "Airline.Block1" ein.
Beenden Sie den Dialog mit "Finish"
Wichtig: Hier wird einer Unterpaket zum Paket Airline angelegt. Der Punkt zwischen "Airline" und "Block1" trennt die beiden Namen. Er ist notwendig. Hier darf kein anderes Zeichen (z.Bsp. Leerzeichen stehen).
4. Anlegen der Klasse Flugzeug im Paket Airline.Block1
Gehen Sie link im Editor mit der Maus zum Paket "Airline.Block". Führen Sie einen Rechstklick aus und halten Sie ihn.
Es erscheint ein modaler Dialog.
Tragen Sie im Feld "Class Name" Flugzeug ein.
Ändern Sie die anderen Felder nicht.
Beenden Sie den Dialog mit dem Button "Finish"
Sie haben jetzt eine leer Klasse Flugzeug im Paket Airline.Block1 angelegt.
Diese Klassen können Sie nun editieren. Der Editor sieht wie folgt aus:
5. Erweitern der Klasse Flugzeug
Geben Sie Flugzeugen die Eigenschaft ein Kennzeichen zu haben. Kennzeichen sollen beliebige Zeichenketten sein. Zeichenketten sind in Java Klassen mit dem Namen String.
public class Flugzeug { String kennzeichen; }
Sichern Sie diese Datei. Sie finden schon raus wie das geht :-)
Wenn die Syntax der Klasse korrekt ist, werden am rechten Rand keine roten Symbole erscheinen.
Hinweis: Sie können Javacode der Vorlesung mit Copy-und-Paste direkt in Eclipse oder Netbeans einfügen. Unter Windows kommt es vor, dass die Zeilenumbrüche von html verloren gehen. Nutzen Sie in diesem Fall die Windows Anwendungen Notepad oder Wordpad als "Zwischenlandeplatz".
6. Anlegen der Klasse Flughafen
Nutzen Sie Ihre neuen Kenntnisse und Erzeugen Sie eine Klasse Flughafen im Paket Airline.Block1.
7. Erweitern der Klasse Flughafen
Flughäfen sollen die folgenden Eigenschaften haben
- einen Namen name der mit Zeichenketten verwaltet wird
- Sechs Flugsteige gate1 bis gate6 an denen Flugzeuge andocken können
- ein Treibstofflage treibstofflager in dem mit einer großen Fließkommazahl (double) der gelagerte Treibstoff verwaltet
Dies geschieht wenn Sie die Klasse Flugzeug mit dem fett gedruckten Text erweitern:
public class Flughafen { String name; Flugzeug gate1; Flugzeug gate2; Flugzeug gate3; Flugzeug gate4; Flugzeug gate5; Flugzeug gate6; double treibstoffLager; }
Es sollten nach Ihren Anpasssungen, links kein Fehler aufleuchten!
8. Schreiben eines Hauptprogramms in der Klasse Flughafen
Fügen Sie eine leer Methode mit dem Namen main() ein. Die Syntax muss exakt so aussehen wie im fett gedruckten Text
public class Flughafen { String name; Flugzeug gate1; Flugzeug gate2; Flugzeug gate3; Flugzeug gate4; Flugzeug gate5; Flugzeug gate6; double treibstoffLager; public static void main(String[] args) { } }
Nach dem Sichern der Datei Flughafen hat sich die Ikone in der linken Spalte verändert. Sie enthält jetzt eine grünes Dreieck.
Das grüne Dreick hat die Bedeutung der bekannten Starttaste. Diese Klasse kann man als Hauptprogramm ausführen! |
Diese Klasse können Sie ausführen indem Sie die Maus über das Symbol der Klasse bewegen |
Unten auf dem Bildschirm taucht eine Konsole auf. Es werden zunächst alle geänderten Klassen übersetzt und dann die gewünschte Klasse gestartet. Da die main() Methode der Klasse Flugzeug aber nichts macht, gibt es auch eine Ausgaben. |
Fügen Sie den folgenden Code innerhalb der geschweiften Klammern ein, die die Methode main() begrenzen.
public static void main(String[] args) { Flughafen pad = new Flughafen(); pad.name="Paderborn"; pad.treibstoffLager = 1000000; System.out.println("*** Unser Flughafen ***"); System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1); System.out.println("Am Gate 2: " + pad.gate2); System.out.println("Am Gate 3: " + pad.gate3); System.out.println("Treibstoff: " + pad.treibstoffLager); System.out.println("***********************"); }
Sichern Sie die Datei und starten Sie die Klasse Flughafen erneut an.
Sie sollten die folgende Konsolenausgabe sehen:
Was ist hier passiert?
Flughafen pad = new Flughafen(); |
Eine Zeigervariable pad vom Typ Flughafen wird angelegt. Es wird ein neues Flughafenobjekt erzeugt und auf die Variable zugewiesen. |
pad.name="Paderborn"; |
Das Objekt pad bekommt seinen Namen "Paderborn" zugewiesen. |
pad.treibstoffLager = 1000000; |
Das Treibstofflager des Objekt pad bekommt den Wert 1000000 zugewiesen. |
System.out.println("*** Unser Flughafen ***"); |
Die Methode System.out.println() druckt Zeichenketten auf der Konsole.
|
System.out.println("Flughafen " + pad.name); |
Drucke den Namen des Objekt pad |
System.out.println("Am Gate 1: " + pad.gate1); |
Drucke die Belegung von gate1 |
System.out.println("Am Gate 2: " + pad.gate2); |
Drucke die Belegung von gate2 |
System.out.println("Am Gate 3: " + pad.gate3); |
Drucke die Belegung von gate3 |
System.out.println("Treibstoff: " + pad.treibstoffLager); |
Drucke die Treibstoffmenge
|
System.out.println("***********************"); |
Drucke Sternchen... |
9. Erzeugen eines Flugzeugs und Anlegen am Gate1
Implementieren Sie die folgenden Befehle am Ende der main() Methode.
- Legen Sie eine Zeigervariable vom Typ Flugzeug mit dem Namen lh1 and und Erzeugen Sie ein Flugzeugobjekt
- Geben Sie dem Flugzeug das Kennzeichen D-ABTL
- "Docken" Sie das Flugzeug am Gate 1 des Flughafens pad an.
- Geben Sie den Namen des Flugzeugs an Gate aus
- Wiederholen Sie all Druckbefehlee des vorhergenden Schritts.
Hierzu muss die folgende Codesequenz in der main() Methode vor der letzten geschwiften Klammer eingefügt werden:
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400 Flugzeug lh1 = new Flugzeug(); lh1.kennzeichen ="D-ABTL"; pad.gate1 = lh1; System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1.kennzeichen); System.out.println("*** Unser Flughafen ***"); System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1); System.out.println("Am Gate 2: " + pad.gate2); System.out.println("Am Gate 3: " + pad.gate3); System.out.println("Treibstoff: " + pad.treibstoffLager); System.out.println("***********************");
Sichern Sie die Datei. Übersetzen Sie Datei und führen Sie sie wieder aus.
Die Ausgabe auf der Konsole ist:
*** Unser Flughafen *** Flughafen Paderborn Am Gate 1: null Am Gate 2: null Am Gate 3: null Treibstoff: 1000000.0 *********************** Flughafen Paderborn Am Gate 1: D-ABTL *** Unser Flughafen *** Flughafen Paderborn Am Gate 1: AirlineSolution.Block1.Flugzeug@677327b6 Am Gate 2: null Am Gate 3: null Treibstoff: 1000000.0 ***********************
10. Anlegen eines zweiten Flugzeugs
Fügen Sie in der main() Methode nach dem Anlegen des ersten Flugzeugs die folgenden Befehle ein:
- Legen Sie eine Variable lh2 für ein zweites Flugzeug und erzeugen Sie ein Flugzeugobjekt wie beim ersten Flugzeug
- Das Kennzeichen des Flugzeugs soll D-AIMA sein
- Legen Sie das Flugzeug an Gate 2 des Flughafens pad
Fügen Sie hinter den Befehl der das erste Flugzeug an Gate 1 ausgibt einen gleichartigen Befehl für Gate 2
Die main() Methode sieht jetzt wie folgt aus. Die neuen Befehle sind fett gedruckt:
public static void main(String[] args) { Flughafen pad = new Flughafen(); pad.name="Paderborn"; pad.treibstoffLager = 1000000; System.out.println("*** Unser Flughafen ***"); System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1); System.out.println("Am Gate 2: " + pad.gate2); System.out.println("Am Gate 3: " + pad.gate3); System.out.println("Treibstoff: " + pad.treibstoffLager); System.out.println("***********************"); // Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400 Flugzeug lh1 = new Flugzeug(); lh1.kennzeichen ="D-ABTL"; pad.gate1 = lh1; // Airbus A380 https://de.wikipedia.org/wiki/Airbus_A380#A380-800 Flugzeug lh2 = new Flugzeug(); lh2.kennzeichen = "D-AIMA"; pad.gate2 = lh2; System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1.kennzeichen); System.out.println("Am Gate 2: " + pad.gate2.kennzeichen); System.out.println("*** Unser Flughafen ***"); System.out.println("Flughafen " + pad.name); System.out.println("Am Gate 1: " + pad.gate1); System.out.println("Am Gate 2: " + pad.gate2); System.out.println("Am Gate 3: " + pad.gate3); System.out.println("Treibstoff: " + pad.treibstoffLager); System.out.println("***********************"); }
Sichern Sie die Datei, übersetzen Sie sie, führen Sie die main() Methode der Klasse Flughafen aus.
*** Unser Flughafen *** Flughafen Paderborn Am Gate 1: null Am Gate 2: null Am Gate 3: null Treibstoff: 1000000.0 *********************** Flughafen Paderborn Am Gate 1: D-ABTL Am Gate 2: D-AIMA *** Unser Flughafen *** Flughafen Paderborn Am Gate 1: AirlineSolution.Block1.Flugzeug@677327b6 Am Gate 2: AirlineSolution.Block1.Flugzeug@14ae5a5 Am Gate 3: null Treibstoff: 1000000.0 ***********************
- 7347 views
1.9 Lösungen
1.9 Lösungen1.8.3 Swing Rechner
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; public class SwingRechner extends javax.swing.JFrame { private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JPanel jPanel1; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; private javax.swing.JTextField jTextFieldOut; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; private void initComponents() { jTextField1 = new javax.swing.JTextField(); jTextField2 = new javax.swing.JTextField(); jTextFieldOut = new javax.swing.JTextField(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("Swing Rechner"); jTextField1.setText("0"); jTextField1.setColumns(6); jTextField2.setText("0"); jTextField2.setColumns(6); jTextFieldOut.setText("0"); jTextFieldOut.setEditable(false); jButton1.setText("+"); jButton2.setText("-"); jButton3.setText("*"); JPanel radioPanel = new JPanel(new GridLayout(1, 0)); radioPanel.add(jButton1); radioPanel.add(jButton2); radioPanel.add(jButton3); jButton1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton1) jTextFieldOut.setText( executeOperation1(jTextField1.getText(), jTextField2.getText())); } } ); jButton2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton2) jTextFieldOut.setText( executeOperation2(jTextField1.getText(), jTextField2.getText())); } } ); jButton3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton3) jTextFieldOut.setText( executeOperation3(jTextField1.getText(), jTextField2.getText())); } } ); this.setBounds(300, 300, 200, 30); setMinimumSize(new Dimension(200,30)); getContentPane().add(jTextField1, BorderLayout.WEST); getContentPane().add(jTextField2, BorderLayout.EAST); getContentPane().add(radioPanel, BorderLayout.NORTH); getContentPane().add(jTextFieldOut, BorderLayout.SOUTH); pack(); } public SwingRechner() { initComponents(); } public static void main(String[] args) { SwingRechner f1 = new SwingRechner(); f1.setVisible(true); } public String executeOperation1(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); // Add Application logic here: int resultInt = op1+op2; return (Integer.toString(resultInt)) ; } public String executeOperation2(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); // Add Application logic here: int resultInt = op1-op2; return (Integer.toString(resultInt)) ; } public String executeOperation3(String s1,String s2) { int op1= Integer.parseInt(s1); int op2= Integer.parseInt(s2); int resultInt = op1*op2; return (Integer.toString(resultInt)) ; } }
Beispiel
1.7.8 Airline Block1
Klasse Flugzeug
package AirlineSolution.Block1;
/**
*
* @author stsch
*/
public class Flugzeug {
String kennzeichen;
}
Klasse Flughafen
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package AirlineSolution.Block1;
/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
double treibstoffLager;
public static void main(String[] args) {
Flughafen pad = new Flughafen();
pad.name="Paderborn";
pad.treibstoffLager = 1000000;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug();
lh1.kennzeichen ="D-ABTL";
pad.gate1 = lh1;
// Airbus A380 https://de.wikipedia.org/wiki/Airbus_A380#A380-800
Flugzeug lh2 = new Flugzeug();
lh2.kennzeichen = "D-AIMA";
pad.gate2 = lh2;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1.kennzeichen);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
// Hänge Flugzeug um. mover bewegt Flugzeug
// von Gate 1 nach Gate 3
Flugzeug mover = pad.gate1;
pad.gate1=null;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
pad.gate3= mover;
mover=null;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3.kennzeichen);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
}
}
- 5744 views
1.10 Fragen
1.10 Fragen
1.10.1 Javakommandos des JDK
|
1.10.2 Zuweisungen
- Welche vier Syntaxkomponenten enhält eine Anweisung?
- In welcher Reihenfolge stehen die vier Syntaxkomponenten
- Welche der folgenden Anweisungen sind erlaubt?
a = 4; a = a; a = b 18 = c; c = 18 + a; d = 18 - c; a,b = 18;
1.10.3 Zuweisungsbeispiel
Welche Belegung haben die Variablen x,y und z wenn alle Zuweisungen ausgeführt?
Notieren Sie zur Hilfe alle Zwischenzustände
Prozessorschritte | x | y | z |
---|---|---|---|
x = 2; y = 3; z = 4; |
2
|
3
|
4 |
Endergebnis | ? | ? | ? |
- 4562 views
Aufgabe 1.10.2 18 = c
Es fehlt das Semikolon. In der Lösung ist das Semikolon wieder vorhanden.
- Log in to post comments
Frage zu 1.10.2
Werden hier in der Frage das Wort Zuweisungen und Anweisungen als Synonym verwendet? Denn im Kapitel 1.7 ist nur von Zuweisungen die Rede.
- Log in to post comments
1.11 Antworten
1.11 Antworten1.11.1 Javakommandos des JDK
- Mit welchem Befehl übersetzt man ein Javaquellprogramm in eine Javabinärdatei? javac
- Welche Datei ensteht beim Übersetzen einer Datei mit dem Namen Fahrzeug.java die eine Klasse Fahrzeug enhält? Fahrzeug.class
- Mit welchem Befehl kann man Javabinärdateien ausführen? java
- Eine Datei Test.java enthält eine Klasse Test. Mit welcher Befehlsequenz übersetzen Sie die Javaquelldatei Test.java und führen die Klasse Test dann aus?
- javac Test.java
- java Test
- Nennen Sie mindestens drei Javabefehle die nur das JDK enthält nicht aber das JRE
- javac
- javadoc
- javap
- Welcher Typ von Dateien wird vom Befehl javadoc erzeugt?
- javadoc erzeugt Dokumentationsdateien im html Format. Die Dateiendung ist daher .html
1.11.2 Zuweisungen
- Welche vier Syntaxkomponenten enhält eine Anweisung?
- Variable
- Zuweisungsoperator =
- Term
- Semikolon
- In welcher Reihenfolge stehen die vier Syntaxkomponenten?
- siehe vorherige Antwort
- Welche der folgenden Anweisungen sind erlaubt?
a = 4; // korrekt a = a; // korrekt a = b // falsch. Das Semikolon fehlt 18 = c; // falsch. 18 ist kein gültiger Bezeichner. c = 18; wäre erlaubt (und sinnvoll) c = 18 + a; // korrekt d = 18 - c; // korrekt a,b = 18; // falsch. Man kann nur einer Variablen gleichzeitig einen Wert zuweisen
1.11.3 Zuweisungsbeispiel
Welche Belegung haben die Variablen x,y und z wenn alle Zuweisungen ausgeführt?
Hinweis: Es wurden nur geänderte Werte eingetragen. Ist ein Feld leer, so gilt der früherer Wert weiter oben
Prozessorschritte | x | y | z |
---|---|---|---|
x = 2; y = 3; z = 4; |
2 |
3 |
4 |
x = 5; |
5 |
||
x = 2 * x; |
10 | ||
y= z * x; |
|
40 |
|
z= 18 |
|
18 |
|
Endergebnis | 10 | 40 | 18 |
- 4946 views
1.12 Lernziele
1.12 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zur Einführung zu beantworten.
Feedback
- 5110 views
2. Typen, Operationen und Zuweisungen
2. Typen, Operationen und ZuweisungenDieser Unterrichtsblock beschäftigt sich mit den Grundlagen von Variablen und ihren Belegungen.
Am Ende dieses Blocks haben Sie ein Grundverständnis von
|
- 9975 views
2.1 Schlüsselwörter, Literale, Variablennamen, Kommentare
2.1 Schlüsselwörter, Literale, Variablennamen, KommentareMotivation
Auf dieser Seite lernen Sie wie man die wichtigsten Teile der Sprache Java nennt. Zum Beispiel die Teile einer Zuweisung: |
Schlüsselwörter
Java Schlüsselwörter sind:
abstract | continue | for | new | switch |
assert | default | if | package | synchronized |
boolean | do | goto | private | this |
break | double | implements | protected | throw |
byte | else | import | public | throws |
case | enum | instanceof | return | transient |
catch | extends | int | short | try |
char | final | interface | static | void |
class | finally | long | strictfp | volatile |
const | float | native | super | while |
Die Schlüsselworte in Kategorien gruppiert ergeben:
The Schlüsselworte const und goto sind reserviert auch wenn sie im Moment nicht benutzt werden.
true und false sind technisch gesehen boolsche Literale. (Java 7 Spez. §3.10.3). Das Gleiche gilt für das null Literal (Java 7 Spez. §3.10.7).
Namen (Identifier)
Siehe Java Spezifikation: Identifier
Regeln für Namen in Java:
- Gültige Namen dürfen nur aus Buchstaben, Ziffern, und den Zeichen "_" und "$" bestehen. Sie dürfen beliebig lang sein.
- Das erste Zeichen muss ein Buchstabe, "_" oder "$" Zeichen sein.
- Leerzeichen sind nicht erlaubt
- Schlüsselworte der Sprache Java dürfen nicht verwendet werden. (Boolsche Literale und das null Literal dürfen auch nicht verwendet werden)
- Groß- und Kleinschreibung von Buchstaben wird unterschieden
Literale
Literale dienen zum Beschreiben konkreter Werte in der Sprache Java.
Die Syntax wurde mit Hilfe von regulären Ausdrücken beschrieben.
Syntax | Typ | Beispiel |
---|---|---|
number | int | 17, -17 |
number[l|L] | long | 24l, -24L |
0[x|X]hex | int in Hexadezimaldarstellung | 0x01234567890ABCDEF |
0octal | int in Oktaldarstellung | 071 |
0b{0|1} | int in Binärdarstellung (erst seit Java 7, JSR 334) | 0b101010101010 |
[+|-][number].number | double (Fließkommazahl) | -3.1415 |
[+|-]number[f|F] | float (Fließkommazahl) | -3.1415F |
[+|-]number[d|D] | double (Fließkommazahl) | -3.1415E13D |
[+|-]number | int mit Vorzeichen (signed number) | -3, +3 |
[+|-]number.number[e|E][+|-]number | Exponentendarstellung | -3.1415E10 |
'character' | einzelnes Zeichen | 'a' |
"characters" | Zeichenkette | "aB10" |
"" | Leere Zeichenkette | "" |
\b | Zeichenposition 1 nach links (back space) | |
\t | Tabulator | |
\n | Zeilenvorschub | |
\f | Seitenvorschub | |
\r | Wagenrücklauf | |
\" | Doppeltes Anführungszeichen | |
\' | Einfaches Anführungszeichen | |
\\ | Schrägstrich rückwärts | |
\uNNNN | Unicodezeichen (NNNN in hexadezimal) | |
true | Boolscher Wert | true |
false | Boolscher Wert | false |
Zahlen
- Ganzzahlen
- Dezimalzahlen mit den Ziffern 0 bis 9 und Vorzeichen
- Beispiele: 1, -19, 45678
- Hexadezimalen mit den Ziffern 0 bis 9 und als Ziffer interpretierte Zeichen a bis f und einer Präfix 0x
- Beispiele: 0x1, 0xffff, 0xcafebabe
- Oktalzahlen mit den Ziffern 0 bis 7 und einer vorgestellten 0
- Beispiel 012 (=1*81+2*80=1010 , 077 (=7*81+7*80=6310)
- Dezimalzahlen mit den Ziffern 0 bis 9 und Vorzeichen
- Gleitkommazahlen,
- dargestellt im vertrauten Dezimalsystem
- Nachkommateil durch Punkt abgetrennt
- Beispiel: 5.4, 6. (stellt Gleitkommazahl 6.0 dar)
- Nachkommateil durch Punkt abgetrennt
- Zehnerpotenzen zur Basis Zehn werden mit dem Buchstaben e oder E bezeichnet
- Beispiel: 4E-2 stellt 4*10-2= 0.04 da.
- dargestellt im vertrauten Dezimalsystem
Ab JDK 7: Gruppierung in Zahlenliteralen
Seit JDK 7 sind durch die Integration des Projekts "Coin" (JSR 334) die Gruppierung von Zifferngruppen in Zahlenliteralen durch den Tiefstrich '_' möglich.
Der JSR Spezifikation entnommene Beispiele der neuen Syntax:
1234_5678 1_2_3_4__5_6_7_8L 0b0001_0010_0100_1000 3.141_592_653_589_793d 0x1.ffff_ffff_ffff_fP1_023 // Double.MAX_VALUE
nicht erlaubt sind:
_1234 0x_1234 1234_ 0x1.0_p_-1022
Warnung: Java 7,8,9 werden noch nicht überall eingesetzt:
- Bei Verwendung von javac Javaübersetzern von JDK 5 oder 6 wird diese Syntax nicht akzeptiert
- Bei Verwendung des Javaübersetzers mit Optionen zur Verwendung alter Syntaxstandards wie javac -source 1.6 in JDK 7,8,9 wird die neue Syntax auch nicht akzeptiert werden!
Zeichen und Zeichenketten
- Zeichen: Ein einzelnes Zeichen (char) wird in einfachen Hochkommas geschrieben
- Beispiele: 'a', '2', '!', ' '
- Zeichenketten: in Doppelhochkamma eingeschlossene Folgen von Zeichen
- Beispiele: "Das ist eine Zeichenkette",
- Tipp: Zeichenketten dürfen nicht über den Zeilenrand hinwegreichen!
Zeichenketten ohne Namen nennt man Literale.
Kommentare
Kommentare erlauben das Dokumentieren eines Programmes. Sie werden vom Übersetzer ignoriert.
Java benutzt das Kommentarkonzept auch zur Generierung von Dokumentation. Dokumentationskommentare sind eine Sonderform der Kommentare und werden im Abschnitt zur Dokumentation vorgestellt.
Zeilenkommentare, Zeilenendkommentare
Zeilenkommentare beginnen nach dem doppelten Schrägstrich //. Der Javaübersetzer wird alle Zeichen hinter diesem Kommentarzeichen bis zum Ende der Zeile ignorieren.
Beispiel:
int a = 17; // Dies ist ein Zeilenendkommentar // Dieser Kommentar umfasst eine ganze Zeile int b = 18;
Mehrzeilige Kommentare
Java erlaubt es eine ganze Reihe von Zeilen als Kommentar zu kennzeichnen. Mehrzeilige Kommentare werden mit den Zeichen "/*" (Schrägstrich Stern) eingeleitet und mit der Zeichenkombination "*/" (Stern Schrägstrich) beendet. Hiermit kann man ganze Bereiche als Kommentar markieren.
Beispiel:
/* Hier beginnt ein Kommentar diese Zeile gehört zum Kommentar int i=1; diese Zeile wird nicht als Befehl sondern als Kommentar verarbeitet der Kommentar endet in der nächsten Zeile */
- 21799 views
2.2 Datentypen und Wertebereiche
2.2 Datentypen und WertebereicheDie Typen die ein Entwickler in Java vewendet sind entweder primitive Datentypen oder komplexe Datentypen. Die beiden Kategorien unterscheiden sich in den folgenden Eigenschaften:
Primitiver Datentyp | Komplexer Datentyp (Javaklassen) | |
---|---|---|
Operatoren | viele hochoptimierte Operationen | Nur Operatoren für Objekte (Vergleich, Referenzierung). Ansonsten Methoden der Klasse |
Lebensdauer | hängt vom umgebenden Block, bzw. Objekt ab | liegt im Freispeicher. Lebensdauer hängt von der letzten Referenz auf Objekt ab |
Speicherverbrauch | konstant | variabel |
Speicherort | im Prozedurblock oder im Objekt einer Klasse | immer im Freispeicher (Heap) |
Syntax | immer klein geschrieben | Systemklassen beginnen immer mit Großbuchstaben (Es ist guter Stil auch Benutzerklassen mit einem Großbuchstaben beginnen zu lassen). |
Im nachfolgenden Diagramm wird die Klassifikation der wichtigsten Typen gezeigt:
Die nächsten beiden Abschnitte behandeln die hier gezeigten Typen. Der dritte Abschnitt beschäftigt sich mit den Risiken von Zuweisungen zwischen Datentypen mit unterschiedlichen Wertebereichen.
- 13972 views
2.2.1 Primitive Javatypen
2.2.1 Primitive JavatypenGanzzahlige Datentypen
Datentyp | Bits | Wertebereich | Wertebereiche | Konstante min. | Konstante max. |
---|---|---|---|---|---|
byte | 8=1 byte | -27 bis 27-1 | -128 bis +127 | ||
short | 16=2 byte | -215 bis 215-1 | -32768 bis +32767 | ||
char | 16=2 byte | 0 bis 216-1 | 0 bis +65535 (Sonderfall!) | ||
int | 32=4 byte | -231bis 231-1 | -2147483648 bis +2147483647 | ||
long | 64=8 byte | -263 bis 263-1 | −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 |
Die maximalen und minimalen Werte der Ganzzahltypen ergeben sich aus der Anzahl der Bits und der internen binären Präsentation. Dies ist am Beispiel des 8-Bit Typen Byte aufgeführt:
Java verwendet für die Darstellung negativer Ganzzahlen das Zweierkomplement. Der Vorteil des Zweierkomplement besteht in der einfacheren Implementierung von Arithmetikeinheiten in Prozessoren. Durch das Zweierkomplement kann das Vorzeichenbit bei Additionen und Subtraktion wie ein regulärers Bit des Wertebereichs behandelt werden.
Dies kann man gut am Beispiel der Zahl -1 erkennen. Addiert man zur Zahl -1 (Binärdarstellung 11111111) eine 1 (Binärdarstellung 00000001) so ergibt durch den Übertrag eine 0 (Binärdarstellung 00000000).
Die Anwendung zur Rechten ist in der Lage alle Ganzzahltypen in die Binärdarstellung umzuwandeln. Das Vorzeichenbit wird in Rot dargestellt. Bei allen Typen die mehr als 16 Bit erfordern wird die Ausgabe nach 16 Bit umgebrochen. Das Bit mit der höchsten Ordnung wird zuerst ausgegeben. Das Bit mit der niedrigsten Ordnung wird am Ende ausgegeben. HinweisDer Typ char ist ein Zahlentyp. Man muss jedoch genau ein beliebiges Zeichen (Buchstabe) im Eingabefenster eingeben. Der Typ char unterscheidet sich vom Typ short in der Benutzereingabe und im Wertebereich. |
Zum Herunterladen: BinaerIntApplet.jar Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus: java -jar BinaerIntApplet.jar Es sollte ein Fenster erscheinen, dass ähnlich dem folgenden Fenster aussieht: Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole. |
Fließkomma-Standarddatentypen
Die Zahlendarstellung der Fließkommazahlen geschieht nach der Norm IEEE 754 getrennt nach Bits für Vorzeichen (V), Mantisse und Exponent mit unterschiedlicherAnzahl von Bits abhängig vom Typ nach der Regel:
z= (-1)V*Mantisse*2Exponent
Datentyp | Bits | V (bits) |
Mantisse (bits) |
Exponent (bits) |
Zahlenbereich | Dezimalstellen in Mantisse |
---|---|---|---|---|---|---|
float | 32=4 Byte | 1 | 23 | 8 | ≈-3.4*1038 bis +3,4*1038 | 7 |
double | 64=8 Byte | 1 | 52 | 11 | ≈-1.7*10308 bis +1.7*10308 | 15 |
Die Minimal- und Maximalwerte als Konstanten können über die Attribute MIN_VALUE und MAX_VALUE der Klassen Float und Double abgerufen werden.
Die Berechnung der dezimalen Werte ist für den menschlichen Betrachter nicht so einfach wie die Umwandlung von Ganzzahlen.
Der IEEE 754 Standard ist recht anschaulich in Wikipedia beschrieben. Eine 32 Bit Fließkommazahl berechnet sich nach IEEE 754 wie folgt: = (-1)Vorzeichen*2(Exponent-127)*Mantisse |
Für den 32 Bit Typ float ergibt sich so nach dem Standard IEEE 754 das folgende Bitmuster für verschiedene Werte: |
Das Applet zur Rechten ist in der Lage 32 Bit Fließkommazahlen in die Binärdarstellung umzuwandeln. Die Knöpfe auf der rechten Seite erlauben die Eingabe von Extremwerten wie
|
Zum Herunterladen: BinaerFloatApplet.jar Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus: java -jar BinaerFloatApplet.jar Es sollte ein Fenster erscheinen, dass ähnlich dem folgenden Fenster aussieht: Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole |
Wahrheitswerte
Datentyp | Bits | Wertebereich | Werte | Konstante |
---|---|---|---|---|
boolean | 8 (in der Oracle VM) | wahr oder falsch | true,false | Boolean.FALSE, Boolean.TRUE |
Die Anzahl der allokierten Bits hängt von der Implementierung ab (Siehe Spezifikation).
Wichtig: Alle Vergleiche (z.Bsp. (a<b) ) haben als Ergebnis einen boolschen Wert!
Zeichen
Java behandelt einzelne Zeichen intern als ganze Zahlen. Man kann auf den Typ char alle Operationen anwenden die auch für Zahlen erlaubt sind. Der wesentliche Unterschied zum Typ short besteht in der Eingabe und Ausgabe, sowie im Wertebereich. Hier werden lexikalische Zeichen ein- oder ausgegeben.
Wichtiger Sonderfall: Der Typ char benutzt 16 Bit zum kodieren wie auch der Typ short. Die Wertebereiche unterscheiden sich jedoch. Der Typ char kodiert nur positive Werte. Im Englischen wird ein solcher Typ "unsigned" gennannt. Es ist ein Typ ohne Vorzeichenbit.
Datentyp | Bits | Wertebereich | Werte | Kommentar |
---|---|---|---|---|
char | 16 | UC='\u0000' bis '\uffff' | 16 Bit Unicode 4.0 Basic Multilingual Plane (BMP) | "supplementary character" Unterstützung seit JDK 1.5 |
- 19203 views
Speicherplatz für boolean
Wieso werden für den Datentyp boolean 8 Bits benötigt? Eigentlich würde doch 1 Bit ausreichen.
- Log in to post comments
Sinnvolle Überlegung
Sie haben recht.
Alle Prozessoren auf denen man Java ausführen kann haben Registergrößen zum Rechnen von 32 oder 64 Bit.
Selbst alte oder einfache Prozessoren haben Registergrößen von 8 oder 16 Bit.
Das Laden und Speichern eines beliebigen Datums aus dem Speicher erfolgt auch immer in Blöcken die ein Vielfaches von 8 Bit (1 Byte) sind.
Die Prozessoren werden nicht effektiver wenn Sie auf kleineren Datenstrukturen arbeiten müssen.
Noch schlimmer:
Wenn man 8 Boolean in ein einzelnes Byte stecken würde, könnte es sein, dass an acht Stellen mit den acht Variablen gleichzeitig gearbeitet werden müsste. 7 Ausführungseinheiten müssten dann immer auf die achte warten die gerade das entsprechende Byte in ihrem Register bearbeitet.
Platzverschwendung: Boolean werden nicht so häufig wie andere Typen verwendet. Es macht bei den heutigen Hauptspeichergrößen für die meissten Anwendungen keinen Unterschied beim Hauptspeicherverbrauch.
- Log in to post comments
2.2.2 Komplexe Javatypen
2.2.2 Komplexe Javatypen
Diese Datentypen werden mit Hilfe von Javaklassen implementiert. Die so erzeugten Datenstrukturen sind Javaobjekte und haben einen anderen Lebenszyklus als primitive Typen die in einer Javaklasse oder einem Programmierblock benutzt werden. Komplexe Datentypen (Javaklassen) haben einen variablegroßen Speicherplatzbedarf. Sie werden auf dem Javafreispeicher (Heap) angelegt. |
Zeichenketten
Zeichenketten sind in Java kein vordefinierter primitiver Typ. Zeichenketten werden im Paket java.lang mit Hilfe der Klasse String implementiert. Die Klasse String kann jedoch ohne eine spezielle Deklaration wie ein primitiver Typ verwendet werden.
Zeichenketten (Strings) sind in Java nicht modifizierbar. Bei jeder Zuweisung wird eine neue Datenstruktur angelegt. Bei primitiven Typen werden die Werte an der gleichen Stelle überschrieben.
Aufzählungstypen
Aufzählungstypen sind seit JDK 5.0 Bestandteil der Sprache.
Aufzählungstypen |
---|
Aufzählungstypen in Java haben einen Wertebereich der aus einer geordneten Menge von Konstanten besteht. Der aktuelle Wert einer Variablen besteht aus einem der Aufzählungskonstanten |
Aufzählungstypen können nur als Bestandteil einer Klasse deklariert werden. Sie können nicht wie die anderen Basistypen innerhalb eines Blocks deklariert werden. Die Syntax einer Deklaration in einer Klasse ist die Folgende:
Beispiel einer Deklaration in einer Klasse:
class AufzaehlungsDemo { enum Kartenfarbe {KARO, HERZ, PIK, KREUZ} enum Wochentag {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG} }
Bei der Benutzung von Aufzählungstypen kann nur eine zuvor deklarierte Aufzählungskonstante benutzt werden.
Beispiel der Benutzung in einer Klassenmethode der zugehörigen Klasse:
Wochentag heute = Wochentag.MITTWOCH; Kartenfarbe dame = Kartenfarbe.HERZ;
Aufzählungstypen wurden wie Referenzvariablen (die erst später vorgestellt werden) implementiert.
Referenzen
Referenztypen erlauben es auf andere Objekte zu referenzieren. Sie werden im Abschnitt 7 behandelt.
Übergroße Zahlen (Big Decimal)
Die Java BigDecimal Klassen erlauben das Rechnen mit einer beliebigen Präzision. BigDecimal Klassen werden im Rahmen dieser Einführung nicht behandelt.
- 20063 views
2.2.3 Typkonversion bei Zuweisung verschiedener Typen
2.2.3 Typkonversion bei Zuweisung verschiedener TypenDie Sprache Java und ihre (Lautzeitumgebung) sind typsicher. Typsicher bedeutet, dass bei Zuweisungen für Variablen der Typ der Variable und der zugewiesene Typ eines Wertes geprüft werden und nur sichere Zuweisungen erlaubt werden. Der Übersetzer (javac) erkennt dies schon beim Parsen (lesen) des Quellcodes und meldet dies als Fehler.
Beispiel: Der Wertebereich einer Variable vom Type short endet bei 65535. Der Typ int hat jedoch einen Wertebereich der bis 4294967295 reicht. Im folgenden Fall kann es also zu einem Überlauf kommen:
short a; int b; .... a = b;
Der Übersetzer wird einen Fehler melden, da es hier zu einem Überlauf/Unterlauf aufgrund der unterschiedlichen Wertebereiche kommen kann.
Die Zuweisungskompatibilität kann man als Teilmengen mit dem Symbol ⊃ darstellen. In Java gilt die folgenden Beziehung:
double ⊃ float ⊃ long ⊃int ⊃ short/char ⊃ byte
- Alle Zuweisungen von byte ➔ double sind erlaubt. Java führt hier eine implizite Konvertierung (impliziter Cast) durch, weil es eine sichere Operation ist.
- Alle Zuweisung von double ➔ byte werden vom Übersetzer nicht akzeptiert.
Bei Berechnungen vor der Zuweisung kommen eventuell verschieden Typen in Operationen (z.Bsp.) Addition vor.
Hier verfährt Java nach der folgenden Regel:
- Der jeweils kleinere Operandentyp wird vor der Operation in den größeren konvertiert.
- Die Operanden werden jedoch zumindest in den Typ int konvertiert
- Das Ergebnis hat den gleichen Typ wie die beiden Operanden (nach Konvertierung)
Explizite Typkonvertierungen(Casts)
Um Zuweisungen zwischen inkompatiblen Typen zu erzwingen kann man den Zieltyp in runden Klammern dem Ausdruck voran stellen. Diesen Operator nennt man Castoperator (cast im englischen: gießen, betonieren).
Beispiel:
short a; int b; .... a = (short)b;
Hiermit erzwingt man Operationen die unsicher sein können! Es kann zu Überläufen in Wertebereichen oder Präzisionsverlusten kommen kann. Der Entwickler übernimmt hier die Verantwortung und überstimmt den Übersetzer. Der Castoperator sollte daher nur wenn nötig eingesetzt werden.
Das Applet zur Rechten ist in der Lage einen beliebigen Ganzzahltypen auf einen beliebigen anderen Ganzzahltypen zu zuweisen. Die Variable y enthält den Eingabewert. Den Typ von y kann man in der rechten Spalte wählen. Der Wert von y wird mit Hilfe einer Typkonversion (Cast) auf x zugewiesen. Den Typ von x kann man links wählen. Nach der Wahl der Typen und des Eingabewerts kann die Zuweisung mit dem "Enter" Button gestartet werden. Fragen:
|
Zum Testen bitte runterladen: BinaerCastApplet.jar Starten Sie die Anwendung mit einem Doppelclick im Download Ordner oder öffen ein Terminal und führen den folgenden Befehl im Download Ordner aus: java -jar BinaerCastApplet.jar Es sollte ein Fenster erscheinen welches wie folgt aussieht:
Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole |
- 13499 views
Genauigkeitsverlust bei Konversion von long-->float
Laut "double ⊃ float ⊃ long ⊃int ⊃ short/char ⊃ byte" ist die Zuweisungen von Long-Literalen auf eine Float-Variable vom Compiler akzeptiert. Aber kommt es hier nicht zu Genauigkeitsverlusten, da long 64 Bit Genauigkeit hat und float nur 32 Bit. Und wenn ja, wieso akzeptiert der Compiler das dann trotzdem?
- Log in to post comments
Korrekte Beobachtung
Korrekte Beobachtung. Ein long Typ kann Werte noch ohne Rundungsfehler korrekt darstellen wo ein float Typ schon im Bereich >1 also 20, 21, 22 etc.
Das ganze ist (wahrscheinlich) erlaubt da
- das Zahlenintervall des Typs float größer als das des Typ long ist. Es gibt also keinen Überlauf an den Grenzen der Zahlenintervalle
- Wer mit dem Typ float arbeitet muss wissen, das hier immer gerundet werden kann.
- Log in to post comments
Applet-Funktionsfehler?
Wenn ich in dem Applett auf der linken Seite den Typ byte wähle und auf der rechten Seite den Typ int. Dann müsste doch oben eigentlich in blauer Schrift die Zuweisung "byte x=(byte) y" stehen, oder? Jedenfalls steht dort "byte x=(int) y"; das würde doch gar nicht funktionieren, denn dann würde ich ja immer noch ein Int-Literal einer Byte-Variablen zuweisen.
- Log in to post comments
2.3 Operatoren und Ausdrücke
2.3 Operatoren und AusdrückeAusdrücke
Ausdrücke in Java sind alles was einen Rückgabewert liefert:
- Konstanten
- Variablen
- Methoden
- Operatoren
Operatoren
Java verfügt über
- unäre (einstellige, monadische) Operatoren
- binäre (zweistellige, dyadische) Operatoren
- einen dreistelligen (ternären, tryadischen) Operator (den Bedingungsoperator "_ ? _ : _")
Unäre Operatoren haben einen einzigen Operanden. Beispiele sind:
|
Binäre Operatoren haben zwei Operanden. Beispiele sind:
|
Arithmetische Operatoren
Die arithmetischen Operatoren können auf die folgenden Typen angewendet werden
- byte
- short
- int
- long
- float
- double
Operator | Beispiel | Semantik |
---|---|---|
+ | a + b | Addition: Summe von a und b |
- | a - b | Subtraktion: Differenz an a und b |
* | a * b | Multiplikation: Produkt von a und b |
/ | a / b | Division: Quotient von a und b |
% | a % b | Modulo: Rest einer ganzzahligen Division von a durch b |
Die Division von Ganzzahlen ergibt immer ganzzahlige Ergebnisse!
Java arbeitet ohne eine Erkennung des Überlaufs der Wertebereiche. Der Entwickler muss selbst die entsprechenden Vorsichtsmaßnahmen ergreifen.
Weiterhin gibt es einstellige (unäre) arithmetische Operatoren
Operator | Beispiel | Semantik |
---|---|---|
+ | +a | Der Wert von a bleibt erhalten (Idempotente Operation) |
- | -a | Der Wert von a wird negiert |
++ |
a++ ++a |
Postinkrement: Der Ausdruck behält ursprünglichen Wert . Der Wert von a wurde um 1 erhöht Präinkrement: Der Wert von a wird um 1 erhöht und der Ausdruck erhält den erhöhten Wert von a |
-- |
x-- --x |
Postdekrement: Der Ausdruck behält ursprünglichen Wert . Der Wert von a wurde um 1 erniedrigt Prädekrement: Der Wert von a wird um 1 erniedrigt und der Ausdruck erhält den verminderten Wert von a |
Die folgen drei Anweisungen bewirken das gleiche:
a = a + 1; a++; ++a;
Bei Inkrementen mit gleichzeitiger Zuweisung ergeben jedoch unterschiedliche Werte für den zugewiesenen Wert
Variante 1 | Wert a | Wert b | Variante 2 | Wert a | Wert b |
---|---|---|---|---|---|
a = 10; b = 4; b = a++; |
10 11 |
4 10 (!) |
a = 10; b = 4; b = ++a; |
10 11 |
4 11 (!) |
Beispiel
Quellcode | Konsolenausgabe |
---|---|
package s1.block2.skript; public class PrePostFixTest { public static void main(String[] args) { int x = 10; int y = 100; System.out.println("x = " + x + "; y = " + y); x++; System.out.println("x++ results in " + x); ++x; System.out.println("++x results in " + x); System.out.println("Set x to 0 "); x=0; System.out.println("x = " + x + "; y = " + y); y=x++; System.out.println("y=x++ (Postfix)"); System.out.println("x = " + x + "; y = " + y); y=++x; System.out.println("y=++x (Prefix)"); System.out.println("x = " + x + "; y = " + y); } |
. . . . . x = 10; y = 100 . x++ results in 11 . ++x results in 12 Set x to 0 . x = 0; y = 100 . y=x++ (Postfix) x = 1; y = 0 . y=++x (Prefix) x = 2; y = 2 . |
Arithmetik der Ganzzahlen
- Alle Operationen auf Ganzzahlen ergeben wieder Ganzzahlen. Dies wird nicht unbedingt von der Division erwartet!
- Versuche durch 0 (Null) zu dividieren lösen eine ArithmeticException Ausnahme aus.
Arithmetik der Fließkommazahlen
Bei der Arithmetik mit Fließkommazahlen werden im Gegensatz zu den Ganzzahlen Überläufe erkannt. Die Fließkommazahlen besitzen eine Reihe Konstanten:
Konstante | Semantik |
---|---|
POSITIVE_INFINITY | Positiv unendlich |
NEGATIVE_INFINITY | Negativ unendlich |
MAX_VALUE | Größter darstellbarer Wert |
MIN_VALUE | Kleinster darstellbarer Wert |
NaN | "Not a number" Dieser Wert ist ungleich zu allen anderen Werten im Wertebereich |
Vergleichsoperatoren
Gleichheit bzw. Ungleichheit bezieht sich auf den Wert der Variablen x und y
Operator | Beispiel | Semantik (Bedeutung) |
---|---|---|
== | x == y | ist x gleich y ? |
!= | x != y | ist x ungleich y ? |
< | x < y | ist x kleiner als y ? |
<= | x <= y | ist x kleiner oder gleich y ? |
> | x > y | ist x größer als y ? |
>= | x >= y | ist x größer oder gleich y ? |
Logische Operatoren
Die logischen Operatoren wirken auf den Typ Boolean der nur den Wert wahr oder falsch kennt.
Operator | Beispiel | Semantik (Bedeutung) |
---|---|---|
! | !a | Negation |
& | a & b | Und |
| | a | b | Oder (inklusiv) |
^ | a ^ b | Entweder-Oder |
&& | a && b | bedingt auswertendes Und |
|| | a || b | bedingt auswertendes Oder |
Bedingungsoperator
Der dreistellige (ternäre) Bedingungsoperator (Konditionaloperator) erlaubt eine Zuweisung von dem Ergebnis einer Bedingung abhängig zu machen. Er hat die Form:
<ausdruck1> ? <ausdruck2> : <ausdruck3>
ausdruck1 muss einen boolschen Wert ergeben. Wird ausdruck1 wahr, so wird ausdruck2 der entsprechenden Variable zugewiesen. Wird ausdruck1 unwahr, so wird der ausdruck3 zugewiesen
Hiermit kann man Zuweisungen wie die Folgende formulieren
int maximum; int x = 1; int y =2 ; maximum = (x > y) ? x : y ;
Das Ergebnis ist 2, da y (=2) größer als x (=1) ist.
Bedingt auswertende logische && und || Operatoren
Die bedingt auswertenden Operatoren werten Terme nur soweit aus bis das Endergebnis fest steht. Dies macht sie sehr effizient.
Im Beispiel:
boolean a = ((1<3) || (4>5));
wird der Term (4>5) nicht mehr ausgewertet. Da (1<3) wahr ist, steht das Endergebnis schon fest.
Die bedingt auswertenden logischen Operatoren wendet man neben Ihrem Geschwindigkeitsvorteil auch gerne an um potentielle Fehler und Ausnahmen zu vermeiden.
Ein Beispiel hierfür ist:
if ((a>0) && (Math.sqrt(a)>2))
Die Wurzel wird nur ausgewertet wenn a größer als Null ist.
Vorsicht: Durch die bedingte Auswertung können unterschiedliche Ergebnisse enstehen wenn in einem Ausdruck gleichzeitig ein Wert verändert wird!
Beispiel:
bedingter "Oder" Operator | einfacher "Oder" Operator | |
---|---|---|
Quellcode |
public static void t1() { int a = 3; int b = 5; if ((a>1) || (a<b++)){ System.out.println ("Hallo"); } System.out.println("b= " + b); } |
public static void t2() { int a = 3; int b = 5; if ((a>1) | (a<b++)){ System.out.println ("Hallo"); } System.out.println("b= " + b); } |
Ausgabe |
Hallo b= 5 |
Hallo b= 6 |
Das Postinkrement (b++) in der linkten bedingten oder Bedingung wird nicht ausgeführt, da der Ausdruck (a>1) schon wahr geworden ist.
Bitoperatoren
Mit Bitoperatoren werden alle Bits einer Variablen einzeln manipuliert.
Operator | Beispiel | Bedeutung |
---|---|---|
~ | ~a | Komplement |
& | a & b | Und |
| | a | b | Oder |
^ | a ^b | exklusives Oder |
Beispiel
package s1.block2.skript; public class BitOperator { public static void main(String[] args) { int a = 7; int b = 6; int result; result = a & b; System.out.println("a = " + a + "; b = " + b + " result = " + result); result = a | b; System.out.println("a = " + a + "; b = " + b + " result = " + result); result = a ^ b; System.out.println("a = " + a + "; b = " + b + " result = " + result); } }
Ergebnis
a = 7; b = 6 result = 6 a = 7; b = 6 result = 7 a = 7; b = 6 result = 1
Erklärung
Variable | Dezimal | Binär |
---|---|---|
a | 7 | 0 0000000 00000000 00000000 00000111 |
b | 6 | 0 0000000 00000000 00000000 00000110 |
result = a & b | 6 | 0 0000000 00000000 00000000 00000110 |
Variable | Dezimal | Binär |
---|---|---|
a | 7 | 0 0000000 00000000 00000000 00000111 |
b | 6 | 0 0000000 00000000 00000000 00000110 |
result = a | b | 7 | 0 0000000 00000000 00000000 00000111 |
Bitschiebeoperatoren
Operator |
Beispiel | Bedeutung |
---|---|---|
<< | a << b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach links verschoben wurden. Es wird mit 0 Bits aufgefüllt. |
>> | a >> b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach rechts verschoben wurden. Es wird mit dem höchsten Bit aufgefüllt. |
>>> | a >>> b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach rechts verschoben wurden. Es wird mit dem "0" Bits aufgefüllt. |
Das kleine Programm rechts erlaubt die drei Bitschiebeoperationen zu testen. Es kann die Bits um jeweils eine Stelle verschieben. |
Hier die jar Datei herunterladen. Das Programm dann mit dem Kommandozeilenbefehl: java -jar IntShiftApplet.jar Es erscheint ein Fenster wie folgt:
Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole. |
Beispiel
package s1.block2.skript; public class ShiftingBits { public static void main(String[] args) { int x = 4; int result; int shift = 1; result = x << shift; System.out.println("x = " + x + "; shift = " + shift + " result = " + result); result = x >> shift; System.out.println("x = " + x + "; shift = " + shift + " result = " + result); result = result >> shift; System.out.println("x = " + x + "; shift = " + shift + " result = " + result); result = result >> shift; System.out.println("x = " + x + "; shift = " + shift + " result = " + result); result = result >> shift; System.out.println("x = " + x + "; shift = " + shift + " result = " + result); } }
Ergebnis
Die interne Darstellung der verwendeten Werte:
Dezimalwert | Binärwert |
---|---|
8 | 0 0000000 00000000 00000000 00001000 |
4 | 0 0000000 00000000 00000000 00000100 |
2 | 0 0000000 00000000 00000000 00000010 |
1 | 0 0000000 00000000 00000000 00000001 |
0 | 0 0000000 00000000 00000000 00000000 |
Zuweisungs- und Verbundoperatoren
Das Gleichzeichen = dient in Java als Zuweisungsoperator. Die Anweisung
x = y + z;
ist nicht als mathematische Gleichung zuverstehen, sondern als Zuweisung des Ausdrucks auf der echten Seite (y+z) auf die Variable x auf der linken Seite.
Zuweisungen wie:
x = y = 8;
sind auch möglich. Sie haben die gleiche Bedeutung wie
y = 8; x = y;
Für die meisten binären Operatoren gibt es Verbundoperatoren mit denen man einer Variable etwas zuweisen kann und gleichzeitig den alten Wert verwenden kann:
Verbundoperator | entspricht |
---|---|
a += b | a = a + b |
a -= b | a = a - b |
a *= b | a = a * b |
a /= b | a = a / b |
a %= b | a = a % b |
a &= b | a = a & b |
a |= b | a = a | b |
a ^= b | a = a ^ b |
a <<= b | a = a << b |
a >>= b | a = a > b |
a >>>= b | a = a >>> b |
Auswertungsreihenfolge
Für Ausdrücke mit mehreren Operatoren gelten die folgenden Regeln in Bezug auf die Reihenfolge der Auswertung:
- Teilausdrücke in runden Klammern werden wie in der Mathematik als erstes ausgewertet
- Ausdrücke mit unären Operatoren werden anschließend ausgewertet
- Zuletzt werden Teilausdrücke mit mehrstelligen Operatoren ausgewertet
Unäre Operatoren haben alle die gleiche Priorität
Ausführungsreihenfolge von Operatoren
Die Ausführungsreihenfolge von Operatoren bestimmt wie ein Term aufgelöst wir.
Tipp: Es ist guter Programmstil Terme übersichtlich zu gestalten. Verwenden Sie im Zweifelsfall Klammern!
Rang | Operator | Beschreibung |
---|---|---|
1 | =, +=, -=, *= ... | Zuweisungsoperator |
2 | ?: | Bedingungsoperator |
3 | || | Logische Oder |
4 | && | Logisches Und |
5 | | | logisches oder bitweises Oder |
6 | ^ | logisches oder bitweises Entweder-Oder |
7 | & | logisches oder bitweises Und |
8 | ==, != | Vergleichsoperatoren: Gleich, Ungleich |
9 | <, <=, >, >= | Vergleichsoperatoren |
10 | <<, >>, >>> | Schiebeoperatoren |
11 | +, - | Addition, Subtraktion, Verketten von Zeichenketten |
12 | *, /, % | Multiplikation, Division, Rest |
13 | ++, --, +, -, ~, ! | unäre (einstellige) Operatoren |
Auswertung von Operatoren mit gleicher Priorität
Es kann vorkommen, dass ein Ausdruck mehrere Operatoren der gleichen Priorität besitzt. In diesen Fällen wird die Auswertereihenreihenfolge durch die Assoziativität der Operatoren bestimmt.
Operatorenasoziativität |
---|
Die Assoziativität von Operatoren ist die Reihenfolge in der Operanden durch Operatoren gleicher Priorität verknüpft werden |
Ist ein Operator linksassoziativ, wird zuerst der linke Operand ausgewertet. Das Beispiel zeigt den Plus- und Minusoperator. Beide haben die gleiche Priorität. Hier wird zuerst der Operand a+b ausgewertet.
Einige Operatoren in Java sind rechtsassoziativ. Ein Beispiel hierfür ist der Zuweisungsoperator
Bewertungsreihenfolge der Operanden eines Operators
Bewertungsreihenfolge der Operanden |
---|
In Java werden die Operanden eines Operators strikt von links nach rechts ausgewertet. |
Diese Regel ist insbesondere wichtig, da Methoden und diverse Operatoren Nebeneffekte haben können. Das bedeutet, dass diese Operatoren den Wert von Variablen während der Auswertung des Gesamtausdrucks verändern. Beispiele sind die Inkrement- und Dekrementoperatoren.
j = i-- -i;
ist ein zulässiger Ausdruck in Java. Der Wert der j zugewiesen wird ist immer 1;
Die Auswertung dieser Zuweisung geschieht in den folgenden Schritten:
- Auswertung des Subtrahenden (und Zwischenspeicherung)
- Dekrement von i
- Auswertung des Minuend und Berechnung der Differenz
- Zuweisung der Differenz auf j
Ein Beispielprogramm zum Testen:
package s1.block2.skript; public class PrePostInkrement { public static void main(String[] args) { int i = 4; int j; j=i-- -i; System.out.println("i: " +i+", j= "+j); } }
Ausgabe:
i: 3, j= 1
Die Auswertung des Ausdrucks und der Zuweisung j= i-- -i; findet wie folgt statt:
i | j | j= i-- -i; | Kommentar |
---|---|---|---|
4 | 0 | j = 4 - i; | Bestimmung des Minuend der Subtraktion |
3 | 0 | j = 4 - i; | Postdekrement von i |
3 | 0 | j = 4 -3; | Bestimmung des Subtrahend der Subtraktion |
3 | 0 | j = 1; | Bestimmung der Differenz |
3 | 1 | Zuweisung |
Regeln für den Ergebnistyp von arithmetischen Ausdrücken (Widening Conversions)
Java kann alle arithmetischen Operationen auch ausführen wenn die Zahlentypen im Ausdruck unterschiedlich sind. Das Ergebnis der Berechnung hängt von den Typen des Ausdrucks ab. Es gilt in der folgenden Reihenfolge:
- Ist einer der Typen ein double, so wird das Ergebnis zum Typ double konvertiert.
- Falls nicht, wird das Ergebnis zu einem float Typen konvertiert wenn ein Typ ein float Typ ist.
- Falls nicht, wird das Ergebnis zu einem long Typen konvertiert wenn ein Typ ein long Typ ist.
- Falls nicht, werden beide Operanden zuerst zu einem int Typen konvertiert.
- 101864 views
Mehrfachzuweisung
Sie schreiben, dass "x=y=8;" die gleiche Bedeutung hat wie "x=8; y=8;". Dabei wird doch x der Wert von y zugewiesen und nicht direkt die Zahl 8, oder? Also müsste "x=y=8;" äquivalent sein mit "y=8; x=y" (da ja die Ausdrücke von rechts nach links ausgewertet werden). Oder?
- Log in to post comments
Richtige Überlegung
Sie haben sehr, sehr wahrscheinlich recht.
Man müsste sich zur finalen Klärung den generierten Bytecode anschauen oder im Debugger zwischen die beiden Zuweisungen kommen.
Der folgende Javacode ist ein starkes Indiz, dass Sie recht haben:
int x,y;
x = y = 8;
//(x = y) = 8; // So etwas geht nicht
x = (y = 8); // So etwas geht. Diese Klammer ist an dieser Stelle wahrscheinlich redundant. y wird hier garantiert vorher belegt.
x = (y = 8)+1; // So etwas geht auch. Damit wird das Prinzip auf die Spitze getrieben. x erhält jetzt den Wert 9.
System.out.println(x);
System.out.println(y);
Hane den Text nach Ihrem Vorschlag geändert.
- Log in to post comments
Hallo Kurs WIBI12C
DHBW-Mannheim - Java Vorlesung - hier sind wir gerade oder ??
Wer guckt alles und findet den comment???
___________c$$$$$$$$$$$$$$$$$$$$$$$$$$h,
________c$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
____d$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
____$$$,d$$$$$$$$$$$$$$$$$$$$$$$$$hc`?$$$$
___$$$u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$h,?$$
___$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$b$$$
___$$$$$P$$?$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
___$$$$$$$`$$$$$$$$$$$$$$$$$$$$$$$$$d$$$$$$
____$$$$$$c ,nn,?$$$$$$$$$$$$$$$,,`$$$$$$$$
_____$$ MMMMM`$$$$$$$$$$$$F,MMMb`$$$$$$$$
______$$$MMTTM.?$$$$$$$$$$$,MMM $$$$$$$$
_______`$$$$$,M;;;`$$$$$$$$$$´M,,`,$$$$$$
__________?$$$$,
____________?$$$$,( ) $$$$$$$$$$ (´ ),$$$
____________`$$$$.`-´ $$$$$$$$$$,`--´,$$$´
____________$$$$$hh$$$$$????$$$hc$$$$$$$´
___________d$$$$$$$$$ `======´ $$$$$$$;
___________ $$$$$$$$$$$$c,,,,c$$$$$$$$$
____________?$$$$P $$$$$$?????$$??$$$$
______________________$$$$$
_____________________4$$$$$c
____________________,$$$$$$$
__________________z$
________________> z$
_________________
________________`$
_________________?L$$$$$$$$$$$:$
__________________?$$$$$$$$$$$d´
___________________`$$$$$$$$$$F
____________________`?$c`??3$F
____________________CCC_CCC
____________________,;CC_CC;;
____________________`CC__CCC
.,,,,,CCCCCCCCCCCCCCC__CCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCC__CCCCCCC,CCCCCCCCCCC
CCCCCCCCCCCCCCCCCC___CCCCC´`´CCCCCCCCCC
- Log in to post comments
Rechnung
public class PrePostInkrement {
public static void main(String[] args) {
int i = 4;
int j;
j=i-- -i;
System.out.println("i: " +i+", j= "+j);
}
}
Müsste hier für j nicht 0 rauskommen? Und i = 3?
Der Operator -- (also i--;) findet doch erst nach der Zuweisung statt oder?
- Log in to post comments
Erklärung
Ich habe den genauen Ablauf in das Skript eingebaut.
Das Dekrement findet statt nachdem der Minuend bestimmt worden ist.
Das bedeutet, das Dekrement findet nach der Zuweisung des Minuenden statt. Nicht nach der Zuweisung auf j!
Die Zuweisung des Minuenden kann man als Zuweisung auf eine temporäre Variable verstehen.
- Log in to post comments
Ausführungsreihenfolge von Operatoren
Laut diesem Unterpunkt ist Strich vor Punkt. Bitte erklären.
- Log in to post comments
Tippfehler
Bei den unären arithmetischen Operatoren steht "Präidekrement" statt "Prädekrement".
- Log in to post comments
Ternärer Operator rechtsassoziativ?
Hallo,
der ternäre Operator ist ja rechtsassoziativ.
Weshalb ergibt der folgende Ausdruck dann " a größer b"?
int a = 10;
int b = 5;
String r = a > b ? "a größer b" : a < b ? "a kleiner b" : "beide gleich";
System.out.println( r );
Es müsste doch eigentlich erst a < b ausgewertet werden, oder?
- Log in to post comments
2.4 Übungen
2.4 Übungen
2.4.1 Übung: Zuweisungen und TypenErstellen Sie folgende Java-Applikation (Name "Calc"):
|
- Wie groß sind die Wertebereiche der einzelnen Typen?
- Was geschieht wenn man zwei Ganzzahlen (int) dividiert und das Ergebnis ist keine ganze Zahl?
- Was geschieht wenn Sie einen Überlauf in einem Wertebereich einer Variablen provozieren?
2.4.2 Übung: Erlaubte Variablennamen
maxvalue maxValue max_value max value end End 10%ofSum sum10 _10PercentOfSum
2.4.3 Übung: Literale
Warum übersetzt das folgende Programm nicht?
package block2; public class Literale { public static void main(String[] args) { long i1 = 4000000000; long i2 = 4000000000L; System.out.println(i1); System.out.println(i2); } }
2.4.4 Übung: Ausdrücke
Gegeben seien folgende Variablendeklarationen in Java:
long a = 3; int b = 4; short c = 5; byte d = 6;
Welchen Wert liefern die folgenden Ausdrücke und von welchem Typ sind sie?
d / b * a c + b * (d + 1) d / (c - 1) * b / 2 d % b -c % b
2.4.5 Übung: Zuweisungen
long a = 3; int b = 4; short c = 5; byte d = 6;
a = b + 3 * (d + 1); b = c * c; c = b / 3; d = (byte)a + b; d = (byte) ( a + b);
2.4.6 Übung: Ausdrücke
Schreiben Sie einen Java-Ausdruck, der eine ganze Zahl x auf das nächstliegende Vielfache von 100 rundet.
Beispiel:
- der Wert 149 soll auf 100 abgerundet werden
- der Wert 150 auf 200 aufgerundet werden.
2.4.7 Übung: Zeitrechnung
2.4.8 Übung: Polynomberechnung
y = a * x3 + b * x2 + c * x + d
2.4.9 Übung: Abstand zwischen Punkten
Schreiben Sie ein Java-Programm, das die x- und y-Koordinaten zweier Punkte einliest und den Abstand zwischen ihnen berechnet und ausgibt.
Tipp: Math.sqrt() berechnet eine Wurzel
2.4.10 Übung: Berechnung von ∏ (Pi)
Die Kreiszahl ∏ kann durch folgende Näherungsformel berechnet werden:
i ∏ n (-1) 1 1 1 1 --- = ∑ ------- = 1 - --- + --- - --- + --- - ... 4 i=0 2*i+1 3 5 7 9
2.4.11 Übung: Analoge Uhr
Implementieren sie notwendigen Berechnungen zum Anzeigen von Sekunden-, Minuten, Stundenzeigern einer analogen Uhr.
Vorbereitungen
- Erzeugen Sie in einem Verzeichnis die beiden Quelldateien Uhr.java und Zeiger.java. Der Quellcode der beiden Dateien ist weiter unten aufgeführt.
- Übersetzen Sie die beiden Dateien in diesem Verzeichnis mit dem Befehl javac Uhr.java Zeiger.java
- Führen Sie das Programm aus: java Uhr
- Beobachten Sie den blauen Sekundenzeiger. Er wächst in jeder Sekunde um ein Pixel nach rechts unten
Aufgabe
Implementieren Sie die korrekte Position der Spitze der drei Zeiger abhängig von der aktuellen Zeit in der Klasse Zeiger.java
Die korrekte Spitze wird durch eine x und y Koordinate beschrieben. Der Ursprung des Koordinatensystems liegt in der Mitte der Uhr. Postive X-Koordinaten reichen nach rechts. Positive Y-Koordinaten reichen nach unten. Die Richtung der Y-Koordinaten ist typisch für für Java GUIs in Swing oder AWT.
Hinweis: Implementieren Sie zuerst den Sekundenzeiger. Er erlaubt ihnen binnen 60 Sekunden die visuelle Kontrolle Ihrer Programmierung. Die Programmierung der Minutenzeiger und Stundenzeiger sind dann sehr einfach, da sie auf der gleichen mathematischen Formel basieren.
Empfohlendes Vorgehen:
- Erstellen Sie die mathematische Formel für die aktuelle Spitze des Sekundezeigers abhängig von der Sekunde zwischen 0 und 59. Sie benötigen hierzu die Sinus- und Cosinusfunktion sowie die Konstante PI
- Finden sie in der Klasse Zeiger.java die Methoden sekundeX und sekundeY
- Ersetzen Sie in der Trivialimplementierung die Zuweisung xs=s; bzw ys=s; durch die korrekte Implementierung der beiden mathematischen Ausdrücke
- Sichern Sie die Datei Zeiger.java
- Übersetzen Sie die Datei Zeiger.java
- Testen Sie das Ergebnis mit dem Kommando: java Uhr
- Wiederholen Sie Schritt 3-6 bis Sie das gewünschte Ergebnis haben.
- Implementieren Sie den Minuten und Stundenzeiger sinngemäß.
Beispiele der Ausgangssituation und der korrekten Implementierung:
Ausgangssituation | Ziel der Übung |
---|---|
Tipps
- maxRadius: Diese Konstante der Klasse Zeiger gibt die maximale (vernünftige) Zeigergröße vor.
- Nutzen Sie diesen Wert für einen großen regulären Sekundenzeiger
- Skalieren Sie die Minuten- und Stundenzeiger relativ zu dieser Größe (z. Bsp. maxRadius*4/5 )
- die Mathematikbibliothek wurde statisch importiert. Sie können direkt die benötigen trigonometrischen Operation und Konstanten benutzen:
- Beachten Sie die Eingabe- und Ausgabeparameter der trigonometrischen Funktionen! Die Berechnungen erfolgen mit Fließkommazahlen. Das Ergebnis muss aber eine ganzzahlige Pixelposition sein.
- Ganzzahloperation runden immer ab. Erwägen Sie die Benutzung einer Fließkommahilfsvariable
- Benutzen nur double als Fließkommatyp. Die Operationen der Mathematikbibliothek beruhen auf dem Typ double. Sie vermeiden so unnötige Konvertierungen zu float Typen.
Klasse Uhr
/* * Zeichnen einer analogen Uhr in einem JFrame */ import java.awt.*; import java.awt.event.*; import java.util.Calendar; import javax.swing.*; /** * * @author sschneid */ public class Uhr extends JPanel { static int sekunde = 0; static int minute = 0; static int stunde = 0; static String tzString; // aktuelle Zeitzone static int initialHeight; static float zoom = 1; /** * Hauptprogramm der Anwendung. Es werden keine Eingabeparameter benötigt * @param args dieser Parameter wird nicht ausgewertet */ public static void main(String[] args) { JFrame hf; // Das Fenster der Anwendung hf = new JFrame("Uhr"); // Beenden der Anwendung bei Schliesen des Fenster hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Uhr dieUhr = new Uhr(); // Aufbau des Contentpanes Container myPane = hf.getContentPane(); myPane.add(dieUhr, BorderLayout.CENTER); // Erzeuge einen Menüeintrag zum Beenden des Programms JMenuBar jmb = new JMenuBar(); JMenu jm = new JMenu("Datei"); JMenuItem exitItem = new JMenuItem("Beenden"); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); jm.add(exitItem); jmb.add(jm); hf.setJMenuBar(jmb); hf.pack(); // Das JFrame sichtbar machen // Gewünschte Größe setzen // 1. Parameter: horizontale Größe in Pixel // 2. Parameter: vertikale Größe in Pixel hf.setSize((int)(2 * Zeiger.maxRadius*zoom + 80), (int)(2 * Zeiger.maxRadius*zoom + 80)); hf.setVisible(true); hf.setAlwaysOnTop(true); // Initialgröße merken. Entspricht Zoomfaktor 1 dieUhr.initialHeight = dieUhr.getHeight(); // Update von Panel in zyklischen Intervallen try { while (true) { Thread.sleep(500); // Schlafe x Millisekunden // Hole Systemzeit und belege statische Variablen Calendar call = Calendar.getInstance(); tzString = call.getTimeZone().getDisplayName(); sekunde = call.get(Calendar.SECOND); minute = call.get(Calendar.MINUTE); stunde = call.get(Calendar.HOUR); dieUhr.repaint(); } } catch (InterruptedException e) { System.out.println( "Die Anwendung wird wegen einer Ausnahme beendet"); } } /** * Überladene Paintmethode. Sie führt alle Zeichenoperationen im Panel aus * @param g vom Laufzeitsystem übergebenes Graphikobjekt. */ @Override public void paint(Graphics g) { super.paint(g); zoom = (float)getHeight()/(float)initialHeight; int maxRadius = Zeiger.maxRadius; int xCenter = (int)(maxRadius*zoom) + 40; int yCenter = (int)(maxRadius*zoom) + 20; float fontSize = g.getFont().getSize2D(); int charCenterOffSet = (int)(fontSize/2); String timeString = stunde + ":" + minute + ":" + sekunde + " " + tzString; // Zeichne Uhrenhintergrung und Koordinatensystem g.setFont(g.getFont().deriveFont(fontSize)); g.setColor(Color.WHITE); // Farbe g.fillArc((int)(xCenter - maxRadius*zoom), (int)(yCenter - maxRadius*zoom), (int)(maxRadius*zoom*2), (int)(maxRadius*zoom*2), 0, 360); g.setColor(Color.BLACK); // Farbe g.fillArc((int)(xCenter - 3*zoom), (int)(yCenter - 3*zoom), (int)(10*zoom), (int)(10*zoom), 0, 360); g.drawLine(xCenter, yCenter, (int)(xCenter + 40*zoom), yCenter); g.drawLine(xCenter, yCenter, xCenter, (int)(yCenter + 40*zoom)); g.drawString("X", (int)(xCenter + 45*zoom), yCenter + +charCenterOffSet); g.drawString("Y", xCenter - charCenterOffSet, (int)(yCenter + 55*zoom)); g.drawString("12",xCenter - charCenterOffSet, (int)(yCenter - maxRadius*zoom)); g.drawString("3", (int)(xCenter + maxRadius*zoom), yCenter + charCenterOffSet); g.drawString("6", xCenter - charCenterOffSet, (int)(yCenter + 2*charCenterOffSet+maxRadius*zoom)); g.drawString("9", (int)(xCenter - maxRadius*zoom - charCenterOffSet), yCenter + charCenterOffSet); // Zeichne aktuelle Zeit zum Debuggen g.drawString(timeString, 0, (int)(yCenter + maxRadius*zoom)); // Zeichne Stundenzeiger g.setColor(Color.BLACK); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.stundeX(stunde)*zoom), (int)(yCenter + Zeiger.stundeY(stunde)*zoom)); g.drawString("h[" + Zeiger.stundeX(stunde) + "," + Zeiger.stundeY(stunde) + "]", 0, (int)(yCenter + maxRadius*zoom - (3*fontSize))); // Zeichne Minutenzeiger g.setColor(Color.RED); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.minuteX(minute)*zoom), (int)(yCenter + Zeiger.minuteY(minute)*zoom)); g.drawString("m[" + Zeiger.minuteX(minute) + "," + Zeiger.minuteY(minute) + "]", 0, (int)(yCenter + maxRadius*zoom - (2*fontSize))); // Zeichne Sekundenzeiger g.setColor(Color.BLUE); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.sekundeX(sekunde)*zoom), (int)(yCenter + Zeiger.sekundeY(sekunde)*zoom-fontSize)); g.drawString("s[" + Zeiger.sekundeX(sekunde) + "," + Zeiger.sekundeY(sekunde) + "]", 0, (int)(yCenter + maxRadius*zoom - fontSize)); } }
Klasse Zeiger
Implementieren sie
import static java.lang.Math.*; /** * * @author sschneid */ public class Zeiger { public static final int maxRadius = 100; // Beeinflusst GUI-Größe ! /** * @param s Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle X Koordinate der Zeigerspitze des Sekundenzeigers * auf dem Bildschirm */ public static int sekundeX(int s) { int xs; /* Implementierung Beginn */ xs = s; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return xs; } /** * @param s Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle Y Koordinate der Zeigerspitze des Sekundenzeigers * auf dem Bildschirm */ public static int sekundeY(int s) { int ys; /* Implementierung Beginn */ ys = s; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return ys; } /** * @param m Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle X Koordinate der Zeigerspitze des Minutenzeigers * auf dem Bildschirm */ public static int minuteX(int m) { int xm; /* Implementierung Beginn */ xm = m; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return xm; } /** * @param m Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle Y Koordinate der Zeigerspitze des Minutenzeigers * auf dem Bildschirm */ public static int minuteY(int m) { int ym; /* Implementierung Beginn */ ym = m; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return ym; } /** * @param h Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12 * @return aktuelle X Koordinate der Zeigerspitze des Stundenzeigers * auf dem Bildschirm */ public static int stundeX(int h) { int xh; /* Implementierung Beginn */ xh = h; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return xh; } /** * @param h Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12 * @return aktuelle Y Koordinate der Zeigerspitze des Stundenzeigers * auf dem Bildschirm */ public static int stundeY(int h) { int yh; /* Implementierung Beginn */ yh = h; // Proformazuweisung. Sie ist zu ersetzen /* Implementierung Ende */ return yh; } }
Hilfestellung
Hier nicht weiterlesen, wenn Sie einfache trigonometrische Aufgaben lösen können!
max-Zeit ist bei Sekunden 60, bei Minuten 60, bei Stunden 12
Die X-Koordinate eines Zeigers berechnet sich wie folgt
x-Koordinate: Sinus(aktuelle-Zeit/max-Zeit*2*PI)*zeiger-länge-in-pixel
Die Y-Koordinate eines Zeigers berechnet sich wie folgt
Y-Koordinate: -Cosinus(aktuelle-Zeit/max-Zeit*2*PI)*zeiger-länge-in-pixel
Beachten Sie, dass in Java das Kommutativgesetz nicht vollständig gilt. Der Typ und die Auswertereihenfolge spielen auch eine Rolle!
2.4.12 Gruppenübung
Je eine Gruppe bearbeitet die folgenden Typen:
- short
- char
- int
- long
- float
- double
Jede Gruppe trägt die folgenden Aspekte eines Typs in 5 Minuten vor
- Schlüsselwort des Typ
- Anzahl der verwendeten Bits
- Wertebreich
- Grenzen mit Konstanten dazu
- Echte Obermengen oder Untermengen der anderen Typen nennen
- Beispiel für die Benutzung dieses Types. Das Beispiel sollte nicht optimal für die anderen Typen sein!
- Syntax der Literale um Werte zuzuweisen
- Programmierbeispiele
- Syntax der Literale benutzen und vorstellen
- Typische Operation und Zuweisung
- mehrere interessante Operationen und Zuweisungen die zu Sonderfällen bzw. Extremfällen führen
Zeitvorgaben
- Selbst lesen: 5 Minuten
- Teamdiskussion: 10 Minuten
- Beispiel Programmieren: 20 Minuten
- Zusammenfassung, Abstimmung Vortrag: 5 Minuten
2.4.13 Airline
Vorbereitung
- Legen Sie ein neues Paker Block2 an
- Nutzen Sie die neue Klasse Flughafen
- Kopieren Sie die Klasse Flugzeug aus dem Block1 in den Block2
Aufgabe:
- Legen Sie in der Klasse Flugzeug die folgenden Attribute als öffentliche (public) Attribute an. Wählen Sie einen passenden Typen
- passagiere
- besatzung
- treibstoff
- leergewicht
- maxGewicht
- Legen Sie zwei statische Variablen an in der Form
- static final int durchschnittsGewicht= 75;
- static final double kerosinGweicht= 0.796; // kg/dm3
- Kopieren der Methoden (siehe unten) in die Klasse Flugzeug
- gewicht()
- toString()
- drucken()
- Implementieren der Methode gewicht()
- Deklarieren Sie eine Variable
- berechnen Sie das aktuelle Gesamtgewicht
- geben Sie diese Variable zurück. Ersetzen Sie die 0 in return durch Ihre Variable
- Erweitern Sie die Klasse Flughafen
- Nutzen Sie das Objekt auf das lh1 zeigt.
- Belegen Sie alle Attribute mit vernünftigen Werten
- Rufen Sie die Methode drucken() für lh1 auf.
- Kontrollieren Sie die Werte
- Testen Sie das Programm in der Klasse Flugzeug
- In der Klasse Flugzeug
- Entnehmen Sie dem Treibstofflager des Flughafen 2000 Liter Treibstoff und füllen Sie Ihn in das Flugzeug lh1
Klasse Flugzeug
Im block2 anlegen!
package Airline.block2;
/**
*
* @author stsch
*/
public class Flugzeug {
String kennzeichen;
/**
*
* @return aktuelles Gewicht;
*/
public double gewicht() {
// Berechnen Sie das aktuelle Gewicht:
// 1. Anlegen einer Variablen
// 2. Berechnen des Gewichts
// 3. Variable in return zurückgeben
return 0;
}
public String toString() {return kennzeichen;}
public void drucken() {
System.out.println("Objekt: " + this);
System.out.println("kennzeichen: " + kennzeichen);
System.out.println("passagiere: " + passagiere);
System.out.println("Besatzung: " + besatzung);
System.out.println("Treibstoff: " + treibstoff);
System.out.println("Leergewicht: " + leergewicht);
System.out.println("maxGewicht: " + maxGewicht);
System.out.println("aktuelles Gewicht: " + gewicht());
}
}
Klasse Flughafen
package Airline.block2;
package Airline.block2;
/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
double treibstoffLager;
public static void main(String[] args) {
Flughafen pad = new Flughafen();
pad.name = "Paderborn";
pad.treibstoffLager = 1000000;
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug();
lh1.kennzeichen ="D-ABTL";
pad.gate1 = lh1;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
pad.gate1=null;
pad.gate2=lh1;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
}
}
- 21082 views
2.5 Lösungen
2.5 Lösungen2.5.1 Zuweisungen und Typen
class ArithmeticTest { public static void main (String args[]) { short x = 6; int y = 4; float a = 12.5f; float b = 7f; System.out.println("x is " + x + ", y is " + y); System.out.println("x + y = " + (x + y)); System.out.println("x - y = " + (x - y)); /* ??? */ System.out.println("x % y = " + (x % y)); System.out.println("a is " + a + ", b is " + b); /* ??? */ } }
2.5.2 Erlaubte Variablennamen
siehe Spezifikation Java 7 (Identifier)
Lösung durch Testen mit javac
public class VariableNames { int maxvalue; int maxValue; int max_value; //int max value; int end; int End; //int 10%ofSum; int sum10; int _10PercentOfSum; public static void main(String[] args) { } }
Erklärung
- maxvalue: korrekter Name
- maxValue: korrekter Name
- max_value: korrekter Name
- max value: Leerstelle im Namen ist nicht erlaubt
- end: korrekter Name
- End: korrekter Name
- 10%ofSum: Ziffer als erstes Zeichen ist nicht erlaubt
- sum10: korrekter Name
- _10PercentOfSum: korrekter Name
2.5.3 Literale
Das Programm übersetzt nicht weil
- der Wert 4000000000 ein "Integer"-Literal ist
- der Wert 4000000000 den Wertebereich einer Ganzzahl vom Typ "Integer" übersteigt
- Die Zuweisung auf eine Variable vom Typ "Long" spielt zu diesem Zeitpunkt noch keine Rolle
2.5.4 Ausdrücke
public class Expressions { public static void main(String[] args) { long a = 3; int b = 4; short c = 5; byte d = 6; int result1; long result2; result2 = d / b * a; System.out.println("d / b * a = " + result2 ); result1 = c + b * (d + 1); System.out.println("c + b * (d + 1) = " + result1 ); result1 = d / (c - 1) * b / 2; System.out.println("d / (c - 1) * b / 2 = " + result1 ); result1 = d % b; System.out.println("d % b = " + result1 ); result1 = -c % b; System.out.println("-c % b = " + result1 ); } }
Ergebnis
d / b * a = 3 c + b * (d + 1) = 33 d / (c - 1) * b / 2 = 2 d % b = 2 -c % b = -1
2.5.5 Zuweisungen
public class AllowedAssigments { public static void main(String[] args) { long a = 3; int b = 4; short c = 5; byte d = 6; a = b + 3 * (d + 1); b = c * c; c = (byte)(b / 3); d = (byte)(((byte)a + b)); d = (byte) ( a + b); } }
2.5.6 Ausdrücke
public class Runden { public static void main(String[] args) { int a, result; a =149; result = (a +50)/100*100; System.out.println("Eingabe = " + a + " Ergebnis = " + result); a =150; result = (a +50)/100*100; System.out.println("Eingabe = " + a + " Ergebnis = " + result); } }
Ergebnis
Eingabe = 149 Ergebnis = 100 Eingabe = 150 Ergebnis = 200
2.5.7 Zeitrechnung
public class timeCalculation { public static void main(String[] args) { int a,h,m,s; a = 0; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); a = 59; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); a = 60; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); a = 100; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); a = 3600; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); a = 4000; h = a/3600; m = (a-h*3600)/60; s = a - h*3600 - m*60; System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s); } }
Ergebnis
Sekunden = 0 = 0:0:0 Sekunden = 59 = 0:0:59 Sekunden = 60 = 0:1:0 Sekunden = 100 = 0:1:40 Sekunden = 3600 = 1:0:0 Sekunden = 4000 = 1:6:40
2.5.8 Polynomberechnung
public class Polynom { public static void main(String[] args) { double a, b, c, d, x, y; a = 1.2; b = -2.3; c = 4.5; d = -6.7; x = 8.9; y = a*x*x*x+b*x*x+c*x+d; System.out.print("Ergebnis = "+ y); System.out.println("\n oder..."); y = a*Math.pow(x,3)+b*Math.pow(x,2)+c*x+d; System.out.print("Ergebnis = "+ y); } }
Ergebnis
Ergebnis = 697.1298 oder... Ergebnis = 697.1297999999999
2.5.9 Abstand zwischen Punkten
public class Main { public static void main(String[] args) { double x1,x2, y1, y2, x, y, d; x1 = 1; y1 = 1; x2 = 4; y2 = 5; x = x1-x2; y = y1-y2; d = Math.sqrt(x*x+y*y); System.out.println("distance = " + d); } }
Ergebnis
distance = 5.0
2.5.10 Berechnung von ∏ (Pi)
Hilfe; Java Spezifikation: Division
public class PiCalculation { public static void main(String[] args) { double pi; pi = (1.0 - 1.0/3.0 + 1.0/5.0 - 1.0/7.0 + 1.0/9.0 - 1.0/11.0 + 1.0/13.0)*4; System.out.println("Pi = "+ pi); System.out.println("\n oder ..."); pi=1; for (int i=1;i<=10000;i++) { pi = pi + Math.pow(-1,i)/(2*i+1); } pi=pi*4; System.out.println("Pi = "+ pi); }
Ergebnis
Pi = 3.2837384837384844 oder ... Pi = 3.1416926435905346
2.5.11 Analoge Uhr
import static java.lang.Math.*; /** * * @author stsch */ public class Zeiger { public static final int maxRadius = 100; // Beeinflusst GUI-Größe ! /** * @param s Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle X Koordinate der Zeigerspitze des Sekundenzeigers * auf dem Bildschirm */ public static int sekundeX(int s) { int xs; /* Implementierung Beginn */ double sx = sin(s*2*PI/60)*maxRadius; xs = (int)sx; /* Implementierung Ende */ return xs; } /** * @param s Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle Y Koordinate der Zeigerspitze des Sekundenzeigers * auf dem Bildschirm */ public static int sekundeY(int s) { int ys; /* Implementierung Beginn */ double sy = -cos(s*2*PI/60)*maxRadius; ys = (int)sy; /* Implementierung Ende */ return ys; } /** * @param m Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle X Koordinate der Zeigerspitze des Minutenzeigers * auf dem Bildschirm */ public static int minuteX(int m) { int xm; /* Implementierung Beginn */ double mx = sin(m*2*PI/60)*maxRadius*0.75; xm = (int)mx; /* Implementierung Ende */ return xm; } /** * @param m Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59 * @return aktuelle Y Koordinate der Zeigerspitze des Minutenzeigers * auf dem Bildschirm */ public static int minuteY(int m) { int ym; /* Implementierung Beginn */ double my = -cos(m*2*PI/60)*maxRadius*0.75; ym = (int) my; /* Implementierung Ende */ return ym; } /** * @param h Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12 * @return aktuelle X Koordinate der Zeigerspitze des Stundenzeigers * auf dem Bildschirm */ public static int stundeX(int h) { int xh; /* Implementierung Beginn */ double hx = sin(h*2*PI/12)*maxRadius*0.5; xh = (int)hx; /* Implementierung Ende */ return xh; } /** * @param h Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12 * @return aktuelle Y Koordinate der Zeigerspitze des Stundenzeigers * auf dem Bildschirm */ public static int stundeY(int h) { int yh; /* Implementierung Beginn */ double hy = -cos(h*2*PI/12)*maxRadius*0.5; yh = (int)hy; /* Implementierung Ende */ return yh; } }
2.5.13 Airlines
Klasse Flughafen
package Airline.Block2;/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
double treibstoffLager;
public static void main(String[] args) {
Flughafen pad = new Flughafen();
pad.name = "Paderborn";
pad.treibstoffLager = 1000000;
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug();
lh1.kennzeichen ="D-ABTL";
pad.gate1 = lh1;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
pad.gate1=null;
pad.gate2=lh1;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
lh1.leergewicht = 184567;
lh1.maxGewicht = 412770;
lh1.passagiere = 200;
lh1.besatzung = 10;
lh1.drucken();
}
}
Klasse Flugzeug
package Airline.Block2;/*
*
* @author stsch
*/
public class Flugzeug {
String kennzeichen;
static final int durchschnittsGewicht= 75;
static final double kerosinGweicht= 0.796; // kg/dm3
public int passagiere;
public int besatzung;
public double treibstoff;
public double leergewicht;
public double maxGewicht;
/**
*
* @return aktuelles Gewicht;
*/
public double gewicht() {
// Berechnen Sie das aktuelle Gewicht:
// 1. Anlegen einer Variablen
// 2. Berechnen des Gewichts
// 3. Variable in return zurückgeben
double meinGewicht = leergewicht + passagiere*durchschnittsgewicht+
besatzung*durchschnittsgewicht+treibstoff*gewichtKerosin;
return meinGewicht;
}
public String toString() {return kennzeichen;}
public void drucken() {
System.out.println("Objekt: " + this);
System.out.println("kennzeichen: " + kennzeichen);
System.out.println("passagiere: " + passagiere);
System.out.println("Besatzung: " + besatzung);
System.out.println("Treibstoff: " + treibstoff);
System.out.println("Leergewicht: " + leergewicht);
System.out.println("maxGewicht: " + maxGewicht);
System.out.println("aktuelles Gewicht: " + gewicht());
}
}
- 10469 views
2.6 Webcast: Java Variablen, Zuweisungen und Operatoren
2.6 Webcast: Java Variablen, Zuweisungen und OperatorenEin graphisch animierter Webcast der die genauen Vorgänge des folgenden Beispielprogramms erläutert:
public class Mitarbeiter { ... public static void main (String[] args) { int a; long b; String eingabe; eingabe = args[0]; a = 15; a++; b = 2*a; System.out.println("Ausgabe :" + eingabe + b); } }
Das folgende Video dauert 8 Minuten.
Wichtige Aspekte:
- genaue Lebensdauer von Variablen
- genaues Vorgehen bei einem Cast
- Verwalten von temporären Datenstrukturen
- 5955 views
Klassenattribute
Sind mitarbeiterID und name nicht Objektattribute? Klassenattribute werden doch normalerweise mit static implementiert?
- Log in to post comments
2.7 Fragen
2.7 Fragen
|
2.7.1 GanzzahlarithmetikGanzzahloperationen haben eine Reihe von Eigenheiten. Es wird auf eine Variable a eine Variable b zugewiesen die um 25% größer sein soll. Hierfür gibt es drei Möglichkeiten. Im ersten Fall sei a klein (=6). Welches ist die beste Implementierung? Welche Operationen sind heikel? |
Implementierung | Ergebnis | |
---|---|---|
. |
int a = 0; int b = 6; a = b*5/4; a = b/4*5; a = b*(5/4); |
Was ist die beste Implementierung wenn man weiß das b groß ist? Z.Bsp. b= 230? Welche Operation ist heikel?
2.7.2 Sichere Zuweisungen
Welche der folgenden Zuweisungen sind sicher b.z.w unsicher?
Eine Zuweisung sei unsicher wenn bei der Zuweisung abhängig von der gewählten Variablenbelegung Datenverluster auftreten können.
Analysieren Sie die sechs Zuweisungen auf potentielle Datenverluste. Die Werte von a_short, b_int, c_long wurden absichtlich am oberen Ende des jeweiligen Wertebereichs gewählt um den Präzisionsverlust zu provozieren.
Hinweis: Vergleichen Sie die Wertebereiche der Ausgangs und Zielvariablen.
Ignorieren Sie die Auswertelogik. Das Programm ist ausführbar. Es wird die Belegungen der einzelnen Variablen auf der Konsole drucken. Es wird bei jedem Fall von Präzisionsverlust einen Text "(Fehler)" hinter die Ausgabe anfügen.
public class UebungTypkonversion {
public static void main(String[] args) {
// Alle Werte sind der drittgrößte Wert des jeweiligen Wertebereichs
short a_short = Short.MAX_VALUE-2; // short = 16 bit
int b_int = Integer.MAX_VALUE-2; // int = 32 bit
long c_long = Long.MAX_VALUE-2; // long = 64 bit
// cast auf float: Welche Zuweisungen können zu Präzisionsverlusten führen?
float c_float = a_short;
float d_float = b_int;
float e_float = c_long;
// cast auf double:Welche Zuweisungen können zu Präzisionsverlusten führen?
double f_double = a_short;
double g_double = b_int;
double h_double = c_long;
// Auswertelogik und Kontrolle
System.out.println("a_short=" + a_short);
System.out.println("a_int=" + b_int);
System.out.println("a_long=" + c_long);
System.out.print("short auf float= " + c_float + " ");
// Sind die Werte nach der Rückkonvertierung noch gleich?
if (a_short == (short)c_float) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("int auf float= " + d_float);
if (b_int == (int)d_float) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("long auf float= " + e_float);
if (c_long == (long)e_float) {System.out.println("(OK)");}
else { System.out.println("(Fehler)");}
System.out.print("short auf double= " + f_double + " ");
if (a_short == (short)f_double) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("int auf double= " + g_double);
if (b_int == (int)g_double) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("long auf double= " + h_double);
if (c_long == (long)e_float) {System.out.println("(OK)");}
else { System.out.println("(Fehler)");}
}
}
- 6332 views
2.8 Antworten
2.8 Antworten2.8.1 Ganzzahlarithmetik
Bei Ganzzahldivisionen werden alle Ergebnisse abgerundet.
Option a.) ist hier die optimale Lösung da der Rundungsfehler relativ gesehen am kleinsten ist wenn man zuerst multipliziert um dann eine möglichst große Zahl zu teilen.
Option c.) ist schlecht, da da 5/4 schon innerhalb der Klammer auf 1 abgerundet wird.
Option b.) ist ebenfalls schlecht da 6/4 ebenfalls auf 1 abgerundet wird
Implementierung | Ergebnis | |
---|---|---|
. |
int a = 0; int b = 6; a = b*5/4; a = b/4*5; a = b*(5/4); |
. . a=7 a=5 a=6 |
Die Variable b ist sehr groß b= 230:
- Option b.) ist vorteilhaft.
- Hier wird die Multiplikation zum Risiko. Der maximale 32 Bit Ganzzahlwert (231-1) kann bei Option a.) und b.) überschritten werden. Der Überlauf wird vom Laufzeitsystem nicht gemeldet und das Ergebnis ist falsch da negativ. Ein Überlauf des Wertebereichs durch die Mutiplkation ist wahrscheinlich schlimmer als ein Rundungsfehler durch eine frühe Division.
2.8.2 Sichere Zuweisungen
Konsolenausgaben:
a_short=32765
a_int=2147483645
a_long=9223372036854775805
short auf float= 32765.0 (OK)
int auf float= 2.14748365E9(Fehler)
long auf float= 9.223372E18(Fehler)
short auf double= 32765.0 (OK)
int auf double= 2.147483645E9(OK)
long auf double= 9.223372036854776E18(Fehler)
Die Mantisse des Typ float ist nicht groß genug um extrem große (oder kleine) Werte des Typs int und long präzise zu verwalten.
Die Mantisse des Typ double ist nicht groß genug, um extrem große (oder kleine) Werte des Typs long präzise zu verwalten.
- 4294 views
2.9 Lernziele
2.9 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu Typen, Operationen und Zuweisungen zu beantworten.
Interessante Fragen aus den Übungen:
- Frage zur Ganzzahlarithmetik beantworten
- Fragen aus den Übungen beantworten:
- 2.4.2 Erlaubte Variablenamen
- 2.4.3 Übung Literale
- 2.4.4 Ausdrücke
- 2.4.5 Zuweisungen
- 2.4.6 Java-Ganzzahlarithmetik zum Runden benutzen
Feedback
- 5156 views
Unterschiede zwischen Ganzzahl- und Fließkommaarithmetik
Ganzzahlarithmetik:
- extrem schnell
- präzise bei Addition, Subtraktion, Multiplikation solange das Ergebnis im Wertebereich bleibt
- keine Fehlermeldungen beim Überschreiten des Wertebereichs
- Javaausnahme nur bei der Division durch Null.
Fließkommanarithmetik
- schnell auf modernen 64 Bitprozessoren
- Alle Operationen sind mehr oder weniger ungenau. Dies hängt von der Größe der Mantisse und des Exponenten ab.
- Besondere Zustände wie "Infinity" oder "Not a number"
- Javaausnahmen beim Überschreiten der Wertbereiche.
- Log in to post comments
3. Ablaufsteuerung, Kontrollstrukturen
3. Ablaufsteuerung, Kontrollstrukturen Stefan Schneider Sun, 06/20/2010 - 15:51- 7445 views
3.1 Verzweigungen und Vergleichsoperationen
3.1 Verzweigungen und VergleichsoperationenDurch Verzweigungen können im Ablauffluß des Programmes verschiedene Fälle geprüft werden und man kann abhängig davon unterschiedliche Anweisungen ausführen.
Befehlsblöcke
Java erlaubt das Zusammenfassen von Javabefehlen durch Blöcke. Blöcke werden geschweiften Klammern beschrieben.
Für Blöcke gilt
- Im Block deklarierte Variablen sind nur innerhalb des Blocks, nach der Deklaration benutzbar
- Befehle innerhalb eines Blocks müssen mit Semikolon getrennt werden
- Auch der letzte Befehl in einem Block muss mit Semikolon abgeschlossen werden
- Blöcke können geschachtelt werden
Beispiel
{ int i = 1; i++; int j = i*2; }
Tipp: Systematisches Einrücken und Klammern die genau übereinander stehen helfen sehr bei der Lesbarkeit
Einfachverzweigung (if, if else)
Eine Einfachverzweigung wird in Java mit dem Schlüsselwort if eingeleitet. Anschließend folgt eine Bedingung in runden Klammern. Ist die Bedingung erfüllt, wird die folgende Anweisung bzw. der Anweisungsblock in geschweiften Klammern ausgeführt. Hierdurch ergibt sich die folgende Syntax:
if (Bedingung) Anweisung;
oder
if (Bedingung) {Anweisung(en);}
Ist die Bedingung nicht erfüllt wird die Anweisung ausgelassen und der normale Ausführungsfluß wird wieder aufgenommen. Ein Beispiel hierfür ist:
int x = 8; if (x > 5) System.out.println("Wert x = " + x + " ist größer als 5"); System.out.println ("Wert x = " + x);
Hier ergeben sich die Ausgaben:
Wert x = 8 ist größer als 5 Wert x = 8
da die bedingte Anweisung sowie die letzte Anweisung durchlaufen wird.
UML Darstellung in Aktivitätsdiagrammen
if Bedingung ohne else Klausel kann man die geschweiften Klammer bei einer einzelnen Anweisung weglassen.
Entweder-oder Kontrollfluss mit dem Schlüsselwort else
Es gibt auch die Möglichkeit eines entweder-oder Kontrollflusses mit Hilfe des Schlüsselworts else:
if (Bedingung) {Anweisung(en);} else {Anweisung(en);};
Hier wird der eine oder der andere Block von Anweisungen ausgeführt.
Ein Beispiel:
int x = 8; int y = 0; if (x > 5) { System.out.println("Wert x = " + x + " ist größer als 5"); y = x; } else { System.out.println("Wert x = " + x + " ist kleiner oder gleich 5"); y = -x; } System.out.println ("Wert x = " + x );
UML Darstellung in Aktivitätsdiagrammen
if Anweisungen lassen sich schachteln um komplexe Bedingungen prüfen zu können. Anbei ein Beispiel:
int a = 5; int b = 10; int maximum =0; if (a > b) // Äussere if Bedingung if (a > 0) { maximum = a;} // Innere if Bedingung else { maximum = 0; } //innere else Bedingung System.out.println ("Wert maximum = " + maximum "); |
Im oben gezeigten Beispiel gibt es eine else Bedingung von der es nicht offensichtlich ist zu welchem if Block sie gehört.
else-Regel |
---|
Ein else-Block gehört immer zum direkt vorhergehenden if-Block |
Tipp: Der Quellcode wird bei geschachtelten if Bedingungen schnell unübersichtlich und fehleranfällig.
Verwenden Sie besser die im Folgenden vorgestellte switch Bedingung um komplexe Abfragen zu implementieren.
Mehrfachverzweigung (switch-case)
Eine Alternative zu if-else Anweisungen ist die switch-case Anweisung. Sie erlaubt eine sehr viel übersichtlichere Programmierung wenn es bei einem ganzzahligen Ausdruck mehr als zwei Alternativen gibt.
Im einfachen Fall sieht eine switch-case Anwendung wie folgt aus:
int wochentag; ... switch (wochentag) { case 7: System.out.println("Sonntag"); break; case 6: System.out.println("Samstag"); break; case 5: case 4: case 3: case 2: case 1: System.out.println("Wochentag"); break; default: System.out.println("Fehler"); } |
int wochentag; ... switch (wochentag) { case 7: System.out.println("Sonntag"); break; case 6: System.out.println("Samstag"); break; case 5: case 4: case 3: case 2: case 1: System.out.println("Wochentag"); break; default: System.out.println("Kein gültiger Wochentag!"); }
Besonderheiten der switch-case Anweisung
- Der zu prüfende Ausdruck muss bestimmten Typen genügen:
- byte, short, int, char, enum, String. (Die Unterstützung von String ist neu seit JDK 7, siehe JSR 334, Projekt Coin)
- Die einzelnen Fälle (cases) werden nicht mit geschweiften Klammern geklammert
- Das Schlüsselwort break dient zum Rücksprung aus einem Fall. Der nächste Fall wird mit abgearbeitet wenn es weggelassen wird!
- Es können mehrere Fälle mit Doppelpunkt aufgeführt werden. Der darauf folgende Block wird dann für alle Fälle abgearbeitet.
- Das Schlüsselwort default erlaubt einen Ausführungsblock anzugeben, der ausgeführt wird wenn kein anderer Fall zum Zuge kam
- Der Wert in einem Fall (case) muss eine Konstante sein.
Allgemeine Form:
... switch (Ausdruck) { case konstante1: Anweisung(en); break; ... case konstante2: case konstante3: ... case konstanteN: Anweisung(en); break; default: Anweisung(en); break; }
Beispiel mit unterschiedlichen Break-Anweisungen
int wochentag; ... switch (wochentag) { case 7: System.out.println("Sonntag"); break; case 6: System.out.println("Samstag"); case 5: case 4: case 3: case 2: case 1: System.out.println("Wochentag"); break; default: System.out.println("Fehler"); } |
- 14307 views
Fehlende break; - Anweisung
Fehlt die break;-Anweisung, werden die folgenden Blöcke ohne Fallüberprüfung ausgeführt? Im UML-Diagramm ("Beispiel mit untersch. Break-Anweisungen) ist dargestellt, dass im case 6: automatisch auch die cases 5-1: angenommen werden und somit "Samstag" UND "Wochentag" gedruckt werden würden.
Ist das tatsächlich der Fall, oder werden die folgenden Cases trotz fehlender break-Anweisung zumindest geprüft? Das "der nächste Fall wird mitabgearbeitet" ist etwas undeutlich formuliert.
Besten Dank
- Log in to post comments
Gut beobachtet
Ja, fehlt ein break, läuft das Programm einfach weiter. Zur Formulierung muss ich erst nochmal nachdenken.
- Log in to post comments
Einfach ausprobieren in der JS-Konsole (mit: console.log)
wochentag=6
switch (wochentag) {
case 7:
console.log("Sonntag");
break;
case 6:
console.log("Samstag");
case 5: case 4: case 3: case 2: case 1:
console.log("Wochentag");
break;
default:
console.log("Fehler");
}
=>
Samstag
Wochentag
- Log in to post comments
break-Schlüsselwort nach default
Im Bsp unter "Besonderheiten der switch-case Anweisung" wird die default-Anweisung mit einem "break;" beendet, im "Beispiel mit unterschiedlichen Break-Anweisungen" jedoch ohne.
Unterliegt beides der korrekten Syntax?
Gruß.
- Log in to post comments
Bei Ihrem Beispiel mit…
Bei Ihrem Beispiel mit verschachtelten if und else Strukturen ist bei dem System.out.println("Wert maximum: " + maximum"); hinter dem letzten Maximum noch ein Anführungszeichen, welches gelöscht werden muss.
- Log in to post comments
3.2 Schleifen und Felder
3.2 Schleifen und FelderSchleifen erlauben die Wiederholungen von Einzelanweisung. Schleifen bestehen typischerweise aus den folgenden Komponenten
- einen Schleifenrumpf mit den auszuführenden Anweisungen
- einem Kopf oder Fuß mit einer Ausführungsbedingung
- oft einer Laufvariablen mit der die Durchläufe kontrolliert werden
Die while Schleife
Die while Schleife überprüft vor dem Schleifeneintritt ob die Ausführungsbedingung (noch) erfüllt ist. Sie enthält die Schleifenbedingung im Kopf der Schleife.
Die Syntax der while Schleife ist die folgende:
while ( Bedingung) { Anweisung ; } oder while ( Bedingung) Anweisung; Beispiel: Arithmetische Summe 1+2+3+4+5 int i = 1; int summe= 0; while (i<=5) { summe = summe + i; i++; } |
Die do-while-Schleife
Die do-while-Schleife überprüft nach dem ersten Schleifendurchlauf ob die Ausführungsbedingung erfüllt ist. Sie enthält die Schleifenbedingung im Fuß der Schleife. Die Syntax der do-while Schleife ist die folgende: do { Anweisung ; } while ( Bedingung ); oder do Anweisung; while ( Bedingung ); Beispiel: Beende Schleife wenn Wert durch 3 ohne Rest teilt int i = 9; int j = 0; // Zählt Schleifendurchläufe do { j++; i--; } while (i%3 != 0); Die Schleife wird dreimal durchlaufen. |
Regel:
- Die while Schleife ist eine abweisende Schleife: Sie wird nicht notwendigerweise durchlaufen.
- Die do-while ist eine nicht abweisende Schleife: Sie wird mindestens einmal durchlaufen.
Diese Unterscheidung ist in verschiedenen Bereichen wichtig
- Variablen werden bei einer abweisenden Schleife eventuell nicht belegt
- Es wird manchmal in der Qualitätssicherung gefordert, dass alle Zeilen eines Programmes duchlaufen werden. Bei abweisenden Schleifen muss man unter Umständen bei der Implementierung der Testabdeckung mehr investieren.
Die for-Schleife
Die for-Schleife überprüft die Schleifenbedingung vor dem Eintritt in die Schleife.
Ihre Syntax ist die anspruchsvollste der drei Schleifenarten:
for (Initialiserung; Bedingung; Veränderung) {Anweisung ;}
oder
for (Initialiserung; Bedingung; Veränderung) Anweisung;
Der Kopf der for-Schleife besteht aus den folgenden drei Teilen:
- Initialisierungsteil: Laufvariable und Startwert werden festgelegt.
- Bedingungsteil: Wird der Wert der Bedingung unwahr (false) wird die Schleife verlassen oder nicht betreten. Ansonsten wird sie weiter fortgesetzt.
- Veränderungsteil: Nach jedem Durchlauf der Schleife wird die Laufvariable entsprechen verändert (Typischerweise inkrementiert oder dekrementiert).
Beispiel einer einfachen for-Schleife
Im folgenden Beispiel wird die for-Schleife "b mal" durchlaufen. Durch das Aufaddieren der Variablen a wird eine Multiplikation von positiven Zahlen ausgeführt.
Ist b gleich Null oder negativ wird die Schleife nicht durchlaufen.
Geschachtelte Schleifen
Oft ist es notwendig Schleifen zu schachteln. Dies kommt oft bei mehrdimensionalen Datenstrukturen vor bei denen jedes Feld bearbeitet werden muss. Bei jedem Durchlauf der äusseren Schleife wird die innere aufgerufen, die ihre eigenen Durchläufe komplett bei jedem Durchlauf der äusseren Schleife durchführt. Anbei ein Beispiel einer naiven Multiplikation die auf Inkrementieren beruht:
class MultiplizierenNaiv { public static void main(String[] args) { int a = 5; int b = 10; int result = 0; for (int i = 1; i <= a; i++) { System.out.println("Äussere Schleife i = " + i); for (int j = 1; j <= b; j++) { System.out.println("Innere Schleife j = " + j); result++; } } System.out.println(a +"*"+b+" = "+result); } }
Geschachtelte Schleifen sind sehr mächtig und sie haben ein viel größeres Potential Rechnerleistung zu binden. Die Aufwände bei zwei geschachtelten Schleifen können quadratisch steigen im Vergleich zum linearen Aufwand einer einfachen Schleife. Generell gilt, dass eine teure Anweisung in einer Schleife n-mal statt einmal durchlaufen werden kann. Es ist daher wichtig auf die folgenden Dinge zu achten:
- Lassen Sie alle Anweisungen die man vor oder nach der Schleife ausführen kann aus dem Schleifenblock draussen
- Definieren und Initialisieren Sie Variablen wenn möglich ausserhalb der Schleife und verwenden Sie die Variablen wieder. Die Variable muss sonst jedes mal neu angelegt und wieder gelöscht werden.
- Verzichten Sie auf unnötige Schleifendurchläufe
- Seien Sie vorsichtig bei mehrfach geschachtelten Schleifen. Die Anzahl der Durchläufe kann theoretisch sehr schnell, sehr groß werden. Eine dreifach geschachtelte Schleife mit jeweils 1000 Durchläufen wird eine Milliarde mal ausgeführt!
Sprunganweisungen und Schleifenabbrüche mit continue und break Schlüsselwörtern
Java verfügt über zwei Möglichkeiten Schleifen in der Mitte von Ausführungsblöcken zu verlassen. Diese Programmiertechnik sollte man normalerweise vermeiden.
In manchen Fällen ist sie jedoch nützlich. Schleifenabbrüche werden durch die beiden folgenden Schlüsselwörter gesteuert:
Die break Anweisung ist schon vom switch Befehl her bekannt. Mit ihr kann man auch eine if Bedingung beenden. Beispiel einer continue Anweisung: ... for (int i=0; i<= 100; i++) { if (i%2 == 0) continue; System.out.println("Die Zahl " + i + " ist ungerade"); } Bei geraden Zahlen wird die Druckanweisung nach der if Anweisung nicht mehr ausgeführt. Die Schleifen werden jedoch alle durchlaufen. |
Beispiel einer break-Anweisung:
... for (int i=1; i<= 100; i++) { if (i== 32) break; System.out.println("Die Zahl " + i + " wurde bearbeitet"); } Die Zahlen von 1 bis 31 werden ausgedruckt. Anschließend wird die Schleife abgebrochen. Die break und continue Anweisungen beziehen sich immer auf die nächst äussere Schleife. Mit Hilfe von Labels (Marken) kann man auch über mehrere geschachtelte Schleifen nach aussen springen. |
Verlassen von Blöcken mit Hilfe von Sprungzielen (Label)
Java erlaubt das Benennen von Blöcken mit Hilfe von "Labeln" (Marken). Mit ihnen kann man mit einer break-Anweisung beliebig viele Blöcke auf einmal verlassen.
Das folgende Beispiel zeigt wie man zum Label "Label1" springt um beide Schleifen auf einmal zu verlassen:
class MultiplizierenNaivLabel { public static void main(String[] args) { int a = 5; int b = 10; int result = 0; Label1: for (int i = 1; i <= a; i++) { System.out.println("Äussere Schleife i = " + i); for (int j = 1; j <= b; j++) { System.out.println("Innere Schleife j = " + j); if ((i == 3) && (j == 3)) break Label1; result++; } } System.out.println(a +"*"+b+" = "+result); System.out.println("Dieses Ergebnis ist falsch..."); } }
Endlosenschleifen
Endlosenschleifen sind Schleifen die nicht terminieren. Ein Programm mit einer Endlosschleife sieht aus wie ein "hängendes" Programm. Es bindet jedoch sehr wahrscheinlich (mindestens) einen physikalischen Prozessor vollständig! Anbei einige Beispiele mehr oder weniger leicht zu erkennende Endloschleifen: while ( true) { /* Anweisung(en) */ } ... for ( ; ; ) { /* Anweisung(en) */ } ... int i=10; while (i>5) i++; ... int i=0; while ( i!=99) {i++; i++;} Der Übersetzer erkennt diverse einfache Endlosschleifen und meldet die darauf folgende Codezeile als "unreachable code". |
Trivia: Welcher Obsthändler hat die Adresse 1 Infinite Loop, Cupertino, CA 95014?
|
Felder (Arrays), Einführung
Die folgende Kurzüberblick von Feldern ist notwendig um die Übungen zu den Kontrollstrukturen zu lösen. Felder werden später noch einmal aufgegriffen und genauer behandelt.
Felder sind Datenbehälter mit einer oder mehreren Dimensionen um eine Anzahl primitive Datentypen gleichen Typs aufzunehmen.
Man kann auf die einzelnen Feldelemente wahlfrei, also direkt mit Hilfe eines Index zugreifen. Java benutzt rechteckige Klammern [] um auf einzelne Elemente eines Feldes zuzugreifen.
Initialisieren und Deklarieren von Feldern
Felder sind im Gegensatz zu Variablen mit primitiven Datentyp Objekte. Dies bedeutet, dass die die Variable nur aus einer Referenz besteht. Das Feld selbst muss dynamisch angelegt werden. Hierfür gibt es eine Reihe von Gründen.
- Der Übersetzer kann nicht wissen wie groß ein Feld werden kann
- Man kann mit einer Feldvariablen dynamisch neue Felder verwalten
- Felder können sehr groß sein. Es ist oft klüger das Feld erst anzulegen wenn man es benötigt und die Größe kennt.
Die Deklaration von Feldern geschieht wie folgt:
int[] x; // Anlegen einer Referenz, es ist noch keine Größe vorgeben, // es können noch keine Feldelemente benutzt werden< double[] y; // Anlegen einer Referenz, es ist noch keine Größe vorgeben, //es können noch keine Feldelemente benutzt werden
Um das eigentliche Feld anzulegen muss der new Operator verwendet werden wie für reguläre Objekte auch um den Speicher zu allozieren. Dies geschieht wie folgt:
int [] x; x = new int[4]; double[] y; y = new double[200];
Jetzt zeigt x auf 4 Datenbehälter für Ganzzahlen. Der Zugriff auf die vier Elemente erfolgt über einen Index im Bereich von 0 (erster Wert) bis 3.
Zugriff auf Felder
Das Feld kann nun wie folgt belegt und bearbeitet werden:
int [] x = new int[4]; x[0] = 99; x[1] = 88; x[2] = x[0]+x[1]; x[3] = x[0]*x[1];
Bestimmen der Länge eines Feldes
...geschieht mit dem Attribut .length. Hier ein Beispiel:
int[] x; int groesse; ... groesse=x.length;
Erweiterte for Schleife (enhanced for-loop)
Seit Java 5 ist es möglich mit der for Schleife Felder komfortabler abzuarbeiten. Man kann mit einer Laufvariable alle Elemente des gegeben Feldes abarbeiten. Wie zum Beispiel:
int [] xxx = { 11, 22, 33, 44, 55}; for ( int position : xxx) // für Element auf Position position in xxx[] System.out.println("Wert: " + position);
Wird das folgende Ergebnis liefern:
Wert: 11 Wert: 22 Wert: 33 Wert: 44 Wert: 55
Die Oracle Java Dokumentation erklärt die Einsatzmöglichkeiten und Grenzen der "enhanced for-loop".
- 16513 views
Endlosschleife
"int i = 10
while (i > 5 ) i++;"
fehlen nach der Bedingung nicht die { }?
Also {i++;} ?
- Log in to post comments
Nein
Das wurde hier in Syntax nicht vollständig korrekt dargestellt.
Die allgemeine Regel für Programmierblöcke ist: Man darf anstatt mehrerer Befehle in einem Block mit geschweiften Klammern immer auch einen einzelnen Befehl ohne geschweifte Klammer schreiben.
Die Syntax müßte wohl als regulärer Ausdruck so aussehen:
while (bedingung) [{ anweisung; {anweisung;}} , anweisung; ]
- Log in to post comments
Bestimmen der Länge eines Feldes
Sollte es nicht heißen
groesse = x.length(); statt groesse = x.length; ?
lg
- Log in to post comments
length ist keine Methode, das Feld ist keine Klasse
das ist ein Spezialfall. Felder sind keine Klassen und haben keine Methoden. length ist ein Attribut welches alle Felder besitzen.
Man kann das auch mal selbst in eine Entwicklungsumgebung eintippen und schauen was passiert...
- Log in to post comments
Mag sein, dass das ein…
Mag sein, dass das ein Erbsenzähler Problem ist, allerdings wird unter dem Punkt 'Bestimmen der Länge eines Feldes' beschrieben, dass die Länge mit der .length()-Methode bestimmt wird.
Sollte hier nicht length-Attribut stehen?
- Log in to post comments
for Schleife
Im Beispiel einer einfachen for Schleife steht, dass sie b mal durchlaufen wird, jedoch ist die Bedingung der for Schleife i
- Log in to post comments
3.2.1 Schleifentransformationen
3.2.1 SchleifentransformationenDie drei Java Schleifentypen sind für unterschiedliche Zwecke entwickelt worden. Man Sie jedoch ineinander Überführen.
Hier sind einige Regeln
While Schleife in Do-While Schleife überführen
von While Schleife | zu do-While Schleife | |
---|---|---|
Java |
while (Bedingung) {Block A } |
do if (Bedingung) { Block A } while ( Bedingung ) |
UML |
Do-While Schleife in While Schleife überführen
Eine Do-While Schleife führt einen Block mindestens einmal aus. Man kann Sie in eine While Schleife tranformieren in dem man den Block kopiert und voran stellt.
Vorsicht: Durch das duplizieren des Codes wird der Code schlechter wartbar!
von Do-While Schleife | zu While Schleife | |
---|---|---|
Java |
do Block A while ( Bedingung ) |
Block A while (Bedingung) {Block A } |
UML |
For-Schleife in eine While Schleife überführen
von For-Schleife | zu While Schleife | |
---|---|---|
Java |
for (Initialiserung; Bedingung; Veränderung) {Block A} |
Initialisierung while (Bedingung) { Block A; Veränderung } |
While Schleife in For-Schleife überführen
While Schleifen kan man recht einfach in For-Schleifen überführen.
von While-Schleife | zu For-Schleife | |
---|---|---|
Java |
while (Bedingung) { Block A;} |
for (; Bedingung;) {Block A} |
Eine solche Überführung wird aber recht selten angewendet, da der Initialisierungsanteil und die Veränderung der for Schleife irgendwo im Block A verborgen sind.
Do-While Schleife in For-Schleife überführen
Do-While Schleifen kan man auch recht einfach in For-Schleifen überführen.
von While-Schleife | zu For-Schleife | |
---|---|---|
Java |
do { Block A;} while (Bedingung) |
Block A; for (; Bedingung;) {Block A;} |
Eine solche Überführung wird aber recht selten angewendet, da der Initialisierungsanteil und die Veränderung der for Schleife irgendwo im Block A verborgen sind.
- 3866 views
Semikolon
Hallo,
muss bei der Transformation von der For Schleife zu der While Schleife hinter die Initialisierung ein Semikolon?
Und mir ist aufgefallen, dass bei der letzten Transformation die Begriffe in der Tabelle nicht stimmen.Es handelt sich um eine Transformation von einer Do While Schleife zu einer For Schleife, aber in der Tabelle steht von einer While Schliefe zu einer While Schleife.
Mit freundlichen Grüßen!
- Log in to post comments
3.3 Übungen
3.3 Übungen
3.3.1 Übung: SchleifenterminierungWelche der folgenden Schleifen terminieren?
|
int i = 1, j = 1; do { i = i + j; j++; } while (i < 200);
int i = 1, j = 20; while (i + j < i) { i = i + 2; j--; }
int i = 1, j = 20; while (i + j > i) { i = i + 2; j--; }
int i = 100, j = 27; while (i != j) { i = i / 2; j = j / 3; }
3.3.2 Übung: Ziffern einer Zahl
Hilfestellung:
Einlesen von Zahlenwerten beim Starten von der Kommandozeile als Option wie zum Beispiel:
$ java Main 17 19
erfolgt durch Einlesen von Zeichenketten und Umwandlung in Zahlen. Anbei ein Rahmenprogramm welches das Problem löst:
public class Main { public static void main(String[] args) { int firstArg=0; int stellen=0; if (args.length > 0) { try { firstArg = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen"); // Implementierung gehört hierher System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!"); } }
Eine andere Möglichkeit besteht darin, dass man das Programm startet und bei Bedarf eine Eingabe von der Konsole anfordert.
Achten Sie hier auf die fettgedruckte Importanweisung der Klasse Scanner. Sie ist notwendig um die Klasse zu benutzen:
import java.util.Scanner; public class Eingabe { public static void main(String[] args) { Scanner eingabe = new Scanner(System.in); System.out.print("Geben Sie die erste Zahl ein: "); int zahl1 = eingabe.nextInt(); System.out.print("Geben Sie die zweite Zahl ein: "); int zahl2 = eingabe.nextInt(); System.out.println("Ergebnis: " + zahl1); System.out.println("Ergebnis: " + zahl2); } }
Das Programm Eingabe kann man auf der Konsole wie folgt bedienen:
$ java Eingabe Geben Sie die erste Zahl ein: 34 Geben Sie die zweite Zahl ein: 56 Ergebnis: 34 Ergebnis: 56
Am einfachsten ist es die Eingaben in der ersten Zeile des Programmes hart zu belegen...
3.3.3 Übung: Quersumme
Schreiben Sie ein Java-Programm, das die Quersumme einer positiven ganzen Zahl berechnet und ausgibt.
Beispiel: Zahl 4711 --> Quersumme 13.
3.3.4 Übung: Zahlenstatistik
Hilfestellung:
Einlesen eines Feldes (Arrays) von der Kommandozeile
package s1.block3; public class Zahlenstatistik { public static void main(String[] args) { int feld[]; if (args.length > 0) { feld = new int[args.length]; try { for (int i=0;i<args.length;i++) { feld[i] = Integer.parseInt(args[i]); // Einlesen der Kommandozeilenargumente und umwandeln in Ganzzahlen } } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } // Ab hier steht das Feld mit allen Werten zur Verfügung } }
3.3.5 Übung: Primfaktorzerlegung
- Die Zahl 100 besteht aus den Primfaktoren 2, 2, 5, 5;
- die Zahl 252 aus den Primfaktoren 2, 2 , 3, 3, 7.
Option: Optimieren sie Ihr Programm auf die kürzeste Laufzeit. Verwenden Sie hierzu den Nanotimer von JDK 6.0 wie folgt:
package s1.block3; public class Primzahlzerlegung { public static void main(String[] args) { int firstArg = 0; int p=0; long time; time= System.nanoTime(); if (args.length > 0) { try { p = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Eingelesene Zahl: " + p); } // Die Eingabe der Kommandozeile liegt als Variable p vor. // Implementierung ... time= System.nanoTime() - time; System.out.println("Zeit in m: "+ time); } }
Überlegungen:
- Welche Zeit wird hier gemessen?
- Was hat ausser dem Algorithmus noch Einfluss auf die Ausführungsgeschwindigkeit?
- Sind Ihre Messungen wiederholbar?
- Welche Risiken hat eine Optimierung des Algorithmus?
- Wie wichtig ist die Schnelligkeit eines Programms?
3.3.6 Übung: Codevereinfachung
// Annahme: j >= 0 i = 0; while (i != j) i++;
while (a < b) { c = a; a = b; b = c; }
3.3.7 Übung: Wochentagberechnung
Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr sowie eine weitere Zahl zwischen 0 und 6 ein, die den Wochentag (Sonntag bis Samstag) des 1. Januars dieses Jahres darstellt. Berechnen Sie den Wochentag des eingelesenen Datums und geben Sie diesen aus. Sie können davon ausgehen, dass die Eingaben korrekt sind. Berücksichtigen Sie auch Schaltjahre.
3.3.8 Übung: Überlaufprüfung
Lesen Sie zwei 32 Bit Ganzahlen (int) a und b ein und prüfen Sie, ob bei ihrer Addition ein Überlauf stattfindet, also eine Summe entstehen würde, die größer als 231 - 1 oder kleiner als -231 ist.
3.3.9 Übung: Schnitt zweier Linien
Lesen Sie die Endpunkte zweier horizontaler oder vertikaler Linien in Form ihrer x- und y-Koordinaten ein und prüfen Sie, ob sich die beiden Linien schneiden. Die beiden Linien befinden sich in einem zweidimensionalen kartesischen System:
Hinweis: Wenn es einen Schnittpunkt gibt ist er (c,b)
3.3.10 Übung: Codevereinfachung
a)
if (b == 0) a = 2 * c; else if (c != 0) a = a * b + 2 * c; else a = a * b;
b)
if (x < 0 && y < 0) a = x * y; else if (x < 0) a = x * (-y); else if (y > 0) a = (-x) * (-y); else a = x * (-y);
3.3.11 Übung: Dreiecksbestimmung
Schreiben Sie ein Java-Programm, das die Seitenlängen eines Dreiecks einliest und prüft, ob es ein
- gleichseitiges
- gleichschenkeliges
- rechtwinkeliges
- sonstiges gültiges
- ungültiges Dreieck ist.
Ein Dreieck ist ungültig, wenn die Summe zweier Seitenlängen kleiner oder gleich der dritten Seitenlänge ist. Beachten Sie, dass ein Dreieck sowohl rechtwinkelig als auch gleichschenkelig sein kann!
3.3.12 Übung: Sortieren
Schreiben Sie ein Java-Programm, das 3 Zahlen a, b und c einliest und sie in sortierter Reihenfolge wieder ausgibt. Im Rahmenprogramm muss die letzte Zeile durch einen Sortieralgorithmus und Ausgaben ersetzt werden.
public class Sortieren { public static void main(String[] args) { int a = 0; int b = 0; int c = 0; if (args.length > 2 ) { try { a = Integer.parseInt(args[0]); b = Integer.parseInt(args[1]); c = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c); System.out.println("Anstatt dieser Zeile muss ein Sortieralgorithmus implementiert werden..."; } }
3.3.13 Übung: Plausibilitätsprüfung
Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr ein. Prüfen Sie, ob es sich um ein gültiges Datum handelt. Berücksichtigen Sie auch Schaltjahre.
Hinweis: Gregorianischer Kalender: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist. Jahre, die durch 100, aber nicht durch 400 teilbar sind, sind keine Schaltjahre.
Vorsicht: In der Musterlösung werden Felder verwendet. Die sind noch nicht vorgestellt worden, sie machen es aber deutlich leichter...
3.3.14 Übung: Textverschlüsselung
Schon im alten Rom verschlüsselte man Nachrichten. Ein einfaches Verschlüsselungsverfahren ist von Julius Cäsar überliefert. Es verschiebt jeden Buchstaben der Nachricht um einen fixen Wert n. Ist n gleich 2, so wird 'A' auf 'C', 'B' auf 'D' und 'Z' auf 'B' verschoben. Ziffern und Sonderzeichen werden nicht verschoben. Schreiben Sie ein Programm, das eine Zahl n und einen beliebigen Text liest und ihn nach obigem Verfahren verschlüsselt wieder ausgibt.
3.3.15 Übung: Verzweigungen
Schreiben Sie ein Java-Programm, das drei Werte x, y und z einliest und prüft, ob
- x, y und z nicht lauter gleiche Werte enthalten,
- x, y und z lauter verschiedene Werte enthalten,
- mindestens zwei Werte gleich sind.
3.3.16 Übung: Häufigkeit von Zeichen
Schreiben Sie ein Programm, das einen Text liest und die Häufigkeit der darinvorkommenden Zeichen berechnet. Geben Sie die Zeichenhäufigkeit als Histogramm aus. Beispiel:
a **** b ** c ******** ...
Hinweis: Dieses Programm setzt Kenntnisse von Feldern vorraus die erst später behandelt werden
3.3.17 Übung: Weckzeiten implementieren
Implementieren Sie zwei logische Tests für die Kontrolle der Weckzeit einer Uhr.
Diese Aufgabe baut auf der Übung zur Implementierung einer analogen Uhr auf.
Sie benötigen 3 Klassen für diese Aufgabe die alle im gleichen Verzeichnis stehen müssen:
- Klasse Zeiger (Lösung von 2.4.11) oder Ihre eigene Implementierung. Diese Klasse muss nicht modifiziert werden.
- Klasse WeckerUhr (weiter unten). Die Klasse muss nicht modifiziert werden. Die Klasse enthält die main() Methode. Es muss immer diese Klasse (java WeckerUhr) zum Starten der Anwendung verwendet werden!
- Klasse Weckzeit: Diese Klasse muss in der Übung modifiziert werden. Die Vorlage der Klasse ist bereits übersetzbasr und ausführbar.
Es reicht die Klasse WeckerUhr zu übersetzen. Die beiden anderen Klassen werden automatisch rekursiv mitübersetzt.
Nach dem Starten der Anwendung mit java WeckerUhr erscheint das folgende Fenster:
Uhr im normalen Zustand |
Uhr beim Klingeln (roter pulsierender Kreis) |
Erste Aufgabe: Kontrolle der Wertebereiche
Die Klasse Weckzeit enthält eine Methode korrekteWeckzeit(int h, int m, int s). Diese Methode prüft die Eingaben des GUI auf korrekte Wertebereiche. Das Ergebnis wird in einer boolschen Variablen result gespeichert. Die Vorlage liefert bei allen Eingaben false. Belegen Sie die Variable so, dass vernünftige Werte akzeptiert werden.
Fragen:
- Was sind vernünftige Werte für Stunde (h), Minute(m), Sekunde(s)?
- Welche logische Verknüpfung muss man nutzen wenn alle Werte im korrekten Bereich sein sollen?
Der Erfolg Ihrer Implementierung können Sie daran erkennen, dass die Eingaben des GUI nach dem klicken des OK Knopfs in das Anzeigenfeld übernommen worden sind.
Zweite Aufgabe: Bestimmen der richtigen Zeitpunkte zum Wecken
Das Lösen der ersten Teilaufgabe ist eine Voraussetzung für den zweiten Teil (Ihr Wecker funktioniert sonst nur Mittags und um Mitternacht...)
Das Hauptprogramm der Klasse WeckerUhr ruft in der aktuellen Implementierung viermal pro Sekunde die Methode klingeln() auf um zu kontrollieren ob der Wecker klingeln soll. Ändern Sie die Referenzimplementierung derart, dass nur nach der eingebenen Weckzeit für eine bestimmte Periode geklingelt wird.
Die zu modifizierende Referenzimplementierung:
result = (aktS%10 == 0);
lässt den Wecker bei allen Sekundenwerten die durch 10 teilbar sind für eine Sekunde klingeln.
Die Variablen aktH, aktM, aktS enthalten die aktuelle Zeit. Die Variablen wzh, wzm, wzs enthalten die Weckzeit.
Fragen:
- Wie lange soll der Wecker klingeln?
- (Zur Verfeinerung) Wie kann man gewährleisten, dass der Wecker auch um 11:59:50 für 15 Sekunden klingelt?
Klasse Weckzeit
package s1.block3; public class Weckzeit { int wzh = 0; // Weckzeit in Stunden int wzm = 0; // Weckzeit in Minuten int wzs = 0; // Weckzeit in Sekunden public void setzeWeckzeit(int hh, int mm, int ss) { if (korrekteWeckzeit(hh,mm,ss)) { wzh = hh; wzm = mm; wzs = ss; } } public boolean korrekteWeckzeit(int h, int m, int s) { boolean result; // benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen result = false; return result; } public boolean klingeln(int aktH, int aktM, int aktS) { boolean result; // benutzen die Variablen der aktuellen Zeit aktH (Stunde), // aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs // um zu bestimmern ob der Wecker klingeln soll // Verbessern Sie diese Zuweisung // In der aktuellen Implementieren klingelt der Wecker // alle 10 Sekunden für 1 Sekunde result = (aktS%10 == 0); return result; } }
Klasse WeckerUhr
package s1.block3; /* * Zeichnen einer analogen Uhr in einem JFrame */ import java.awt.*; import java.awt.event.*; import java.util.Calendar; import javax.swing.*; /** * * @author sschneid */ public class WeckerUhr extends JPanel { int sekunde = 0; int minute = 0; int stunde = 0; boolean klingeln = false; String tzString; // aktuelle Zeitzone int initialHeight; float zoom = 1; boolean an = false; JFrame hf; // Das Fenster der Anwendung Container myPane; JTextField h,m,s; JButton eingabe; Weckzeit wz; int klingelRadius = 0; /** * Konstruktor der Klasse. Er initialisiert die Grafik */ public WeckerUhr() { wz = new Weckzeit(); hf = new JFrame("Uhr"); h = new JTextField(2); m = new JTextField(2); s = new JTextField(2); eingabe = new JButton("OK"); eingabe.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int stunde, minute, sekunde; try { stunde = Integer.parseInt(h.getText()); minute = Integer.parseInt(m.getText()); sekunde = Integer.parseInt(s.getText()); } catch (NumberFormatException ex) { // Es wurde keine korrekte Zahl eingegeben stunde = -100; minute = -100; sekunde= -100; } wz.setzeWeckzeit(stunde,minute,sekunde); } }); // Beenden der Anwendung bei Schließen des Fenster hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Aufbau des Contentpanes myPane = hf.getContentPane(); myPane.add(this, BorderLayout.CENTER); JPanel wzPanel = new JPanel(new GridLayout(1,0)); wzPanel.add(new JLabel("hh:mm:ss")); wzPanel.add(h); wzPanel.add(m); wzPanel.add(s); wzPanel.add(eingabe); myPane.add(wzPanel,BorderLayout.SOUTH); // Erzeuge einen Menüeintrag zum Beenden des Programms JMenuBar jmb = new JMenuBar(); JMenu jm = new JMenu("Datei"); JMenuItem exitItem = new JMenuItem("Beenden"); exitItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); jm.add(exitItem); jmb.add(jm); hf.setJMenuBar(jmb); hf.pack(); // Das JFrame sichtbar machen // Gewünschte Größe setzen // 1. Parameter: horizontale Größe in Pixel // 2. Parameter: vertikale Größe in Pixel hf.setSize(2 * Zeiger.maxRadius + 80, 2 * Zeiger.maxRadius + 130); hf.setVisible(true); hf.setAlwaysOnTop(true); initialHeight = getHeight(); } /** * Hauptprogramm der Anwendung. Es werden keine Eingabeparameter benötigt * @param args dieser Parameter wird nicht ausgewertet */ public static void main(String[] args) { WeckerUhr dieUhr = new WeckerUhr(); dieUhr.tickTack(); } /** * Diese Methode verwaltet den Zeitgeber-thread. Dieser Thread belegt * die statischen Variablen der Uhrzeit neu */ public void tickTack() { try { boolean blinken = false; while (true) { Thread.sleep(250); // Schlafe x Millisekunden // Hole Systemzeit und belege statische Variablen Calendar call = Calendar.getInstance(); tzString = call.getTimeZone().getDisplayName(); sekunde = call.get(Calendar.SECOND); minute = call.get(Calendar.MINUTE); stunde = call.get(Calendar.HOUR); klingeln = wz.klingeln(stunde,minute,sekunde); if (blinken){ klingelRadius=100; } else { klingelRadius=30; } blinken = !blinken; repaint(); } } catch (InterruptedException e) { System.out.println( "Die Anwendung wird wegen einer Ausnahme beendet"); } } /** * Überladene Paintmethode. Sie führt alle Zeichenoperationen im Panel aus * @param g vom Laufzeitsystem übergebenes Graphikobjekt. */ @Override public void paint(Graphics g) { super.paint(g); zoom = (float)getHeight()/(float)initialHeight; int maxRadius = Zeiger.maxRadius; int xCenter = (int)(maxRadius*zoom) + 40; int yCenter = (int)(maxRadius*zoom) + 20; float fontSize = g.getFont().getSize2D(); int charCenterOffSet = (int)(fontSize/2); String timeString = stunde + ":" + minute + ":" + sekunde + " " + tzString; String klingelString; klingelString = wz.wzh +":"+wz.wzm + ":" +wz.wzs +" klingeln"; if (klingeln) { g.setColor(Color.red); g.fillOval(xCenter-((int)(zoom*klingelRadius/2)), yCenter-((int)(zoom*klingelRadius/2)), (int)(klingelRadius*zoom), (int)(klingelRadius*zoom)); } // Zeichne Uhrenhintergrung und Koordinatensystem g.setFont(g.getFont().deriveFont(fontSize)); g.setColor(Color.BLACK); // Farbe g.drawArc(xCenter - 5, yCenter - 5, 10, 10, 0, 360); g.drawLine(xCenter, yCenter, xCenter + 40, yCenter); g.drawLine(xCenter, yCenter, xCenter, yCenter + 40); g.drawString("X", (int)(xCenter + 45*zoom), yCenter + +charCenterOffSet); g.drawString("Y", xCenter - charCenterOffSet, (int)(yCenter + 55*zoom)); g.drawString("12",xCenter - charCenterOffSet, (int)(yCenter - maxRadius*zoom)); g.drawString("3", (int)(xCenter + maxRadius*zoom), yCenter + charCenterOffSet); g.drawString("6", xCenter - charCenterOffSet, (int)(yCenter + 2*charCenterOffSet+maxRadius*zoom)); g.drawString("9", (int)(xCenter - maxRadius*zoom - charCenterOffSet), yCenter + charCenterOffSet); // Zeichne aktuelle Zeit zum Debuggen g.drawString(timeString, 0, (int)(yCenter + maxRadius*zoom)); // Zeichne Weckzeit zum Debuggen g.drawString(klingelString, 0, (int)(yCenter + maxRadius*zoom + 25)); // Zeichne Stundenzeiger g.setColor(Color.BLACK); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.stundeX(stunde)*zoom), (int)(yCenter + Zeiger.stundeY(stunde)*zoom)); g.drawString("h[" + Zeiger.stundeX(stunde) + "," + Zeiger.stundeY(stunde) + "]", 0, (int)(yCenter + maxRadius*zoom - (3*fontSize))); // Zeichne Minutenzeiger g.setColor(Color.RED); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.minuteX(minute)*zoom), (int)(yCenter + Zeiger.minuteY(minute)*zoom)); g.drawString("m[" + Zeiger.minuteX(minute) + "," + Zeiger.minuteY(minute) + "]", 0, (int)(yCenter + maxRadius*zoom - (2*fontSize))); // Zeichne Sekundenzeiger g.setColor(Color.BLUE); g.drawLine(xCenter, yCenter, (int)(xCenter + Zeiger.sekundeX(sekunde)*zoom), (int)(yCenter + Zeiger.sekundeY(sekunde)*zoom-fontSize)); g.drawString("s[" + Zeiger.sekundeX(sekunde) + "," + Zeiger.sekundeY(sekunde) + "]", 0, (int)(yCenter + maxRadius*zoom - fontSize)); } }
- 13703 views
Primfaktorzerlegung
die Zahl 252 aus den Primfaktoren 3, 3, 4, 7.
4 ist keine Primzahl hier müsste 2x die 2 hin
- Log in to post comments
3.4 Lösungen
3.4 Lösungen3.4.1 Schleifenterminierung
int i = 1, j = 1; do { i = i + j; j++; } while (i < 200);
int i = 1, j = 20; while (i + j < i) { i = i + 2; j--; }
int i = 1, j = 20; while (i + j > i) { i = i + 2; j--; }
int i = 100, j = 27; while (i != j) { i = i / 2; j = j / 3; }
3.4.2 Ziffern einer Zahl
public class Main { public static void main(String[] args) { int firstArg = 0; int stellen = 0; int a; if (args.length > 0) { try { firstArg = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen"); a = firstArg; while (a !=0) { stellen++; a/=10; } } System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!"); } }
3.4.3 Quersumme
package s1.block3; public class Quersumme { public static void main(String[] args) { int firstArg = 0; int querSumme = 0; int a; if (args.length > 0) { try { firstArg = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Die Zahl " + firstArg + " wurde eingelesen"); a = firstArg; while (a !=0) { querSumme+= a%10; a/=10; } System.out.println("Die Zahl " + firstArg + " hat die Quersumme " + querSumme + "!"); } } }
3.4.4 Zahlenstatistik
package s1.block3; public class Zahlenstatistik { public static void main(String[] args) { int feld[]; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; float average = 0; if (args.length > 0) { feld = new int[args.length]; try { for (int i=0;i<args.length;i++) { feld[i] = Integer.parseInt(args[i]); if (feld[i] < min) {min=feld[i];} if (feld[i] > max) {max=feld[i];} average += feld[i]; } } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } average= average/(float)args.length; System.out.println("Anzahl eingelesene Werte: " + args.length); System.out.println("Kleinster Wert: " + min); System.out.println("Größter Wert: " + max); System.out.println("Durchschnitt: " + average); } } }
3.4.5 Primzahlzerlegung
package s1.block3; public class Primzahlzerlegung { public static void main(String[] args) { int firstArg = 0; int p=1; long time; time= System.nanoTime(); if (args.length > 0) { try { p = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Eingelesene Zahl: " + p); while (p>1) { for (int i=2; i<= p; i++) { while (p%i == 0) { System.out.println("Primfaktor: " + i); p /= i; } } } } time= System.nanoTime() - time; System.out.println("Zeit in m: "+ time); } }
Optimiert:
// Gute Kandidaten // 2^30-1 = 1073741823 // 2^31-1 = 2147483647 public class Main { public static void Primzahlzerlegung(String[] args) { int firstArg = 0; long p = 1; long time; time = System.nanoTime(); if (args.length > 0) { try { p = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } long i = 2; System.out.println("Eingelesene Zahl: " + p); while ((p > 1) && (i <= p)) { if (p % i == 0) { System.out.println("Primfaktor: " + i); p /= i; i = 2; } else { if (i > Math.sqrt(p)) { // Beende Suche wenn Wurzel von P erreicht i = p; } else { i++; } } } } time = System.nanoTime() - time; System.out.println("Zeit in m: " + time); } }
3.4.6 Codevereinfachung
a.)
// Annahme: j >= 0 i = j;
a = (a<b) ? b : a; b = (a<b) ? a : b; c = (a<b) ? a : c;
3.4.7 Wochentagberechnung
Hier wurde nur der julianische Kalender implementiert nicht der (aktuelle) gregorianische Kalender.
Hier wurden Felder verwendet, die noch nicht vorgestellt wurden.
package s1.block3; public class WochentagBerechnung { public static void main(String[] args) { int monatOffSet[] = new int[13]; int tag = 0; int monat = 0; int jahr=0; int wochentag0101startJahr= 6; int jahresOffSet; // Bedeutung siehe Feld tagText String[] tagText = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"}; int wochentag; int startJahr = 2010; int anzSchaltJahre; if (args.length > 1 ) { try { tag = Integer.parseInt(args[0]); monat = Integer.parseInt(args[1]); jahr = Integer.parseInt(args[2]); wochentag0101startJahr = Integer.parseInt(args[3]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } monatOffSet[1] = 0; monatOffSet[2] = (monatOffSet[1]+31)%7; monatOffSet[3] = (monatOffSet[2]+28)%7; monatOffSet[4] = (monatOffSet[3]+31)%7; monatOffSet[5] = (monatOffSet[4]+30)%7; monatOffSet[6] = (monatOffSet[5]+31)%7; monatOffSet[7] = (monatOffSet[6]+30)%7; monatOffSet[8] = (monatOffSet[7]+31)%7; monatOffSet[9] = (monatOffSet[8]+31)%7; monatOffSet[10] = (monatOffSet[9]+30)%7; monatOffSet[11] = (monatOffSet[10]+31)%7; monatOffSet[12] = (monatOffSet[11]+30)%7; jahresOffSet = (monatOffSet[12]+31)%7; if (monat>2) anzSchaltJahre=jahr/4-startJahr/4; else anzSchaltJahre=(jahr-1)/4-startJahr/4; wochentag = (monatOffSet[monat] + tag - 1 + wochentag0101startJahr + (jahr-startJahr)*jahresOffSet + anzSchaltJahre )%7; System.out.println ("Der 1.1."+ startJahr+" war ein " + tagText[wochentag0101startJahr]); System.out.println ("Der "+ tag + "."+monat+"."+jahr+ " ist ein " + tagText[wochentag]); // Optional: Datumsbestimmung mit Hilfe der Java Infrastruktur Calendar myCal = new GregorianCalendar(jahr,monat+1,tag); System.out.println ("Gregorianischer Java Kalender:"); System.out.println ("Der " + tag + "." + monat + "." + jahr+ " ist ein " + tagText[myCal.get(Calendar.DAY_OF_WEEK)]); } } }
3.4.8 Überlauf
Bei der Addition von Ganzzahlen gibt es keine Überlaufsprüfung.
package s1.block3; public class Ueberlauf { public static void main(String[] args) { int a = 0; int b = 0; int result; if (args.length > 1 ) { try { a = Integer.parseInt(args[0]); b = Integer.parseInt(args[1]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } System.out.println("Eingelesene Werte: " + a + ", " + b); result = a + b; if ((result < a) || (result < b)) System.out.println("Überlauf!"); else System.out.println(a + " + " + b + " = " + result); } }
3.4.9 Schnitt zweier Linien
Eingabereihenfolge der Parameter ist: a1, a2, b ,c , d1, d2
package s1.block3; public class GeradenSchnitt { public static void main(String[] args) { double a1 = 0.0; double a2 = 0.0; double b = 0.0; double c = 0.0; double d1 = 0.0; double d2 = 0.0; double tmp; if (args.length > 5) { try { a1 = Double.parseDouble(args[0]); a2 = Double.parseDouble(args[1]); b = Double.parseDouble(args[2]); c = Double.parseDouble(args[3]); d1 = Double.parseDouble(args[4]); d2 = Double.parseDouble(args[5]); } catch (NumberFormatException e) { System.err.println("Argument muss Fließkommazahl sein"); System.exit(1); } } System.out.println("Linie 1: (" + a1 + "," + b + ") bis (" + a2 + "," + b + ")"); System.out.println("Linie 2: (" + c + "," + d1 + ") bis (" + c + "," + d2 + ")"); // Schnittpunkt ist (c,b) // Sortieren von a1, a2 . if (a1 > a2) { tmp = a1; a1 = a2; a2 = tmp;} // Sortieren von d1, d2 . if (d1 > d2) { tmp = d1; d1 = d2; d2 = tmp; } System.out.println("Nach sortieren..."); System.out.println("Linie 1: (" + a1 + "," + b + ") bis (" + a2 + "," + b + ")"); System.out.println("Linie 2: (" + c + "," + d1 + ") bis (" + c + "," + d2 + ")"); if ((a1 <= c) && (c <= a2) && (d1 <= b) && (b <= a2)) System.out.println("Die beiden Strecken schneiden sich"); else System.out.println("Die beiden Strecken schneiden sich nicht"); } }
Beispielläufe
java s1.block3.GeradenSchnitt 3.1 8.2 4.0 5.2 11.1 4.9 Linie 1: (3.1,4.0) bis (8.2,4.0) Linie 2: (5.2,11.1) bis (5.2,4.9) Nach sortieren... Linie 1: (3.1,4.0) bis (8.2,4.0) Linie 2: (5.2,4.9) bis (5.2,11.1) Die beiden Strecken schneiden sich nicht java block3.GeradenSchnitt 3.1 8.2 6.0 5.2 11.1 4.9 Linie 1: (3.1,6.0) bis (8.2,6.0) Linie 2: (5.2,11.1) bis (5.2,4.9) Nach sortieren... Linie 1: (3.1,6.0) bis (8.2,6.0) Linie 2: (5.2,4.9) bis (5.2,11.1) Die beiden Strecken schneiden sich
3.4.10 Codevereinfachung
a)
a = a * b + 2 * c;
b)
x<0 | x>=0 | |
---|---|---|
y<0 | a=x*y | a=x*(-y) |
y>=0 | a = x*(-y) | a=(-x)*(-y) |
y = ((x<0) && (y<0)) ? y : -y; x = ((y>=0) && (x>=0))? -x : x; a = x * y;;
3.4.11 Dreiecksbestimmung
package s1.block3; public class Dreiecksvergleich { public static void main(String[] args) { float x[] = new float[3]; int j; if (args.length > 2 ) { try { float a; for (int i=0; i <3; i++) { x[i] = Float.parseFloat(args[i]); j=i; // Sortiere Eingabe in steigender Reihenfolge while (j>0) if (x[j] < x[j-1]) { a = x[j-1]; x[j-1] = x[j]; x[j]= a; j--; } else j=0; } } catch (NumberFormatException e) { System.err.println("Argument muss Fließkommazahl sein"); System.exit(1); } System.out.println("Eingebene Werte: " + x[0] + "; " + x[1] + "; " + x[2]); // Die folgenden Vergleiche verlassen sich darauf, dass das Feld // aufsteigend sortiert ist. if (x[0]+x[1]<=x[2]) System.out.println("Dreieck ist ungültig"); else if (x[0] == x[2]) System.out.println("Dreieck ist gleichseitig (und gleichschenkelig)"); else { if ((x[0] == x[1]) || (x[1] == x[2])) System.out.println("Dreieck ist gleichschenkelig (nicht gleichseitig)"); if (x[0]*x[0]+x[1]*x[1]==x[2]*x[2]) System.out.println("Dreieck ist rechtwinklig"); } } } }
3.4.12 Sortieren
package s1.block3; public class Sortieren { public static void main(String[] args) { int a = 0; int b = 0; int c = 0; if (args.length > 2 ) { try { a = Integer.parseInt(args[0]); b = Integer.parseInt(args[1]); c = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c); // alle Moeglichkeiten testen if ((a <= b) && (b<=c)) System.out.println("Sortierte Werte: " + a + ", " + b + ", " + c); else if ( (a <= c) && (c <= b) ) System.out.println("Sortierte Werte: " + a + ", " + c + ", " + b); else if ( (b <= a) && (a <= c) ) System.out.println("Sortierte Werte: " + b + ", " + a + ", " + c); else if ( (b <= c) && (c <= a) ) System.out.println("Sortierte Werte: " + b + ", " + c + ", " + a); else if ( (c <= a) && (a <= b) ) System.out.println("Sortierte Werte: " + c + ", " + a + ", " + b); else if ( (c <= b) && (b <= a) ) System.out.println("Sortierte Werte: " + c + ", " + b + ", " + a); } }
3.4.13 Plausibilitätsprüfung
package s1.block3; public class Plausibilitaetspruefung { public static void main(String[] args) { int monatTage[] = {0, 31, 28, 31, 30, 31, 30,
int tag = 0; int monat = 0; int jahr = 0; if (args.length > 2 ) { try { tag = Integer.parseInt(args[0]); monat = Integer.parseInt(args[1]); jahr = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } System.out.println("Eingabe ist: " + tag + "." + monat + "." + jahr); if ((monat>0) && (monat < 13) && (tag>0)) if (monatTage[monat] >= tag) System.out.println("Datum ist gültig"); else if ((monat == 2) && (tag == 29) && (jahr%4 == 0) && ((jahr%100 !=0) || (jahr%400==0)) ) System.out.println("Datum ist gültig (Schalttag)"); else System.out.println("Datum ist ungültig"); } } }
3.4.14 Textverschlüsselung
Die vorgestellte Lösung ist eine naive, aber in diesem Fall korrekte Lösung da hier der implementierte Wert eines Zeichen im Vordergrund steht und nicht die Bedeutung des Zeichens.
Sie basiert auf der naiven Annahme, dass die den Zeichen (Buchstaben) zugeordnete Zahlenwerte der Sortierreihenfolge der Buchstaben entspricht. Diese Annahme ist für lateinische Zeichensätze meißten, mehr oder weniger richtig. Im Allgemeinen sollte man bei Programmentwicklung an die Internationalisierung denken. Hier gilt:
- Die lexikografische Ordnung (Sortierreihenfolge) ist länderabhängig und hängt von der Landeseinstellung (locale, Codepage) der eingestellten Umgebung ab.
- Beispiel: Die Buchstaben ß, ö, Ö, ü etc haben in Unicode oder ISO 8852 Werte die nicht der deutschen lexikographischen Ordnung entsprechen.
- Die von Java verwendete Unicodecodierung erlaubt die Codierung der meisten Zeichen der Welt gleichzeitig. Die Sortierreihenfolge ist hier nicht garantiert.
- Beispiel: Es gibt Zeichen die in der japanischen sowie in der chinesichen Sprache verwendet werden. Diese Zeichen sind identisch, haben jedoch eine unterschiedliche Bedeutung. Sie werden daher in China und Japan unterschieldlich sortiert.
package s1.block3; public class Textverschluesselung { public static void main(String[] args) { String myText = ""; int offSet = 0; char c; if (args.length > 1 ) { offSet = Integer.parseInt(args[0]); myText = args[1]; } System.out.println("Eingabe: <<" + myText + ">>, Verschiebung: " + offSet); System.out.print("Ausgabe: <<"); offSet = offSet%26; // Bei 26 Buchstaben kann der Versatz nur module 26 sein for (int i=0;i < myText.length(); i++) { // Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feld c = myText.charAt(i); if ((c>='A') && (c<='Z')) { c = (char)(c + offSet); if (c > 'Z') c-=(char)26; //Ziehe vom Ergebnis 26 ab da es jenseits von "Z" liegt. System.out.print(c); } } System.out.println(">>"); } }
3.4.15 Verzweigung
package s1.block3; public class Verzweigung { public static void main(String[] args) { int x[] = new int[3]; if (args.length > 2 ) { try { for (int i=0; i<3; i++) x[i]= Integer.parseInt(args[i]); } catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } System.out.println("Eingabe: " + x[0] +", " + x[1] + ", "+ x[2]); if ((x[0]==x[1]) && (x[1]==x[2])) System.out.println("Alle Werte sind gleich."); if ((x[0]==x[1]) || (x[1]==x[2]) || (x[0]==x[2])) System.out.println("Mindestens zwei Werte sind gleich."); if ((x[0]!=x[1]) && (x[1]!=x[2]) && (x[0]!=x[2])) System.out.println("Alle Werte sind verschieden."); } }
3.4.16 Häufigkeit von Zeichen
package s1.block3; public class Haeufigkeitzeichen { public static void main(String[] args) { String myText=""; char c; int histogram[] = new int[Character.MAX_VALUE]; if (args.length > 0 ) { myText=args[0]; } System.out.println("Eingabe: <<" + myText + ">>"); for (int i=0;i < myText.length(); i++) { // Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feld c = myText.charAt(i); histogram[c]++; } for (int i=0; i < Character.MAX_VALUE; i++) if (histogram[i]!= 0) { // Wichtig: unterdrücke alle leeren Einträge. // Das Feld hat ~65000 Zellen! System.out.print((char)i + ": "); for (int j=0; j< histogram[i]; j++) System.out.print('*'); System.out.println(); } } }
3.4.17 Weckzeiten implementieren
package s1.block3; public class WeckzeitLoesung { int wzh = 0; // Weckzeit in Stunden int wzm = 0; // Weckzeit in Minuten int wzs = 0; // Weckzeit in Sekunden public void setzeWeckzeit(int hh, int mm, int ss) { if (korrekteWeckzeit(hh,mm,ss)) { wzh = hh; wzm = mm; wzs = ss; } } public boolean korrekteWeckzeit(int h, int m, int s) { boolean result; // benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen result = ((h>=0) && (h<=12 && (m>=0) && (m<=59)&& (s>=0) && (s<=59))); return result; } public boolean klingeln(int aktH, int aktM, int aktS) { boolean result; // benutzen die Variablen der aktuellen Zeit aktH (Stunde), // aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs // um zu bestimmern ob der Wecker klingeln soll // Bestimme aktuelle Zeit in Sekunden int aktZeit = aktH*3600 + aktM*60+aktS; // Bestimme Weckzeit in Sekunden int weckZeit = wzh *3600 + wzm *60+wzs; // Ist die aktuelle Zeit größer aber nicht größer als 10 Sekunden? result = (aktZeit-weckZeit>=0) && (aktZeit-weckZeit<10); return result; }
}
- 8580 views
3.5 Lernziele
3.5 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zur Ablaufsteuerung und die Frage aus der Übung 3.3.1 Schleifenterminierung zu beantworten.
Feedback
- 4708 views
4. Prozedurales, modulares Programmieren, Unterprogramme, Funktionen, Methoden
4. Prozedurales, modulares Programmieren, Unterprogramme, Funktionen, Methoden Stefan Schneider Sat, 07/24/2010 - 17:30- 7578 views
4.1 Methoden
4.1 MethodenMethoden
Methoden sind ein wichtiges Strukturierungs- und Modularisierungsmittel.
Die Grundidee einer Methode ist wiederkehrende Aufgaben an einer Stelle zu implementieren und damit Redundanz zu vermeiden.
Ein zweiter wichtiger Aspekt ist die Senkung der Komplexität. Dem Benutzer muss die Implementierung einer Methode nicht bekannt sein um sie zu benutzen.
Der in Java verwendete Begriff wird in anderen Programmiersprachen auch wie folgt bezeichnet:
- Prozedur
- Unterprogramm
- Funktion
In Java wurden alle diese Begriffe der Übersichtlichkeit wegen im Begriff der Methode zusammen gefasst. Methoden gehören immer zu Klassen. Man kann in Java keine alleinstehenden Funktionen, Prozeduren etc. implementieren
Implementierung einer Methode
Methoden bestehen aus
- einem Methodenkopf bestehend aus der Deklaration
- Namen
- Signatur: Aufrufargumente, Rückgabewert
- Methodenrumpf
- der Rumpf wird von geschweiften Klammern eingeschlossen
- der Rumpf enthält die Implementierung der Methode
Zum Aufruf der Methode ist nur die Kenntnis der Deklaration notwendig
Methodenkopf
Durch ihn wird die Methode deklariert. Das bedeutet, das hiermit eine Schnittstelle festgelegt wird die alle Informationen enthält die ein Benutzer der Methode kennen muss um sie zu benutzen.
public static int multiplizieren( int faktor1, int faktor2) { // Methodenrumpf } ^ ^ ^ ^ ^ 1. 2. 3. 4. 5.
1. Zugriffsspezifikation; die Sichtbarkeit der Methode
- public : Die Methode darf von überall aufgerufen werden
- private: Die Methode darf nur innerhalb der Klasse verwendet werden
- protected: Die Methode darf nur innerhalb der Klasse oder von abgeleiteten Klassen verwendet werden
2. Statische Methoden: Das Schlüsselwort static erlaubt den Aufruf der Methode auch wenn es für die Klasse keine Instanz gibt. Das Konzept von Klassen, Objekten und Instanzen wird erst später eingeführt.
3. Rückgabetyp: Der Typ des Ergebnis welche die Methode zurückliefert. Es gibt zwei Möglichkeiten:
- Die Methode liefert ein Ergebnis zurück. Der Typ ist ein beliebiger Typ
- Die Methode liefert kein Ergebnis zurück. Hierzu wird das Schlüsselwort void verwendet.
4. Methodenname: Hiermit werden Methoden in einer Klasse unterschieden.
5. Parameterliste: Sie enthält den Typ und den Namen aller übergebenen Werte innerhalb von runden Klammern.
- Die übergebenen Werte werden in der Methode als Variablen verwendet.
- Die übergebenen Parameter werden mit Komma getrennt.
- Sollen einer Methode keine Parameter mitgegeben werden, so wird ein leeres Klammernpaar ohne Werte verwendet. Bsp. ()
- Die übergebenen Parametervariablen sind immer Kopien der aufrufenden Variablen. Dies bedeutet, dass man in der Methode eine Parametervariable ändern kann, ohne dass die Variable im äusseren Block die den Wert übergeben hat geändert wird.
Methodenrumpf
In Methodenrümpfe kann man:
- Variablen und Konstanten deklarieren
- Anweisungen und Schleifen ausführen
- Methoden der gleichen oder anderer Klassen aufrufen
- die Übergabeparameter nutzen
In Methoden können die im Methodenkopf deklarierten Parameter direkt verwendet werden. Bei einem gegebenen Rückgabeparameter muss dieser mit dem Schlüsselwort return zurückgegeben werden. Mit final bezeichnete Parameter dürfen im Methodenrumpf nicht mehr verändert werden. Beispiel
public static String textverkettung (String text, final int anzahl) { String s=""; for (int i=0; i<anzahl; i++) { s = s + text; } return s; }
Die Rückgabe von Werten mit Hilfe des Schlüsselwort return funktioniert nicht nur mit Variablen, man kann auch Ausdrücke zurückgeben die den passenden Typ haben.
Wie zum Beispiel in der Methode textRahmen() die noch ein Textrahmen von jeweils 3 Zeichen um das Ergebnis legt:
public static String textRahmen (String text) { return ">>>" + text + "<<<"; }
Bei einem gegebenen Rückgabeparameter kann der Rumpf an verschiedenen Stellen verlassen werden.
Siehe Beispiel:
package s1.block4; public class Textverkettung { public static void main(String[] args) { System.out.println(textverkettung("Sonnenschein", 3)); System.out.println(textverkettung("Sonnenschein", 0)); } public static String textverkettung(String text, final int anzahl) { String s=""; if (anzahl < 1) { return "Gewählte Anzahl ist zu klein"; } else { for (int i = 0; i < anzahl; i++) { s = s + text; } return s; } } }
Der Typ des Rückgabeparameter muss vom Typ her zum deklarierten Rückgabetyp der Methode passen.
Die Methode kann auch ohne einen Rückgabewert definiert werden. Im folgenden Beispiel wird sie den Text direkt selbst ausdrucken:
public class TextverkettungDrucken { public static void main(String[] args) { textverkettungDrucken("Sonnenschein", 3); textverkettungDrucken("Sonnenschein", 0); } public static void textverkettungDrucken(String text, final int anzahl) { String s=""; if (anzahl >0) for (int i = 0; i < anzahl; i++) s = s + text; System.out.println("Ergebnis: " + s); } }
Aufruf von Methoden
Direkter Aufruf: Aufruf nur durch Methodenname und aktuelle Parameter
- Die aufgerufene Methode gehört zur gleichen Klasse oder einer Oberklasse (Fall 1 im Beispiel)
- Die Methode ist nicht statisch und wird auf das gleiche Objekt wie die umgebende Methode angewendet (Fall 1 im Beispiel)
- Die Methode ist statisch und kann ohne die Existenz eines Objekts verwendet werden (Fall 2 im Beispiel)
Aufruf mit vorgestelltem Objekt oder Klassenname (statische Methode)
- Die Methode soll für ein bestimmtes Objekt aufgerufen werden. Sie ist nicht statisch (Fall 3 im Beispiel)
- Die Methode ist statisch und gehört zu einer anderen Klasse. (Fall 4 im Beispiel)
Beispiel:
public class Flugzeug { String kennzeichen; int leerGewicht; int besatzung; public void drucken () { System.out.println(kennzeichen + " , Crew: " + besatzung + ", "+leerGewicht + "kg"); } public static void main(String[] args) { Flugzeug f = new Flugzeug(); f.drucken(); f.kennzeichenAusgeben(); // Fall 3: Aufruf einer Methode für ein Objekt eigenschaft(); // Fall 2: Aufruf einer statischen Methode der gleichen Klasse Flughafen.eigenschaft(); // Fall 4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehören kann double pi = Math.sqrt(2.0); // Fall 4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehört } public void kennzeichenAusgeben() { drucken(); // Fall 1: Nicht statischer Methodenaufruf einer Methode der gleichen Klasse } public static void eigenschaft() { System.out.println("kann fliegen"); } } public class Flughafen { public static void eigenschaft() { System.out.println("ist laut und schafft Arbeitsplätze"); } }
Anwenden von Methoden
Methoden mit Eingabeparametern können bei jedem Aufruf mit anderen Werten arbeiten. Wichtig ist hierbei zu wissen, dass es bei mehreren Parameter auf die Reihenfolge der Parameter ankommt. Sie müssen beim Aufruf, in ihrer Reihenfolge, zur Deklaration passen. Der Übersetzer wird Parameter mit nicht passenden Typen als Fehler anzeigen. Sind die Typen mehrerer Parameter gleich oder kompatibel, muss der Entwickler selbst auf die Reihenfolge achten!
Formalparameter von Methoden |
---|
Die in der Deklaration verwendeten Parameter einer Methode werden Formalparameter genannt |
Aktualparameter von Methoden |
---|
Die beim Aufruf einer Methoden verwendeten Parameter werden Aktualparameter (aktuelle Parameter) genannt. |
Die oben gezeigte Methode textverkettung kann bei jedem Aufruf einen anderen Text verketten. Man kann die Methode wie folgt aufrufen.
package s1.block4; public class Textverkettung { public static void main(String[] args) { String result; String text1 = "Mustertext"; result= textverkettung("PingPong", 2); System.out.println("Ergebnis: " + result); result= textverkettung(text1, 3); System.out.println("Ergebnis: " + result); } public static String textverkettung(String text, final int anzahl) { String s; s = text; if (anzahl < 1) { return "Gewählte Anzahl ist zu klein"; } else { for (int i = 1; i < anzahl; i++) { s = s + text; } return s; } } }
Das oben gezeigte Codestück sollte auf der Konsole das folgende Ergebnis ausdrucken:
Ergebnis: PingPongPingPong Ergebnis: MustertextMustertextMustertext
Der Aufruf von Methoden ohne Rückgabeparameter ist noch einfacher. Im folgenden Beispiel wird wie Methode textverketttungDrucken() aufgerufen:
... String result; String text1 = "Mustertext"; textverkettungDrucken("PingPong", 2); textverkettungDrucken(text1, 3); ...
Man kann die Methode ohne eine Ergebnisvariable mit einer vereinfachten Syntax aufrufen. Ruft man Methoden mit Ergebnisvariablen in der oben gezeigten Form, ohne Zuweisung des Ergebnis zu einer Variablen, auf, so geht das Ergebnis verloren. Die Implementierung wird jedoch übersetzt und abgearbeitet. Es tritt keine Fehlermeldung auf.
Verschachtelte Methodenaufrufe und Methodenaufrufe innerhalb von Methoden
Methodenaufrufe mit Ergebnissen können überall benutzt werden wo in einem Ausdruck oder einer Zuweisung der entsprechende Typ gelesen werden soll.
Im folgenden Beispiel wird die Methode textverkettung() verschachtelt mit der Methode zur Rahmenerzeugung textRahmen() aufgerufen. Der geschachtelte Aufruf erfolgt in der Methode testTextRahmen():
public static String textverkettung(String text, final int anzahl) { String s=""; if (anzahl < 1) { return "Gewählte Anzahl ist zu klein"; } else { for (int i = 0; i < anzahl; i++) { s = s + text; } return s; } } public static String textRahmen(String s) { return "<< " + s + " >>"; } public static void testTextRahmen() { String s = "Inhalt"; String result = textRahmen(textverkettung(s,2)); System.out.println(result); }
Ein Aufruf der Methode testTextRahmen() ergibt die Ausgabe:
<< InhaltInhalt >>
Methoden können nicht nur wie eben beschrieben geschachtelt werden. Man kann innerhalb eines Methodenrumpfes auch eine andere Methode aufrufen. Dies geschah oben in der Methode testTextRahmen.
Überladen von Methoden
Java erkennt eine Methode einer Klasse an den folgenden Merkmalen:
- Name der Methode
- Reihenfolge und Typ der Eingabeparameter
Man kann also den gleichen Namen einer Methode wiederverwenden solange sich die Typen der Eingabeparameter unterscheiden. Hiermit kann man Methoden implementieren, die "ähnliches" implementieren. Die "Ähnlichkeit" kann man mit dem gleichen Namen dokumentieren, die Parameterlisten müssen sich aber unterscheiden.
Überladene Methoden |
---|
Überladene Methoden einer Klasse sind Methoden die den gleichen Namen besitzen und Formalparameterlisten mit unterscheidlichen Typen bzw. unterschiedlicher Anzahl von Parametern besitzen |
Überladene Methoden sind nützlich wenn man den prinzipiell gleichen Algorithmus für unterschiedliche Datentypen oder unterschiedliche Parameterkombinationen ausführen will.
Wichtig: Der Rückgabeparameter einer Methode wird in Java nicht bei der Unterscheidung überladener Methoden beachtet!
Methoden mit Rückgabeparameter können auch ohne Zuweisung ihres Rückgabewertes an eine Variable aufgerufen werden.
In diesem Fall kann der Übersetzer nicht feststellen welche Methode benutzt werden soll, wenn es mehrere Methoden gibt die sich nur im Rückgabewert unterscheiden.
Ein typisches Beispiel ist das drucken von unterschiedlichen Werten. Anbei vier überladene Methoden einer Klasse;
public class drucker { public void drucken(String s) { System.out.println("String:"+ s); } public void drucken(int i) { System.out.println("Zahl: " + i); } public void drucken(String s, int i) { System.out.println("String/Zahl" + s + "/" + i); } public void drucken(int i, String s) { System.out.println("Anders: String/Zahl" + s + "/" + i); } }
Die folgenden zwei Methoden sind keine erlaubten (zusätzlichen) überladene Methoden:
public void drucken(String z, int a) { System.out.println("String/Zahl" + z + "/" + a); } public int drucken(String s, int i) { System.out.println("String/Zahl" + s + "/" + i); return 2*i; }
Diese erste Methode besitzt die gleichen Formalparametertypen wie die dritte Methode. Die Namen der Parameter sind nicht relevant.
Die zweite Methode hat zwar einen anderen Rückgabeparameter, jedoch die gleichen Formalparameter wie die dritte Methode. Java ignoriert die Rückgabeparameter und verweigert das Übersetzen der Methode.
Entwurfsprinzipien für Methoden
- Methoden sollten eine klar definierte Teilaufgabe lösen
- Wird eine Methode groß und unübersichtlich sollte man sie in sinnvolle Teilmethoden zerlegen
- Gute Methoden werden an verschiedenen Stellen wieder verwendet
- Methoden sollten möglichst gekapselt sein
- Die Übergabeparameterlisten sollten so kurz wie möglich sein
- Methoden sollten möglichst wenig globale Variablen verwenden. Dem Verwender ist das nicht unbedingt bewusst!
- Ergebnisse sollten wenn möglich als Rückgabewert ausgegeben werden. Das Ändern von globalen Variablen sollte man wenn möglich vermeiden.
- Methoden und ihre Funktionen und Seiteneffekte sollten immer gut dokumentiert sein. Ein Verhältnis von Kommentar zu Quellcode von 1:1 ist durchaus vernünftig!
Programmstrukturierung mit Hilfe von Methoden
Methoden sind einer der Bausteine die die Entwicklung komplexer Anwendungen erlauben. Sie geben Strukturierungsmöglichkeiten um die Gesamtkomplexität von Anwendungen zu reduzieren:
- Abstraktion: Verwender einer Methode müssen nicht wissen wie sie intern funktioniert. Sie können sich auf die Beschreibung und die korrekte Ausführung der Methode verlassen
- Codereduktion: Das wieder verwenden von Methoden erlaubt es mit deutlich weniger Programmcode auszukommen. Die Kosten für die Pflege der Quellen und die Fehlerhäufigkeit der Anwendung sinkt. Bei replizierten Codestücken müssten alle Codestücke gefunden und individuell angepasst werden.
- Strukturierung und Hierarchisierung: Das explizite Zusammenfassen von Codestücken die eine bestimmte Aufgabe lösen erleichtert Verständnis und Pflege der Anwendung. Methoden erlauben durch Aufruf von anderen Methoden die Strukturierung in vielschichtige Hierarchien.
- Teile und Herrsche: Das abstrahieren und Verbergen der Implementierung (Information hiding) erlaubt die Zusammenarbeit in Teams und die Entwicklung komplexer Anwendungen. Das Konzept der Schnittstelle ist ein "Vertrag" zwischen Anbieter und Konsument bei dem nur die semantische Wirkung der Methode verstanden sein muss.
Methoden sind nur das erste von mehreren Strukurierungshilfsmittel der Sprache Java die im Rahmen dieser Vorlesung vorgestellt werden. Klassen, Pakete, Vererbung und globale vs. lokale Variablen sind andere Möglichkeiten.
- 10579 views
Überschrift
nur eine Kleinigkeit, aber das "Überladen von Methoden", das über dem Text "Java erkennt eine Methode einer Klasse an den folgenden Merkmalen:" in Konsolenausgabe steht, sollte sicher eigentlich als neue Überschrift gedacht sein, oder?
- Log in to post comments
Danke, stimmt!
Vielen Dank,
der Fehler wurde gerichtet und der dazugehörende Abschnitt etwas umformuliert.
- Log in to post comments
Ein kleiner Verbesserungsvorschlag
Eine Methode leitet doch einen Programmblock ein. Bei der Erklärung des Methodenkopfes endet dieser jedoch mit einem Semikolon. Vielleicht kann man diesem noch mit einer geschweiften Klammer austauschen.
- Log in to post comments
4.2 Variablen, (Sichtbarkeit lokal, global)
4.2 Variablen, (Sichtbarkeit lokal, global)Variablen sind immer nur in einem gewissen Umfang sichtbar. Sichtbar bedeutet hier benutzbar, gültig.
Die Sichbarkeit von Variablen hängt vom Kontext, dem Block ab in dem sie deklariert worden sind.
Blöcke können sein:
- Programmcode zwischen geschweiften Klammern
- Programmcode innerhalb einer Schleife, Ausführungsektionen (z.Bsp. if Bedingung)
- Bereich einer Methode
- Bereich einer Klasse
- Global
Es gibt hier abhängig von der Sichtbarkeit verschiedene Bereiche
- Blocklokale Variablen
- Methodenlokale Variablen
- Klassenlokale Variablen
Die Gültigkeit von Variablen steht im Zusammenhang mit dem Stapel (Stackkonzept) des Laufzeitsystems.
Das Laufzeitsystem legt beim Beginn eines jeden Blocks die neuen Variablen hinter den Variablen des gerade aktuellen Blocks an.
Nach dem Verlassen eines Blocks wird der Zeiger auf das obere Ende des Stapels wieder auf die Variablen des letzen Blocks zurückgesetzt.
Der Speicher der Variablen des verlassenen Blocks ist hierdurch zur Wiederverwendung wieder freigegeben worden.
Das Verfahren eines Stapels entspricht der üblichen Handhabung von Tellern im Schrank.
- Neue Teller (hier Blöcke) werden immer oben an den Stapel angefügt
- Nicht mehr benötigte Teller werden nur von oben entfernt
Das Stackkonzept des Laufzeitssystems:
- 8409 views
public int guthaben
Hallo Hr. Schneider,
in der main-Methode steht public int guthaben = 200; im Stack steht guthaben mit einem Wert von 20.
Müsste der Wert im Stack nicht auch 200 haben?!
Viele Grüße
- Log in to post comments
4.3 Konstruktoren (1)
4.3 Konstruktoren (1)Konstruktoren sind spezielle Methoden mit denen Objekte einer Klasse initialisert werden. Das Java Laufzeitsystem legt automatisch einen Standardkonstruktor an wenn kein klassenspezifischer Konstruktor implementiert wird.
Konstruktoren haben die folgende Eigenschaften
- Der Konstruktor hat immer den Namen der Klasse als Methodenname
- Konstruktoren haben keine Rückgabewerte (auch kein Schlüsselwort void!)
- Die Auswahl des passenden Konstruktur erfolgt über die Typen der Eingabeparameter
- Konstruktoren können auch parameterlos sein
- Besitzt eine Klasse einen Konstruktor, so muss einer der Konstruktoren benutzt werden. Man kann die Klasse dann nicht mehr mit dem trivialen (System-)Konstruktor initialisieren.
Beispiel:
package s1.block4; public class Employee {
public String surName;
public String firstName;
public int employeeId;
public double salary;
/**
* Der Konstruktor initialisert die Attribute für Employee
* @param ln Nachname
* @param fn Vorname
* @param id Mitarbeiternummer
* @param sal Gehalt. Gehalt wird im Konstruktor auf 100000 begrenzt
*/
public Employee (String ln, String fn, int id, double sal) {
surName = ln;
firstName = fn;
employeeId = id;
if (sal > 100000) salary = 100000;
else salary= sal;
}
public void printRecord() {
System.out.print(employeeId + ", " + surName + " " + firstName);
System.out.println(",Salary :" + salary);
}
/**
* Teste die Konstruktoren
* @param args werden nicht benutzt
*/
public static void main(String[] args) {
Employee ceo = new Employee("Doe","John",1,80000.0);
Employee cio = new Employee("Doe","Jane",1,70000.0);
ceo.printRecord();
cio.printRecord();
}
}
- 7765 views
4.4 Iteration und Rekursion
4.4 Iteration und RekursionIn den vorangegangen Abschnitten wurden verschiedene Schleifenkonstrukte vorgestellt mit denen man Codestrecken wiederholt durch laufen kann. Dieses Verfahren nennt man Iteration.
Methoden können aber nicht nur andere Methoden aufrufen, sie können auch sich selbst aufrufen. Dieses Verfahren nennt man Rekursion. Beide Verfahren sind von der Theorie her gleichwertig. Sie können wechselseitig eingesetzt werden. Im folgenden Beispiel wird die Multiplikation durch fortgesetzte Addition nach dem folgenden Prinzip iterativ und rekursiv gelöst.
Rekursive Algorithmen wenden das "Teile und Herrsche" Prinzip an indem Sie ein gegebenes Problem zerlegen in
- ein trivial lösbares Problem
- ein Restproblem welches gleich dem ursprünglichen Problem strukturiert ist (und einfacher ist!)
Die rekursive Methode fib() basiert auf den folgenden Definition von Fibonaccifolgen fib(0) = 0 (ein trivial lösbares Problem) fib(1) = 1 (ein trivial lösbares Problem) für alle n > 1 fib(n) = fib(n-1) + fib(n-2) (das einfachere Restproblem) |
Fibonacciberechnung |
Beispielprogramm:
package s1.block4; public class Fibonacci { public static long fib(int f) { long ergebnis=0; switch (f) { case 0: { ergebnis = 0;break;} // Triviales Problem. Keine Rekursion case 1: { ergebnis = 1;break;} // Triviales Problem. Keine Rekursion default: { // Die Rekursion ergebnis = fib(f - 1) + fib(f - 2); break; } // Ende default } // Ende switch return ergebnis; } // Ende Methode public static void main(String[] args) { int a = 10; //Anzahl der berechneten Fibonaccizahlen System.out.println("Fibonacciberechnung von fib(0) bis fib(" + a + ")"); for (int i=0; i<=a; i++) System.out.println("fib("+i+")= " + fib(i)); } // Ende main() } // Ende Klasse
Anmerkung: Diese naive Implementierung ist sehr ineffizient. Das Programm berechnet zu jeder Fibonaccizahl die beiden vorhergehenden Zahlen neu. Der Aufwand zur Berechnung der Fibonaccizahlen steigt daher exponentiell mit der Potenz 2. Dies macht den hier gewähltenAlhorithmus, zur Berechnung für größerer Fibonaccizahlen, ungeeignet. |
Beispiel: Quersummenberechnung
Die Methode berechne() berechnet eine Quersumme iterativ mit Hilfe einer while Schleife. Die Methode berechneRekursiv() berechnet das Ergebnis rekursiv (mit Selbstaufruf).
package s1.block4; public class Quersumme { /** * Hauptprgramm zum Berechnen der Quersumme * @param args wird nicht benutzt */ public static void main (String[] args) { long eing = 12345678; long ausg = berechne(eing); System.out.println ("Ausgabe:" + ausg + "; Eing: " +eing); System.out.println("Berechne rekursiv"); ausg = berechneRekursiv(eing); System.out.println ("Ausgabe:" + ausg + "; Eing: " +eing); } /** * Iterative Berechnung einer Quersumme mit einer while Schleife * @param eing * @return ausgabe */ public static long berechne(long eing){ long a = 0; while (eing>0) { //Abbruch bei Null a += eing%10; //Aufaddieren der letzen Stelle eing /= 10; //Teilen der Zahl durch 10 } return a; } /** * Rekursive Berechnung einer Quersumme * @param eing * @return ausgabe */ public static long berechneRekursiv (long eing){ long a = 0; if (eing>0) a = (eing%10)+berechneRekursiv(eing/10); else a = 0; // Triviale Loesung. Nicht rekursiv. return a; } }
Beispiel: Die Türme von HanoiDie Türme von Hanoi sind ein einfaches Solitärspiel bei dem die folgenden Regeln gelten:
Siehe Wikipedia für weiterführende Erklärung und Animation. Die Strategie
|
Lösung für 3 Scheiben |
Implementierung der Türme von Hanoi in Java
package s1.block4; public class Hanoi { public static void bewegeScheiben(int scheiben, String von, String nach, String hilfsstab){ if (scheiben >0) { bewegeScheiben(scheiben-1, von, hilfsstab,nach); System.out.println(scheiben + ".te Scheibe von " + von + " nach " + nach ); bewegeScheiben(scheiben-1, hilfsstab, nach, von); } } public static void main(String[] args) { bewegeScheiben(3, "links", "mitte", "rechts"); } }
Ablauf des Programmes
Konsolenausgabe1.te Scheibe von links nach mitte 2.te Scheibe von links nach rechts 1.te Scheibe von mitte nach rechts 3.te Scheibe von links nach mitte 1.te Scheibe von rechts nach links 2.te Scheibe von rechts nach mitte 1.te Scheibe von links nach mitte |
Rekursive Aufrufe der Methoden
|
- 18347 views
4.5 Übungen
4.5 Übungen
4.5.1 Übung: ArithmetikmethodenImplementieren Sie eine einfache Arithmetik für den Ganzzahltyp int sowie wie den Fließkommatyp double. Implementieren Sie für beide Typen die folgenden 4 Methoden:
Benutzen Sie die unten aufgeführte Klasse Main mit dem gegebenen Hauptprogramm um einige Tests auszuführen. |
Was geschieht wenn man die Operationen von verschiedenen Typen mischt?
package s1.block4; public class Rechenarten { /* Implementierung */ public static void main(String[] args) { double ergebnis1; int ergebnis2; ergebnis1 = add(5.0, 4.0); System.out.println(" 5.0 + 4.0 = " + ergebnis1); ergebnis1 = div(9.0, 4.0); System.out.println(" 9.0 / 4.0 = " + ergebnis1); ergebnis1 = sub(9.0, 4.0); System.out.println(" 9.0 - 4.0 = " + ergebnis1); ergebnis1 = add(div(9.0, 4.0), 3.0); System.out.println(" 9.0 / 4.0 + 3.0 = " + ergebnis1); ergebnis2 = add(5, 4); System.out.println(" 5 + 4 = " + add(5, 4)); ergebnis2 = div(9, 4); System.out.println(" 9 / 4 = " + div(9, 4)); ergebnis2 = sub(9, 4); System.out.println(" 9 - 4 = " + sub(9, 4)); ergebnis2 = add(div(9, 4), 3); System.out.println(" 9 / 4 + 3 = " + add(div(9, 4), 3)); } }
4.5.2 Übung: Flächenberechnung von Dreiecken
Implementieren Sie eine Klasse mit Namen Dreieck. Die Klasse soll Methoden zu Flächenberechnung enthalten. Implementieren sie einzelne Methoden zur Berechnung der folgenden Dreieckstypen. Passen Sie die Anzahl der Parameter an die Gegebenheiten an
- beliebiges Dreieck: 3 Seiten gegeben
- gleichschenkliges Dreieck; 2 Seiten gegeben
- gleichseitiges Dreieck: 1 Seite gegeben
- rechtwinkliges Dreieck:
- 2 Katheten gegeben
- 1 Hypotenuse, 1 Kathete gegeben
Nutzen Sie wenn möglich schon implementierte Methoden zur Berechnung der Fläche. Dies bedeutet, dass man ein gleichschenkliges Dreieck für das man nur zwei Eingabeparameter benötigt mit Hilfe der Methode zur Berechnung eines allgemeinen Dreiecks berechnen kann. Das gleichschenklige Dreieck ist ein Spezialfall des allgemeinen Dreiecks.
Tipp:
- Nutzen Sie den Satz von Heron
- Benutzen Sie die Systemklasse Math für das Ziehen der benötigten Wurzel. Dies geschieht mit Math.sqrt().
- Hinweis. Durch das vorranstellen von Math. benötigen Sie keine Importdeklaration für das Math-Paket.
4.5.3 Übung: Rekursive Addition und Multiplikation
1. Übersetzen Sie das gegebene Beispielprogramm und testen Sie die Funktionsweise
- Vereinfachung: Die Anwendung muss nur für nicht negative Zahlen funktionieren
package s1.block4; public class Arithmetik {
/**
* Liest von der Kommazeile zwei Ganzzahlen ein und multipliziert sie
* @param args
*/
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
// Kontrolle der Eingaben
System.out.println("Eingabe a: " + a +"; b: " +b);
c = a *b;
// Ergebnisprüfung
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
}
2. Benutzen einer for-Schleife mit Addition
Ersetzen Sie die Anweisung:
c = a*b;
durch eine for Schleife in der nur noch addiert wird
3. Einführen einer Methode mult()
Ersetzen Sie die Berechnung des Ergebnisses mit einer for-Schleife durch einen Methodenaufruf:
c = mult(a,b);
Implementieren Sie eine dazu passende Methode mult() die die Multplikation mit der for-Schleife durchführt
4. Eine rekursive Methode mult()
Ersetzen Sie die for-Schleife in der Methode mult() durch einen rekursiven Aufruf.
Tipps:
- Sie benötigen eine Bedingung für das Rekursionsende
- Definition des zu selbst zu lösenden Teilproblems
- Delegation des Restproblems durch rekursiven Aufrufs
5. Ersetzen der Addition durch eine rekursive Additionsmethode add() die nur die Inkrementfunktion benutzt
In gesamten Anwendung sollte jetzt kein * oder + Operator mehr vorkommen...
4.5.4 Fragen
Typische klausurrelevante Fragen:
- Aus welchen 2 Teilen besteht eine Methode?
- Aus welchen Teilen besteht ein Methodenkopf?
- Welches Schlüsselwort wird verwendet wenn kein Rückgabewert existiert?
- Wie sieht die Parameterliste des Methodenkopfes aus falls es keine Eingabeparameter gibt?
- Nenne ein Beispiel für eine überladene Methode.
4.5.5 Beispiel aus der Vorlesung
Diese Klassen liegen im Paket s1.airlineSolution.block4.
Klasse Flugzeug
package s1.airlineSolution.block4;
/**
*
* @author stsch
*/
public class Flugzeug {
final static double durchschnittsgewicht = 75;
String kennzeichen;
int passagiere;
private double maximalesGewicht;
double minimalGewicht;
Flugzeug(double minGewicht, double maxGewicht) {
System.out.println("Hallo, ich baue ein Flugzeug");
maximalesGewicht = maxGewicht;
// Kontrolle des Minimalgewichts
if ((minGewicht > 0) && (minGewicht <= maximalesGewicht)) {
minimalGewicht = minGewicht;
} else {
minimalGewicht = 5;
}
// Eine schwachsinnige Initialisierung
passagiere = 1;
}
public double maxG() {
return maximalesGewicht;
}
public void einsteigen() {
passagiere++;
}
public void einsteigen(int einsteiger) {
passagiere = passagiere + einsteiger;
}
double gewicht() {
double ergebnis;
ergebnis = minGewicht + passagiere * durchschnittsgewicht;
return ergebnis;
}
}
Klasse Flughafen
package s1.airlineSolution.block4;
/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
public static void main(String[] args) {
Flughafen pb;
pb = new Flughafen();
pb.name = "Paderborn";
Flugzeug lh1 = new Flugzeug(222.0D,100000D);
lh1.kennzeichen = "D-A123";
lh1.passagiere = 23;
Flugzeug lh2 = new Flugzeug(3333.0D,100000D);
lh2.kennzeichen = "D-A456";
lh2.passagiere = 11;
pb.gate1 = lh1;
lh1.einsteigen();
lh1.einsteigen();
double meinGewicht = lh1.gewicht();
lh1.gewicht();
pb.gate2 = lh2;
lh2.einsteigen(88);
System.out.println("Mein Flughafen: " + pb.name);
System.out.println("Gate 1: " + pb.gate1.kennzeichen +
", Passagiere: " + pb.gate1.passagiere +
", akt. Gew.: " + pb.gate1.gewicht());
System.out.println("Gate 2: " + pb.gate2.kennzeichen +
", Passagiere: " + pb.gate2.passagiere);
if (pb.gate3 == null) {
System.out.println("Gate 3: leer");
} else {
System.out.println("Gate 3: " + pb.gate3.kennzeichen);
}
}
}
- 10779 views
Aufgabe 4.5.2 Rechtschreibfehler
In Aufgabe 4.5.2 müsste es "Kathete" und nicht "Kathede" heißen
- Log in to post comments
4.6 Lösungen
4.6 Lösungen4.6.1 Arithmetikmethoden
public class Rechenarten { public static double add(double a, double b) { return (a + b); } public static double sub(double a, double b) { return (a - b); } public static double mul(double a, double b) { return (a * b); } public static double div(double a, double b) { return (a / b); } public static int add(int a, int b) { return (a + b); } public static int sub(int a, int b) { return (a - b); } public static int mul(int a, int b) { return (a * b); } public static int div(int a, int b) { return (a / b); } public static void main(String[] args) {
double ergebnis1;
int ergebnis2; ergebnis1 = add(5.0, 4.0); System.out.println(" 5.0 + 4.0 = " + ergebnis1); ergebnis1 = div(9.0, 4.0); System.out.println(" 9.0 / 4.0 = " + ergebnis1); ergebnis1 = sub(9.0, 4.0); System.out.println(" 9.0 - 4.0 = " + ergebnis1); ergebnis1 = add(div(9.0, 4.0), 3.0); System.out.println(" 9.0 / 4.0 + 3.0 = " + ergebnis1); ergebnis2 = add(5, 4); System.out.println(" 5 + 4 = " + add(5, 4)); ergebnis2 = div(9, 4); System.out.println(" 9 / 4 = " + div(9, 4)); ergebnis2 = sub(9, 4); System.out.println(" 9 - 4 = " + sub(9, 4)); ergebnis2 = add(div(9, 4), 3); System.out.println(" 9 / 4 + 3 = " + add(div(9, 4), 3)); } }
4.6.2 Flächenberechnung vom Dreieck
public class Dreiecksflaeche {
/**
* Berechnung der Fläche eines Deiecks mit drei beliebigen Seiten.
* Es wird nicht geprüft ob die Seitenlängen ein korrektes Dreieck ergeben
* @param a Länge Seite 1
* @param b Länge Seite 2
* @param c Länge Seite 3
* @return Fläche des Dreiecks
*/
public static double flaeche(double a, double b, double c) {
double s = (a+b+c)/2;
return Math.sqrt(s*(s-a)*(s-b)*(s-c));
}
/**
* Berechnung der Fläche eines gleichschenkligen Deiecks.
* Es wird nicht geprüft ob die Seitenlängen ein korrektes Dreieck ergeben
* @param gleicherSchenkel die Länge der beiden gleichlangen Seiten
* @param basis Länge der Basis
* @return
*/
public static double flaeche(double gleicherSchenkel, double basis) {
return flaeche(gleicherSchenkel,gleicherSchenkel,basis);
}
/**
* Berechnung der Fläche eines gleichschenkligen Deiecks
* @param gleicheSeite Länge der Seite
* @return
*/
public static double flaeche(double gleicheSeite) {
return flaeche(gleicheSeite,gleicheSeite);
}
/**
* Berechnung der Fläche eines rechtwinkligen Dreiecks mit zwei Katheden
* @param k1 Länge erste Kathede
* @param k2 Länge zweite Kathede
* @return Fläche
*/
public static double flaecheKathedenDreieckeck(double k1, double k2){
return flaeche(Math.sqrt(k1*k1+k2*k2),k1,k2);
}
/**
* Berechnung der Fläche eines rechtwinkligen Dreiecks mit einer
* Hypotenuse und einer Kathede
* @param h Länge Hypothenuse
* @param k Länge Kathede
* @return Fläche
*/
public static double flaecheKathedeHypothenuseDreieck(double h, double k){
return flaecheKathedenDreieckeck(k,Math.sqrt(h*h-k*k));
}
/**
* Hauptprogrsam zum Testen
* @param args keine Eingabeparameter
*/
public static void main(String[] args) {
double aa = 3.0d;
System.out.println(flaeche(aa,4D,5D));
System.out.println(flaeche(5D,4D));
System.out.println(flaeche(3D));
System.out.println(flaecheKathedenDreieckeck(3D,4D));
System.out.println(flaecheKathedeHypothenuseDreieck(5D,4D));
}}
4.6.3 Rekursive Addition und Multiplikation
Multiplikation mit Hilfe einer addierenden for-Schleife:
public class Arithmetik1 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
for (int i=0; i<b; i++) {
c += a;
}
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
}
Delegation an eine Methode
public class Arithmetik2 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
public static int mult(int x, int y) {
int ergebnis=0;
for (int i=0; i<x; i++) {
ergebnis += y;
}
return ergebnis;
}
}
Eine rekursive Multiplikation
public class Arithmetik3 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
/**
* Diese Methode multipliziert zwei Zahlen rekursiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int mult(int x, int y) {
int ergebnis=0;
if (x==0)
ergebnis=0;
else
ergebnis=mult(y,(x-1))+y;
return ergebnis;
}
}
Multiplikation mit rekursiver Addition und Multiplikation
public class Arithmetik4 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
/**
* Diese Methode multipliziert zwei Zahlen rekursiiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int mult(int x, int y) {
int ergebnis=0;
if (x==0)
ergebnis=0;
else
ergebnis=add(mult(y,(x-1)),y);
return ergebnis;
}
/**
* Diese Methode addiert zwei Zahlen rekursiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int add(int x, int y) {
int ergebnis=0;
if (y==0)
ergebnis=x;
else {
ergebnis=add(x,(y-1));
ergebnis++;
}
return ergebnis;
}
}
- 6226 views
4.7 Lernziele
4.7 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu Methoden zu beantworten und die Übung 4.5.4 (Fragen) zu lösen.
Feedback
- 5233 views
5. Konzepte objektorientierter Programmierung
5. Konzepte objektorientierter ProgrammierungDie objektorientierte Programmierung hilft große Softwareentwicklungsprojekte überschaubarer und handlicher zu machen.
Die Grundidee besteht darin die zu verarbeitenden Daten und die Algorithmen (Methoden) mit denen die Daten verarbeitet werden zu handlichen, wartbaren Einheiten zusammenzufassen.
Objektorientierte Programmierung fasst man mit den folgenden Konzepten zusammen:
- Klassenbildung
- Datenkapselung (Information Hiding)
- Polymorphimus
- Vererbung
Ein Beispiel: Dateien im Betriebsystem
Graphische Oberflächen in Betriebssystemen implementieren eine Form von Objektorientierung.
Die Dateien kann man als Instanzen/Objekte einer Klasse sehen
Die Dateien haben gemeinsame Eigenschaften:
- Sie brauchen Platz auf der Festplatte
- Sie enthalten Daten und haben eine bestimmte Größe
- Sie haben einen Namen
Das Betriebsystem betreibt Datenkapselung auf den Dateien
- Sie können eine Datei mit einem Doppelklick öffnen. Es interessiert den Benutzer normalerweise nicht was da im Hintergrund passiert...
- Sie können Dateien bewegen, kopieren, löschen. Das funktioniert ohne das Sie wissen wie groß eine Datei ist und was sie enthält...
Das Betriebssystem wendet Vererbung und Polymorphismus an
- Hierzu benutzt es die Endung der Datei (z.Bsp. .txt .doc) bzw. den Mime-Type.
- Ein Doppelklick startet die richtige Anwendung. Auch wenn die Datei ein Anhang einer Email ist!
- Bei Operationen auf einer Datei (Doppelklick, schieben der Datei auf Druckersymbol) wird dynamisch die richtige Operation ausgewählt.
- 7916 views
5.1 Einführung Objektorientierung
5.1 Einführung Objektorientierung"Information Hiding" und Datenkapselung im abstrakten Datentyp
Ein Bestandteil der Objektorientierung ist das "information Hiding" welches schon von den abstrakten Datentypen her bekannt ist. Der Zustand des Objekts wird durch seine Attribute bestimmt. Die Attribute sollen aber nicht beliebig geändert werden können. Die Methoden agieren als Wächter für die Zustandsübergänge und "bewachen" so zu sagen die Attribute des Objekts. Dies hat zwei wesentliche Vorteile
- Der Entwickler kann denn Zustand seines Objekts bzw. Datentyps immer genau kontrollieren
- Der Entwickler kann die interne Implementierung des Objekts an neue Anforderungen anpassen ohne, dass er dies mit den Benutzern des Objekts kommunizieren muss. Die Methoden bilden hierdurch eine Schnittstelle zwischen der Implementierung und der externen Sicht des Objekts
Methoden erfüllen in diesem Kontext mehrere Aufgaben:
- Sie lesen die internen, geschützten Datenstrukturen aus
- Sie ändern die internen Datenstrukturen
- Sie können komplexe Berechnungen durchführen
- Sie können wiederum andere Objekte manipulieren (von denen der Benutzer nichts weiß)
Information Hiding: Ein Teilsystem darf nichts von der Implementierung eines anderen Teilsystems wissen
Klasse
Nach Wikipedia:
Unter einer Klasse versteht man in der objektorientierten Programmierung ein abstraktes Modell bzw. einen „Bauplan“ für eine Reihe von ähnlichen Objekten.
Die Klasse dient als Bauplan für Abbildung von realen „Objekten“ in Softwareobjekten und enthält Attribute (Eigenschaften) und Methoden (Verhaltensweisen) der Objekte. Verallgemeinernd könnte man auch sagen, dass eine Klasse dem Datentyp eines Objekts entspricht.
Klassen
- sind eine Menge von gleichartigen, individuellen Objekten
- sind ein schematische Modell
- beschreiben
- Eigenschaften (die Attribute einer Klasse)
- Verhalten (Methoden)
Objekt
Nach Wikipedia:
Ein Objekt bezeichnet in der objektorientierten Programmierung (OOP) ein Exemplar eines bestimmten Datentyps oder einer bestimmten Klasse (auch „Objekttyp“ genannt). In diesem Zusammenhang werden Objekte auch als „Instanzen einer Klasse“ bezeichnet. Objekte sind also konkrete Ausprägungen („Instanzen“) eines Objekttyps.
- 7039 views
5.1.1 Datenkapselung
5.1.1 DatenkapselungDas erste Konzept, die Datenkapselung, kann man als eine technische Weiterentwicklung der abstrakten Datentypen sehen.
- Primitive Datentypen (z.Bsp. int x = 10;)
- Strukturierte Datentypen wie Felder oder struct in der Programmiersprache C erlauben mehrere Typen in einer Struktur zusammenzufassen.
- Klassen, die es erlauben mehrere Datentypen (diese werden Attribute genannt) mit den dazugehörigen Methoden zusammenzufassen und zu kapseln.
- Beispiel: Eine Linie, die aus zwei Punkten besteht und den Zugriff auf die Punkte nur nach bestimmten Vorschriften in Methoden kapselt.
Datenkapselung ist das Verbergen von Attributen und Methoden durch Einschränkung des Zugriffs von Ausserhalb der Klasse.
Durch dieses Prinzip ergeben sich beim Entwurf und bei der Wartung von Anwendungen eine Reihe von Vorteilen:
- Divide et impera (Teile und herrsche): Benutzer und Implementierer einer Klasse können unabhängig von einander entwickeln. Der Entwickler muss nur darauf achten die externen Schnittstellen (öffentliche Methoden und Attribute) stabil zu halten.
- Integrität: Unbeabsichtigte Zustandsänderungen werden unterbunden. Beim Setzen eines Attributs müssen eventuell noch andere Operationen durchgeführt werden.
Datenkapselung ist die Voraussetzung zur Implementierung von Schnittstellen:
Schnittstelle |
---|
Die Gesamtheit der öffentlichen Strukturen einer Datenstruktur(Klasse) mit der Konsumenten(Verwender) einer Datenstruktur interagieren können. Schnittstellen stellen die Funktionalität einer Datenstruktur(Klasse) nach außen zur Verfügung. |
Entwickler sollten beim Anwendungsentwurf eine:
- maximale Datenkapselung und
- minimale Schnittstelle
einplanen. Klassen sollten möglichst autark sein und auf möglichst wenig andere Schnittstellen und Klassen zugreifen.
Sichtbarkeitssteuerung in Java mit Hilfe der Schlüsselwörter public und private
Java erlaubt den Zugriff auf Methoden und Attribute mit Hilfe der Schlüsselwörter private und public vor dem Namen des Attributs oder Methode zu steuern:
- public: Methoden und Attribute einer Klasse können
- von Methoden der Klasse selbst genutzt werden
- von Methoden andere Klassen genutzt werden (externe Benutzung)
- private: Methoden und Attribute einer Klasse können
- von Methoden der Klasse selbst genutzt werden
- nicht von Methoden ausserhalb der Klasse genutzt werden
Diese Zugriffssteuerung erlaubt die Implementierung der folgenden Empfehlung
|
Attribute und Methoden ohne ein Schlüsselwort werden in Java als public Attribute und Methoden behandelt um den Zugriff im gleichen Paket zu erlauben.
Java verfügt auch über das Schlüsselwort protected welches den Zugriff nur innerhalb eines Pakets erlaubt. Pakete werden erst in der weiterführenden Vorlesung eingeführt werden.
Zugriffssteuerung mit Get- und Set- Methoden
Es ist eine Konvention (und guter Programmierstil) in Java den Zugriff auf private Attribute mit Methoden zu gewähren denen man vor dem Attributnamen get- bzw. set- voranstellt:
class Person() { private String name; public void setName(String n) { name = n;} public String getName() { return name;} } |
Dieser Programmierstil bietet eine Reihe von Vorteilen:
- zukünftige Wartungsaufwände und Erweiterungen können leichter implementiert werden, da externe Benutzer ihre Implementierung nicht ändern müssen
- Die interne Implementierung kann vollständig geändert werden solange die Signatur der öffentlichen Methoden unverändert bleibt.
- get-, set- Methoden erlauben das Implementieren weitergehender Konsistenzprüfungen.
Anmerkung: Laufzeiteinbußen durch solche Trivialmethoden sind in der Regel nicht zu befürchten. Der Java "Just in Time" (JIT) Übersetzer des Laufzeitsystems wird mit hoher Wahrscheinlichkeit solche Methoden zur Laufzeit durch Methoden-inlining wegoptimieren.
- 9925 views
5.1.2 Architekturprinzipien
5.1.2 ArchitekturprinzipienTrennung der Zuständigkeiten
Trennung von Zuständigkeiten(Separation of Concerns) ist ein Konzept welches auf Datenkapselung aufbaut.
Die Idee besteht darin Zuständigkeiten Klassen zu zuordnen.
Alle Aufgaben die in einen Bereich fallen sollen möglichst von genau einer Klasse implementiert werden. Ziel ist es, dass unabhängige Dinge auch in der Implementierung unabhängig voneinander bleiben. Hierdurch
- sinkt die Gesamtkomplexität und Systeme sind einfacher zu verstehen
- unterschiedliche Komponenten können unabhängig von einander gepflegt werden
- Fehler in einem Bereich sollen sich möglichst nicht in einem anderen Bereich bemerkbar machen
Ziel ist es Klassen so zu modellieren, dass sie möglichst in sich abgeschlossen sind.
Ein Beispiel hierfür:
- Die Webarchitektur
- html (Hypertext Markup Language) ist die Seitenbeschreibungssprache
- css (Cascading Style Sheets) ist die Sprache zur Beschreibung des Layouts der Seite (getrennt vom Inhalt)
- http ist das Protokoll zur Übertragung der html Seiten (getrennt vom Inhalt)
- html (Hypertext Markup Language) ist die Seitenbeschreibungssprache
Entwurfsmuster "Model View Controller" (MVC)
MVC ist ein Entwurfmuster welches aus den folgenden drei Einheiten besteht:
|
Die Einteilung einer Anwendung in die folgenden drei Bereich ist oft vorteilhaft da häufig
- die Benutzerschnittstellen angepasst oder ausgetauscht werden müssen. Das Datenmodell ist dann nicht betroffen. Die Benutzerschnittstelle sollte unabhängig von den anderen Komponenten sein um unterschiedliche Technologien (Web, rich client, OS spezifische Bibliotheken) nutzen zu können
- das Datenmodell auf andere Datenbankprodukte angepasst werden muss ohne das andere Komponenten davon betroffen sein sollen
- die Ablauflogik angepasst werden muss und die Änderungen in den Benutzerschnittstellen minimal gehalten werden sollen
Wichtig ist zu verstehen, dass die drei Komponenten zusammenarbeiten und idealerweise unabhängig voneinander sind. Das Model sollte zum Beispiel auf Aufrufe von View und Controller reagieren, jedoch nicht selbst diese Komponenten aufrufen.
Schichtenarchitektur
Schichtenarchitekuren sind eine andere Ausprägug der "Separation of Concerns".
Java selbst und Javaanwendungen basieren auf dem Schichtenmodell.
Eine Javaanwendung soll nur auf die Dienste der Java Runtime zurückgreifen. Die Java Runtime bietet hierfür eine reichhaltige Infrastruktur für GUI Programmierung, Datenbankzugriff, Netzwerkkommunikation, Dateizugriff etc. Beschränkt man sich auf die von der Java Runtime angebotenen Dienste wird man unabhängig vom Betriebssystem und der darunter liegenden Hardware. Ziel bei der Entwicklung einer Anwendung sollte es sein unterschiedliche Schichten zu identifizieren und von Klassen nur auf die eigene Schicht oder die darunter liegende Schicht zu beschränken. |
- 7200 views
5.1.3 Entwurfsmuster (Design Patterns)
5.1.3 Entwurfsmuster (Design Patterns)
Nach Wikipedia:
|
Referenzen
Im zweiten Semester werden die Entwurfsmuster
benutzt.
In der Vorlesung des ersten Semesters werden einige wenige, ausgewählte Entwurfsmuster vorgestellt:
- 5918 views
Factory (Fabrikmethode)
Factory (Fabrikmethode)Es gibt Situation, in denen möchte man die Erzeugung neuer Objekte nicht im Konstruktor einer Klasse durchführen weil man z.Bsp.
- Zusätzliche Verwaltungsaufgaben durchführen möchte (z.Bsp. Registrierung des Objekts)
- Nicht mehr benötigte Objekte wieder verwenden möchte ( z.Bsp. Datenbankverbindungen)
- Die Wahl haben möchte Unterschiedliche Spezialisierungen einer Klasse oder die Implementierung einer Schnittstelle nutzen möchte.
Dieser Anwendungsfall wird mit dem Entwurfsmuster "Factory" (Fabrikmethode) beschrieben.
VerwendungEine Fabrikmethode
Eine Fabrikmethode (Factory) besteht typischer Weise aus
KategorieEine Fabrikmethode (Factory) gehört zur Kategorie der Erzeugungsmuster (Creational Pattern). |
UML Diagramm
|
Naive Javaimplementierung
/** * Einfache Implementierung der Fabrikmethode (Factory) */ public class Factory { /** * privater Konstruktor der nur innerhalb der Klasse * aufgerufen werden kann */ private Factory() { // Individuelle Initialisierung erfolgt hier } /** * Erzeugen der Objekte. */ public static Factory getInstance() { // Vorarbeiten instanz = new Factory(); // Nacharbeiten return instanz; }}
- 2059 views
Singleton (Einzelstück)
Singleton (Einzelstück)Es gibt Anwendungsfälle in denen es gewünscht ist genau ein Objekt global zur Verfügung zu stellen. Dieser Anwendungsfall wird mit dem Entwurfsmuster "Singleton" (Einzelstück) beschrieben. Beispiele für solche Anwendungsfälle sind:
- Implementierung eines seriellen logging Mechanismus
- Implementierung eines Puffers für einen Drucker
VerwendungEin Einzelstück
Ein Singleton (Einzelstück) implementiert eine ähnliche Struktur wie eine globale Variable. KategorieDas Einzelstück (Singleton) gehört zur Kategorie der Erzeugungsmuster (Creational Pattern). |
UML Diagramm
|
Naive Javaimplementierung (Lazy initialization)
/** * Einfache Implementierung des Einzelstueck (Singleton */ public class Einzelstueck { private static final Einzelstueck instanz; /** * privater Konstruktor der nur innerhalb der Klasse * aufgerufen werden kann */ private Einzelstueck() { // Individuelle Initialisierung erfolgt hier } /** * Erzeugen des einzigen Objekts falls noch keines existiert. * Rückgabe des Objekts falls es schon existiert * Diese Methode ist statisch. Sie kann auch ohne die Existenz einer Instanz aufgerufen werden. * Die Methode ist die einzige öffentliche Methode */ public static Einzelstueck getInstance() { if (instanz == null) { instanz = new Einzelstueck(); } return instanz; }}
Das gezeigte Beispiel verwendet eine "Lazy initialization". Das Objekt wird erst erzeugt wenn es auch wirklich benötigt wird. Hierdurch kann das unnötige Allokieren von Ressourcen vermieden werden.
Der Nachteil dieser Implementierung besteht darin, dass sie nicht threadsicher ist. In einem Javaprogramm mit mehreren Threads (Ausführungseinheiten) können zwei Threads gleichzeitig ein Objekt erzeugen und damit das gewünschte Ziel des Einzelstücks durchkreuzen.
Threadsichere Javaimplementierung
/** * Threadsichere Implementierung des Entwurfsmuster Einzelstueck (Singleton) */ public class Einzelstueck { private static Einzelstueck instanz = new Einzelstueck(); /** * privater Konstruktor der nur innerhalb der Klasse * aufgerufen werden kann */ private Einzelstueck() { // Individuelle Initialisierung erfolgt hier } /** * Diese Methode ist statisch. Sie kann auch ohne die Existenz einer Instanz aufgerufen werden. * Die Methode ist die einzige öffentliche Methode */ public static Einzelstueck getInstance() { return instanz; } }
Die hier gezeigte Implementierung ist threadsicher da die Instanz schon beim Laden der Klasse erzeugt wird.
- 7289 views
5.2 Objektorientierung in Java
5.2 Objektorientierung in Java- 6019 views
5.2.1 Javaklassen und -objekte
5.2.1 Javaklassen und -objekteBisher wurden Klassen nur benutzt um mit Methoden zu arbeiten. Die main() Methode wurde immer als Hauptprogramm genutzt um Methoden aufzurufen. Klassen sind jedoch Strukturen die auch Variablen und Konstanten in Form von Attributen aufnehmen können.
Javaklasse |
---|
Javaklassen bestehen aus
|
Im folgenden Beispiel wird ein Kraftwagen modelliert:
class Kraftwagen { public String nummernschild; public double leergewicht; public int neupreis; /** * Ein selbstgewählter Konstruktor * @param kennzeichen Das Kennzeichen des Fahrzeugs */ public Kraftwagen(String kennzeichen) { nummernschild = kennzeichen; } public void drucken() { System.out.println("Nummernschild: " + nummernschild); System.out.println("Leergewicht: " + leergewicht ); System.out.println("Neupreis: " + neupreis ); } }
Die Klasse Kraftwagen unterscheidet sich in einer Reihe von Aspekten von den bisher benutzten Klassen:
- Sie besitzt keine main() Methode. Sie ist nicht als Hauptprogramm ausführbahr.
- die drucken() Methode is nicht als static deklariert
- die Klasse verfügt über Attribute
Die Klasse Kraftwagen kann nun von anderen Klassen verwendet werden:
class Fuhrpark{ public static void main( String[] args ) { Kraftwagen wagen1 = new Kraftwagen("M-O-4711"); Kraftwagen wagen2 = new Kraftwagen("MA-Y-11"); Kraftwagen wagen3 = new Kraftwagen("S-AY-7"); wagen1.leergewicht = 890.0d; wagen1.neupreis = 20000; wagen2.leergewicht = 1050.0d; wagen2.neupreis = 15000; wagen3.leergewicht = 1250.0d; wagen3.neupreis = 28000; wagen1.drucken(); wagen2.drucken(); wagen3.drucken(); } }
Die Methode main() der Klasse Fuhrpark legt hier drei Objekte (Instanzen) der Klasse Kraftwagen an. Die Attribute der drei Objekten können mit individuellen Werten belegt werden. Die Attribute wagen1, wagen2 und wagen3 nennt man Objektvariablen. Objektvariablen können auf Objekte eines bestimmten Typs zeigen. Im Beispiel oben können sie auf ein beliebiges Objekt vom Typ Kraftwagen zeigen. Die Beziehung zwischen einer Klasse und ihren Instanziierungen, den Objekten beschreibt man in UML wie rechts zu sehen ist. |
Erzeugen von Objekten
Die Instanzen von Klassen, also die Objekte, haben einen anderen Lebenszyklus als normale Variablen mit einfachen Typen. Instanzen (Objekte) können auch ausserhalb eines Blocks oder einer Methode existieren. Sie liegen nicht auf dem Stapel (Stack). Sie werden dynamisch im (prozess-)globalen Adressraum angelegt und sie bestehen so lange sie von Objektvariablen referenziert werden.
Mit dem new() Operator werden neue Objekte von Klassen angelegt. Beim Anlegen eines Objekts werden alle Attribute durch die Konstruktoren initialisiert. Im Beispiel oben wird ein Konstruktor mit einem Übergabeparameter benutzt. In diesem Fall wird der selbst definierte Konstruktor mit einem Parameter der Klasse zur Initialisierung aufgerufen. Existiert dieser Konstruktor nicht, so wird ein Standardkonstruktor aufgerufen.
Der Standardkonstruktor initialisiert alle Basistypen auf Nullwerte. Nach der Initialisierung steht das Objekt zur Verfügung und belegt Platz im Prozessspeicher. Die erzeugten Objekte liegen auf dem "Heap", dem Freispeicher. Im Gegensatz zu Variablen können mehrere Referenzvariablen auf das gleiche Objekt zeigen wie im folgenden Beispiel zu sehen ist: |
Kraftwagen wagen1 = new Kraftwagen ("M-O-4711"); Kraftwagen wagen2 = wagen1; Kraftwagen wagen3 = new Kraftwagen("S-AY-7"); |
Benutzen der Objektvariablen und Methoden (Punktoperator)
Wie im Beispiel oben gezeigt kann man in der Klasse Fuhrpark mit der Objektvariablen wagen1 auch auf die Attribute und Methoden der Klasse Kraftwagen zugreifen. Hierzu dient der Punktoperator:
... wagen1.leergewicht = 980.0d; wagen1.drucken(); ...
Man kann mit dem Punktoperator
- auf Attribute (hier leergewicht) zugreifen oder man kann
- Methoden (hier drucken() ) aufrufen.
Information Hiding in Java mit Hilfe des Schlüsselworts "private"
Java erlaubt es den Zugriff auf Methoden und Variablen mit Hilfe des Schlüsselworts private zu beschränken. Ist eine Objektvariable oder eine Methode mit private gekennzeichnet, so kann man sie nur innerhalb der Klasse benutzen. Der Zugriff ist dann nur noch über öffentlich zugängliche Methoden möglich.
Mit dem Schlüsselwort public wird der öffentliche Zugriff explizit gewährt. Ein Schlüsselwort das den Zugriff regelt ist protected. Es wird später behandelt.
Im folgenden Beispiel sieht man wie man die Variable neupreis der Klasse Kraftwagen vor dem Zugriff der Klasse Fuhrpark schützt. Die Methode getNeupreis erlaubt den Zugriff von ausserhalb der Klasse.
class Kraftwagen { public String nummernschild; public double leergewicht; private int neupreis; public int getNeupreis() { return neupreis;} ... } In UML werden private Attribute mit einem vorgestellten Minus "-" gekennzeichnet. Öffentliche Attribute erhalten ein vorgestelltes Plus "+": |
Klassenvariablen
- Klassenvariablen sind Variablen die zur Klasse gehören und nicht zu einem einzelnen Objekt.
- Klassenvariablen sind Variablen die für alle Objekte einer Klasse gelten.
- Klassenvariablen können auch ohne die Existenz von Objekten einer Klasse verwendet werden
- Klassenvariablen werden mit dem Schlüsselwort static gekennzeichnet.
Beispiel:
class Fuhrpark{ static int anzahlFahrzeuge; public static void main( String[] args ) { int a = Fuhrpark.anzahlFahrzeuge; // Aufruf ausserhalb der Klasse Fuhrpark a = anzahlFahrzeuge; // Aufruf innerhalb der Klasse Fuhrpark ... } }
Hinweis: Die "normalen" nicht mit static gekennzeichneten Attribute einer Klasse nennt man zur Unterscheidung auch Instanzvariablen, da sie nur im Kontext einer Instanz (Objekt) existieren können.
Klassenmethoden
Klassenmethoden werden ebenfalls mit dem Schlüsselwort static gekennzeichnet. Sie können wie normale Methoden im Kontext von Objekten aufgerufen werden.
Unterschied zu normalen Methoden: Sie können jedoch auch ohne die Existenz von Objekten benutzt werden.
Will man eine Methode einer Klasse ohne eine existierende Instanz aufrufen, muss die Methode vorher mit static gekennzeichnet worden sein.
Im Falle der Klasse Fuhrpark kann man sie zum Beispiel zum Auslesen der Anzahl der Fahrzeuge benutzen:
class Fuhrpark{ private static int anzahlFahrzeuge; public static int getAnzahlFahrzeuge() {return anzahlFahrzeuge;} ... } }
Die main() Methode sowie die Methoden der Klasse Math sind typische Methoden, die mit static deklariert sind, da sie ohne Objektinstanzen auskommen (müssen).
- 13446 views
Objektvariablen Verwirrung
Hier wird einmal der wagen1 als Objektvariable benannt und später in der Klasse Kraftwagen das Attribut nummernschild.
Was ist also eine Objektvariable?
- Log in to post comments
Erklärung
Objektvariablen sind Variablen die auf Objekte zeigen. Man kann z.Bsp. mit dem Punktoperator den man auf eine solche Variable anwendet die Attribute und Methoden benutzen.
Objektvariablen können Attribute von Klassen sein. Sind die Attribute keine Objektvariablen so müssen es Basistypen oder Felder sein.
- Log in to post comments
Instanzvariablen-Objektvariablen
Entsprechen Instanzvariablen dann Objektvariablen bzw. sind Synonyme?
Oder verstehe ich das falsch?
- Log in to post comments
Antwort
Objektvariablen zeigen auf Objekte.
Instanzvariablen sind Attribute einer Klasse die für jedes Objekt anders belegt werden können.
Klassenvariablen (static) sind Attribute einer Klasse die zwar auch veränderlich sind. Sie sind aber für alle Objekte(Instanzen) die gleichen. Sie gelten also für die Klasse und nicht für individuelle Objekte.
- Log in to post comments
Referenzvariablen
Wieso macht es Sinn eine Referenzvariable auf das gleiche Objekt zeigen zu lassen ?
- Log in to post comments
Sehr interessante Frage
Die Frage habe ich mir nie gestellt. Sie ist aber recht interessant.
Hier ein Beispiel:
Meine Tochter (Instanz von Person) eines Kraftwagen's (Instanz von Kraftwagen) hält eine Referenzvariable auf das Fahrzeug weil Sie es ab zu einmal fährt. Sie interessiert typischerweise für den Tankinhalt (Attribut von KFZ) wenn er zu niedrig ist. Es sollte dann ja jemand tanken...
Der Familienvater (Instanz von Person) begleicht alle Rechnungen. Dazu benutzt er ein Feld (Array) mit Zeigern auf alles das etwas kostet.
Beide Mitglieder der Familie (Zwei Objekte) haben Zeiger auf das KFZ da sie verschiedene Attribute des Fahrzeugs benutzen.
Es kann jetzt zum Beispiel vorkommen, dass das Fahrzeug umgemeldet werden muss und ein neues Kennzeichen erhält. Der Zeiger beider Personen auf das Fahrzeug (Objekt) bleibt erhalten. Das Attribut mit dem Kennzeichen wird aber nur genau einmal geändert, da es das Kfz-Objekt nur einmal gibt.
- Log in to post comments
5.2.2 this Referenz
5.2.2 this ReferenzJava verfügt über das Schlüsselwort this um auf die aktuelle Instanz innerhalb eines Methodenrumpfes zu referenzieren. Mit Hilfe der folgenden Notation kann man mit dem Schlüsselwort this die Methoden und Attribute der eigenen Klasse referenzieren:
- this.Attributname
- this.methodenName()
Hinweis: Die this Referenz ist final. D.h. man kann ihr keinen anderen Wert zuweisen.
Es gibt eine Reihe von typischen Anwendungsfällen:
Einfache Selbstreferenz
Eine Methode einer Klasse soll die Instanz der Klasse einem Konsumenten bekannt machen. Zum Beispiel:
- Selbst Hinzufügen bzw. Registrieren zu einem Addressbuch innerhalb des Konstruktors der Klasse Person
- Methoden oder Attributaufruf (siehe Aufruf von setNachName(nn) im Konstruktor)
- Rückgabe einer Referenz auf sich selbst (siehe setNachName() )
import class AddressBuch; class Person { private String nachName; private String vorName; ... public Person (String vn, String nn) { this.setNachName(nn); vorName = vn; Adressbuch.eintragen(this); ... } public Person setNachName(String nn) { nachName = nn; return this; } }
Auflösen von Mehrdeutigkeiten
Die this Referenz erlaubt auch das Auflösen von Mehrdeutigkeiten innerhalb eines Namensraumes. Im folgenden Beispiel verdecken die Übergabeparameter nachName, vorName im Konstruktor der Klasse Person die gleichnamigen Attribute:
import class AddressBuch; class Person { private String nachName; private String vorName; ... public Person (String vorName, String nachName) { this.nachName = nachName; this.vorName = vorName; Adressbuch.eintragen(this); ... } }
Aufruf alternativer Konstruktoren mit this()
Das Schlüsselwort this kann auch als Methodenaufruf this() verwendet werden. Dies erlaubt den Aufruf von Konstruktoren der eigenen Klasse.
Diese Technik ist sehr nützlich um bei mehreren Konstruktoren die Replikation von Code zu vermeiden. Dies geschieht im folgenden Beispiel der Klasse Person() bei einem Konstruktor ohne Parameter. Er benutzt den Konstruktor mit Parameter zum Setzen des Namens indem er einen Standardnamen verwendet:
import class AddressBuch; class Person { private String nachName; private String vorName; ... public Person (String vorName, String nachName) { this.nachName = nachName; this.vorName = vorName; Adressbuch.eintragen(this); ... } public Person() { /* Rufe den Konstruktor Person(String vorName, String nachName) auf Dieser Kommentar ist das einzige Kommando welches vor einem this() Aufruf stehen darf! */ this("John","Doe"); } }
Der Vorteil dieser Programmiertechnik liegt in der Vermeidung von Redundanzen. Beim Aufruf des Konstruktors ohne Parameter wird durch den Aufruf des Konstruktors mit Name, Vorname zum Beispiel auch das Addressbuch gepflegt.
- 17499 views
Aufruf alternativer Konstruktoren mit this
Ich habe Schwierigkeiten zu verstehen, wieso man nur beim Aufruf des Konstruktors ohne Parameter, das Adressbuch weiter pflegt.
Wäre dies nicht auch bei einer ganz normalen Verwendung des Konstruktors der Fall gewesen ?
- Log in to post comments
Antwort
Hmm,
nur das unterste Beispiel besitzt zwei Konstruktiven. Ich gehe davon aus, dass es sich hierum handelt.
Da der Konstruktion mit zwei Parameter immer den Konstruktor ohne Parameter aufruft, wird der Code mit dem Pflegen des Adressbuchs immer aufgerufen wenn ein Konstruktor aufgerufen wird.
Beim Aufruf des Konstrukteurs ohne Parameter wird also auch das Adressbuch gepflegt (aber nicht nur).
Ich hoffe das hilft.
Beste Grüße
- Log in to post comments
5.2.3 Konstruktoren (2)
5.2.3 Konstruktoren (2)Die schon früher angesprochenen Konstruktoren sind eine wichtige Komponente der Datenkapselung, da man mit ihnen eine komplexe und korrekte Initialisierung von Objekten erzwingen kann.
Konstruktoren mit Parametern
Konstruktoren können wie Methoden Übergabeparameter enthalten. Sie werden vom new() Operator erfasst, der dann nach dem Initialisieren der Datenstrukturen den entsprechenden Konstruktor aufruft.
Punkt p1 = new Punkt (3.3D, 4.1D);
Hier wird ein Konstruktor der Klasse Punkt aufgerufen der folgende Methodenparameter hat
package s1.block5; class Punkt { private double x; private double y; public Punkt (double xKoord, double yKoord) { x = xKoord; y = yKoord; } }
Das Schreiben von Konstruktoren ist optional. Es können mehrere Konstruktoren implementiert werden. Alle Konstruktoren müssen sich jedoch in der Parameterliste unterscheiden. Hier zählt die Reihenfolge und Anzahl der Parametertypen, jedoch nicht der Name der Parametervariablen. Der Übersetzer braucht diese Information um die unterschiedlichen Konstruktoren auszuwählen.
Überladene Konstruktoren
Überladen von Methoden und Konstruktoren |
---|
Das Implementieren von mehreren namensgleichen Methoden oder Konstruktoren mit unterschiedlichen Eingabe-Parameterlisten nennt man überladen. |
Java unterscheidet die unterschiedlichen Methoden und Konstruktoren an den Eingabelisten der Parameter jedoch nicht am Rückgabeparameter!
Es kann sehr nützlich sein mehrere Konstruktoren zur Initialisierung einer Klasse zur Verfügung zu stellen wie man am Beispiel der Klasse Punkt sehen kann. Die Klasse Punkt erlaubt hier die folgenden Initialisierungsarten:
- Initialisierung mit Nullpunkt(0,0)
- Initialisierung mit x,y Koordinate
- Initialisierung mit den Werten eines anderen Punkts
package s1.block5;
public class Punkt {
private double x;
private double y;
public Punkt (double xKoord, double yKoord) {
x = xKoord;
y = yKoord;
}
public Punkt (Punkt p) { this(p.x,p.y);}
public Punkt () { this(0,0); }
public static void main(String[] args) {
Punkt p1 = new Punkt(); // Initialisierung mit (0,0)
Punkt p2 = new Punkt(1.1D,2.2D); // Initialisierung mit (1.1,2.2)
Punkt p3 = new Punkt(p2); // Initialisierung mit (1.1,2.2) durch Punkt p2 }
}
Der Default-Konstruktor (ohne Parameter) wurde hier selbst implementiert. Er ruft mit Hilfe des Schlüsselworts this den Konstruktor mit den beiden Fliesskommazahlen als Parameter auf. Er initialisiert die Datenstruktur nicht selbst, sondern er delegiert die Initialisierung an einen anderen Konstruktor. Dies ist nicht notwendig aber üblich.
Der Konstruktor mit der Parameterliste Punkt verfährt ähnlich.
Aufrufe von Konstruktoren durch Konstruktoren (der gleichen Klasse) |
---|
Konstruktoren können andere Konstruktoren der gleichen Klasse mit this(parameter-liste) aufrufen. Wichtig: Der this() Aufruf muss jedoch der erste Befehl in einem Konstruktor sein. |
Ein optionaler this() Aufruf muss das erste Kommando im Codeblock des Konstruktors sein um die Integrität des mit this() aufgerufenen Konstruktors zu gewährleisten.
Ein Konstruktor ist die erste Methode die ein Objekt initialisiert. Das ist aber nicht mehr für den aufgerufenen Konstruktor geährleistet, wenn dem aufrufenden Konstruktor schon Objektmanipulationen vor dem this() Aufruf erlaubt sind.
Regeln zum Aufruf von Konstruktoren
Java stellt einen Standardkonstruktor (Default-Konstruktor) ohne Parameter zur Verfügung der alle Attribute mit Standardwerten belegt. Implementiert man eigene Konstruktoren gelten die folgenden Regeln für die Benutzer von Konstruktoren:
- Wurde kein Konstruktor implementiert, generiert Java einen Default-Konstruktor ohne Parameter.
- Wird mindestens ein Konstruktor selbst implementiert so generiert Java zur Laufzeit keinen Standardkonstruktor. Benutzer müssen einen der selbst implementierten Konstruktoren verwenden.
- Wird ein Konstruktor ohne Parameter implementiert wird dieser anstatt eines Standardonstruktor benutzt, da er für den Anwender die gleiche Syntax hat.
Dieses Vorgehen ist notwendig um eine Datenkapselung zu erzwingen.
Bei einer Implementierung mit eigenen Konstruktoren aber ohne einen Default-Konstruktor führt der folgende Konstruktoraufruf zu einem Übersetzungsfehler:
class Punkt { private double x; private double y; public Punkt (double xKoord, double yKoord) {...} public Punkt (Punkt p) { this(p.x,p.y);} ... }
... Punkt p1 = new Punkt(); // Initialisierung mit Default-Konstruktor ist nicht möglich ...
Erklärung der von Java geforderten Semantik:
- Hat ein Entwickler keinen Konstruktor implementiert, so war ihm die Initialisierung des Objekts nicht wichtig.
- Das Objekt wird von einem automatisch generierten Konstruktor mit Nullwerten initialisiert
- Der Standardkonstruktor wird benutzt wie ein Konstruktor ohne Parameter
- Hat der Entwickler mindestens einen Konstruktor selbst implementiert, so ist eine spezielle Initialisierung des Objekts gewünscht
- Ein Konsument muss das Objekt mit Hilfe einer der selbstimplementierten Konstruktoren initialisieren
- Würde das Java einen zusätzlichen Standardkonstruktor generieren könnte man die vom Entwickler gewünschte Initialisierung umgehen. Dies ist nicht gewünscht.
Verbieten des Instanziieren von Objekten einer Klasse
Java bietet die Möglichkeit einen Konstruktor als private zu deklarieren. Hiermit kann niemand ausserhalb der Klasse Instanzen erzeugen. Diese Möglichkeit erlaubt die Anzahl von Instanzen bei Bedarf genau zu kontrollieren. Ein Anwendungsfall ist ist das Benutzen einer statischen Methode und eines privaten Kontruktors:
class Punkt { private double x; private double y; public static Punkt createPunkt (double xKoord, double yKoord) { Punkt pp = new Punkt(xKoord, yKoord); return pp; } private Punkt (double xx, double yy) { x=xx; y=yy;} } ... Punkt p1 = Punkt.createPunkt(1.1D,2.2D); Punkt p2 = new Punkt(4.4D,5.5D); // Fehler!! ...
Ein anderer Anwendungsfall ist die Benutzung des Entwurfsmodell "Singleton" d.h. einer Klasse von der es genau eine Instanz gibt. Ein Beispiel ist die Klasse Addressbuch:
package s1.block5;
public class Adressbuch {
private static Adressbuch myInstance;
private Adressbuch() {
// Initialisiere Adressbuch
}
public static Adressbuch getAdressbuch() {
if (myInstance == null) myInstance = new Adressbuch();
return myInstance;
}
public static void main(String[] args) {
Adressbuch ab = getAdressbuch();
Adressbuch cd = getAdressbuch();
}
}
Mit dieser Implementierung wird beim ersten Anfordern eines Adressbuchs genau einmal ein Adressbuch angelegt.
- 12619 views
5.2.4 Ablauf der Initialisierung einer Klasse
5.2.4 Ablauf der Initialisierung einer KlasseBegriffsbestimmung:
Instanziierung einer Klasse |
---|
Erzeugen eines neuen Objekts einer Klasse auf dem Java Heap (Freispeicher) |
Initialisierung (von Datenfeldern) |
---|
Das Belegen von existierenden Datenfeldern mit Standardwerten oder mit von Konstruktoren individuell implementierten Werten |
Beim Erzeugen eines Javaobjekts wie zum Beispiel einer Instanz der Klasse Punkt:
class Punkt { private double x; private double y; public Punkt (double xKoord, double yKoord) { x = xKoord; y = yKoord; } } ... Punkt p2 = new Punkt(1.1,2.2); ... Mit der Variable p2 l laufen die folgenden Schritte beim Aufruf der Programmzeile Punkt p2=new Punkt() ab:
|
- 8599 views
5.3 Pakete (Java Packages) im Überblick
5.3 Pakete (Java Packages) im Überblick
Javapakete (englisch packages) erlauben das Zusammenfassen von Klassen und "Interfaces" (Schnittstellen) in logischen Gruppen. Das Gruppieren von Klassen und Schnittstellen (Interfaces) in Pakete erlaubt:
Javapakete werden mit dem Schlüsselwort package deklariert und mit Hilfe des Schlüsselworts import importiert. |
Daumenregeln
- Der Paketname steht durch einen Punkt getrennt vor dem Klassennamen
- Pakete können auch wieder Pakete enthalten
- Eine Klasse mit all Ihren äusseren Paketen ist ein eindeutiger Name
- Klassen ohne einen Paketnamen liegen im Default-Package
- Klassen ohne Paketnamen zu verwenden geht nicht mehr in neueren Java Versionen!
- Nutzt man einen Klassennamen ohne den Paketkontext, so kommt sie aus dem gleichen Paket
Diese Daumenregelen reichen nicht um die Klausur zu bestehen...
- 1835 views
5.3.1 Pakete (Java Packages) im Detail
5.3.1 Pakete (Java Packages) im Detail
Javapakete (englisch packages) erlauben das Zusammenfassen von Klassen und "Interfaces" (Schnittstellen) in logischen Gruppen. Das Gruppieren von Klassen und Schnittstellen (Interfaces) in Pakete erlaubt:
Javapakete werden mit dem Schlüsselwort package deklariert und mit Hilfe des Schlüsselworts import importiert. Jede Javaklasse die zu einem gegebenen Paket gehören soll, muss in einer Quelldatei stehen die mit dem Schlüsselwort package beginnt: |
package DemoPackage; ... class Demonstration1 { ... } ... class Demonstration2 { ... }
Struktur von *.java Quelldateien |
---|
|
Hinweis: Der Javaübersetzer javac wird für jede einzelne Klasse eine Datei mit dem Klassennamen und der Dateierweiterung .class erzeugen.
Alle Javaklassen die mit dem gleichen Paketnamen versehen sind gehören logisch zusammen. Dies bedeutet, dass diese Klassen typischerweise gemeinsam verwendet werden und das für sie ähnliche Zugriffsrechte gelten.
Alle Klassen für die kein Paket spezifiziert wurde, gehören automatisch dem namenlosen Standardpaket an.
Javapakete und Verzeichnisstrukturen
Bei der Verwendung von Javapaketen ist darauf zu achten, dass das Javalaufzeitsystem (Kommando java) standardmäßig davon ausgeht, dass eine *.class Datei in einem Unterverzeichnis zu finden ist, welches den Paketnamen besitzt. Der Javaübersetzer (javac) ignoriert jedoch in seinen Standardeinstellungen Unterverzeichnisse und speichert Javaklassen im aktuellen Verzeichnis.
Hierzu das folgende Beispiel mit der Datei HelloWorld.java:
package Demo; class A { public static void main(String[] args) { B.printHelloWorld(); } } class B { public static void printHelloWorld() { System.out.println("Hello World!"); } }
Es empfiehlt sich im Rahmen des Kurses die .java Dateien in einem Verzeichnis zu pflegen welches den Namen des verwendeten Pakets besitzt. Der Javaübersetzer javac sollte in dem Paketverzeichnis aufgerufen werden. Das Java Laufzeitsystem java sollte im darüber liegenden (allgemeineren) Verzeichnis aufgerufen werden.
Wichtig: In einer Quelldatei dürfen zwar mehrere Klassen vorhanden sein, es darf jedoch nur maximal eine Klasse als public deklariert sein.
Empfehlung: Im Normalfall ist es üblich in einer Datei nur eine Klasse zu implementieren. Die Datei sollte genau den Namen der Klasse tragen.
Steuern der Paketsuche durch "classpath"
Java findet die Standardklassen der SE Edition immer automatisch. Zum Finden von anwenderspezifischen Paketen wird in Java der classpath verwendet. classpath ist eine Umgebungsvariable des Betriebsystems die man setzen kann. Die Javakommandos werden dann diese Variable zum Suchen der Pakete verwenden. Die Art und Weise des Setzens dieser Variable sind Betriebssystem spezifisch. Unter Windows geschieht dies beispielsweise durch das folgende Kommando:
set classpath=.;D:\Paket1;D:\Paket2
Hier wird zuerst im aktuellen Verzeichnis (.) gesucht dann in den Verzeichnissen Paket1 und Paket2 des Laufwerks D.
Die Javakommandos verfügen auch über eine Option -classpath mit der man die Suchpfade für einen bestimmten Aufruf vorgeben kann. Die Syntax der -classpath Option kann man mit Hilfe des Kommandos java -help erfragen:
java -help ...-classpath <class search path of directories and zip/jar files> A : separated list of directories, JAR archives, and ZIP archives to search for class files. ...
Namensräume
Klassen und Schnittstellen (Interfaces) in einem Paket bilden einen Namensraum. Dies bedeutet:
- Nur Klassen aus dem gleichen Paket können ohne weiteres verwendet werden
- Klassen und Interfacenamen innerhalb eines Pakets müssen eindeutig sein
- Zur Verwendung von Klassen aus anderen Paketen muss
- die Klasse für die gesamte Quelldatei importiert werden (expliziter Import). Beispiel
-
import Demo.B;
-
- oder die Klasse muss mit dem Paket genau bei der Benutzung spezifiziert werden (impliziter Import). Beispiel:
-
Demo.B.printHelloWorld();
-
- die Klasse für die gesamte Quelldatei importiert werden (expliziter Import). Beispiel
- Es können Klassen mit dem gleichen Namen in unterschiedlichen Paketen vorkommen.
Export von Klassen und Schnittstellen(Interfaces)
Nur das Paket selbst bestimmt welche Klassen exportiert werden. Dies bedeutet, dass die entsprechenden Klassen von Aussen sichtbar und benutzbar sind.
Die Benutzung einer Klasse außerhalb eines Pakets wird duch das Schlüsselwort public vor dem Schlüsselwort class deklariert. Beispiel:
package Demo; ... public class A { ... }
Import von Klassen und Schnittstellen(Interfaces)
Expliziter Import
Der explizite Import von Klassen eines anderen Pakets geschieht durch das Schlüsselwort import gefolgt vom Paketnamen und dem Klassennamen der vom Paketnamen durch den Punktoperator getrennt wird.
package DemoConsumer; ... import Demo.A; ... class Consumer { ... A.eineMethode(); // Aufruf der Methode eineMethode() der Klassse A ... }
Neben der Möglichkeit bestimmte Klassen eines Pakets zu importieren, kann man auch alle Klassen eines Pakets importieren. Beispiel:
package DemoConsumer; ... import Demo.*; ... class Consumer { ... A.eineMethode(); // Aufruf der Methode Demo.A.eineMethode() B.andereMethode(); // Aufruf der Methode Demo.B.andereMethode() ... }
Impliziter Import
Fremde Klassen können auch adhoc verwendet werden indem man den Klassennamen mit vorangestelltem Paketnamen und dem Punktoperator verwendet. Beispiel;
package DemoConsumer; ... class Consumer { ... Demo.A.main(); Aufruf der Methode main() der Klassse A Demo.B.printHelloWorld(); ... }
Der implizite Import ist nützlich wenn zwei Klassen mit identischem Namen aus zwei verschiedenen Paketen benutzt und unterschieden werden müssen. Beim freiwilligen, impliziten Import muss man zwischen den folgenden Vor- und Nachteilen abwägen:
- Vorteil: Man benutzt nur die deklarierte Klasse, die Herkunft der Klasse ist für den Leser des Quellcodes direkt sichtbar
- Nachteil: Bei jeder Verwendung muss der Paketnamen vorgestellt werden welches den "Textverbrauch" die langen Klassen- und Paketnamen erheblich steigern kann. Eine Programmzeile sollte nicht mehr als 80 Zeichen haben!
Statischer Import
Die bisher vorgestellten Importvarianten erlauben das Importieren einer oder mehrerer Klassen. Beim Benutzen von statischen Attributen, Konstanten und Methoden muss man bei Java jedoch immer den Klassennamen voranstellen (Beispiel: Math.cos() ).
Statische Importe erlauben diese statischen Elemente einer Klasse im Namensraum einer Datei direkt bekannt zu machen. Hiermit kann man auf statische Attribute, Konstanten, Methoden einer Klasse zugreifen wie auf die lokalen Objektelemente einer Klasse. Man erspart sich das explizite Nennen der Klasse.
Implementierung mit explizitem Import:
package test; import java.lang.Math; ... class calc { public void main(String[] args) { float x = Math.PI; float y = Math.cos(2*x); } }
Mit Hilfe des des statischen Imports kann man z.Bsp. die Klasse Math importieren um deren statischen Elemente direkt benutzen zu können. Durch den statischen Import ergibt sich für das vorhergehende Beispiel die folgende Implementierung:
package test; import static java.lang.Math.*; ... class calc { public void main(String[] args) { float x = PI; float y = cos(2*x); } }
Wichtig:
- beim statischen Import müssen Klassen immer explizit angegeben werden.
- Namenskonflikte werden vom Übersetzer beanstandet. Sie müssen dann durch einen impliziten Import aufgelöst werden.
Zugriffsrechte auf Methoden und Attribute
Klassen außerhalb eines Pakets können auf Methoden und Attribute von Klassen eines Pakets nur zugreifen insofern es sich selbst mit den Schlüsselworten public class selbst zum Export freigibt. Falls das der Fall ist kann auf Methoden und Attribute abhängig von der Schlüsselwörten public, protected, private zugregriffen werden. Hierfür gilt:
- public: Zugriff innerhalb der Klasse, außerhalb der Klasse, inner- und außerhalb des Pakets
- protected: Zugriff nur durch Klassen und Methoden des eigenen Pakets oder Methoden von Unterklassen
- private: Zugriff nur innerhalb der Klasse
- keine Angabe: Zugriff nur innerhalb des Pakets (Im folgenden Diagramm "package" genannt)
Exportierendes Schlüsselwort | Konsument außerhalb des Pakets | Konsument innerhalb des Pakets | Konsument in einer Unterklasse | Konsument innerhalb der Klasse |
---|---|---|---|---|
private | - | - | - | Benutzung erlaubt |
"kein Schlüsselwort" (package) | - | Benutzung erlaubt | Benutzung erlaubt | Benutzung erlaubt |
protected | Benutzung nur erlaubt wenn Klasse Unterklasse ist | Benutzung erlaubt | Benutzung erlaubt | Benutzung erlaubt |
public | Benutzung erlaubt | Benutzung erlaubt | Benutzung erlaubt | Benutzung erlaubt |
Dies ergibt in Bezug auf die Benutzbarkeit von anderen Klassen die folgenden geschachtelten Mengen:
- 13689 views
statische Importe
Ich glaube, beim package test unter "statische importe" für statische Importe beim beispiel expliziter import fehlt ein .* hinter import java.lang.Math
:)
- Log in to post comments
5.3.2 Lernziele
5.3.2 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zu Paketen zu beantworten:
- 3620 views
5.4 Übungen
5.4 Übungen
5.4.1 Übung: VektorrechnungEin Vektor im zweidimensionalen Raum kann durch 2 reelle Zahlen dargestellt werden.
Erstellen Sie eine Klasse "Vektor", die Operationen(Methoden) für
Schreiben Sie auch einen geeigneten Konstruktor und implementieren Sie eine Ausgabemethode drucken().
Normalisieren Sie die Vektoren nach jeder Berechnung, so dass ihr Betrag den Wert 1 hat.
|
5.4.2 Übung: Komplexe Zahlen
Eine komplexe Zahl (z.B. 3.2 + i1.75) besteht aus einem reellen und einem imaginären Teil, beide vom Typ double. Erstellen Sie eine Klasse Complex, die komplexe Zahlen implementiert. Als Operationen (Methoden) sollen die vier Grundrechenarten sowie ein geeigneter Konstruktor angeboten werden. Hinweis:
Für jede
z1 = a + bi a,b ∈ ℝ z2 = c + di c,d ∈ ℝ z1 + z2 = (a + ib) + (c + id) = (a + c) + i(b + d) z1 - z2 = (a + ib) - (c + id) = (a - c) + i(b - d) z1 * z2 = (a + ib) * (c + id) = (a*c - b*d) + i(a*d + b*c) z1 / z2 = (a + ib) / (c + id) = (a*c + b*d)/(c*c + d*d) + i(b*c - a*d)/(c*c + d*d)
Benutzen Sie die Klasse Main als Hauptprogramm um auf die Klasse Complex zuzugreifen:
package s1.block5; public class Main { public static void main(String[] args) { Complex a = new Complex (1.0,2.0); Complex b = new Complex (3.0,4.0); Complex c,d,e,f; c = a.add(b); d = a.sub(b); e = a.mul(b); f = a.div(b); System.out.println (" a = " + a.toString()); System.out.println (" b = " + b.toString()); System.out.println (" c = " + c.toString()); System.out.println (" d = " + d.toString()); System.out.println (" e = " + e.toString()); System.out.println (" f = " + f.toString()); } } |
Die Klasse Complex soll neben den Methoden add(), sub(), mul(), div() auch eine Methode toString() besitzen, die eine Zeichenkette mit dem Wert der komplexen Zahl im Format "(1.1 + i 2.2)" für einen Realteil von 1.1 und einem Imaginärteil von 2.2 ausgibt.
5.4.3 Übung: Modellierung der Datenkapselung
Erweitern Sie eine Klasse Flugzeug.java von einer einfachen Klasse mit einem öffentlichen Attribut zu einer objektorientierten Klasse mit geschützten Attributen und Zugriffsmethoden.
Vorsicht: Der unten aufgeführte Quellcode liegt nicht im default package. Der Quellcode liegt in package block5! Achten Sie auf das korrekte Paket beim Anlegen der Klassen.
- Kopieren Sie sich die beiden Klassen Flugzeug.java und FlugzeugTest.java auf Ihren Rechner. Die Klasse FlugzeugTest dient zum Testen und Starten der Anwendung.
- (Schritt 1-5) Implementieren die notwendigen Attribute als geschützte Attribute. Die Namen der Attribute sind auch in der drucken() Methode zu finden
- (Schritt 6) Implementieren Sie die Methode zur Berechnung des aktuellen Gewichts
- (Schritt 7): Entfernen Sie die Kommentare in der drucken() Methode.
- (Schritt 8): Implementieren Sie einen Konstruktor für die Klasse Flugzeug. Der Konstruktor soll die Eingaben auf unvernünftige Werte prüfen und die Eingaben korrigieren (Werte kleiner Null, Maximalgewicht kleiner Leergewicht etc.)
- (Schritt 9): Schalten Sie im Hauptprogramm FlugzeugTest.main() den Aufruf der Methode phase1() frei und testen Sie das Programm durch den Aufruf der Klasse FlugzeugTest
- (Schritt 10-13): Implementieren Sie alle benötigten Methoden (siehe Diagramm)
- (Schritt 14): Schalten Sie im Hauptprogramm FlugzeugTest.main() den Aufruf der Methode phase2() frei und testen Sie das Programm durch den Aufruf der Klasse FlugzeugTest
- (Schritt 15): Schalten Sie im Hauptprogramm FlugzeugTest.main() den Aufruf der Methode phase3() frei und testen Sie das Programm durch den Aufruf der Klasse FlugzeugTest. Analysieren Sie die Umsteigeimplementierung in phase3(). Testen Sie einige Sonderfälle durch Veränderung der Passagierzahlen. Beispiel: Was geschieht wenn das defekte Flugzeug mehr Passagiere hat als das Ersatzflugzeug?
- (Schritt 16): Implementieren Sie die Methode phase4() und schalten Sie im Hauptprogramm FlugzeugTest.main() den Aufruf der Methode phase4() frei. Testen Sie das Programm durch den Aufruf der Klasse FlugzeugTest.
- Implementieren Sie in Phase das Umsteigen von einem Airbus mit 560 Passagieren in zwei kleinere Jumbo Jets.
- Verändern Sie die Ausgangssituation derart, dass in den Jumbo Jets schon zu viele Passagiere eingestiegen sind und nicht alle Passagiere des Airbus untergebracht werden können. Werden stehen gelassene Passagiere auf der Konsole gemeldet?
Gegeben:
Klasse Flugzeug
package block5; public class Flugzeug {
public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
// 3. Leergewicht in privates Attribut ändern
// Ein Attribut vom Typ einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
/**
* 8. Konstruktor implementieren
* Konstruktor der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
/**
* einsteigen()
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
/**
* aussteigen()
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
/**
* anzahlPassagiere()
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
/**
* gewicht()
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
/**
* passagierkapazität()
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Aufgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
//System.out.println("Leergewicht: " + leergewicht + "kg");
//System.out.println("Maximalgewicht: " + maxgewicht + "kg");
//System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
//System.out.println("Passagiere: " + passagiere);
//System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("*****************************");
}
}
Klasse FlugzeugTest.java
package block5;
public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
//phase1(); // 9. Phase 1 testen
//phase2(); // 14. Phase 2 testen
//phase3(); // 15. Phase 3 testen
//phase4();
}
/* Entfernen zum Testen von Phase 1
public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT",360,191000,400000);
a380 = new Flugzeug("D-AIMD",560,286000,500000);
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}
*/
/* Entfernen zum Testen von Phase 2
public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i=0; i<300; i++) jumbo.einsteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
}
*/
/* Entfernen zum Testen von Phase 3
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU",360,191000,400000);
Flugzeug a380Neu = new Flugzeug("D-AIME",560,286000,500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i=0; i<300; i++) jumboAlt.einsteigen();
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i=0; i<100; i++) a380Neu.einsteigen();
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere()> 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}
*/
/* Entfernen zum Testen von Phase 4
public static void phase4() {
System.out.println(" Phase 3: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV",360,191000,400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW",360,191000,400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME",560,286000,500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i=0; i<50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i=0; i<560; i++) a380Defekt.einsteigen();
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
*/
}
5.4.4 Klassenvariablen (static variables)
Die Übung baut auf der vorherigen Übung aus. Bitte benutzen Sie die Musterlösung falls Sie die vorherige Übung nicht gelöst haben.
Implementieren Sie einen Zähler für jedes Flugzeugobjekt der Klasse Flugzeug welches erzeugt wird.
Empfohlenes Vorgehen:
- Implementieren Sie eine statische, geschützte Variable in der Klasse Flugzeug
- Inkrementieren Sie diese Variable bei jedem Konstruktoraufruf der Klasse Flugzeug
- Erweitern Sie die Methode drucken() und geben Sie die aktuelle Gesamtzahl von Flugzeugen aus
- Implementieren Sie eine öffentliche, statische Methode anzahlFlugzeuge() die den Zähler ausgibt
- Benutzen Sie das existierende Programm in der Klasse FlugzeugTest zum Testen Ihrer Implementierung
Lernziel: Unterscheiden der Variablen die objektspezifisch sind und derer die klassenspezifisch sind.
5.4.5 Überladene Methoden
Die Übung baut auf der vorherigen Übung aus. Bitte benutzen Sie die Musterlösung falls Sie die vorherige Übung nicht gelöst haben.
Implementieren sie überladene Methoden um das Einsteigen und Aussteigen in der Klasse Flugzeug zu vereinfachen. Die neuen Methoden haben
- die gleichen Namen wie die Methoden zum Ein- und Aussteigen eines einzelnen Passagiers,
- sie haben einen Übergabeparameter der die Anzahl der Passagiere zum Ein- und Aussteigen angibt
- es werden nur nicht negative Werte beachtet
- die Operation wird nur ausgeführt falls ALLE Passagiere in der aktuellen Anforderung ein- oder aussteigen können.
Testen Sie die Implementierung mit der Klasse FlugzeugTest:
- Kopieren Sie die Methoden phase3() und phase4(). Benennen Sie diese um in phase3a(), phase4a()
- Ändern Sie die Implementierung deart, dass anstatt der for-Schleifen einzelne Aufrufe der neuen Methoden durchgeführt werden
- Tipp: Ändern Sie die Flugzeugkennzeichen. Es werden neue Objekte angelegt. Es ist daher nützlich nicht zwei Flugzeuge mit dem gleichen Kennzeichen zu benutzen.
- Fügen die Methoden phase3a() und phase4a() in die main() Methode ein.
5.4.6 Teamübung: Fuhrparkmanagement
Die folgende Übung kann in einem Team von drei Parteien ausgeführt werden. Jede Partei implementiert eine Klasse. Die Übung findet in den folgenden Phasen statt.
- Jedes Team entwirft die entsprechende Klasse und stellt die Klasse und alle öffentlichen Methoden und Variablen vor
- Jedes Team implementiert seine Klasse und schreibt in der eigenen main() Methode einige Tests.
- Alle Teams integrieren die Klassen zu einer lauffähigen Anwendung und testen sie
- Jeweils 2 Teams stellen die Implementierungen wechselseitig vor und kontrollieren sie.
Anforderung an alle: Implementieren Sie sinnvolle Konstruktoren
Klasse 1: Kraftwagen
Anforderungen
- verwalten von
- Verkaufspreis
- Einkaufspreis
- Kennzeichen
- Schutz der Variablen gegen inkonsistente Änderungen mit Hilfe von Zugriffs- und Auslesemethoden
- Methode drucken() zum Drucken der Attribute eines Wagens
- Zugriffsmethoden (Lesen/Schreiben) die alle drei Attribute kapseln und einfache Konsistenzchecks durchführen
- Implementierung eines Konstruktors der alle drei Parameter erfasst
- Konsistenzcheck für die Preise: Ist der Verkaufspreis niedriger als der Einkaufspreis so werden die Werte getauscht.
- Zum Testen
- Methode public static void main(String[] args) Anlegen von mehreren Kraftwagen mit verschiedenen Werten. Testen der Konsistenzprüfungen und Ausgaben der Werte
Klasse 2: Verkaeufer
Anforderungen
- verwalten von mindestens 2 Kraftwagen pro Verkäufer
- Verkäuferattribute und Methoden
- Name
- bisher gemachter Gewinn
- gebundene Mittel (Summe aller Einkaufspreise aller Wagen)
- Verkauf eines Wagens zu gegebenem Preis
- Austragen aus dem Fahrzeugbestand
- Pflegen des Gewinn
- Aufnehmen eines Wagens in den persönlichen Bestand
- Existierende Wagen im Bestand können ersatzlos ersetzt werden
- Abfrage der Anzahl der Wagen im Bestand
- Abfrage eines Kraftwagens aus dem Bestand
- Abfrage der gebundenen Mittel
- Abfrage des geplanten Umsatzes
- Methode drucken() zum Ausdrucken aller Daten des Verkäufers und aller Daten des Fahrzeugbestands
- Warnen bei Verkauf eines Wagen unter Einkaufspreis (Konsolenausgabe)
- Konstruktor zum Anlegen eines Verkäufers mit einem Namen. Der Namen darf nicht mehr geändert werden.
- Zum Testen:
- Methode public static void main(String[] args) : Anlegen von drei Verkaeufern mit verschiedenen Werten und Fahrzeugen.
- Verkauf diverser Fahrzeuge und Abfrage des Status mit Hilfe der Methode drucken()
- Testen gegen diverse Fehlerfälle beim Verkauf
Klasse 3: Haendler
Anforderungen
- Verwalten von mindestens 3 Verkäufern
- Methoden
- Einstellen von drei Verkäufern mit gegebenem Namen
- Geschäftseröffnung mit Einstellen von 6 Fahrzeugen und beliebige Verteilung auf 3 Verkäufer.
- Zugriff auf jeden der drei Verkäufer
- gesamter Wert des Fuhrparks
- gesamter Gewinn
- gesamter Umsatz
- verkaufe Fahrzeug für gegebenen Verkäufer (index) und Wagen des Verkäufers (index) zu gegebenen Preis
- stelle neues Fahrzeug für Verkäufer (index) auf einer bestimmten Position des Verkäufers ein
- suche des Verkäufers mit den größten Gewinn
- Methode drucken() zur Ausgabe aller Daten des Unternehmens
- Zum Testen im Hauptprogramm public static void main(String[] args)
- Ausdruck des Zustandes des gesamten Unternehmens
- Berechnen der geplanten Erlöse des Fuhrparks
- Berechnen des gesamten Werts der Flotte
- Verkäufer mit dem meisten Umsätzen finden
- Verkauf eines Wagens
- Einstellen eines neuen Wagens
Hinweis: Die Spezifikation ist nicht vollständig. Das Team muss auf die folgenden Dinge selbst achten:
- Zeitplanung (Integrationstests benötigen viel Zeit!)
- Übergang der Phasen (inklusive notwendige Rückschritte)
- Gegenseitige Überprüfung der Spezifikation und sinngemässe Ergänzung
- Man darf sich von anderen Klassen zusätzliche Methoden wünschen wenn es sinnvoll ist.. Man sollte dann auch zur Implementierung beitragen...
- Gegenseitige Hilfe bei der Implementierung ist erlaubt und erwünscht (Die einzelnen Teilaufgaben können unterschiedlich aufwendig sein!)
5.4.7 Mehrere Konstruktoren
Benutzen Sie das Beispiel der Klasse Flugzeug und der Klasse FlugzeugTest aus der Übung 5.4.5 Überladenen Methode.
Fügen zur Klasse Flugzeug einen weiteren Konstruktor hinzu. Der Konstruktor soll im Gegensatz zum existierenden Konstruktor keinen Parameter zur Passagierkapazität besitzen.
- Berechnen Sie die Passagierkapazität aus der Differenz des Maximal- und Leergewicht. Teilen Sie die Differenz durch das durchschnittliche Passagiergewicht.
- Sie müssen nicht alle Zuweisungen und Kontrollen des alten Konstruktors neu implementieren!
- Rufen Sie den alten Konstruktor mit dem this() Schlüsselwort auf.
Der Dokumentationskommentar zum zu implementierenden Konstruktor
/** * Konstruktur der Klasse Flugzeug * Berechnet Passagierkapazität automatisch * @param kennz Kennzeichen des Flugzeugs * @param leergew Leergewicht in kg * @param maxgew Maximalgewicht in kg */
Hinweis:
- Die Konstante PASSAGIERGEWICHT muss bei der Deklaration in der Klasse Flugzeug mit dem Schlüsselwort static versehen werden. Man darf zu diesem Zeitpunkt im Konstruktor noch keine Objektvariablen verwenden.
- Vorsicht beim Kopieren. Die Klasse Flugzeug befindet sich im Paket block5. Kopieren Sie die Klasse Flugzeug in das Paket block6 oder verwenden Sie import Anweisungen!
Testen Sie das Programm mit der Routine phase6a() der Klasse FlugzeugTest:
package block6; public class FlugzeugTest { /** * Die Methode main() wir zum Starten des Programms benutzt * @param args Übergabe von Konsolenparameter. Hier nicht genutzt */ public static void main(String[] args) { phase6a(); } /** * Testen des überladenen Konstruktors */ public static void phase6a() { Flugzeug b737_500 = new Flugzeug("D-ABIAA", 31900, 52000); Flugzeug b737_300 = new Flugzeug("D-ABIAB", 12815, 56470); System.out.println("Kapazität Boing 737-300: " + b737_300.passagierkapazitaet()); System.out.println("Kapazität Boing 737-500: " + b737_500.passagierkapazitaet()); } }
Warum ist die automatisch berechnete Sitzkapazität der Boing 737 viel zu groß?
Was wurde bei der Modellierung der Klasse nicht berücksichtigt?
5.4.8 Flughafen Beispiel
Klasse Flugzeug
package block6;
/**
* Die Klasse Flugzeug dient zur Modellierung von Flugzeugen
* und vielem mehr...
* @author stsch
* @version 2.0
* @see Flughafen
*/
public class Flugzeug {
final static double durchschnittsgewicht = 75;
String kennzeichen;
/**
* aktuelle Anzahl der Passagiere
*/
int passagiere;
public int getPassagiere() {
return passagiere;
}
public void setPassagiere(int passagiere) {
this.passagiere = passagiere;
}
/**
* das aktuelle Gewicht. Bitte nicht mit rumspielen
*/
private double maximalesGewicht;
double minimalGewicht;
/**
* Konstruktor mit Kennzeichen vom Leitwerk. Z.Bsp. D-ABYD
* @param kennz Das amtliche Kennzeichen des Flugzeugs
*/
public Flugzeug(String kennz) {
kennzeichen = kennz;
System.out.println("Hallo, ich baue ein Flugzeug mit Namen " + kennzeichen);
}
/**
* Konstruktor mit Kennzeichen vom Leitwerk. Z.Bsp. D-ABYD
* @param minGewicht Minimalgewicht
* @param maxGewicht Maximalgewicht
*/
Flugzeug(String kennzei, double minGewicht, double maxGewicht) {
this(kennzei);
System.out.println("Hallo, ich baue ein Flugzeug mit Gewicht");
maximalesGewicht = maxGewicht;
// Kontrolle des Minimalgewichts
if ((minGewicht > 0) && (minGewicht <= maximalesGewicht)) {
minimalGewicht = minGewicht;
} else {
minimalGewicht = 5;
}
// Eine schwachsinnige Initialisierung
passagiere = 1;
}
public double maxG() {
/*
Das ist ein mehrzeiliger Kommentar
Hier auch noch
*/
double x = 17.1D;
x = Double.MAX_VALUE -10000D;
x = Double.MIN_VALUE;
if ( x == Double.POSITIVE_INFINITY) {
System.out.println("Oops. Unendlich");
}
return maximalesGewicht;
}
public void einsteigen() {
passagiere++;
}
public void einsteigen(int einsteiger) {
passagiere = passagiere + einsteiger;
}
/**
* aktuelles Gewicht des Flugzeug
* @return das aktuelle Gewicht in Kilogramm
*/
double gewicht() {
double ergebnis;
ergebnis = 1000 + passagiere * durchschnittsgewicht;
return ergebnis;
}
}
Klasse Flughafen
package block6;
/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
public static void main(String[] args) {
Flughafen pb;
pb = new Flughafen();
pb.name = "Paderborn";
Flugzeug lh1 = new Flugzeug("D-A123");
lh1.passagiere = 23;
Flugzeug lh2 = new Flugzeug("D-A456",3333.0D,100000D);
lh2.passagiere = 11;
pb.gate1 = lh1;
lh1.einsteigen();
lh1.einsteigen();
double meinGewicht = lh1.gewicht();
lh1.gewicht();
pb.gate2 = lh2;
lh2.einsteigen(88);
System.out.println("Mein Flughafen: " + pb.name);
System.out.println("Gate 1: " + pb.gate1.kennzeichen +
", Passagiere: " + pb.gate1.passagiere +
", akt. Gew.: " + pb.gate1.gewicht());
System.out.println("Gate 2: " + pb.gate2.kennzeichen +
", Passagiere: " + pb.gate2.passagiere);
if (pb.gate3 == null) {
System.out.println("Gate 3: leer");
} else {
System.out.println("Gate 3: " + pb.gate3.kennzeichen);
}
Flughafen fra;
fra = new Flughafen();
fra.name = "Rhein/Main";
}
}
- 9776 views
5.5 Lösungen
5.5 Lösungen5.5.1 Vektorrechnung
package s1.block5;public class Vektor {
private double x;
private double y;Vektor(double xx, double yy) {
x = xx;
y = yy;
normalisierung();
}public void normalisierung() {
double laenge = Math.sqrt(x*x+y*y);
x = x/laenge;
y = y/laenge;
}public void addition(Vektor v) {
x = v.x + x;
y = v.y + y;
normalisierung();
}
public void multi(double skalar) {
x = x * skalar;
y = y * skalar;
// normalisierung();
}
public void skalarprodukt(Vektor v) {
x = x * v.x;
y = y * v.y;
normalisierung();
}
public void drucken() {
System.out.println("(" + x + "," + y + ")");
}public static void main(String[] args) {
Vektor a = new Vektor(1.0, 2.0);
Vektor b = new Vektor(2.0, 1.0);
Vektor c = new Vektor(3.0, 3.0);a.drucken();
b.drucken();
c.drucken();a.addition(b);
a.drucken();
a.multi(10);
a.drucken();
b.skalarprodukt(c);
b.drucken();
}
}
5.5.2 Komplexe Zahlen
package s1.block5; public class Complex { private double re; // Realteil der Zahl private double im; // Imaginaerteil der Zahl (i) public Complex(double r, double i) { re = r; im = i; } public Complex add(Complex z) { Complex k = new Complex(re+z.re,im+z.im); return k; } public Complex sub(Complex z) { Complex k = new Complex(re-z.re,im-z.im); return k; } public Complex mul(Complex z) { Complex k = new Complex(re*z.re-im*z.im,re*z.im+im*z.re); return k; } public Complex div(Complex z) { Complex k = new Complex((re*z.re+im*z.im)/(z.im*z.im+z.im*z.im), (im*z.re-im*z.im)/(z.im*z.im+z.im*z.im)); return k; } public String toText() { String s = "(" + re + " + i" + im + ")"; return s; } }
5.5.3 Übung: Modellierung der Datenkapselung
Klasse FlugzeugTest.java
package s1.block5;public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
phase1(); // 9. Phase 1 testen
phase2(); // 14. Phase 2 testen
phase3(); // 15. Phase 3 testen
phase4();
}
public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT",360,191000,400000);
a380 = new Flugzeug("D-AIMD",560,286000,500000);
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}
public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i=0; i<300; i++) jumbo.einsteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
}
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU",360,191000,400000);
Flugzeug a380Neu = new Flugzeug("D-AIME",560,286000,500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i=0; i<300; i++) jumboAlt.einsteigen();
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i=0; i<100; i++) a380Neu.einsteigen();
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere()> 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}
public static void phase4() {
System.out.println(" Phase 4: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV",360,191000,400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW",360,191000,400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME",560,286000,500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i=0; i<50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i=0; i<560; i++) a380Defekt.einsteigen();
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen benutzt werden falls notwendig
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
while (a380Defekt.anzahlPassagiere()> 0) {
if (jumbo1.anzahlPassagiere() < jumbo1.passagierkapazität()) {
a380Defekt.aussteigen();
jumbo1.einsteigen();
}
else // Jumbo 1 is voll...
if (jumbo2.anzahlPassagiere() < jumbo2.passagierkapazität()) {
a380Defekt.aussteigen();
jumbo2.einsteigen();
}
else // Beide Jumbos sind voll
{
a380Defekt.aussteigen();
System.out.println("Ein Passagier bleibt zurück...");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
}
Klasse Flugzeug.java
package s1.block5;public class Flugzeug {
public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final static int PASSAGIERGEWICHT = 85;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("*****************************");
}
public int passagierkapazität() {return maxPassagiere;}
}
5.5.4 Lösung: Statische Klassenattribute
Klasse Flugzeug
Erweitert um die statische Variable
package s1.block5;
public class Flugzeug {public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}
public int passagierkapazität() {return maxPassagiere;}}
5.5.5 Überladene Methoden
Klasse Flugzeug
package s1.block5;
public class Flugzeug { public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette // 1. Privates Attribut zur Verwaltung der Passagierkapazität// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die einsteigen sollen
*/
public void einsteigen(int anzahl) {
if ((anzahl >0) && (passagiere+anzahl) <= maxPassagiere) {
passagiere+= anzahl;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die aussteigen sollen
*/
public void aussteigen(int anzahl) {
if ((anzahl >0) && (passagiere-anzahl) >=0) {
passagiere-= anzahl;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public static int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}
public int passagierkapazität() {return maxPassagiere;}}
Klasse FlugzeugTest
package s1.block5;public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
phase1(); // 9. Phase 1 testen
phase2(); // 14. Phase 2 testen
phase3(); // 15. Phase 3 testen
phase4();
phase3a();
phase4a();
}public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT", 360, 191000, 400000);
a380 = new Flugzeug("D-AIMD", 560, 286000, 500000);
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i = 0; i < 300; i++) {
jumbo.einsteigen();
}
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i = 0; i < 200; i++) {
jumbo.aussteigen();
}
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i = 0; i < 200; i++) {
jumbo.aussteigen();
}
jumbo.drucken();}
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU", 360, 191000, 400000);
Flugzeug a380Neu = new Flugzeug("D-AIME", 560, 286000, 500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i = 0; i < 300; i++) {
jumboAlt.einsteigen();
}
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i = 0; i < 100; i++) {
a380Neu.einsteigen();
}
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere() > 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}public static void phase4() {
System.out.println(" Phase 4: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV", 360, 191000, 400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW", 360, 191000, 400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME", 560, 286000, 500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i = 0; i < 50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i = 0; i < 560; i++) {
a380Defekt.einsteigen();
}
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
while (a380Defekt.anzahlPassagiere() > 0) {
if (jumbo1.anzahlPassagiere() < jumbo1.passagierkapazitaet()) {
a380Defekt.aussteigen();
jumbo1.einsteigen();
} else // Jumbo 1 is voll...
if (jumbo2.anzahlPassagiere() < jumbo2.passagierkapazitaet()) {
a380Defekt.aussteigen();
jumbo2.einsteigen();
} else // Beide Jumbos sind voll
{
a380Defekt.aussteigen();
System.out.println("Ein Passagier bleibt zurück...");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}public static void phase3a() {
System.out.println(" Phase 3a: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYA", 360, 191000, 400000);
Flugzeug a380Neu = new Flugzeug("D-AIMF", 560, 286000, 500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
jumboAlt.einsteigen(300);
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
a380Neu.einsteigen(100);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
int warteRaum = jumbo.anzahlPassagiere();
jumboAlt.aussteigen(jumbo.anzahlPassagiere());
// Zu diesem Zeitpunkt hat man ohne warteRaum die Passagiere des
// Jumbos vergessen!
a380Neu.einsteigen(warteRaum);
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}public static void phase4a() {
System.out.println(" Phase 4a: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYX", 360, 191000, 400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYY", 360, 191000, 400000);
Flugzeug a380Defekt = new Flugzeug("D-AIMG", 560, 286000, 500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
jumbo1.einsteigen(50);
jumbo2.einsteigen(50);
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
a380Defekt.einsteigen(560);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.// Alle aus Airbus in den Warteraum
int warteRaum = a380.anzahlPassagiere();
int freiJumbo1 = jumbo1.passagierkapazitaet() - jumbo1.anzahlPassagiere();
a380Defekt.aussteigen(a380.anzahlPassagiere());if (warteRaum <= freiJumbo1) { // Alle Passagiere passen in jumbo1
jumbo1.einsteigen(warteRaum);
} else { // Nur ein Teil der Passagiere kann mitgenommen werden
jumbo1.einsteigen(freiJumbo1); // Jumbo ist voll
warteRaum -= freiJumbo1; // warteRaum reduziert
// Nutzen des zweiten Jumbo...
int freiJumbo2 = jumbo2.passagierkapazitaet() - jumbo2.anzahlPassagiere();
if (warteRaum <= freiJumbo2) { // Alle Passagiere passen in jumbo2
jumbo2.einsteigen(warteRaum);
} else { // Nur ein Teil der Passagiere kann mitgenommen werden
jumbo2.einsteigen(freiJumbo2); // jumbo2 ist jetzt voll
warteRaum -= freiJumbo2; // warteRaum reduziert
System.out.println(warteRaum + " Passagiere können nicht umbebucht werden.");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
}
Flughafen
package s1.block5;
public class Flughafen {
String name;
public Flugzeug gate1;
Flugzeug gate2;
Flugzeug gate3;
public Flugzeug gate4;
Flugzeug gate5;
Flugzeug gate6;
double treibstoffLager;public static void main(String[] args) {
Flughafen pad = new Flughafen();
pad.name="Paderborn";
pad.treibstoffLager = 1000000;
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug("ABTL",450000,200,200);
lh1.einsteigen();double aktGewicht=lh1.gewicht();
System.out.println("gewicht" + aktGewicht);lh1.drucken();
pad.gate1 = lh1;
pad.gate2 = lh1;
System.out.println("Boarding für lh1 nach Mannheim...");
pad.gate1.einsteigen();
pad.gate1.einsteigen();
pad.gate2.einsteigen();
lh1.einsteigen();lh1.drucken();
// Airbus A380 https://de.wikipedia.org/wiki/Airbus_A380#A380-800
Flugzeug lh2 = new Flugzeug("ABTL",500000,100,200);lh2.einsteigen();
lh2.drucken();
pad.gate2 = lh2;
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1.kennzeichen);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");// Hänge Flugzeug um. mover bewegt Flugzeug
// von Gate 1 nach Gate 3Flugzeug mover = pad.gate1;
pad.gate1=null;System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");pad.gate3= mover;
mover=null;System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate1);
System.out.println("Am Gate 2: " + pad.gate2.kennzeichen);
System.out.println("Am Gate 3: " + pad.gate3.kennzeichen);
System.out.println("Treibstoff: " + pad.treibstoffLager);
System.out.println("***********************");
}
}
5.5.6 Fuhrparkmanagement
Klasse Kraftwagen
package s1.block5;public class Kraftwagen {
private double verkaufsPreis;
private double einkaufsPreis;
private String kennzeichen;public Kraftwagen(double ek, double vk, String nummernSchild) {
// Initialisierung der Variablen mit einfachen Konsistenzprüfungen
verkaufsPreis = (ek < vk) ? vk : ek;
einkaufsPreis = (ek < vk) ? ek : vk;
kennzeichen = nummernSchild;
}public double get_Epreis() {
return einkaufsPreis;
}public void set_Epreis(double pr) {
einkaufsPreis = pr;
}public double get_Vpreis() {
return verkaufsPreis;
}public void set_Vpreis(double pr) {
verkaufsPreis = pr;
}public String get_kennzeichen() {
return kennzeichen;
}public void set_kennzeichen(String s) {
kennzeichen = s;
}
public void drucken() {
System.out.println("Einkaufspreis: " + einkaufsPreis);
System.out.println("Verkaufspreis: " + verkaufsPreis);
System.out.println("Kennzeichen: " + kennzeichen);
}public static void Main (String args) {
}
}
Klasse Verkaeufer
package s1.block5;public class Verkaeufer {
private String name;
private double gewinn;
private double gebundeneMittel;
private Kraftwagen[] meineWagen;public Verkaeufer(String nam) {
meineWagen = new Kraftwagen[2];
name = nam;
gewinn = 0.0;
}public String get_name(){return name;}
public void neuerWagen(int pos, Kraftwagen w) {
if ((pos >= 0) && (pos < 2)) {
meineWagen[pos] = w;
}}
public Kraftwagen imBestand(int pos) {
if ((pos >= 0) && (pos < 2)) {
return meineWagen[pos];
} else {
return null;
}
}public Kraftwagen verkaufeFahrzeug(int pos, double erloes) {
Kraftwagen result;
if (erloes < meineWagen[pos].get_Vpreis()) {
System.out.println("Wagen mit Kennzeichen " +
meineWagen[pos].get_kennzeichen() +
" sollte unter Einkaufspreis verkauft werden.");
result = null;
} else {
gewinn += erloes-meineWagen[pos].get_Epreis();
result = meineWagen[pos];
meineWagen[pos] = null; // Wagen gehört nicht mehr zum Bestand
}
return result;
}public int fahrzeugeInFlotte() {
int result = 0;
int i = 0;
while (i < meineWagen.length) {
if (meineWagen[i] != null) {
result++;
}
i++;
}
return result;
}public double gebundeneMittel() {
double result = 0.0;
for (int i = 0; i < meineWagen.length; i++) {
if (meineWagen[i] != null) {
result += meineWagen[i].get_Epreis();
}
}
return result;
}public double geplanterUmsatz() {
double result = 0.0;
for (int i = 0; i < meineWagen.length; i++) {
if (meineWagen[i] != null) {
result += meineWagen[i].get_Vpreis();
}
}
return result;
}public double get_gewinn() {return gewinn;}
public void drucken() {
Kraftwagen w1 = imBestand(0);
Kraftwagen w2 = imBestand(1);System.out.println("<*** Verkäufername : " + name + " ***>");
System.out.println("Erlös bisher: " + gewinn);
System.out.println("Gebundene Mittel: " + gebundeneMittel());
System.out.println("Geplanter Umsatz: " + geplanterUmsatz());
System.out.println("Fahrzeuge in Flotte: " + fahrzeugeInFlotte());
if (w1 != null) w1.drucken();
if (w2 != null) w2.drucken();
System.out.println("<*********************************>");
}public static void main(String[] args) {
Kraftwagen w1 = new Kraftwagen(20000.00, 30000.00, "M-S 1234");
Kraftwagen w2 = new Kraftwagen(10000.00, 15000.00, "B-A 5678");
Kraftwagen w3 = new Kraftwagen(5000.00, 10000.00, "D-T 8901");Verkaeufer v1 = new Verkaeufer("Hans");
v1.neuerWagen(0, w1);
v1.neuerWagen(1, w2);
v1.drucken();Kraftwagen verkWagen = v1.verkaufeFahrzeug(0, 50000);
System.out.println("Verkaufter Wagen: " + verkWagen.get_kennzeichen());
v1.drucken();
}
}
Klasse Haendler
package s1.block5;
/**
*
* @author s@scalingbits.com
*/
public class Haendler {
private Verkaeufer[] team;
private String name;public Haendler(String n ) {
name = n;
team = new Verkaeufer[3];
}public void einstellenTeam(String name1, String name2, String name3) {
team[0] = new Verkaeufer(name1);
team[1] = new Verkaeufer(name1);
team[2] = new Verkaeufer(name1);
}
public void geschäftsEröffnung(Kraftwagen[] k) {
team[0].neuerWagen(0, k[0]);
team[1].neuerWagen(0, k[1]);
team[2].neuerWagen(0, k[2]);
team[0].neuerWagen(1, k[3]);
team[1].neuerWagen(1, k[4]);
team[2].neuerWagen(1, k[5]);
}public double gesamtGewinn() {
double g =0;
for (int i=0; i<team.length; i++) {
g += team[i].get_gewinn();
}
return g;
}public double gesamtWertFuhrpark() {
double g =0;
for (int i=0; i<team.length; i++) {
g += team[i].gebundeneMittel();
}
return g;
}public double geplanterUmsatz() {
double g =0;
for (int i=0; i<team.length; i++) {
g += team[i].geplanterUmsatz();
}
return g;
}public Verkaeufer getVerkaeufer(int pos) {
return team[pos];
}public Kraftwagen verkaufeFahrzeug(int indexVerk, int indexWagen, double preis) {
Verkaeufer v = team[indexVerk];
return v.verkaufeFahrzeug(indexWagen, preis);
}public void einstellenFahrzeug(int indexVerk, int indexWagen, Kraftwagen k) {
Verkaeufer v = team[indexVerk];
v.neuerWagen(indexWagen, k);
}public void drucken() {
System.out.println("*** Name Händler: " + name + " ***");
System.out.println("Gesamter Wert der Flotte: " + gesamtWertFuhrpark());
System.out.println("Gesamter Gewinn: " + gesamtGewinn());
for (int i=0; i< team.length; i++) team[i].drucken();
}public Verkaeufer besterVerkaeufer() {
Verkaeufer bestSeller = team[0];
for (int i=1; i<team.length; i++)
if (team[i].get_gewinn() > bestSeller.get_gewinn())
bestSeller = team[i];
return bestSeller;
}public static void main(String[] args){
Kraftwagen[] flotte= new Kraftwagen[8];
flotte[0] = new Kraftwagen(11111.11,22222.22,"A-A-11");
flotte[1] = new Kraftwagen(22222.22,22222.22,"B-A-22");
flotte[2] = new Kraftwagen(33333.33,44444.22,"C-A-33");
flotte[3] = new Kraftwagen(44444.44,50000.22,"D-A-44");
flotte[4] = new Kraftwagen(55555.55,60000.22,"E-A-55");
flotte[5] = new Kraftwagen(66666.66,88888.22,"F-A-66");
flotte[6] = new Kraftwagen(77777.77,80000.00,"G-A-77");
flotte[7] = new Kraftwagen(88888.88,90000.00,"G-A-88");Haendler h = new Haendler("Gebrauchtwagen-Schmidt");
h.einstellenTeam("Achim", "Bolle", "Caesar");
h.geschäftsEröffnung(flotte);
h.drucken();
h.verkaufeFahrzeug(0, 0, 40404.04);
h.einstellenFahrzeug(0, 0, flotte[7]);
Verkaeufer v = h.besterVerkaeufer();
System.out.println("Größter Gewinn von " + v.get_name());
h.drucken();}
}
5.5.7 Mehrere Konstruktoren
Klasse Flugzeug
package block6;
/**
* Eine Flugzeug mit Gewicht und Passagieren
* Am besten mit {@link block6.FlugzeugTest#main FlugzeugTest} testen
* @author sschneid
*/
public class Flugzeug {
public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
private int maxPassagiere;
private int passagiere;
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
private int maxgewicht;
public static final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
*
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* Konstruktur der Klasse Flugzeug
* Berechnet Passagierkapazität automatisch
* @param kennz Kennzeichen des Flugzeugs
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int leergew, int maxgew) {
this(kennz,(maxgew-leergew)/PASSAGIERGEWICHT,leergew,maxgew);
}
/**
* Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* @param anzahl Anzahl der Passagiere die einsteigen sollen
*/
public void einsteigen(int anzahl) {
if ((anzahl >0) && (passagiere+anzahl) <= maxPassagiere) {
passagiere+= anzahl;
}
}
/**
* Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* Entfernt Passagiere des aktuellen Flugzeugs
* @param anzahl Anzahl der Passagiere die aussteigen sollen
*/
public void aussteigen(int anzahl) {
if ((anzahl >0) && (passagiere-anzahl) >=0) {
passagiere-= anzahl;
}
}
/**
* Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public static int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}
}
- 8479 views
bei Lösung 5.43 haben Sie in
bei Lösung 5.43 haben Sie in der Klasse FlugzeugTest in der while-Schleife von Phase 4 "passagierkapazität" statt "passagierkapazitaet" stehen.
- Log in to post comments
5.6 Lernziele
5.6 Lernziele
. |
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu objekt orientierten Programmierung beantworten. Die Fragen benötigen zum Teil das Wissen der nachfolgenden Blöcke.
Feedback
- 4044 views
6. Referenzen, strukturierte Datentypen
6. Referenzen, strukturierte DatentypenObjektvariablen enthalten Referenzen auf Objekte. Sie unterscheiden sich von Variablen primitiver Datentypen in einer Reihe von Eigenschaften:
Variablen primitiver Datentypen | Objektvariablen | |
---|---|---|
Initialisierung | Standardwert des Typs; direkt benutzbar | Null Zeiger, Objekt muss separat erzeugt werden |
Exklusivität | Variable gehört exklusiv zum Objekt | referenziertes Objekt gehört nicht exklusiv zum umgebenden Objekt |
Lebensdauer | die des umgebenden Objekts | referenziertes Objekt existiert so lange es von irgendeiner Referenz referenziert wird |
Ausnahmebehandlung | nicht relevant | Der Versuch eine Methode oder Objektvariable mit einer Referenz auf null zu benutzen führt zu einer Ausnahme |
Parameterübergabe bei Methoden | Kopie wird angelegt | Objekt wird nicht kopiert. Nur Referenz wird kopiert |
Neue Objekte werden im Adressraum nur mit dem new() Operator angelegt. Referenzen speichern nur die eindeutige Identität unter der das Objekt zu erreichen ist. Die in der Referenz verwaltete eindeutige Objektidentität bleibt für die gesamte Lebensdauer des Objekts konstant. Sie ist eine logische Kennung. Das unten aufgeführte Programmbeispiel mit Diagramm ist jedoch nur als Gedankenmodell zu verstehen. Die konkrete Implementierung in einer virtuellen Java Maschine kann durchaus anders aussehen. Sie ist für den Javaentwickler transparent. Kraftwagen wagen1 = new Kraftwagen ("0-1"); Kraftwagen wagen2 = wagen1; Kraftwagen wagen3 = new Kraftwagen("0-2"); Das Referenzkonzept von Java ist die einzige Möglichkeit auf Objekte zuzugreifen. Das Referenzenkonzept von Java ist syntaktisch sehr ähnlich zu dem Pointerkonzept von C und C++. Es gibt jedoch wichtige Unterschiede mit signifikanten Auswirkungen auf die Stabilität der Anwendungen: |
Java | C/C++ | |
---|---|---|
Begriff | Referenz (Reference) | Zeiger (Pointer) |
Implementierung | abstrakter Datentyp (Klasse) | Speicherbereich |
direkter Speicherzugriff | nein | ja |
Typcheck zum Übersetzungszeitpunkt | ja | ja (Normalerweise) |
Zugriffskontrolle auf eine nicht initialisierte Referenz | Check mit eventuellem Wurf einer behandelbaren Ausnahme | kein Check auf Existenz oder Gültigkeit des Objekts |
Bemerkung: Der neue C++11 Standard bietet viele Verbesserungen dieser historisch bedingten Defizite von C/C++. Da C++ oft für Anwendungen deren Leistung sehr wichtig ist (Systemprogrammierung) benutzt wird, sind alle zusätzlichen Checks von C++11 immer nur optional.
- 10268 views
6.1 Die "null" und "this" Referenz
6.1 Die "null" und "this" Referenz6.1.1 Die "null" Referenz
Java verfügt über eine "null" Konstante mit deren Wert man Referenzen belegen kann um:
- ein Objekt zu dereferenzieren weil die Anwendungslogik dies erfordert. Das dereferenzierte Objekt wird (irgendwann) gelöscht nachdem niemand mehr auf es zeigt
- Referenzen zu initialisieren.
Im folgenden Beispiel ist zu sehen was geschieht wenn wagen2 und wagen3 mit der null Referenz belegt werden: package s1.block6.reference; public class Main { public static void main(String[] args) { Kraftwagen wagen1 = new Kraftwagen("0-1"); Kraftwagen wagen2 = wagen1; Kraftwagen wagen3 = new Kraftwagen("0-2"); wagen2 = null; wagen3 = null; } } Der Wagen"0-2" wird ein Kandidat zum Löschen. Der Wagen "0-1" wird noch über die Referenz wagen1 referenziert. Enthält eine Referenz eine Null Variable so zeigt sie nicht mehr auf ein Objekt und man kann nicht mehr die Methoden und Datenfelder einer Instanz aufrufen. Mit static deklarierte Methoden und Attribute sind hiervon ausgenommen. |
Das Javalaufzeitsystem wird in diesem Fall eine NullPointerException werfen und das Programm beenden. Das folgende Beispiel zeigt den Fall einer unbehandelten Ausnahme sowie die Behandlung einer Ausnahme:
package s1.block6.reference; public class Main { public static void main(String[] args) { Kraftwagen wagen1 = new Kraftwagen("O-1"); Kraftwagen wagen2 = wagen1; Kraftwagen wagen3 = new Kraftwagen("O-2"); wagen2 = null; wagen3 = null; try{ System.out.println(wagen2.getKennzeichen()); // Erste Ausnahme } catch (NullPointerException e) { System.out.println("Variable wagen2 ist eine Null Referenz"); } System.out.println(wagen3.getKennzeichen()); // Zweite Ausnahme //Das Programm wurde wegen der unbehandelten Ausnahme beendet // Die folgende Zeile nicht nicht mehr erreicht System.out.println("Diese Zeile des Programms wird nicht erreicht.."); } }
Das Programm wird bei der Ausführung die folgenden Ausgaben auf der Konsole erzeugen;
Variable wagen2 ist eine Null Referenz Exception in thread "main" java.lang.NullPointerException at s1.block6.reference.Main.main(Main.java:20)
Die erste Ausnahme, der Aufruf von wagen2.getKennzeichen() wird mit mit einem try-catch Block aufgefangen. Nach der Behandlung im catch-Block wird die Methode weiter ausgeführt.
Der Aufruf von wagen3.getKennzeichen() wird nicht aufgefangen und führt zu einer Ausnahme in Zeile 20 die das Programm beendet. Ausnahmen und Ausnahmebehandlungen werden in einem späteren Abschnitt, im Kurs 2 behandelt.
6.1.2 Die Eigenreferenz mit dem Schlüsselwort this
Es gibt Fälle in denen man als Parameter einer aufzurufenden Methode einer anderen Klasse einen Zeiger auf die aktuelle Instanz, in deren Methoden man gerade arbeitet, mitgeben möchte. Hierzu dient das Schlüsselwort this. Es kann benutzt werden um auf die aktuelle Instanz zu zeigen, solange man sich nicht in einer static Methode befindet. static Methoden können auch ohne eine existierende Instanz benutzt werden. Eine Objektinstanz existiert daher nicht unbedingt.
Beispiel:
class Person { ... public void sitzPlatzZuweisen (Reservierung r) { ... r.buchung(this); ... } } ... // Beliebige andere Klasse Person p = new Person(); Reservierung buchung; buchung = new Reservierung("LH454","17B"); // Zeitpunkt 1 p.sitzPlatzZuweisen(buchung); // Zeitpunkt 2 ... |
- 8863 views
Schreibfehler?
Müsste es ganz oben über dem ersten grauen Kasten bei der null- Referenz nicht heißen "...wenn wagen2 und wagen3 mit der null- referenz belegt werden"?
- Log in to post comments
6.2 Heap-Größe und Freigabe von Speicher (Garbage Collection)
6.2 Heap-Größe und Freigabe von Speicher (Garbage Collection)Die Java Objekte werden innerhalb des Javaprozesses in einem Speicherbereich mit dem Namen "Java Heap" verwaltet. In diesem Speicherbereich werden alle Datenstrukturen mit einer nicht festen Größe verwaltet. Diese Datenstrukturen sind:
- Objekte (Instanzen von Klassen)
- Felder von Basistypen oder Objekten
- Zeichenketten (Sonderfall da Zeichenketten nicht mit dem new Operator angelegt werden müssen)
Dieser Bereich
- hat eine initiale Größe
- kann bis zu einem gegeben Limit wachsen
- zwingt die Java Laufzeitumgebung eine OutOfMemory Ausnahme zu werfen wenn der Speicherbereich voll gelaufen ist.
Java VM Optionen
Optionen zum Heapmanagement(siehe auch Java Optionen (Referenzdokumentation)
- initiale Heapgröße -Xms
- maximale Heapgröße -Xmx
- Protokollieren von Garbagekollektorläufen:-Xlog:gc
Beispiel: Starten einer Anwendung Main.class mit 500 Megabytes initialem Heap (Freispeicher), 800 Megabytes maximalem Heap und Protokollierung von Garbagekollektorläufen:
java -Xms500m -Xmx800m -Xlog:gc Main
Nicht mehr referenzierte Objekte werden von der JavaVM automatisch, im Hintergrund von einem "Garbage Collector" gelöscht.
Dieser "Garbage Collector" (GC) kann manuell getriggert werden. Dies sollte man aber nicht in einem produktiven Programm durchführen. Der explizit angestossene GC bringt die Anwendung während seiner Arbeit zum Stehen! Automatisch ausgeführte GCs bewirken dies (normalerweise) nicht. Das explizite Anstoßen geschieht mit der statischem Methode System.gc(). Man kann sie wie folgt aufrufen:
System.gc();
Die Konsolenausgabe eines Programms welches mit der Option -Xlog:gc sieht wie folgt aus:
[GC 38835K->38906K(63936K), 0.1601889 secs] [GC 39175K(63936K), 0.0050223 secs] [GC 52090K->52122K(65856K), 0.1452102 secs] [GC 65306K->65266K(79040K), 0.1433074 secs]
Sie zeigt an wieviel Speicher die Objekte von der "Garbage Collection" vorher und nachher belegen. Die benötigte Zeit wird ebenfalls angezeigt.
Verwalten von Objekten im Freispeicher (Heap)
Der Freispeicher (Heap) kann je nach Konfiguration eine konstante Größe haben oder er kann dynamisch bis zu einer vorgegebenen maximalen Größe wachsen. Der Entwickler ist daran interessiert, dass seine Anwendung alle benötigten Objekte im Freispeicher (Heap) verwalten kann. Kann das Javalaufzeitsystem keine neuen Objekte mehr anlegen wird es die Anwendung mit einer OutOfMemoryError Ausnahme beenden.
Dies wird normalerweise durch den Garbage-Collector der automatisch alle Objekte löscht die nicht mehr referenziert werden vermieden.
Nicht referenzierte Objekte |
Nicht referenzierte Objekte können weder von der Anwendung noch vom Javalaufzeitsystem mit Hilfe von Referenzen erreicht werden. Dies bedeutet es gibt keine Kette von Referenzen zu einem Objekt die an den folgenden Orten beginnt:
Nicht referenzierte Objekte werden vom Javalaufzeitsystem bei Bedarf zu einem beliebigen Zeitpunkt gelöscht. Der genaue Zeitpunkt des Löschens ist nicht für den Entwickler vorhersehbar. |
Beispiel
Im folgenden Programm werden eine Reihe von Personen erzeugt, die über eine Vater und Mutterbeziehung aufeinander referenzieren können.
// Zeitpunkt 1
// Zeitpunkt 2
// Zeitpunkt 3
Das Hauptprogramm main() erzeugt zwei Instanzen der Klasse Person und ruft dann die Methode aufruf() auf die ein kleines Feld und einige weitere Instanzen erzeugt.
Hinweis: Im folgenden Beispiel wird ein Feld verwendet. Javafelder werden im Detail im folgenden Kapitel erklärt.
Zum Zeitpunkt 1 sind in der main() Methode die folgenden Objekte erzeugt:
Zum Zeitpunkt 2 wurde in der main() Methode bereits die Vater-Referenz und der ursprüngliche Zeiger p2 mit null dereferenziert.
Zum Zeitpunkt 3 wurde die Methode aufruf() aufgerufen und das Feld persFeld mit zwei Feldern auf dem Heap angelegt.
Zum Zeitpunkt 4 sind drei weitere Personen angelegt worden. Die drei neuen Personen sind über den Index 0 von der Variablen persFeld erreichbar.
Zu diesem Zeitpunkt können alle Objekte auf dem Heap direkt, oder indirekt von Datenstrukturen auf dem Stapel (stack) erreicht werden.
Zum Zeitpunkt 5 wurde die Methode aufruf() bereits verlassen. Die lokalen Variablen der Methode aufruf() wurden vom Stack gelöscht und stehen nicht mehr zur Verfügung.
Hierdurch können eine Reihe von Instanzen der Klasse Person und das Feld auf dem Heap (Freispeicher) nicht mehr erreicht werden:
Die nicht mehr erreichbaren Objekte sind Müll (Garbarge) geworden und belegen verfügbaren Speicherplatz. Sie werden bei Bedarf vom Garbage-Collector (GC) gelöscht. Der Garbage-Collector wird alle Objekte löschen die vom Stack und statischen Variablen nicht mehr erreichbar sind. Dies hat für die Anwendung keine Implikationen da die Objekte auch von der Anwendung nicht mehr erreichbar sind.
"Memory Leak"(engl. wikipedia)
Objekte die versehentlich bzw. ungewollt referenziert werden können nicht gelöscht werden. Diese Objekte können nach und nach den Heap füllen und zu einem Programmabbruch mangels Hauptspeicher (OutOfMemoryError Ausnahme) führen. Diesen Zustand der früher oder später zum ungewollten Abbruch eines Programms führen kann, nennt man im englischen "Memory Leak" (Speicherleck). Da man über die Zeit nutzbaren Speicher verliert wie ein Tank Wasser durch ein Leck verlieren kann.
Modifiziertes Beispiel (Memory Leak)
Ein Speicherleck kann durch eine minimale Änderung im vorhergehenden Beispiel entstehen. Gibt die Methode aufruf() als Ergebnis einen Zeiger auf das Feld von Personen zurück werden die drei Personen und das Feld nicht dereferenziert.
Zum Zeitpunkt 6 sieht Objektmodell noch aus wie im vorhergehenden Beispiel:
Durch die Rückgabe der Referenz auf das Feld beim Beenden der Methode aufruf(), ist das Feld und die drei Objekte noch vom Stack erreichbar:
Die Variable f in main() referenziert das Feld. Das Feld wiederum referenziert 3 weitere Objekte.
Hier liegt ein Speicherleck nur vor, wenn der Entwickler nicht davon ausgeht, dass das Feld und alle referenzierten Objekte noch erreichbar sind. Ein Speicherleck ist kein Problem des Laufzeitsystems, da das Laufzeitsystem nicht zwischen noch benötigten und nicht mehr benötigten Objekten unterscheiden kann.
Dieses Problem wird vom Javaentwickler durch das Dereferenzieren von Objekten vermieden.
Dereferenzieren von Objekten
Der Javaentwickler muss nicht (und kann nicht) wie in anderen Programmiersprachen nicht mehr benötigte Objekte selbst löschen. Es verbleibt jedoch die Aufgabe sicherzustellen, dass nicht mehr benötigte Objekte nicht mehr referenziert werden um ein vollaufen des Heap zu vermeiden.
Implizites Dereferenzieren
Zeigt eine lokale Variable auf ein Objekt, so verschwindet die Referenz auf das Objekt mit dem Verlassen des Blocks in dem die lokale Referenzvariable definiert war.
Im der unten aufgeführten Methode warePrüfen() gibt es zwei Referenzvariablen w und w1 die auf eine Instanz einer Ware zeigen
public void warePrüfen(Ware w) { Ware w1 = w; .... }
Beim Aufruf dieser Methode erhöht sich die Anzahl der Referenzen auf eine bestimmte Instanz der Klasse Ware um zwei Referenzen. Da die beiden Variablen aber am Ende des Blocks beim Verlassen der Methode wieder gelöscht werden erniedrigt sich die Anzahl der Referenzen auf ein gegebenes Objekt um zwei.
"Memory Leaks" entstehen daher typischerweise nicht durch lokale Variablen. Werden Methoden jedoch sehr spät verlassen (main() Methode!) werden auch die entsprechenden lokalen Referenzvariablen erst sehr spät gelöscht.
Beispiel
Die erste Variante des Beispiels in dem das Feld auf Personen nur innerhalb des Methodenblocks verwendet wurde ist ein Fall von impliziten Dereferenzieren
Explizites Dereferenzieren
Entwickler sollten Referenzvariablen auf Objekte explizit mit dem null belegen wenn Sie wissen, dass ein Objekt sicher nicht mehr benötigt wird.
o.ref = a-reference; ... o.ref = null;
Das Derefenzieren einer Referenzvariable und den null Wert ist insbesondere wichtig wenn die Variable ein Attribut einer Klasse ist. Die Lebensdauer des Objekts o ist nicht unbedingt ersichtlich für den Entwickler. Das Setzen der Referenzvariablen o.ref auf null gewährleistet, dass das Objekt auf das mit der Variable a-reference gezeigt wird bei Bedarf gelöscht werden kann.
Beispiel
Das Speicherleck im zweiten Beispiel kann durch zwei verschiedene Änderungen vermieden werden:
1. Möglichkeit: Dereferenzieren der Variable persFeld in der Methode aufruf()
public static Person[] aufruf(Person p) { Person[] persFeld = new Person[2]; persFeld[1] = p; persFeld[0] = new Person(); persFeld[0].vater = new Person(); persFeld[0].mutter = new Person(); // Zeitpunkt 1 persFeld = null; return persFeld; }
2. Möglichkeit: Dereferenzieren der Variable f in der main() Methode:
public static void main (String[] args ) { Person p1 = new Person(); Person p2 = new Person(); p1.vater = p2; p2 = null; Person[] f = aufruf(p1); //Zeitpunkt 2 f = null; }
- 22474 views
6.3 Kopieren von Objekten
6.3 Kopieren von ObjektenObjekte kopieren ist nicht trivial. Die folgende Java-Anweisung
Person p1; Person p2; p1 = new Person("John", "Doe"); p2=p1;
dupliziert nicht das erzeugte Objekt, es dupliziert nur die Referenz auf das gleiche Objekt. Dies bedeutet, dass man das Objekt über p1 sowie über p2 erreichen und modifizieren kann.
Das duplizieren von Objekten muss vom Entwickler explizit implementiert werden. Dies geschieht typischer in einem eigen Konstruktor dem "Copy Construktor". Der typische "Copy Constructor" wird wie folgt implementiert:
- Der Eingabeparameter für den Konstruktor ist das zu duplizierende Objekt
- Alle Attribute des zu kopierenden Objekts werden dupliziert
Das Vorgehen beim Kopieren der Objektvariablen obliegt dem Implementierer. Er hat hier zwei Möglichkeiten mit verschiedener Semantik:
- Die Referenz wird umkopiert: Das geklonte Objekt zeigt auf die gleichen Objekte wie das ursprüngliche Objekt. Die referenzierten Objekte werden geteilt.
- Das referenzierte Objekt wird ebenfalls geklont. Jetzt hat das neue Objekt einen exklusiven Satz von Objekten. Hierbei ist zu beachten, dass man bei diesem Vorgang keinen endlos rekursiven Kopiervorgang anstößt.
Das rekursive Kopieren von Objekten wird im Englischen auch als "deep copy" bezeichnet.
Das einfache wiederbenutzen der Objekte ist im folgenden Beispiel implementiert:
public class Person { Person vater; Person mutter; String name; ... public Person (Person orig) { vater = orig.vater; mutter = orig.mutter; name = orig.name; } public Person(String n){ name = n; } } ... Person p1 = new Person("John Doe"); p1.mutter = new Person("mum"); p1.vater = new Person("dad"); Person p2 = new Person(p1); |
Das Kopieren einer Person "John Doe" ohne das Duplizieren der referenzierten Objekte führt zum folgenden Objektmodell: |
Das tiefe, rekursive Klonen (Kopieren) wird wie folgt implementiert: public class Person { Person vater; Person mutter; String name; ... public Person (Person orig) { if (orig.vater != null) vater = new Person(orig.vater); if (orig.mutter != null) mutter = new Person(orig.mutter); name = orig.name; } public Person(String n){ name = n; } } ... Person p1 = new Person("John Doe"); p1.mutter = new Person("mum"); p1.vater = new Person("dad"); Person p2 = new Person(p1); |
- 14548 views
6.4 Vergleiche zwischen Objektvariablen
6.4 Vergleiche zwischen ObjektvariablenIn Java muss man bei Vergleichen die folgenden, unterschiedlichen Fälle unterscheiden:
- Vergleiche von primitiven Typen
- Vergleiche von Objekten
- Vergleich auf Identität (gleiche Objektinstanz)
- Vergleich auf Inhalt (enthalten zwei Instanzen die gleichen Informationen)
Vergleiche mit den == und != Operatoren
Bei primitiven Typen ist der Vergleich recht einfach
int i = 1; int j = 2; if (i ==j) ...
Beim Vergleich von Objekten ist hier jedoch Vorsicht geboten. Beispiel:
class Person { String name; public Person (String nameparam) { ...} } ... Person p1 = new Person ("John Doe"); if (p1 == "John Doe") ...
p1 ist eine Referenz auf ein Objekt. p1 wird hier direkt mit dem Zeichenliteral "John Doe" verglichen. Dieser Vergleich ist erlaubt, er führt jedoch nicht zum gewünschten Ergebnis. Die Zeichenkette "John Doe" hat einen anderen Typ und ein anderes Objekt als p1.
Vergleiche mit der equals() Methode
In Java erben alle Klassen die Methoden der Basisklasse Object. Alle Klassen erben eine Implementierung der equals() Methode von der Klasse Object.
Diese erlaubt das vergleichen von Objekten wie im folgenden Beispiel
Person p1; Person p2; ... if (p1.equals(p2)) ...
Die Methode equals()
- kann auf mit null belegte Referenzen aufgerufen werden (Das Ergebnis ist immer false)
- vergleicht zuerst auf Identität
- vergleicht in der vererbten Implementierung der Klasse Object nur auf Identität
- kann für jede eigene Klasse selbst mit einer komplexen Logik implementiert werden
Vorsicht: Implementiert man diese Methode selbst, dann sollte auch die Methode hashCode() selbst implementiert (überschrieben) werden. Sie wird von vielen Hilfsklassen im Zusammenhang mit equals() gleichzeitig benutzt.
Beispiel:
class Person { String name; ... public boolean equals(Object obj) { boolean result; Person target = (Person) obj; result = (name.equals(target.name)); return result; } }
- 13379 views
6.5 Das Schlüsselwort "final" und Objektvariablen
6.5 Das Schlüsselwort "final" und ObjektvariablenDas Schlüsselwort final erzwingt man, dass Variablen nur genau einmal belegt werden dürfen (Siehe Javaspezifikation).
Variablen dürfen dann später nicht mehr modifiziert werden.
Siehe Beispiel:
public class Person { ... final String name; ... final Adresse geburtsort;
Das Attribut name ist eine Zeichenkette. Man kann genau einmal eine Zeichenkette zuweisen. Eine spätere Änderung ist nicht mehr möglich.
Der Modifizierer final (englisch modifier) hat bei Referenztypen (hier die Klasse Adresse) eine besondere Wirkung:
- Man kann auf genau ein Objekt referenzieren
- Man kann nicht später auf ein anderes Objekt referenzieren
- Man kann jedoch die Attribute des referenzierten Objekts gemäß seiner Implementierung verändern!
- 12300 views
6.6 Zeichenketten (Strings)
6.6 Zeichenketten (Strings)Zeichenketten werden in Java mit der Klasse String verwaltet. Sie sind Objekte.
Sie wurden bisher wie die Basistypen Character, Integer oder Float behandelt. Zeichenketten werden jedoch ähnlich wie Referenzen behandelt da Zeichenketten unterschiedlich lang sein können. Man kann Zeichenketten daher nicht die Zahlentypen mit fester Größe einfach in den Speicherbereich eines Objekts einbetten.
Zeichenketten werden bei Bedarf im Hauptspeicher (Heap) angelegt und referenziert. Die Referenz auf die Zeichenkette selbst hat eine konstante Größe.
Zeichenketten können mit einer direkten Zuweisung erzeugt werden. Sie können jedoch auch gleichwertig mit dem new() Operator und dem Konstruktor der Klasse String angelegt werden. Beide Varianten sind nur in der Syntax unterschiedlich. Der einzige Unterschied besteht darin, dass das Objekt "John" schon beim Laden der Klasse angelegt wird. Das Objekt "Doe" wird erst beim Aufruf des Befehls angelegt.
String a = "John"; String b = new String("Doe"); |
a sowie b sind Objektreferenzen nicht aber der direkte Datenbehälter!
Zeichenketten(Strings) können einfach mit dem plus "+" Operator verkettet werden. Der plus Operator konvertiert automatisch andere Basistypen in Zeichenketten bevor die neue verkettete Zeichenkette zugewiesen wird:
String a = "Das Ergebnis ist "; double pi = 3.1415; String b = "***"; String c = a + pi + b;
Tipp: Die Klasse String verfügt über eine ganze Reihe von hilfreichen Methoden. Hier eine Auswahl:
- (static) valueOf(beliebiger Typ): Konvertierung eines beliebigen Typs (Basistypen, Objekte, Felder etc.) in eine Zeichenkette
- length(): Länge der Zeichenkette
- toLowerCase(): Umwandlung in Kleinbuchstaben
- equalsIgnoreCase(String s): Vergleicht eine gegebene Zeichenkette mit einer Zeichenkette s ohne Groß- und Kleinschreibung zubeachten
Zeichenketten werden von der virtuellen Maschine als unveränderbare ("immutable") Objekte gehandhabt. Sie können daher nicht überschrieben oder verändert werden. Die Implikationen können am folgenden Beispiel gezeigt werden.
Schritt 1: Anlegen der Zeichenketten
String a = "John"; String b = a; String c = " Doe"; |
Schritt 2: Verkettung und Objektallokation
Eine Verkettung der Zeichenketten mit dem plus Operator ergibt das folgende Speicherabbild c = a + c; Die Variable c zeigt jetzt auf ein neues Objekt. Die Zeichenkette " Doe" ist eventuell nicht mehr referenziert und wird dann vielleicht irgendwann gelöscht. Bei der oben gezeigten Verkettung entstehen typischerweise sehr viele neue Objekte und es werden sehr viele Stringobjekte zum Löschen freigesetzt! |
Vergleich
Da ein Objekt vom Typ String eine Referenz auf eine Zeichenkette ist, muss man beim Vergleich von Zeichenketten unterscheiden, ob man die Referenz oder den Inhalt der Zeichenkette vergleichen möchte um Fehler zu vermeiden.
Der Javaübersetzer und das Javalaufzeitsystem speichern Literale wenn möglich nur einmal im Hauptspeicher. Konstante Literale die man zur Übersetzungszeit erkennen kann werden in Referenzen auf ein einziges Objekt zusammengefasst. Dynamisch erzeugte Zeichenketten werden in eigenen Objekten verwaltet. Die genaue Semantik ist in der Java Sprachspezifikation (3.te Version von 2005, Paragraph 3.10.5 String Literals) beschrieben.
Wichtig: Identische Zeichenketten haben nicht unbedingt die gleiche Objektidentität. Ein Vergleich mit Hilfe der Objektidentität ist daher im Normalfall nicht ratsam.
String a = "John"; String b = a; String c = "John"; String d = "Jo"+"hn"; // Der konstante Ausdruck // wird schon bei der Übersetzung aufgelöst! String e = new String("John"); |
das Speicherabbild zum Quellcode links: |
Ein Vergleich mit dem == Operator vergleicht nur die Adressen der referenzierten Objekte nicht aber deren Inhalt. Zum Vergleich der Inhalte wird die .equals() Methode benötigt. Siehe
if (a == b) System.out.println(" a und b zeigen auf das gleiche Objekt"); else System.out.println(" a und b zeigen nicht auf das gleiche Objekt"); if (a == c) System.out.println(" a und c zeigen auf das gleiche Objekt"); else System.out.println(" a und c zeigen nicht auf das gleiche Objekt"); if (a == d) System.out.println(" a und d zeigen auf das gleiche Objekt"); else System.out.println(" a und d zeigen nicht auf das gleiche Objekt"); if (a == e) System.out.println(" a und e zeigen auf das gleiche Objekt"); else System.out.println(" a und e zeigen nicht auf das gleiche Objekt"); if (a.equals(b)) System.out.println(" a und b sind gleiche Zeichenketten"); else System.out.println(" a und b sind nicht gleiche Zeichenketten"); if (a.equals(c)) System.out.println(" a und c sind gleiche Zeichenketten"); else System.out.println(" a und c sind nicht gleiche Zeichenketten"); if (a.equals(d)) System.out.println(" a und d sind gleiche Zeichenketten"); else System.out.println(" a und d sind nicht gleiche Zeichenketten"); if (a.equals(e)) System.out.println(" a und e sind gleiche Zeichenketten"); else System.out.println(" a und e sind nicht gleiche Zeichenketten");
Führt zum Ergebnis:
a und b zeigen auf das gleiche Objekt a und c zeigen auf das gleiche Objekt a und d zeigen auf das gleiche Objekt a und e zeigen nicht auf das gleiche Objekt a und b sind gleiche Zeichenketten a und c sind gleiche Zeichenketten a und d sind gleiche Zeichenketten a und e sind gleiche Zeichenketten
Zeichenkettenverwaltung im "Stringpool"
Der javac Übersetzer erkennt Literale schon beim Übersetzen einer Klasse und speichert sie nur einmal in der .class Datei mit dem Binärcode. Zur Laufzeit eines Javaprogramms wird die entsprechende Klasse bei der ersten Benutzung dynamisch geladen und die konstanten Zeichenketten (Literale) zu einem "Stringpool" hinzugefügt, wenn sie vorher noch nicht im Stringpool existierten. Dieses Verfahren minimiert den Speicherverbrauch und steigert den Durchsatz da Zeichenketten, in der Regel, kleine Objekte sind, die sonst den Freispeicher (Heap) dauerhaft belegen und fragmentieren.
Der Stringpool ist ein spezialisierter Bereich des Freispeichers. Objekte im allgemeinen Freispeicher und im Stringpool verhalten sich gleich. Der einzige Unterschied besteht darin, dass alle Literale im Stringpool nur genau einmal vorkommen.
Wird eine Zeichenkette dynamisch mit new String() angelegt so wird sie wie ein normales Objekt auf dem Freispeicher (Heap) angelegt und nicht im Stringpool. Die Klasse String hat jedoch eine Methode String.intern() die immer auf das kanonisierte Literal im Stringpool zeigt.
Der Zusammenhang zwischen Stringobjekten im Freispeicher wird durch das folgende Beispielprogramm deutlich:
String a = new String("Test"); String b = a.intern(); String c = new String("Test"); String d = c.intern(); String e = "Test"; if (a == c) System.out.println ("a,c sind die gleichen Objekte"); if (b == d) System.out.println ("b,d sind die gleichen Objekte"+ " im Stringpool"); if (e == d) System.out.println ("e,d sind die gleichen Objekte"+ " im Stringpool"); Ausgabe: b,d sind die gleichen Objekte im Stringpool d,e sind die gleichen Objekte im Stringpool |
Effiziente Zeichenverwaltung mit der Klasse Stringbuffer
Die Klasse StringBuffer dient dem effizienten Handhaben von Zeichenketten. Ihre wesentlichen Eigenschaften sind:
- "Threadsave" beim parallelen Ausführen von Threads in einem Javaprogramm werden Zeichenketten nicht versehentlich zerstört. Die Klasse ist hiermit sicher jedoch langsamer als eine nicht threadsichere Klasse (aber immer noch schneller als die Klasse String!)
- Die Zeichenketten sind veränderbar
Wichtige Methoden Methoden der Klasse Stringbuffer sind:
- append(beliebiger Typ): Fügt einen beliebigen Typ in Form einer Zeichenkette am Ende an
- insert(offset, beliebiger Typ): Fügt einen beliebigen Typ an der Stelle offset ein.
Die Klasse StringBuffer verfügt über viele weitere Methoden zur Manipulation von Zeichenketten die in der API Dokumentation beschrieben werden.
Tipp: Seit JDK 5.0 steht die Klasse StringBuilder für Anwendungen die extreme Leistung benötigen zur Verfügung. Sie bietet jedoch keine Synchronisation zwischen Threads.
- 8081 views
Schwer verständlich ausgedrückt
Bitte erläutern Sie die Stelle mit "[...]dynamisch geladen[...]" sowie die Stelle mit [...]auf das kanonisierte Literal [...].
Was bedeutet dynamisch geladen? Was hat es mit dem kanonisierten Literal auf sich, also was heißt kanonisiert?
Können Sie die Methode String.intern() genauer erklären bitte?
Danke im Voraus und lG
- Log in to post comments
Versuch einer informellen Erklärung
Motivation für diese Optimierung
- Zeichenketten wind wahrscheinlich die häufigsten Objekte auf dem Heap.
- Sie werden wahrscheinlich oft dereferenziert.
- Sie sind in der Regel kleine Objekte
- Sie müssen vom Garbagekollektor identifiziert und geköscht werden
Die Kosten zum Löschen sind also in Relation zum gewonnenen Speicherplatz hoch. Man versucht ausserdem viele dieser konstanten Objekte in einem gemeinsamen Speicherbereich eng zu packen. Dadurch wird das cachen von Hauptspeicherseiten im Prozessor effizienter.
Erste Überlegung:
- Strings sind immer nicht veränderliche Javaobjekte. Das bedeutet: Es kann mehrere Objekte geben, die den gleichen Inhalt besitzen. Java arbeitet immer mit Zeigern auf diese Objekte. Da die Objekte alle unveränderbar sind, könnten auch alle Referenzen auf das gleiche Objekt zeigen, das würde Platz sparen und Verwaltungsaufwand (gc!) sparen.
Die zweite Überlegung: Wann werden dem Laufzeitsystem Zeichenketten bekannt gemacht:
- Beim Programmieren im Quellcode: Hier werden viele Zeichenketten "als Konstanten" (das war eine konstantes Javaliteral) deklariert. Alle diese Zeichenketten können schon vom Übersetzer (javac) so zusammengefasst werden, dass in jeder *.class Datei jede Zeichenkette die in der Klasse verwendet wird nur noch einmal vorkommen muß. Alle Benutzer in der Klasse können auf die gleiche Zeichenkette zeigen. Beim Starten eines Javaprogramms werden alle *.class Dateien gelesen. Jetzt werden nochmals alle statischen und schon bekannten Zeichenketten in einem Stringpool konsolidert. Alle Zeichenketten die schon im Quellcode bekannt waren werden jetzt genau einmal (kanonisch) gespeichert und als Objekte angelegt.
- Es gibt aber auch Zeichenketten die zur Laufzeit des Programms dynamisch angelegt werden müssen. Ein Webserver muss zum Beispiel die URL "http://www.scalingbits.com/about" als Zeichenkette dynamisch anlegen können um eine entrsprechende Seite auszuliefern. Diese Zeichenketten werden (nicht unbedingt) im Stringpool verwaltet. Sie werden wie normale Objekte verwaltet. Man kann eine Kopie eines solchen Objekts aber mit der Methode .intern() in den Stringpool befördern und sich die Referenz zurückgeben lassen.
- Log in to post comments
6.7 Übungen
6.7 Übungen
6.7.1 Referenzieren, Dereferenzieren und GCImplementieren Sie die Klasse Person mit folgenden Eigenschaften:
|
Hauptprogramm mit folgenden Eigenschaften:
|
Testen des Programms mit den Optionen -Xms -Xmx -Xlog:gc
Beispiel:
java -Xms64m -Xmx256m -Xlog:gc block7.gc.Main
Dies bewirkt das Starten einer Javaanwendung mit
- 64 Megabyte initialem Freispeicher (-Xms64m)
- 256 Megabyte maximalem Freispeicher (-Xmx256m)
- Protokollieren auftretender Garbagecollections (-Xlog:gc)
Allgemeine Überlegungen:
- Wie kann man erzwingen das die erste Person und Ihre Vorfahren zum Löschen freigegeben werden?
- Fügen die unten aufgeführte Kontrollmethode zum Auslesen der Speicherdaten an interessanten Stellen aus.
Tipp: Benutzen die folgende Beispielmethode zum Auslesen des Speicherverbrauchs.
Aufgaben
Variieren Sie den initialen, maximalen Speicher sowie die Anzahl der Generationen um
- Implizite GCs zu provozieren, zu vermeiden
- Eine OutOfMemory Ausnahme zu provozieren
- Erhöhen Sie Ihren Speicherverbrauch bis Sie an die Grenzen Ihres Systems kommen. Vorsicht! Nähern Sie sich Ihren Systemgrenzen in kleinen Schritten. Ihr System kann für eine gewisse Zeit durch Überlastung unbenutzbar werden!
Ein Testprogramm
Anbei ein einfaches Testprogramm welches eine Person ohne Vorfahren erzeugt und dann in einer Schleife neue Personen mit immer mehr Vorfahren erzeugt.
Die Konstante mit den Generationen kann bis zu einem Wert von etwa 20 erhöht werden.
package s1.block6; public class Test { /** * Zu erzeugende Generationen */ public static int generationen = 3; // Initial testen //public static int generationen = 19; // mit -Xlog:gc //public static int generationen = 22; // mit -Xmx500m -Xlog:gc //public static int generationen = 23; // mit -Xms500m -Xmx1024m -Xlog:gc public static void main(String[] args) { Person p; systemStatus(); for (int i=0; i<= generationen; i++) { System.out.println("*** Erzeuge " + i + " Generationen ***"); // Der alte Personenbaum wird implizit dereferenziert p = new Person(i); // Verlängern der Laufzeit. Dies erlaubt eine bessere Beobachtung mit jonsole // Der alte Vorfahrenbaum wird durch die Zuweisung dereferenziert //p = new Person(i); //p = new Person(i); systemStatus(); System.out.println("*** Ende. Erzeuge " + i + " Generationen ***"); } } public static void systemStatus(){ Runtime r = Runtime.getRuntime(); System.out.println("*** Systemstatus ***"); System.out.println("* Prozessoren : " + r.availableProcessors()); System.out.println("* Freier Speicher: " + r.freeMemory()); System.out.println("* Maximaler Speicher: " + r.maxMemory()); System.out.println("* Gesamter Speicher: " + r.totalMemory()); System.out.println("*** Ende Systemstatus ***"); } }
6.7.2 Tiefes Kopieren
Benutzen Sie den Quellcode der vorhergehenden Übung.
Schreiben Sie eine Klasse mir dem Namen Adresse und den folgenden Eigenschaften
- öffentliche Attribute ort, plz, strasse, hausnummer
- einen öffentlichen Konstruktor der alle Attribute erfasst
- einen "Copy Constructor" für die Klasse
Erweitern Sie die Klasse Person wie folgt:
- Attribut wohnort vom Typ Adresse
- "Copy Constructor" für die Klasse
Wenden Sie den "Copy Constructor" im Hauptprogramm an.
Optional
Schreiben Sie einen "Copy Constructor" der maximal n Generationen kopiert.
6.7.3 Tiefes Vergleichen
Erweitern Sie die Klassen aus den vorgehenden Beispielen wie folgt:
Klasse Person
- Fügen Sie einen Konstruktor hinzu der Name und alle vier Parameter für einen Geburtsort erfasst
- Implementieren eine equals() Methode die den Vergleich basierend auf Namen und den Inhalten des Geburtsort ausführt
Klasse Adresse
- Implementieren Sie eine equals() Methode die auf alle Attribute der Klasse prüft
Hauptprogramm
- Entwickeln Sie Testroutinen die die unterschiedlichen möglichen Fälle abprüfen
Tipp Die equals() Methode muss aus Typisierungsgründen ein Objekt vom Typ Object übernehmen. Mit dem unten aufgeführten Codestück kann man testen ob der EIngabeparameter vom gleichen Typ wie das aktuelle Objekt ist. Ist dies nicht der Fall so können die Objekte nicht gleich sein.
public boolean equals(Object obj) { ... if (this.getClass() == obj.getClass() )
6.7.4 "Walkthrough" Beispiel
Die hier gezeigte Übung wird interaktiv in der Vorlesung entwickelt. Die Arbeitsanweisungen sind im Quellcode enthalten.
Der Ausgangspunkt ist die Klasse Ware und die Klasse Lager die als Hauptprogramm dient:
Ziel ist es eine Lösung zu entwickeln in der die Klasse Ware mit einem Lager verwendet werden kann:
Klasse Ware
package s1.block6; /** * Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern * mit Preisen und Namen in einem Lager. * @author Stefan Schneider * @version 1.1 * @see Lager */ public class Ware { /* * 7. Anlegen eine Copy Constructor * 7.1 Alle Werte des Objekts werden kopiert * 7.2 Es wird bei Bedarf eine neue Empfehlung angelegt */ /** * Der aktuelle Mehrwertsteuersatz 2010. * Er liegt zur Zeit bei {@value}. * @since 1.0 * @version 1.0 */ public static final double mws = 0.19; private double nettoPreis; //Deklaration public boolean halbeMws; private String name; public Ware empfehlung; /** * Konstruktor fuer die Klasse Ware * @param n der Name der Ware * @param np der Nettorpreis * @param hmws halber Mehrwertsteuersatz für Ware gueltig */ public Ware(String n, double np, boolean hmws) { name = n; nettoPreis = np; halbeMws = hmws; } /** * Liefert den Namen einer Ware zurueck. * @return Name der Ware */ public String get_name() { return name; } /** * Setzen eines neuen Nettopreis * @param npr der neue Nettopreis */ public void set_nettoPreis(double npr) { nettoPreis = npr; } /** * Ausdrucken aller Werte auf der Konsole */ public void drucken() { drucken(0); } /** * Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung * für Empfehlungen * @param einruecken eingerueckte Stellen für Empfehlungen */ private void drucken(int einruecken) { String leerStellen = ""; for (int i = 0; i < einruecken; i++) { leerStellen = leerStellen + " "; } System.out.println(leerStellen + "Name: " + name); System.out.println(leerStellen + "netto: " + nettoPreis); System.out.println(leerStellen + "Brutto: " + bruttoPreis()); System.out.println(leerStellen + "Halbe Mws:" + halbeMws); if (empfehlung != null) { // Empfohlene Bücher werden eingerückt empfehlung.drucken(einruecken + 2); } } /** * Ausgabe des Nettopreis * @return der Nettopreis */ public double nettoPreis() { return nettoPreis; } /** * Ausgabe des Bruttopreis * @return der Bruttopreis */ public double bruttoPreis() { double bp; //temporaere Variable; keine Klassenvariable if (halbeMws) { bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100; } else { bp = Math.round(nettoPreis * (mws + 1) * 100) / 100; } return bp; } }
Klasse Lager
package s1.block6; /** * Die Klasse Lager verwaltet eine bestimmte Anzahl von Waren * @author s@scalingbits.com */ public class Lager { /* * Aufgaben * 1. Verwalten von n Waren in einem Feld * 1.1 Deklarieren eines privaten Feldes * 1.2 Zugriffsmethoden zum Setzen und Auslesen des Feldes * 2. Implementieren eines Konstruktors der das Lager * für n Waren initialisiert * 3. Methode zum Ausräumen des Lagers * 4. Erzeugung eines Singletons zum Erzeugen genau eines Lagers * 5. Anlegen einer neuen Klasse MainLager * 5.1 Umkopieren der main() Methode aus der Klasse Lager in die Klasse * MainLager.main() * 8. Testen des Copy Constructors * 8.1 Belegen Sie die ersten 500 Lagerpositionen mit Waren * 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die * folgenden 500 Lagerpositionen mit Ihnen * 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen * 8.4 Implementieren Sie eine Schleife die einige Minuten läuft * und testen Sie den Speicherverbrauch mit jconsole oder */ public static void main(String[] args) { Ware ware1 = new Ware("Zeitung",12.12,true); System.out.println("Ware1 ohne Empfehlung:"); ware1.drucken(); double p = ware1.nettoPreis(); Ware ware2 = new Ware("Potter Bd1",31.12,false); Ware ersterBand = ware2; ware1.empfehlung= ware2; System.out.println("Ware1 mit Empfehlung:"); ware1.drucken(); // Erzeugen einer Ware mit 7 verketteten Empfehlungen Ware ware3; Ware hp1=ware2; for (int i=2; i<= 7; i++) { ware3 = new Ware("Potter Bd" + i,31.25+i,false); ware2.empfehlung = ware3; ware2 = ware3; } System.out.println("Alle Harry Potter Baende drucken"); hp1.drucken(); } }
Klasse MainLager
package s1.block6; /** * * Eine Hilfsklasse zur Implementierung eines Hauptprogramms * * @author sschneid * @version 1.0 */ public class MainLager { /* * 6. Testen der Klasse Main * 6.1 Aufruf des Singleton der Klasse Lager * 6.2 Einfügen zweier Waren * 6.3 Drucken zweier Waren */ public static void main(String[] args) { Ware ware1 = new Ware("Zeitung", 12.12, true); ware1.drucken(); double p = ware1.nettoPreis(); Ware ware2 = new Ware("Potter Band 1", 31.12, false); Ware ersterBand = ware2; Ware ware3; for (int i = 2; i <= 7; i++) { ware3 = new Ware("Potter Band" + i, 31.12, false); ware2.empfehlung = ware3; ware2 = ware3; } } }
- 6887 views
6.8 Lösungen
6.8 Lösungen6.8.1 Referenzieren, Derefenzieren und GC
Klasse Main
Das folgende Programm wird wie folgt gestartet
java s1.block6.Main 3 gc
Es erwartet zwei Kommandozeilenargumente. Das erste Argument beschreibt die Anzahl der zu erzeugenden Generationen.
Das zweite Argument is optional. Steht hier GC wird eine System Garbarge Collection angestoßen
package block6.gc; public class Main { /** * Auslesen von Systemparametern */ public static void systemStatus(){ Runtime r = Runtime.getRuntime(); System.out.println("*** System Status ***"); System.out.println("Prozessoren : " + r.availableProcessors()); System.out.println("Freier Speicher: " + r.freeMemory()); System.out.println("Maximaler Speicher: " + r.maxMemory()); System.out.println("Gesamter Speicher: " + r.totalMemory()); System.out.println("*** ***"); } public static void main(String[] args) { int vorfahren = 0; boolean systemCollection=false; Person p1; systemStatus(); // Parsen des ersten Arguments // Diese Zeichenkette enthält die ANzahl der Generationen if (args.length > 0 ) { // Gibt es ein erstes Argument? try { vorfahren = Integer.parseInt(args[0]);} catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } // Auslesen des zweiten Arguments // Steht hier die Zeichenkette GC in Klein- oder Grossschreibung if (args.length > 1 ) // Gibt es ein zweites Argument systemCollection = ((args[1].equalsIgnoreCase("gc")); p1 = new Person(vorfahren); //p1 = null; System.out.println(); System.out.println("Erster Schritt erledigt: " + Person.zaehler + " Instanzen erzeugt"); systemStatus(); if (systemCollection) { System.out.println("Starte System GC"); System.gc(); } p1 = new Person(vorfahren); System.out.println(); System.out.println("Zweiter Schritt erledigt: " + Person.zaehler + " Instanzen erzeugt"); systemStatus(); } }
Klasse Person
package s1.block6; public class Person { Person vater; Person mutter; static int zaehler=0; public Person (int vorfahren) { if ( vorfahren>0) { vater = new Person(vorfahren-1); mutter = new Person(vorfahren-1); } zaehler++; if (zaehler%1000 == 0) System.out.print("."); } }
6.8.2 "Copy Constructor" Klasse
Klasse Main
package s1.block6; public class Main { public static void systemStatus(){ Runtime r = Runtime.getRuntime(); System.out.println("*** System Status ***"); System.out.println("Prozessoren : " + r.availableProcessors()); System.out.println("Freier Speicher: " + r.freeMemory()); System.out.println("Maximaler Speicher: " + r.maxMemory()); System.out.println("Gesamter Speicher: " + r.totalMemory()); System.out.println("*** ***"); } public static void main(String[] args) { int vorfahren = 0; boolean systemCollection=false; Person p1; systemStatus(); if (args.length > 0 ) { try { vorfahren = Integer.parseInt(args[0]);} catch (NumberFormatException e) { System.err.println("Argument muss Ganzzahl sein"); System.exit(1); } } if (args.length > 1 ) systemCollection = (args[1].equalsIgnoreCase("gc")); p1 = new Person(vorfahren); //p1 = null; System.out.println(); System.out.println("Erster Schritt erledigt: " + Person.zaehler + " Instanzen erzeugt"); systemStatus(); if (systemCollection) { System.out.println("Starte System GC"); System.gc(); } p1 = new Person(p1); System.out.println(); System.out.println("Zweiter Schritt erledigt: " + Person.zaehler + " Instanzen erzeugt"); systemStatus(); } }
Klasse Adresse
package s1.block6; public class Adresse { public String ort; public String plz; public String strasse; public String hausnr; public Adresse (String o, String p, String s, String h){ ort = o; plz = p; strasse = s; hausnr = h; } public Adresse (Adresse orig) { ort = orig.ort; plz = orig.plz; strasse = orig.strasse; hausnr = orig.hausnr; } }
Klasse Person
package s1.block6; public class Person { Person vater; Person mutter; String name; Adresse wohnort; static int zaehler=0; public Person (int vorfahren) { if ( vorfahren>0) { vater = new Person(vorfahren-1); mutter = new Person(vorfahren-1); } wohnort = new Adresse("Berlin", "10117","Platz der Republik","1"); name = "John Doe"; zaehler++; if (zaehler%1000 == 0) System.out.print("."); } public Person (Person orig) { if (orig.vater != null) vater = new Person(orig.vater); if (orig.mutter != null) mutter = new Person(orig.mutter); if (orig.wohnort != null) wohnort = new Adresse(orig.wohnort); name = orig.name; zaehler++; if (zaehler%1000 == 0) System.out.print("."); } }
Optional: "Copy Constructor mit begrenzter Kopiertiefe
public Person (Person orig, int vorfahren) { if (vorfahren > 0) { if (orig.vater != null) vater = new Person(orig.vater, vorfahren-1); if (orig.mutter != null) mutter = new Person(orig.mutter, vorfahren-1); } if (orig.wohnort != null) wohnort = new Adresse(orig.wohnort); name = orig.name; zaehler++; if (zaehler%1000 == 0) System.out.print("."); }
6.8.3 Tiefes Vergleichen Klasse Person
Klasse Person
class Person { ... public Person (String name1, String gort, String gplz, String gstrasse, String gnr){ name = name1; geburtsort = new Adresse(gort,gplz,gstrasse,gnr); zaehler++; if (zaehler%1000 == 0) System.out.print("."); } ... public boolean equals(Object obj) { Person target; // Type checking is optional for this level of skills if (this.getClass() == obj.getClass() ) { target = (Person) obj; return (name.equals(target.name) && (geburtsort.equals(target.geburtsort))); } else return false; } ... }
Klasse Adresse
public boolean equals(Object obj) { Adresse target; // Type checking is optional for this level of skills if (this.getClass() == obj.getClass() ) { target = (Adresse) obj; return (ort.equals(target.ort) && plz.equals(target.plz) && strasse.equals(target.strasse) && hausnr.equals(target.hausnr)); } else return false; }
6.8.4 Ware/Lager Beispiel
Klasse MainLager
package s1.block6;
/**
* * Eine Hilfsklasse zur Implementierung eines Hauptprogramms
*
* @author s@scalingbits.com
* @version 1.1
*/
public class MainLager {
/*
* 5. Anlegen einer neuen Klasse MainLager * 5.1 Umkopieren der main() Methode
* aus der Klasse Lager in die Klasse MainLager.main()
*/
/*
* 8. Testen des Copy Constructors
* 8.1 Belegen Sie die ersten 500
* Lagerpositionen mit Waren
* 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die
* folgenden 500 Lagerpositionen mit Ihnen
* 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen
* 8.4 Implementieren Sie eine Schleife die einige Minuten läuft und testen Sie
* den Speicherverbrauch mit jconsole und jps
*/public static void main(String[] args) {
Lager lager1 = Lager.dasLager();
int position;
for (int j = 0; j < 100000; j++) {
for (int i = 0; i < 500; i++) {
position = lager1.einlagern(new Ware("Buch der Zahl " + i, 2 * i, false));
//System.out.println("Ware auf Position" + position + " eingelagert");
}
for (int i = 0; i < 500; i++) {
position = lager1.einlagern(new Ware(lager1.holen(i)));
//System.out.println("Ware auf Position" + position + " eingelagert");
}
lager1.ausraeumen();System.out.println("Schleife " + j + " von 100000");
}
}
}
Klasse Ware
package s1.block6.loesung;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.1
* @see Lager
*/
public class Ware {
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*/
public static final double mws = 0.19;
private double nettoPreis; //Deklaration
public boolean halbeMws;
private String name;
public Ware empfehlung;
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Ware(String n, double np, boolean hmws) {
name = n;
nettoPreis = np;
halbeMws = hmws;
}
/*
* 7. Anlegen eine Copy Constructor
* 7.1 Alle Werte des Objekts werden kopiert
* 7.2 Es wird bei Bedarf eine neue Empfehlung angelegt
*/
/**
* Copy-Konstruktor fuer die Klasse Ware
* @param w die zu klonende Ware
*/
public Ware(Ware w) {
name = w.name;
nettoPreis = w.nettoPreis;
halbeMws = w.halbeMws;
if (w.empfehlung != null)
empfehlung = new Ware(w.empfehlung);
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/ public String get_name() {
return name; }
/** * Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void set_nettoPreis(double npr) {
nettoPreis = npr;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() {
System.out.println("Name: " + name);
System.out.println("netto: " + nettoPreis);
System.out.println("Brutto: " + bruttoPreis());
System.out.println("Halbe Mws:" + halbeMws);
if (empfehlung != null) {
empfehlung.drucken(2);
}
}
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/ public void drucken(int einruecken) {
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}
System.out.println(leerStellen + "Name: " + name);
System.out.println(leerStellen + "netto: " + nettoPreis);
System.out.println(leerStellen + "Brutto: " + bruttoPreis());
System.out.println(leerStellen + "Halbe Mws:" + halbeMws);
if (empfehlung != null) {
empfehlung.drucken(einruecken + 2);
}
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/ public double nettoPreis() {
return nettoPreis; }
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/ public double bruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
}
Klasse Lager
package s1.block6;
/** * Die Klasse Lager verwaltet eine bestimmte Anzahl von Waren
* @author s@scalingbits.com
* @version 1.1
*/
public class Lager {
/*
* Aufgaben
* 1. Verwalten von n Waren in einem Feld
* 1.1 Deklarieren eines privaten Feldes
*/
private int lagerGroesse = 1000;
private Ware[] warenLager;
// 1.2 Zugriffsmethoden zum Setzen und Auslesen des Feldes
public Ware holen(int i) {
if ((i<0) || (i>=lagerGroesse)) return null;
else return warenLager[i];
} public int einlagern(Ware w) { int i=0; while ((i<lagerGroesse) && (warenLager[i] != null)) i++; if (i<lagerGroesse) { warenLager[i] = w; return i; } else return -1; } // 3. Methode zum Ausräumen des Lagers public void ausraeumen() { for (int i=0; i<lagerGroesse; i++) warenLager[i]=null; }/*
* 2. Implementieren eines Konstruktors der das Lager
* für n Waren initialisiert
*/
private Lager() {
// Es wird ein Feld von Referenzen auf Waren angelegt
// Es werden keine Waren angelegt!
warenLager = new Ware[lagerGroesse];
}
/*
* 4. Erzeugung eines Singletons zum Erzeugen genau eines Lagers
*/
private static Lager meinLager; // Eine Objektvariable die auf das Lager zeigt
static public Lager dasLager() {
if (meinLager== null) meinLager= new Lager();
return meinLager;
}
/*
* 5. Anlegen einer neuen Klasse MainLager
* 5.1 Umkopieren der main() Methode aus der Klasse Lager in die Klasse
* MainLager.main()
*/
// 6. Testen der Factory
/*Lager lager1 = Lager.dasLager();
int position;
for (int i=0; i<500; i++)
position = lager1.einlagern(new Ware("Buch " +i, 2*i, false));
for (int i=0; i<500; i++) {
Ware w = lager1.holen(i);
System.out.println("Position " + i);
w.drucken();
}
*/
/*
* 8. Testen des Copy Constructors
* 8.1 Belegen Sie die ersten 500 Lagerpositionen mit Waren
* 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die
* folgenden 500 Lagerpositionen mit Ihnen
* 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen
* 8.4 Implementieren Sie eine Schleife die einige Minuten läuft
* und testen Sie den Speicherverbrauch mit jconsole oder
*/
public static void main(String[] args) {
Ware ware1 = new Ware("Zeitung",12.12,true);
System.out.println("Ware1 ohne Empfehlung:");
ware1.drucken();
double p = ware1.nettoPreis();
Ware ware2 = new Ware("Potter Bd1",31.12,false);
Ware ersterBand = ware2;
ware1.empfehlung= ware2;
System.out.println("Ware1 mit Empfehlung:");
ware1.drucken();
// Erzeugen einer Ware mit 10 verketteten Empfehlungen
Ware ware3;
Ware hp1=ware2;
for (int i=2; i<= 7; i++) {
ware3 = new Ware("Potter Bd" + i,31.25+i,false);
ware2.empfehlung = ware3;
ware2 = ware3;
}
System.out.println("Alle Harry Potter Baende drucken");
hp1.drucken();
// ersterBand.drucken();
}
}
- 4751 views
6.9 Lernziele
6.9 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu Referenzen zu beantworten.
Feedback
- 3390 views
7. Felder
7. FelderDefinition:
- Ein Array(Feld) ist ein Javaobjekt, das aus Elementen eines gemeinsamen Basistyps zusammengesetzt ist.
- Der Zugriff auf ein Feld erfolgt immer über den Index des Feldes
- In Java beginnen Felder mit n Elementen mit dem Index [0] und enden mit dem Index [n-1]
Felder sind Javaobjekte und werden im Freispeicher (Heap) verwaltet, da Felder variable Größen haben. Aus diesem Grund können sie nicht statisch auf dem Stack (Stapel) angelegt werden. Anbei die Speichersicht, die ähnlich zu Strings ist. Das abgebildete Diagramm zeigt ein Feld mit Platz für 6 Ganzzahlen:
Felder sind häufig verwendete Datenstrukturen da man sehr einfach mit Schleifen über den Index alle Feldelemente erreichen kann.
Der Zugriff auf Feldelemente erfolgt über eine Syntax mit Verwendung von rechteckigen [index] Klammern wie man im folgenden Beispiel sehen kann:
int[] d1 = { 1000, 100, 10}; int a = d1[2]; // Auslesen von 10 aus Position 2 d1[0]= 99; // Zuweisen des Werts 99 auf Position 0
Weitere Quellen
Oracle Tutorial: Arrays (in Englisch)
- 8190 views
7.1 Eindimensionale Felder
7.1 Eindimensionale FelderDas Definieren und Erzeugen von eindimensionalen Feldern erfolgt nach ähnlichen Prinzipien wie die Verwaltung von Strings.
Es gibt hier zwei Varianten.
Anlegen und befüllen mit new() Operator
Die Deklaration anhand eines Feldes von Ganzzahlen mit Hilfe des new() Operators in 3 Schritten:
int[] array1; |
Hiermit wurde nur die Referenz auf ein (zukünftiges) Feld von Ganzzahlen angelegt. Siehe unten: Mit Hilfe des new() Operators wird ein leeres Feld angelegt. Nach dem Anlegen des Feldes kann man die Größe des Feldes mit dem Attribut length abfragen.:
array1 = new int[6]; int s = array1.length; |
Das Definieren der Variablen array1 und das Aufrufen des new() Operators hätte auch in einem Schritt erfolgen können
int[] array1 = new int[6];
Jetzt kann das Feld mit Werten belegt werden. Zum Beispiel mit Vielfachen der Zahl 17:
for ( int i=0; i<s; i++) {array1[i] = i*17;} |
Aufzählende Wertzuweisung
Java erlaubt auch die 3 Schritte von oben in einer einzigen Operation auszuführen. Hier können alle Werte als Aufzählung innerhalb von geschweiften Klammern direkt zugewiesen werden:
int[] array1 = {0,17,34,51,68,85};
Belegen mit einem festen Wert
Die Methode fill() erlaubt das belegen eines Feldes mit einem festen Wert
array.fill(-1);
Hier werden alle Elemente eines Feldes mit dem Wert -1 belegt.
Zuweisungen zwischen Arrayvariablen
Arrayvariablen verhalten sich wie Referenzen bezüglich Zuweisungen. Das Zuweisen des Nullwerts löscht die Referenz auf ein Feld(Array). Es ist dann ein Kandidat zum Löschen falls keine andere Referenz auf das Feld vorhanden ist. Das folgende Beispiel zeigt eine Zuweisung zwischen den Feldvariablen.
int[] array1 = {0,17,34,51,68,85}; int[] array2 = array1; if (array1[2] == array2[2]) System.out.println("Dieser Text sollte " + "ausgedruckt werden!"); |
Beide Variablen array1 und array2 zeigen auf dasselbe Feld
Durch das Zuweisen des null Werts werden die Referenzen gelöscht. Das Feld bleibt bestehen. Es wird jedoch gelöscht, wenn es keine andere Referenz mehr auf das Feld gibt.
array1 = null; array2 = null; |
Kopieren von Feldern
Das naive Kopieren von Feldern mit vollständiger Replikation kann man mit der erweiterten Notation der for Schleife leicht selbst implementieren:
int[] array1 = {0,17,34,51,68,85}; int[] array2 = new int[array1.length]; for (int i=0; i<array1.length; i++) {array2[i]= array1[i];} |
Alle Elemente des Felds werden einzelnen kopiert. Die erweiterte Notation erlaubt es die Anweisung ohne explizite Verwendung der Feldgröße anzugeben. Das Ergebnis hat eine Speicherstruktur wie folgt:
Eine effizientere Möglichkeit bildet die Methode System.arraycopy() aus der System Klasse. Diese Methode hat die folgende Signatur:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Sie erlaubt:
- von einem Feld src ab der Position srcPos
- in ein Feld dest an die Position destPos
- die Anzahl length Objekte zu kopieren
Das oben aufgeführte Beispiel würde wie folgt mit der Methode arrayCopy() implementiert:
int[] array1 = {0,17,34,51,68,85}; int[] array2 = new int[array1.length]; System.arrayCopy(array1,0,array2,0,array1.length);
Ausnahmen und Fehler bei Arbeiten mit Arrays (Feldern)
Beim Lesen und Manipulieren von Feldern können unterschiedliche Fehler auftreten. Sie können beim Über- oder Unterlauf eines Feldes auftreten oder bei dem Versuch Typen zuzuweisen die das Feld nicht erlaubt. Die folgenden Ausnahmen (Exceptions) können auftreten:
- IndexOutOfBoundsException: Der gewünschte Zugriff liegt ausserhalb des definierten Bereichs
- NullPointerException: Eine der Objektreferenzen ist ein null Zeiger
- ArrayStoreException: Ein Objekt welches gespeichert werden soll hat nicht den erlaubten Typ
- 7878 views
7.2 Felder als Parameter von Methoden
7.2 Felder als Parameter von MethodenFelder(Arrays) können wie Basistypen oder Referenzen als Übergabe- und Rückgabeparameter von Methoden verwendet werden.
Ein typisches Beispiel ist:
public static void main(String[] args) { ... if (args != null) if (args.length > 2 ) { tag = Integer.parseInt(args[0]); monat = Integer.parseInt(args[1]); jahr = Integer.parseInt(args[2]); ... }
Die beim Programmstart mitgegebenen Argumente werden in Form eines Feldes von Zeichenketten übergeben. Man erkennt beim Beispiel oben, dass im Quellcode eine Reihe von Fehlerfällen geprüft werden bevor die Anwendung auf das "unbekannte" Feld zugreift und die Zeichenketten in Zahlen verwandelt:
- args ist eventuell ein null Zeiger
- die Anzahl der Argumente ist unbekannt. Nur wenn mehr als zwei Elemente im Feld sind werden die ersten 3 Elemente ausgelesen.
Hierbei ist wichtig, dass die Felder nach dem gleichen Konzept wie Referenzen übergeben werden:
- Call by Reference: Das Feld wird nicht dupliziert. Es steht der Methode nicht unbedingt exklusiv zur Verfügung. Es wird nur eine Referenz übergeben.
Entsprechend können Felder auch als Rückgabeparameter dienen. Der Rückgabeparameter muss mit Typ und rechteckiger Klammer genannt werden. Hier int[]:
public static int[] createRandom(int size, int range) { int[] array1 = new int[size]; for (int i=0; i<size;i++) array1[i] = (int)(Math.random()*range); return array1;
Methoden mit variabler Argumentenanzahl
Es gibt Anwendungsfälle bei bei denen es wünschenswert ist eine variable Anzahl von Parameter zu benutzen. Seit Java 5 muss man hier nicht mehr ein Feld übergeben. Für sogenannte "varargs" Methoden gibt es ein "..." Konstrukt (drei Punkte) mit dem man die variable Anzahl der Parameter eines bestimmten Typs deklarieren kann.
Anbei das Beispiel einer Methode, die die Namen von Haustieren entgegen nimmt:
public void hausTiereMelden(String ... tierNamen) { for ( String t : tierNamen) System.out.println("Haustier: " +t); }
public static void test () { hausTiereMelden("Waldi","Bello","Rufus"); hausTiereMelden("Mikesh", "Napoleon"); hausTiereMelden(); // Keine Parameter! }
Hinweis: "varargs" Methoden dürfen auch ganz ohne Parameter aufgerufen werden. Die Implementierung der Methode muss daher auch mit einer null Belegung umgehen können!
- 8299 views
Müsste es im Beispiel Call by
Müsste es im Beispiel Call by Reference nicht (Math.random()%range) heißen?
- Log in to post comments
Eigentlich nicht
Das Ergebnis von Math.random() liegt immer im Intervall von 0-1. Multipliziert man es bekommt man ein Ergbnis im Bereich 0 bis range. Durch das Casten auf (int) werden dann die Nachkommastellen abgeschnitten.
- Log in to post comments
7.3 Mehrdimensionale Felder
7.3 Mehrdimensionale FelderEindimensionale Felder entsprechen Listen mit einem wahlfreien Zugriff. Java erlaubt auch die Benutzung von zwei- und mehrdimensionalen Feldern.
Zweidimensionale Felder
Ein zweidimensionales Feld besteht entsprechend aus Zeilen und Spalten. Die Java Syntax hierzu ist eine Konkatenierung von eckigen Klammern. Zur Erzeugung eines Feldes mit 3 Zeilen und 6 Spalten nutzt man die folgende Notation:
int[][] array1 = new int[3][6];
Hiermit ergibt sich eine Speicherstruktur die aus einem Spaltenfeld besteht welches alle Zeilenfelder enthält um eine zweidimensionale Struktur aufzuspannen. Man kann hier jetzt 3*6=18 Werte speichern:
Das Setzen und Lesen von Werten geschieht wie folgt:
int k = 222; array1[2][5]= k; int n = array1[0][0];
Aufzählende Initialisierung mehrdimensionaler Felder
Auch bei mehrdimensionalen Feldern ist eine aufzählende Initialisierung möglich:
int[][] array1 = {{1,2,3,4,5,6},{5,10,15,20,25,30},{10,20,30,40,50,60}}
Hiermit ergibt sich eine zweidimensionale Tabelle mit der folgenden Belegung:
Bestimmung der Größe mehrdimensionaler Felder
Das Attribut length erlaubt auch bei mehrdimensionalen Feldern die Bestimmung der Größe. Hier liefert das Attribut jedoch nicht die Gesamtgröße der Datenstruktur sondern nur die Größe einer bestimmten Dimension.
int d =array1.length;
liefert im oben gezeigten Beispiel eine 3 für die erste Dimension, die Zeilen.
int d = array1[2].length;
gibt eine 6 zurück. Da die dritte Zeile 6 Elemente hat.
Anmerkung: Höher dimensionale Felder haben nicht unbedingt in allen Dimensionen die gleiche Größe. Dies bedeuted, dass ein zweidimensionales Feld muss nicht rechteckig sein muss!
Die folgende Implementierung erzeugt ein "dreieckiges" zweidimensionales Feld: int[][] array1; array1 = new int[3][]; array1[0] = new int[1]; array1[1] = new int[2]; array1[2] = new int[3]; |
Die Speicherstruktur zu diesem Feld sieht wie folgt aus: |
Eine aufzählende Initialisierung ist auch möglich:
int[][] array1 = {{1},{11,22},{111,222,333}};
Das length Attribut liefert bei dieser Struktur die unterschiedlichsten Ergebnisse.
Höherdimensionale Felder
Nach dem gleichen Verfahren können dreidimensionale oder noch höherdimensionale Felder erzeugt und verwaltet werden.
Person[][][] Mitarbeiter = new Person[10][10][10]; Mitarbeiter[7][8][9] = new Person ("Jane","Doe");
Im vorliegenden Beispiel handelt es sich um ein dreidimensionales Feld mit einem Objekttyp (Person). Bei Objektfeldern werden nur die Felder angelegt nicht aber die zugehörigen Objekte. Sie müssen individuell erzeugt werden. Nach der initialen Felderzeugung sind alle Felder mit Nullreferenzen belegt. Mit dieser Belegung lässt sich normalerweise schlecht arbeiten. Bei Basistypen ist dies anders. Sie werden auch auf Null initialisiert und können direkt verwendet werden. Bei Feldern mit Referenzen werden jedoch nicht direkt die benötigten Objekte angelegt.
- 7700 views
7.4 Übungen
7.4 Übungen
7.4.1 Telefonbuchanwendung (1)Implementieren Sie eine Telefonbuchanwendung die es erlaubt die folgenden Datensätze zu verwalten:
|
Benötigte Zeit: 30-60 Minuten für einen gübten Programmierer
Die Klasse Telefonbuch soll die folgenden Eigenschaften haben:
- Verwaltung des Namens, Vornamens, Telefonnummer in drei Feldern mit den entsprechenden Typen
- Eine Methode die das gesamte Telefonbuch ausdruckt
- Suche nach allen drei Attributen mit drei verschiedenen Methoden
- Eine Methode zum "bevölkern" des Telefonbuchs mit mindestens 10 Namen
- Alle Datensätze seien unterschiedlich
- Das Telefonbuch soll initial Felder für 4 Einträge besitzen. Beim Vollaufen des Telefonbuchs sollen neue Felder angelegt werden die um 50% größer sind.
- Kapseln Sie die gesamte Implementierung der Felder innerhalb der Klasse
- Implementieren Sie eine Methode zum Löschen eines gesuchten Datensatzes
- Beim Löschen sollen keine leeren Einträge in den Feldern entstehen
- Implementieren Sie eine Testmethode die
- 10 Adressen einträgt
- 2 Adressen löscht
- 1 Adresse hinzufügt
Hinweise:
- Benutzen Sie Methoden für alle sinnvollen Teilaufgaben
- Das Telefonbuch ist nicht notwendigerweise sortiert. Man muss alle Datensätze durchsuchen
Tipp: Die Suchanfragen lassen sich mit wenig Aufwand vom einer grafischen Swingoberfläche steuern. Ein Beispielprogramm finden Sie hier.
7.4.2 "Objektorientierte" Telefonbuchanwendung (2)
Überarbeiten Sie die Telefonbuchanwendung aus der vorhergehenden Aufgabe derart, dass Sie:
- Eine Klasse Person mit den drei Attributen nutzen
- Nur ein Feld vom Typ Person in dem alle Daten verwaltet werden
Welche der beiden Lösungen gefällt Ihnen besser? Warum?
7.4.3 Zufallszahlengenerator
Schreiben Sie ein Programm welches die Zuverlässigkeit des Java Zufallszahlengenerator prüft.
- Erzeugen Sie ein Feld mit 1 Million (Anzahl konfigurierbar) Zufallszahlen im Bereich von 0 bis 999. Nutzen Sie die Methode Math.random() und das existierende Beispielprogramm.
- Erzeugen Sie ein dreidimensionales Feld für Ganzzahlen mit einer Größe von 10*10*10 Einträgen (Index jeweils 0..9).
- Speichern Sie in diesem Feld die Häufigkeit einer vorgekommenen Zahl.
- Bsp: Erhöhen sie den Zähler der Position [5][4][3] um wenn Sie eine Zufallszahl "534" gefunden haben. Die Zelle [5][4][3] speichert die Anzahl der gefundenen Zahlen "542".
- Zählen Sie die Zufallszahlen und tragen Sie sie in das dreidimensionale Feld ein
- Beispiel: Inkrementieren den Wert der Zelle array[2][4][5] für jedes Vorhandensein der Zahl 245
- Schreiben Sie eine Methode zum Ausdrucken des Feldes
- Schreiben Sie Methoden die die folgenden Fragen beantworten
- Welche Zahl kommen am häufigsten vor?
- Welche Zahl kommen am seltensten vor?
- Optional (keine Musterlösung):
- Gibt es lokale Häufungen?
- Welche Einer-, Zehner, Hunderterziffer ist am häufigsten?
- Haben Zellen mit über/unterdurchschnittlich vielen Einträgen auch Nachbarn mit über/unterdurchschnittlich vielen Einträgen?
- Eine Zelle array[x][y][z] hat meistens 8 Nachbarn array[x+/-1][y+/-1][z+/-1]
- Gibt es lokale Häufungen?
7.4.4 Conway: Das Spiel des Lebens
Das "Spiel des Lebens" wurde 1970 vom Mathematiker John Horton Conway 1970 entworfen.
Das Simulationsspiel basiert auf auf einem zweidimensionalen zellulären Automaten. Die Regeln des Automaten sind im Wikipediaartikel zu finden.
Die gelöste Aufgabe kann man in der unten genannten Anwendung sehen.
- Das Setzen von Elementen ist mit Mausklicks auf der entsprechenden Zelle möglich
- Eine neue Generation wird mit dem Button ">" berechnet
- Der Button ">>>" erlaubt das automatische Erzeugen von neuen Generationen. Mehrfaches Klicken halbiert die Wartezeit zwischen Generationen.
- Der Button "Stop" beendet den automatischen Modus
Laden Sie das jar-Archiv Conway.jar und starten Sie es im gleichen Verzeichnis mit dem Kommando
java -jar Conway.jar
Aufgabe
Vervollständigen die Klasse Generation.java. Nutzen Sie das Hauptprogramm SpielDesLebens.java zum Testen Ihrer Klasse.
Die Interaktion der Klasse Generation mit dem Rahmenprogramm ist im folgenden Diagramm dargestellt:
Klasse Generation.java
Hinweise:
- Das Hauptprogramm erwartet die Klasse Generation mit den vorgegebenen Methoden im gleichen Paket
- Sie können weitere Methoden wenn nötig implementieren
- Das Hauptprogramm wird genau eine Instanz der Klasse Generation erzeugen.
Beim Berechnen der Nachbarn eines Feldes ist auf die Sonderfälle am Rand zu achten:
Weitere Hilfestellungen sind den Kommentaren zu entnehmen. Die Klasse kann natürlich auch ohne die Hilfestellung entwickelt werden. Das Feld kann initial zum Testen sehr klein (2) sein. Die Buttons werden dann erst nach dem Vergrößern des Fenster sichtbar. Eine Größe von 50x50 ist für aktuelle Rechner ausführbar. Pro Zelle werden etwa 10 Pixel benötigt.
package s1.block7; /* * @author scalingbits.com */ public class Generation { // Hier ein Feld für alten Zustand deklarieren // Hier ein Feld für neuen Zustand deklarieren // die Felder muessen zweidimensional, vom Typ boolean sein, quadratisch sein
/**
* Groesse des quadratischen Feldes
*/
// Variable für Groesse des Feldes anlegen. Empfohlen 50 ==> GUI benötigt dann etwa 500 Pixel
/**
* Anlegen aller benoetigten Felder mit Initialwerten
* Alle Feldelemente sollen mit dem Zustand "false" = leer versehen sein
*/
public Generation() {
// Initialisieren sie die beiden Felder
// alle Felder sollen den Zustand "false" haben. Dies ist ein leeres Feld
}
/**
*
* @return Kantenlaenge des quadratischen Felds
*/
public int groesse() {return 0;} //Richtigen Wert zurueckgeben!!
/**
* Berechnen einer neuen Generation.
* Legen Sie ein neues Feld an. Berechnen Sie den neuen Zustand
* aller Feldelement aus dem alten Feld
*/
void neueGeneration() {
// Tipps:
// Weisen Sie die aktuelle Generation auf die alte zu
// Erzeugen oder wiederverwenden Sie ein Feld für eine neue Generation
// Nutzen Sie eine doppelt geschachtelte Schleife zum Ueberstrichen des aktuellen Felds
// Zaehlen Sie die Nachbarn der aktuellen Position in der alten Generation
// Achten Sie auf die Feldgrenzen!!
// Setzen Sie den Wert des aktuellen Felds auf "true" falls ein Objekt erhalten oder erzeugt werden soll
// Setzen Sie dem Wert des aktuellen Felds auf "false" falls kein Objekt in der neuen Generation existieren soll
}
/**
* Das Feld mit den aktuellen Werten
* @return
*/
public boolean[][] status() {return null;} // Hier das aktuelle Feld zurückgeben
}
Klasse SpielDesLebens.java
Das Hauptprogramm. Achten Sie auf die Paketstruktur!
Beim vorgebenen Paket kann das Programm mit dem folgenden Befehl gestartet werden
$ java s1.block7.SpielDesLebens
package s1.block7;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
/**
* Das Spiel des Lebens nach Conway
* @author s@scalingbits.com
*/
public class SpielDesLebens extends JApplet implements Runnable {
final private int size;
final private int xRaster=10;
final private int yRaster=10;
final private Generation gen;
final private JButton[][] buttonFeld;
private static boolean appletMode = true;
private static boolean autoMode = false;
private ImageIcon belegtIcon;
private ImageIcon freiIcon;
private static SpielDesLebens myself;
private int sleeptime = 2000; // Millisekunden im Automode
public class Zelle extends JButton {
final public int x;
final public int y;
public Zelle (Icon ic, int x, int y) {
super(ic);
this.x=x;
this.y=y;
}
}
/**
* Der Konstruktor ohne Argumente wird nur beim einem Start als Applet
* benutzt. Hier wird ein Applet mit einem Grid erzeugt.
*/
public SpielDesLebens() {
erzeugeIcons();
myself=this;
gen = new Generation();
size = gen.groesse();
JFrame f = null;
if (!appletMode) f = new JFrame("Game");
JPanel jp = new JPanel();
jp.setLayout(new GridLayout(size, size));
buttonFeld = new JButton[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
buttonFeld[i][j] = createButton(i, j);
jp.add(buttonFeld[i][j]);
}
}
JButton naechste = new JButton(">");
naechste.setToolTipText("Erzeuge nächste Generation");
naechste.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) { nextGen();}
} // Ende innere Klasse
);
JButton auto = new JButton(">>>");
auto.setToolTipText("Starte Film");
auto.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
sleeptime /=2; // Verdoppeln der Geschwindigkeit
if (!autoMode) {
autoMode=true;
Thread t1 = new Thread(SpielDesLebens.myself);
t1.start();
}
}
} // Ende innere Klasse
);
JButton stop = new JButton("Stop");
stop.setToolTipText("Stoppe Film");
stop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
autoMode=false;
sleeptime=4000;
}
} // Ende innere Klasse
);
JPanel buttonPanel = new JPanel();
buttonPanel.add(naechste);
buttonPanel.add(auto);
buttonPanel.add(stop);
Container co;
if (!appletMode) co =f;
else co=this;
co.setLayout(new BorderLayout());
co.add(jp,BorderLayout.CENTER);
co.add(buttonPanel,BorderLayout.SOUTH);
co.setPreferredSize(new Dimension(size * (xRaster+3),size * (yRaster+3)));
if (!appletMode) {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
/**
* Starten der Anwendung als eigenständiges Programm
* @param args werden nicht benutzt
*/
public static void main(String[] args) {
appletMode = false;
SpielDesLebens k = new SpielDesLebens();
}
/**
* Erzeugen eines JButtons für jede Zelle des Feldes
* @param xx x Koordinate im Feld
* @param yy y Koordinate im Feld
* @return einen Buttton mit ActionListener
*/
private JButton createButton(int xx, int yy) {
JButton myButton = new Zelle(freiIcon,xx,yy);
myButton.setToolTipText(("("+xx+","+yy+")"));
myButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
if(!autoMode) {
Zelle f = (Zelle) ae.getSource();
//System.out.println("Action auf" +f.x + " " + f.y);
boolean[][] g = gen.status();
if (g[f.x][f.y]) {
f.setIcon(freiIcon);
g[f.x][f.y]=false;
}
else {
f.setIcon(belegtIcon);
g[f.x][f.y]=true;
}
f.updateUI();
}
}
} // Ende innere Klasse
);
return myButton;
}
/**
* Erzeuge die beiden Ikonen für eine freies und ein belegtes Feld
*/
public final void erzeugeIcons() {
BufferedImage belegt =
new BufferedImage(xRaster, yRaster, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = belegt.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, xRaster-1, yRaster-1);
g.setColor(Color.black);
g.fillOval(1, 1, xRaster-2, yRaster-2);
g.dispose();
belegtIcon = new ImageIcon(belegt);
BufferedImage frei =
new BufferedImage(xRaster, yRaster, BufferedImage.TYPE_4BYTE_ABGR);
g = frei.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, xRaster-1, yRaster-1);
g.dispose();
freiIcon = new ImageIcon(frei);
}
/**
* Erzeugen einer neuen Generation und Abgleich der JButtons mit neuen
* Ikonen
*/
private void nextGen() {
gen.neueGeneration();
boolean[][] stat = gen.status();
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
if (stat[i][j]) buttonFeld[i][j].setIcon(belegtIcon);
else buttonFeld[i][j].setIcon(freiIcon);
}
/**
* Lasse neue Generationen automatisiert in einem eigenen Thread
* erzeugen
*/
@Override
public void run() {
try {
while (autoMode) {
Thread.sleep(sleeptime);
nextGen();
}
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
Lösung der Aufgabe in Zwischenschritten
1. Die Anwendung übersetzt und läuft fehlerfrei
- Variable für Feldgröße als Objektvariable dekarieren und mit Konstante belegen
- Wert 5 wählen; Fenster per Hand vergrößern
- Methode zur Rückgabe der Feldgröße implementieren
- Zweidimensionales Feld als Objektvariable für aktuelles Feld deklarieren. Benutzen Sie den Typ boolean.
- Im Konstruktor alle Zellen des Felds mit doppelt geschachtelter Schleife initialisieren
- Methode zur Rückhabe des Felds implementieren
- Methode neueGeneration() implementieren:
- Trivialimplementierung die genau ein beliebiges aber festes Feldelement auf "true" setzt
- Testen der Implementierung: Initialiserung des aktuellen Feldes kann so getestet werden
Ergebnis
Es wurde als Feldgröße 20 verwendet. Es wurde die Zelle [3,3] auf true gesetzt
1. Nach Starten des Programms | 2. Nach Klicken des ">" Button | 3. Nach mehrfachem Kicken auf Zellen |
---|---|---|
Der Benutzer sollte in der Lage sein, den Zustand von Zellen durch einfaches anklicken zu invertieren. Hier wurde im Schritt 3 ein "Happy Face" gezeichnet.
Die Position [3,3] sollte man jetzt auch mit einem Mausklick löschen können. Nach Klicken des ">" Button sollte das Pixel wieder gezeigt werden weil die Methode neueGeneration() aufgerufen wurde.
2. Zellen abhängig von anderen Zellen belegen: Methode neueGeneration()
Erweitern Sie die Methode neueGeneration()
- Setzen Sie jedes Feldelement (x,y) auf "true" wenn das Element (x+1.y+1) belegt ("true") war. Hierdurch werden aus Punkten Striche die sich nach rechts unten fortpflanzen.
- Sie benötigen hierfür eine doppelt geschachtelte Schleife die alle Feldelemente abarbeitet.
- Achten Sie darauf, dass Sie keine Elemente auf Positionen die größer als die Feldgrenze sind!
- Testen Sie die Anwendung: Jeder Punkt sollte in der Nachfolgegeneration einen neuen links oberhalb erhalten. Es steht pro Generation eine neue belegte Zelle links oberhalb.
Ergebnis
Es wurde als Feldgröße 20 verwendet. Es wurden Zellen nach dem Start des Programms mit der Maus markiert:
1. Nach Starten des Programms und Markieren zweier Zellen | 2. Nach Klicken des ">" Button | 3. Nach zweitem Klicken des ">" Buttons |
---|---|---|
3. Berechnen einer neuen Generation aus einer alten Generation
Man benötigt zu Lösung der Aufgabe zwei Felder. Das erste Feld enthält die Zellen der alten Generation. Das zweite Feld wird für die Belegung der neuen Generation benötigt. Mit nur einem Feld würde man nicht zwischen alter und neuer Generation entscheiden können. Man würde Zellen inkorrekt berechnen und belegen.
- Deklarieren Sie eine weitere Objektvariable die auf das alte Feld zeigt
- Initialisieren Sie das Feld in der gleichen Größe im Konstruktor. Belegen Sie alle Zellen mit dem Wert "false";
- Erweitern Sie die Methode neueGeneration()
- Referenzieren Sie mit dem Zeiger der alten Generation die aktuelle Generation.
- Erzeugen sie ein Feld für die neue (aktuelle) Generation
- Initialiseren Sie das neue Feld mit vernünftigen Werten.
- Belegen Sie jedes Feld der neuen Generation mit "true" wenn der rechte, untere Nachbar der Vorgängergeneration existiert. Dieses Problem wurde schon in der vorgehenden Phase gelöst.
- Testen Sie die Anwendung: Sie soll die gleichen Ausgaben produzieren
- Setzen Sie die Größe des Felds wieder auf 50 (oder mehr)
Jetzt sollte das Umkopieren von neuen auf alte Generationen funktionieren. Alle Schleifen sollten fehlerfrei laufen.
4. Berechnen der korrekten Nachbarschaftsbeziehung in der Methode neueGeneration()
Erweitern Sie die Methode neueGeneration()
- Zählen Sie für jedes Feld die Anzahl der Nachbarn. Achten Sie auf die Feldgrenzen. Prüfen Sie keine Feldelemente ab, die ausserhalb des Feldes liegen.
- Bestimmen Sie anhand der Nachbarn und es dem Wert der alten Zelle den Zustand der aktuellen Zelle.
Ergebnis
Es wurde als Feldgröße 20 verwendet. Nach dem Start wurde mit der Maus ein "Glider", ein senkrechter Dreierbalken und ein Rechteck aus sechs Zellen gezeichnet.
1. Nach Starten des Programms und Markieren diverser Zellen | 2. Nach Klicken des ">" Button | 3. Nach zweitem Klicken des ">" Buttons |
---|---|---|
7.4.5 Flughafenbeispiel mit Feldern
Klasse Flughafen
package airline.block7;
/**
*
* @author stsch
*/
public class Flughafen {
String name;
Flugzeug[] gate;
double treibstoffLager;
public Flughafen(int anzahlGates) {
gate = new Flugzeug[anzahlGates];
}
public void drucken() {
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + name);
for (int i =0;i<gate.length;i++)
System.out.println("Am Gate " + i + ": " + gate[i]);
System.out.println("Treibstoff: " + treibstoffLager);
System.out.println("***********************");
}
public static void main(String[] args) {
Flughafen pad = new Flughafen(6);
pad.name="Paderborn";
pad.treibstoffLager = 1000000;
pad.drucken();
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug(40000, 400000);
lh1.kennzeichen ="D-ABTL";
lh1.einsteigen(3);
System.out.println("Unsere grosse Gesellschaft" + Flugzeug.meineLG());
double aktuellesGewicht = lh1.gewicht();
System.out.println("aktuelle Gewicht von lh1: " + aktuellesGewicht);
pad.gate[1] = lh1;
// Airbus A380 https://de.wikipedia.org/wiki/Airbus_A380#A380-800
Flugzeug lh2 = new Flugzeug(40000, 400000, "D-AIMA");
lh2.einsteigen(11);
lh2.einsteigen();
lh2.einsteigen(4);
// Wir wollen wieder austteigen
lh2.aussteigen(5);
pad.gate[2] = lh2;
System.out.println("Flughafen " + pad.name);
System.out.println("Am Gate 1: " + pad.gate[1].kennzeichen +
" Passagiere: " + pad.gate[1].getPassagiere() +
" akt. Gewicht: " + pad.gate[1].gewicht());
System.out.println("Am Gate 2: " + pad.gate[2].kennzeichen +
" Passagiere: " + pad.gate[2].getPassagiere() +
" akt. Gewicht: " + pad.gate[2].gewicht());
pad.drucken();
}
}
Klasse Flugzeug
package s1.airline.block7;
/**
* Das ist ein Flugzeug des 7.ten Blocks
* @author s@scalingbits.com
*/
public class Flugzeug {
final static double durchschnittsgewicht = 75;
String kennzeichen;
private int passagiere;
int maxPassagiere = 100;
final double maximalesGewicht;
final double leerGewicht;
public int getPassagiere() {
return passagiere;
}
public Flugzeug(double minGewicht, double maxGewicht) {
System.out.println("Ich baue jetzt ein Flugzeug");
if (minGewicht > 0) leerGewicht = minGewicht;
else leerGewicht = 0;
if ((maxGewicht > 0) && (maxGewicht>=leerGewicht))
maximalesGewicht = maxGewicht;
else maximalesGewicht = leerGewicht;
passagiere = 0;
}
public Flugzeug(double minGewicht, double maxGewicht, String kennz) {
this(minGewicht,maxGewicht);
kennzeichen = kennz;
}
/**
* Einsteigen eines einzelnen Passagiers
*/
public void einsteigen() {
if (passagiere >= maxPassagiere) { // Zuviele Einsteiger
System.out.println("Fehler: Einsteigen verweigert ");
} else { // passt schon
passagiere++;
}
}
/**
* Einsteigen einer vorgegebenen Anzahl Passagiere.
* und sie funktioniert auch!
* Wenn zuviele einsteigen möchten, darf keiner einsteigen!
* @param einsteiger Anzahl der Passagiere die einsteigen.
*/
public void einsteigen(int einsteiger) {
if (passagiere+einsteiger > maxPassagiere) {
System.out.println("Fehler: Einsteigen verweigert ");
} else {
System.out.println("Ich lasse " + einsteiger + " Passagiere eingestiegen");
einsteigen(); // Einer steigt ein
if (einsteiger > 1)
einsteigen(einsteiger-1); //Der Rest steigt ein
}
}
public void aussteigen(final int aussteiger) {
if (passagiere >= aussteiger) {
passagiere -= aussteiger;
} else {
System.out.println("Fehler: Flugzeug ist leer... ");
}
} // Ende der Methode aussteigen
/**
* berechnet das aktuelle Gewicht des Flugzeugs
* @return aktuelle Gewicht
*/
public double gewicht() {
//double g = leerGewicht+passagiere*durchschnittsgewicht;
return leerGewicht+passagiere*durchschnittsgewicht;
}
public static String meineLG() { return "Lufthansa";}
}
- 8587 views
7.5 Lösungen
7.5 Lösungen7.5.1 Telefonbuchanwendung (1)
package s1.block7;
/**
*
* @author s@scalingbits.com
*/
public class Telefonbuch1 {
private String[] vorname;
private String[] nachname;
private String[] telefonnummer;
/**
* Anlegen eines Telefonbuchs für 4 Einträge
*/
public Telefonbuch1 () {
// Telefonbuch mit initial 4 Einträgen
vorname = new String[4];
nachname = new String[4];
telefonnummer = new String[4];
}
/**
* Einfügen einer neuen Telefonnummer mit automatischem
* Vergrössern um 50% des Telefonbuchs
* @param vn Vorname
* @param nn Nachname
* @param nr Telefonnummer
*/
public void einfuegen(String vn, String nn, String nr) {
//Leere Einträge haben keine Telefonummer!
int pos=0; //Suchposition
while ((pos<telefonnummer.length)
&& !(telefonnummer[pos]==null)
) pos++;
if (pos>=telefonnummer.length) // kein freier Eintrag!
{ // Feld um 50% vergroessern und alles umkopieren
String[] tempFeld;
tempFeld = new String[vorname.length*3/2];
System.arraycopy(vorname,0,tempFeld,0,vorname.length);
vorname = tempFeld;tempFeld = new String[nachname.length*3/2];
System.arraycopy(nachname,0,tempFeld,0,nachname.length);
nachname = tempFeld;tempFeld = new String[telefonnummer.length*3/2];
System.arraycopy(telefonnummer,0,tempFeld,0,telefonnummer.length);
telefonnummer = tempFeld;
// Jetzt ist Platz um etwas pos zu speichern!
}
vorname[pos]= vn;
nachname[pos]= nn;
telefonnummer[pos] = nr;
}
/**
* Loeschen eines Datensatzes wenn alle Parameter identisch sind
* @param vn vorname
* @param nn Nachname
* @param nr Vorname
*/
public void loesche(String vn, String nn, String nr) {
//Leere Einträge haben keine Telefonummer!
int pos=0; //Suchposition
while ((pos<telefonnummer.length)
&& (telefonnummer[pos]!=null)
&& !(telefonnummer[pos].equals(nr))
&& !(vorname[pos].equals(vn))
&& !(nachname[pos].equals(nn))
) pos++;
if (pos<telefonnummer.length) // Kandidat zum loeschen steht aus pos
{ // Suche hoechsten Eintrag
int loesche = pos;
while ((pos<telefonnummer.length)
&& (telefonnummer[pos]!=null)
) pos++;
pos--; // Einmal zurücksetzen
// Höchsten Datensatz umkopieren und dann ausnullen
// Der alte Datensatz wird dereferenziert
vorname[loesche] = vorname[pos];
vorname[pos] = null;
nachname[loesche] = nachname[pos];
nachname[pos] = null;
telefonnummer[loesche] = telefonnummer[pos];
telefonnummer[pos]=null;
}
}
/**
* Ausdrucken des Telefonbuchs
*/
public void drucken() {
System.out.println("Telefonbuch. Groesse: " + telefonnummer.length);
for (int i=0; i<telefonnummer.length; i++ ){
System.out.print("[" + i + "]: ");
System.out.print( vorname[i] + " | ");
System.out.print( nachname[i] + " | ");
System.out.println( telefonnummer[i] + " |");
}
}
/**
* Testroutine
*/
public static void test() {
Telefonbuch1 buch = new Telefonbuch1();
System.out.println("*** Leeres Telefonbuch ***");
buch.einfuegen("Manuel","Neuer","0171 1");
buch.einfuegen("Philipp","Lahm","0171 2");
buch.einfuegen("Jérome","Boateng","0171 3");
buch.einfuegen("Mats","Hummels","0171 4");
buch.einfuegen("Benedikt","Höwedes","0171 5");
buch.einfuegen("Christoph","Kramer","0171 6");
buch.einfuegen("Bastian","Schweinsteiger","0171 8");
buch.einfuegen("Thomas","Müller","0171 9");
buch.einfuegen("Toni","Kroos","0171 10");
buch.einfuegen("Per","Mertesacker","0171 11");
buch.einfuegen("Miroslav","Klose","017 12");
//
System.out.println("*** Ganze Mannschaft im Telefonbuch ***");
buch.drucken();
System.out.println("*** Kramer raus, Schürrle rein ***");
buch.loesche("Christoph","Kramer","0171 6");
buch.einfuegen("André","Schürrle","0171 7");
buch.drucken();
System.out.println("*** Klose raus, Götze rein ***");
buch.einfuegen("Miroslav","Klose","017 12");
buch.einfuegen("Mario","Götze","0171 13");
buch.drucken();
System.out.println("... und Weltmeister");
}
/**
* Hauptprogramm
* @param args
*/
public static void main(String[] args) {
test();
}
}
7.5.2 Telefonbuch (2)
Klasse Person
package s1.block7;
/**
*
* @author s@scalingbits.com
*/
public class Person {
final public String vorname;
final public String nachname;
final public String telefonnummer;
/**
* Der Konstruktor erlaubt das Belegen der Attribute. Sie können
* spaeter nicht mehr geändert werden
* @param vn Vorname
* @param nn Nachname
* @param nr Telefonnummer
*/
public Person (String vn, String nn, String nr) {
vorname = vn;
nachname = nn;
telefonnummer = nr;
}
/**
* Standardisierte Vergeleichsoperation in Java
* @param o Das Objekt mit dem verglichen werden soll
* @return wahr wenn Objekte gleich sind
*/
@Override
public boolean equals(Object o) {
Person p = (Person) o;
return ((vorname.equals(p.vorname))
&&(nachname.equals(p.nachname))
&&(telefonnummer.equals(p.telefonnummer)));
}
/**
* Standardisierte Methode zum Konvertieren eines Objekts in eine
* Zeichenkett
* @return Das Objekt in einer Repräsentation als Zeichenkette
*/
@Override
public String toString(){
return (" " +vorname + " | " + nachname + " | " + telefonnummer +" |");
}
}
Klasse Telefonbuch2 (Hauptprogramm)
package s1.block7;/**** @author s@scalingbits.com*/public class Telefonbuch2 {private Person[] leute;/*** Anlegen eines Telefonbuchs für 4 EInträge*/public Telefonbuch2 () {// Telefonbuch mit initial 4 Einträgenleute = new Person[4];}/*** Einfügen einer neuen Telefonnummer mit automatischem* Vergrössern um 50% des Telefonbuchs* @param p Person*/public void einfuegen(Person p) {//Leere Einträge haben keine Telefonummer!int pos=0; //Suchpositionwhile ((pos<leute.length)&& (leute[pos]!=null)) pos++;if (pos>=leute.length) // kein freier Eintrag!{ // Feld um 50% vergroessern und alles umkopierenPerson[] tempFeld;tempFeld = new Person[leute.length*3/2];System.arraycopy(leute,0,tempFeld,0,leute.length);leute = tempFeld;// Jetzt ist Platz um etwas pos zu speichern!}leute[pos]= p;}/*** Loeschen eines Datensatzes wenn alle Parameter identisch sind* @param p zu löschende Person*/public void loesche(Person p) {//Leere Einträge haben keine Telefonummer!int pos=0; //Suchpositionwhile ((pos<leute.length) // Noch nicht am Ende des Feldes&& (leute[pos]!=null) // Es gibt einen Eintrag&& !(leute[pos].equals(p)) ) // Er passt nichtpos++;if (pos<leute.length) // Kandidat zum loeschen steht aus pos{ // Suche hoechsten Eintragint loesche = pos;while ((pos<leute.length)&& (leute[pos]!=null)) pos++;pos--; // Einmal zurücksetzen// Höchsten Datensatz umkopieren und dann ausnullen// Der alte Datensatz wird dereferenziertleute[loesche] = leute[pos];leute[pos] = null;}}/*** Ausdrucken des Telefonbuchs*/public void drucken() {System.out.println("Telefonbuch. Groesse: " + leute.length);for (int i=0; i< leute.length; i++)System.out.println("[" + i + "]: " +leute[i]);}/*** Testroutine*/public static void test() {Telefonbuch2 buch = new Telefonbuch2();System.out.println("*** Leeres Telefonbuch ***");buch.einfuegen(new Person("Manuel","Neuer","0171 1"));buch.einfuegen(new Person("Philipp","Lahm","0171 2"));buch.einfuegen(new Person("Jérome","Boateng","0171 3"));buch.einfuegen(new Person("Mats","Hummels","0171 4"));buch.einfuegen(new Person("Benedikt","Höwedes","0171 5"));buch.einfuegen(new Person("Christoph","Kramer","0171 6"));buch.einfuegen(new Person("Bastian","Schweinsteiger","0171 8"));buch.einfuegen(new Person("Thomas","Müller","0171 9"));buch.einfuegen(new Person("Toni","Kroos","0171 10"));buch.einfuegen(new Person("Per","Mertesacker","0171 11"));buch.einfuegen(new Person("Miroslav","Klose","017 12"));//System.out.println("*** Ganze Mannschaft im Telefonbuch ***");buch.drucken();System.out.println("*** Kramer raus, Schürrle rein ***");buch.loesche(new Person("Christoph","Kramer","0171 6"));buch.einfuegen(new Person("André","Schürrle","0171 7"));buch.drucken();System.out.println("*** Klose raus, Götze rein ***");buch.einfuegen(new Person("Miroslav","Klose","017 12"));buch.einfuegen(new Person("Mario","Götze","0171 13"));buch.drucken();System.out.println("... und Weltmeister");}/*** Hauptprogramm* @param args*/public static void main(String[] args) {test();}}
7.5.3 Zufallszahlengenerator
package s1.block7; /** * * @author s@scalingbits.com */public class Zufallszahlen {
/**
* Drucke dreiminensionale Felder
* @param feld dieses Feld wird gedruckt
*/
public static void feldDrucken(int[][][] feld) {
for (int i=0; i<feld.length; i++) {
System.out.println("*** Kubusebene["+i+"] ***");
feldDrucken(feld[i]);
}
}
/**
* Drucke zweidimensionale Felder
* @param feld dieses Feld wird gedruckt
*/
public static void feldDrucken(int[][] feld) {
for (int i=0; i<feld.length; i++) {
System.out.print("["+i+"]: ");
feldDrucken(feld[i]);
System.out.println(""); // Zeilenumbruch provozieren
}
}
/**
* Drucke eindinensionale Felder
* @param feld dieses Feld wird gedruckt
*/
public static void feldDrucken(int[] feld) {
for (int i=0; i<feld.length; i++) {
System.out.print(" "+feld[i]+" |"); // kein Zeilenumbruch
}
}
/**
* Suche die am häufigsten vorkommende Zahl
* @param feld Feld mit Häufigkeiten
* @return haeufigste Zahl im Feld
*/
public static int haeufigsteZahl(int[][][] feld) {
int max = -1;
int ergebnis = 0;
for (int i=0; i<feld.length; i++)
for (int j=0; j<feld[i].length; j++)
for (int k=0; k<feld[i][j].length;k++)
if (feld[i][j][k] > max) {
max = feld[i][j][k];
ergebnis=i*100+j*10+k;
}
System.out.println("Häufigste Vorkommen: " + max);
return ergebnis;
}
/**
* Suche die am seltensten vorkommende Zahl
* @param feld Feld mit Häufigkeiten
* @return das Eregebnis
*/
public static int seltensteZahl(int[][][] feld) {
int min = Integer.MAX_VALUE;
int ergebnis = 0;
for (int i=0; i<feld.length; i++)
for (int j=0; j<feld[i].length; j++)
for (int k=0; k<feld[i][j].length;k++)
if (feld[i][j][k] < min) {
min = feld[i][j][k];
ergebnis=i*100+j*10+k;
}
System.out.println("Seltenstes Vorkommen: " + min);
return ergebnis;
}
public static void main(String[] args) {
int sampleSize = 1000000;
int[] sample = new int[sampleSize];
System.out.println("Feld mit " + sampleSize + " Zellen angelegt." );
for (int i=0; i<1000000; i++)
sample[i] = (int)(Math.random()* 1000D);
System.out.println("Feld mit " + sampleSize + " Zufallszahlen belegt." );
int[][][] verteilung = new int[10][10][10];
for (int wert: sample) {
verteilung[(wert/100)%10][(wert/10)%10][wert%10]++;
}
System.out.println("Verteilungsfeld belegt mit " + sampleSize + " Zufallszahlen." );
feldDrucken(verteilung);
System.out.println("Suche häufigst vorkommende Zahl");
System.out.println("Häufigste Zahl " + haeufigsteZahl(verteilung));
System.out.println("Suche seltenst vorkommende Zahl");
System.out.println("Seltenste Zahl " + seltensteZahl(verteilung));
}
}
7.5.4 Conway's Spiel des Lebens
package s1.block7;/*
* @author scalingbits.com
*/
public class GenerationLoesung {
/**
* Groesse des quadratischen Feldes
*/
private int size = 50;
private boolean[][] alt;
private boolean[][] aktuell;/**
* Anlegen aller benoetigten Felder mit Initialwerten
* Alle Feldelemente sollen mit dem Zustand "false" = leer versehen sein
*/
public GenerationLoesung() {
aktuell = new boolean[size][size];
alt = new boolean[size][size];
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++) {
aktuell[i][j] = false;
alt[i][j] = false;
}
}/**
* @return Kantenlaenge des quadratischen Felds
*/
public int groesse() {
return size;
}/**
* Berechnen einer neuen Generation.
* Legen Sie ein neues Feld an. Berechnen Sie den neuen Zustand
* aller Feldelement aus dem alten Feld
*/
void neueGeneration() {
alt = aktuell;
aktuell = new boolean[size][size];
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++) {
aktuell[i][j] = false;
// Zaehle Nachbarn
int nachbar = 0;
if ((i > 0) && alt[i - 1][j]) nachbar++; //links
if ((i + 1 < size) && alt[i + 1][j]) nachbar++; //rechts
if ((j > 0) && alt[i][j - 1]) nachbar++; //oben
if ((j + 1 < size) && alt[i][j + 1]) nachbar++; //unten
if ((i > 0) && (j > 0) && alt[i - 1][j - 1]) nachbar++; //links,oben
if ((i > 0) && (j + 1 < size) && alt[i - 1][j + 1]) nachbar++; //links, unten
if ((i + 1 < size) && (j > 0) && alt[i + 1][j - 1]) nachbar++; //rechts, oben
if ((i + 1 < size) && (j + 1 < size) && alt[i + 1][j + 1]) nachbar++; //rechts, unten// Übernehmen des alten Status als Default
aktuell[i][j] = alt[i][j];
// Geburt einer neuen Zelle
if ((!alt[i][j]) && (nachbar == 3)) aktuell[i][j] = true;
// Tod einer alten Zelle wegen Einsamkeit
if ((alt[i][j]) && (nachbar < 2)) aktuell[i][j] = false;
// Tod einer alten Zelle wegen Überbevölerung
if ((alt[i][j]) && (nachbar > 3)) aktuell[i][j] = false;
}
}/**
* Das Feld mit den aktuellen Werten
*
* @return
*/
public boolean[][] status() {
return aktuell;
}
}
7.5.5 Flughafenbeispiel aus der Vorlesung
Mit einem Feld eingebaut
Flughafen
package s1.airlineSolution.block7;
public class Flughafen {
String name;
Flugzeug[] gate;
double treibstoffLager;
/**
*
* @param name Name des Flugzeuge
* @param gates Anzahl der Gates
*/
public Flughafen(String name, int gates) {
this.name = name;
gate = new Flugzeug[gates];
}
public static void main(String[] args) {
Flughafen pad = new Flughafen("Paderborn",6);
pad.treibstoffLager = 1000000;
// Boeing 747, https://de.wikipedia.org/wiki/Boeing_747#747-400
Flugzeug lh1 = new Flugzeug("ABTL",450000,200,200);
lh1.einsteigen(120);
double aktGewicht=lh1.gewicht();
System.out.println("gewicht" + aktGewicht);
lh1.drucken();
pad.gate[1] = lh1;
pad.gate[2] = lh1;
System.out.println("Boarding für lh1 nach Mannheim...");
pad.gate[1].einsteigen(17);
pad.gate[1].einsteigen(2);
pad.gate[2].einsteigen();
lh1.einsteigen(2);
lh1.drucken();
// Airbus A380 https://de.wikipedia.org/wiki/Airbus_A380#A380-800
Flugzeug lh2 = new Flugzeug("ABTL",500000,100,200);
lh2.einsteigen(100);
lh2.drucken();
pad.gate[2] = lh2;
pad.drucken();
// Hänge Flugzeug um. mover bewegt Flugzeug
// von Gate 1 nach Gate 3
Flugzeug mover = pad.gate[1];
pad.gate[1]=null;
pad.drucken();pad.gate[3]= mover;
mover=null;
}
/**
* druckt alle wichtigen Informationen ueber einen Flughafen auf die Konsole
*/
public void drucken() {
System.out.println("*** Unser Flughafen ***");
System.out.println("Flughafen " + name);
for (int i=0; i<gate.length; i++) {
if (gate[i] != null)
System.out.println("Am Gate " + i +": " + gate[i]);
else
System.out.println("Gate " + i +" unbelegt");
}
System.out.println("Treibstoff: " + treibstoffLager);
System.out.println("***********************");
}
}
Flugzeug
package s1.airlineSolution.block7;
public class Flugzeug {public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die einsteigen sollen
*/
public void einsteigen(int anzahl) {
if ((anzahl >0) && (passagiere+anzahl) <= maxPassagiere) {
passagiere+= anzahl;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die aussteigen sollen
*/
public void aussteigen(int anzahl) {
if ((anzahl >0) && (passagiere-anzahl) >=0) {
passagiere-= anzahl;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public static int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}
/**
* Ueberschriebene Methode die das Kennzeichen anstatt der Objektidentitaet
* druckt
* @return
*/
public String toString() {return kennzeichen;}}
- 5798 views
7.6 Lernziele
7.6 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zu Feldern zu beantworten.
Feedback
- 4591 views
8. Vererbung
8. VererbungVererbung ist ein wichtiger Bestandteil der objektorientierten Programmierung. Vererbung ergänzt die bisher vorgestellten Konzepte der
- Aggregation (Klassen, Felder, Datenstrukturen)
- Assoziation (Referenzen)
um ein weiteres Strukturierungsmittel.
Der erste Abschnitt beschäftigt sich mit dern Konzepten der Vererbung, die folgenden beschreiben die Implementierung in Java.
- 9570 views
8.1 Das Konzept der Vererbung
8.1 Das Konzept der VererbungDie Ziele die mit Vererbung in der objektorientierten Programmierung verfolgt werden sind:
- Wiederverwendung
- eine Klasse vererbt Eigenschaften an eine andere.
- Dies bedeutet: Man nutzt Daten und Methoden einer Klasse
- Modellierung in Hierarchien
- Hierarchien sind gängige Konzepte des Alltagsleben und finden sich in der Begrifflichkeit des Alltagslebens wieder. Eine Modellierung die Hierarchien unterstützt und vereinfacht das Verständnis und die Kommunikation.
- Hierarchien sind Konzepte die stark bei der Abgrenzung zwischen ähnlichen Dingen helfen
Beispiel einer Vererbungshierarchie: |
Die gleiche Begriffshierarchie lässt sich auch als Mengendiagramm darstellen: |
Alle Mitglieder einer Teilmenge haben die gleichen Eigenschaften.
Das Mengendiagramm verdeutlicht, dass es sich bei der Vererbungshierarchie um eine "Ist-ein" Relation handelt.
Vererbung und Klassen
Die Konzepte der Vererbungshierarchien lässt sich leicht auf Klassen abbilden
Das Diagramm rechts verdeutlicht die Beziehungen zwischen den Klassen, ihrer Bedeutung und der Typisierung. |
Ein- und Mehrfachvererbung
Programmiersprachen wie C++ erlauben die Benutzung von Mehrfachvererbung. Die Sprache Java erlaubt jedoch nur eine einfache Vererbung.
- Einfachvererbung: Eine Unterklasse erbt von genau einer Oberklasse
- Mehrfachvererbung: Eine Unterklasse erbt von mehreren Oberklassen
Das Konzept der Mehrfachvererbung durchbricht zu einem gewissen Grad das Konzept der Spezialisierung-Generalisierung. Bei der Einfachvererbung sind Personen Mitarbeiter oder Berater aber nicht beides gleichzeitig. Bei der Mehrfachvererbung mit einer einzelnen Basisklasse werden Unterklassen zuerst als disjunkte Klassen definiert. Später sollen Unter-Unterklassen jedoch die gleichen Eigenschaften über verschieden Vererbungspfade erben.
Anmerkung
Mehrfachvererbung erhöht die Mächtigkeit sowie die Komplexität einer Sprache erheblich. Es kann hier leicht zu Effekten kommen die ein Entwickler nicht als intuitiv empfindet. Das Risiko des Fehlverhaltens einer Anwendung steigt hierdurch.
Beim Entwurf der Sprache Java wurde bewusst auf Mehrfachvererbung verzichtet um die Sprache einfach zu halten und um die Implementierung eines effizienten Laufzeitsystems zu vereinfachen.
Die Java Interface (Schnittstellen) Technologie bietet eine "Hintertür" zur Implementierung von Fällen in denen eine Mehrfachvererbung wünschenswert ist.
- 11442 views
8.2 Vererbung in Java
8.2 Vererbung in JavaDie Vererbungsbeziehung wird in Java mit dem Schlüsselwort extends beschrieben. Beispiel:
class Mitarbeiter extends Person { /* Klassenimplementierung */ }
Die Klasse Mitarbeiter ist eine Spezialisierung der Klasse Person. Sie erbt die öffentlichen Attribute und Methoden der Klasse Person.
Alle Klassen in Java erben direkt oder indirekt von der Java Basisklasse Object. Wird bei einer Klassendeklaration keine extends Klausel angegeben so wird die Klasse automatisch von der Klasse Object abgeleitet.
Die Klasse Object
... stellt für alle Javaklassen eine minimale Infrastruktur zur Verfügung:
- die Möglichkeit dynamisch Objekte auf dem Heap anzulegen
- eine Identität und die damit verbundene Möglichkeit zum Vergleich von Objekten
- die Möglichkeit des automatischen Löschens wenn keine Referenz mehr auf eine Instanz zeigt.
- Möglichkeit der Instrospektion (Dynamisches Erkunden der Struktur der Klasse)
Zugriff auf geerbte Methoden und Attribute
Oberklassen vererben öffentliche (public, protected) Methoden und Attribute an Unterklassen. Das heißt, dass sie wie eigene Attribute verwendet werden können.
Die Beispielklasse Person verfügt über:
- private Attribute für Vor- und Nachnamen
- öffentliche Methode zum Auslesen des Gesamtnamens
- öffentliche Methode zum Setzen des Namens
- ein öffentliches Attribut zum Verwalten des Alters
- Einen Konstruktor zum Setzen des Namens
Die Oberklasse Person
package s1.block8; public class Person { private String name; private String firstName; public int age; public Person(String ln, String fn) { name = ln; firstName = fn; } public Person() { this("Doe","John");} public void setName(String ln, String fn) { name = ln; firstName = fn; } public String fullName() {return (name + " " + firstName);} }
Die Klasse Employee soll jetzt die öffentlichen Eigenschaften der Oberklasse Person nutzen. So kann eine redundante Programmierung der schon existierenden Funktionalität vermieden werden.
Die Unterklasse Employee
package s1.block8; public class Employee extends Person { private String employeeId; public Employee(String ln, String fn, String EmpId, int a) { super(ln,fn); // Java ruft hier den Konstruktor der Oberklasse auf employeeId = EmpId; age = a; } public String getEmployeeId() {return employeeId;} public String printAll() { return ( fullName() + " " + employeeId + " " + age); } public static void main(String[] args) {
Employee ceo = new Employee("Doe","Jane", "1", 25);
Employee cto = new Employee("Miller","John","2", 30);cto.age++;
System.out.println(ceo.printAll());
System.out.println(cto.printAll());
} }
Benutzung der Klasse
Die so implementierte Klasse kann wie folgt verwendet werden:
Doe Jane 1 25 Miller John 2 31
Alle öffentlichen Attribute und Methoden (Beispiel age) der Klasse Person können in der Klasse Employee genutzt werden als ob es eigene/lokale sind.
Die Vererbung funktioniert jedoch nicht rückwärts. Man kann einer Instanz von Person keine Angestelltennummer (employeeId) zuweisen. Sie existiert in der Klasse Person nicht.
"Vertragsrecht": Alle Instanzen von Employee müssen sich auch genau wie Instanzen von Person verhalten. Sie sind auch Instanzen der Klasse Person. Andere Konsumenten im Programm verlassen sich auf dieses Verhalten, diesen "Vertrag". Dies bedeutet:
- Die Unterklasse muss eventuell erzwungene Intialisierungen der Oberklasse achten in dem Sie rekursiv deren Konstruktoren aufruft.
- Veröffentlichte Attribute und Methoden können/sollen nicht mehr blockiert werden. Beispiel: In einem Unternehmen darf das Alter aufgrund von rechtlichen Bestimmungen nicht veröffentlicht werden
Vererbung in UML
Implementierung ohne Vererbung | Implementierung mit Vererbung |
---|---|
Hier eine Implementierung der Klasse Employee ohne Vererbung. Die Implementierung entspricht der rechten Implementierung mit Vererbung. Die Methoden der Klasse haben hier aber direkten Zugriff auf die privaten Attribute name, firstName. |
Eine Implementierung der Klasse Employee mit Vererbung der Klasse Person Vererbung |
Vererbung privater Attribute und Methoden
Private Attribute werden nicht vererbt!
Sie sind nur innerhalb der Klasse sichtbar. Eine Unterklasse hat keinen priviligierten Zugriff auf private Methoden. Sie werden nicht vererbt. Eine Unterklasse muss wie jede andere Klasse über vorhandene öffentliche Zugriffsmethoden auf die privaten Attribute zugreifen.
Vererbung und Konstruktoren
Konstruktoren werden in Java nicht vererbt. Man Sie nur indirekt über den new() Operator für eine gegebene Klasse aufrufen.
Konstruktoren müssen für jede Unterklasse implementiert werden. Die Konstruktoren der Unterklasse müssen jedoch in der Lage sein existierende Konstruktoren der Oberklasse aufzurufen, da sie für die Initialisierung der Oberklasse notwendig sind. Hierzu wird das Schlüsselwort super() verwendet.
Hiermit kann man jetzt die Klasse Employee() eleganter und robuster Initialisieren
public class Employee extends Person { private String employeeId; public Employee(String ln, String fn, String EmpId, int a) { super(fn,ln); // Java ruft hier den Konstruktor der Oberklasse auf employeeId = EmpId; age = a; } public void printAll() { System.out.println ( fullName() + " " + employeeId + " " + age); }
Wichtig: Der Aufruf des Konstruktors der Oberklasse muss als erster Befehl im Konstruktor der Unterklasse stehen. Die Attribute der Oberklasse müssen als erstes initialisiert werden, bevor die Attribute der Unterklasse initialisiert werden. Nur so kann gewährleistet werden dass sich Instanzen der Oberklasse korrekt verhalten. Würde der Aufruf des Konstruktors der Oberklasse nicht als erstes geschehen, könnte die Unterklasse die Oberklasse inkorrekt manipulieren.
Unterklassenkonstruktoren ohne super() Aufruf
Konstruktoren müssen nicht einen bestimmten Konstruktor der Oberklasse explizit aufrufen.
Wird kein super() Aufruf implementiert, so generiert der Übersetzer automatisch einen super() Aufruf der den Standard(default)konstruktor aufruft.
Hiermit wird immer ein vollständiges Durchlaufen der Klassenhierachie beim Initialisieren gewährleistet.
Besitzt die Oberklasse nur Konstruktoren mit Übergabeparameter so muss einer dieser Konstruktoren über super() aufgerufen werden.
Im folgenden Beispiel kann man die Initialisierungskette einer Klasse C sehen die aus B abgeleitet wird. B wird aus A abgeleitet:
public class A { public A() {System.out.println("Constructor A called");} } public class B extends A{ public B() {System.out.println("Constructor B called");} } public class C extends B { public C() {System.out.println("Constructor C called");} }
Das erzeugen einer Instanz von C:
C c = new C();
Triggert die folgenden Ausgaben:
Constructor A called Constructor B called Constructor C called
- 147424 views
8.3 Überschreiben (Overriding)
8.3 Überschreiben (Overriding)Das im vorgehenden Abschnitt vorgestellte Konzept des Vererbens ging immer von einer Erweiterung der Klasse aus. Das heißt mehr und mehr Attribute und Methoden kommen hinzu.
Es kommt jedoch vor, das eine spezialisierte Klasse eine existierende Methode der Oberklasse "verfeinern" möchte. Dieses Konzept der Implementierung einer Methode oder Attributs welches eine Methode oder Attribut aus der Oberklasse neu implementiert, wird in der objektorientierten Programmierung Überschreiben (Overriding) genannt.
Überschreiben von Methoden
Java erlaubt die Methode einer Oberklasse zu überschreiben, das bedeutet sie zu verdecken, wenn die folgenden Bedingungen für die Deklaration der Methode erfüllt sind
- Namensgleicher Methodenname
- Eingabeparameter sind von Anzahl, Reihenfolge und Typ identisch
- Rückgabeparameter ist identisch
- Zugriffsrechte der Methode der Oberklasse (public, protected, private) werden nicht eingeschränkt
Der Methodenrumpf kann mit einer beliebigen Implementierung ausgeführt werden. Mit dieser Technik kann eine Unterklasse eine eigene Implementierung für eine allgemeinere Methode einer Überklasse zur Verfügung stellen.
Im vorhergehenden Fall der Klasse Employee kann man eine spezialisierte Klasse Manager implementieren, die ein zusätzliches Budget verwalten kann. Das Budget soll ebenfalls mit der Methode printAll() ausgeben werden.
package s1.block8; public class Manager extends Employee { public int budget; public Manager(String ln, String fn, String EmpId, int a, int b) { super(ln, fn,EmpId,a); budget = b; } public String printAll() { return ( fullName() + " " + getEmployeeId() + " " + age + " " + budget); } public static void main(String[] args) {
Employee ceo = new Employee("Doe","Jane", "1", 25); Employee cto = new Employee("Miller","John","2", 30); Employee man1 = new Manager("Johnson","Susan","3", 29, 30000); cto.age++; System.out.println(ceo.printAll()); System.out.println(cto.printAll()); System.out.println(man1.printAll());; } }
...erzeugt die folgenden Ausgaben:
Doe Jane 1 25 Miller John 2 31 Johnson Susan 3 29 30000
Überschriebene Methoden sind miteinander verwandt und eine Benutzung der Methode der Oberklasse ist gewünscht um Codereplikation und Redundanz zu vermeiden. Hierfür existiert das Schlüsselwort super. Es erlaubt das Aufrufen der überschriebenen Methode mit der folgenden Syntax:
super.MethodenName(para_1, ..,para_n)
Im Fall der Klasse Manager kann man die printAll() Methode mit dem Schlüsselwort super vereinfachen:
public class Manager extends Employee { public int budget; public Manager(String ln, String fn, String EmpId, int a, int b) { super(ln, fn,EmpId,a); budget = b; } public String printAll() { return super.printAll() + " " + budget; } }
Hinweis: Die Syntax super.super.methodenname() ist nicht möglich. Man kann nicht die Methode einer Ober-Oberklasse unter Auslassung der Oberklasse aufrufen.
Suchalgorithmus der Laufzeitumgebung
Da in Java alle Klassen einzeln übersetzt werden können, kann man erst zur Laufzeit entscheiden welche Methode aufgerufen werden muss (dynamic invocation). Die Laufzeitumgebung geht bei jedem Aufruf wie folgt vor
- Bestimme Typ des Objekts
- Versuche Methode zum passenden Typ (Klasse) auszuführen
- Versuche rekursiv in der Oberklasse die Methode auszuführen
Überschreiben von Attributen
Für das Überschreiben von Attributen gelten die gleichen Regeln wie für das Überschreiben von Methoden:
- Namensgleicher Attributname
- Zugriffsrechte des Attributs der Oberklasse (public, protected) werden nicht eingeschränkt
Private Attribute werden nicht vererbt. Sie sind der Unterklasse weder bekannt noch zugänglich!
Der Zugriff auf ein überschriebenes Attribut der Oberklasse geht symmetrisch zu den Methoden mit dem super Schlüsselwort. Beispiel:
a = super.a * 2;
Die @Override Annotation
Java kennt seit JDK 1.5 Annotationen. Annotationen sind Hinweise für den Übersetzer.
Es ist guter Stil wenn man einer Methode die Überschrieben wird die Annotation
@Override
voranstellt. @Overriding ist eine fest eingebaute Annotation, die den Übersetzer zu einer Fehlermeldung zwingt wenn die aufgewählte Methode nicht die Methode der Oberklasse überschreibt (Siehe Oracle Tutorial Annotationen).
Javaentwicklungsumgebungen tendieren eine Warnung zu erzeugen, falls eine überschriebene Methode nicht mit dieser Annotation erzeugt wurde.
- 51548 views
"In vielen Fällen sind
"In vielen Fällen sind überladene Methoden miteinander verwandt und eine Benutzung der Methode der Oberklasse ist gewünscht um Codereplikation und Redundanz zu vermeiden."
Das sollte doch bestimmt überschriebene statt überladene heißen? :)
- Log in to post comments
Aua, stimmt.
Da habe ich mir selbst ein Bein gestellt. Fehler wurde korrigiert. Der Satz wurde auch leicht verändert.
- Log in to post comments
8.4 Abstrakte und finale Klassen
8.4 Abstrakte und finale KlassenFinale Klassen und Methoden
Java bietet die Möglichkeit Klassen und Methoden zu implementieren die nicht mehr abgeleitet oder überschrieben werden dürfen. Hierzu dient das Schlüsselwort final.
Finale Methoden
Mit dem Schlüsselwort final kann man das Überschreiben der Methode in einer Unterklasse unterbinden. Ein typsiches Beispiel hierfür ist:
public final void print() { /* Methodenrumpf */}
Unterklassen mit überschriebenen Methoden sind ein sehr mächtiges Konzept für den Entwickler einer Unterklasse. Eine Unterklasse kann inhaltlich eine Oberklasse in weiten Bereichen neu implementieren, solange die Signaturen der Methoden gewahrt bleiben und ein Konstruktor der Oberklasse benutzt. Die Kontrolle liegt fast vollständig in den Händen der Unterklasse. das Schlüsselwort final erlaubt dem Entwickler der Oberklasse
- das Überschreiben zu verbieten
- und damit die Sichtbarkeit der Methode der Oberklasse für beliebige Konsumenten der Unterklasse zu garantieren
Dem Entwickler der Unterklasse steht es nach wie vor frei Methoden mit anderen Signaturen zu verwenden.
Finale Klassen
Finale Klassen verbieten die Spezialisierung einer Klasse durch eine Unterklasse.
Das Schlüsselwort final kommt im folgenden Beispiel zum Einsatz:
final class chiefExecutiveOfficer extends Manager { /* Klassen Implementierung */}
Beispiel
- Systemklasse String: Die Klasse String ist final, da Zeichenketten hochoptimiert und nicht veränderbar sind. Das Javalaufzeitsystem verwendet spezielle Optimierungen wie Stringpools und es nutzt die Unveränderbarkeit von Zeichenketten um dem Anwender Zugriff auf spezielle Zeichenketten des Laufzeitsystems zu geben. Die Möglichkeit diese Klasse zu spezialisieren wurde unterbunden um Sicherheit und Leistung zu garantieren.
Abstrakte Klassen und Methoden
Abstrakte Klassen
Abstrakte Klassen und Java-Schnittstellen (Stoff des zweiten Semester) dienen in Java zur Modularisierung und damit oft zur Arbeitsteilung zwischen Entwicklern. Abstrakte Klassen erlauben es dem Entwickler eine Oberklasse zu implementieren die nicht selbst instanziiert werden darf. Dieses Konzept erlaubt es Klassen als Vorlagen zu implementieren und gleichzeitig zu erzwingen, dass die Entwickler der Unterklasse fehlende oder eigene Methoden und Attribute zur Verfügung stellen. Hiermit lassen sich allgemeine Konzepte implementieren die zu allgemein sind um Instanziierungen zu erlauben, Dies erfolgt mit dem Schlüsselwort abstract bei der Spezifikation der Klasse.
Die Klasse Person im vorhergehenden Beispiel ist eine Klasse von der man nicht erlauben möchte, dass sie instanziiert wird. Dies geschieht zum Beispiel so:
public abstract class Person { private String name; private String firstName; public int age; public Person(String ln, String fn) { name = ln; firstName = fn; } public Person() { this("Doe","John");} public void setName(String ln, String fn) { name = ln; firstName = fn; } public String fullName() {return (name + " " + firstName);} }
Versucht man nun eine Person zu instanziieren:
Person p1 = new Person("persona","nongrata");
...führt dies beim Übersetzen zu folgendem Fehler:
javac Main.java Main.java:8: Person is abstract; cannot be instantiated Person p1 = new Person("persona","nongrata"); ^ 1 error
Abstrakte Klassen haben die typischen Eigenschaften von normalen Javaklassen. Sie haben Attribute und Methoden die vererbt werden können.
Wichtige Eigenschaften abstrakter Klassen |
---|
|
Beispiel
Die Systemklasse Number: Die Klasse Number enthält gemeinsame Methoden für zahlenartige Typen. Man kann sie jedoch nicht direkt instanziieren. Die daraus abgeleiteten Klassen Byte, Float etc. sind final. Sie sind hochoptimiert und sollen aus Performance- und Sicherheitsgründen nicht spezialisiert werden.
Abstrakte Methoden
Normale Methoden und Attribute werden an die Unterklasse vererbt und können auch bei Bedarf überschrieben werden.
Wird jedoch eine Methode mit dem Schlüsselwort abstract gekennzeichnet so muss sie von einer nicht abstrakten Unterklasse implementiert und überschrieben werden.
Beispiel
Die Klasse Person kann so erzwingen, dass eine printAll() Methode für alle Unterklassen implementiert muss, ohne sie selbst zu implementieren.
public abstract class Person { private String name; private String firstName; public int age; public Person(String ln, String fn) { name = ln; firstName = fn; } public Person() { this("Doe","John");} public void setName(String ln, String fn) { name = ln; firstName = fn; } public String fullName() {return (name + " " + firstName);} public abstract String printAll(); }
Regel: Eine Unterklasse einer abstrakten Klasse muss:
- alle abstrakten Methoden implementieren um nicht "abstrakt" zu sein
oder
- selbst eine abstrakte Klasse sein, falls sie nicht alle oder keine der abstrakten Methoden implementiert.
- 30156 views
Wort fehlt
Bei dem ersten Satz nach der Überschrift „Finale Methoden“ fehlt das Verb „kann“.
- Log in to post comments
8.5 Entwurfsüberlegungen zur Vererbung
8.5 Entwurfsüberlegungen zur VererbungDie Möglichkeiten der Vererbung sind sehr ausdrucksstark, haben viele Vorteile, bergen aber auch Risiken. Hierbei muss man mindestens drei verschieden Entwicklerrollen beachten:
- Implementierer der Oberklasse
- entwirft ein semantisches Konzept für eine Klassenhierarchie
- implementiert allgemeingültige Methoden und Attribute
- gibt Rahmenwerk durch Zugriffsmethoden, Zugriffskontrolle, explizite Möglichkeit zum Vererben und Überschreiben vor
- Implementierer der Unterklasse
- nutzt Infrastruktur
- muss sich an Syntax und Zugriffskontrollen halten
- macht Annahmen über Semantik der Oberklasse
- verfeinert und verändert Funktionalität
- Benutzer der Klassenhierarchie
- macht Annahmen zur gesamten Klassenhierarchie
- nutzt Syntax
- erschliesst Semantik aus Klassendeklaration, Dokumentation und experimenteller Benutzung
- muss sich an Syntax und Zugriffskontrollen aller Klassen halten
- macht Annahmen zur gesamten Klassenhierarchie
Semantische Integrität von Unterklassen
Unterklassen müssen Spezialisierungen der Oberklassen sein. Sie müssen sich in allen semantischen Belangen wie die Oberklasse verhalten.
Vorteile
- Existierende Methoden können
- benutzt werden
- komplett überschrieben werden
- überschrieben und die Methode der Oberklasse rekursiv genutzt werden
- Existierende Funktionalität von Oberklassen können sehr einfach genutzt werden
Risiken
- Überschriebene Methoden haben eine Semantik die nicht vom Entwickler der Oberklasse oder dem Benutzer der Unterklasse verstanden werden
- Das heißt, dass die Methoden eventuell unter falschen Annahmen benutzt werden und entsprechenden Schaden anrichten
- Unterklassen erben unnötige Infrastruktur (Methoden) die den Pflegeaufwand in die Höhe treiben oder externe Benutzer verwirren
Ändern von Oberklassen
Änderungen an Oberklassen sind heikel, da sie die Implementierungen der Unterklassen invalidieren können:
- Entfernen von öffentlichen Methoden oder Attributen wird die Unterklassen invalidieren. Überschriebene Methoden werden plötzlich zu regulären. Der Polymorphismus gilt nicht für identische Methoden zweier Unterklassen wenn die Methode nicht in der Oberklasse als abstrakte Methode implementiert ist
- Nicht abstrakte Methoden abstrakt machen invalidiert die Unterklassen. Alle Unterklassen müssen die abstrakte Methode implementieren
- Hinzufügen von Methoden oder Attributen birgt das Risiko, dass sie Aufgrund zufälliger Namensgleichheit in den Unterklassen zu überschriebenen Methoden führen
Wichtig: Die Modellierung einer Klassenhierarchie muss sehr sorgfältig geschehen. Das möglichst exakte Abbilden realer Hierarchien ist für eine langlebige und wartbare Implementierung sehr wichtig.
Alternativen zur Vererbung
Vererbung birgt auch Risiken da eine Klassenhierarchie relativ starr ist und individuelle Änderungen weitreichende Auswirkungen haben können.
Vererbung sollte immer dann eingesetzt werden wenn man eine "ist-ein" Beziehung modellieren möchte.
In vielen Fällen liegt aber auch eine "hat-ein" Beziehung vor, eine Assoziation. Hier sollte man erwägen zwei Objekte zu erzeugen und sie über eine Referenz zu verwalten.
Im vorhergehen Beispiel ist es eine Überlegung wert, das Budget der Klasse Manager als Referenz von der Klasse Employee auf eine Klasse Budget zu implementieren. Hierbei sind die folgenden Dinge abzuwägen:
- Nachteile der Assoziation
- Höherer initialer Implementierungsaufwand zur Datenkaspelung
- Referenz + Zugriffsmethoden + Belegung im Konstruktor mit Objekterzeugung des Budgets
- Jeder zusätzliche Aspekt der Klasse muss als Assoziation implementiert werden
- Zusätzliche Aspekte der Klasse sind nicht in der Klassenhierarchie sichtbar
- Zusätzlicher Speicherverbrauch durch zusätzliche Objekte
- Höherer initialer Implementierungsaufwand zur Datenkaspelung
- Vorteile der Assoziation
- Zukünftige Änderungen der assoziierten Klasse haben weniger Einfluss auf die gesamte Klassenhierarchie
- Der Implementierungs-Overhead einer eigenen Klasse entfällt (Konstruktoren, überschriebene Methoden etc.)
- 11066 views
8.6 Übungen
8.6 Übungen
8.6.1 Aufruf von überschriebenen MethodenImplementieren sie die Klasse TopClass mit den folgenden Eigenschaften
|
Implementieren Sie die Klasse LowClass welche aus TopClass abgeleitet mit den folgenden Eigenschaften
Implementieren Sie ein Hauptprogramm, dass folgende Befehle ausführt
|
Zu beobachten:
- Wieviel Instanzen welcher Klasse (TopClass, LowClass) wurden erzeugt?
- Wieviel Instanzen wurden insgesamt erzeugt?
- Was geschieht wenn das Attribut zaehler der Klasse LowClass gelöscht wird?
Methode zum Testen
Nutzen Sie die main() Methode der Klasse LowClass zum Testen:
public static void main(String[] args) { int size = 3; TopClass[] feldT = new TopClass[size]; LowClass[] feldL = new LowClass[size]; for ( int i=0; i<size; i++) feldT[i]= new TopClass(); for ( int i=0; i<size; i++) { feldL[i]= new LowClass(); feldL[i].test(); } System.out.println(TopClass.getZaehler() + " Instanzen TopClass generiert, "+ LowClass.getZaehler() + " Instanzen LowClass generiert"); }
8.6.2 Vererbung und Assoziation
Implementieren Sie eine Klasse Point mit den folgenden Eigenschaften:
- private Fliesskommaattribute für x und y Koordinate
- öffentlicher Konstruktor mit x und x als Parameter
- öffentliche setXY(x,y) Methode
- Auslesemethoden getX(), getY()
- print() Methode die eine Zeichenkette mit allen Werten liefert
- überschreiben die die toString() Methode von Object so das sie alle Werte für Point ausgibt.
Implementieren Sie eine Klasse CircleIsPoint die die Eigenschaften der Klasse Point hat und zusätzlich einen Radius verwalten kann mit folgenden Eigenschaften:
- erbt von Point
- das des Zentrum des Kreises sind die (x,y) Koordinaten von Point
- privates Fliesskomma Attribut für den Radius radius
- getRadius() und setRadius(r) Methoden zum Pflegen des Attributs
- öffentlicher Konstruktor für (x,y) Zentrum und Radius
- print() Methode die eine Zeichenkette mit allen Werten liefert
- überschreiben die die toString() Methode von Object so das sie alle Werte für Point ausgibt.
Implementieren Sie eine Klasse CircleHasPoint die die Eigenschaften der Klasse Point hat und zusätzlich einen Radius verwalten kann mit folgenden Eigenschaften:
- erbt nicht von Point
- benutzt privates Attribut vom Referenztyp auf Point
- das des Zentrum des Kreises sind die (x,y) Koordinaten von Point
- alle öffentlichen Methoden sind identisch zu CircleIsPoint!
Was unterscheidet beide Implementierungen? Welche ist die bessere Implementierung?
Klasse Main
Die drei obigen Klassen sollten mit der folgenden main() Methode in CircleIsPoint funktionieren:
package s1.block9; public class Main { public static void main(String[] args) { Point p1 = new Point (2.2, 3.3); Point p2 = new Point (2.22, 3.33); CircleIsPoint cip1 = new CircleIsPoint(4.4,5.5,6.6); p1.print(); cip1.print(); CircleHasPoint chp1 = new CircleHasPoint(44.4,55.5,66.6); chp1.print(); } }
8.6.3 Flughafenbeispiel
Die Referenzimplementierung ist im GitHub Repository dhbwjava im Block 8 zu finden.
- 9493 views
8.7 Lösungen
8.7 Lösungen8.7.1 Vererbung und Überschreiben
Klasse TopClass
package s1.block8; public class TopClass { protected static int zaehler; public TopClass() {zaehler++;} protected static int getZaehler() {return zaehler;} }
Klasse LowClass
public class LowClass extends TopClass { protected static int zaehler; public LowClass() {zaehler++;} protected static int getZaehler() {return zaehler;} protected void test (){ System.out.println( getZaehler() + " Instanzen der Klasse LowClass erzeugt"); System.out.println( TopClass.getZaehler() + " Instanzen der Klasse TopClass erzeugt"); } public static void main(String[] args) { int size = 3; TopClass[] feldT = new TopClass[size]; LowClass[] feldL = new LowClass[size]; for ( int i=0; i<size; i++) feldT[i]= new TopClass(); for ( int i=0; i<size; i++) { feldL[i]= new LowClass(); feldL[i].test(); } System.out.println(TopClass.getZaehler() + " Instanzen TopClass generiert, "+ LowClass.getZaehler() + " Instanzen LowClass generiert"); } // Ende Methode main() } // Ende Klasse LowClass
Ausgaben (mit Attribut zaehler in Klasse LowClass)
1 Instanzen der Klasse LowClass erzeugt 4 Instanzen der Klasse TopClass erzeugt 2 Instanzen der Klasse LowClass erzeugt 5 Instanzen der Klasse TopClass erzeugt 3 Instanzen der Klasse LowClass erzeugt 6 Instanzen der Klasse TopClass erzeugt 6 Instanzen TopClass generiert, 3 Instanzen LowClass generiert
Ausgaben (ohne Attribut zaehler in Klasse LowClass)
5 Instanzen der Klasse LowClass erzeugt 5 Instanzen der Klasse TopClass erzeugt 7 Instanzen der Klasse LowClass erzeugt 7 Instanzen der Klasse TopClass erzeugt 9 Instanzen der Klasse LowClass erzeugt 9 Instanzen der Klasse TopClass erzeugt 9 Instanzen TopClass generiert, 9 Instanzen LowClass generiert
8.7.2 Vererbung und Assoziation
Klasse Point
package s1.block8; public class Point { private double x; private double y; public Point(double xx, double yy) { x = xx; y = yy; } public void setXY(double xx, double yy) { x = xx; y = yy; } public double getX() { return x;} public double getY() { return y;} public void print() {System.out.println(toString());} public String toString() {return ("x: " + x + " y: " + y);} }
Klasse CircleIsPoint
package s1.block8; public class CircleIsPoint extends Point{ private double radius; public CircleIsPoint(double xx, double yy, double r) { super(xx,yy); radius=r; } public double getRadius() {return radius;} public void setRadius(double r) {radius=r;} public String toString() {return (super.toString() + " , r: " + radius);} }
Klasse CircleHasPoint
package s1.block8; public class CircleHasPoint { private double radius; private Point p; public CircleHasPoint(double xx, double yy, double r) { p = new Point(xx,yy); radius=r; } public double getRadius() {return radius;} public void setRadius(double r) {radius=r;} public void setXY(double xx, double yy) { p.setXY(xx,yy);} public double getX() { return p.getX();} public double getY() { return p.getY();} public void print() {System.out.println(toString());} public String toString() {return ("x: " + p.getX() + " y: " + p.getY() + " r: "+ radius);} }
Klasse Main
package s1.block8; public class Main { public static void main(String[] args) { Point p1 = new Point (2.2, 3.3); Point p2 = new Point (2.22, 3.33); CircleIsPoint cip1 = new CircleIsPoint(4.4,5.5,6.6); p1.print(); cip1.print(); CircleHasPoint chp1 = new CircleHasPoint(44.4,55.5,66.6); chp1.print(); } }
Ausgabe:
x: 2.2 y: 3.3 x: 4.4 y: 5.5 radius: 6.6 x: 44.4 y: 55.5 radius: 66.6
8.7.3 Beispiel der Vorlesung
Schritt 1: Klonen der Klasse Ware zu Buch und Anpassen
Klasse Buch (nicht von Ware abgeleitet!)
package s1.block8; /** * Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern * mit Preisen und Namen in einem Lager. * @author Stefan Schneider * @version 1.2 * @see Lager */ public class Buch { /* * 1. Erzeugen Sie eine Klasse Buch in dem Sie die Klasse * Ware mit Ihrem Inhalt kopieren * 1.1 Anpassen Name * 1.2 Anpassen Konstruktoren * 1.3 Anpassen equals Methode * 1.4 Anlegen einer ISBN Nummer * 1.5 Anlegen einer hardcodierten halben Mehrwertsteuer * 1.6 Referenz auf eine private, optionale (ältere) Auflage * Zugriffsmethoden anlegen * 1.7 Anpassen der Druckenmethode * 4. Ableiten der Klasse aus der Klasse aus der Klasse Ware * 4.1 extends Schlüsselwort benutzen * 4.2 Löschen von allem redundanten Code * 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware * 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren * 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll * abstrakt werden */ /** * Der aktuelle Mehrwertsteuersatz 2010. * Er liegt zur Zeit bei {@value}. * * @since 1.0 */ public static final double mws = 0.19; private double nettoPreis; //Deklaration private boolean halbeMws; private String name; public Buch empfehlung; private String isbn; private Buch alteAuflage; /** * Referenz aus alte Auflage * @return */ public Buch getAlteAuflage() { return alteAuflage; } /** * zuweisen einer alten Auflage * @param alteAuflage */ public void setAlteAuflage(Buch alteAuflage) { this.alteAuflage = alteAuflage; } /** * Auslesen der ISBN-Nummer (eine Zeichenkette) * @return */ public String getISBN() { return isbn; } /** * Konstruktor fuer die Klasse Ware * @param n der Name der Ware * @param np der Nettopreis * @param myISBN die ISBN Nummer */ public Buch(String n, double np, String myISBN) { name = n; nettoPreis = np; halbeMws = true; //Bei Büchern immer der Fall isbn=myISBN; // zusätzliche Initialiserungen.... } /** * tiefes Vergleichen. Berücksichtigt beim Namen nicht * die Groß/kleinschreinung. Berücksichtigt rekursiv die * Empfehlungen * @param dieAndereWare * @return wahr wenn beide Waren dem Vergleich entsprechen. */ public boolean equals(Buch dieAndereWare) { boolean result; result = getName().equalsIgnoreCase(dieAndereWare.getName()) && (getNettoPreis() == dieAndereWare.getNettoPreis()) && (getHalbeMws() == dieAndereWare.getHalbeMws()) && (((getEmpfehlung() == null) && (dieAndereWare.getEmpfehlung() == null)) || ((getEmpfehlung() != null) && (dieAndereWare.getEmpfehlung() != null) && (getEmpfehlung().equals(dieAndereWare.getEmpfehlung())) ) && (isbn.equals(dieAndereWare.isbn)) && (((alteAuflage == null) && (dieAndereWare.alteAuflage == null)) || ((alteAuflage != null) && (dieAndereWare.alteAuflage != null) && (alteAuflage.equals(dieAndereWare.alteAuflage)) ) ) ); return result; } /** * Liefert den Namen einer Ware zurueck. * @return Name der Ware */ public String getName() { return name; } /** * Setzen eines neuen Nettopreis * @param npr der neue Nettopreis */ public void setNettoPreis(double npr) { nettoPreis = npr; } /** * liefere wahr zurück wenn Mwssatz reduziert ist * @return */ public boolean getHalbeMws() {return halbeMws;} public Buch getEmpfehlung() { return empfehlung; } public void setEmpfehlung(Buch empfehlung) { this.empfehlung = empfehlung; } /** * Ausdrucken aller Werte auf der Konsole */ public void drucken() { drucken(0); } /** * Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung * für Empfehlungen * @param einruecken eingerueckte Stellen für Empfehlungen */ private void drucken(int einruecken) { String leerStellen = ""; for (int i = 0; i < einruecken; i++) { leerStellen = leerStellen + " "; } System.out.print(leerStellen + "Name: " + getName()); System.out.print(" ;Netto: " + getNettoPreis()); System.out.print(" ;Brutto: " + getBruttoPreis()); if (halbeMws) System.out.println(" (Halbe Mws)"); else System.out.println(" (Volle Mws)"); if (empfehlung != null) { System.out.println(leerStellen + "Empfehlung:"); empfehlung.drucken(einruecken + 2); } System.out.println(leerStellen + "ISBN:" +isbn); if (alteAuflage != null) { System.out.println(leerStellen + "Alte Auflage:"); alteAuflage.drucken(einruecken + 2); } } /** * Ausgabe des Nettopreis * @return der Nettopreis */ public double getNettoPreis() { return nettoPreis; } /** * Ausgabe des Bruttopreis * @return der Bruttopreis */ public double getBruttoPreis() { double bp; //temporaere Variable; keine Klassenvariable if (halbeMws) { bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100; } else { bp = Math.round(nettoPreis * (mws + 1) * 100) / 100; } return bp; } /** * Erzeugt verkette Liste von Empfehlungen * @param empf Anzahl Empfehlungen */ public void generiereEmpfehlungen(int empf) { Buch ware1 = this; Buch ware2; for (int i = 1; i <= empf; i++) { ware2 = new Buch(ware1.getName() + " besser" + i, 31.12, isbn+"-1"); ware1.setEmpfehlung(ware2); ware1 = ware2; } } }
Klasse Main
package s1.block8; /** * Eine Hilfsklasse zur Implementierung eines Hauptprogramms * @author s@scalingbits.com * @version 1.1 */ public class MainWare { /** * Die Methode wareTesten testet die Implementierung der * von Waren mit tiefem Kopieren, tiefem Vergleichen. * Sie nutzt nicht die Vererbung aus. */ public static void warenTesten() { Ware ware1, ware2; Lager dasLager; // Testen der Klasse Ware ware1 = new Ware("Zeitung", 12.12, true); ware1.drucken(); double p = ware1.getNettoPreis(); // Generieren von Empfehlungen ware2 = new Ware("Potter Band 1", 31.12, false); ware2.generiereEmpfehlungen(7); // Abfrage des Lagers dasLager = Lager.getLager(); dasLager.einlagern(ware1, 0); dasLager.einlagern(ware2, 1); // Prüfen der Lagerbestände mit tiefem Vergleichen Ware testWare = dasLager.holen(0); if (testWare == ware2) System.out.println("testware und ware2 sind identisch (gut)"); if (testWare.equals(ware2)) System.out.println("testware und ware2 sind inhaltsgleich (gut)"); // vollständiges Befüllen des Lager for (int i = 0; i < 1000; i++) { ware2 = new Ware("Band" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2); } System.out.println("Lager vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); for (int i = 0; i < 1000; i++) { ware2 = new Ware("Volume" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2,i); } System.out.println("Lager erneut vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); } /** * Diese Methode dient zum Testen der Klasse Buch. * Sie nutzt die Veererbung in Java aus. */ public static void buecherTesten() { /* * 2. Testen: Anlegen einer Testmethode für Bücher * Erzeugen von Büchern * Anlegen einer Referenz auf eine alte Auflage * Drucken zweier Bücher * 3. Frage: Wie kann man Bücher in das Lager einfügen? * 5. Einfügen der Bücher in das Lager * 8. Anpassen der Hauptroutine * 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse * Ware jetzt abstrakt ist. */ Lager dasLager; dasLager = Lager.getLager(); Buch buch1 = new Buch("Das Grauen",22.22,"9876543"); Buch buch2 = new Buch("Das Kapital",33.33,"9876543"); buch1.setAlteAuflage(buch2); buch1.drucken(); //dasLager.einlagern(buch1); //dasLager.einlagern(buch2); dasLager.drucken(); } /** * Das Hauptprogramm * * @param args */ public static void main(String[] args) { //warenTesten(); buecherTesten(); } }
Schritt 2: Die Klasse Buch wird aus der Klasse Ware abgeleitet
Klasse Buch
package s1.block8; /** * Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern * mit Preisen und Namen in einem Lager. * @author Stefan Schneider * @version 1.2 * @see Lager */ public class Buch extends Ware { /* * 1. Erzeugen Sie eine Klasse Buch in dem Sie die Klasse * Ware mit Ihrem Inhalt kopieren * 1.1 Anpassen Name * 1.2 Anpassen Konstruktoren * 1.3 Anpassen equals Methode * 1.4 Anlegen einer ISBN Nummer * 1.5 Anlegen einer hardcodierten halben Mehrwertsteuer * 1.6 Referenz auf eine private, optionale (ältere) Auflage * Zugriffsmethoden anlegen * 1.7 Anpassen der Druckenmethode * 4. Ableiten der Klasse aus der Klasse aus der Klasse Ware * 4.1 extends Schlüsselwort benutzen * 4.2 Löschen von allem redundanten Code * 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware * 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren * 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll * abstrakt werden */ /** * Der aktuelle Mehrwertsteuersatz 2010. * Er liegt zur Zeit bei {@value}. * * @since 1.0 */ private String isbn; private Buch alteAuflage; /** * Referenz aus alte Auflage * @return */ public Buch getAlteAuflage() { return alteAuflage; } /** * zuweisen einer alten Auflage * @param alteAuflage */ public void setAlteAuflage(Buch alteAuflage) { this.alteAuflage = alteAuflage; } /** * Auslesen der ISBN-Nummer (eine Zeichenkette) * @return */ public String getISBN() { return isbn; } /** * Konstruktor fuer die Klasse Ware * @param n der Name der Ware * @param np der Nettorpreis * @param hmws halber Mehrwertsteuersatz für Ware gueltig */ public Buch(String n, double np, String myISBN) { super(n,np,true); isbn=myISBN; // zusätzliche Initialiserungen.... } /** * tiefes Vergleichen. Berücksichtigt beim Namen nicht * die Groß/kleinschreinung. Berücksichtigt rekursiv die * Empfehlungen * @param dieAndereWare * @return wahr wenn beide Waren dem Vergleich entsprechen. */ public boolean equals(Buch dieAndereWare) { boolean result; result = super.equals((Ware)dieAndereWare) && (isbn.equals(dieAndereWare.isbn)) && (((alteAuflage == null) && (dieAndereWare.alteAuflage == null)) || ((alteAuflage != null) && (dieAndereWare.alteAuflage != null) && (alteAuflage.equals(dieAndereWare.alteAuflage)) ) ); return result; } /** * Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung * für Empfehlungen * @param einruecken eingerueckte Stellen für Empfehlungen */ protected void drucken(int einruecken) { super.drucken(einruecken); String leerStellen = ""; for (int i = 0; i < einruecken; i++) { leerStellen = leerStellen + " "; } System.out.println(leerStellen + "ISBN:" +isbn); if (alteAuflage != null) { System.out.println(leerStellen + "Alte Auflage:"); alteAuflage.drucken(einruecken + 2); } } }
Klasse MainBuch
Die Instanzen der Klasse Buch können jetzt in das Lager eingefügt werden
package s1.block8; /** * Eine Hilfsklasse zur Implementierung eines Hauptprogramms * @author sschneid */ public class MainBuch { /** * Die Methode wareTesten testet die Implementierung der * von Waren mit tiefem Kopieren, tiefem Vergleichen. * Sie nutzt nicht die Vererbung aus. */ public static void warenTesten() { Ware ware1, ware2; Lager dasLager; // Testen der Klasse Ware ware1 = new Ware("Zeitung", 12.12, true); ware1.drucken(); double p = ware1.getNettoPreis(); // Generieren von Empfehlungen ware2 = new Ware("Potter Band 1", 31.12, false); ware2.generiereEmpfehlungen(7); // Abfrage des Lagers dasLager = Lager.getLager(); dasLager.einlagern(ware1, 0); dasLager.einlagern(ware2, 1); // Prüfen der Lagerbestände mit tiefem Vergleichen Ware testWare = dasLager.holen(0); if (testWare == ware2) System.out.println("testware und ware2 sind identisch (gut)"); if (testWare.equals(ware2)) System.out.println("testware und ware2 sind inhaltsgleich (gut)"); // vollständiges Befüllen des Lager for (int i = 0; i < 1000; i++) { ware2 = new Ware("Band" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2); } System.out.println("Lager vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); for (int i = 0; i < 1000; i++) { ware2 = new Ware("Volume" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2,i); } System.out.println("Lager erneut vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); } /** * Diese Methode dient zum Testen der Klasse Buch. * Sie nutzt die Veererbung in Java aus. */ public static void buecherTesten() { /* * 2. Testen: Anlegen einer Testmethode für Bücher * Erzeugen von Büchern * Anlegen einer Referenz auf eine alte Auflage * Drucken zweier Bücher * 3. Frage: Wie kann man Bücher in das Lager einfügen? * 5. Einfügen der Bücher in das Lager * 8. Anpassen der Hauptroutine * 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse * Ware jetzt abstrakt ist. */ Lager dasLager; dasLager = Lager.getLager(); Buch buch1 = new Buch("Das Grauen",22.22,"9876543"); Buch buch2 = new Buch("Das Kapital",33.33,"9876543"); buch1.setAlteAuflage(buch2); buch1.drucken(); dasLager.einlagern(buch1); dasLager.einlagern(buch2); dasLager.drucken(); } /** * Das Hauptprogramm * * @param args */ public static void main(String[] args) { //warenTesten(); buecherTesten(); } }
Stufe 3: Klasse MusikCD
Klasse MusikCD
package s1.block8; /** * Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern * mit Preisen und Namen in einem Lager. * @author s@scalingbits.com * @version 1.3 * @see Lager */ public class MusikCD extends Ware { /* * 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware * 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren * 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll * abstrakt werden */ /** * Konstruktor fuer die Klasse Ware * @param n der Name der Ware * @param np der Nettorpreis * @param hmws halber Mehrwertsteuersatz für Ware gueltig */ public MusikCD(String n, double np) { super(n,np,false); } /** * tiefes Vergleichen. Berücksichtigt beim Namen nicht * die Groß/kleinschreinung. Berücksichtigt rekursiv die * Empfehlungen * @param dieAndereWare * @return wahr wenn beide Waren dem Vergleich entsprechen. */ public boolean equals(MusikCD dieAndereWare) { return super.equals((Ware)dieAndereWare); } }
Klasse MainCD
package s1.block8; /** * Eine Hilfsklasse zur Implementierung eines Hauptprogramms * @author s@scalingbits.com * @version 1.3 */ public class Main { /** * Die Methode wareTesten testet die Implementierung der * von Waren mit tiefem Kopieren, tiefem Vergleichen. * Sie nutzt nicht die Vererbung aus. */ public static void warenTesten() { Ware ware1, ware2; Lager dasLager; // Testen der Klasse Ware ware1 = new Ware("Zeitung", 12.12, true); ware1.drucken(); double p = ware1.getNettoPreis(); // Generieren von Empfehlungen ware2 = new Ware("Potter Band 1", 31.12, false); ware2.generiereEmpfehlungen(7); // Abfrage des Lagers dasLager = Lager.getLager(); dasLager.einlagern(ware1, 0); dasLager.einlagern(ware2, 1); // Prüfen der Lagerbestände mit tiefem Vergleichen Ware testWare = dasLager.holen(0); if (testWare == ware2) System.out.println("testware und ware2 sind identisch (gut)"); if (testWare.equals(ware2)) System.out.println("testware und ware2 sind inhaltsgleich (gut)"); // vollständiges Befüllen des Lager for (int i = 0; i < 1000; i++) { ware2 = new Ware("Band" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2); } System.out.println("Lager vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); for (int i = 0; i < 1000; i++) { ware2 = new Ware("Volume" + i + "B", 12.12, true); ware2.generiereEmpfehlungen(7); dasLager.einlagern(ware2,i); } System.out.println("Lager erneut vollständig gefüllt mit " + dasLager.lagerGroesse() + " Waren."); } /** * Diese Methode dient zum Testen der Klasse Buch. * Sie nutzt die Veererbung in Java aus. */ public static void buecherTesten() { /* * 2. Testen: Anlegen einer Testmethode für Bücher * Erzeugen von Büchern * Anlegen einer Referenz auf eine alte Auflage * Drucken zweier Bücher * 3. Frage: Wie kann man Bücher in das Lager einfügen? * 5. Einfügen der Bücher in das Lager * 8. Anpassen der Hauptroutine * 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse * Ware jetzt abstrakt ist. */ Lager dasLager; dasLager = Lager.getLager(); Buch buch1 = new Buch("Das Grauen",22.22,"9876543"); Buch buch2 = new Buch("Das Kapital",33.33,"9876543"); buch1.setAlteAuflage(buch2); buch1.drucken(); dasLager.einlagern(buch1); dasLager.einlagern(buch2); dasLager.drucken(); } /** * Diese Methode dient zum Testen der Klasse Buch. * Sie nutzt die Veererbung in Java aus. */ public static void CDsTesten() { /* * 2. Testen: Anlegen einer Testmethode für Bücher * Erzeugen von Büchern * Anlegen einer Referenz auf eine alte Auflage * Drucken zweier Bücher * 3. Frage: Wie kann man Bücher in das Lager einfügen? * 5. Einfügen der Bücher in das Lager * 8. Anpassen der Hauptroutine * 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse * Ware jetzt abstrakt ist. */ Lager dasLager; dasLager = Lager.getLager(); MusikCD cd1 = new MusikCD("Thriller",8.88); MusikCD cd2 = new MusikCD("Peter und der Wolf",9.99); cd1.setEmpfehlung(cd2); cd1.drucken(); dasLager.einlagern(cd1); dasLager.einlagern(cd2); dasLager.drucken(); } /** * Das Hauptprogramm * * @param args */ public static void main(String[] args) { //warenTesten(); buecherTesten(); CDsTesten(); } }
- 7536 views
die Zeile: public int
die Zeile:
public int price = 99;
in der Klasse CircleIsPoint ist dort sicher nur aus Versehen hineingeraten?
- Log in to post comments
8.8 Lernziele
8.8 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zur Vererbung zu beantworten:
Feedback
- 4157 views
9. Polymorphie
9. PolymorphieDer Begriff Polymorphie
Polymorphie nach Wikipedia: (gr. πολυμορφία Polymorphia) Vielgestaltigkeit
Durch das Konzept der Vererbung sollen Anwendungen mit Objekten von Unterklassen ohne explizites Wissen über die Unterklassen umgehen können.
Objekte von Unterklassen können sich aber anders verhalten wenn die Methoden der Oberklasse in den entsprechenden Unterklassen überschrieben und neu implementiert wurden. Objekte einer Klassenhierarchie können daher abhängig von der Unterklasse verschieden Methodenimplementierungen verwenden und sich polymorph (Vielgestaltig) verhalten.
Das "Liskov Substitution Principle"
Barbara Liskov führte 1987 das nach ihr benannte Prinzip welches sich mit der Substitution (Ersetzbarkeit) in der objektorientierten Programmierung beschäftigt:
Wenn eine Klasse S eine Unterklasse(Subtyp) von T ist, können alle Instanzen von T (Oberklasse) durch Instanzen von S (Unterklasse) ersetzt werden ohne dass sich das Verhalten des Programmes ändert. |
Eng mit diesem Prinzip ist auch Betrand Meyer's in der Programmiersprache Eiffel umgesetztes "Design by contract" verwandt.
- 8584 views
9.1 Polymorphes Verhalten bei Vererbung und Erweiterung
9.1 Polymorphes Verhalten bei Vererbung und ErweiterungDer Aspekt der Erweiterung bei der Vererbung ermöglicht problemlos polymorphes Verhalten von Objekten.
Im unten gezeigten Beispiel wird eine Klassenhierarchie verwendet in der in jeder Unterklasse ein Attribut und eine Methode hinzugefügt wird
Ein Programm welches nur Instanzen der Klasse Kraftwagen kennt hat keine Probleme Instanzen von LastKraftwagen oder Bus zu verwenden. Die Instanzen besitzen alle ein Attribut nummernschild und eine Methode getKennZeichen().
Polymorphismus funktioniert in Java nur bei Attributen und Methoden die von einer gemeinsamen Basisklasse geerbt worden sind! |
Klassen mit namensgleichen Attributen und Methoden profitieren nicht vom Polymorphismus. Der Übersetzer kontrolliert die Klassenhierarchie beim Übersetzen und unterbindet Zuweisungen zwischen Typen(Klassen) die namensgleiche Methoden besitzen, diese aber nicht von einer gemeinsamen Klasse erben.
Dieses Konzept lässt sich in Java wie folgt umsetzen:
import Kraftwagen; import LastKraftwagen; import Bus; ... Kraftwagen[] flotte = new Kraftwagen[3]; flotte[0] = new Kraftwagen(); flotte[1] = new LastKraftwagen(); flotte[2] = new Bus(); ... for (i=0; i<3; i++) { // Das Programm kennt im Moment nur Kraftwagen! System.out.println(flotte[i].getKennZeichen()); } ...
Das Programm kann innerhalb der for-Schleife mit Instanzen der Klasse LastKraftwagen und Bus umgehen. Es muss sich aber auf die in der Klasse Kraftwagen bekannten Eigenschaften des Objekts beschränken.
- 10258 views
Letzter Satz
Haben Sie im letzten Satz: "Das Programm kann mit Instanzen der Klasse LastKraftwagen und Bus umgehen da es sich auf die in der Klasse Kraftwagen bekannten Eigenschaften des Objekts beschränken muss."
das Wort "nicht" vergessen?
Müsste der Satz nicht:"Das Programm kann NICHT mit Instanzen der Klasse LastKraftwagen und Bus umgehen da es sich auf die in der Klasse Kraftwagen bekannten Eigenschaften des Objekts beschränken muss." heißen?
- Log in to post comments
9.2 Polymorphie beim Überschreiben
9.2 Polymorphie beim ÜberschreibenJava erlaubt es Methoden von Oberklassen in Unterklassen zu verdecken und neu zu implementieren. Hierdurch können Methoden an die Anforderungen einer spezialisierten Klasse angepasst werden. Man spricht hier vom Überschreiben von Methoden wenn die folgende Definition gilt:
Überschreiben einer Methode |
---|
Bei einer überschriebenen Methode müssen die Signatur und der Rückgabewert der überschriebenen Methode identisch mit der Signatur und dem Rückgabewert der Methode einer Oberklasse sein. |
Beim Überschreiben von Methoden und Attributen ist das "Liskov Substitution Principle" nicht mehr automatisch garantiert. Hier wir die überschreibende Methode der Unterklasse an die Stelle der überschriebenen Methode der Oberklasse bei allen Instanzen der Unterklasse verwendet. Es wird ein anderer Code ausgeführt. Dieser Code könnte theoretisch auch zu einem anderen Verhalten führen.
Anbei das Beispiel einer Klasse Point, die einen zweidimensionalen Punkt modelliert und der Klasse CircleIsPoint die von Punkt erbt und zusätzlich ein Attribut zur Verwaltung des Radius hat:
Klasse Point
public class Point { private double x; private double y; public Point(double xx, double yy) { x = xx; y = yy; } public void setXY(double xx, double yy) { x = xx; y = yy; } public double getX() { return x;} public double getY() { return y;} public void print() {System.out.println(toString());} public String toString() {return ("x: " + x + " y: " + y);} }
Klasse CircleIsPoint
public class CircleIsPoint extends Point{ private double radius; public CircleIsPoint(double xx, double yy, double r) { super(xx,yy); radius=r; } public double getRadius() {return radius;} public void setRadius(double r) {radius=r;} public void print() { //Ueberschriebene Methode System.out.println(super.toString() + " radius: " + radius); } }
Ein Hauptprogramm
public class Main { public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (7.7, 8.8); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); } }
Bei der Ausführung des Programms ergibt sich die folgende Objekstruktur:
Die Ausgabe ist die folgende:
x: 2.2 y: 3.3 x: 7.7 y: 8.8 x: 4.4 y: 5.5 radius: 6.6
Die überschriebene print() Methode erzeugt eine sinnvolle Ausgabe, die zusätzlich den Radius ausgibt ohne das das komsumierende Hauptprogramm "weiß", das es sich um eine überschriebene Methode handelt.
Da der Begriff einer "sinnvollen" Erweiterung nicht von Werkzeugen abgeprüft werden kann, ergibt sich ein Risiko des Missbrauchs. Ein Beispiel ist die folgende Implementierung der Methode print() in der Klasse CircleIsPoint().
public class CircleIsPoint extends Point{ ... public void print() { System.out.println("Methode print() muss noch implementiert werden..."); } }
Der Unterschied zwischen der recht menschlichen Implementierung und der Implementierung die auch den Radius ausgibt ist nur gering.
Es gibt im schlimmsten Fall Programme, die diesen Text lesen und weder die zusätzlichen Information über den Radius interpretieren können, noch mit einem anderen Text umgehen können.
Ist eine Klasse und/oder die Methode nicht als final deklariert, muss ein Konsument dieser Klasse mit einem Überschreiben von Methoden rechnen!
Hiermit ergibt sich für den Polymorphismus beim Überschreiben die folgende Abschätzung:
Vorteil | Nachteil |
---|---|
Kostensenkung Entwicklung: Existierender Code kann unmodifiziert mit neuen Unterklassen umgehen | Kostensteigerung QA und Service: Der Polymorphismus kann durch Missbrauch zu unerwarteten Effekten führen, die erst zur Laufzeit auftreten. |
Begriffsbestimmung: Überschriebene und überladene Methoden
Es gibt in Java überschriebene (englisch overriding) und überladene (englisch overloading) Methoden. Beide Begriffe werden leicht verwechselt!
Überschriebene Methode | Überladene Methode | |
---|---|---|
Methodenname | identisch | identisch |
Eingabeparameterliste | identisch (Typen, Anzahl, Folge) | unterschiedlich! |
Rückgabewert | identisch | nicht relevant |
Klassenbeziehung der Methoden | Methoden gehören zu unterschiedlichen Klassen die aber in einer Ober- Unterklassebeziehung stehen | Methoden gehören zur gleichen Klasse |
Zur vollständigen Begriffsverwirrung: Methoden können andere Methoden der gleichen Klasse überladen während sie eine Methode der Oberklasse überschreiben.
Siehe die Methode mult() im folgenden Beispiel:
- 10178 views
Kann es sein dass hier ein
Kann es sein dass hier ein Fehler vorliegt?
"Hier tritt nun die überschreibende Methode an die Stelle der überschriebenen Methode."
- Log in to post comments
Das war schon so gemeint
Danke für das Feedback. Ich habe den entsprechenden Satz umformuliert. Ich hoffe, dass das jetzt besser verständlich ist.
- Log in to post comments
Fehlerhafte Ausgabe bei Ausführung der main() Methode?
Sollte die Ausgabe beim Ausführen der Methode main() in der Klasse Main nicht
x: 2.2 y: 3.3
x: 7.7 y: 8.8
x: 4.4 y: 5.5 radius: 6.6
sein?
(Vielleicht haben sie sich bei der zweiten Zeile beim y verschrieben.)
- Log in to post comments
9.3 Casting: Typkompabilität bei Zuweisungen
9.3 Casting: Typkompabilität bei ZuweisungenBisher wurden bereits implizit das Konzept von Referenzvariablen verwendet, die wechselseitig auf Objekte ihres Typs (Klasse) oder auf Objekte von Unterklassen gezeigt haben.
Java ist trotz des Polymorphismus eine streng typisierte Sprache. Das bedeutet:
Strenge Typisierung |
---|
Der Übersetzer erlaubt schon beim Übersetzen nur typsichere Zuweisungen und Operationen (und meldet unsichere Operationen als Fehler) |
Dieses Konzept steigert die Qualität der ausgelieferten Anwendungen. Fehler aufgrund inkorrekter Typen können beim Anwender zur Laufzeit nicht mehr auftreten. Der Übersetzer zwingt den Entwickler nur typsichere Operationen durchzuführen. Eine typsichere Operation ist zum Beispiel das Aufrufen einer Methode von der man sicher weiß, das sie für eine gegebene Sprache existiert.
"Casting" in der Programmierung |
---|
engl. Casting hier: Formen, gießen, krümmen Das implizite oder explizite Ändern des Typs eines Objekts oder Datenstruktur bei einer Zuweisung oder der Methodenauswahl |
Der "Upcast"
Ein "Upcast" ist eine Typkonversionen einer Instanz einer Unterklasse auf den Typ einer Oberklasse. Sie wurden bisher implizit in den vorhergehenden Beispielen verwendet, wie zum Beispiel bei der Klasse CircleIsPoint die aus der Klasse Point abgeleitet wurde:
import Point; import CircleIsPoint; ... Point p1 = new Point (2.2, 3.3); CircleIsPoint c1 = new CircleIsPoint(4.4,5.5,6.6); Point p2 = c1; //Das explizite Wissen über den Objekttyp CircleIsPoint geht verloren ... p1.print(); p2.print();
Die Zuweisung von c1 auf p1 ist
- sicher da sich alle Instanzen der Klasse CircleIsPoint wie Instanzen der Klasse Point verhalten
- vom Javaübersetzer erlaubt da sicher!
Upcast |
---|
Der "Upcast" ist eine sichere Typkonversation da spezialisierte Klasseninstanzen auf Referenzen von allgemeineren Klassen zugewiesen werden. |
Der "Downcast" oder unsichere "Cast"
Das umgekehrte Prinzip gilt jedoch nicht.
Eine Instanz einer allgemeineren Klasse hat nicht alle Eigenschaften einer spezialisierten Klasse. Das folgende Beispiel wird nicht vom Javaübersetzer akzeptiert:
import Point; import CircleIsPoint; ... Point p1 = new Point (2.2, 3.3); CircleIsPoint c1 = new CircleIsPoint(4.4,5.5,6.6); CircleIsPoint c2 = p1; ... double r1 = c1.getRadius(); double r2 = c2.getRadius();
Eine Instanz vom Typ Point hat nicht die Eigenschaften einer Instanz vom Type CircleIsPoint. Im UML Diagramm sieht der obige Versuch wie folgt aus:
Der Übersetzer erzeugt schon beim Versuch der Zuweisung eine Fehlermeldung:
Main.java:30: incompatible types found : l9vererbungassoziation.Point required: l9vererbungassoziation.CircleIsPoint CircleIsPoint c2 = p1; 1 error
Es gibt jedoch auch Fälle in denen eine Typkonversion erzwungen werden muss. Im verwendeten Beispiel ist dies der Fall wenn man einen Kreis in einem Feld von Point verwaltet. Wenn man die Referenz benutzt um den Radius auszulesen ergibt sich folgendes Problem wenn man zum Auslesen den Typ CircleIsPoint wie folgt verwendet:
public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); CircleIsPoint cip1 = pf[2]; double r2 = cip1.getRadius(); }
Der Übersetzer meldet den folgenden Fehler da die beiden Typen keine sichere Typkonversion zulassen:
Main.java:24: incompatible types found : l9vererbungassoziation.Point required: l9vererbungassoziation.CircleIsPoint CircleIsPoint cip1 = pf[2]; 1 error
Dieses Problem kann man versuchen zu Lösen in dem man die Variable cip1 mit dem Typ Point versieht:
public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); Point cip1 = pf[2]; double r2 = cip1.getRadius(); }
Dies Maßnahme erlaubt das Auslesen der Feldvariable. Sie scheitert jedoch eine Zeile weiter beim Aufruf der Methode getRadius(). Die Klasse Point kennt keine Methode getRadius()...
Main.java:25: cannot find symbol symbol : method getRadius() location: class l9vererbungassoziation.Point double r2 = cip1.getRadius(); 1 error
Die Lösung besteht in einer expliziten Typkonversion (" cast") mit Hilfe einer runden Klammer und Angabe des Typs:
public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); CircleIsPoint cip1 = (CircleIsPoint)pf[2]; double r2 = cip1.getRadius(); }
Mit dieser Typkonversion zwingt der Entwickler den Übersetzer zur Zuweisung. Der Übersetzer kann sich dieses Wissen nicht von alleine herleiten. Die Verantwortung liegt jetzt beim Entwickler. Würde der Entwickler den folgenden Cast erzwingen, würde das Programm auch fehlerfrei übersetzen:
CircleIsPoint cip1 = (CircleIsPoint)pf[1];
double r2 = cip1.getRadius();
Beim Aufruf der Methode getRadius() würde jetzt jedoch zur Laufzeit eine Ausnahme (Exception) geworfen werden. Der Entiwckler hat in diesem Fall den Übersetzer zu einem inkorrekten Übersetzungsvorgang gezwungen.
Der Fehler tritt jetzt erst zur Laufzeit auf. Dies kann für den Entwickler sehr teuer werden, da die Software den Fehler eventuell erst beim Kunden zeigt.
Zusammenfassung explizite Typkonversion (Downcast)
Die Syntax
KlasseC variable2; ... KlasseA variable1 = (KlasseB) variable2;
erlaubt die Zuweisung einer Objektreferenz variable2 auf eine Objektreferenz variable1 solange der Typ KlasseB in der Klammer eine sichere Konvertierung von KlasseB auf KlasseA erlaubt. Diese Zuweisung funktioniert unabhängig vom tatsächlichen Typ (KlasseC) von variable2.
Explizite Typkonversionen sind in manchen Fällen aufgrund des Polymorphismus notwendig. Sie haben jedoch eine Reihe von Konsequenzen:
- Der Entwickler übernimmt explizit die Verantwortung für eine korrekte Konversion. Diese hat normalerweise der Übersetzer!
- Fehler werden nicht mehr zum Übersetzungszeitpunkt erkannt.
- Fehler werden erst zur Laufzeit erkannt. Sie führen zur Ausnahmebehandlung zur Laufzeit. Die Anwendung beendet sich falls diese Ausnahmen nicht gefangen und behandelt werden.
Downcasts sind problematische, explizite Typkonvertierungen. Ihre Verwendung sollte wenn möglich vermieden werden, da bestimmte Fehlerklassen zum Übersetzungszeitpunkt nicht geprüft werden können. |
- 13658 views
Div.
Der "Upcast"
Sie wurden bisher implizit in den vorhergehenden Beispielen verwendet, wie zum Beispiel bei der Klasse CircleIsPoint die aus der Klasse Point abgeleitet wurde:
CircleIsPoint = Point ist genau aber ein Downcast, obiger Satz ist vielleicht nicht optimal.
---------
Die Zuweisung von c1 auf p1 ist
Ist hier vielleicht die Zuweisung von c1 auf p2 gemeint?
- Log in to post comments
Danke für die Frage
Ich verstehe Ihre Frage nicht so 100%...
- Ein Upcast ist eine Zuweisung von eines Objekts einer speziellen Klasse auf eine allgemeinere Klasse. Diese Zuweisung ist sicher es geht aber Wissen verloren. Eben das Wissen welches zur spezielleren Klasse gehört
- Eine Downcast ist prinzipiell unsicher. Java kann nicht wissen ob sich hinter dem Zeiger auf ein allgemeines Objekt, ein Objekt versteckt welches die Eigenschaften der speziellen Klasse hat.
Macht das Sinn?
- Log in to post comments
9.4 Sonderfall: überschriebene Attribute
9.4 Sonderfall: überschriebene AttributeDer Polymorphismus funktioniert in Java nicht im Fall von überschriebenen Attributen die einen unterschiedlichen Typ besitzen.
Bei überschriebenen Attributen wendet Java den Typ der Referenz an um das Attribut zu bestimmen.
Im Beispiel der Klassen Point und CircleIsPoint wird dies mit Hilfe des Attributs layer gezeigt:
Klasse Point
public class Point { ... public double layer = 5D; ... public void print() {System.out.println(toString());} public String toString() {return ("x: " + x + " y: " + y);} }
Klasse CircleIsPoint
public class CircleIsPoint extends Point{ ... public int layer = 1; ... public void print() { System.out.println(super.toString() + " radius: " + radius); } }
Hauptprogramm
public class Main { public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); Point p2 = pf[2]; p2.print(); // toString von CircleIsPoint wird benutzt double t1 = 1000.0 + p2.layer; // layer von Point wird benutzt System.out.println(t1); CircleIsPoint cip = (CircleIsPoint)pf[2]; cip.print(); // toString von CircleIsPoint wird benutzt double t2 = 1000.0 + cip.layer; // price von CircleIsPoint wird benutzt System.out.println(t2); } }
Hiermit ergeben sich die folgenden Belegungen:
Konsolenausgabe
x: 2.2 y: 3.3 x: 2.22 y: 3.33 x: 4.4 y: 5.5 radius: 6.6 x: 4.4 y: 5.5 radius: 6.6 1005.0 x: 4.4 y: 5.5 radius: 6.6 1001.0
Im ersten Fall (p2) wird das Attribut layer vom Typ double benutzt. Im Fall der Variable cip vom Typ CircleIsPoint wird die Variable layer vom Typ int benutzt.
Es kommt kein Polymorphismus zum Tragen, die Auswahl des Attributs richtet sich nach dem Typ der Referenz!
Tipp: Durch das strikte Anwenden von Datenkapselung greift man nur noch über Zugriffsmethoden auf Attribute zu. Hierdurch wird der Polymorphismus auch beim Zugriff auf Attribute (intuitiv) gewährleistet. Das Ausnutzen des nicht polymorphen Verhalten betrifft dann nur noch Sonderfälle in denen man dies explizit wünscht.
- 5992 views
Aufruffabfolge
p2. print() im Hauptprogramm ruft print() von Punkt auf. Hier ist dann die Frage welches Attribut ausgelesen wird. cip.print() ruft die print() Methode aus CircleIsPoint auf. Hier muss mann dann auch überlegen welches Attribut ausgelesen wird.
- Log in to post comments
Richtig :-(
Ich habe hier ein Diagramm aus einem anderen Beispiel verwendet. Ich muss überlegen ob ich es lösche oder anpasse.
- Log in to post comments
9.5 instanceof-Operator
9.5 instanceof-OperatorNormalerweise sind zur Laufzeit eines Programmes bereits alle Typen bekannt und wurden vom Übersetzer schon beim Übersetzen des Programms kontrolliert.
Durch die Möglichkeit der Typkonvertierung durch "casten", und "Upcasting" kann man die genaue Information über einen Typ einer Instanz zur Laufzeit verlieren. Das bedeutet, sie ist im Kontext einer Methode oder Klasse nicht mehr bekannt.
Zur Bestimmung eines Typs zur Laufzeit bietet Java den instanceof Operator an mit dem man Instanzen auf Ihren Typ prüfen kann.
Im vorhergehenden Beispiel mit den Klassen Point und CircleIsPoint kann man den instanceof Operator verwenden um einen Laufzeitfehler bei einer cast-Operation zu vermeiden:
public class Main { public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); double r2 = 0; CircleIsPoint cip1; if (pf[2] instanceof CircleIsPoint) { cip1 = (CircleIsPoint)pf[2]; r2 = cip1.getRadius(); } } }
Weiterführend: getClass() Methode
Die Javalaufzeitumgebung verfügt über ein vollständiges, dynamisches System zur Verwaltung von Metainformationen inklusive der Informationen über eine Klasse.
Hierzu dient die Klasse Class die man über die Methode Object.getClass() benutzen kann. Die Methode getClass() wird an alle Klassen vererbt. Hiermit kann man Informationen über die Klasse selbst, wie zum Beispiel ihren Name und die Oberklasse auslesen:
public class Main { public static void main(String[] args) { Point[] pf = new Point[3]; pf[0] = new Point (2.2, 3.3); pf[1] = new Point (2.22, 3.33); pf[2] = new CircleIsPoint(4.4,5.5,6.6); pf[0].print(); pf[1].print(); pf[2].print(); double r2 = 0; CircleIsPoint cip1; Class myClass = pf[2].getClass(); Class mySuperClass = myClass.getSuperclass(); System.out.println("pf[2] Klassenname: " + myClass.getSimpleName()); System.out.println("pf[2] Oberklasse: " + mySuperClass.getSimpleName()); if (pf[2] instanceof CircleIsPoint) { cip1 = (CircleIsPoint)pf[2]; r2 = cip1.getRadius(); } } }
Die Konsolenausgabe des Programmes ist die Folgende:
x: 2.2 y: 3.3 x: 2.22 y: 3.33 x: 4.4 y: 5.5 radius: 6.6 pf[2] Klassenname: CircleIsPoint pf[2] Oberklasse: Point
- 7174 views
9.6 Übungen
9.6 Übungen
9.6.1 Haustierpolymorphismus |
Klasse Haustier
- abstrakte Klasse
- Attribute
- privat: name
- privat: steuerpflichtig
- privat: jahreskostenTierarzt
- Methoden
- Lese und Schreibmethode für Namen
- Lesemethode für Steuerpflicht
- Konstruktor der Namen, Steuerpflicht und Jahreskosten Tierarzt erfasst
- Methode beschreibung() Typ String. Gibt Text mit Namen und eventueller Steuerpflicht zurück.
Klasse Hund
- abgeleitet aus Haustier
- Hunde sind steuerpflichtig
- privates Attribut: rasse
- Methoden
- Konstruktor der Namen, Jahreskosten Tierarzt und Rasse erfasst
- Lesemethode für Rasse
- Überschriebene Methode beschreibung() die zusätzlich hundespezifische Daten zurück gibt
Klasse Katze
- abgeleitet aus Haustier
- Katzen sind nicht steuerpflichtig
- privates Attribut: lieblingsVogel (Referenz auf Vogel)
- Methoden
- Konstruktor der Namen des Katze, Jahreskosten Tierarzt und die Referenz des Lieblingsvogels erfasst
- vogel() Ausgabe ist der Name des Vogels als Zeichenkette
- Überschriebene Methode beschreibung() die zusätzliche katzenspezifische Daten zurück gibt.
- setzen des Lieblingsvogelattribut
Klasse Vogel
- abgeleitet aus Haustier
- Vögel sind nicht steuerpflichtig
- privates Attribut: singvogel (boolscher Wert)
- Methoden
- Konstruktor der Namen des Vogels, JahresKosten Tierarzt und Singvogeleigenschaft erfasst
- Lesemethode für Singvogeleigenschaft
- Überschriebene Methode beschreibung() die zusätzliche vogelspezifische Eigenschaften ausgibt
Klasse TierTest
Schreiben Sie ein Hauptprogramm das folgende Aufgaben ausführt:
- Verwaltung von mindestens 2 Haustieren pro Tierart einem gemeinsamen statischen Feld vom Typ Haustier
- Methoden
- populate(): Anlegen von mindestens 2 Tieren pro Tierart im statischen Haustierfeld
- neuerLieblingsvogel(): Benutzen Sie einen zweiten Vogel, der aus dem Haustierfeld geholt und weisen Sie ihn einer Katze aus dem Haustierfeld zu
- prüfen Sie vor den Zuweisungen, dass Sie eine Katze, beziehungsweise einen Vogel aus dem Feld ausgelesen haben
- iterate(): Itererieren über das Feld und folgende Information ausdrucken
- Typ des Tieres
- Inhalt von beschreibung()
- Summerien Sie die Tierarztkosten auf und geben Sie sie am Ende aus.
- Benutzen Sie den unten angebenen Rumpf für Ihre Implementierung
package s1.block9; public class TierTest { private static Haustier hausTiere[]; public static void main(String[] args) { populate(); neuerLieblingsvogel(); iterate(); } public static void populate() { hausTiere = new Haustier[6]; /* Implementierung */ } public static void neuerLieblingsvogel() { /* Implementierung */ } public static void iterate() { /* Implementierung */ } }
Optional
Versuchen Sie eine Zuweisung einer Referenz eines inkompatiblen Typen als Lieblingvogel zu einer Katze. Die Routine soll durch casten übersetzen. Zur Laufzeit soll die Zuweisungweisungsmethode der Katze den Fehlversuch auf der Konsole dokumentieren
Tipp:
Die Methode .getClass() der Klasse Object liefert eine Referenz auf die Beschreibung der Klasse. Man kann diese Referenz direkt ausdrucken. Es wird der Name der Klasse inklusive eventueller Paketzugehörigkeit ausgedruckt.
Will man nur den Klassennamen ausdrucken, muss man von einer Instanz vom Typ Class die Methode .getSimpleName()aufrufen.
- 6822 views
9.7 Lösungen
9.7 Lösungen9.7.1 Haustierpolymorphismus
Klasse Haustier
package s1.block9;public abstract class Haustier {private String name;private boolean steuerpflichtig;private double kostenTierarzt;/*** Get the value of kostenTierarzt** @return the value of kostenTierarzt*/public double getKostenTierarzt() {return kostenTierarzt;}/*** Set the value of kostenTierarzt** @param kostenTierarzt new value of kostenTierarzt*/public void setKostenTierarzt(double kostenTierarzt) {this.kostenTierarzt = kostenTierarzt;}/*** Get the value of steuerpflichtig** @return the value of steuerpflichtig*/public boolean getSteuerpflichtig() {return steuerpflichtig;}/*** Set the value of steuerpflichtig** @param steuerpflichtig new value of steuerpflichtig*/public void setSteuerpflichtig(boolean steuerpflichtig) {this.steuerpflichtig = steuerpflichtig;}/*** Get the value of name** @return the value of name*/public String getName() {return name;}/*** Set the value of name** @param name new value of name*/public void setName(String name) {this.name = name;}public Haustier(String name, boolean steuerpflichtig, double kostenTierarzt) {this.name = name;this.steuerpflichtig = steuerpflichtig;this.kostenTierarzt = kostenTierarzt;}public String beschreibung() {String stpf = (steuerpflichtig) ? ", " : ", nicht ";String b = "Name :" + name+ stpf + "steuerpflichtig, Kosten: "+ kostenTierarzt;return b;}}
Klasse Hund
package s1.block9;/**** @author s@scalingbits.com*/public class Hund extends Haustier{private String rasse;/*** Get the value of rasse** @return the value of rasse*/public String getRasse() {return rasse;}public Hund(String name, double kostenTierarzt, String rasse) {super(name,true,kostenTierarzt);this.rasse = rasse;}public String beschreibung() {return super.beschreibung() + ", Rasse: " + rasse;}}
Klasse Katze
package s1.block9;public class Katze extends Haustier {private Vogel lieblingsVogel;public String vogel() {String vname;if (lieblingsVogel != null)vname = lieblingsVogel.getName();else vname = "keinen Vogel";return vname;}/**** @param v setzen des Lieblingsvogel*/public void setVogel(Vogel v) { lieblingsVogel=v;}public Katze(String name, double kostenTierarzt, Vogel lieblingsVogel) {super(name, false, kostenTierarzt);if ((lieblingsVogel !=null) && (lieblingsVogel instanceof Vogel))this.lieblingsVogel = lieblingsVogel;}public String beschreibung() {return super.beschreibung() + ", mag " + vogel();}}
Klasse Vogel
package s1.block9;/**** @author s@scalingbits.com*/public class Vogel extends Haustier{private boolean singvogel;/*** Get the value of singvogel** @return the value of singvogel*/public boolean getSingvogel() {return singvogel;}public Vogel(String name, double kostenTierarzt, boolean singvogel) {super(name, false, kostenTierarzt);this.singvogel = singvogel;}public String beschreibung() {String saenger = (singvogel) ? "ein" : "kein";return super.beschreibung() + ", ist "+ saenger + " Singvogel";}}
Klasse TierTest
package s1.block9;public class TierTest {private static Haustier hausTiere[];public static void main(String[] args) {populate();neuerLieblingsvogel();iterate();}public static void populate() {hausTiere = new Haustier[6];hausTiere[0] = new Vogel("Hansi", 50.55f, true);hausTiere[1] = new Vogel("Piep", 50.44f, false);hausTiere[2] = new Hund("Waldi", 222.22f, "Dackel");hausTiere[3] = new Hund("Fiffi", 202.22f, "Terrier");hausTiere[4] = new Katze("Isis", 88.88f, (Vogel) hausTiere[0]);hausTiere[5] = new Katze("Napoleon", 77.77f, null);}public static void neuerLieblingsvogel() {Vogel v;Katze k;if ((hausTiere[1] instanceof Vogel)&& (hausTiere[4] instanceof Katze)) {v = (Vogel) hausTiere[1];k = (Katze) hausTiere[4];k.setVogel(v);}}public static void iterate() {double kosten = 0;for (int i = 0; i < hausTiere.length; i++) {kosten += hausTiere[i].getKostenTierarzt();System.out.println("Art: " + hausTiere[i].getClass().getSimpleName()+ "; " + hausTiere[i].beschreibung());}
System.out.println("Gesamtjahrekosten "+ kosten +" Euro");}}
Ausgaben:
Art: Vogel; Name :Hansi, nicht steuerpflichtig, Kosten: 50.55, ist ein Singvogel
Art: Vogel; Name :Piep, nicht steuerpflichtig, Kosten: 50.44, ist kein Singvogel
Art: Hund; Name :Waldi, steuerpflichtig, Kosten: 222.22, Rasse: Dackel
Art: Hund; Name :Fiffi, steuerpflichtig, Kosten: 202.22, Rasse: Terrier
Art: Katze; Name :Isis, nicht steuerpflichtig, Kosten: 88.88, mag Piep
Art: Katze; Name :Napoleon, nicht steuerpflichtig, Kosten: 77.77, mag keinen Vogel
Gesamtjahrekosten 692.08 Euro
- 5595 views
9.8 Lernziele
9.8 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zum Polymorphismus zu beantworten
Feedback
- 4234 views
10. Ausnahmen (Exceptions)
10. Ausnahmen (Exceptions)Notwendigkeit von Ausnahmebehandlung
Beim Ausführen von Programmen können Ereignisse auftreten die nicht zum normalen Ablauf gehören und trotzdem sinnvoll behandelt werden müssen. Nicht normaler Ablauf bedeutet hier, dass diese Ausnahmen nicht direkt von den Eingaben oder Anweisungen des Programmcodes hervorgerufen werden. Beispiele hierfür sind
- Division einer Ganzzahl durch Null
- Lesen aus einer Datei die nicht vorhanden ist
- Fehlende Resourcen wie Hauptspeicher
- Verlust einer Netzwerkverbindung
- Feldüberlauf etc.
Es ist guter (und erforderlicher) Programmierstil mit solchen Ausnahmen umgehen zu können und sie angemessen behandeln.
Zur Erkennung und Behandlung solcher abnormaler Situationen gibt es eine Reihe von Möglichkeiten die von der gewählen Technologie abhängen können:
- Präventives Kontrollieren von potentiellen Aushnahmen
- Beispiel: Prüfung von Eingabewerten oder Zwischenwerten von Berechnungen auf potentielle Sonderdebingungen wie Nullwerte oder Feldgrenzenüberläufe
- Fehlercoderückgaben bei jedem Operationsaufruf
- Unixkommandos geben Beispielsweise immer einen Fehlerwert mit aus. Ist dieser ungleich Null liegt ein bestimmter Fehler (Ausnahme) vor
- Unterbrechungsgesteuerte (Interrupthandling) Ausnahmen
- Der reguläre Programmablauf wird verlassen und einer anderen Stelle fortgesetzt.
Bei der Behandlung von Ausnahmen steht der Entwickler in der Regel vor dem folgenden Zielkonflikten
niedriger Implementierungsaufwand mit hohem Fehlerrisiko | hoher Implementierungsaufwand mit geringem Fehlerrisiko |
übersichlicher aber wenig robuster Code | unübersichtlicher (und fehleranfälliger) Code mit vielen Präventivüberprüfungen |
Moderne Programmiersprachen und -umgebungen bieten eine Infrastruktur um die diese Zielkonflikte zu mildern. Die Technologie einer Rückgabe von Fehlerzuständen bei jeder einzelnen Operation macht es seht unwahrscheinlich, dass ein Entwickler bei jeder einzelnen Operation diesen Wert auswertet und individuelle Maßnahmen ergreift.
Die Programmiersprache Java unterstützt den Entwickler mit einem mächtigen Konzept zur Ausnahmebehandlung (Exceptionhandling) mit dem man Unterstützung in den folgenden Bereichen bekommt:
- Möglichkeit Entwickler zur Behandlung von Ausnahmen zu zwingen
- Möglichkeit potentielle Ausnahmen in größeren Codeblöcken an einer Stelle zu behandlen
- Objektorientierte Konzepte um Ausnahmehierarchien zu modellieren und damit die Behandlung ganzer Klasse von Ausnahmen zu erlauben
Beispiel einer Ausnahme in Java
Die Zuweisung des Ausdrucks auf die Variable c löst eine Ausnahme aus, da hier durch Null dividiert wird:
package Kurs2.Exception;
public class Beispiel {
public static void main(String args[]) {
int a = 18;
int b = 6;
int c = (a+b)/(3*b-18); int d = 17; // wird nicht erreicht
}
}
Bei der Division durch Null erzeugt Java ein Ausnahmeobjekt in dem die Ausnahme beschrieben ist. Wird die Ausnahme nicht vom Entwickler behandelt nutzt Java diese Information. Gibt sie auf der Konsole aus und beendet das Programm.
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Kurs2.Exception.Beispiel.main(Beispiel.java:6)
Syntax einer Java-Ausnahme auf der Konsole
Die Klassenhierarchie des Java-Laufzeitsystems
Java verwendet eine Reihe von Klassen zur Verwaltung von Ausnahmen. Bei Auftreten von Ausnahmen werden Instanzen dieser Klassen erzeugt. Mit Hilfe dieser Instanzen kann man Ausnahmen analysieren und behandeln. Anbei ein Auszug der Klassenhierarchie des Java-Laufzeitsystems:
Exceptions (Ausnahmen) in UML
UML benutzt Unterbechungskanten um Ausnahmen in Aktivitätsdiagrammen zu beschreiben. Unterbrechungskanten werden als Pfeile in Form eines Blitzes gezeichnet. Die Ausnahmebedingung wird in rechteckigen Klammern an der Unterbrechungskante dokumentiert. Da folgende Beispiel zeigt das Sortieren von Personen welches abgebrochen wird wenn eine Person eine leere Zeichenkette für den Nachnamen besitzt.
UML Notation mit Pfeil in Blitzform |
UML Notation mit Aktivitätspfeil |
---|---|
Weiterführende Quellen
- Javaworld: Java 101: Mastering Java exceptions, Part 1
- Zwei Seiten auf englisch, die alle wichtigen Kernkonzepte abdecken
- 8753 views
10.1 Java-Ausnahmen (Exceptions) behandeln
10.1 Java-Ausnahmen (Exceptions) behandelnJava zwingt den Entwickler zur Behandlung von Ausnahmen wenn er Methoden aufruft die zu "Checked Exceptions" führen können.
Entwickler haben hier zwei Lösungsoptionen
- die Möglichkeit die Behandlung einer Ausnahme an den Aufrufer der eigenen Methode zu delegieren
- die Behandlung der Ausnahme im aktuellen Block selbst zu implementieren.
Der Javaübersetzer wird den quellcode nicht übersetzen falls der Entwickler keine Maßnahme ergriffen. Die zwangsweise Behandlung von "Checked Exceptions" ist Teil des Schnittstellenvertrags zur Benutzung einer Methode.
Behandeln einer Ausnahme mit einem try-catch Block
Java erlaubt die Ausnahmebehandlung mit Hilfe von try-catch-finally Programmblöcken die aus drei Teilen bestehen:
Block | Anzahl | Inhalt |
---|---|---|
try | einmal | Block enthält den eigentlichen Anwendungscode. Er wird "versucht" (try) komplett auszuführen. In ihm können gewisse Ausnahmen auftreten. Er wird verlassen wenn wenn eine Ausnahme auftritt |
catch | einmal pro Exceptiontyp | Block wird ausgeführt wenn eine bestimmte Ausnahme auftritt. Er "fängt" die Ausnahme und behandelt sie. |
finally | einmal, optional | Block wird unbedingt ausgeführt. Er wird entweder nach dem regulären durchlaufen des try-Blocks oder nach dem Durchlaufen des catch-Blocks ausgeführt. Hier werden typischerweise Resourcen wie offene Dateien geschlossen und wieder freigegeben. |
Der try Block wird beim Auftreten einer Ausnahme direkt verlassen und es wird in den catch-Block gesprungen.
Die Java hat die folgende Syntax:
try { ... // in diesem Block wird der reguläre Code implementiert // Dieser Code kann an beliebigen Stellen Ausnahmen auslösen } catch (ExceptionTypeA myName1) { ... // dieser Block wird aufgerufen wenn im try-Block eine Ausnahme // vom Typ ExceptionTypeA auslöst } catch (ExceptionTypeB myName2) { ... // dieser Code wird aufgerufen wenn im try-Block eine Ausnahme // vom Typ ExceptionTypeB auslöst } finally { ...// Dieser Block ist optional // er wird immer aufgerufen. Er hängt nicht vom Auslösen einer Ausnahme ab }
Ablaufsteuerung
Wichtig: "Geworfene" Ausnahmen führen zum Beenden des Programmes falls sie nicht innerhalb eines try-Blocks mit der entsprechenden catch-Klausel und passendem Exception-typ gefangen und bearbeitet werden! Das Javaprogramm wird jede weitere Ausführung einstellen und die entsprechenden Ausführungsblöcke verlassen. Es wird auf der Kommandozeile das Auftreten der entsprechende Ausnahme dokumentieren.
Der try-Block kann jedoch in einem beliebigen äusseren Block implementiert sein. Das heißt, Methoden die einen solchen Codepfad direkt oder indirekt aufrufen, müssen in einem solchen try-catch-Block eingehüllt sein.
Ablauf mit finally Block | Ablauf ohne finally Block |
---|---|
; |
Beispiel 1: NumberFormatException
Im folgenden Beispiel wird das erste Kommandozeilenargument in eine ganze Zahl umgewandelt. Hier kann eine NumberFormatException Ausnahme bei der Umwandlung der Eingabezeichenkette args[0] auftreten. Tritt diese Ausnahme auf, wird der Variablen firstArg eine Null zugewiesen.
package s1.block10; public class Main1 { public static void main(String[] args) { int firstArg=0; int stellen=0; if (args.length > 0) { try { firstArg = Integer.parseInt(args[0]); } catch (NumberFormatException e) { firstArg = 0; } finally { System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen"); } } } }
In diesem Beispiel wurde die Größe des Eingabefeldes präventiv mit einer if Bedingung abgeprüft.
Beispiel 2: ArrayIndexOutOfBoundsException
Die präventive Prüfung der Feldgröße kann aber auch in einem zweiten catch Block zum gleichen try Block durchgeführt werden.
package s1.block10; public class Main2 { public static void main(String[] args) { int firstArg = 0; int stellen = 0; try { firstArg = Integer.parseInt(args[0]); } catch (ArrayIndexOutOfBoundsException e) { firstArg = -1; System.out.println("Argument auf Position " + e.getMessage() + " nicht vorhanden"); } catch (NumberFormatException e) { firstArg = 0; } finally { System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen"); } } }
Das Behandeln des Feldüberlaufs mit Hilfe eines catch-Blocks ist die flexiblere Lösung da sie jeglichen Feldüberlauf innerhalb des try-Blocks fangen kann.
Fehleranalyse im catch-Block
Dem catch-Block wird eine Instanz der bestimmten Klasse der Ausnahme mitgegeben. In Beispiel 2 wird die Ausnahmeinstanz e benutzt um mit Hilfe der Methode e.getMessage() die Postion im Feld auszulesen die die ArryIndexOutOfBoundsException Ausnahme auslöste.
Behandeln einer Ausnahme durch Delegation an die nächst äussere Methode
Die zweite Möglichkeit eines Entwicklers besteht darin, die Methode nicht direkt zu behandeln und das Behandeln der Ausnahme dem Aufrufer der akuellen Methode zu überlassen. Dies geschieht in dem man im Kopf der Methode dokumentiert welche Ausnahmen durch den Aufruf einer Methode ausgelöst werden können. Die Methode stringLesen2() kann zum Beispiel zum Auslösen einer IOException Ausnahme führen:
public static String StringLesen2() throws IOException{ ... }
Methoden können mehr als eine Ausnahme auslösen. Die Syntax des Methodenkopfs ist die folgende:
[Modifikatoren] Rückgabewert Name-der-Methode ([Parameter]) throws Name-Ausnahme [, Name-Ausnahme]
Der Aufrufer der Methode muss dann die Behandlung selbst durchführen.
Hinweis: Die Angabe der Ausnahmen die von einer Methode ausgelöst haben keine Bedeutung bei der Unterscheidung von überladenen Methoden.
Beispiel
Im folgenden Fall werden zwei Methoden zum Einlesen einer Konsoleneingabe gezeigt.
Die Methode stringLesen1() behandelt die IOException Ausnahme selbst. Der Aufrufer im main() Hauptprogramm muss keine Maßnahmen ergreifen.
Due Methode stringLesen2() behandelt die Ausnahme nicht und "exportiert" sie an den Aufrufer. Hier muss das Hauptprogramm main() die Ausnahme selbst.
package s1.block10;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class Main3 {
public static void main(String[] args) {
String eingabe = "";
eingabe = StringLesen1();
System.out.println(eingabe);
try {
eingabe = StringLesen2();
} catch (IOException e) {
eingabe = " KEINE EINGABE BEI ZWEITEM AUFRUF;";
}
System.out.println(eingabe);
}public static String StringLesen1() {
String input = "Konsoleneingabe(1): ";
// liest einen vom Benutzer eingegebenen Text (String) ein
BufferedReader keyboard =
new BufferedReader(new InputStreamReader(System.in));
try {
input = input + keyboard.readLine() + ";";
} catch (IOException e) {
input = input + " KEINE EINGABE;";
}
return input;
}public static String StringLesen2() throws IOException{
String input = "Konsoleneingabe(2): ";
// liest einen vom Benutzer eingegebenen Text (String) ein
BufferedReader keyboard =
new BufferedReader(new InputStreamReader(System.in));
input = input + keyboard.readLine() + ";";
return input;
}
}
Wichtig |
---|
Eine Methode kann nur "Checked Exceptions" auslösen die von ihr in der throws Klausel angegeben wurden. |
Abarbeitung von mehreren catch-Klauseln
Java sucht bei mehreren catch-Klauseln (Handler) für eine Ausnahmebehandlung alle Klauseln von oben nach untern. Die erste Klausel mit einem passenden Typ der Ausnahme wird dann aufgerufen.
Aufgrund des Polymorphismus kann es mehrere Möglichkeiten geben. Klauseln die für eine Oberklasse einer Ausnahme geschrieben wurden werden benutzt werden wenn eine speziellere Ausnahme abgehandelt werden muss.
Beim Benutzen von mehreren catch Klauseln sollten daher die speziellen Ausnahmen vorne stehen und die allgemeinen Ausnahmen am Ende.
Finally Block (Vertiefung)
Der finally Block wird immer ausgeführt. Dies im Extremfall zu ungewöhnlichen Situationen führen. Analysieren Sie das folgende Programm:
package s1.block10;class BeispielFinally {
public static int finallyTest (int a) {
int result=0;
try {
result = 17/a; // Hier kann eine ArithmeticException ausgelöst werden
}
catch (ArithmeticException e) {
return 0;
}
finally {
return 1;
}
//System.out.println("Methode beendet. Wir nie erreicht. Daher auskommentiert");
return result;
}
public static void main(String[] args) {
int x=0;
System.out.println(finallyTest(0));
}
}
Es erzeugt die folgende Konsolenausgabe:
1
- 10835 views
10.2 Java-Ausnahmen deklarieren und werfen
10.2 Java-Ausnahmen deklarieren und werfenWerfen von Ausnahmen
Java erlaubt nicht nur die vom Laufzeitsystem erzeugten Ausnahmen zu behandeln. Java erlaubt es auch selbst Ausnahmen zur Erzeugen. Dies geschieht durch das Erzeugen einer Instanz einer Klasse die aus der Klasse Throwable abgeleitet ist. Im folgenden Beispiel geschieht dies, in dem vor einer Division auf eine mögliche Division durch Null geprüft wird :
package s1.block10; import java.lang.IllegalArgumentException; public class Grundrechenarten1 { public static int division(int a, int b) { IllegalArgumentException ex; if (b==0) { ex = new IllegalArgumentException("Division durch 0!"); throw ex; } return a/b; } public static void main(String[] args) { int c = division(3,0); } }
Das Werfen einer eigenen Ausnahme erfolgt in zwei Schritten:
- Dem Erzeugen einer Instanz der Ausnahmeklasse. Hier kann ein Text mit Informationen im Konstruktor mitgegeben werden.
- Dem Werfen der Ausnahme mit dem Schlüsselwort throw und der Angabe eines Ausnahmeobjekts
Beim Ausführen des oben gezeigten Programms wird die folgende Meldung ausgegeben:
Exception in thread "main" java.lang.IllegalArgumentException: Division durch 0! at s1.block10.Grundrechenarten1.division(Grundrechenarten.java:9) at s1.block10.Grundrechenarten1.main(Grundrechenarten.java:16)
Da das Ausnahmeobjekt in der Regel nicht weiter oder wieder verwendet wird, kann man das Erzeugen des Objekts direkt mit dem Werfen der Ausnahme verbinden:
throw new IllegalArgumentException("Division durch 0!");
Selbstimplementierte Ausnahmeklassen
Java erlaubt auch das Implementieren eigener Ausnahmeklasse. Man kann zum Beispiel die Klasse IllegalArgumentException zu einer eigenen Klasse ableiten:
package s1.block10; public class DivisionException extends IllegalArgumentException { int dividend; int divisor; public DivisionException(int a, int b) { super ("Versuch von Division " + a + "/" + b); dividend =a; divisor = b; } }
Das Hauptprogramm muss entsprechend angepasst werden:
package s1.block10; import java.lang.IllegalArgumentException; public class Grundrechenarten2 { public static int division2(int a, int b) { DivisionException ex; if (b==0) { ex = new DivisionException(a,b); throw ex; } return a/b; } public static void main(String[] args) { int c = division2(3,0); } }
Es wird dann eine angepasste Fehlermeldung geworfen:
Exception in thread "main" Kurs2.Exception.DivisionException: Versuch von Division 3/0 at s1.block10.Grundrechenarten2.division2(Grundrechenarten.java:18) at s1.block10.Grundrechenarten2.main(Grundrechenarten.java:25) Java Result: 1
- 12903 views
10.3 "Checked" und "Unchecked Exceptions"
10.3 "Checked" und "Unchecked Exceptions"Die Klasse Throwable
Die Klasse Throwable ist die Basisklasse der gesamten Ausnahmehierarchie. Instanzen von ihr sorgen dafür das Ausnahmen "geworfen" werden, die dann behandelt werden können.
Alle Klassen in der Throwable Hierarchie können den Grund für das Werfen einer Ausnahme im Konstruktor als Zeichenkette beschreiben.
Die Klasse Error
Die Klasse Error wird für Probleme der Java virtuellen Maschine selbst verwendet. Diese Fehler sollten nicht auftreten. Fehler die durch diese Klasse gemeldet werden sind in der Regel so schwerwiegend, dass keine weitere Fortführung des Programms sinnvoll ist.
Ein Beispiel hierfür ist das Fehlen von freiem Hauptspeicher. Er wird mit einem OutOfMemoryError Fehler gemeldet. Eine sinnvolle Behandlung dieses Fehlers ist nur schwer möglich. Selbst ein Berichten über die Details dieses Problem benötigt Speicherplatz zum Verwalten von Zeichenketten. Der Versuch einer Behandlung eines solchen Fehlers ist nur sinnvoll wenn man in der Lage ist, in nativen Bibliotheken die die Anwendung selbst verwaltet unmittelbar auf C/C++ Ebene Speicher freizugeben.
Da alle Fehler in dieser Klassenhierarchie (Errror) nicht direkt von der entsprechenden Sektion im Programm abhängen und sehr selten sind, zählen diese Fehler zu den "unchecked Exceptions". Ein Entwickler muss Codestrecken nicht mit einem try-catch Block versehen und eine Behandlung implementieren.
Wichtig |
---|
Alle abgeleiteten Klassen der Klasse Error sind "unchecked exceptions". Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden. |
Hintergrund: Fehler in dieser Kategorie können in jeder Java-Codestrecke auftreten. Der Entwickler müsste seinen gesamten Code mit try-catch Blöcken versehen. Hinzukommt, dass eine Behandlung in der Regel nicht möglich ist. Try-catch Blöcke sind allso nicht sinnvoll für diese Kategerie von Ausnahmen.
Oberklasse Error | Bedeutung |
---|---|
InstantiationError | Versuch des Erzeugens einer Instanz einer abstrakten Klasse oder einer Schnittstelle |
StackOverflowError | Überlauf des Stapel (Stack). Typische Ursachen sind Programm mit extrem vielen und tiefen Methodenaufrufen oder ein nicht terminierendes rekurvsives Programm |
OutOfMemoryError | Der Javaprozess hat keinen freien Hauptspeicher mehr und kann keine weiteren Objekte allokieren |
Die Klasse Exception
Aus der Klasse Exception werden alle Ausnahmen abgeleitet, die ein Entwickler zur Laufzeit behandeln kann. Alle Klassen in dieser Unterhierarchie sind "Checked Exceptions" mit Ausnahme der Klasse RuntimeException. Dies bedeutet Sie müssen mit try-catch Blöcken behandelt werden.
Die Klasse RuntimeException
In der Hierarchie der Klasse RuntimeException sind alle Ausnahmen zu finden die im "normalen" Betrieb des Laufzeitsystems auftreten können. Sie sind auch "unchecked Exceptions" die man nicht im Code behandeln muss, da sie jederzeit auftreten können.
Wichtig |
---|
Alle abgeleiteten Klassen der Klasse RuntimeException sind "unchecked exceptions". Implikation: Sie müssen nicht mit try-catch Blöcken abgesichert werden. |
Einige Ausnahmeklassen aus dieser Hierarchie sind:
Oberklasse RuntimeException | Bedeutung |
---|---|
ArithmeticException | Division durch Null bei Ganzzahlwerten. |
NegativeArraySizeException | Versuch der Erzeugung eines Feldes mit einer negativen Größe. |
NullPointerException | Versuchter Zugriff auf eine Instanz deren Referenz mit null belegt ist. |
Das Konzept von "Checked" und "Unchecked Exceptions"
Die Unterscheidung dieser beiden Arten von Ausnahmen hat für den Entwickler die Konsequenz, dass er bei Aufrufen von Methoden die zu "Checked Exceptions" führen, eine entsprechende Behandlung bei der Implementierung durchführen muss. Behandlung bedeutet. "Unchecked Exceptions" müssen nicht in der Implementierung berücksichtig und behandelt werden.
Checked Exception
Bei Aufrufen von Methoden die zu "Checked Exceptions" führen können hat der Entwickler zwei Möglichkeiten:
- Aufruf der entsprechen Methode innerhalb eines try-catch Blocks und der entsprechenden Behandlung der Ausnahme
- Weiterreichen der Ausnahme indem die gesamte Methode das Schlüsselwort throws und die Ausnahme benutzt um die Behandlung der Ausnahme an den aufrufenden Block weiterreicht.
Wird eine "checked Exception nicht mit einem try-catch Block behandelt so besteht auch die Möglichkeit die Ausnahme nicht zu behandeln. In diesem Fall muss die Ausnahme aus der aktuellen Methode herausgereicht werden. Hierfür muss die Methode im Kopf der entsprechenden Methode mit Hilfe des Schlüsselwort throws nach Aussen weitergereicht werden. Die Syntax des Methodenkopfs ist:
qualifier [return-parameter] methoden-name (parameter-liste) {throws eine-ausnahme} {...}
Ein konkreter Methodenkopf ist z. Bsp.
public boolean istKleinerAls (Object s) throws NachnameExceptionRT {...}
Beispiel: Die Klasse BufferReader.readLine() wird benötigt um Konsoleneingaben zu Lesen.
Unchecked Exception
"Unchecked Exception" sind sehr vielfältig und sie können in fast jeder Zeile Code auftreten. Diese Ausnahmen müssen nicht zwingend mit try-catch Blöcken abgefangen werden.
Der Entwickler muss bei "Unchecked Exceptions" selbst die Entscheidung treffen welche Codestrecken das Potential haben eine Ausnahme auszulösen.
Das Entwurfsziel besteht darin sicher zu codieren und den Code noch lesbar zu gestalten.
Beispiel:
Eine potentielle Division durch Null kann an sehr vielen Stellen auftreten. Müsste man jede Division durch Null mit einem try-catch Block ummanteln wäre das sehr aufwendig und der Code wird sehr schlecht lesbar sein.
In der folgenden Tabelle können in den ersten beiden Fällen keine Divisionen durch Null auftreten. Im dritten Fall kann eine solche Division abhängig vom Eingabewert c auftreten. Hier obliegt es dem Entwickler das Risiko einer Division durch Null abzuschätzen und die Division eventuell mit einem try-catch Block zu schützen.
Fall 1: kein Risiko | Fall 2: kein Risiko | Fall 3: Risiko einer Ausnahme |
---|---|---|
public int test(int a) { int b = a / 2; return b; } |
public int test(int a) { int c = 2; int b = a / c; return b; } |
public int test(int a, int c) { int b = a / c; return b; } |
- 13835 views
Checked Exceptions
Warum kann eine Methode erben? (Konzept der Checked Exceptions)
- Log in to post comments
Bitte nochmal neu formulieren...
Die Frage ist für mich nicht richtig verständlich. Klassen vererben Methoden on andere Klassen. Exceptions sind Klassen. Bitte noch mal neu formulieren.
- Log in to post comments
10.4 Weiterreichen von Ausnahmen
10.4 Weiterreichen von AusnahmenJava erlaubt es nicht nur in catch Blöcken Ausnahmen zu behandeln, Java erlaubt es in einem catch Block die Ausnahme zu bearbeiten und anschließend trotzdem unbehandelt weiter zureichen.
Hierdurch kann eine Aktion durchgeführt werden und die finale Lösung des Problems einem äusserem Block überlassen werden.
try { DemoException einProblem = new DemoException ("Anwendungsfehler 17"); throw einProblem; } catch (DemoException e) { //Analyse der Ausnahme if (e.getMessage().equals("Anwendungsfehler 17")) throw e; }
Hinweis: Die Anwendung muss nicht notwendigerweise die gleiche Ausnahme weiterreichen.
Es ist auch möglich eine beliebige neue Ausnahme zu werfen. Dies kann nützlich sein, wenn man nur noch anwendungsspezifische Ausnahmen nach aussen reichen will, um andere Klassen von Ausnahmen zu verbergen.
- 6198 views
10.5 Der try-with-resources Befehl
10.5 Der try-with-resources BefehlDies ist eine weiterführendes Thema...
Den try-with-resources Befehl kann man seit Java 7 benutzen. Er erlaubt das Schliesen von Ressourcen und ersetzt damit den finally Block.
Java erlaubt es mit externen Ressourcen wie Dateien oder Streams umzugehen indem man Sie öffnet. Diese Ressourcen müssen aber auch wieder geschlossen werden, da das Betriebsystem hierfür Ressourcen allokieren muss.
Vor Java 7 schloß man Ressourcen im folgenden Stil:
BufferedReader buffr = new BufferedReader(new FileReader(path)); try { return buffr.readLine(); } finally { // Wird auf jeden Fall ausgeführt! if (buffr != null) {buffr.close()}; }
Ab Java 7 kann man das eleganter beschreiben:
try (BufferedReader buffr = new BufferedReader(new FileReader(path))) { return buffr.readLine(); }
Um diesen Stil zu verwenden, muss die Klasse die Schnittstelle java.lang.AutoCloseable b.z.w. java.io.Closeable implementieren.
Dieser try-Block unterdrückt die geworfene Ausnahme!
Mehr Informationen gibt es im folgenden Oracle Tutorial.
- 2933 views
10.6 Übungen (Ausnahmen)
10.6 Übungen (Ausnahmen)Übung 1: Werfen einer Ausnahme
Verfeinern Sie die Implementierung der Klasse Person. Werfen Sie eine Ausnahme der Klasse UnsupportedOperationException wenn beim Vergleich in der Methode compareTo() eine leere Zeichenkette ("") als Nachname implementiert ist.
Nutzen Sie die Klasse Person:
package s1.block10;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
public class Person implements Comparable {
private String nachname;
private String vorname;
private static int maxLaenge = 10; // Maximale Laenge von Zufallszeichenketten
private static Locale myLocale = Locale.GERMANY; // Zu verwendende Sortierordnung
/**
* Konstruktor der Klasse Person
* @param nn Nachname
* @param vn Vorname
*/
public Person(String nn, String vn) {
nachname = nn;
vorname = vn;
}
/**
*
* @param s Objekt mit dem verglichen wird
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
Person p;
if (s.getClass() == this.getClass()) {
p = (Person) s;
// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}
/**
* Erlaubt den Vor- und Nachnamen als Text auszudrucken
* @return
*/
@Override
public String toString() {
return nachname + ", " + vorname;
} /** * * @param args wird nicht ausgewertet */public static void main(String[] args) {
// Aufzählende Iniatialisierung eines Felds
Person[] psn = {
new Person("Schneider", "Hans"),
new Person("Schmidt", "Hans"),
new Person("Schneider", "Gretel"),
new Person("Schmitt", "Hans"),
new Person("Meier", "aschenputtel"),
new Person("", "Aschenputtel")
};
Arrays.sort(psn);
System.out.println("Sortiert:");
for ( Person p : psn) {
System.out.println(p);
}
}
}
Spoileralam für versierte Programmier; nicht weiterlesen!
Man kann einen leeren String mit dem folgenden Test erkennen:
if (p.vorname.equals(""))
Übung 2: Werfen einer selbstimplementierten Ausnahme
Implementieren Sie eine Klasse Kurs2.Exception.NachnameException mit den folgenden Eigenschaften:
- Abgeleitet aus der Klasse RuntimeException oder CheckedException
- Aus welcher der beiden Klassen können Sie Ihre Ausnahme nicht ableiten. Warum nicht? Wo gibt es einen Konflikt
- Aus welcher der beiden Klassen können Sie Ihre Ausnahme nicht ableiten. Warum nicht? Wo gibt es einen Konflikt
- Erfasst den Vornamen als Text der Ausnahme
- Speichern des Vornamens für potentielle Wiederverwendung bei der Analyse der Ausnahme
Vorlage für Implementierung. Wählen Sie die passende der beiden Klassen aus der Sie die Ausnahme ableiten
package Kurs2.Exception; public class NachnameException extends [RuntimeException|CheckedException] { ... public NachnameException(String vn) {...} }
Nutzen sie die Klasse Person der Übung 1. Passen Sie die Klasse Person so an, dass die neue Ausnahmenklasse aufgerufen wird.
Übung 3: Werfen einer selbstimplementierten Ausnahme und Behandeln
Behandeln Sie die Ausnahme in der main() Methode und geben Sie eine passende Konsolenausgabe.
Optionale Übung
Falls Ihre selbst geschriebene Ausnahme einen Zeiger auf das Objekt mit einem Null-String verwaltet, können Sie diesen Null-String mit etwas anderem ersetzen und den Sortiervorgang erneut aufrufen!
- 5302 views
10.7 Lösungen (Ausnahmen)
10.7 Lösungen (Ausnahmen)Übung 1: Werfen einer Ausnahme
Hinweis: Alle Klassen dieser Übung werden im Paket Kurs2.Exception neu implementiert. Sie haben daher andere import Kommandos als die Klassen im Paket Kurs2.Sort.
Klasse Person (nur die Methode mit den erforderlichen Änderungen)
...
/**
*
* @param o
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
PersonUebung1 p;
if (s.getClass() == this.getClass()) {
p = (PersonUebung1) s;
if (p.vorname.equals("")) {
throw new UnsupportedOperationException("Vorname fehlt");
}
if (nachname.equals("")) {
throw new UnsupportedOperationException("Nachname fehlt");
}
// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}
...
Übung 2: Werfen einer selbstimplementierten Ausnahme
Klasse NachnameException:
package s1.block10;public class NachnameException extends RuntimeException {
public String vorname;public NachnameException(String vn) {
super(vn + " hat keinen Nachnamen");
vorname = vn;
}}
In der Klasse Person der vorherigen Übung muss die Methode compareTo() auch angepasst werden:
...
/**
*
* @param o
* @return -1 wenn der Eingabewert kleiner ist
* 0 wenn der Eingabewert gleich ist
* +1 wenn der Eingabewert größer ist
*/
@Override
public int compareTo(Object s) {
int result = -1;
PersonUebung2 p;
if (s.getClass() == this.getClass()) {
p = (PersonUebung2) s;
if (p.vorname.equals("")) {
throw new UnsupportedOperationException("Vorname fehlt");
}
if (nachname.equals("")) {
throw new NachnameException(vorname);
}
// Konfiguriere die Sortierordnung
Collator myCollator = Collator.getInstance(myLocale);
myCollator.setStrength(Collator.TERTIARY);
result = myCollator.compare(nachname, p.nachname);
if (result ==0)
result = myCollator.compare(vorname, p.vorname);
}
return result;
}...
Übung 3: Werfen einer selbstimplementierten Ausnahme und Behandeln
Klasse NachnameExceptionChecked
package Kurs2.Exception;public class NachnameExceptionChecked extends Exception {
public String vorname;
public Boolean zweitesArgument;
/**
*
* @param vn Vorname des Objekts ohne Nachnamen
* @param zweitesArg ist wahr wenn das zweite Objekt beim Vergleich
* keinen Nachnamen hat. Ist falsch wenn das erste Objekt keinen
* Nachnamen hat
*/
public NachnameExceptionChecked(String vn, Boolean zweitesArg) {
super(vn + " hat keinen Nachnamen");
vorname = vn;
zweitesArgument = zweitesArg;
}}
Klasse Person
Die Methode main():
public static void main(String[] args) {
Person[] psn = new Person[6];
psn[0] = new Person("Schneider", "Hans");
psn[1] = new Person("Schmidt", "Hans");
psn[2] = new Person("Schneider", "Gretel");
psn[3] = new Person("Schmitt", "Hans");
psn[4] = new Person("Meier", "aschenputtel");
psn[5] = new Person("", "Aschenputtel");
try {
Arrays.sort(psn);
}
catch (NachnameException e) {
System.out.println("Das Feld konnte nicht sortiert werden, da " +
e.vorname + " keinen Nachnamen besitzt");
}
System.out.println("Sortiert:");
for ( PersonUebung3 p : psn) {
System.out.println(p);
} }
- 3946 views
10.8 Beispielprogramme aus der Vorlesung
10.8 Beispielprogramme aus der VorlesungIn diesem Beispiel wird die Klasse Konto Schritt für Schritt weiterentwickelt um die den Einsatz von Assertions und Ausnahmen zu diskutieren.
Die durchnummierten Klassen repräsentieren verschiedene Enwticklungsstufen. Sie erlauben es wieder den Einstieg zu finden falls man beim Mittippen nicht mehr mitgekommen ist.
Klasse Konto
Beispiel eines Bankkontos. In der main() Methode werden einfache Überweisungsoperationen ausgeführt.
Welche inkorrekten Aufrufe können in dieser Klasse erfolgen?
package s1.block10; public class Konto { int betrag; public Konto(int startBetrag) { betrag = startBetrag; } private void einzahlen(int b) { betrag += b; } private void auszahlen(int b) { betrag -= b; } public void ueberweisenAuf (Konto k, int wert) { auszahlen(wert); k.einzahlen(wert); } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto a1 = new Konto(500); Konto a2 = new Konto(100); a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); }
}
Klasse Konto1
package s1.block10; public class Konto1 { int betrag; public Konto1(int startBetrag) { betrag = startBetrag; } private void einzahlen(int b) { betrag += b; } private void auszahlen(int b) { betrag -= b; } public void ueberweisenAuf (Konto1 k, int wert) { auszahlen(wert); k.einzahlen(wert); } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto1 a1 = new Konto1(500); Konto1 a2 = new Konto1(100); a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto2
In diesem Beispiel werden die Methoden so modifiziert, dass sie einen Wahrheitswert zurückgeben der über die korrekte Ausführung informiert.
Welche Nachteile hat eine solche Implementierung?
package s1.block10; public class Konto2 { int betrag; public Konto2(int startBetrag) { betrag = startBetrag; } private boolean einzahlen(int b) { if (b>=0) { betrag += b; return true; } else return false; } private boolean auszahlen(int b) { if (b>=0) { betrag -= b; return true; } else return false; } public boolean ueberweisenAuf (Konto2kb, int wert) { boolean korrekt; korrekt = auszahlen(wert); if (korrekt) korrekt= k.einzahlen(wert); return korrekt; } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nicht die interne Implementierung der Klasse. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto2 a1 = new Konto2(500); Konto2 a2 = new Konto2(100); boolean korrekt; korrekt = a2.ueberweisenAuf(a1, 50); if (korrekt) System.out.println("a1: "+ a1 + "; a2= " +a2); else System.out.println("Fehler; a1: "+ a1 + "; a2= " +a2); korrekt = a2.ueberweisenAuf(a1, -500); if (korrekt) System.out.println("a1: "+ a1 + "; a2= " +a2); else System.out.println("Fehler; a1: "+ a1 + "; a2= " +a2); } }
Kasse Konto3
In dieser Implementierung werden "Assertions" verwendet.
Die Verwendung der Assertion wird mit Hilfe der Option -ea aktiviert.
Man startet das Programm mit dem Befehl
$ java -ea Kurs1.Exception.Demo.Konto3
Jetzt werden die Assertions ausgeführt und geprüft. Das Programm wird bei der ersten Verletzung einer Assertion beendet.
package s1.block10; public class Konto3 { int betrag; public Konto3(int startBetrag) { betrag = startBetrag; } private void einzahlen(int b) { assert (b>=0): "Versuch " + b + " zu einzuzahlen"; betrag += b; } private void auszahlen(int b) { assert (b>=0): "Versuch " + b + " zu auszuzahlen"; betrag -= b; } public void ueberweisenAuf (Konto3 k, int wert) { auszahlen(wert); k.einzahlen(wert); } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto3 a1 = new Konto3(500); Konto3 a2 = new Konto3(100); boolean korrekt; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Tipp für Netbeansbenutzer
Auf Eclipse und IntelliJ ist sinngemäss das gleiche zu tun.
Einschalten des Assertionchecking für die Klasse Konto3:
- Rechtsklicken und halten auf dem Projektsymbol (Kaffeetassensymbol) in dem sich die Klasse Konto3 befindet.
- Auswahl des letzten Menüpunkts "Properties".
- Auswahl der Kategory "Run"
- In "Configuration" den "New" Knopf drücken
- Im modalen Dialog einen Konfigurationsname wählen (z.Bsp. Konto3Assertion)
- Feld "Main Class" belegen in dem man den Knopf rechts davon "Browse" drückt und die gewünschte Klasse im Projekt wählt
- Es werden alle Klassen im Projekt angezeigt, die als Startprogramm dienen können. Wählen Sie die Klasse Konto3 aus.
- Fügen sie im Textfeld "VM Options" "-ea" um Assertions einzuschalten.
- Speichern Sie den Dialog duech Drücken der Taste "OK"
Starten des Programms
- Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
- Auswahl des letzten Menüpunkts "Set Configuration".
- Wählen Sie die im Vorgängerschritt angelegte Konfiguration
- Starten Sie das Programm mit Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
- Wählen Sie "Run" aus
Das Programm kann jetzt wahrscheinlich auch mit der Ikone mit dem großen grünen Dreieck in der Menüleiste gestartet werden.
Klasse Konto4
Das Verwenden von Assertions in den privaten Methoden einzahlen() und auszahlen() ist sinnvoll, da die beiden Methoden nur innerhalb der Klasse aufgerufen werden können. Der Entwickler der Klasse kann garantieren, dass sie immer mit korrekten Werten aufgerufen werden. Ein negativer Betrag als Parameter soll nicht vorkommen.
In der folgenden Klasse wir die Methode ueberweisenAuf() auch mit einer Assertion geschützt. Die Methode ist öffentlich und kann auch von externen Klassen aufgerufen werden.
Warum ist dies keine gute Implementierung?
package s1.block10; public class Konto4 { int betrag; public Konto4(int startBetrag) { betrag = startBetrag; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto4 k, int wert) { assert(wert>=0): "Versuch einer negativen Überweisung von " + wert ; auszahlen(wert); k.einzahlen(wert); } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto4 a1 = new Konto4(500); Konto4 a2 = new Konto4(100); boolean korrekt; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto5
Die Klasse Konto5 erweitert die Implementierung. Es wird ein Überweisungslimit eingeführt. Das Überweisungslimit wird im Konstruktor erfasst. Es sollen keine Überweisungen mit einem höheren Betrag als dem Überweisungslimit ausgeführt.
package s1.block10; public class Konto5 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public Konto5(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto5 k, int wert) { assert(wert>=0); auszahlen(wert); k.einzahlen(wert); } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto5 a1 = new Konto5(500, 50); Konto5 a2 = new Konto5(100, 80); boolean korrekt; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, 100); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto6
In dieser Implementierung werden inkorrekte Überweisungen mit negativen Beträgen oder Beträgen jenseits des Überweisungslimit mit Hilfe von unterschiedlichen Rückgabewerten und Konstanten gemeldet.
Die gewählte Implementierung vermeidet Fehler und meldet diese ausführlich.
Die Behandlung der Fehlerwerte ist jedoch aufwändig.
package s1.block10; public class Konto6 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto6(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public int ueberweisenAuf (Konto6 k, int wert) { if (wert < 0) return NEGATIVERWERT; else if ((wert > ueberweisungsLimit )|| (wert > k.ueberweisungsLimit )) return LIMITUEBERSCHRITTEN; else { auszahlen(wert); k.einzahlen(wert); return OK; } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto6 a1 = new Konto6(500, 50); Konto6 a2 = new Konto6(100, 80); int status; status = a2.ueberweisenAuf(a1, 50); if (status != OK) System.out.println("Fehler: " + status); else System.out.println("a1: "+ a1 + "; a2= " +a2); status = a2.ueberweisenAuf(a1, -500); if (status != OK) System.out.println("Fehler: " + status); else System.out.println("a1: "+ a1 + "; a2= " +a2); status = a2.ueberweisenAuf(a1, 100); if (status != OK) System.out.println("Fehler: " + status); else System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto7
In dieser Implementierung wird die Ausnahme IllegalArgumentException aus den Javastandardklassen verwendet.
package s1.block10; public class Konto7 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto7(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto7 k, int wert) { if (wert < 0) throw new IllegalArgumentException("Negativer Wert " + wert); else if (wert > ueberweisungsLimit ) throw new IllegalArgumentException("Limit ueberschritten " + wert + "; Limit: " + ueberweisungsLimit); else if (wert > k.ueberweisungsLimit ) throw new IllegalArgumentException("Limit ueberschritten " + wert + "; Limit: " + k.ueberweisungsLimit); else { auszahlen(wert); k.einzahlen(wert); } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto7 a1 = new Konto7(500, 50); Konto7 a2 = new Konto7(100, 80); int status; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, 100); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto8 und Ueberweisungsausnahme
In dieser Implementierung werden eigens implementierte Ausnahmen (Exceptions) verwendet um eine Ausnahmebehandlung durchzuführen.
package s1.block10; public class Konto8 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto8(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto8 k, int wert) { if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert); else if (wert > ueberweisungsLimit ) throw new UeberweisungsAusnahme("Limit ueberschritten " + wert + "; Limit: " + ueberweisungsLimit); else if (wert > k.ueberweisungsLimit ) throw new UeberweisungsAusnahme("Limit ueberschritten " + wert + "; Limit: " + k.ueberweisungsLimit); else { auszahlen(wert); k.einzahlen(wert); } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto8 a1 = new Konto8(500, 50); Konto8 a2 = new Konto8(100, 80); int status; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, 100); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Die Klasse Ueberweisungsausnahme
Die Klasse wird aus der Klasse IllegalArgumentException abgeleitet. Sie muss nicht behandelt werden da diese Ausnahme eine Spezialisierung der RuntimeException ist. Das Auftreten der Ausnahme im Hauptprogramm kann aber behandelt werden.
package s1.block10; public class UeberweisungsAusnahme extends IllegalArgumentException { public UeberweisungsAusnahme(String text) { super(text); } }
Klasse Konto9
In der Klasse Konto9 wird in der main() Methode eine geworfene Ausnahme behandelt.
package s1.block10; public class Konto9 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto9(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } public void einzahlen(int b) { assert (b>=0); betrag += b; } public void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto9 k, int wert) { if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert); else if (wert > ueberweisungsLimit ) throw new UeberweisungsAusnahme("Limit ueberschritten " + wert + "; Limit: " + ueberweisungsLimit); else if (wert > k.ueberweisungsLimit ) throw new UeberweisungsAusnahme("Limit ueberschritten " + wert + "; Limit: " + k.ueberweisungsLimit); else { auszahlen(wert); k.einzahlen(wert); } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto9 a1 = new Konto9(500, 50); Konto9 a2 = new Konto9(100, 80); int status; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); try { a2.ueberweisenAuf(a1, -500); } catch (UeberweisungsAusnahme ue) { System.out.println("Fehler aufgetreten" + ue.getMessage()); System.out.println ("Operation nicht ausgeführt"); } System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, 100); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto10
In der Klasse Konto10 wird zwischen einer allgemeinen ÜberweisungsAusnahme und einer LimitAusnahme unterschieden.
package s1.block10; public class Konto10 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto10(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } public void einzahlen(int b) { assert (b>=0); betrag += b; } public void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto10 k, int wert) { if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert); else { int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ? ueberweisungsLimit: k.ueberweisungsLimit; if (wert > maxlimit ) throw new LimitAusnahme("Limit ueberschritten " + wert, maxlimit); else { auszahlen(wert); k.einzahlen(wert); } } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto10 a1 = new Konto10(500, 50); Konto10 a2 = new Konto10(50, 80); int status; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); try { a2.ueberweisenAuf(a1, -500); } catch (UeberweisungsAusnahme ue) { System.out.println("Fehler aufgetreten" + ue.getMessage()); System.out.println ("Operation nicht ausgeführt"); } System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, 100); System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse LimitAusnahme
Diese Klasse kann das Limit welches verletzt wurde verwalten. Hierdurch können Nutzer mehr Informationen erhalten und intelligenter reagieren.
package s1.block10; public class LimitAusnahme extends UeberweisungsAusnahme { public int limit; public LimitAusnahme(String text, int ll) { super(text); limit = ll; } }
Klasse Konto11
Die Klasse Konto11 reagiert auf eine Limitverletzung durch mehrere Überweisungen die unter dem Limit bleiben.
package s1.block10; public class Konto11 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto11(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf (Konto11 k, int wert) { if (wert < 0) throw new UeberweisungsAusnahme("Negativer Wert" + wert); else { int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ? ueberweisungsLimit: k.ueberweisungsLimit; if (wert > maxlimit ) throw new LimitAusnahme("Limit ueberschritten " + wert, maxlimit); else { auszahlen(wert); k.einzahlen(wert); } } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse weis. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto11 a1 = new Konto11(500, 50); Konto11 a2 = new Konto11(100, 80); int status; a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); try { a2.ueberweisenAuf(a1, -500); } catch (UeberweisungsAusnahme ue) { System.out.println("Fehler aufgetreten " + ue.getMessage()); System.out.println ("Operation nicht ausgeführt"); } System.out.println("a1: "+ a1 + "; a2= " +a2); int betrag = 100; try { a2.ueberweisenAuf(a1, betrag); } catch (LimitAusnahme ue) { // Splitte Überweisung in mehrere Ueberweisungen unterhalb des Limits int dasLimit = ue.limit; while (betrag > dasLimit) { betrag = betrag - dasLimit; a2.ueberweisenAuf(a1, dasLimit); System.out.println("a1: "+ a1 + "; a2= " +a2); } a2.ueberweisenAuf(a1, betrag); } System.out.println("a1: "+ a1 + "; a2= " +a2); } }
Klasse Konto12
In der Klasse Konto12 wird eine andere Ausnahme bei einem Überweisungsfehler geworfen. Die Ausnahmeklasse UeberweisungsAusnahme12 ist aus der Klasse Exception abgeleitet.
Da die Ausnahme in der Methode ueberweisenAuf() geworfen wird muss
- ... dies im Kopf der Methode dokumentiert werden
- ... bei der Verwendung der Methode ueberweisenAuf() immer ein tra catch Block implementiert werden.
package s1.block10; public class Konto12 { int betrag; private int ueberweisungsLimit; private static final int MINLIMIT =1; public static final int OK = 0; public static final int NEGATIVERWERT = 1; public static final int LIMITUEBERSCHRITTEN = 2; public Konto12(int startBetrag, int ll) { betrag = startBetrag; if (ll>0) ueberweisungsLimit=ll; else ueberweisungsLimit = MINLIMIT; } private void einzahlen(int b) { assert (b>=0); betrag += b; } private void auszahlen(int b) { assert (b>=0); betrag -= b; } public void ueberweisenAuf(Konto12 k, int wert) throws UeberweisungsAusnahme12 { if (wert < 0) throw new UeberweisungsAusnahme12("Negativer Wert" + wert); else { int maxlimit = (ueberweisungsLimit<k.ueberweisungsLimit) ? ueberweisungsLimit: k.ueberweisungsLimit; if (wert > maxlimit ) throw new LimitAusnahme("Limit ueberschritten " + wert, maxlimit); else { auszahlen(wert); k.einzahlen(wert); } } } @Override public String toString() {return betrag + " Euro";} /** * Die main Methode sei eine Methode die von einem Team gepflegt wird * welches nichts von der internen Implementierung der Klasse kennt. * Die Methode wurde nur aus Gründen der Kompaktheit in dieser Klasse * implementiert * @param args */ public static void main(String[] args) { Konto12 a1 = new Konto12(500, 50); Konto12 a2 = new Konto12(100, 80); int status; try { a2.ueberweisenAuf(a1, 50); System.out.println("a1: "+ a1 + "; a2= " +a2); a2.ueberweisenAuf(a1, -500); System.out.println("a1: "+ a1 + "; a2= " +a2); int betrag = 100; try { a2.ueberweisenAuf(a1, betrag); } catch (LimitAusnahme ue) { // Splitte Überweisung in mehrere unter des Limits int dasLimit = ue.limit; while (betrag > dasLimit) { betrag = betrag - dasLimit; a2.ueberweisenAuf(a1, dasLimit); System.out.println("a1: "+ a1 + "; a2= " +a2); } a2.ueberweisenAuf(a1, betrag); } System.out.println("a1: "+ a1 + "; a2= " +a2); } catch (UeberweisungsAusnahme12 ue) { System.out.println("Fehler aufgetreten " + ue.getMessage()); System.out.println ("Eine Überweisung wurde nicht ausgeführt"); } } }
Klasse Ueberweisungsausnahme12
Die Klasse wird aus der Klasse Exception abgeleitet. Sie ist eine "checked Exception" und muss behamdelt oder deklariert werden
package s1.block10; public class UeberweisungsAusnahme12 extends Exception { public UeberweisungsAusnahme12(String text) { super(text); } }
- 3988 views
10.9 Lernziele (Ausnahmen)
10.9 Lernziele (Ausnahmen)
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Ausnahmen (Exception)
Feedback
- 3212 views
11. Weitere Konzepte
11. Weitere Konzepte Stefan Schneider Sat, 11/17/2018 - 12:00- 1399 views
11.1 Schnittstellen (Interfaces)
11.1 Schnittstellen (Interfaces)Javaklassen enthalten Methoden und Datenfelder. Die öffentlich zugänglichen Methoden und Datenfelder bilden die Schnittstellen einer Klasse. Schnittstellen sind wichtige Hilfsmittel bei Entwurf komplexer Softwaresysteme. Sie erlauben es unterschiedliche Subsysteme definiert zu koppeln (Coupling). Die Wechselwirkung zwischen diesen Subsystemen wird auch Coherence oder Cohesion genannt.
Schnittstellen unterstützen das Konzept des Information Hiding und sollten möglichst stabil sein. Stabile Schnittstellen erlauben es mehreren Teams unabhängig von einander zu arbeiten. Sie erlauben eine Trennung von
- Spezifikation und
- Implementierung
Schnittstellen (engl. Interfaces) sind ein wichtiges Sprachmittel beim Entwurf von Softwaresystemen.
Sie enthalten daher nur die für die Spezifikation wichtige Elemente:
- Methodendeklarationen
- öffentliche Attribute
Klassen hingegen enthalten die Spezifikation, alle Attribute sowie zusätzlich die Implementierung der Methoden durch die Methodenrümpfe.
Javaschnittstellen (Interfaces)
Java bietet im Gegensatz zu C++ das Schlüsselwort interface zur Beschreibung von Schnittstellen. Java-Interfaces muss man gedanklich als abstrakte Klassen verstehen, die selbst über keine Implementierung von Methoden verfügen.
Klassen enthalten die Spezifikation und zusätzlich die Implementierung der Methoden durch die Methoden Rümpfe/Blöcke. Schnittstellen in Java enthalten (in der Regel, siehe unten) nur die Spezifikation.
Eigenschaften | Klasse | abstrakte Klasse | Schnittstelle |
---|---|---|---|
Öffentliche Attribute | Ja | Ja | Ja |
Private Attribute | Ja | Ja | Nein |
Methodenköpfe (die Spezifikation) | Ja | Ja | Ja |
Methodenrumpf | Ja | Nein (bei abstr. Meth.) | Nein (in der Regel) |
kann von Klasse erben | Ja | Ja | Nein |
kann von Schnittstelle erben | Nein | Nein | Ja |
kann eine oder mehrere Schnittstelle implementieren | Ja | Ja | Nein |
Für Java-Schnittstellen gilt:
- alle Attribute müssen öffentliche, finale und initialisierte Konstanten sein
- alle Methoden sind abstrakt (abstract), öffentlich (public), nicht final und nicht statisch
- sie geben keine Konstruktoren vor. Die Gründe sind:
- Konstruktoren haben immer den Namen der aktuellen Klasse.
- Ein Konstruktor in einer Implementierung kann nicht den Namen der implementierten Schnittstelle besitzen.
Notation einer Java-Schnittstelle
[public] interface Interface-name [extends Interface1, Interface2, ... ];
- Das Schlüsselwort public erlaubt wie auch bei Klassen das Interface ausserhalb des zugehörigen Pakets zu benutzen
- dem Schlüsselwort interface folgt der Name der Schnittstelle und
- eine optionale Liste von Schnittstellen von denen die Schnittstelle erbt. Diese Liste von Schnittstellen wird mit dem Schlüsselwort extends eingeleitet
Schnittstellen können nur von Schnittstellen abgeleitet werden.
Klassen können nur von Klassen abgeleitet werden. Klassen implementieren jedoch beliebig viele Schnittstellen durch das Schlüsselwort implements.
[public|private|protected] class Klassenname1 extends Klassenname2 [implements Interface1, Interface2, ....]
Die Klasse Klassenname1 wird aus Klassenname2 abgeleitet und implementiert (Schlüsselwort implements) optional eines oder mehrere Interfaces.
Beispiel
Personen (class Person), sowie Lieferanten (class Supplier) implementieren das Interface BankAccount. BankAccount muss mit einer IBAN Nummer und dem Namen einer Bank umgehen können. Das Interface macht keinerlei Vorgaben zum Setzen dieses Daten. Das Erfassen dieser Daten obliegt dem Implementierer der Schnittstelle.
interface BankAccount { public long iban(); public String bank(); } class Person implements BankAccount { private String myIban; private String myBank; public name() { // Implementierung } public long iban() { ... return myIban; } public String bank() { ... return myBank; } } class Supplier implements BankAccount { private String supplierIban; private String supplierBank; public String getLicense() { //Implementierung } public long iban() { ... return supplierIban; } public String bank() { return supplierBank; } } ... BankAccount b1 = new Person(); BankAccount b2 = new Supplier(); ... System.out.println(b1.bank()); System.out.println(b2.bank());
Die Klasse Person implementiert alle Methoden von BankAccount und verhält sich in allen Aspekten wie das Interface.
Für Klassen die Schnittstellen(Interfaces) implementieren gilt:
- sie können beliebig viele Interfaces gleichzeitig implementieren.
- sie implementieren alle Methoden und Attribute des Interface
- implementieren sie nicht alle Aspekte des Interface müssen sie als abstrakte Klasse deklariert werden. Erst abgeleitete Klassen die alle Aspekte der Schnittstelle zur Verfügung stellen müssen nicht mehr abstrakt sein.
Schnittstellen und Vererbung
- Schnittstellen können von Schnittstellen erben (Schlüsselwort extends)
- Dies deutet für den Entwickler, dass er alle Methoden der Vererbungshierarchie implementieren muss. In diesen Aspekt verhalten sich Schnittstellenmethoden wie abstrakte Methoden.
- Klassen können nicht von Schnittstellen erben. Sie implementieren Schnittstellen!
UML Notation
In UML Klassendiagrammen wird die Java Interface-beziehung als Verfeinerung mit gestrichelten Pfeilen gekennzeichnet: | Alternativ kann man die zu implementierende Klasse auch mit einem Strich und einem Kreis darstellen: |
Anwendung von Schnittstellen (Interfaces)
Schnittstellen (Schlüsselwort interface) und Vererbung (Schlüsselwort extends) sind zwei Vererbungskonzepte in Java. Sie haben unterschiedliche Zielsetzungen bezüglich Strukturierung und Planung.
- Schnittstellen (Interfaces)
- fördern modularen Aufbau auf Softwaresystemen
- erlauben unabhängige Implementierung und vereinfachen den Austausch von Schnittstellenimplementierungen
- fordern nur die Implementierung bestimmter Methoden.
- entkoppeln Systeme und fördern die genaue Spezifikation von benötigten Diensten (Verträge!)
- schaffen Strukturen die in späteren Phasen der Softwareentwicklung die Komplexität der Implementierung senken
- fördern modularen Aufbau auf Softwaresystemen
- Vererbung
- erleichert Codewiederverwendung
- führt zu einer engeren Kopplung von Systemen.
- Da immer eine Implementierung der Oberklasse genutzt werden muss. Man muss z.Bsp. den Konstruktor der Oberklasse immer nutzen
- Eine Unterklasse muss immer alle Eigenschaften der Oberklasse aufweisen
Vererbung (extends) | Schnittstelle (interface) | |
---|---|---|
Spezifikation | wird von der Oberklasse vorgegeben | wird vom Interface vorgegeben |
Programmcode | wird von der Oberklasse vorgegeben. wird von einer abstrakten Oberklasse gefordert |
Interface fordert eine Implementierung |
Mehrfachvererbung/Implementierung | nein | ja |
Abhängigkeit der Unterklasse/Interfaceimplementierung | hängt stark von der Oberklasse ab | hängt schwächer vom zu implementierenden Interface ab |
Implikationen
Schnittstellen (Interfaces) müssen Aufgrund ihrer großen Bedeutung für den Entwurf von Softwaresystemen sehr sorgsam entworfen werfen. Änderungen in Schnittstellen führen dazu, dass alle Klassen die eine Schnittstelle (Interface) implementieren, bei einer Änderung vom Laufzeitsystem nicht mehr akzeptiert werden. Bei einer Schnittstellenänderung müssen alle Klassen der neuen Schnittstelle angepasst und neu übersetzt werden.
Schnittstellen versus Vererbung mit Polymorphismus
Schnittstellen sind oft eine bessere Lösung für langlebige Softwaresysteme. Die enge Kopplung an die vererbenden Basisklassen werden sich ändernden Anforderungen auch oft ein Hindernis. Der Vorteil alle ererbten Methoden direkt benutzen zu können, kann hier auch zum Nachteil werden, insbesondere wenn Klassenhierarchien tief und unübersichtlich sind.
Die Konzepte von
- Assoziation und
- Schnittstellen
führen oft zu einem initial höheren Implementierungsaufwand. Sie bieten jedoch eine bessere Entkopplung von Softwarekomponenten.
Weiterführende Quellen: "Why extends is evil"
Genau hingeschaut: Methodenimplementierungen von Schnittstellen
Weiter oben wird behauptet, dass in der Deklaration einer Schnittstelle keine Implementierung von Methoden möglich sind. Diese Aussage ist seit Java 8 so nicht mehr korrekt. Sie gilt aber nach wie vor für sehr, sehr viele Anwendungsfälle.
Die neuen Möglichkeiten von "default" Methoden in Schnittstellen werden im Rahmen dieser Vorlesung nicht beachtet. Man sollte diesen Sonderfall kennen. Für das grundlegende Verständnis des Schnittstellenkonzepts ist dieser "exotische" Anwendungsfall aber nicht notwendig.
Hintergrund
Vor Java 8 waren alle Methoden eines Interfaces abstrakt. Das heißt, es lag nur der Methodenkopf mit der Signatur vor. Seit Java 8 kann man in Interfaces
- default-Methoden implementieren
- statische Methoden implementieren
Die Notwendigkeit dieser Konzepte entstanden durch die Integration von Lamdba-Ausdrücken (JSR 335) . Man benötigt diese Konzepte um die Rückwärtskompatibilität zu älteren Javaimplementierungen zu gewährleisten. Es beruht auf dem Problem der Java Interface-Evolution bei der Benutzung der Collection Klassen in Lamdba-Ausdrücken. Angelika Langer hat dieses Problem sehr gut in einem deutschsprachigen Internetartikel im Java Magazin beschrieben.
- 14442 views
11.1.1 Beispiele Schnittstellenanwendung
11.1.1 Beispiele SchnittstellenanwendungSerialisierung
Serialisierung ist ein Beispiel bei dem man gut sieht wie eine Klasse durch Implementierung einer Schnittstelle wichtige Eigenschaften gewinnen man. Man kann sie in einer Datei speichern oder in einen beliebigen Stream stecken!
Java bietet die Möglichkeit Objekte im Hauptspeicher in einen seriellen Datenstron zu konvertieren den man zum Beispiel in Dateien schreiben kann. Diese Technologie nennt man Serialisierung(engl. Serialization siehe Wikipedia). Java kann Klassen serialisieren die die Schnittstelle Serializable implementieren.
Java kann Objekte serialisieren wenn
- die entsprechende Klasse die Schnittstelle Serializable implementiert und
- wenn alle Attribute die auf Objekte zeigen auch die Schnittstelle Serializable implementieren
Das folgende Beispielprogramm ist in der Lage eine Person und ihre Adresse in eine Datei zu schreiben und wieder zu lesen.
Alles was man tun muss um die Klassen Person und Adresse serialisierbar zu machen ist die Schlüsselworte "implements Serializable" hinzuzufügen
|
Überlegungen:
- Die Schnittstelle Serializable fordert keine Methoden zu implementieren!
- Warum sind 4 Objekte in der Datei?
- Was würde geschehen wenn die die Klasse Person mit Stammbaum aus der ersten Vorlesung serialisieren würde?
- Was würde geschehen wenn man weitere Attribute mit Basistypen zu den Klassen hinzufügt?
- Was geschieht wenn man auf eine Klasse referenziert die nicht serialisierbar ist?
Klasse Serialisierung
package s1.block11; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * * @author s@scalingbits.com */ public class Serialisierung { /** * Erzeuge eine Person und die Adresse. Schreibe sie in eine * Datei und lese sie aus der Datei * @param args */ public static void main(String[] args) { final String meineDatei = "serialisiert.ser"; // Erzeuge das Objekt der Person und das Objekt mit der Adresse Person p = new Person("Urmel", new Adresse("Lummerland")); System.out.println("Zu schreibende Person " + p.name + " wohnt in " + p.wohnort.stadt); // Schreibe die Objekte in eine Datei schreiben(p, meineDatei); Person p1 = (Person) lesen(meineDatei); System.out.println("Gelesene Person " + p1.name + " wohnt in " + p1.wohnort.stadt); System.out.println("Die geschrieben Adresse und die gelesene"+ " Adresse " + p.wohnort.stadt + " sind" + (p.wohnort==p1.wohnort? " " : " nicht ") + "identisch"); } /** * Diese Methode schreibt ein beliebiges serialisierbares Objekt * in eine Datei * @param o Objekt welches in eine Datei geschrieben wird. * Es muss serialisierbar sein! * @param datei Die Datei in die geschrieben werden soll */ public static void schreiben(Object o, String datei) { try { // Erzeuge einen Stream der in eine Datei geleitet wird FileOutputStream fileOut = new FileOutputStream(datei); // Erzeuge einen Stream der Objekte in einen Filestream leitet ObjectOutputStream out = new ObjectOutputStream(fileOut); // Schreibe ein beliebiges Objekt in diesen Objectstream out.writeObject(o); // Schliesse Stream out.close(); // Schliesse Datenstrom. Die letzten Bytes werden so raus geschrieben fileOut.close(); System.out.println("Serialisierte Daten sind gespeichert in Datei " + datei); } catch (IOException i) { // Hier können Ausnahmen auftreten i.printStackTrace(); } } /** * * @param datei die Datei aus der gelesen werden soll * @return */ public static Object lesen(String datei) { System.out.println("Lesen aus " + datei); Object o; try { // Hier können Ausnahmen Auftreten // Öffne Datei aus der gelesen werden soll FileInputStream fileIn = new FileInputStream(datei); // Erzeuge Objectstream der aus Datei liest ObjectInputStream in = new ObjectInputStream(fileIn); //Lies Objekt aus Stream o = in.readObject(); // Schließe Objectstream in.close(); // Schließe Datei fileIn.close(); } catch (IOException i) { // Wird ausgeführt wenn Probleme auftreten i.printStackTrace(); return null; } catch (ClassNotFoundException c) { System.out.println("Gelesene Klasse nicht gefunden"); c.printStackTrace(); return null; } return o; } }
Klasse Adresse
package s1.block11; import java.io.Serializable; /** * * @author s@scalingbits.com */ public class Adresse implements Serializable { String stadt; public Adresse (String s) { stadt =s; } }
Klasse Person
package s1.block11; import java.io.Serializable; /** * * @author s@scalingbits.com */ public class Person implements Serializable { String name; Adresse wohnort; public Person(String n, Adresse w) { wohnort = w; name=n; } }
Weiterführende Übungen und Überlegungen zu diesem Beispiel
- Was geschieht wenn zwei Personen auf das gleiche Adressobjekt zeigen? Wird die Adresse zweimal gespeichert oder nur einmal? Selbst wenn die Adresse nur einmal gespeichert wird. Was geschieht beim Lesen der serialisierten Datei. Wird das Addressobjekt verdoppelt?
- Was geschieht beim Übersetzen und Ausführen wenn man den Term "implements Serializable" in der Klasse Adresse oder Person weglässt?
- Versioning Problem: Was geschieht wenn man die Objekte in eine Datei serialisiert. Dann ein Attribut zu einer Klasse hinzufügt oder weglässt. Dann die serialisierten Objekt wieder einliest?
- Was muss man tun wenn man ein Attribut (zBsp. stadt) nicht speichern möchte? Suchen Sie in der Java-Spezifikation oder mit einer Suchmaschine...
- 4639 views
11.1.2 Übung: Schnittstellen und abstrakte Klassen
11.1.2 Übung: Schnittstellen und abstrakte KlassenÜbung 1: Implementiere die Klasse Dollar aus der abstrakten Klasse Number
Die Java Laufzeitumgebung bietet die abstrakte Klasse Number aus der alle Zahlentypen abgeleitet werden.
Implementieren Sie eine Klasse Dollar die aus der Klasse Number abgeleitetet wird.
- Die Klasse soll aus Sicherheitsgründen mit unveränderbaren Attributen belegt sein. Der Wert des Objekts darf nur im Konstruktur gesetzt werden.
- Im Konstruktor soll der Betrag nach Dollar und Centbeträgen getrennt erfasst werden. Unsinnige Centbeträge sollen auf Null Cent gesetzt werden.
Vorgehen:
- Arbeiten Sie am besten im Paket Kurs2.Schnittstelle. Beim Kopieren sind dann keine Anpassungen notwendig.
- Nutzen Sie die Klasse Kurs2.SchnittstelleTestDollar zum Testen Ihrer Implementierung.
- Legen Sie eine Klasse Kurs2.Schnittstelle.Dollar an, die aus der Klasse Number abgeleitet wird.
- Tipp: Verwaltung des Betrags. Es ist einfacher den gesamten Betrag als Centbetrag in einem long zu speichern
- Geben Sie bei allen Methoden die ganze Zahlen implementieren nur den Dollarbetrag aus
- Geben Sie bei den Fließkommamethoden den Dollar und Centbetrag auf 2 Nachkommastellen aus (Rundungsfehler können ignoriert werden)
Tipp: Vorsicht beim Berechnen des Centbetrag im Konstruktor bei negativen Werten. Es gilt:
- $3.25 = 3 *100 + 25 cents
- $-3.25 = -3 * 100 - 25 cents
- daher gilt die Rechenregel cents = signum(dollar)*( abs(dollar)*100+abs(cents) )
Nutzen Sie hierfür die statischen Importdeklarationen:
import static java.lang.Math.abs;
import static java.lang.Math.signum;
Das Testprogramm TestDollar
package s1.block11;
public class TestDollar {
/**
*
* Das Hauptprogramm benötigt keine Parameter
* Es testet die wichtigsten Eigenschaften der Klasse Dollar
*/
public static void main(String[] args) {
System.out.println("Phase 1: Einfache Tests");
Dollar konto1 = new Dollar(172, 12);
long d1 = konto1.longValue();
System.out.println("Dollar.longValue().Sollwert 172; Istwert: " + d1);
double d2 = konto1.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert 172.12; Istwert: " + d2);
float d3 = konto1.floatValue();
System.out.println("Dollar.floatValue().Sollwert 172.12; Istwert: " + d3);
System.out.println("Phase 2: Härtere Tests");
Dollar konto2 = new Dollar(-380, 25);
d1 = konto2.longValue();
System.out.println("Dollar.longValue().Sollwert -380; Istwert: " + d1);
d2 = konto2.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert -380.25; Istwert: " + d2);
d3 = konto2.floatValue();
System.out.println("Dollar.floatValue().Sollwert -380.25; Istwert: " + d3);
Dollar konto3 = new Dollar (-382,225);
d1 = konto3.longValue();
System.out.println("Dollar.longValue().Sollwert -382; Istwert: " + d1);
d2 = konto3.doubleValue();
System.out.println("Dollar.doubleValue().Sollwert -382; Istwert: " + d2);
d3 = konto3.floatValue();
System.out.println("Dollar.floatValue().Sollwert -382; Istwert: " + d3);
}
}
Die Ausgaben sollten etwa wie folgt aussehen:
Phase 1: Einfache Tests
Dollar.longValue().Sollwert 172; Istwert: 172
Dollar.doubleValue().Sollwert 172.12; Istwert: 172.12
Dollar.floatValue().Sollwert 172.12; Istwert: 172.12
Phase 2: Härtere Tests
Dollar.longValue().Sollwert -380; Istwert: -380
Dollar.doubleValue().Sollwert -380.25; Istwert: -380.25
Dollar.floatValue().Sollwert -380.25; Istwert: -380.25
Dollar.longValue().Sollwert -382; Istwert: -382
Dollar.doubleValue().Sollwert -382; Istwert: -382.0
Dollar.floatValue().Sollwert -382; Istwert: -382.0
Übung 2: Implementieren von Schnittstellen
Implementieren Sie die Klasse Euro. Die Klasse Euro
- wird aus der Klasse Number abgeleitet
- implementiert die Schnittstelle Comparable
- implementiert die SchnittStelle Waehrung
- soll noch die Methode public String toString() überschreiben um einen formatierten Wert ausgeben zu können.
Hinweise zur Implementierung der abstrakten Klasse Number
Kopieren Sie sich alle abstrakten Methoden der Klasse Number in die Klasse Dollar! Somit stellen Sie sicher, dass die Signatur identisch ist und das Überschreiben funktioniert.
Hinweise zur Implementierung der Schnittstelle Waehrung
Geben Sie bei der Methode symbol() ein "€" Zeichen zurück.
Implementierung der Methode mult(double faktor).
Diese Methode dient der Zinsberechnung. Durch die Multiplikation mit dem Wert 1.03 soll man den Wert um 3% Vergrößeren oder Verkleinern können.
- Erzeugen Sie ein neues Objekt vom Typ Euro und geben Sie es direkt zurück. Verändern Sie da aktuelle Objekt nicht.
- Berechnen des Centbetrags: Nehmen Sie den Absolutwert ihres neu berechneten Centbetrags und dann davon den Rest einer Division durch 100 (Modulo). Negative Beträge werden so korrekt berechnet.
Hinweise zu Implementierung der Schnittstelle Comparable()
Diese Schnittstelle erlaubt vielen Hilfsklassen in Java eine Sortierung vorzunehmen. Die Methode gibt abhängig vom Vergleich der Objekte negative, postive oder einen Nullwert zurück. Die Logik des Vergleichs liegt in der Verantwortung des Programmieres.
Tipp: Sie müssen in der Lage sein einen Euro gegen ein beliebiges Objekt zu vergleichen!
- Prüfen Sie den Typ des Objekts vor dem Vergleich (siehe Kapitel Polymorphismus)
- Geben Sie einen konstanten Betrag (-1 oder 1) zurück wenn Sie die Typen nicht vergleichen können
- Falls Sie sicher sind, dass Sie Euro mit Euro vergleichen, können das Signum der Differenz des Wert in Cent benutzen.
Die Schnittstelle Waehrung
Benutzen Sie dies Klasse
package s1.block11;/**
*
* @author s@scalingbits.com
* @ version 1.1
*/
public interface Waehrung {
/**
*
* @return Währungssymbol
*/
public String symbol();
/**
* Multipliziert den Wert des Objekts mit der Fließkommazahl
*
* @param f
* @return neues Objekt welches das Produkt enthält
*/
public Waehrung mult(double f);
}
Ein Testprogramm TestEuro
package s1.block11; import java.util.Arrays; /** * * @author s@scalingbits.com * @ version 1.1 */public class TestEuro {
public static void main(String[] args) {
System.out.println("Phase 1: Einfache Tests");
Euro konto1 = new Euro(172, 12);
long d1 = konto1.longValue();
System.out.println(" Euro.longValue().Sollwert 172; Istwert: " + d1);
double d2 = konto1.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert 172.12; Istwert: " + d2);
float d3 = konto1.floatValue();
System.out.println(" Euro.floatValue().Sollwert 172.12; Istwert: " + d3);
System.out.println(" Euro.symbol().Sollwert €; Istwert: " + konto1.symbol());
System.out.println(" Euro.toString().Sollwert 172.12€; Istwert: " + konto1);
System.out.println("Phase 2: Härtere Tests");
Euro konto2 = new Euro(-380, 25);
d1 = konto2.longValue();
System.out.println(" Euro.longValue().Sollwert -380; Istwert: " + d1);
d2 = konto2.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert -380.25; Istwert: " + d2);
d3 = konto2.floatValue();
System.out.println(" Euro.floatValue().Sollwert -380.25; Istwert: " + d3);
Euro konto3 = new Euro (-382,225);
d1 = konto3.longValue();
System.out.println(" Euro.longValue().Sollwert -382; Istwert: " + d1);
d2 = konto3.doubleValue();
System.out.println(" Euro.doubleValue().Sollwert -382; Istwert: " + d2);
d3 = konto3.floatValue();
System.out.println(" Euro.floatValue().Sollwert -382; Istwert: " + d3);
System.out.println("Phase 3: Multiplikation testen");
Waehrung konto10 = new Euro(1,23);
double m1 = 2;
Waehrung konto11 = konto10.mult(m1);
System.out.println(" Euro.mult(): "+konto10 +" * "+ m1 + " = " + konto11);
Waehrung konto20 = new Euro(1,98);
double m2 = 2;
Waehrung konto21 = konto20.mult(m2);
System.out.println(" Euro.mult(): "+konto20 +" * "+ m2 + " = " + konto21);
Waehrung konto30 = new Euro(-1,98);
double m3 = 2;
Waehrung konto31 = konto30.mult(m3);
System.out.println(" Euro.mult(): "+konto30 +" * "+ m3 + " = " + konto31);
Waehrung konto40 = new Euro(-1,98);
double m4 = -2;
Waehrung konto41 = konto40.mult(m4);
System.out.println(" Euro.mult(): "+konto40 +" * "+ m4 + " = " + konto41);
Waehrung konto50 = new Euro(10,10);
double m5 = -2.01;
Waehrung konto51 = konto50.mult(m5);
System.out.println(" Euro.mult(): "+konto50 +" * "+ m5 + " = " + konto51);
Waehrung konto60 = new Euro(10,1);
double m6 = -2.001;
Waehrung konto61 = konto60.mult(m6);
System.out.println(" Euro.mult(): "+konto60 +" * "+ m6 + " = " + konto61);
System.out.println("Phase 4: Einfache Tests für Comparable");
if (konto1.compareTo(konto2) >0)
{System.out.println(" "+konto1 + " ist groeßer als " + konto2);}
else
{System.out.println(" "+konto1 + " ist nicht groeßer als " + konto2);}
if (konto3.compareTo(konto2) >0)
{System.out.println(" "+konto3 + " ist groeßer als " + konto2);}
else
{System.out.println(" "+konto3 + " ist nicht groeßer als " + konto2);}
sortierTest();
}
public static void sortierTest() {
System.out.println("Phase 4: Testen der Schnittstelle Comparable");
Euro[] bank = {
new Euro(99,99),
new Euro(66,66),
new Euro(33,33),
new Euro(11,11),
new Euro(22,22),
new Euro(88,88),
new Euro(55,55),
new Euro(22,22),
new Euro(44,44),
new Euro(0,0)};
System.out.println(" Unsortiertes Feld:");
for (Euro konto : bank) System.out.println(" "+konto);
// Diese Methode sortiert ein Feld in seiner natürlichen Ordnung
// Die ntürliche Ordnung wird durch die Schnittstelle Comparable
// festgelegt
Arrays.sort(bank);
System.out.println(" Sortiertes Feld:");
for (Euro konto : bank) System.out.println(" "+konto);
}
}
- 11743 views
11.1.3 Lösung: Schnittstellen und abstrakte Klassen
11.1.3 Lösung: Schnittstellen und abstrakte KlassenLösung Übung 1: Klasse Dollar
package s1.block11; import static java.lang.Math.signum; /**
*
* @author s@scalingbits.com
* @version 1.2
*/
public class Dollar extends Number{
public final long cents;
public Dollar(int dollars, int cents) {
// Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist
if ((cents<0) || (99<cents)) cents=0;
if (dollars == 0)
this.cents = cents;
else
// Signum ist notwendig da
// -2.20= -2 - 0.2 sind. Falsch: -2 + 0.2 ergibt -1.8!
this.cents = dollars*100+cents*(long)signum(dollars);
}
@Override
public int intValue() {
return (int)cents/100;
}
@Override
public long longValue() {
return cents/100;
}
@Override
public float floatValue() {
return cents/100f;
}
@Override
public double doubleValue() {
return cents/100d;
}
}
Lösung Übung 2: Klasse Euro
package s1.block11;
import static java.lang.Math.abs;
import static java.lang.Math.signum;
/**
*
* @author s@scalingbits.com
* @version 1.2
*/
public class Euro extends Number implements Waehrung, Comparable{
/**
* Der gesamte Betrag wird intern in Cents verwaltet
*/
public final long cents;public Euro(long euros, long cents) {
// Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist
if ((cents<0) || (99<cents)) cents=0;
if (euros == 0)
this.cents = cents;
else
// Signum ist notwendig da
// -2.20= -2 - 0.2 sind. Falsch: -2 + 0.2 ergibt -1.8!
this.cents = euros*100+cents*(long)signum(euros);
}
@Override
public int intValue() {
return (int)cents/100;
}
@Override
public long longValue() {
return cents/100;
}
@Override
public float floatValue() {
return cents/100f;
}
@Override
public double doubleValue() {
return cents/100d;
}
@Override
public String symbol() {
return "€";
}
@Override
public String toString() {
// Füge eine Null bei Centbeträgen zwischen 0 und 9 eine
String leerstelle = ((abs(cents)%100)<10) ? "0" : "";
return Long.toString(cents/100L) + "." + leerstelle +
Long.toString(abs(cents%100L)) + symbol();
}
@Override
public Waehrung mult(double d) {
long temp;
temp = (long)(cents *d);
return new Euro(temp/100L,abs(temp%100L));
}
@Override
public int compareTo(Object o) {
int result;
if (o instanceof Euro) {
Euro e = (Euro) o;
result = (int)(this.cents-e.cents);
}
else {result = -1;} // Alles was kein Euro ist, ist kleiner
return result;
}
}
- 7247 views
11.1.4 Lernziele (Schnittstellen)
11.1.4 Lernziele (Schnittstellen)
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Java Schnittstellen (Interfaces)
- 3538 views
11.2 Assertions
11.2 Assertions
Assertions (engl. Versicherung, Zusage) erlauben den Entwicklern für Testzwecke bestimmte Randbedingungen zu prüfen die immer erfüllt sein sollen.
Assertions geben dem Entwickler die Möglichkeit logische Bedingungen für die folgenden Fälle zu Programmieren:
- Interne Invarianten
- Invarianten im Kontrollfluß
- Vorbedingungen, Nachbedingungen, Klasseninvarianten
Durch die Implementierung dieser Invarianten kann der Entwickler die Qualität seiner Implementierung erhöhen, da Bedingungen geprüft werden können die nie verletzt sein sollen.
Der Unterschied zu Ausnahmen (Exceptions) besteht darin, dass Assertions immer gelten sollten und diese daher nicht im Normalfall kontrolliert werden müssen, da sie den Programmablauf nur unnötig verlangsamen würden. Assertions haben eine große Ähnlichkeit mit Ausnahmen sie dienen jedoch unterschiedlichen Zwecken:
Assertion | Ausnahme (Exception) | |
---|---|---|
Einsatzbereich | Nur wenn die Logik des Programm in den Augen des Entwicklers verletzt wird | Jederzeit da sie Teil der regulären Ablaufsteuerung sind |
Auswirkungen bei der normalen Programmausführung | Keine. Sie werden nicht geprüft! | Können immer Auftreten. |
Sichtbarkeit für Endanwender | Nie: Wenn das Programm nicht explizit mit entsprechenden Optionen (-ea) gestartet wurde | Nur wenn sie nicht abgefangen und behandelt werden |
Zielgruppe | Helfen dem Entwickler bei der Fehlersuche (auch beim Endanwender) |
Zusammenarbeit der Entwickler: Teil der regulären externen Spezifikation von Klassen Endanwender: Klassifikationschema für Fehlermeldungen bei Programmabbrüchen (Unbehandelte Ausnahmen) |
Theoretischer Hintergrund | Erlauben das Implementieren von Invarianten oder Vor- und Nachbedingungen von Schleifen und Routinen |
Elegantes Konstrukt zum Verlassen von Blöcken zur Behandlung von seltenen Ereignissen. Implementierter Code wird übersichtlicher da man nicht bei jeder Operation einzeln Sonderfäller prüfen muss |
Assertions bieten die folgenden Vorteile für den Entwickler:
- Der Entwickler kann Annahmen von denen er ausgeht als logische Ausdrücke implementieren und ist nicht auf Kommentare angewiesen.
- Die Konsistenzprüfungen werden im Normalfall nicht abgearbeitet und produzieren daher keinerlei Laufzeitkosten für den Anwender
- Sie geben dem Entwickler die Möglichkeit auch nach der Auslieferung seiner Anwendung zusätzliche Informationen durch Einschalten des Assertionchecking zu erhalten.
- Hintergrund: Bei C und C++ Anwendungen werden bei Kundenproblemen oft spezielle Programme mit extra Debuginformationen ausgeliefert. Dies ist bei Java nicht nötig
Notation
Einfache Variante einer Assertion:
assert Ausdruck1;
Der Ausdruck Audruck1 wird ausgewertet. Hat das Ergebnis den Wert true (wahr) so ist die Zusage wahr und das Programm weiter ausführt. Trifft die Zusage (Assertion) nicht zu wird das Programm mit einem AssertionError abgebrochen (falls es den entspechenden Optionen zum Checken der Assertions aufgerufen wurde).
Eine zweite Syntaxvariante ist:
assert Ausdruck1: Ausdruck2;
Hier fährt das Programm wie im Fall zuvor mit der Ausführung fort wenn Ausdruck1 wahr ist. Ist Ausdruck1 jedoch unwahr wird Ausdruck2 ausgewertet und der entsprechenden Instanz von AssertionError als Parameter mitgegeben und dann in der Fehlermeldung mit ausgegeben. Dieser Rückgabewert unterstützt den Entwickler bei der Analyse des aufgetretenen Fehlerfalls.
Einschalten der Prüfungen im Laufzeitsystem
Das Prüfen von Assertions kann beim Starten einer Javaanwendung mit den Optionen -ea bzw. -enableassertions eingeschaltet werden oder mit der Option -da bzw. -disableassertions ausgeschaltet werden. Die Option wird vor dem Klassennamen dessen main() Methode gestartet werden soll angegeben:
java -ea Klassenname1 java -ea paketname1... Klassenname1 java -ea paketname1.Klassename2 Klassenname1 java -da Klassenname1 java -da paketname1... Klassenname1 java -da paketname1.Klassename2 Klassenname1java
Wichtig: Die Notation mit den drei Punkten paketname1... ist teil der Aufrufsyntax. Mit ihr werden die Assertions für alle Klassen in einem Paket angeschaltet.
Anwendungsbeispiele
Überprüfen korrekter Wertebereiche
Prüfen des Personenalters bei Rentenberechnungen
assert ((personenAlter>0) && (personenAlter<150)); assert (rentenEintrittsAlter>0);
Die gleichen Assertions in der Variante mit einer Ausgabe für die Konsole
assert ((personenAlter>0) && (personenAlter<150)): personenAlter; assert (rentenEintrittsAlter>0): "negatives Renteneintrittsalter "+ rentenEintrittsAlter;
Das Kontrollieren des Werts eines Monats:
int monat; ... switch (monat) { case 1: case 2: case 3: System.out.println("Q1"); break; case 4: case 5: case 6: System.out.println("Q2"); break; case 7: case 8: case 9: System.out.println("Q3"); break; case 10: case 11: case 12: System.out.println("Q4"); break; default: assert false; }
Prüfen eines Kontrollflusses
In einen Programm soll eine der Bedingungen Ausdruck1 oder Ausdruck2 immer erfüllt sein.
void testMethode() { for (int k = 0; k<= 99; k++) { if (k == 50) return; } assert false; }
Die Assertion kann Prüfen ob ein Fehlerfall vorliegt.
Geschichtlicher Hintergrund
Assertions wurden in Java durch JSR 42 (A Simple Assertion Facility) in JDK 1.4 eingeführt. Dies führt zu einem gewissen Kompatiblitätsproblem:
- Quellcode der das Schlüsselwort assert als normalen Bezeichner in JDK 1.3 verwendete wird in JDK 1.4 nicht übersetzen da das Schlüsselwort nicht als Namen akzeptiert wird
- Quellcode der für JDK 1.4 geschrieben wurde wird nicht unter JDK 1.3 übersetzen, da javac in JDK 1.3 nicht der Syntax von assertions umgehen kann.
- 6315 views
11.2.1 Übungen (Assertions)
11.2.1 Übungen (Assertions)Übung 1: Einfügen von Assertions
Nutzen Sie das Beispiel aus dem Abschnitt zu Schnittstellen.
Modifizieren Sie die Klasse Euro so, daß
- Beim Setzen des Centbetrags eine Assertion geworfen wird falls der Centbetrag nicht im korrekten Wertebereich ist. Nutzen Sie die erweiterte Syntax um eine vernünftige Fehlermeldung auszugeben.
- Melden Sie beim Multiplizieren eines Eurobetrags einen Faktor von Null (0) mit Hilfe einer Assertion. Nutzen Sie die erweiterte Syntax um eine Fehlermeldung auszugeben.
Starten Sie das Programm mit der Klasse TestEuro: Es sollte wie zuvor funktionieren
Starten Sie die Klasse TestEuro so, daß Assertions beachtet werden.
- Welche Option muß man beim Programmstart einfügen?
- Modifizieren Sie das Testprogramm so, dass die Assertion für die Multiplikation ausgelöst wird. Der Fall einer Multiplikation mit Null wird im aktuellen Testprogramm nicht getestet.
- 3089 views
11.2.2 Lösungen (Assertions)
11.2.2 Lösungen (Assertions)Übung 1: Einfügen von Assertions
Klasse Euro
package s1.block11; import static java.lang.Math.abs; import static java.lang.Math.signum; /** * * @author s@scalingbits.com * @version 1.1 */ public class Euro extends Number implements Waehrung, Comparable{ /** * Der gesamte Betrag wird intern in Cents verwaltet */ public final long cents; public Euro(long euros, long cents) { assert ((cents>=0) && (cents < 101)): "Cents Bereichsverletzung"; // Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist if ((cents<0) || (cents>=100)) cents=0; this.cents = (abs(euros)*100+cents) *(long)signum(euros); } @Override public int intValue() { return (int)cents/100; } @Override public long longValue() { return cents/100; } @Override public float floatValue() { // Signum und Absolutwert sind notwendig // da -2.20= -(2 + 0.2) sind. Falsch: -2 + 0.2 ergibt -1.8! return (float)cents/100f; } @Override public double doubleValue() { // Signum und Absolutwert sind notwendig // da -2.20= -(2 + 0.2) sind. Falsch: -2 + 0.2 ergibt -1.8! return (double)cents/100d; } @Override public String symbol() { return "€"; } @Override public String toString() { // Füge eine Null bei Centbeträgen zwischen 0 und 9 eine String leerstelle = ((abs(cents)%100)<10) ? "0" : ""; return Long.toString(cents/100L) + "." + leerstelle + Long.toString(abs(cents%100L)) + symbol(); } @Override public Waehrung mult(double d) { assert (d!=0): "Multplikation mit " + d + "nicht erlaubt"; long temp; temp = (long)((double)cents *d); return new Euro(temp/100L,abs(temp%100L)); } @Override public int compareTo(Object o) { int result; if (o instanceof Euro) { Euro e = (Euro) o; result = (int)(this.cents-e.cents); } else {result = -1;} // Alles was kein Euro ist, ist kleiner return result; } }
- 3021 views
11.3.3 Lernziele (Assertions)
11.3.3 Lernziele (Assertions)
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Annahmen (Assertions))
- 3159 views
11.3 Dokumentieren von Javaprogrammen (javadoc)
11.3 Dokumentieren von Javaprogrammen (javadoc)Die Dokumentation von Methoden, Variablen und Klassen ist Teil der Sprache und wird von Standardwerkzeugen des JDK unterstützt.
Die genauen Spezifikationen zum Dokumentieren von Javaklassen sind im Oracle Dokument "Requirements for Writing Java API Specifications" beschrieben. Nach diesen Richtlinien können auch eigene Javaklassen dokumentiert werden. Oracle bietet hierzu ein recht gutes Tutorial an.
Konzept
- Dokumentation zu Variablen und Methoden werden in den Javaquelldateien als Javakommentar in einem besonderen Format beschrieben
- Das Hilfsprogramm javadoc liest Javaquelldateien und erzeugt html Seiten mit der passenden Dokumentation einer Klasse.
Das Format von Kommentaren für Dokumentation
Das Format für die Dokumentation ist ein Sonderfall des mehrzeiligen Javakommentars. Es sieht wie folgt aus:
/** * Hier steht Dokumentationskommentar * Hier steht eine weitere Zeile mit Dokumentationskommentar */
Javakommentare können mit html formatiert werden.
Zusammenfassung für eine Klasse
Beginnen Sie die Dokumentation einer Klasse mit einer Zusammenfassung ihrer Funktion.
Für die Klasse Ware.java kann das wie folgt aussehen:
/** * Ware dient zum Verwalten von Guetern mit Preisen und Namen in einem Lager. * @author Stefan Schneider * @version 1.1 * @see Lager */ public class Ware { ... }
Die Klassendokumentation erlaubt die Verwendung von Kennzeichnungen (englisch "Tags") mit denen man weitere Informationen beisteuern kann. Man kann für Klassen die folgenden Kennzeichnungen "Tags" verwenden:
Dokumentation von Klassenvariablen
Zur Dokumentation von Attributen wird die Dokumentation der Deklaration vorangestellt. Bei der Klasse Ware kann die zum Beispiel wie folgt geschehen:
public class Ware { ... /** * Der aktuelle Mehrwertsteuersatz 2010. * Er liegt zur Zeit bei {@value} . * * @since 1.0 */ public static final double mws = 0.19; ... }
Bei Klassenvariablen können die folgenden Kennzeichnungen "Tags" verwendet werden:
Dokumentation von Konstruktoren und Methoden
Die Dokumentation von Konstruktoren und Methoden wird ebenfalls direkt der Implementierung der entsprechenden Methode als Javakommentar vorangestellt. Hiermit kann man neben der Bedeutung der Methode auch die Eingabe- und Ausgabeparameter dokumentieren. Siehe folgendes Beispiel:
/** * Liefert den Namen einer Ware zurueck. * @return Name der Ware */ public String get_name(){return name;} /** * Setzen eines neuen Nettopreis * @param npr der neue Nettopreis */ public void set_nettoPreis(int npr) { ... }
Bei Methoden und Konstruktoren sind die folgenden Tags möglich:
@see
@since
@deprecated
@param
@return
@throws
und@exception
@serialData
{@link}
{@linkplain}
{@inheritDoc}
{@docRoot}
javadoc: Der Java API Generator
Das JDK Programm javadoc (Oracle Dokumentation) erzeugt aus Javaquelldateien Java API Beschreibungen im html Format.
In seiner einfachsten Form kann man eine Java API Dokumentation mit dem folgenden Kommando erzeugen:
$ javadoc JavaQuelldatei.java ... JavaQuelldatei1.java
Das Kommando javadoc hat zahlreiche Optionen (siehe Oracle Dokumentation) die direkt nach dem Kommando eingefügt werden können. Die wichtigsten sind:
- -author Generierung der Dokumentation unter Berücksichtigung des @author tag
- -d Verzeichnis Generiert die Dokumentation in dem angegeben Verzeichnis
- -help zeigt die online Hilfe
- -private generiert Dokumentation auch für private Attribute
- -sourcepath sourcepathlist Liste der Verzeichnisse in denen nach Quelldateien gesucht wird
- -version Generierung der Dokumentation unter Berücksichtigung des @version tag
Beispiel
Für eine Klasse Ware.java mit allen Komponenten:
/** * Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern * mit Preisen und Namen in einem Lager. * @author Stefan Schneider * @version 1.1 * @see Lager */ public class Ware { /** * Der aktuelle Mehrwertsteuersatz 2010. * Er liegt zur Zeit bei {@value}. * * @since 1.0 */ public static final double mws = 0.19; private double nettoPreis; //Deklaration public boolean halbeMws; private String name; /** * Konstruktor fuer die Klasse Ware * @param n der Name der Ware * @param np der Nettopreis * @param hmws halber Mehrwertsteuersatz für Ware gueltig */ public Ware(String n, double np, boolean hmws) { name = n; nettoPreis = np; halbeMws = hmws; } /** * Liefert den Namen einer Ware zurueck. * @return Name der Ware */ public String get_name() { return name; } /** * Setzen eines neuen Nettopreis * @param npr der neue Nettopreis * @see "Der kleine Kaufmann. BWL für Einzelhändler" */ public void set_nettoPreis(double npr) { nettoPreis = npr; } /** * Ausdrucken aller Werte auf der Konsole */ public void drucken() { System.out.println("Name: " + name); System.out.println("netto: " + nettoPreis); System.out.println("Brutto: " + bruttoPreis()); System.out.println("Halbe Mws:" + halbeMws); } /** * Ausgabe des Nettopreis * @return der Nettopreis */ public double nettoPreis() { return nettoPreis; } /** * Ausgabe des Bruttopreis * @return der Bruttopreis */ public double bruttoPreis() { double bp; //temporaere Variable; keine Klassenvariable if (halbeMws) { bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100; } else { bp = Math.round(nettoPreis * (mws + 1) * 100) / 100; } return bp; } }
Die Dokumentation kann mit dem Kommando javadoc generiert werden. Für das oben gezeigte Beispiel werden zwei Optionen zur Generierung des Autors und der Version benötigt. Die Optionen erlauben die Informationen über Autoren und Versionen auf Wunsch wegzulassen:
$ javadoc -author -version Ware.java
Das Kommando erzeugt eine Reihe von html Dateien im gleichen Verzeichnis. Die generierte Datei index.html sieht wie folgt aus (Screen shot):
- 57883 views
11.3.1 Lernziele
11.3.1 Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu javadoc zu beantworten:
- Dokumentationskommentare und Datenkapselung (enthält auch Teilfragen anderer Bereiche)
- 13180 views
11.4 Packen mit jar
11.4 Packen mit jarEinführung jar: Java Archive Tool
Javaprogramme können aus sehr vielen Klassen und externen Bibliotheken bestehen. "jar" Dateien erlauben das Bündeln von Klassendateien und anderer Dateien um Installation und die Verteilung von Javaprogrammen zu vereinfachen. Mit jar Dateien werden die folgenden Ziele verfolgt:
- Bessere Wartbarkeit: Bündelung von zusammengehörigen Klassen und Dateien um Fehler bei der Anwendung zu vermeiden
- Schnellerer download von Applet der Java Web Start Anwendungen durch Kompression und Begrenzung des Downloads auf eine einzelne Datei
- Gewährleistung der Integrität und Authentizität von Anwendungen: jar Dateien können signiert werden
- Vereinfachte Softwaredistribution und Installation durch Beschränkung auf eine einzige Datei
jar Dateien werden mit dem gleichnamigen Kommando jar des JDK manipuliert. jar steht für Java ARchive.
Das jar Format ist plattformunabhängig und ist daher auf allen Javaplattformen verfügbar.
Hinweis: Das jar Format basiert auf dem zip Dateiformat. Das heißt, dass man auch mit zip,unzip Inhalte von jar Dateien inspizieren kann! Zum Erzeugen von jar Dateien ist zip jedoch nicht geeignet da jar zusätzliche Informationen wie Manifestinformationen etc. erzeugt.
Operation | Befehl |
---|---|
Erzeugen einer jar Datei | jar cf jar-Datei liste-Dateien |
Auflisten des Inhalts einer jar Datei | jar tvf jar-Datei |
Auslesen einer jar Datei | jar xf jar-Datei |
Auslesen bestimmter Dateien einer jar Datei | jar xf jar-Datei auszupackende-Datei |
Starten einer Javaanwendung mit Hilfe einer jar Datei | java -jar anwendung.jar |
Erzeugen einer jar Datei
Das allgemeine Format zum Erzeugen einer Datei mit einem jar-Archiv ist:
jar cf jar-Datei Eingabedatei(en)
Die hier verwendeten Optionen und Argumente bedeuten
- c Option: Erzeugen (c = create) einer Datei
- f Option: Erzeugen einer Datei (anstatt Ausgabe des Ergebnis auf der Standardausgabe bzw. Konsole)
- jar-Datei: Name der zu erzeugenden Datei. Die Extension *.jar ist nicht zwingend, sie ist jedoch üblich.
- Eingabedatei(en): Eine oder mehrere Dateien die in das Archiv aufgenommen werden sollen.
- Bei mehr als einer Datei werden die Dateien durch Leerstellen getrennt
- Werden Verzeichnisse angegeben so wird der vollständige Inhalt der Verzeichnisse rekursiv in das Archiv aufgenommen
- Es können mit dem "Wildcard"-Operator mehrere Dateien aufgenommen werden. Beispiel *.class um alle Javabytecodedateien eines Verzeichnisses aufzunehmen.
Beim Erzeugen einer jar Datei legt das Kommando jar immer eine Manifest Datei MANIFEST.MF im folgenden Verzeichnis des Archivs an:
META-INF/MANIFEST.MF
Der Inhalt dieser Datei ist im einfachsten Fall:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.)
Im Manifest des Archivs werden Metadaten wie zum Beispiel Signaturen, das gewählte Hauptprogramm zum Starten, oder Urheberansprüche verwaltet.
Weitere Optionen:
- v (verbose) detaillierte Informationen während der Ausführung des Kommandos
- 0 keine Kompression verwenden
- M kein Standardmanifest generieren
- m Einfügen einer eigenen Manifestdatei
Beispiel
Gegeben sei eine Anwendung bei der drei Klassen Database.class, Main.class und GUI.class und alle Bilddateien im Verzeichnis bilder in das jar Archiv gepackt werden sollen:
Dies geschieht mit dem Befehl:
$ jar cvf appl.jar *.class bilder Manifest wurde hinzugefügt. Hinzufügen von: Database.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: GUI.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: Main.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/ (ein = 0) (aus = 0) (gespeichert 0 %) Hinzufügen von: bilder/Bild1.jpg (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/Bild2.jpg (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/Bild3.jpg (ein = 10240) (aus = 27) (komprimiert 99 %)
Inspektion eines jar Archivs
jar Archive können mit der der jar Option t inspiziert werden:
jar tvf jar-Datei
Die hier verwendeten Optionen und Argumente bedeuten:
- t Option: Ausgabe des Inhaltsverzeichnis ( t = table of contents)
- f Option: Das zu inspizierende Archiv ist eine Datei
- jar-Datei: Die zu inspizierende Datei falls die Option t gewählt wurde
- v Option: (verbose) zusätzliche Informationen wie Dateigrößen und Änderungsdatum der Archivdateien
Beispiel
Für das oben angeführte Beispiel ergibt sich der folgende Befehl
$ jar tf appl.jar META-INF/ META-INF/MANIFEST.MF Database.class\r\nGUI.class nMain.class bilder/ bilder/Bild1.jpg bilder/Bild2.jpg bilder/Bild3.jpg
Einen detaillierten Überblick kann man mit der v Option (v: verbose, engl. "ausführlich") gewinnen
$ jar tvf appl.jar 0 Sun Dec 12 16:27:56 CET 2010 META-INF/ 60 Sun Dec 12 16:27:56 CET 2010 META-INF/MANIFEST.MF 10240 Sun Dec 12 16:26:10 CET 2010 Database.class 10240 Sun Dec 12 16:26:00 CET 2010 GUI.class 10240 Sun Dec 12 16:25:50 CET 2010 Main.class 0 Sun Dec 12 16:27:12 CET 2010 bilder/ 10240 Sun Dec 12 16:27:02 CET 2010 bilder/Bild1.jpg 10240 Sun Dec 12 16:27:04 CET 2010 bilder/Bild2.jpg 10240 Sun Dec 12 16:27:12 CET 2010 bilder/Bild3.jpg
Extrahieren eines jar Archivs
jar Archive werden mit dem folgenden Befehl ausgepackt (ausgelesen);
$ jar xvf jar-Datei [archivierte-Datei(en)]
Die hier verwendeten Optionen und Argumente bedeuten:
- x Option: Extrahiere Dateien aus einem jar Archiv
- f Option: Extrahiere Dateien aus einer Datei (und nicht von der Standardeingabe)
- jar-Datei: die Datei mit dem zu extrahierenden jar Archiv
- archivierte-Datei(en): eine optionale, mit Leerzeichen separierte Liste von Dateien die extrahiert werden sollen. jar wird alle Dateien des Archivs extrahieren falls diese Liste nicht angegeben wird.
Der jar Befehl wird beim Extrahieren
- existierende Dateien überschreiben
- bei Bedarf neue Unterverzeichnisse anlegen
- die ursprüngliche Archivdatei nicht verändern.
Beispiel
Auspacken des jar Archivs mit ausführlicher Protokollierung:
$ jar xvf appl.jar erstellt: META-INF/ dekomprimiert: META-INF/MANIFEST.MF dekomprimiert: Database.class dekomprimiert: GUI.class dekomprimiert: Main.class erstellt: bilder/ dekomprimiert: bilder/Bild1.jpg dekomprimiert: bilder/Bild2.jpg dekomprimiert: bilder/Bild3.jpg
Hinzufügen von Dateien zu jar Archiven
Die Option u (update) erlaubt das Hinzufügen von Dateien zu Archiven mit der folgenden Syntax
$ jar uf jar-Archiv Datei(en)
Benutzen von jar Archiven beim Ausführen von Programmen
Das Javalaufzeitsystem sucht beim Aufruf mit Hilfe des "Classpath" (Pfad zu den Klassen) nach ausführbaren Dateien mit der Endung .class .
Wird kein expliziter "Classpath" angegeben, so wird im aktuellen Verzeichnis und den darunterliegenden Verzeichnissen gesucht. Unterverzeichnisse können Pakete mit deren Klassen enthalten.
Mit Hilfe der Option -cp oder -classpath kann man die Suche nach Klassendateien steuern. Man kann hier eine Liste der folgenden Dinge angeben:
- Verzeichnisse
- jar Dateien
- zip Dateien
Die Elementeliste der jar Archive und Suchverzeichnisse mit mit dem Zeichen ":" getrennt. Hiermit kann man ein Javaprogramm mit einem jar Archiv starten:
$ java -cp appl.jar Main
Starten von Programmen aus jar Archiven
jar Archive können benutzt werden um direkt Programme aus ihnen heraus anzustarten. Dies geschieht mit der Option -jar im Kommando java:
$ java -jar jar-Datei
Hierzu muss in der Manifestdatei des Archivs ein einzeiliger Eintrag mit der Klasse stehen deren Methode main() aufgerufen werden soll. Dieser Eintrag muss im folgenden Format geschehen:
Main-Class: klassenname
Der obige Eintrag muß mit einem "Carriage return" (Zeilenumbruch) abgeschlossen werden, da er sonst nicht korrekt ausgelesen wird (Siehe Oracle Tutorial).
Zum Erzeugen des Manifesteintrags gibt es eine Reihe von Möglichkeiten
Option m (Manifest): Übergabe einer Manifestdatei mit Startklasse
Eine Manifestdatei mit den gewünschten Einträgen wird selbst erstellt und dann beim Erzeugen des Archivs mit Hilfe der m-Option mit angegeben.
Beispiel:
$ jar cfm appl.jar Manifest.txt *.class bilder
Option e (Entrypoint): Angabe der zu startenden Klasse
Die Klasse mit der main() Methode wird direkt angegeben.
Beispiel:
$ jar cfe appl.jar Main *.class bilder
Referenzen
- Oracle Tutorial: "Packaging Programs in jar Files" (englisch)
- Oracle Tutorial: "Signing JAR Files" (englisch)
- jar Referenzdokumentation für Windows
- 9882 views
11.4.1 Lernziele
11.4.1 Lernziele
Die klausurrelevanten Abschnitte dieses Kapitels sind das Packen mit dem Java-archive Werkzeug jar und der Befehl javadoc Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die Fragen zu jar zu beantworten:
- 3317 views
12. Begleitende Themen
12. Begleitende Themen
- 5204 views
12.1 Java API
12.1 Java APIDas Java API ist eine plattformunabhängige Bibliothek die zum Javalaufzeitsystem gehört. Das Java API ist ein wichtiger Grund für die Popularität von Java. Man findet hier tausende von Klassen die bekannte Probleme korrekt und effizient lösen. Hieraus ergeben sich eine Reihe von Vorteilen
- die Produktivität steigt
- der selbstimplementierte Code wird geringer
- die Qualität steigt
- Testaufwände sinken
- der Transport des Codes vereinfacht sich
- die Klassen im Java API sind hocheffizient implementiert und untertützen oft zusätzliche Konzepte wie
- Internationalisierung
- Multithreading (Nebenläufigkeit)
- Die Verwendung dieser Klassen garantiert auch, dass die Anwendungen auf den unterschiedlichsten Plattformen laufen.
Der beste Zugang zum Java API findet über die online Dokumentation von JDK Standard Edition 7 statt.
Vorsicht: Will man Anwendungen schreiben, die auch mit älteren JDKs funktionieren sollen so sollte man sich an die Schnittstellen der Klassen der älteren JDKs halten.
Das API wächst von Version zu Version und enthält eventuell Klassen und Methoden die in einer älteren Version von Java nicht enthalten sind.
Im Folgenden werden einige ausgewählte Klassen und Methoden des Java API vorgestellt. Die Liste ist nicht vollständig, sie enthält nützliche Klassen die im Rahmen dieses Kurses verwendet werden:
- Error und Exception (Stoff der weiterführenden Vorlesung)
- Generics (Stoff der weiterführenden Vorlesung)
- Collections (Stoff der weiterführenden Vorlesung)
- 6936 views
12.1.1 Konsolen Ein- und Ausgabe
12.1.1 Konsolen Ein- und AusgabeKonsolenausgabe mit System.out
Die Klasse System gehört zum Paket java.lang und muss nicht mit einem Importbefehl deklariert werden. Man kann sie direkt benutzen. das Attribut System.out ist eine Instanz der Klasse PrintStream. Es erlaubt Ausgaben auf der Konsole in den folgenden Varianten:
- System.out.println(a): a kann ein beliebiger Typ sein. Am Ende der Zeile wird ein Zeilenumbruch eingefügt. Die nächste Konsolenausgabe wird in einer neuen Zeile beginnen.
- System.out.print(a): a kann ein beliebiger Typ sein. Am Ende der Zeile wird kein Zeilenumbruch angefügt. Man kann durch mehrere Aufrufe Ausgaben in der gleichen Zeile durchführen.
- System.out.printf(formatString,a): a kann ein beliebiger Typ sein.Der String formatString erlaubt die Formatierung des Objekts a.
- System.out.flush(): Stellt sicher das die gesamte Ausgabe auch gedruckt wird. Daten werden oft in einem "Stream" gepuffert da einzelnes abarbeiten der Daten ineffizient ist.
Konsoleneingaben mit System.in
Einlesen eines Texts mit Hilfe von System.in und eines InputStreamReader. Dies geschieht am besten mit einer eigenen Methode:
import java.io; ... public static String StringLesen (){ // liest einen vom Benutzer eingegebenen Text (String) ein BufferedReader keyboard = new BufferedReader( new InputStreamReader(System.in)); try { return keyboard.readLine(); } catch (IOException e) { throw new RuntimeException( e ); } }
Konsoleneingaben mit java.util.Scanner
Die Klasse Scanner erlaubt das einfache lesen von primitiven Typen und Zeichenketten mit Hilfe regulärer Ausdrücke.
Man kann z.Bsp eine Ganzzahl(Integer) wie folgt einlesen:
Scanner sc = new Scanner(System.in); int i = sc.nextInt();
Der Scanner kann auch mit anderen Begrenzungen als Leerzeichen umgehen. Hier ein Beispiel mit dem Text "Trenner" als Begrenzung:
String input = "47 Trenner 11 Trenner Montag Trenner Dienstag Trenner"; Scanner s = new Scanner(input).useDelimiter("\\s*Trenner\\s*"); System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); System.out.println(s.next()); s.close();
Wird das folgende Ergebnis liefern:
47 11 Montag Dienstag
Parsen von Übergabeparametern mit einer eigenen Methode
Im folgenden Beispiel wird gezeigt wie man eine beliebige Anzahl von Eingabeparameter in einer eigenen Methode einliest und in Ganzzahlen umwandelt.
public class MethodenDemo { public static void main(String[] args) { int[] zahlenFeld; zahlenFeld = ganzZahlenEingabe(args); System.out.println(args.length + " arguments found"); for (int i = 0; i < zahlenFeld.length; i++) { System.out.println("zahlenFeld[" + i + "] = " + zahlenFeld[i]); } } public static int[] ganzZahlenEingabe(String[] myArgs) { int[] z = new int[myArgs.length]; if (myArgs.length > 0) { try { for (int i = 0; i < myArgs.length; i++) { z[i] = Integer.parseInt(myArgs[i]); } } catch (NumberFormatException e) { System.err.println("Input Error: " + e.getMessage()); System.exit(1); } } return z; } }
Das Programm erzeugt die folgenden Ausgaben:
$ java MethodenDemo 33 22 11 3 arguments found zahlenFeld[0] = 33 zahlenFeld[1] = 22 zahlenFeld[2] = 11
- 10266 views
12.1.2 Swing
12.1.2 SwingDie folgenden Seiten zeigen einen stark vereinfachten Überblick über die wichtigsten Swing Klassen und Methoden. Es werden hier nur die Klassen und Methoden dokumentiert, die im Rahmen des Kurses benötigt werden.
- 4723 views
JFrame
JFrameDie Klasse JFrame erlaubt das Erzeugen von eigenen Fenstern für eine Javaanwendung.
Die API Dokumentation zur Klasse JFrame enthält die vollständige Dokumentation aller Methoden dieser Klasse.
Im Folgenden werden die wichtigsten Methoden (für den Kontext des Kurses) in ihrer natürlichen Benutzungsreihenfolge vorgestellt.
Methode | Beschreibung | Geerbt von |
---|---|---|
Konstruktor: JFrame() | Erzeugen eines (noch) unsichtbaren Fensters ohne einen Titel | - |
Konstruktor: JFrame(String title) | Erzeugen eines (noch) unsichtbaren Fensters mit einem Titel | - |
void setSize(int breite, int hoehe) | Setzen der Fenstergröße durch Breite und Höhe in Pixel | java.awt.Window |
setLayout(LayoutManager manager) | Durch das Setzen eines Layout Managers wird die Anordnung der Komponenten vorbestimmt | - |
Component add(Component comp) | Hinzufügen einer Komponente zum Container | java.awt.Container |
setJMenuBar(JMenuVar menubar) | Hinzufügen einer Menüleiste zum Fenster | - |
setDefaultCloseOperation(int Operation) | Aktion die beim Schließen des Fensters geschieht. Mögliche Konstanten als Parameter: DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE, DISPOSE_ON_CLOSE, EXIT_ON_CLOSE | - |
pack() | Berechnen des Layouts des Fensters | java.awt.Window |
setVisible(boolean visible) | Erlaubt ein Fenster anzuzeigen oder auszublenden | java.awt.WIndow |
Beispiel... eines sehr einfachen Fensters welches mit der Klasse JFrame erzeugt wurde: import javax.swing.JFrame; |
Ergibt das folgende Fenster:
|
Anmerkung: Das hier gezeigte Fenster benutzt das Standard "Look and Feel" von MacOS. Die Rahmendekoration sieht bei anderen Betriebssystemen bzw. anderen "Loak and Feel" Themen anders aus. Der oben gezeigt Code ist jedoch plattformneutral.
- 4004 views
JMenuBar, JMenu, JMenuItem
JMenuBar, JMenu, JMenuItemDie Klassen JMenuBar, JMenu, JMenuItem , erlauben Konfiguration von Menüleisten, Pulldownmenüs und Menülisteneinträge für Fenster die mit JFrame erzeugt wurden.
Im Folgenden werden die wichtigsten Methoden (für den Kontext dieses Kurses) aufgeführt. Die obengenannten Klassen sind sehr viel mächtiger als hier beschrieben. Sie haben zusätzliche Methoden für:
- Lokalisierung
- unterschiedliche Arten von Menüeinträgen
- konfigurierbares Look and Feel
- Untermenüs
- markieren von Einträgen
- Anklicken durch Buchstaben Abkürzungen der Tastatur
- etc.
Die Klasse JMenuBar
Methode | Beschreibung | Geerbt von |
---|---|---|
Konstruktor: JMenuBar() | Erzeugen einer Menüleiste für ein JFrame Fenster | - |
JMenu add(JMenu c) | Hinzufügen eines Menüs zur Leiste | - |
Die Klasse JMenu
Methode | Beschreibung | Geerbt von |
---|---|---|
Konstruktor: JMenu() | Erzeugen einer Menüspalte am Ende der Menüleiste | - |
Konstruktor: JMenu(String title) | Erzeugen einer Menüspalte mit Titel am Ende der Menüleiste | - |
JMenuItem add(JMenuItem c) | Hinzufügen eines Menüeintrags am Ende des Menüs | - |
Die Klasse JMenuItem
Methode | Beschreibung | Geerbt von |
---|---|---|
Konstruktor: JMenuItem(String text) | Erzeugen eines Menüeintrags mit einem gegebenen Text | - |
void addActionListener(ActionListener l) | Registrieren eines Listeners der aufgerufen wird wenn der Menülisteneintrag geklickt wird | java.swingx.AbstractButton |
void setEnabled(boolean) | Anzeigen oder ausblenden eines Menülisteneintrags | - |
Beispiel
... Menüs mit einem einzigen Eintrag :
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; ... JFrame hf = new JFrame(); JMenuBar menueleiste; JMenu ablageMenue; JMenuItem beendenMenueEintrag; menueleiste = new JMenuBar(); ablageMenue = new JMenu("Ablage"); beendenMenueEintrag = new JMenuItem("Beenden"); beendenMenueEintrag.addActionListener(this); beendenMenueEintrag.setEnabled(true); ablageMenue.add(beendenMenueEintrag); menueleiste.add(ablageMenue); hf.setJMenuBar(menueleiste); hf.setVisible(true);
Einen einfachen Actionlistener kann man als Klasse mit der folgenden Methode implementieren:
public void actionPerformed(ActionEvent e) {JMenuItem source = (JMenuItem) (e.getSource());if (source == beendenMenueEintrag)System.exit(0);}
beendenMenueEintrag muss hier auf das gleiche Objekt wie bei der obigen Erzeugung der Menüleiste zeigen. Der Actionlistener beendet die Anwendung bei Aufruf!
Hierdurch ergibt sich das folgende Fenster:
Anmerkung: Das hier gezeigt Fenster benutzt das Standard "Look and Feel" von MacOS. Die Rahmendekoration sieht bei anderen Betriebssystemen bzw. anderen "Look and Feel" Themen anders aus. Der oben gezeigt Code ist jedoch plattformneutral.
- 8260 views
12.1.3 GUI IO (Swing)
12.1.3 GUI IO (Swing)Java bietet mit AWT (Another Window Tool Kit) und Swing reichhaltige Bibliotheken zum Entwurf von graphischen Benutzeroberflächen.
Hinweis: "Swing" (Schaukel) ist der Projektname. Swing ist Teil der Java Foundation Classes (JFC).
Einfache grafische "Swing" Oberfläche
Das folgende Beispiel dient als einfaches Rahmenprogramm für Text Ein- und Ausgaben.
Es übernimmt die Erzeugung eines Fensters mit:
- Eingabefeld für einen Text
- Ausgabefeld für einen Text
- 3 Buttons zum Verarbeiten des Texts in drei Methoden
Der eigene Algorithmus wird in der Methode public String executeOperation1(String) oder der beiden anderen executeOpteration Methoden implementiert.
Hierzu werden die folgenden Swing-Klassen verwendet
- JFrame: Erzeugen eines Fensters
- JLabel: Erzeugen eines festen Texts
- JTextField: Textfelder zur Ein- und Ausgabe von Texten
- JButton: Knopf(Button) zum Triggern einer Aktion
- ActionListener:Ausführen einer Aktion. Hier als anonyme Klasse implementiert.
Das erscheinende Fenster sieht wie folgt aus:
Das Fenster nach einer Texteingabe ("Eingabe"):
Das Fenster nach dem Drücken des Knopf "Do twice". Es wurde die Methode executeOperation2() aufgerufen:
Die Methoden executeOperation1(), executeOperation2(), executeOperation3() erlauben eine Implementierung des eigenen Algorithmus. Der erforderliche String in der Rückgabe wird dann im Fenster angezeigt.
Anbei das Beispielprogramm mit der Klasse DemoFrame:
package s2.swing; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JPanel; public class DemoFrame extends javax.swing.JFrame { private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JPanel jPanel1; private javax.swing.JLabel jLabel1; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; private void initComponents() { jLabel1 = new javax.swing.JLabel(); jTextField1 = new javax.swing.JTextField(); jTextField2 = new javax.swing.JTextField(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("My first JFrame"); jLabel1.setText("Input Text:"); jTextField1.setText("Input"); jTextField2.setText("Output"); jTextField2.setEditable(false); jButton1.setText("Do once:"); jButton2.setText("Do twice:"); jButton3.setText("Do three times:"); JPanel radioPanel = new JPanel(new GridLayout(1, 0)); radioPanel.add(jButton1); radioPanel.add(jButton2); radioPanel.add(jButton3); jButton1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton1) jTextField2.setText( executeOperation1(jTextField1.getText())); } } ); jButton2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton2) jTextField2.setText( executeOperation2(jTextField1.getText())); } } ); jButton3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(e.getSource() == jButton3) jTextField2.setText( executeOperation3(jTextField1.getText())); } } ); this.setBounds(300, 300, 200, 30); setMinimumSize(new Dimension(200,30)); getContentPane().add(jLabel1, BorderLayout.WEST); getContentPane().add(jTextField1, BorderLayout.CENTER); getContentPane().add(radioPanel, BorderLayout.NORTH); getContentPane().add(jTextField2, BorderLayout.SOUTH); pack(); } public DemoFrame() { initComponents(); } public static void main(String[] args) { DemoFrame f1 = new DemoFrame(); f1.setVisible(true); } public String executeOperation1(String s) { // Add Application logic here: String result = "Button 1:" + s; return (result) ; } public String executeOperation2(String s) { // Add Application logic here: String result = "Button 2:" + s + s; return (result) ; } public String executeOperation3(String s) { // Add Application logic here: String result = "Button 3:" + s + s +s; return (result) ; } }
Eine Swingoberfläche zum Erfassen von drei Texten
Die Klasse DemoFrame1 stellt drei Eingabefelder zum Erfassen von Texten zur Verfügung und erlaubt das Starten von 3 Operationen:
Implementierung der Klasse DemoFrame 1
package s2.swing; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; /** * * @author s@scalingbits.com */ public class DemoFrame1 extends javax.swing.JFrame { private ButtonGroup buttonGroup1; private JPanel jPanel1; private JLabel jLabel1; private JTextField jTextFieldin[]; private JTextField jTextField2; private JButton jButton1; private JButton jButton2; private JButton jButton3; /** * Anzahl der Eingabefelder */ private int inFields = 3; /** * Initialisieren aller Komponenten */ private void initComponents() { jLabel1 = new JLabel(); jTextFieldin = new JTextField[inFields]; for (int i = 0; i < inFields; i++) { jTextFieldin[i] = new JTextField(); } jTextField2 = new JTextField(); jButton1 = new JButton(); jButton2 = new JButton(); jButton3 = new JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("My second JFrame"); jLabel1.setText("Input Text:"); // Belegen alle Eingabefelder mit Standwerten for (int i = 0; i < inFields; i++) { jTextFieldin[i].setText("Input" + i); } // Belegen des Augabefeldes mit Standartwert jTextField2.setText("Output"); jTextField2.setEditable(false); // Erzeugen dreier Buttons jButton1.setText("Do once:"); jButton2.setText("Do twice:"); jButton3.setText("Do three times:"); // Ezeugen einer Datenstruktur(Panel) die drei Buttons aufnimmt JPanel radioPanel = new JPanel(new GridLayout(1, 0)); radioPanel.add(jButton1); radioPanel.add(jButton2); radioPanel.add(jButton3); // Führe Operation 1 aus wenn Button 1 gedrückt wird jButton1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getSource() == jButton1) { jTextField2.setText(executeOperation1( jTextFieldin[0].getText(), jTextFieldin[1].getText(), jTextFieldin[2].getText())); } } }); // Führe Operation 2 aus wenn Button 2 gedrückt wird jButton2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getSource() == jButton2) { jTextField2.setText(executeOperation2( jTextFieldin[0].getText(), jTextFieldin[1].getText(), jTextFieldin[2].getText())); } } }); // Führe Operation 3 aus wenn Button 3 gedrückt wird jButton3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getSource() == jButton3) { jTextField2.setText(executeOperation3( jTextFieldin[0].getText(), jTextFieldin[1].getText(), jTextFieldin[2].getText())); } } }); // Allgemeine Konfiguration des Fensters this.setBounds(300, 300, 200, 30); setMinimumSize(new Dimension(200, 30)); // Einfügen der Eingabeaufforderung links getContentPane().add(jLabel1, BorderLayout.WEST); // Panel zum verwalten von mehreren Eingabefelder JPanel inputPanel = new JPanel(new GridLayout(inFields, 0)); for (int i = 0; i < inFields; i++) { inputPanel.add(jTextFieldin[i]); } // Einfügen des Panels mit Eingabefeldern in der Mitte getContentPane().add(inputPanel, BorderLayout.CENTER); // Einfügen des Panels mit Buttons oben getContentPane().add(radioPanel, BorderLayout.NORTH); // Einfügen der Ausgabezeile unten getContentPane().add(jTextField2, BorderLayout.SOUTH); pack(); } /** * Initialierung alle Komponenten */ public DemoFrame1() { initComponents(); } /** * Hauptprogramm: Erzeuge eine Instanz von DemoFrame1 und zeige sie an * @param args */ public static void main(String[] args) { DemoFrame1 f1 = new DemoFrame1(); f1.setVisible(true); } /** * Führe Operation 1 aus (Button 1 wurde gedrückt) * @param s1 * @param s2 * @param s3 * @return */ public String executeOperation1(String s1, String s2, String s3) { // Add Application logic here: String result = "Button 1:" + s1 + s2 + s3; return (result); } /** * Führe Operation 2 aus (Button 2 wurde gedrückt) * @param s1 * @param s2 * @param s3 * @return */ public String executeOperation2(String s1, String s2, String s3) { // Add Application logic here: String result = "Button 2:" + s1 + s2 + s3; return (result); } /** * Führe Operation 3 aus (Button 3 wurde gedrückt) * @param s1 * @param s2 * @param s3 * @return */ public String executeOperation3(String s1, String s2, String s3) { // Add Application logic here: String result = "Button 3:" + s1 + s2 + s3; return (result); } }
- 8586 views
12.1.4 Math
12.1.4 MathDie Math Klasse enthält alle mathematischen Hilfsroutinen wie z.Bsp.
public static double random(): Zufallszahlengenerator
...erzeugt zufällige Fliesskommazahlen im Intervall von 0.0 bis 1.0
Die folgende Methode erzeugt ein beliebig großes Feld von zufälligen Ganzzahlen in einem bestimmten Bereich (range)
public static int[] createRandom(int size, int range) { int[] array1 = new int[size]; for (int i=0; i<size;i++) array1[i] = (int)(Math.random()*range); return array1; }
- 4925 views
12.1.5 Systemresourcencheck mit der Klasse Runtime
12.1.5 Systemresourcencheck mit der Klasse RuntimeDie Java Runtime bietet die Möglichkeit die folgenden Systemwerte auszulesen:
- verfügbare Prozessoren
- freier Speicher
- maximal verfügbarer Speicher
- gesamter allokierter Speicher der VM
Die Klasse bietet ebenfalls die Möglichkeit eine Reihe von Operationen zu starten:
- expliziter Aufruf eines "Garbage Collectors"
- expliziter Aufruf des Finalizers
- externer Aufruf eines Betriebssystemkommandos
- beenden des Prozesses
- laden von dynamischen Bibliotheken
Warnung: Alle aufgeführten Operationen haben erheblichen Einfluss auf die gesamte VM. Im normalen Entwicklungsumfeld größerer Anwendungen führen die oben genannten Operationen in der Regel zu erheblichen negativen Seiteneffekten für Sicherheit, Stabilität und Performanz von Anwendungen.
Die folgende Methode nutzt die Klasse Runtime um die wichtigsten Werte des Prozesses auszulesen:
public static void systemStatus(){ Runtime r = Runtime.getRuntime(); System.out.println("*** System Status ***"); System.out.println("Prozessoren : " + r.availableProcessors()); System.out.println("Freier Speicher: " + r.freeMemory()); System.out.println("Maximaler Speicher: " + r.maxMemory()); System.out.println("Gesamter Speicher: " + r.totalMemory()); System.out.println("*** ***"); }
- 5908 views
12.1.6 Collator (Vergleichen von Zeichenketten)
12.1.6 Collator (Vergleichen von Zeichenketten)Der Vergleich von Zeichenketten hängt oft von der lexikographischen Ordnung ab. Die lexikographische Ordnung selbst hängt wieder von der Region ab. Java bietet hierfür die Klasse Collator (Collation engl. : Der Textvergleich) an, die Zeichenketten nach bestimmten Kriterien sortieren kann. Die Methode compare() erlaubt das Vergleichen zweier Zeichenketten.
Zur Bestimmung der Sortierreihenfolge wird die Klasse java.util.Locale benötigt, die die entsprechende Region und deren Sprache bestimmt.
Das Vorgehen zum Konfigurieren und Vergleichen von Zeichenketten ist das folgende:
- Auswahl der gewünschten Region und Sprache (Locale). Z.Bsp. Locale.GERMANY
- Bestimmung der gewünschten Sortierstärke für den Collator ( setStrength() )
- Collator.PRIMARY : nur die grundlegende Abfolge wie z.Bsp 'a' und 'b' werden berücksichtigt
- Collator.SECONDARY: Unterschiede wie z.Bsp. 'a' und 'ä' werden berücksichtigt falls nach den Regeln der Kategorie "Primary" kein Unterschied existiert.
- Collator.TERTIARY: Unterschiede wie z. Bsp. 'a' und 'A' werden berücksichtigt falls nach den Regeln der Kategorie "Secondary" kein Unterschied existiert
- Vergleichen der Zeichenketten mit compare() oder equals()
Beispiel
Vergleichen von Vor- und Nachnamen einer Person nach deutschen Sortierregeln inklusive besonderer deutscher Zeichen mit der Methode compare() der Klasse Collator.
package s2.sort; import java.text.Collator; import java.util.Locale; public class Person{ private String nachname; private String vorname; private static Locale myLocale = Locale.GERMANY; public boolean istKleinerAls(Person p) { boolean kleiner = false; Collator myCollator = Collator.getInstance(myLocale); // Beim Vergleich auch Gross- und Kleinschreibung unterscheiden myCollator.setStrength(Collator.TERTIARY); // Konfiguriere die Sortierordnung if (myCollator.compare(nachname, p.nachname) < 0) { kleiner = true; } else if (myCollator.compare(nachname, p.nachname) == 0) { kleiner = myCollator.compare(vorname, p.vorname) < 0; } return kleiner; } }
Lauffähiges Programm zum Testen von Locales und Sortierordnungen
Das Programm Lexikographisch akzeptiert die folgende Kommandozeilensyntax:
java s2.sort.Lexikographisch String1 String2 [[German|French|UK|US] [PRIMARY|SECONDARY|TERTIARY]]
Beispiele
Quellcode
package Kurs2.Sort; import java.text.Collator; import java.util.Locale; /** * * @author sschneid */ public class Lexikographisch { public static void main(String[] args) { String s1 = "Test1"; String s2 = "Test2"; Locale myLocale = Locale.GERMANY; int strength = Collator.TERTIARY; if (args.length < 2) { System.out.println("Erforderliche Mindestparameter: String1 String2");
} else { s1 = args[0]; s2 = args[1]; } if (args.length >= 3) { String loc = args[2]; if (loc.equalsIgnoreCase("German") || loc.equalsIgnoreCase("Germany")) { myLocale = Locale.GERMAN; } if (loc.equalsIgnoreCase("France") || loc.equalsIgnoreCase("French")) { myLocale = Locale.FRENCH; } if (loc.equalsIgnoreCase("US")) { myLocale = Locale.US; } if (loc.equalsIgnoreCase("UK") || loc.equalsIgnoreCase("English")) { myLocale = Locale.UK; } // Add more locales here... } if (args.length >= 4) { String s = args[3]; if (s.equalsIgnoreCase("PRIMARY")) { strength = Collator.PRIMARY; } if (s.equalsIgnoreCase("SECONDARY")) { strength = Collator.SECONDARY; } if (s.equalsIgnoreCase("TERTIARY")) { strength = Collator.TERTIARY; } } vergleich(s1, s2, myLocale, strength); } private static void vergleich(String s1, String s2, Locale myLocale, int strength) { Collator myCollator = Collator.getInstance(myLocale); // Beim Vergleich auch Gross- und Kleinschreibung unterscheiden // Konfiguriere die Sortierordnung myCollator.setStrength(strength); if (myCollator.compare(s1, s2) == 0) { System.out.println(s1 + " ist gleich " + s2); } if (myCollator.compare(s1, s2) < 0) { System.out.println(s1 + " vor " + s2); } if (myCollator.compare(s1, s2) > 0) { System.out.println(s2 + " vor " + s1); } } }
- 9369 views
12.2 Kodierstil
12.2 Kodierstil
Es gibt ein paar wichtige Regeln um Code auch in Zukunft noch verstehen zu können. In der Javawelt gelten die folgenden Konventionen:
Siehe auch Wikibooks: Standard für Variablen und Bezeichner |
- 5519 views
12.3 Entwicklungsumgebungen
12.3 EntwicklungsumgebungenHier werden nur kostenlose Entwicklungsumgebungen vorgestellt.
Das Ziel ist es den Studenten eine Orientierung für den Programmierkurs zu geben. Die Leistung kommerziell verfügbarer Werkzeuge soll hiermit nicht geschmälert werden.
wikipedia.org verfügt über eine gute Sammlung von Java Entwicklungsumgebungen.
gedit: Ein intelligenter Texteditor
- Text Editor des Gnome Projekt
- kostenlos
- Eigenschaften
- Syntax Highlighting
- Der Editor ist keine eigentliche Javaentwicklungsumgebung da er kein Übersetzen, debuggen etc. unterstützt
- Für den Kurs sehr gut geeignet
bluej.org
- Javaentwicklungstool für die Lehre
- kostenlos
- sehr schöne UML Integration
- begrenzte Möglichkeiten
Eclipse
- Profesionelles Entwicklungswerkzeug für Java Anwendungen
- Am weitesten verbreitete Entwicklungsumgebung
- Kostenlos
- Sehr gute Windowsanbindung
- Sehr komplex
- Sehr viele Plugins
- Nicht vollständig in Java entwickelt
- 4469 views
12.3.1 Intellij
12.3.1 IntellijMeine persönlichen Preferenz ist aktuell IntelliJ IDEA Community version für Windows und Mac.
Anbei zwei Youtube Videos zur Installation und Konfiguration
- Youtube-video zum Installieren
- Youtube-video zum Konfigurieren von Github (Modifizieren Sie diesen Code besser nicht. Kopieren Sie sich Ihn bei Bedarf in Ihr Arbeitsprojekt)
Es gibt den Quellcode zur Vorlesung bei Github. Das zweite Video zeigt wie man sich diesen Quellcode in einem eigenen Projekt verfügbar macht. Der Quellcode in diesem Archiv wird immer mal wieder angepasst und erneuert. Die html Seiten dieses Webservers sind eher hintendran. Sie müssen händisch gepflegt werden...
Der Code in Ihrem Intellij wird aber nicht automatisch erneuert. Sie müssen sich die neuen Codeänderungen mit einem "Pull Request" nachträglich herunterladen.
Dies geschieht in IntelliJ durch die folgenden Schritte:
Führen sie einen Maus-Rechts-Klick auf dem Namen Ihres Projekts aus. Hier "dhbwjava". Ein Menü erscheint. |
Wählen sie git -> Repository->Pull aus, um einen "Pull Request" einzuleiten. |
Wählen Sie den "master" Zweig aus. Es kann bei Github noch andere Codezweige (Englisch: branches) geben. Das ist hier nicht der Fall. Klicken Sie auf den "Pull" Knopf. |
Jetzt gibt es zwei Szenarios...
- Das einfache Szenario: Sie haben in diesem Projekt nie etwas geändert!
- Willigen Sie in alles ein, ihr Code wird eneuert und aktualisiert.
- Fertig!
- Das komplizierte Szenario: Sie haben den Code früher einmal modifiziert
- IntelliJ erkennt Ihre Änderungen und will Sie nicht so einfach überschreiben. IntelliJ will den Code abmischen (auf Englisch: Mergen) damit Ihre Änderungen nicht verloren gehen
- Sie müssen jetzt eine Entscheidung treffen:
- Ihr geänderter Code darf überschrieben werden!
- Erlauben Sie IntelliJ das Überschreiben.
- Weinen Sie Ihrem Code eine Träne nach.
- Fertig
- Sie möchten Ihren geänderten Code behalten
- Kochen Sie eine Kanne eines legalen Suchtgetränks, streichen Sie alle anderen Termine des Tages.
- Besorgen Sie sich einen dunkelbraunen oder schwarzen Gürtel von git...
- IntelliJ wird Ihnen jetzt alle Änderungen der neuen Version und Ihre Änderungen vorlegen. Sie müssen für jede Änderung entscheiden, welche Sie behalten möchten.
- Das ganze wird sich für jede neue Version auf Github wiederholen.
- Sie können aber auch Ihrem Referenten Ihre Änderungen mit einem "Merge Request" empfehlen. Vielleicht akzeptiert er ihn...
- Hierfür benötigen Sie ein Konto bei github und ein eigenes github-Projekt.
- Sie müssen dann Ihren Code bei zukünftigen Versionen nicht mehr abmischen
- Fertig!
- Ihr geänderter Code darf überschrieben werden!
- 197 views
12.3.2 Entscheidungshilfe: IntelliJ oder Eclipse?
12.3.2 Entscheidungshilfe: IntelliJ oder Eclipse?Anbei eine Entscheidungshilfe für die Teilnehmer der Vorlesung der dualen Hochschule.
Grundsätzliches:
- Man benötigt für den Kurs nicht unbedingt eine hochentwicklete Enwicklungsumgebung. Man benötigt streng genommen nur die Installation des Java JDK, ein Texteditor und Kommandozeilenzugriff
- Jede Entwicklungsumgebung die zumindestens Pakete unterstützt ist ausreichend für den Kurs
- Es ist durchaus ein gute Idee sich mehrere Entwicklungsumgebungen anszuschauen um die zu wählen die einem am besten gefällt.
- Die Benutzung der graphischen Oberflächen von Eclipse und Netbeans im Rahmen des Kurses ist fast identisch.
Typischerweise stellt sich für fast jeden Javaentwickler irgendwann die Frage:
IntelliJ oder Eclipse?
Diese Frage können sie recht gut nach den folgenden Kriterien beantworten:
Ich verwende schon Entwicklungsumgebung XY
Bleiben Sie bei Ihrer Entwicklungsumgebung XY.
Mein Unternehmen verwendet bevorzugt die Entwicklungsumgebung XY
Benutzen Sie die Umgebung XY und fragen Sie einen Kollegen nach der firmenspezifischen Installation (Erweiterungen!). Sie werden wahrscheinlich später in diesem Kontext arbeiten.
Ich habe keine Präferenzen und ich bin Windowsbenutzer
Erwägen Sie IntelliJ. IntelliJ ist sehr gut an Windows angepasst.
Eclipse ist der Marktführer.
Ich habe keine Präferenzen und ich bin MacOS Benutzer
Erwägen Sie IntelliJ. IntelliJ bettet sich natürlicher in MacOS ein. Der Referent benutzt ebenfalls IntelliJ auf MacOS.
Softwareentwicklung ist ein völlig neues Thema für mich
Erwägen Sie IntelliJ
- Sie sind "click" kompatibel zum Referenten
- Der Referent kann Ihnen leichter helfen
- Der syntaxsensitive Editor scheint etwas besser zu sein. Schreibfehler werden etwas zuverlässiger und schneller angezeigt.
Zusammenfassung
Beide Produkte sind kostenlos und für den Kurs gleichwertig. Sie installieren sich am besten in der zweiten Kurshälfte das andere Produkt und entscheiden dann selbst.
- 3916 views
12.3.3 Shortcuts (Befehlsabkürzungen)
12.3.3 Shortcuts (Befehlsabkürzungen)Anbei einige Befehlsabkürzungen in Eclipse und Netbeans
Abkürzung | IntelliJ | Eclipse |
---|---|---|
Referenzen | Menü:preferences ->Java->Editor->Templates | |
System.out.println("") | sout<tab> | sysout<ctrl><Leertaste> |
public static void main(String[] args) {} | psvm<tab> | |
String | ||
switch | switch<ctrl><Leertaste> | |
main | main<ctrl><Leertaste> |
- 5758 views
12.4 Java unter Windows
12.4 Java unter WindowsKonfigurieren des Suchpfads für Kommandozeileneingaben
Im Kurs ist es notwendig Javakommandos auf der Kommandozeile aufzurufen. Auf Windows muss man entweder immer den vollen Pfad (z.Bsp. C:\Program Files\Java\jdk1.8.0_60\bin\java ) eingeben. Oder man konfiguriert Windows so, dass es im entsprechenden Verzeichnis nach diesen Befehlen sucht. Nach erfolgreicher Konfiguration kann man die Befehle java, javac, jar etc. wie folgt verwenden:
Für MacOs Anwender gibt es nichts zu tun!
Als erstes muss man das das bin Verzeichnis des JDK finden. In ihm stehen alle Programme des JDK. Man kann es an den Dateien javac.exe und javadoc.exe erkennen Ein typischer Ort kann zum Beispiel sein:
C:\Program Files\Java\jdk1.8.0_60\bin\
Dieses Verzeichnis muss zum Standardsuchpfad des Benutzers hinzugefügt werden. Bei Windows geschieht das wie folgt:
Öffnen des Control Panels
Bei Windows 2012R2 finden Sie einen Eintrag bei den "Apps" den Anwendungen
Öffnen der Systemsteuerung
Klicken Sie dann auf "System and Security"
Klicken Sie auf "System"
Klicken Sie auf "Advanced system settings"
Ein Dialog mit dem Titel "System Properties" erscheint.
Wählen Sie hier den Button "Environment Variables"
Suchen Sie hier den Eintrag "Path" in "System variables" und klicken Sie dann den "Edit" Button
Beim Einpflegen des zusätzlichen Suchpfades ist das Folgende zu beachten
- Die verschiedenen Verzeichnisse im Suchpfad werden mit Semikolons abgetrennt
- Der Pfad zu den Java JDK-kommandos wird hinter den existierenden Suchverzeichnissen eingetragen. So werden Javakommandos nur dann ausgeführt wenn sie in keinem anderen Verzeichnis gefunden wurden.
Vorsicht:
- Dies ist eine systemweite Einstellung. Eine Fehlkonfiguration wird alle Anwendungen auf dem System betreffen!
- Ist im Pfad schon ein anderes JDK konfiguriert, können Konflikte auftreten.
Kontrolle
Bei einem erlfogreich gepflegten Pfad kann man in einem neuen Fenster zum Beispiel die Javaversion mit dem Befehl "java -version" kontrollieren.
Funktioiert dieser Befehl ist sichergestellt, dass ein Java JRE oder JDK installiert ist. Zum Sicherstellen, dass das JDK garantiert installiert und konfiguriert ist, muss man "javac -version" aufrufen. Der Javaübersetzer ist nur Teil des JDK.
- 5527 views
12.5 Typische Fehler
12.5 Typische FehlerSystem.out.print() und System.out.println()
Beide Befehle erzeugen einer Konsolenausgabe von Texten.
- Die Methode print() lässt den Schreibzeiger an der aktuellen Position stehen. Die nächste Konsolenausgabe erfolgt in der gleichen Zeile
- Die Methode println() (ln= Line) erzeugt am Ende des ausgegebenen Texts einen Zeilenumbruch. Die nächste Konsolenausgabe erfolgt in der nächsten Zeile.
Beispiele
Quellcode | Ausgabe |
---|---|
System.out.print("Person: "); |
Person: John Doe **************** |
aber:
Quellcode | Ausgabe |
---|---|
System.out.println("Person: "); |
Person: John Doe **************** |
String ist eine Klasse und kein Basistyp
Java kann Zeichenketten nicht als Basistypen verwalten. Hierzu verwendet Java eine Klasse mit dem Namen String. Zeichenketten werden wie Objekte behandelt
Die hat eine Reihe von Konsequenzen:
- String ist kein Schlüsselwort
- Variablen vom Typ String sind nur Zeiger auf Zeichenketten
- Es gibt Situationen in denen zwei Variablen auf zwei Zeichenketten mit dem gleichen Inhalt zeigen. Dies können eventuell verschiedene Objekte sein.
Verwechslung von Zuweisungsoperator (=) und Vergleichsoperator (==)
Mit
i=j;
wird der Wert von j auf i zugewiesen. Mit
if(i==j)
werden zwei Variablen verglichen.
Zuweisungen tauchen nicht in boolschen Termen auf!
Die folgende Syntax ist korrekt:
int i=0; int j=1; boolean b = (i==j);
Die Boolsche Variable b wird mit false belegt da i ungleich j ist.
- 4821 views
12.6 IntelliJ Installation (Windows)
12.6 IntelliJ Installation (Windows)Installieren Sie sich die Community Edition von IntelliJ. Versionen und GUIs werden sich ändern. Die beiden Youtube Videos stammen vom September 2020
- Installation von IntelliJ (Youtube)
- Anlegen eines zweiten Projekts mit Quellen der Vorlesung von GitHub
Ein paar Tipps
- Halten Sie das Video an. Sie werden nicht so schnell tippen können...
- Hilf ich kann die Schrift nicht lesen!
- Stellen Sie mit dem "Zahnrad" die Auflösung hoch
- Benutzen Sie mit dem unterbrochenen Rechteck den Gesamtschirmmodus
- Sie verwenden einen Mac oder Linux?
- Die Installation ist hier sinngemäß sehr, sehr ähnlich.
- 128 views
12.7 Feedback
12.7 FeedbackDatum | 2020 Teiln. | 2020 Bewertung | 2021 Teiln. | 2021 Bewertung | Kommentar | |
---|---|---|---|---|---|---|
Block 1 | 5.10.2021 | 9/25 | 4,3 |
|
||
Block 2 | 14.10.2021 | 19 | 3,8 | 4/25 | 4,4 |
|
Block 3 | 21.10.2020 | 10/10 | 4,2/4,4 | 3/25 | 4,7 |
|
Block 4 | 27.10.2020 | 8 | 3,8 | 3/25 | 3.7 |
Mir hat gefallen:
Ich würde das folgende anders machen:
|
Block 5 | 5-6.11.2020 | 14/2 | 3,6/3,5 | |||
Block 6 | 12-13.11.2020 | 11/11 | 3,7/4,4 | |||
Block 7 | 20.11.2020 | 10 | 3,6 | |||
Block 8 | 26-27.11.2020 | 9 | 3,7 |
|
||
Block 9 | 3-4.12 | 2/5 | 4,5/4,2 |
|
||
Block 10 | 10.12.2020 | 5 | 3,6 |
|
||
Block 11 |
- 136 views
13. Ressourcen und Quellen
13. Ressourcen und Quellen
Lernen
Javabilder
|
Javakurse
- 4127 views
Vielen Dank
Ich möchte mich hier für die übersichtliche und nachvollziehbare Darstellung des Themas Programmierung bedanken. Sehr gelungen
- Log in to post comments
13.1 Quellcode der in der Vorlesung verwendeten Applets
13.1 Quellcode der in der Vorlesung verwendeten AppletsHier finden Sie den Quellcode der in dieser Vorlesung verwendeten Applets.
Die meißten in diesem Skript integrierten Applets kann man auch als reguläre Programme starten.
Alle Applets stehen Ihnen unter der sehr liberalen BSD Lizenz zur Verfügung.
Die Applets wurden entwickelt um die Vorlesung interaktiver zumachen. Der Quellcode wurde nicht auf den besten Programmierstil optimiert.
Ich freue mich daher über Feedback und verbesserte Versionen.
Die hier verwendeten Applets sind sicher nicht perfekt, sie wurden mit den folgenden Zielen implementiert
- Wenig Code:
- Copy und Paste der Quellen soll einfach sein
- Hierdurch werden leider auch viele Fehlerfälle nicht behandelt
- Alles in einer Klasse: Vereinfacht Copy und Paste für den Anwender
- Beschränkung auf Konzepte die in der Vorlesung behandelt werden
- die Implementierungen sollen am Ende der Vorlesung nachvollziehbar sein
- Entwicklungszeit: Auch kleine Applets brauchen Ihre Zeit. Man kann sicherlich Dinge eleganter lösen.
- 2417 views
13.1.2 Klasse s1.block2.BinaerIntApplet
13.1.2 Klasse s1.block2.BinaerIntAppletStarten des Applets als Hauptprogramm
Laden Sie die jar Datei Kurs1Applet.jar.
Die Anwendung kann von der Kommandozeile mit dem folgenden Kommando gestartet werden.
java -cp Kurs1Applet.jar s2.block2.BinaerIntApplet
Die Datei Kurs1Applet.jar muss im gleichen Verzeichnis sein
Der Quellcode
package s1.block2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
/**
*
* @author sschneid
* @version 1.2
*
* Copyright (c) 2013, Dr. Stefan Schneider, schneider4me.de
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
public class BinaerIntApplet extends JApplet implements ActionListener {
private JTextField eingabeText;
private JButton enterButton;
private JRadioButton byteButton;
private JRadioButton charButton;
private JRadioButton shortButton;
private JRadioButton intButton;
private JRadioButton longButton;
private int[] bits;
private String eingabeWert = "0";
private String typeTxt = "int";
/**
* Konstruktor der Klasse. Hier werden alle graphischen
* Objekte angeliegt.
*/
public BinaerIntApplet() {
JPanel buttonPanel;
JFrame f;
Container co;
bits = new int[32];
// Erzeugen der Buttons und Texteingabefeld
JLabel eingabeLabel = new JLabel("Eingabewert: ");
eingabeText = new JTextField(eingabeWert);
eingabeText.setPreferredSize(new Dimension(100, 20));
enterButton = new JButton("Enter");
enterButton.addActionListener(this);
byteButton = new JRadioButton("byte");
byteButton.setMnemonic(KeyEvent.VK_B);
charButton = new JRadioButton("char");
charButton.setMnemonic(KeyEvent.VK_C);
shortButton = new JRadioButton("short");
shortButton.setMnemonic(KeyEvent.VK_S);
intButton = new JRadioButton("int");
intButton.setMnemonic(KeyEvent.VK_I);
intButton.setSelected(true);
longButton = new JRadioButton("long");
longButton.setMnemonic(KeyEvent.VK_L);
ButtonGroup intType = new ButtonGroup();
intType.add(byteButton);
intType.add(charButton);
intType.add(shortButton);
intType.add(intButton);
intType.add(longButton);
JPanel typePanel = new JPanel();
typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.Y_AXIS));
typePanel.add(byteButton);
typePanel.add(charButton);
typePanel.add(shortButton);
typePanel.add(intButton);
typePanel.add(longButton);
// Einfügen der drei Komponenten in ein Panel
// Das Gridlayout führt zum Strecken der drei Komponenten
buttonPanel = new JPanel();
buttonPanel.add(eingabeLabel);
buttonPanel.add(eingabeText);
buttonPanel.add(enterButton);
JPanel centerPanel=new JPanel();
centerPanel.setSize(500, 500);
Container myPane = getContentPane();
myPane.add(buttonPanel, BorderLayout.SOUTH);
myPane.add(typePanel, BorderLayout.EAST);
myPane.add(centerPanel, BorderLayout.CENTER);
}
/**
* Dies Methode erlaubt das Malen der Vektorgraphik
* auf dem Hintergrund der graphischen Komponente
* @param g
*/
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.blue);
g.drawString(typeTxt + ", " +bits.length + " bits, "
+eingabeWert +": ", 20, 25);int rows=0;
int cols=0;
for (int i = (bits.length - 1); i >= 0; i--) {
if (i == bits.length - 1) {
g.setColor(Color.red);
} else {
g.setColor(Color.black);
}
g.drawString(Integer.toString(bits[i]),
20 + 10 * cols,
50+ 25*rows);
if (cols!=15) cols++;
else {cols=0; rows++;}
}
}
/**
* Diese Methode wird nach einer Eingabe aufgerufen.
* Sie dekodiert die Eingabe des Benutzers
* @param e
*/
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
try {
eingabeWert = eingabeText.getText();
if (source == enterButton) { //enter Button aufgerufen
if (byteButton.isSelected()) {
typeTxt = "byte";
byte wert = Byte.parseByte(eingabeWert);
bits = decode(wert);
}
if (shortButton.isSelected()) {
typeTxt = "short";
short wert = Short.parseShort(eingabeWert);
bits = decode(wert);
}
if (charButton.isSelected()) {
typeTxt = "char";
char wert = eingabeWert.charAt(0);
bits = decode(wert);
eingabeWert = eingabeWert.substring(0, 1);
}
if (intButton.isSelected()) {
typeTxt = "int";
int wert = Integer.parseInt(eingabeWert);
bits = decode(wert);
}
if (longButton.isSelected()) {
typeTxt = "long";
long wert = Long.parseLong(eingabeWert);
bits = decode(wert);
}
}
} catch (java.lang.NumberFormatException ex) {
// Fehlerbehandlung bei fehlerhafter Eingabe
eingabeWert="Fehler";
eingabeText.setText(eingabeWert);
bits = decode((byte)0);
}
repaint();
}
public static int[] decode(short s) {
int size = 16;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
public static int[] decode(byte s) {
int size = 8;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}public static int[] decode(char s) {
return decode((short) s);
}public static int[] decode(int s) {
int size = 32;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
public static int[] decode(long s) {
int size = 64;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (int) (1 & s);
s >>>= 1;
}
return stellen;
}
/**
* Starten der Anwendung als eigenständiges Programm
*
* @param args
*/
public static void main(String[] args) {
// Es wird ein JFrame benötigt, in das das Applet als Komponente
// gesteckt wird.
JFrame f = new JFrame("BinaerIntApplet-Standalone");
f.add(new block2.BinaerIntApplet());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
/**
* Berechnen eines neuen Tupels von Kubikwurzel
* und dazu gehöriger dritter Potenz
*/
}
- 4585 views
13.1.3 Klasse s1.block2.BinaerFloatApplet
13.1.3 Klasse s1.block2.BinaerFloatAppletStarten des Applets als Hauptprogramm
Laden Sie die jar Datei Kurs1Applet.jar.
Die Anwendung kann von der Kommandozeile mit dem folgenden Kommando gestartet werden.
java -cp Kurs1Applet.jar s1.block2.BinaerFloatApplet
Die Datei Kurs1Applet.jar muss im gleichen Verzeichnis sein
Der Quellcode
package s1.block2;import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;/**
*
* @author sschneid
* @version 1.2
*
* Copyright (c) 2013, Dr. Stefan Schneider, schneider4me.de
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
public class BinaerFloatApplet extends JApplet implements ActionListener {private JTextField eingabeText;
private JButton enterButton;
private JButton nanButton;
private JButton negativeInfinityButton;
private JButton positiveInfinityButton;
private JButton maxButton;
private JButton minButton;
private int[] bits;
private String eingabeWert = "1";
private int vorzeichen;
private float mantisse;
private int exponent;
float wert = 0;
public BinaerFloatApplet() {
JPanel buttonPanel;
bits = new int[32];
bits = decode(1.0f);
// Erzeugen der Buttons und Texteingabefeld
JLabel eingabeLabel = new JLabel("Eingabewert: ");
eingabeText = new JTextField(eingabeWert);
eingabeText.setPreferredSize(new Dimension(100, 20));
enterButton = new JButton("Enter");
enterButton.addActionListener(this);
nanButton = new JButton("N.a.N.");
nanButton.addActionListener(this);
negativeInfinityButton = new JButton("-Infinity");
negativeInfinityButton.addActionListener(this);
positiveInfinityButton = new JButton("+Infinity");
positiveInfinityButton.addActionListener(this);
maxButton = new JButton("MAX_VALUE");
maxButton.addActionListener(this);
minButton = new JButton("MIN_VALUE");
minButton.addActionListener(this);
JPanel optPanel = new JPanel();
optPanel.setLayout(new BoxLayout(optPanel, BoxLayout.Y_AXIS));
optPanel.add(nanButton);
optPanel.add(minButton);
optPanel.add(maxButton);
optPanel.add(negativeInfinityButton);
optPanel.add(positiveInfinityButton);
buttonPanel = new JPanel();
buttonPanel.add(eingabeLabel);
buttonPanel.add(eingabeText);
buttonPanel.add(enterButton);
JPanel centerPanel = new JPanel();
centerPanel.setSize(500, 500);
Container myPane = getContentPane();
myPane.add(buttonPanel, BorderLayout.SOUTH);
myPane.add(optPanel, BorderLayout.EAST);
myPane.add(centerPanel, BorderLayout.CENTER);
}
@Override
public void paint(Graphics g) {
super.paint(g);
int hoffSet = 8; // Ziffernbreite
int voffset = 15; // Zeilenabstand
int binaerStart = 100; // Horizontaler Offset für Bitsdarstellung
int dezimalStart = hoffSet * 2;
g.setColor(Color.blue);
g.drawString("32 Bit float:", dezimalStart, 25);
g.drawString(eingabeWert + " = ", dezimalStart, 45);
dezimalStart += 10+(eingabeWert.length()) * hoffSet;
g.drawString("(-1)", dezimalStart, 45);
dezimalStart += 3 * hoffSet;
g.drawString(Integer.toString(bits[31]), dezimalStart, 40);
dezimalStart += 1 * hoffSet;
g.drawString("*2", dezimalStart, 45);
dezimalStart += 2 * hoffSet;
g.drawString("(", dezimalStart, 40);
dezimalStart += 1 * hoffSet;
g.drawString(Integer.toString(exponent), dezimalStart, 40);
dezimalStart += Integer.toString(exponent).length() * hoffSet;
g.drawString("-127)", dezimalStart, 40);
dezimalStart += 5 * hoffSet;
g.drawString("*", dezimalStart, 45);
dezimalStart += 1 * hoffSet;
g.drawString(Float.toString(mantisse), dezimalStart, 45);
int rows = 0;
int cols;
g.setColor(Color.black);
g.drawString("Vorzeichen : ",20,60 + voffset * rows);
String vorz = "-1*";
if (bits[31] == 0) {
vorz = "1*";
}
g.drawString(Integer.toString(bits[31]),
binaerStart,
60 + voffset * rows);
rows++;
g.setColor(Color.black);
g.drawString("Exponent : ",
20,
60 + voffset * rows);
cols = 0;
for (int i = 30; i >= 23; i--) {
g.drawString(Integer.toString(bits[i]),
binaerStart + 10 * cols,
60 + voffset * rows);
if (cols != 7) {
cols++;
} else {
cols = 0;
rows++;
}
}
g.drawString("Mantisse :",20,60 + voffset * rows);
cols = 0;
g.setColor(Color.blue);
g.drawString("1",
binaerStart + 10 * cols,
60 + voffset * rows);
g.setColor(Color.black);
cols++;
for (int i = 22; i >= 0; i--) {
g.drawString(Integer.toString(bits[i]),
binaerStart + 10 * cols,
60 + voffset * rows);
if (cols != 7) {
cols++;
} else {
cols = 0;
rows++;
}
}
g.setColor(Color.blue);
g.fill3DRect(20,(70 + voffset * rows), 200, 40, rootPaneCheckingEnabled);
rows++;
g.setColor(Color.black);
g.drawString("Die führende 1 der Mantisse ist",
20,70 + voffset * rows);
rows++;
g.drawString("nicht Teil der Datenstruktur!",
20, 70 + voffset * rows);}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == nanButton) { //>> Button aufgerufen
eingabeWert = eingabeText.getText();
wert = Float.NaN;
}
if (source == minButton) {
wert = Float.MIN_VALUE;
}
if (source == maxButton) {
wert = Float.MAX_VALUE;
}
if (source == negativeInfinityButton) {
wert = Float.NEGATIVE_INFINITY;
}
if (source == positiveInfinityButton) {
wert = Float.POSITIVE_INFINITY;
}
if (source == enterButton) {
try {
eingabeWert = eingabeText.getText();
wert = Float.parseFloat(eingabeWert);
} catch (java.lang.NumberFormatException ex) {
// Fehlerbehandlung bei fehlerhafter Eingabe
eingabeText.setText("Fehler");
wert = 0;
}
}
bits = decode(wert);
eingabeText.setText(Float.toString(wert));
eingabeWert = Float.toString(wert);
repaint();
}
public int[] decode(float s) {
int size = 32;
int t = Float.floatToRawIntBits(s);
mantisse = Float.intBitsToFloat((t & 0x007FFFFF) | 0x40000000);
mantisse /= 2;System.out.println("Binär Mantisse: "
+ Integer.toBinaryString(Float.floatToRawIntBits(mantisse)));
exponent = (t & 0x7F800000) >> 23;
vorzeichen = (t & 0x80000000) >> 31;
System.out.println("Mantisse:" + mantisse);
System.out.println("Exponent:" + (exponent - 127));
System.out.println("Vorzeichen:" + vorzeichen);
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & t);
t >>>= 1;
}
return stellen;
}
public int[] decode(double s) {
int size = 64;
long t = Double.doubleToRawLongBits(s);
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (int) (1 & t);
t >>>= 1;
}
return stellen;
}
/**
* Starten der Anwendung als eigenständiges Programm
*
* @param args
*/
public static void main(String[] args) {
// Es wird ein JFrame benötigt, in das das Applet als Komponente
// gesteckt wird.
JFrame f = new JFrame("BinaerFloatApplet-Standalone");
f.add(new block2.BinaerFloatApplet());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setSize(400, 300);
f.setVisible(true);
}
}
- 3860 views
13.1.4 Klasse s1.block2.BinaerCastApplet
13.1.4 Klasse s1.block2.BinaerCastAppletStarten des Applets als Hauptprogramm
Laden Sie die jar Datei Kurs1Applet.jar.
Die Anwendung kann von der Kommandozeile mit dem folgenden Kommando gestartet werden.
java -cp Kurs1Applet.jar s1.block2.BinaerCastApplet
Die Datei Kurs1Applet.jar muss im gleichen Verzeichnis sein
Der Quellcode
package s1.block2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
/**
*
* @author sschneid
* @version 1.2
*
* Copyright (c) 2013, Dr. Stefan Schneider, schneider4me.de
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
public class BinaerCastApplet extends JApplet implements ActionListener {
private JTextField eingabeText;
private JButton enterButton;
private JRadioButton byteButton;
private JRadioButton charButton;
private JRadioButton shortButton;
private JRadioButton intButton;
private JRadioButton longButton;
private JRadioButton vonByteButton;
private JRadioButton vonCharButton;
private JRadioButton vonShortButton;
private JRadioButton vonIntButton;
private JRadioButton vonLongButton;
private int[] vonBits;
private int[] zuBits;
private String eingabeWert = "0";
private String typeTxt = "int";
private String vonTypeTxt = "int";
private String zuString = "0";
private String vonString = "0";
public BinaerCastApplet() {
JPanel buttonPanel;
vonBits = new int[32];
zuBits = new int[32];
// Erzeugen der Buttons und Texteingabefeld
JLabel eingabeLabel = new JLabel("Eingabewert y: ");
eingabeText = new JTextField(eingabeWert);
eingabeText.setPreferredSize(new Dimension(100, 20));
enterButton = new JButton("Enter");
enterButton.addActionListener(this);
byteButton = new JRadioButton("byte");
byteButton.setMnemonic(KeyEvent.VK_B);
charButton = new JRadioButton("char");
charButton.setMnemonic(KeyEvent.VK_C);
shortButton = new JRadioButton("short");
shortButton.setMnemonic(KeyEvent.VK_S);
intButton = new JRadioButton("int");
intButton.setMnemonic(KeyEvent.VK_I);
intButton.setSelected(true);
longButton = new JRadioButton("long");
longButton.setMnemonic(KeyEvent.VK_L);
vonByteButton = new JRadioButton("byte");
vonCharButton = new JRadioButton("char");
vonShortButton = new JRadioButton("short");
vonIntButton = new JRadioButton("int");
vonIntButton.setSelected(true);
vonLongButton = new JRadioButton("long");
ButtonGroup intType = new ButtonGroup();
intType.add(byteButton);
intType.add(charButton);
intType.add(shortButton);
intType.add(intButton);
intType.add(longButton);
JPanel typePanel = new JPanel();
typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.Y_AXIS));
typePanel.add(new JLabel(" x"));
typePanel.add(byteButton);
typePanel.add(charButton);
typePanel.add(shortButton);
typePanel.add(intButton);
typePanel.add(longButton);
ButtonGroup vonIntType = new ButtonGroup();
vonIntType.add(vonByteButton);
vonIntType.add(vonCharButton);
vonIntType.add(vonShortButton);
vonIntType.add(vonIntButton);
vonIntType.add(vonLongButton);
JPanel vonTypePanel = new JPanel();
vonTypePanel.setLayout(new BoxLayout(vonTypePanel, BoxLayout.Y_AXIS));
vonTypePanel.add(new JLabel(" y"));
vonTypePanel.add(vonByteButton);
vonTypePanel.add(vonCharButton);
vonTypePanel.add(vonShortButton);
vonTypePanel.add(vonIntButton);
vonTypePanel.add(vonLongButton);
// Einfügen der drei Komponenten in ein Panel
// Das Gridlayout führt zum Strecken der drei Komponenten
buttonPanel = new JPanel();
buttonPanel.add(eingabeLabel);
buttonPanel.add(eingabeText);
buttonPanel.add(enterButton);
JPanel centerPanel=new JPanel();
centerPanel.setSize(500, 500);
Container myPane = getContentPane();
myPane.add(buttonPanel, BorderLayout.SOUTH);
myPane.add(vonTypePanel, BorderLayout.EAST);
myPane.add(typePanel, BorderLayout.WEST);
myPane.add(centerPanel, BorderLayout.CENTER);
}
@Override
public void paint(Graphics g) {
super.paint(g);
int offSetLinks = 80;
g.setColor(Color.blue);
g.drawString(vonTypeTxt + " y = "+vonString +"; ", offSetLinks, 20);
g.drawString(typeTxt + " x = ("+typeTxt +") y;", offSetLinks, 40);
g.setColor(Color.black);
g.drawString(" y: " + vonString +"; binär:",offSetLinks, 65);
int rows=0;
int cols=0;
for (int i = (vonBits.length - 1); i >= 0; i--) {
if (i == vonBits.length - 1) {
g.setColor(Color.red);
} else {
g.setColor(Color.black);
}
g.drawString(Integer.toString(vonBits[i]),
offSetLinks + 10 * cols,
80+ 15*rows);
if (cols!=15) cols++;
else {cols=0; rows++;}
}
if (cols==8) rows++; // habe ein byte gemalt.
cols=0;
g.drawString(" x: " + zuString +"; binär:",offSetLinks, 85+ 15*rows);
rows++;
for (int i = (zuBits.length - 1); i >= 0; i--) {
if (i == zuBits.length - 1) {
g.setColor(Color.red);
} else {
g.setColor(Color.black);
}
g.drawString(Integer.toString(zuBits[i]),
offSetLinks + 10 * cols,
85+ 15*rows);
if (cols!=15) cols++;
else {cols=0; rows++;}
}
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
try {
eingabeWert = eingabeText.getText();
if (source == enterButton) { //enter Button aufgerufen
if (byteButton.isSelected()) typeTxt = "byte";
if (shortButton.isSelected()) typeTxt = "short";
if (charButton.isSelected()) typeTxt = "char";
if (intButton.isSelected()) typeTxt = "int";
if (longButton.isSelected()) typeTxt = "long";
if (vonByteButton.isSelected()) {
vonTypeTxt = "byte";
byte wert = Byte.parseByte(eingabeWert);
vonBits = decode(wert);
vonString = Byte.toString(wert);
if (byteButton.isSelected()) {
byte zuWert = wert;
zuBits = decode(zuWert);
zuString = Byte.toString(zuWert);
}
if (shortButton.isSelected()) {
short zuWert = wert;
zuBits = decode(zuWert);
zuString = Short.toString(zuWert);
}
if (charButton.isSelected()) {
char zuWert = (char)wert;
zuBits = decode(zuWert);
zuString = "'"+Character.toString(zuWert)+"'";
}
if (intButton.isSelected()) {
int zuWert = wert;
zuBits = decode(zuWert);
zuString = Integer.toString(zuWert);
}
if (longButton.isSelected()) {
long zuWert = wert;
zuBits = decode(zuWert);
zuString = Long.toString(zuWert);
}
}
if (vonShortButton.isSelected()) {
vonTypeTxt = "short";
short wert = Short.parseShort(eingabeWert);
vonBits = decode(wert);
vonString = Short.toString(wert);
if (byteButton.isSelected()) {
byte zuWert = (byte)wert;
zuBits = decode(zuWert);
zuString = Byte.toString(zuWert);
}
if (shortButton.isSelected()) {
short zuWert = wert;
zuBits = decode(zuWert);
zuString = Short.toString(zuWert);
}
if (charButton.isSelected()) {
char zuWert = (char)wert;
zuBits = decode(zuWert);
zuString = "'"+Character.toString(zuWert)+"'";
}
if (intButton.isSelected()) {
int zuWert = wert;
zuBits = decode(zuWert);
zuString = Integer.toString(zuWert);
}
if (longButton.isSelected()) {
long zuWert = wert;
zuBits = decode(zuWert);
zuString = Long.toString(zuWert);
}
}
if (vonCharButton.isSelected()) {
vonTypeTxt = "char";
char wert = eingabeWert.charAt(0);
vonBits = decode(wert);
vonString = "'"+Character.toString(wert)+"'";
if (byteButton.isSelected()) {
byte zuWert = (byte)wert;
zuBits = decode(zuWert);
zuString = Byte.toString(zuWert);
}
if (shortButton.isSelected()) {
short zuWert = (short)wert;
zuBits = decode(zuWert);
zuString = Short.toString(zuWert);
}
if (charButton.isSelected()) {
char zuWert = wert;
zuBits = decode(zuWert);
zuString = "'"+Character.toString(zuWert)+"'";
}
if (intButton.isSelected()) {
int zuWert = wert;
zuBits = decode(zuWert);
zuString = Integer.toString(zuWert);
}
if (longButton.isSelected()) {
long zuWert = wert;
zuBits = decode(zuWert);
zuString = Long.toString(zuWert);
}
}
if (vonIntButton.isSelected()) {
vonTypeTxt = "int";
int wert = Integer.parseInt(eingabeWert);
vonBits = decode(wert);
vonString = Integer.toString(wert);
if (byteButton.isSelected()) {
byte zuWert = (byte)wert;
zuBits = decode(zuWert);
zuString = Byte.toString(zuWert);
}
if (shortButton.isSelected()) {
short zuWert = (short)wert;
zuBits = decode(zuWert);
zuString = Short.toString(zuWert);
}
if (charButton.isSelected()) {
char zuWert = (char)wert;
zuBits = decode(zuWert);
zuString = "'"+Character.toString(zuWert)+"'";
}
if (intButton.isSelected()) {
int zuWert = wert;
zuBits = decode(zuWert);
zuString = Integer.toString(zuWert);
}
if (longButton.isSelected()) {
long zuWert = wert;
zuBits = decode(zuWert);
zuString = Long.toString(zuWert);
}
}
if (vonLongButton.isSelected()) {
vonTypeTxt = "long";
long wert = Long.parseLong(eingabeWert);
vonBits = decode(wert);
vonString = Long.toString(wert);
if (byteButton.isSelected()) {
byte zuWert = (byte)wert;
zuBits = decode(zuWert);
zuString = Byte.toString(zuWert);
}
if (shortButton.isSelected()) {
short zuWert = (short)wert;
zuBits = decode(zuWert);
zuString = Short.toString(zuWert);
}
if (charButton.isSelected()) {
char zuWert = (char)wert;
zuBits = decode(zuWert);
zuString = "'"+Character.toString(zuWert)+"'";
}
if (intButton.isSelected()) {
int zuWert = (int)wert;
zuBits = decode(zuWert);
zuString = Integer.toString(zuWert);
}
if (longButton.isSelected()) {
long zuWert = wert;
zuBits = decode(zuWert);
zuString = Long.toString(zuWert);
}
}
}
} catch (java.lang.NumberFormatException ex) {
// Fehlerbehandlung bei fehlerhafter Eingabe
eingabeWert="Fehler";
eingabeText.setText(eingabeWert);
vonBits = decode((byte)0);
}
repaint();
}
public static int[] decode(short s) {
int size = 16;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
public static int[] decode(byte s) {
int size = 8;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
public static int[] decode(char s) {
return decode((short) s);
}
public static int[] decode(int s) {
int size = 32;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
public static int[] decode(long s) {
int size = 64;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (int) (1 & s);
s >>>= 1;
}
return stellen;
}
/**
* Starten der Anwendung als eigenständiges Programm
*
* @param args
*/
public static void main(String[] args) {
// Es wird ein JFrame benötigt, in das das Applet als Komponente
// gesteckt wird.
JFrame f = new JFrame("BinaerCastApplet-Standalone");
f.add(new block2.BinaerCastApplet());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setSize(400, 300);
f.setVisible(true);
}
}
- 4162 views
13.1.5 Klasse s1.block2.IntShiftApplet
13.1.5 Klasse s1.block2.IntShiftAppletStarten des Applets als Hauptprogramm
Laden Sie die jar Datei Kurs1Applet.jar.
Die Anwendung kann von der Kommandozeile mit dem folgenden Kommando gestartet werden.
java -cp Kurs1Applet.jar s1.block2.IntShiftApplet
Die Datei Kurs1Applet.jar muss im gleichen Verzeichnis sein
Der Quellcode
package s1.block2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
*
* @author sschneid
* @version 1.2
*
* Copyright (c) 2013, Dr. Stefan Schneider, schneider4me.de
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
public class IntShiftApplet extends JApplet implements ActionListener {private JTextField eingabeText;
private JButton enterButton;
private JButton leftShiftNullFillButton;
private JButton rightShiftButton;
private JButton rightShiftNullFillButton;
private JButton maxButton;
private JButton minButton;
private int[] bits;
private String eingabeWert = "1";
private int vorzeichen;
private float mantisse;
private int exponent;
int wert = 1;
public IntShiftApplet() {
JPanel buttonPanel;
bits = new int[32];
bits = decode(1);
// Erzeugen der Buttons und Texteingabefeld
JLabel eingabeLabel = new JLabel("Eingabewert: ");
eingabeText = new JTextField(eingabeWert);
eingabeText.setPreferredSize(new Dimension(100, 20));
enterButton = new JButton("Enter");
enterButton.addActionListener(this);
leftShiftNullFillButton = new JButton("a << 1");
leftShiftNullFillButton.addActionListener(this);
rightShiftButton = new JButton("a >> 1");
rightShiftButton.addActionListener(this);
rightShiftNullFillButton = new JButton("a >>> 1");
rightShiftNullFillButton.addActionListener(this);
maxButton = new JButton("MAX_VALUE");
maxButton.addActionListener(this);
minButton = new JButton("MIN_VALUE");
minButton.addActionListener(this);
JPanel optPanel = new JPanel();
optPanel.setLayout(new BoxLayout(optPanel, BoxLayout.Y_AXIS));
optPanel.add(minButton);
optPanel.add(maxButton);
optPanel.add(leftShiftNullFillButton);
optPanel.add(rightShiftButton);
optPanel.add(rightShiftNullFillButton);
buttonPanel = new JPanel();
buttonPanel.add(eingabeLabel);
buttonPanel.add(eingabeText);
buttonPanel.add(enterButton);
JPanel centerPanel = new JPanel();
centerPanel.setSize(500, 500);
Container myPane = getContentPane();
myPane.add(buttonPanel, BorderLayout.SOUTH);
myPane.add(optPanel, BorderLayout.EAST);
myPane.add(centerPanel, BorderLayout.CENTER);
}
@Override
public void paint(Graphics g) {
super.paint(g);
int hoffSet = 8; // Ziffernbreite
int voffset = 15; // Zeilenabstand
int binaerStart = 80; // Horizontaler Offset für Bitsdarstellung
int dezimalStart = hoffSet * 2;
int vertikal = 25;
g.setColor(Color.blue);
g.drawString("32 Bit Integer", dezimalStart, vertikal);
g.setColor(Color.black);
vertikal += hoffSet*2;
g.drawString("Dezimal: a = "+ eingabeWert, dezimalStart, vertikal);
vertikal += hoffSet*2;
g.drawString("Binär: a =", dezimalStart, vertikal);int rows = 0;
int cols = 0;
for (int i = (bits.length - 1); i >= 0; i--) {
if (i == bits.length - 1) {
g.setColor(Color.red);
} else {
g.setColor(Color.black);
}
g.drawString(Integer.toString(bits[i]),
binaerStart + 10 * cols,
vertikal+ 15*rows);
if (cols != 7) cols++;
else {cols=0; rows++;}
}
}
/**
* Diese Methode decodiert Benutzer
* @param e
*/
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == leftShiftNullFillButton) {
eingabeWert = eingabeText.getText();
wert = wert << 1;
}
if (source == minButton) {
wert = Integer.MIN_VALUE;
}
if (source == maxButton) {
wert = Integer.MAX_VALUE;
}
if (source == rightShiftButton) {
wert = wert >> 1;
}
if (source == rightShiftNullFillButton) {
wert = wert >>> 1;
}
if (source == enterButton) {
try {
eingabeWert = eingabeText.getText();
wert = Integer.parseInt(eingabeWert);
} catch (java.lang.NumberFormatException ex) {
// Fehlerbehandlung bei fehlerhafter Eingabe
eingabeText.setText("Fehler");
wert = 0;
}
}
bits = decode(wert);
eingabeText.setText(Integer.toString(wert));
eingabeWert = Integer.toString(wert);
repaint();
}
public static int[] decode(int s) {
int size = 32;
int[] stellen = new int[size];
for (int i = 0; i < size; i++) {
stellen[i] = (1 & s);
s >>>= 1;
}
return stellen;
}
/**
* Starten der Anwendung als eigenständiges Programm
*
* @param args
*/
public static void main(String[] args) {
// Es wird ein JFrame benötigt, in das das Applet als Komponente
// gesteckt wird.
JFrame f = new JFrame("IntShiftApplet-Standalone");
f.add(new block2.IntShiftApplet());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setSize(400, 300);
f.setVisible(true);
}
}
- 2468 views