Skript: Grundlagen der Programmierung

Skript: Grundlagen der Programmierung

Kursname: Programmierung I (W3WI_108)

Modulbeschreibung International Management for Business and Information Technology

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

  • Darstellung von Algorithmen
  • Erstellen von Quellcode
  • Programmierstil
  • Übersetzen
  • Programmausführung

Aufbau der Programmiersprache

  • Grundstruktur eines Programms
  • Variablen
  • einfache Datentypen
  • Operatoren und Ausdrücke
  • Anweisungen
  • Ablaufsteuerung
  • Kontrollstrukturen
  • strukturierte Datentypen
  • Referenzdatentypen (Felder, Klassen)

Prozedurales und modulares Programmieren:

  • Unterprogramme
  • Funktionen
  • Methoden
  • Rekursion

Grundprinzipien der objektorientierten Programmierung mit 

  • Kapselung
  • Klassen und Objekten
  • Klassenvariablen
  • Instanzvariablen
  • Klassenmethoden
  • Zugriffsrechte
  • Vererbung, Unterklassen
  • Polymorphie

Fragen und Antworten zur Selbstkontrolle

Stefan Schneider Sun, 06/20/2010 - 10:13

1. Einführung

1. Einführung

 Dieser Abschnitt gibt...

  • einen allgemeinen Überblick über Java. 
  • stellt die wichtigsten Programme des JDK vor
  • erklärt das Konzept von Variablen und Zuweisungen.
Stefan Schneider Sun, 06/20/2010 - 11:00

1.1 Buchreferenzen

1.1 Buchreferenzen
 

Duke reading a book

  • 
Grundkurs Programmieren in Java
    • D. Ratz, J. Scheffler, D. Seese & J. Wiesenberger 

    • 
ISBN 978-3446440739, 34.90€
    • 7. Auflage, Hanser 2014, URL Amazon
  • Java-Grundkurs für Wirtschaftsinformatiker: Die Grundlagen verstehen - Objektorientierte Programmierung - Fortgeschrittene Konzepte kennenlernen - Betriebswirtschaftlich orientierte Beispiele
    • Klaus-Georg Deck,
    • Herbert Neuendorf
    • ISBN:978-3-8348-1222-3
    • Broschiert, 456 Seiten
    • Zweite Auflage 26. März 2010
    • URL Amazon
  • Java als erste Programmiersprache: Vom Einsteiger zum Profi
    • Cornelia Heinisch, Frank Müller-Hofmann, Joachim Goll
    • ISBN: 978-3658121174
    • 1140 Seiten
    • 5.te Auflage 2007 URL Amazon
    • 6.te Auflage 2010 URL Amazon
    • 7.te Auflage 2013 URL Amazon
    • 8.te Auflage 2016 URL Amazon
  • UML 2.5: Das umfassende Handbuch.
    • Christoph Kecher
    • ISBN: 978-3836260183
    • 450 Seiten
    • Fünfte Auflage 2017, URL Amazon
  • Java Klassen API

Kostenlose E-Books

 

Stefan Schneider Sun, 06/20/2010 - 11:24

1.2 Algorithmen

1.2 Algorithmen
Vorgehen vom Problem zur AUsführung

Im Diagramm links wird das typische Vorgehensmodell gezeigt welches von einer Problemstellung zur einer ausführbaren Anwendung führt.

  1. Lösungsidee: Der erste Schritt zur Lösung der in der Vorlesung vorgestellten Probleme basiert auf einer Idee zur Lösung des Problems
  2. Algorithmus: Im nächsten Schritt wird die Lösungsidee zu einem Algorithmus verfeinert und ausgearbeitet
  3. Programm: Ein wesentlicher Teil der Vorlesung wird aus der Umsetzung eines Algorithmus in ein ausführbares Java-Programm bestehen

Ü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“
Definition
Algorithmus
  • Ein Algorithmus ist ein generelles, schrittweises, präzises, allgemeingültiges, endliches Lösungsverfahren für eine bestimmte Aufgabenart.
  • Er beruht auf elementaren Verarbeitungsschritten.
  • Alle Aufgaben einer bestimmten Art sind prinzipiell lösbar.

 

Stefan Schneider Wed, 08/21/2013 - 16:36

Beispiel

Beispiel

Das 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:

Screenshot der Kubikwurzelanwendung

Herunterladen der jar Datei mit Demoprogramm.

Starten:

  • Ihr JRE ist korrekt konfiguriert: Doppelklick auf der Datei Kubikwurzel.jar in Ihrem Download Ordner
  • oder: starten im Kommandozeilenfenster im Download Ordner mit dem Befehl java -jar Kubikwurzel.jar

Quellcode der Anwendung auf Github.

Problem

Bestimmen von Kubikwurzeln deren Ergebnis eine ganze Zahl zwischen Null und Hundert ist.

Das Demoprogramm links erlaubt Ihnen Ihre Fähigkeiten zu testen.

 

 

Stefan Schneider Fri, 08/23/2013 - 09:43

Lösungsidee

Lösungsidee

Lö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.

Stefan Schneider Wed, 08/28/2013 - 13:50

Algorithmus

Algorithmus

Vorbedingung

Die vorgebene Zahl k soll eine Kubikwurzel aus den natürlichen Zahlen kleiner, gleich 100 besitzen

Der Algorithmus

Verbale Beschreibung UML Diagramm
  1. Merke Dir Ergebnis e mit dem Wert 1
  2. Bestimme einen Primfaktor f der Zahl k
  3. Multipliziere e mit f und merke das Ergebnis als e
  4. Teile die Zahl k dreimal durch den Primfaktor f.
  5. Merke dieses Ergebnis als k
  6. Wiederhole Algorithmus ab Schritt 2 wenn k größer als 1 ist.
  7. Die Lösung des Problems ist e
UML Flussdiagramm

 

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
  1. Setze f auf 1
  2. Erhöhe f um 1
  3. Ist k geteilt durch f (Ganzzahldivision) mal f = k ?
    1. Wenn ja: Gib f aus
    2. Wenn nein: Ist f kleiner als k ?
      1. Wenn ja: Springe zurück zum Erhöhen
      2. Wenn nein: Gib 1 aus (kein "echter" Primfaktor gefunden

Wie kann man diesen Algorithmus schneller machen?

 

Flussdiagramm Primfaktor

Stefan Schneider Wed, 08/28/2013 - 14:23

Implementierung in Java

Implementierung in Java

 Implementierung 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...

Stefan Schneider Fri, 09/06/2013 - 09:29

1.3 Grundlegende Konzepte

1.3 Grundlegende Konzepte

 Im 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.
Stefan Schneider Thu, 08/09/2012 - 14:37

1.3.1 Grundkonzepte von Programmiersprachen

1.3.1 Grundkonzepte von Programmiersprachen

 Programmiersprachen 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:

Komponenten von Programmiersprachen

Stefan Schneider Thu, 08/09/2012 - 14:57

Anonymous (not verified)

Sun, 10/13/2019 - 15:14

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

Stefan Schneider

Mon, 10/14/2019 - 08:25

In reply to by Anonymous (not verified)

Wurde verbessert. Danke!

1.3.2 Klassen und Objekte

1.3.2 Klassen und Objekte

Bevor 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.

UML Klassen, Objekte

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.

Einfache Klasse mit Erklärung der wichtigsten Syntax 

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
Stefan Schneider Sat, 07/16/2011 - 10:45

1.4 Java Abgrenzung, Begriffsbestimmung

1.4 Java Abgrenzung, Begriffsbestimmung

Java und andere Initiativen

Java ist:

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.
  • 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.
Stefan Schneider Sun, 10/17/2010 - 10:37

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.

 

Stefan Schneider Sun, 08/15/2010 - 13:35

Anonymous (not verified)

Sun, 11/16/2014 - 18:15

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

Stefan Schneider

Sun, 11/16/2014 - 19:14

In reply to by Anonymous (not verified)

Danke. Habe den Fehler korrigiert.

1.4.2 Java Enterprise Edition

1.4.2 Java Enterprise Edition

 Die 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.

Stefan Schneider Sun, 10/17/2010 - 15:03

1.5 Java Historie

1.5 Java Historie
  Freigabe 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!

Stefan Schneider Mon, 09/14/2020 - 16:16

1.6 Programme des Java JDK

1.6 Programme des Java JDK

 Java Standard Edition (SE) in seiner Distribution von Oracle kann in zwei Varianten benutzt werden:

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

JDK 8.0 Dokumentation 

Stefan Schneider Fri, 10/15/2010 - 15:24

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!
Stefan Schneider Thu, 08/26/2010 - 21:22

javac

javac

Die (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
Stefan Schneider Tue, 04/05/2011 - 08:10

1.6.2 Generieren von Schnittstelleninformation

1.6.2 Generieren von Schnittstelleninformation

Schnittstellengenerierung 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.

Stefan Schneider Fri, 10/15/2010 - 16:40

1.6.3 Generieren von Javadokumentation

1.6.3 Generieren von Javadokumentation

1. 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)

 

Stefan Schneider Fri, 10/15/2010 - 16:48

1.6.4 Javaprozess-Monitoring mit jconsole, jinfo, jps

1.6.4 Javaprozess-Monitoring mit jconsole, jinfo, jps

Monitoring 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:


Stefan Schneider Sat, 10/16/2010 - 09:13

1.7 Variablen und Zuweisungen

1.7 Variablen und Zuweisungen

Zur 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:

 Deklaration von Variablen

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:

Syntax Zuweisung
  • Das Gleichzeichen "=" ist in Java ein Operator. Es hat hier eine andere Bedeutung als in der Mathematik. Es handelt sich nicht um eine mathematische Gleichung!
  • Eine Zuweisung wird immer mit einem Semikolon beendet
  • Es ist optional möglich eine Variable in einem Schritt zu deklarieren und ihr gleichzeitig einen Wert zuzuweisen.

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
    1. Zuweisungsoperator =
    2. Semikolon
    3. Variable (links vom Zuweisungsoperator)
    4. 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;
Stefan Schneider Tue, 10/19/2010 - 13:52

1.8 Übungen

1.8 Übungen

Duke als Boxer

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

Duke auf Schaukel

  1. Kopieren Sie den Quellcode des folgenden grafischen Programms DemoFrame in eine Datei mit dem Namen DemoFrame.java.
  2. Übersetzen Sie das Programm in Bytecode
  3. führen Sie das Programm aus
  4.  Ändern Sie durch experimentieren das Programm wie folgt ab:
  • Ändern Sie die den konstanten Text "Input Text:" zu einem anderen Text
  • Ändern Sie die Standardbelegung des Eingabetexts "Input" zu einer anderen Vorbelegung
  • Legen Sie das Ausgabefeld im Fenster über die Eingabezeile mt dem "Button" 

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

Neues Projekt

Sie erhalten dann einen modalen Dialog. Wählen Sie "Java Application" dann den Button "next".

modaler Dialog zur Projekt Erzeugung

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"

Auswahl des Projektnamens

 

 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:

Anlegen eines neuen Pakets

Es erscheint der folgende modale Dialog:

Package erzeugen

 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

Auswahl eines Javapakets

Es erscheint ein modaler Dialog. Tragen Sie hier im Feld "Package Name" den Wert "Airline.Block1" ein.

Bennen des Pakets

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.

Anlegen einer Klasse

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"

Eintragen des Klassennamen

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:

leere Klasse Flugzeug

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.

Klasse mit main Methode in Netbeans Das grüne Dreick hat die Bedeutung der bekannten Starttaste. Diese Klasse kann man als Hauptprogramm ausführen!
Starten einer Klasse Diese Klasse können Sie ausführen indem Sie die Maus über das Symbol der Klasse bewegen
Konsolenausgabe 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:

Konsolenausgabe

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.
Drucke: *** Unser Flughafen ***

 

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
***********************

 

 

Stefan Schneider Sun, 06/20/2010 - 11:08

1.9 Lösungen

1.9 Lösungen

Running Duke

1.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("***********************");

}
}

 

Stefan Schneider Sun, 10/17/2010 - 18:26

1.10 Fragen

1.10 Fragen

Duke grübelt

1.10.1 Javakommandos des JDK

  • Mit welchem Befehl übersetzt man ein Javaquellprogramm in eine Javabinärdatei?
    • Welche Datei ensteht beim Übersetzen einer Datei mit dem Namen Fahrzeug.java die eine Klasse Fahrzeug enhält?
  • Mit welchem Befehl kann man Javabinärdateien ausführen?
  • 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?
  • Nennen Sie mindestens drei Javabefehle die nur das JDK enthält nicht aber das JRE
  • Welcher Typ von Dateien wird vom Befehl javadoc erzeugt?

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;
x = 5;
x = 2 * x;
y= z * x;
z= 18;

2

 

3

 

4

Endergebnis ? ? ?
Stefan Schneider Sun, 09/25/2011 - 10:50

Anonymous (not verified)

Fri, 10/11/2019 - 15:01

Es fehlt das Semikolon. In der Lösung ist das Semikolon wieder vorhanden.

Anonymous (not verified)

Fri, 10/15/2021 - 20:45

Werden hier in der Frage das Wort Zuweisungen und Anweisungen als Synonym verwendet? Denn im Kapitel 1.7 ist nur von Zuweisungen die Rede.

Die Ergebnisse von Anweisungen werden auf Variablen zugewiesen.

1.11 Antworten

1.11 Antworten

1.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?
    1. Variable
    2. Zuweisungsoperator =
    3. Term
    4. 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

 

 

Stefan Schneider Sun, 09/25/2011 - 13:30

1.12 Lernziele

1.12 Lernziele

Am Ende dieses Blocks können Sie:

  • ... die wichigsten Eigenschaften eines Algorithmus erklären
  • ... die wichtigsten Programme des JRE und JDKs benutzen
    • Sie können erklären welche Typen von Dateien von den Programmen des JDKs gelesen und erzeugt werden.
  • ... ein einfaches Javaprogramm editieren, übersetzen und ausführen lassen.
  • ... die Unterschiede zwischen den Javaeditionen JME, JSE, JEE nennen.
  • ... das Konzept einer Variablen erklären und anwenden.
  • ... die Syntax zur Zuweisung eines Wertes auf eine Variable erklären.
  • ... bei nacheinander erfolgenden Zuweisungen zu verstehen welche Variable welchen Wert zu einem bestimmten Zeitpunkt hat.
  • ... zwischen Klassen und Objekten unterscheiden und deren Beziehung erklären.

Lernzielkontrolle

Sie sind in der Lage die Fragen zur Einführung zu beantworten.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Thu, 08/09/2012 - 15:55

2. Typen, Operationen und Zuweisungen

2. Typen, Operationen und Zuweisungen

Dieser Unterrichtsblock beschäftigt sich mit den Grundlagen von Variablen und ihren Belegungen.

Motivation zu den Themen dieses Blocks

Am Ende dieses Blocks haben Sie ein Grundverständnis von

  • Schlüsselwörter: Vorbelegte Worten in der Sprache Java
  • Variablen: Den Behältern von Werten
  • Zuweisungen: Das Belegen dieser Behälter mit Werten
  • Typen: Die unterschiedlichen Arten von Informationsbehältern
  • Literalen: Die textuelle Beschreibung von Werten
  • Operatoren: Die Verknüpfung von Werten um Berechnungen vorzunehmen
Stefan Schneider Sun, 06/20/2010 - 14:52

2.1 Schlüsselwörter, Literale, Variablennamen, Kommentare

2.1 Schlüsselwörter, Literale, Variablennamen, Kommentare

Motivation

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:

Diagramm Schlüsselworte

 

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:

  1. Gültige Namen dürfen nur aus Buchstaben, Ziffern, und den Zeichen "_" und "$" bestehen. Sie dürfen beliebig lang sein.
  2. Das erste Zeichen muss ein Buchstabe, "_" oder  "$" Zeichen sein.
  3. Leerzeichen sind nicht erlaubt
  4. Schlüsselworte der Sprache Java dürfen nicht verwendet werden. (Boolsche Literale und das null Literal dürfen auch nicht verwendet werden)
  5. 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.

Literale
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)
  • Gleitkommazahlen,
    • dargestellt im vertrauten Dezimalsystem
      • Nachkommateil durch Punkt abgetrennt
        • Beispiel: 5.4, 6. (stellt Gleitkommazahl 6.0 dar)
    • Zehnerpotenzen zur Basis Zehn werden mit dem Buchstaben e oder E bezeichnet
      • Beispiel: 4E-2 stellt 4*10-2= 0.04 da.

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
*/

 

Stefan Schneider Sun, 07/04/2010 - 17:24

2.2 Datentypen und Wertebereiche

2.2 Datentypen und Wertebereiche

Die 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:

Klassifikation der Javatypen

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.

Stefan Schneider Sun, 06/20/2010 - 16:43

2.2.1 Primitive Javatypen

2.2.1 Primitive Javatypen

Ganzzahlige Datentypen

Ganzzahlige Datentypen
Datentyp Bits Wertebereich Wertebereiche Konstante min. Konstante max.
byte 8=1 byte -27 bis 27-1 -128 bis +127

Byte.MIN_VALUE

Byte.MAX_VALUE

short 16=2 byte -215 bis 215-1 -32768 bis +32767

Short.MIN_VALUE

Short.MAX_VALUE

char 16=2 byte 0 bis 216-1 0 bis +65535 (Sonderfall!)

Character.MIN_VALUE

Character.MAX_VALUE

int 32=4 byte -231bis 231-1 -2147483648 bis +2147483647

Integer.MIN_VALUE

Integer.MAX_VALUE

long 64=8 byte -263 bis 263-1 −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807

Long.MIN_VALUE

Long.MAX_VALUE

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.

Hinweis

Der 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:

Screen shot BinaerIntApplet

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

Fließkomma-Standarddatentypen
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 Exponent ist um 127 verschoben um auch negative Zahlen darstellen zu können.
  • Bei der Mantisse wurde im Diagramm eine symbolische (rote) Eins eingefügt, die nicht Teil der 32 Bit Muster ist. Jede Mantisse muss mit einer führenden 1 beginnen damit sie normalisiert ist. Da dieser Wert immer konstant ist muss man ihn nicht extra im 32 bit Wort speichern.

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

  • NaN: Not a Number (z,Bsp das Ergebnis der Wurzel von -1
  • positiv, negativ unendlich
  • Größter Wert
  • Kleinster Wert der noch größer als Null ist

 

 

 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:

Screenshot BinaerFloatApplet

Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole

Wahrheitswerte

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.

Zeichen
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

 

Stefan Schneider Sat, 10/29/2011 - 14:46

Anonymous (not verified)

Sun, 12/16/2012 - 13:21

Wieso werden für den Datentyp boolean 8 Bits benötigt? Eigentlich würde doch 1 Bit ausreichen.

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.

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.

Definition
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:

Syntax eines Aufzählungstyps

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.

Stefan Schneider Sat, 10/29/2011 - 14:48

2.2.3 Typkonversion bei Zuweisung verschiedener Typen

2.2.3 Typkonversion bei Zuweisung verschiedener Typen

Die 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:

doublefloatlongintshort/char byte

  • Alle Zuweisungen von bytedouble 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:

  • Was geschieht bei der Typkonversion mit negativen Werten (siehe rotes Vorzeichenbit)?
  • Was geschieht bei der Zuweisung von großen Typen auf kleine Typen? Was geschieht mit den Vorzeichen?
  • Was geschieht wenn Zeichen (Typ char) auf andere Typen zugewiesen werden?

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: 

Screenshot BinearCastApplet

 

Quellcode des Applets und Anleitung zum Starten als Javaprogramm von der Konsole

Stefan Schneider Sat, 10/29/2011 - 15:25

Anonymous (not verified)

Sun, 12/16/2012 - 13:33

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?

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

  1. das Zahlenintervall des Typs float größer als das des Typ long ist. Es gibt also keinen Überlauf an den Grenzen der Zahlenintervalle
  2. Wer mit dem Typ float arbeitet muss wissen, das hier immer gerundet werden kann.

Anonymous (not verified)

Sun, 12/16/2012 - 13:41

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.

2.3 Operatoren und Ausdrücke

2.3 Operatoren und Ausdrücke

Ausdrü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:

  • Vorzeichenoperator: -a
  • Postinkrement: a++
  • Negation: !a
Unärer Operator

 

Binäre Operatoren haben zwei Operanden. Beispiele sind:

  • Addition:  a + b
  • logischer Vergleich: a ==  b
Binaerer Operator

Arithmetische Operatoren

Die arithmetischen Operatoren können auf die folgenden Typen angewendet werden

  • byte
  • short
  • int
  • long
  • float
  • double
Zweistellige arithmetische Operatoren
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

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

Vergleichsoperatoren
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.

Logische Operatoren
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.

Bitoperatoren
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

result = a & b
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
result = a | b
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 

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:

 Bild des IntShiftApplet

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


 
x = 4; shift = 1 result = 8
x = 4; shift = 1 result = 2
x = 4; shift = 1 result = 1
x = 4; shift = 1 result = 0
x = 4; shift = 1 result = 0

Die interne Darstellung der verwendeten Werte:

Binäre Darstellung
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
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:

  1. Teilausdrücke in runden Klammern werden wie in der Mathematik als erstes ausgewertet
  2. Ausdrücke mit unären Operatoren werden anschließend ausgewertet
  3. 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!

Ausführungsreihenfolge von Operatoren
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.

Definition
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.

linksassoziative Operanden

Einige Operatoren in Java sind rechtsassoziativ. Ein Beispiel hierfür ist der Zuweisungsoperator

Bewertungsreihenfolge der Operanden eines Operators

Definition
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:

  1. Auswertung des Subtrahenden (und Zwischenspeicherung)
  2. Dekrement von i
  3. Auswertung des Minuend und Berechnung der Differenz
  4. 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:

  1. Ist einer der Typen ein double, so wird das Ergebnis zum Typ double konvertiert.
  2. Falls nicht, wird das Ergebnis zu einem float Typen konvertiert wenn ein Typ ein float Typ ist.
  3. Falls nicht, wird das Ergebnis zu einem long Typen konvertiert wenn ein Typ ein long Typ ist.
  4. Falls nicht, werden beide Operanden zuerst zu einem int Typen konvertiert.

 

Stefan Schneider Tue, 07/06/2010 - 09:48

Stefan Schneider

Sun, 12/16/2012 - 14:54

In reply to by Anonymous (not verified)

Danke. Ich habe den Fehler korrigiert.

Anonymous (not verified)

Sun, 12/16/2012 - 13:53

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?

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.

Anonymous (not verified)

Wed, 01/16/2013 - 13:35

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

Anonymous (not verified)

Tue, 01/22/2013 - 16:39

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?

Stefan Schneider

Tue, 01/22/2013 - 20:22

In reply to by Anonymous (not verified)

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.

Anonymous (not verified)

Fri, 10/11/2019 - 11:24

Bei den unären arithmetischen Operatoren steht "Präidekrement" statt "Prädekrement".

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?

2.4 Übungen

2.4 Übungen

Duke als Boxer

2.4.1 Übung: Zuweisungen und Typen 

Erstellen Sie folgende Java-Applikation (Name "Calc"):
  • Deklarieren Sie in der main() Routine Variablen mit unterschiedlichen Datentypen (z.B. int, longshort, float).
  • Initialisieren Sie die Variablen mit geeigneten Werten.
  • Führen Sie mit den Variablen folgende arithmetischen Operationen aus:
    • Addition einer Konstanten zu einer int-Variablen,
    • Multiplikation einer int-Variablen mit einer Konstanten,
    • Subtraktion zweier Variablen unterschiedlichen Datentyps (int, short),
    • Division einer int-Variablen durch eine Konstante,
    • Multiplikation zweier Variablen (davon eine vom Typ "float").
Geben Sie die Variablenwerte jeweils aus.
Zu beantworten:
  • 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?

Beispiellösung

2.4.2 Übung: Erlaubte Variablennamen

Welche der folgenden Symbole sind gültige Variablennamen in Java?
Begründen Sie Ihre Antwort.
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

Gegeben seien folgende Variablendeklarationen in Java:
long a = 3;
int b = 4;
short c = 5;
byte d = 6;
Welche der folgenden Zuweisungen sind in Java erlaubt?
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

Schreiben Sie ein Java-Programm, das eine Anzahl von Sekunden einliest und in
die Anzahl von Stunden, Minuten und Sekunden umrechnet.
Testen Sie Ihr Programm mit vernünftigen Eingabewerten, z. B. mit 0, 59, 60,
100, 3600 und 4000.
 
Beispiel: Die Eingabe 1234 soll zur Ausgabe von 0 : 20 : 34 führen.

2.4.8 Übung: Polynomberechnung

Schreiben Sie ein Javaprogramm, das die Koeffizienten a, b, c, d sowie den Wert x des Polynoms
y = a * x3  + b * x2  + c * x + d
einliest und das Ergebnis y ausgibt.

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
Schreiben Sie ein Java-Progamm, das ∏ mit dieser Formel annähert.
Bemerkung: Die Reihe konvergiert nur seht langsam gegen ∏ .

2.4.11 Übung: Analoge Uhr

Implementieren sie notwendigen Berechnungen zum Anzeigen von Sekunden-, Minuten, Stundenzeigern einer analogen Uhr.

Vorbereitungen

  1. Erzeugen Sie in einem Verzeichnis die beiden Quelldateien Uhr.java und Zeiger.java. Der Quellcode der beiden Dateien ist weiter unten aufgeführt.
  2. Übersetzen Sie die beiden Dateien in diesem Verzeichnis mit dem Befehl javac Uhr.java Zeiger.java
  3. Führen Sie das Programm aus: java Uhr
  4. 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:

  1. 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
  2. Finden sie in der Klasse Zeiger.java die Methoden sekundeX und sekundeY
  3. Ersetzen Sie in der Trivialimplementierung die Zuweisung xs=s; bzw ys=s; durch die korrekte Implementierung der beiden mathematischen Ausdrücke
  4. Sichern Sie die Datei Zeiger.java
  5. Übersetzen Sie die Datei Zeiger.java
  6. Testen Sie das Ergebnis mit dem Kommando: java Uhr
  7. Wiederholen Sie Schritt 3-6 bis Sie das gewünschte Ergebnis haben.
  8. Implementieren Sie den Minuten und Stundenzeiger sinngemäß.

Beispiele der Ausgangssituation und der korrekten Implementierung: 

Ausgangssituation Ziel der Übung
analoge Uhr, initial Zustand Analoge Uhr als Lösung nit korrekten Zeigern

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("***********************");

}
}

 

Stefan Schneider Sun, 06/20/2010 - 14:56

2.5 Lösungen

2.5 Lösungen

2.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());
}
}

 

Stefan Schneider Sun, 06/20/2010 - 15:58

2.6 Webcast: Java Variablen, Zuweisungen und Operatoren

2.6 Webcast: Java Variablen, Zuweisungen und Operatoren

Ein 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
Stefan Schneider Fri, 10/29/2010 - 15:48

Anonymous (not verified)

Wed, 03/20/2013 - 12:21

Sind mitarbeiterID und name nicht Objektattribute? Klassenattribute werden doch normalerweise mit static implementiert?

Richtig beobachtet. Das Beispiel ist insofern unglücklich, als das es nichts von der Klasse Mitarbeiter nutzt.

2.7 Fragen

2.7 Fragen

Duke grübelt

2.7.1 Ganzzahlarithmetik

Ganzzahloperationen 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

.
.
a.)
b.)
c.)

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)");}

}
}

 

Stefan Schneider Mon, 02/28/2011 - 17:14

2.8 Antworten

2.8 Antworten

2.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

.
.
a.)
b.)
c.)

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.

 

Stefan Schneider Tue, 03/01/2011 - 08:28

2.9 Lernziele

2.9 Lernziele

Am Ende dieses Blocks können Sie:

  • ....die Begriffe Schlüsselwort, Variable, Zuweisung, Typ, Literal, Operator im Javakontext erkennen, unterscheiden, erklären und anwenden.
  • ...Javaschlüsselworte erkennen und etwa 30 nennen (Dieses Lernziel soll erst am Ende der ersten Vorlesung erreicht sein)
  • ...erlaubte Namen in Java und Schlüsselworte unterscheiden.
  • ...den Unterschied zwichen Literalen und Namen erklären.
  • ...drei Arten von Kommentaren benutzen.
  • ...die unterschiedlichen primitiven Datentypen kennen und Ihren Werte aus dem Format herleiten.
  • ...wichtige Unterschiede in der Ganzzahlarithmetik und der Fließkommaarithmetik erklären
  • ...sichere von unsicheren Zuweisungen unterscheiden
  • ...erklären warum String kein primitiver Datentyp ist
  • ...ein Beispiel für einen Aufzählungstyp nennen
  • ...die Notwendigkeit von expliziten Typkonversionen nennen und das damit verbundene Risiko abschätzen
  • ...je einen unären, einen binären und einen ternären Operator nennen
  • ...die Auswertereihenfolge eines Terms beschreiben
  • ...mit Post- und Präinkrementoperatoren umgehen

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu Typen, Operationen und Zuweisungen zu beantworten.

Interessante Fragen aus den Übungen:

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Thu, 08/16/2012 - 16:34

Anonymous (not verified)

Sun, 11/23/2014 - 14:08

Was genau meinen Sie mit "...wichtige Unterschiede in der Ganzzahlarithmetik und der Fließkommaarithmetik erklären"? Können Sie dies bitte etwas genauer erläutern?

lG

Stefan Schneider

Sun, 11/23/2014 - 16:00

In reply to by Anonymous (not verified)

 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.

3. Ablaufsteuerung, Kontrollstrukturen

3. Ablaufsteuerung, Kontrollstrukturen Stefan Schneider Sun, 06/20/2010 - 15:51

3.1 Verzweigungen und Vergleichsoperationen

3.1 Verzweigungen und Vergleichsoperationen

Durch 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 ");
else-Regel

 

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");
}
Switch Anweisung in UML

 

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");
}
Switch Anweisung mit komplexen break statements

 

 

Stefan Schneider Sun, 07/11/2010 - 16:26

Anonymous (not verified)

Tue, 11/18/2014 - 14:15

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

Anonymous (not verified)

Mon, 12/12/2016 - 18:29

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ß.

Anonymous (not verified)

Fri, 10/29/2021 - 19:07

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.

3.2 Schleifen und Felder

3.2 Schleifen und Felder

Schleifen 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++;
}

UML Diagramm while Schleife

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.

UM Diagramm do while Schleife

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.

einfache for-Schleife

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:

  • break: die weitere Abarbeitung im Schleifenblock/rumpf wird abgebrochen. Das Programm verlässt die Schleife und nimmt die Abarbeitung hinter der Schleife wieder auf. die Abbruchkriterien der Schleife werden hier nicht mehr beachtet.
  • continue: die Abarbeitung des aktuellen Schleifendurchlaufs wird beendet. Die Schleife wird jedoch nicht verlassen. Die nächste Iteration wird geplant ausgeführt

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.

for mit continue Befehl
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.

for mit break Befehl

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?

Apple Hauptquartier

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".

Stefan Schneider Sun, 07/11/2010 - 16:28

Anonymous (not verified)

Mon, 11/24/2014 - 19:29

"int i = 10
while (i > 5 ) i++;"

fehlen nach der Bedingung nicht die { }?
Also {i++;} ?

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; ]

Anonymous (not verified)

Mon, 11/09/2015 - 19:18

Im Beispiel einer einfachen for Schleife steht, dass sie b mal durchlaufen wird, jedoch ist die Bedingung der for Schleife i

Stefan Schneider

Thu, 11/12/2015 - 18:26

In reply to by Anonymous (not verified)

Gute Überlegung.
Die Variable i dient zum Schleifenabruch und wird bei jedem Durchlauf imkrementiert. Sie startet bei 1 und endet bei b. Das sollte schon hinkommen.

Anonymous (not verified)

Fri, 11/15/2019 - 15:35

Ich glaube es fehlt bei der while, do-while und for Schleife immer ein Semikolon hinter Anweisung(en).
while (Bedingung) {Anweisung(en) ; }
do {Anweisung(en) ; } while (Bedingung);
for (Initialisierung; Bedingung; Veränderung) {Anweisung(en) ; }

Stefan Schneider

Mon, 11/18/2019 - 16:56

In reply to by Anonymous (not verified)

habe ich verbessert.

3.2.1 Schleifentransformationen

3.2.1 Schleifentransformationen

Die 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 UML DIagramm do-While Schleife While Schleife

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 UML DIagramm While Schleife do While Schleife

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.

Stefan Schneider Fri, 11/11/2016 - 19:14

Anonymous (not verified)

Sun, 12/11/2016 - 16:02

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!

3.3 Übungen

3.3 Übungen

Duke als Boxer

3.3.1 Übung: Schleifenterminierung

Welche der folgenden Schleifen terminieren?
    int i = 1, j = 1;
    do {
      i = i + j;
      j++;
    } while (i < 200);
b)  
    int i = 1, j = 20;
    while (i + j < i) {
      i = i + 2;
      j--;
    }
c)  
    int i = 1, j = 20;
    while (i + j > i) {
      i = i + 2;
      j--;
    }
d)  
    int i = 100, j = 27;
    while (i != j) {
      i = i / 2;
      j = j / 3;
    }

3.3.2 Übung: Ziffern einer Zahl

Schreiben Sie ein Java-Programm, das eine positive ganze Zahl n einliest und
feststellt, aus wie vielen Ziffern sie besteht.

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

Schreiben Sie ein Java-Programm, das eine Zahlenfolge liest und ihren
größten und kleinsten Wert sowie ihren Mittelwert berechnet und ausgibt.

Tipp: Die Wertebereiche für die Zahlen sind Ihnen freigestellt. Der Durchschnittswert sollte korrekt sein

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

Schreiben Sie ein Java-Programm, das eine positive ganze Zahl n einliest und in ihre Primfaktoren zerlegt.
Beispiel:
  • 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

Vereinfachen Sie folgende Codestücke:
 
a)  
// Annahme: j >= 0
    i = 0;
    while (i != j) i++;
b)  
  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:

Diagramm zu Schneiden zweier Linien

Hinweis: Wenn es einen Schnittpunkt gibt ist er (c,b)

3.3.10 Übung: Codevereinfachung

Vereinfachen Sie folgende Codestücke:

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:

Weckeruhr im normalen Zustand

Uhr im normalen Zustand

Weckeruhr beim Klingeln

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));
   }
}
Stefan Schneider Sun, 07/11/2010 - 16:30

Anonymous (not verified)

Thu, 11/09/2017 - 12:11

die Zahl 252 aus den Primfaktoren 3, 3, 4, 7.
4 ist keine Primzahl hier müsste 2x die 2 hin

3.4 Lösungen

3.4 Lösungen

3.4.1 Schleifenterminierung

Welche der folgenden Schleifen terminieren?
 
a)  Terminiert
    int i = 1, j = 1;
    do {
      i = i + j;
      j++;
    } while (i < 200);
b)  Terminiert: Schleife wird nicht durchlaufen
    int i = 1, j = 20;
    while (i + j < i) {
      i = i + 2;
      j--;
    }
c)  Terminiert
    int i = 1, j = 20;
    while (i + j > i) {
      i = i + 2;
      j--;
    }
d)  Terminiert
    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;
b)  
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)

Wahrheitstafel
  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,
                             31, 31, 30, 31, 30, 13};

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;
    }

}

 

Stefan Schneider Sun, 07/11/2010 - 16:32

3.5 Lernziele

3.5 Lernziele

Am Ende dieses Blocks können Sie:

  • ... Befehlsblöcke im Javaquellcode erkennen und erstellen
  • ... die allgemeine Syntax von if, while, do while und for Kommandos und Schleifen nennen
  • ... in einer mehrfach, geschachtelten if Anweisung einen else Block der zugehörigen if-Bedingung zuordnen
  • ... die Typen nennen die man bei Mehrfachverzweigungen benutzen kann
  • ... den Ablauf einer Mehrfachverzweigung mit case, break und default Schlüsselwörten analysieren
  • ... for Schleifen in while bzw. do while Schleifen transformieren
  • ... die Syntax einer for-Schleife beschreiben

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zur Ablaufsteuerung und die Frage aus der Übung 3.3.1 Schleifenterminierung zu beantworten.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Sat, 08/18/2012 - 13:02

4. Prozedurales, modulares Programmieren, Unterprogramme, Funktionen, Methoden

4. Prozedurales, modulares Programmieren, Unterprogramme, Funktionen, Methoden Stefan Schneider Sat, 07/24/2010 - 17:30

4.1 Methoden

4.1 Methoden

Methoden

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

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!

Definition
Formalparameter von Methoden
Die in der Deklaration verwendeten Parameter einer Methode werden Formalparameter genannt

 

Definition
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.

Definition

Ü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.

 

Stefan Schneider Fri, 07/30/2010 - 17:03

Anonymous (not verified)

Tue, 11/17/2015 - 11:02

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?

Stefan Schneider

Wed, 11/18/2015 - 11:42

In reply to by Anonymous (not verified)

Vielen Dank,
der Fehler wurde gerichtet und der dazugehörende Abschnitt etwas umformuliert.

Anonymous (not verified)

Tue, 12/19/2017 - 18:51

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.

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:

Stefan Schneider Sat, 08/07/2010 - 14:45

Anonymous (not verified)

Sat, 12/09/2017 - 12:39

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

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();
}
}

 

Stefan Schneider Sat, 08/21/2010 - 16:55

4.4 Iteration und Rekursion

4.4 Iteration und Rekursion

In 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
von fib(0) bis fib(10):
fib(0)= 0
fib(1)= 1
fib(2)= 1
fib(3)= 2
fib(4)= 3
fib(5)= 5
fib(6)= 8
fib(7)= 13
fib(8)= 21
fib(9)= 34
fib(10)= 55 

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. Ablauffolge Methodenaufrufe

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 Hanoi

Die Türme von Hanoi sind ein einfaches Solitärspiel bei dem die folgenden Regeln gelten:

  • Es gibt drei Stapel.
  • Ausgangssituation: Auf dem ersten Stapel sind alle Scheiben zu einer Pyramide aufgetürmt.
  • Es müssen immer kleinere Scheiben auf größeren Scheiben liegen
  • Es darf immer nur eine Scheibe von einem Stapel zu einem anderen bewegt werden
  • Alle Scheiben sollen auf einen Zielstapel bewegt werden

Siehe Wikipedia für weiterführende Erklärung und Animation.

Die Strategie

  • Sind Null Scheiben zu bewegen muss nichts getan werden (trivial)
  • Sind n Scheiben (mit n>0) vom Startstapel zum Zielstapel bewegen, so sind die folgenden Schritte auszuführen:
    • Bewege n-1 Scheiben vom Startstapel zum Hilfsstapel
    • Bewege (verbleibende) Scheibe vom Startstapel zum Zielstapel
    • Bewege n-1 Scheiben vom Hilfsstapel zum Zielstapel

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

Konsolenausgabe

1.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

 Rejursive Methodenaufrufe

 

Stefan Schneider Sat, 08/21/2010 - 16:58

4.5 Übungen

4.5 Übungen

Duke als Boxer

4.5.1 Übung: Arithmetikmethoden

Implementieren 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:

  • add: Addition
  • sub: Subtraktion
  • mul: Multiplikation
  • div: Division

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: 

  1. Nutzen Sie den Satz von Heron
  2. 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);
}
}
}
 

 

Stefan Schneider Sat, 07/24/2010 - 17:32

4.6 Lösungen

4.6 Lösungen

4.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;
}


 

Anonymous (not verified) Tue, 11/08/2011 - 15:44

4.7 Lernziele

4.7 Lernziele

Am Ende dieses Blocks können Sie:

  • .... in Java Methoden erkennen, erklären und anwenden.
    • Sie können die verschiedenen syntaktischen Bestandteile nennen und beschreiben
  • ...formale von aktuellen Parametern unterscheiden
  • ...wissen Sie wann Sie Methoden zur Programmstruktierung einsetzen
  • ...den Geltungsbereich und die Geltungsdauer von Variablen in Blöcken und Methoden bestimmen
  • ...können das Konzept eines Programmstacks (Programmstapel) im Kontext von Methoden und Variablen erklären
  • ...kennen semantischen Möglichkeiten die Ihnen Konstruktoren bieten
  • ...können die syntakischen Bestandteile von Konstruktoren nennen und den Unterschied zu gewöhnlichen Methoden eklären
  • ...sind in der Lage zu erkennen ob ein Javaimplementierung auf einem rekursiven oder iterativen Ansatz beruht

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu Methoden zu beantworten und die Übung 4.5.4 (Fragen) zu lösen.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Thu, 09/20/2012 - 13:02

5. Konzepte objektorientierter Programmierung

5. Konzepte objektorientierter Programmierung

Duke in 3D

Die 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.

 

Stefan Schneider Mon, 08/23/2010 - 19:42

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.

Stefan Schneider Mon, 08/23/2010 - 19:39

5.1.1 Datenkapselung

5.1.1 Datenkapselung

Das erste Konzept, die Datenkapselung, kann man als eine technische Weiterentwicklung der abstrakten Datentypen sehen.

  1. Primitive Datentypen (z.Bsp. int x = 10;)
  2. Strukturierte Datentypen wie Felder oder struct in der Programmiersprache C erlauben mehrere Typen in einer Struktur zusammenzufassen.
  3. 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

  • Eine Klasse sollte nur die Attribute und Methoden veröffentlichen die externe Konsumenten unbedingt brauchen
  • Man sollte als Standard alle Attribute als private deklarieren
  • Man sollte als Standard Zugriff auf Attribute immer nur indirekt über öffentliche Methoden (public) gewährleisten

 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;}
}
getter und setter Methoden

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.

Stefan Schneider Fri, 10/15/2010 - 09:15

5.1.2 Architekturprinzipien

5.1.2 Architekturprinzipien

Trennung 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)

Entwurfsmuster "Model View Controller" (MVC)

MVC ist ein Entwurfmuster welches aus den folgenden drei Einheiten besteht:

  • Model (Datenmodell) Alle Aspekte die die Daten betreffen (Integrität, Speicherung, Konvertierung etc.)
  • View (Präsentation): typischerweise die Benutzerschnittstelle einer Anwendung mit allen graphischen und interaktiven Komponenten
  • Controller(Programmsteuerung): Die Ablauflogik und Steuerung eines Programmes
MVC pattern

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.

Stefan Schneider Fri, 10/15/2010 - 13:17

5.1.3 Entwurfsmuster (Design Patterns)

5.1.3 Entwurfsmuster (Design Patterns)

Duke mit Blueprint

Nach Wikipedia:

Entwurfsmuster

Entwurfsmuster (engl. design patterns) sind bewährte Lösungs-Schablonen für wiederkehrende Entwurfsprobleme in der Softwarearchitektur und -entwicklung.

Sie stellen damit eine wiederverwendbare  Vorlage zur Problemlösung dar, die in einem bestimmten Zusammenhang einsetzbar sind

Referenzen

Im zweiten Semester werden die Entwurfsmuster

benutzt.

In der Vorlesung des ersten Semesters werden einige wenige, ausgewählte Entwurfsmuster vorgestellt:

Stefan Schneider Tue, 03/29/2011 - 20:53

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. 

Verwendung

Eine Fabrikmethode

  • erzeugt bei jedem Aufruf neue Objekte
  • unterbindet den direkten Zugriff auf die Konstruktoren einer Klasse

Eine Fabrikmethode (Factory) besteht typischer Weise aus

  • einem privaten Konstruktor
  • einer öffentlichen statischen Methode die den privaten Konstruktor nutzt

Kategorie

Eine 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;
    }}

 

Stefan Schneider Sat, 12/15/2018 - 13:56

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

Verwendung

Ein Einzelstück

  • verwaltet genau ein eine Klasse mit genau einem Objekt
  • unterbindet die Erzeugung von anderen Objekten einer Klasse
  • erlaubt einfachen Zugriff auf ein solches Objekt

Ein Singleton (Einzelstück) implementiert eine ähnliche Struktur wie eine globale Variable.

Kategorie

Das 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.

 

Stefan Schneider Tue, 03/29/2011 - 20:56

5.2 Objektorientierung in Java

5.2 Objektorientierung in Java

Duke in 3D

Stefan Schneider Tue, 08/24/2010 - 17:57

5.2.1 Javaklassen und -objekte

5.2.1 Javaklassen und -objekte

Bisher 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 wagen1wagen2 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.

Instanzen

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).

Stefan Schneider Tue, 08/24/2010 - 17:13

Anonymous (not verified)

Thu, 11/26/2015 - 13:04

Hier wird einmal der wagen1 als Objektvariable benannt und später in der Klasse Kraftwagen das Attribut nummernschild.
Was ist also eine Objektvariable?

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.

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.

Anonymous (not verified)

Tue, 12/15/2015 - 22:26

Wieso macht es Sinn eine Referenzvariable auf das gleiche Objekt zeigen zu lassen ?

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.

5.2.2 this Referenz

5.2.2 this Referenz

Java 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.

Stefan Schneider Sat, 09/11/2010 - 15:08

Anonymous (not verified)

Sat, 12/19/2015 - 12:22

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 ?

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

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.

Stefan Schneider Sat, 09/11/2010 - 15:11

5.2.4 Ablauf der Initialisierung einer Klasse

5.2.4 Ablauf der Initialisierung einer Klasse

 Begriffsbestimmung:

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:

  1. Die Referenzvariable p2 wird angelegt und mit dem Wert null belegt
  2. Der new Operator der Klasse Punkt wird aufgerufen. Auf dem Java-Heap wird der Platz für ein Objekt der Klasse Punkt zur Verfügung gestellt. Es wird instanziiert.
  3. Die Datenfelder des Objekts werden initialisiert. Dies geschieht je nach Typ mit den Belegungen: null, false, 0, 0f etc.
  4. Der Methodenblock des entsprechenden Konstruktors wird ausgeführt.
  5. Der new Operator gibt die Referenz auf das Objekt zurück welches in p2 gespeichert wird.
Stefan Schneider Sat, 09/11/2010 - 15:12

5.3 Pakete (Java Packages) im Überblick

5.3 Pakete (Java Packages) im Überblick

Duke mit Paketen

Javapakete (englisch packages) erlauben das Zusammenfassen von Klassen und "Interfaces" (Schnittstellen) in logischen Gruppen.

Das Gruppieren von Klassen und Schnittstellen (Interfaces) in Pakete erlaubt:

  • Bildung von eigenen Namensräumen für Klassen und Schnittstellen (Interfaces)
  • Definierter Export von Klassen, bzw. "Information Hiding" von Klassen die nicht exportiert werden sollen
  • Definierter Import von Klassen von Fremdpaketen in eigene Klassen

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...

 

Stefan Schneider Fri, 09/13/2019 - 15:56

5.3.1 Pakete (Java Packages) im Detail

5.3.1 Pakete (Java Packages) im Detail

Duke mit Paketen

Javapakete (englisch packages) erlauben das Zusammenfassen von Klassen und "Interfaces" (Schnittstellen) in logischen Gruppen.

Das Gruppieren von Klassen und Schnittstellen (Interfaces) in Pakete erlaubt:

  • Bildung von eigenen Namensräumen für Klassen und Schnittstellen (Interfaces)
  • Definierter Export von Klassen, bzw. "Information Hiding" von Klassen die nicht exportiert werden sollen
  • Definierter Import von Klassen von Fremdpaketen in eigene Klassen

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
  • Datei beginnt optional mit Schlüsselwort package und Paketname
    • Alle Klassen und Interfaces in dieser Datei gehören zum entsprechenden Paket
  • gefolgt von einer oder mehreren Java Klassen oder "Interfaces"
  • genau eine Klasse darf als public deklariert sein.

 

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();
  • 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)
Zugriffsrechte abhängig von der Deklaration im Paket
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:

Reichweite der Benutzbarkeit in Abhängigkeit vom Schlüsselwort

 

 

Stefan Schneider Fri, 10/22/2010 - 11:34

Anonymous (not verified)

Mon, 11/18/2019 - 00:12

Ich glaube, beim package test unter "statische importe" für statische Importe beim beispiel expliziter import fehlt ein .* hinter import java.lang.Math
:)

Ich glaube der explizite Import is in Ordnung. Er importiert eine Klasse. Testen Sie doch mal den Code in der Entwicklungsumgebung.

5.3.2 Lernziele

5.3.2 Lernziele

Am Ende dieses Blocks können Sie:

  • .... mit Hilfe von Paketen verschiedene Namensräume für Klassennamen nutzen
  • ... erkennen welche Methoden und Klassen aus anderen Paketen verwendet werden dürfen
  • ... die Modifizierer public, protected, private verwenden um den Zugriff auf Klassen, Methoden und Attribute in Paketen zu kontrollieren
  • ... die Sonderformen des statischen und impliziten Imports anwenden

Lernzielkontrolle

Sie sind in der Lage die Fragen zu Paketen zu beantworten:

Stefan Schneider Fri, 10/19/2012 - 10:48

5.4 Übungen

5.4 Übungen
Duke als Boxer

5.4.1 Übung: Vektorrechnung

Ein Vektor im zweidimensionalen Raum kann durch 2 reelle Zahlen dargestellt werden.
Erstellen Sie eine Klasse "Vektor", die Operationen(Methoden) für
  • die Addition von Vektoren,
  • die Multiplikation eines Vektors mit einem Skalar,
  • die Bildung des Skalarprodukts zweier Vektoren (d.h. die Summe der Produkte der entsprechenden Komponenten) anbietet.
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());
    }
}
komplexe Zahl

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.

UML Diagramm eines Flugzeugs

 

  1. Kopieren Sie sich die beiden Klassen Flugzeug.java und FlugzeugTest.java auf Ihren Rechner. Die Klasse FlugzeugTest dient zum Testen und Starten der Anwendung.
  2. (Schritt 1-5) Implementieren die notwendigen Attribute als geschützte Attribute. Die Namen der Attribute sind auch in der drucken() Methode zu finden
  3. (Schritt 6) Implementieren Sie die Methode zur Berechnung des aktuellen Gewichts
  4. (Schritt 7): Entfernen Sie die Kommentare in der drucken() Methode.
  5. (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.)
  6. (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
  7. (Schritt 10-13): Implementieren Sie alle benötigten Methoden (siehe Diagramm)
  8. (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
  9. (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?
  10. (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.

  1. Jedes Team entwirft die entsprechende Klasse und stellt die Klasse und alle öffentlichen Methoden und Variablen vor
  2. Jedes Team implementiert seine Klasse und schreibt in der eigenen main() Methode einige Tests.
  3. Alle Teams integrieren die Klassen zu einer lauffähigen Anwendung und testen sie
  4. 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:

  1. 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.
  2. 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";

}

}
 

 

Stefan Schneider Wed, 08/25/2010 - 14:49

5.5 Lösungen

5.5 Lösungen

5.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 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("***********************");
}
}

 

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.");
}
}

 

Stefan Schneider Wed, 08/25/2010 - 14:51

Anonymous (not verified)

Thu, 11/26/2015 - 20:13

bei Lösung 5.43 haben Sie in der Klasse FlugzeugTest in der while-Schleife von Phase 4 "passagierkapazität" statt "passagierkapazitaet" stehen.

5.6 Lernziele

5.6 Lernziele

.Learning Curve Duke Cartoon

Am Ende dieses Blocks können Sie:

  • ....die wichtigsten Aspekte der Objektorientierung anwenden und erklären.
  • ...an einfachen Beispielen das Konzept der Datenkapselung in Java anwenden indem Sie eine Klasse mit öffentlichen Attributen mit Hilfe von Methoden kapseln.
  • ...den Unterschied einer Klasse zu einem Objekt in Java eklären
  • ...Objektvariablen benutzen und erkennen ob Objektvariablen auf das gleiche oder ein anderes Objekt zeigen
  • ...Klassenvariablen und Klassenmethoden anhand des Schlüsselwort static erkennen.
  • ...den Unterschied von Klassenvariablen und Klassenmethoden zu normalen Methoden (Objektmethoden) und Variablen(Objektvariablen) erklären
  • .... den Begriff der Datenkapselung erläutern und anwenden
  • ... Beispiele für die Trennung von Zuständigkeiten nennen
  • ... den Begriff der Schichtenarchitektur erklären
  • ... die this Referenz nutzen und erklären
  • ... Konstruktoren mit Parametern benutzen und überladene Konstruktoren erkennen und die Aufrufreihenfolge der Konstruktoren interpretieren
  • ... das Entwurfsmuster eines Singleton (Einzelstücks) in Java mit Hilfe von Konstruktoren implementieren
  • ... den Unterschied zwischen Instanziieren und Initialisieren erklären

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

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Mon, 09/24/2012 - 08:21

6. Referenzen, strukturierte Datentypen

6. Referenzen, strukturierte Datentypen

Objektvariablen 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.

Stefan Schneider Mon, 08/23/2010 - 19:46

6.1 Die "null" und "this" Referenz

6.1 Die "null" und "this" Referenz

6.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
...

 

Stefan Schneider Sat, 09/11/2010 - 14:44

Anonymous (not verified)

Tue, 12/18/2018 - 19:11

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"?

Danke, stimmt, wurde verbessert.

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:

  • von einer lokalen Referenzvariable im Systemstack
  • keine static Referenzvariable

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.

 
package s1.block6.dereferenzieren;
 
public class Person {
 
    public Person vater;
    public Person mutter;
 
    public static void main (String[] args ) {
        Person p1 = new Person();
        Person p2 = new Person();
// Zeitpunkt 1
        p1.vater = p2;
        p2 = null;
// Zeitpunkt 2
        aufruf(p1);
        //Zeitpunkt 5
    }
 
    public static void aufruf(Person p) {
        Person[] persFeld = new Person[2];
// Zeitpunkt 3
        persFeld[1] = p;
        persFeld[0] = new Person();
        persFeld[0].vater = new Person();
        persFeld[0].mutter = new Person();
        // Zeitpunkt 4
    }
 
}

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.

 
package s1.block6.dereferenzieren;
 
public class Person {
 
    public Person vater;
    public Person mutter;
 
    public static void main (String[] args ) {
        Person p1 = new Person();
        Person p2 = new Person();
        p1.vater = p2;
        p2 = null;
        Person[] f = aufruf(p1);
        //Zeitpunkt 7
    }
 
    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 6
return persFeld;
    }
}

 

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;
    }

 

 

Stefan Schneider Sat, 09/25/2010 - 15:49

6.3 Kopieren von Objekten

6.3 Kopieren von Objekten

 Objekte 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);

Stefan Schneider Sat, 09/11/2010 - 14:46

6.4 Vergleiche zwischen Objektvariablen

6.4 Vergleiche zwischen Objektvariablen

 In 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;
     }
}
Stefan Schneider Sat, 09/11/2010 - 14:48

6.5 Das Schlüsselwort "final" und Objektvariablen

6.5 Das Schlüsselwort "final" und Objektvariablen

 Das 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!
Stefan Schneider Sat, 09/11/2010 - 14:50

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");
Drei Referenzen auf zwei Zeichenketten

 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:

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";
Drei Referenzen auf zwei Zeichenketten

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! 

Drei Referenzen auf zwei Zeichenketten

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:

4 Referenzen auf zwei Zeichenketten

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
Stringobjekte und Stringpoolbeispiel

 

 

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:

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.

Stefan Schneider Mon, 10/11/2010 - 09:07

Anonymous (not verified)

Wed, 12/03/2014 - 23:53

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

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:

  1. 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.
  2. 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&quot; 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.

6.7 Übungen

6.7 Übungen

Duke als Boxer

6.7.1 Referenzieren, Dereferenzieren und GC

Implementieren Sie die Klasse Person mit folgenden Eigenschaften:

  • Attribute für Vater und Mutter vom Typ Person
  • Statisches Attribut zum Zählen aller erzeugten Instanzen
  • Konstruktor
    • Eingabeparameter Anzahl der Vorfahrengenerationen
    • Erzeugen der Vorfahren durch Belegen der Attribute Vater, Mutter
    • Erhöhen Sie den Personenzähler
    • Drucken Sie einen Punkt pro tausend erzeugter Objekte
    • Tipp: Der Konstruktor ist rekursiv!

Hauptprogramm mit folgenden Eigenschaften:

  • Eingabe von n Generationen
  • Eingabe ob explizite GC erfolgen soll
  • Anlegen einer Person p1 mit n Generationen
  • Anstoßen einer Garbage Collection falls gewünscht
  • Anlegen einer neuen Person auf p1 mit n Generationen
Person mit Vorfahren

 

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:

KLasse Ware mit den erzeugten Instanzen

Ziel ist es eine Lösung zu entwickeln in der die Klasse Ware mit einem Lager verwendet werden kann:

Lager und Waren in UML

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;
        }
    }
}

 

Stefan Schneider Sat, 09/25/2010 - 18:17

6.8 Lösungen

6.8 Lösungen

6.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();
}
}

 

Stefan Schneider Sat, 09/25/2010 - 18:22

6.9 Lernziele

6.9 Lernziele

Am Ende dieses Blocks können Sie:

  • .... den Unterschied zwischen Variablen primitive Datentypen und Objektvariablen erkennen, erklären und anwenden
  • ... die "null" Referenz anwenden um den Hauptspeicherverbrauch (Java-Heap) zu managen
  • ... Beispiele für die Verwendung von Eigenreferenzen (this Referenz) nennen
  • ... die this Referenz nutzen und erklären
  • ... die wichtigsten Optionen des Kommando java zur Konfiguration des Java-Heap nennen und anwenden
  • ... Objekte identifizieren die in einer Anwendung nicht mehr referenziert werden
  • ... erkennen ob Objekte oder nur die Referenzen auf die Objekte kopiert werden
  • ... den Unterschied zwischen dem == bzw. != Operator und der equals() Methode beim Vergleichen von Objekten erklären und anwenden
  • ...die Besonderheiten des final Modifizierer bei Objektvariablen erklären
  • ...mit der Javaklasse String umgehen, kennen die wichtigsten Methoden und können deren Verwaltung im Speicher erklären

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu Referenzen zu beantworten.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Thu, 09/27/2012 - 10:11

7. Felder

7. Felder

Definition:

  • 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)

Stefan Schneider Mon, 08/23/2010 - 19:51

7.1 Eindimensionale Felder

7.1 Eindimensionale Felder

Das 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: 

 

Stefan Schneider Mon, 10/11/2010 - 12:57

Anonymous (not verified)

Thu, 11/26/2015 - 19:48

Fehlt hier bei dem Anlegen in 3 Schritten bei der for-Schleife, wo steht: {array[i] = i*17} nicht die 1 hinter array? Damit man sich auf das array1, welches deklariert wurde bezieht?

LG

7.2 Felder als Parameter von Methoden

7.2 Felder als Parameter von Methoden

Felder(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!

Stefan Schneider Mon, 10/11/2010 - 15:49

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.

7.3 Mehrdimensionale Felder

7.3 Mehrdimensionale Felder

Eindimensionale 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.

Stefan Schneider Mon, 10/11/2010 - 15:50

7.4 Übungen

7.4 Übungen

Duke als Boxer

 7.4.1 Telefonbuchanwendung (1)

Implementieren Sie eine Telefonbuchanwendung die es erlaubt die folgenden Datensätze zu verwalten:

  • Name String
  • Vorname String
  • Telefonnummer Ganzzahl

 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]

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

Bsp. Conway Anwendung

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:

Interaktion der Klassen

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:

Anzahl NAchbarn

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

Bildschirmausgabe
1. Nach Starten des Programms 2. Nach Klicken des ">" Button 3. Nach mehrfachem Kicken auf Zellen
nach Start Nach erstem Klick Nach dem Klicken 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:

Bildschirmausgabe
1. Nach Starten des Programms und Markieren zweier Zellen 2. Nach Klicken des ">" Button 3. Nach zweitem Klicken des ">" Buttons
nach Start Nach erstem Klick Nach dem Klicken auf Zellen

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.

Bildschirmausgabe
1. Nach Starten des Programms und Markieren diverser Zellen 2. Nach Klicken des ">" Button 3. Nach zweitem Klicken des ">" Buttons
nach Start Nach erstem Klick Nach dem Klicken auf Zellen

 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";}

}
 

 

Stefan Schneider Mon, 10/11/2010 - 18:54

7.5 Lösungen

7.5 Lösungen

7.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ägen
        leute = 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; //Suchposition
        while ((pos<leute.length)
               && (leute[pos]!=null)
                ) pos++;
        if (pos>=leute.length) // kein freier Eintrag!
        { // Feld um 50% vergroessern und alles umkopieren
            Person[] 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; //Suchposition
        while ((pos<leute.length) // Noch nicht am Ende des Feldes
                && (leute[pos]!=null) // Es gibt einen Eintrag
                && !(leute[pos].equals(p)) ) // Er passt nicht
            pos++;
        if (pos<leute.length) // Kandidat zum loeschen steht aus pos
        { // Suche hoechsten Eintrag
            int 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 dereferenziert
            leute[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;}

}

 

Stefan Schneider Sat, 12/03/2011 - 22:15

7.6 Lernziele

7.6 Lernziele

Am Ende dieses Blocks können Sie:

  • ... die Javasyntax zum Umgang mit ein- und mehrdimensionalen Feldern beschreiben und anwenden.
  • ... die Lebensdauer eines Feldes und seiner Unterobjekte bestimmen.
  • ... die Index eines Felds benutzen um Daten zu lesen und zu schreiben.
  • ... die Länge eines Felds bstimmern und Unter- bzw. Überläufe beim Zugriff auf das Feld vermeiden.
  • ... das Objektmodell von Feldern bei Zuweisungen und Kopien von Feldern und Feldvariablen erklären und anwenden.
  • ... Felder als Parameter von Methoden benutzen und das "Call by Reference" Prinzip bei deren Übergabe erklären.
  • ... Methoden implementieren und nutzen die eine variable Anzahl von Argumenten besitzen
  • ... mit mehrdimensionalen Feldern umgehen (initialisieren, auslesen, belegen, Über- und Unterläufe vermeiden)

Lernzielkontrolle

Sie sind in der Lage die Fragen zu Feldern zu beantworten.

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Thu, 09/27/2012 - 12:18

8. Vererbung

8. Vererbung

Vererbung 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.

Stefan Schneider Mon, 08/23/2010 - 19:55

8.1 Das Konzept der Vererbung

8.1 Das Konzept der Vererbung

Die 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:

Klassenhierarchie

Die gleiche Begriffshierarchie lässt sich auch als Mengendiagramm darstellen:

Mengendiagramm

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

  • Unterklassen können von Oberklassen Methoden und Attribute erben und direkt in Ihrer Implementierung benutzen
  • Typischerweise haben Unterklassen nur direkten Zugriff auf öffentliche Methoden und Attribute
  • Unterklassen können selbst zusätzliche Attribute und Methoden implementieren und so spezialisiertere Aufgaben übernehmen
  • Instanzen von Unterklassen müssen sich wie Instanzen der Oberklassen verhalten. Da die Instanzen der Unterklasse immer eine Teilmenge der Instanzen der Oberklasse sind. Sie dürfen jedoch zusätzliche Eigenschaften implementieren

Das Diagramm rechts verdeutlicht die Beziehungen zwischen den Klassen, ihrer Bedeutung und der Typisierung. 

Sub- Supertypbeziehung

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.

Stefan Schneider Tue, 10/12/2010 - 09:56

8.2 Vererbung in Java

8.2 Vererbung in Java

Die 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

 

Stefan Schneider Tue, 10/12/2010 - 09:59

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.

 

Stefan Schneider Tue, 10/12/2010 - 10:01

Anonymous (not verified)

Sat, 12/19/2015 - 20:46

"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? :)

Da habe ich mir selbst ein Bein gestellt. Fehler wurde korrigiert. Der Satz wurde auch leicht verändert.

8.4 Abstrakte und finale Klassen

8.4 Abstrakte und finale Klassen

Finale 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
  • Sie können nicht instanziiert werden.
  • Sie können nicht final sein.

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.

Abstrake und finale Klassen

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.

 

Stefan Schneider Tue, 10/12/2010 - 19:43

T.B. (not verified)

Thu, 12/03/2020 - 18:08

Bei dem ersten Satz nach der Überschrift „Finale Methoden“ fehlt das Verb „kann“.

8.5 Entwurfsüberlegungen zur Vererbung

8.5 Entwurfsüberlegungen zur Vererbung

Die 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

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
  • 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.)

 

Stefan Schneider Tue, 10/12/2010 - 10:03

Anonymous (not verified)

Thu, 08/24/2017 - 09:44

Implementierer der Oberklasse
entwirft ein semantisches

Stefan Schneider

Thu, 08/24/2017 - 10:56

In reply to by Anonymous (not verified)

Wurde verbessert.

8.6 Übungen

8.6 Übungen

Duke als Boxer

8.6.1 Aufruf von überschriebenen Methoden

Implementieren sie die Klasse TopClass mit den folgenden Eigenschaften

  • statische, geschütztes (protected) Attribut zaehlerTop zum Zählen der erzeugten Instanzen der Klasse TopClass
  • parameterlosen Konstruktor der den Instanzenzähler inkrementiert.
  • eine statische geschützte Methode getZaehler() die das Attribut zaehlerTop ausgibt.

Implementieren Sie die Klasse LowClass welche aus TopClass abgeleitet mit den folgenden Eigenschaften 

  • statische, geschütztes (protected) Attribut zaehler zum Zählen der erzeugten Instanzen der Klasse LowClass
  • parameterlosen Konstruktor der den Instanzenzähler inkrementiert
  • eine statische geschützte Methode getZaehler() die das Attribut zaehler ausgibt.
  • eine Methode test() die
    • die lokale Methode getZaehler() aufruft und das Ergebnis auf der Konsole ausgibt
    • die Methode getZaehler() der Oberklasse aufruft und das Ergebnis auf der Konsole ausgibt

Implementieren Sie ein Hauptprogramm, dass folgende Befehle ausführt

  • Anlegen eines Feldes der Klasse TopClass mit n Elementen
  • Anlegen eines Feldes der Klasse LowClass mit n Elementen
  • Erzeugen von jeweils n Elementen der Klasse TopClass und Einfügen in das zugehörige Feld
  • Aufruf der getZaehler() Methode der Klasse TopClass und Ausgabe des Wertes auf der Konsole
  • Erzeugen von jeweils n Elementen der Klasse LowClass und Einfügen in das zugehörige Feld
  • Aufruf der getZaehler() Methode der Klasse LowClass und Ausgabe des Wertes auf der Konsole
  • Aufruf der getZaehler() Methode der Klasse TopClass und Ausgabe des Wertes auf der Konsole
UML Diagramm TopClass, LowClass

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?

Point und Circle, Delegation versus Assoziation

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.

UML Diagramm Flughafenbeispiel mit Vererbung

 

Stefan Schneider Wed, 10/13/2010 - 12:42

8.7 Lösungen

8.7 Lösungen

8.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

Ware, Lager, Buch, ohne Vererbung

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 ist von 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();
    }
}

 

Stefan Schneider Wed, 10/13/2010 - 15:33

Anonymous (not verified)

Fri, 12/04/2015 - 19:34

die Zeile:
public int price = 99;

in der Klasse CircleIsPoint ist dort sicher nur aus Versehen hineingeraten?

8.8 Lernziele

8.8 Lernziele

Am Ende dieses Blocks können Sie:

  • ... die Konzepte der Spezialisierung und Verallgemeinerung im Kontext der Vererbung erklären und anwenden
  • ... die Oberklasse einer Klasse mit Hilfe des Schlüsselworts extends identifizieren
  • ... alle Methoden und Attribute einer Klasse nennen
  • ... vererbte Methoden und Atribute einer Klasse von lokalen Methoden und Atrributen unterscheiden
  • ... in UML die Vererbungsbeziehung zweier Klassen erkennen
  • ... das Schlüsselwort super nutzen um gezielt einen Konstruktor der Oberklasse zu nutzen.
  • ... im Quellcode bei der Benutzung eines new Operators die Aufrufabfolge der beteiligten Konstruktoren analysieren und beschreiben
  • ... das Konzept des "Überschreibens" von Methoden und Attributen erklären, erkennen und nutzen
  • ... abstrakte und finale Klassen nutzen und Beispiele für eine vorteilhafte Anwendung nennen
  • ... das Zusammenspiel von abstrakten Methoden und abstrakten Klassen erklären und anwenden

Lernzielkontrolle

Sie sind in der Lage die Fragen zur Vererbung zu beantworten:

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Sun, 10/07/2012 - 23:49

9. Polymorphie

9. Polymorphie

Der 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.

Stefan Schneider Mon, 08/23/2010 - 19:57

9.1 Polymorphes Verhalten bei Vererbung und Erweiterung

9.1 Polymorphes Verhalten bei Vererbung und Erweiterung

 Der 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.

Stefan Schneider Wed, 10/13/2010 - 17:11

Anonymous (not verified)

Fri, 12/04/2015 - 14:15

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?

Stefan Schneider

Fri, 12/04/2015 - 15:26

In reply to by Anonymous (not verified)

Der Satz war schlecht formuliert. Ich habe ihn neu formuliert. Ich hoffe, dass ist jetzt besser verständlich.

9.2 Polymorphie beim Überschreiben

9.2 Polymorphie beim Überschreiben

Java 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:

Definition
Ü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:

Überschriebene und überladene Methoden

Stefan Schneider Wed, 10/13/2010 - 17:12

Anonymous (not verified)

Mon, 12/01/2014 - 01:40

Kann es sein dass hier ein Fehler vorliegt?
"Hier tritt nun die überschreibende Methode an die Stelle der überschriebenen Methode."

Anonymous (not verified)

Wed, 12/17/2014 - 16:23

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.)

9.3 Casting: Typkompabilität bei Zuweisungen

9.3 Casting: Typkompabilität bei Zuweisungen

Bisher 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.

 

Stefan Schneider Wed, 10/13/2010 - 17:21

Anonymous (not verified)

Wed, 11/13/2019 - 09:28

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?

Stefan Schneider

Wed, 11/13/2019 - 14:21

In reply to by Anonymous (not verified)

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?

9.4 Sonderfall: überschriebene Attribute

9.4 Sonderfall: überschriebene Attribute

Der 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.

Stefan Schneider Wed, 10/13/2010 - 17:14

Anonymous (not verified)

Sat, 12/14/2019 - 14:27

Warum wird im Hauptprogramm durch p2.print(); die Methode der Klasse CircleIsPoint aufgerufen und nicht die Methode von Point?

Stefan Schneider

Sun, 12/15/2019 - 10:17

In reply to by Anonymous (not verified)

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.

Anonymous (not verified)

Sat, 12/14/2019 - 14:34

Die Namen und Werte im Diagramm passen nicht zum Code. Daher wird das Beispiel nicht klar.

Stefan Schneider

Sun, 12/15/2019 - 10:12

In reply to by Anonymous (not verified)

Ich habe hier ein Diagramm aus einem anderen Beispiel verwendet. Ich muss überlegen ob ich es lösche oder anpasse.

9.5 instanceof-Operator

9.5 instanceof-Operator

Normalerweise 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
Stefan Schneider Wed, 10/13/2010 - 17:23

9.6 Übungen

9.6 Übungen

Duke als Boxer

9.6.1 Haustierpolymorphismus

Haustierklassen

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.

Stefan Schneider Wed, 10/13/2010 - 17:26

9.7 Lösungen

9.7 Lösungen

 9.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

 

Stefan Schneider Wed, 10/13/2010 - 17:27

9.8 Lernziele

9.8 Lernziele

Am Ende dieses Blocks können Sie:

  • ... den Begriff der Polymorphie und das "Liskov Substitution" Prinzip im Javakontext erklären und anwenden
  • ... erkennen wenn zwei Javaklassen polymorphe Methoden haben
  • ... das Konzept des Überschreibens in Java erklären und anwenden
  • ... überschriebene Methoden und überladene Methoden unterscheiden (Anmerkung: Fällt auch mir immer wieder schwer...)
  • ... erkennen was erfüllt sein muss um eine Methode zu überschreiben
  • ... die Typkonversionen "Upcasts" und "Downcasts" in Java erkennen und erklären welche Zuweisungen sicher sind und welche nicht
  • ... erklären was überschriebene Attribute von überschreibenen Methoden unterscheidet
  • ... verschiedene Methoden zur Bestimmung des Typs eines Objekts nennen.

Lernzielkontrolle

Sie sind in der Lage die Fragen zum Polymorphismus  zu beantworten

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Mon, 10/08/2012 - 00:09

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

Zielkonflikte
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

Syntax einer 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:

Exceptionklassen 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
und separatem Blitzsymbol

Weiterführende Quellen

Stefan Schneider Sat, 10/23/2010 - 14:37

10.1 Java-Ausnahmen (Exceptions) behandeln

10.1 Java-Ausnahmen (Exceptions) behandeln

Java 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
Aktivitätsdiagramm try-catch-finally Block Aktivitätsdiagramm try-catch 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

 

Stefan Schneider Sat, 01/29/2011 - 12:05

10.2 Java-Ausnahmen deklarieren und werfen

10.2 Java-Ausnahmen deklarieren und werfen

Werfen 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

 

Stefan Schneider Sat, 01/29/2011 - 12:11

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.

Beispiele
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:

Beispiele
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;
}

 

 

Stefan Schneider Fri, 04/01/2011 - 19:49

Anonymous (not verified)

Sat, 12/14/2019 - 18:11

Warum kann eine Methode erben? (Konzept der Checked Exceptions)

10.4 Weiterreichen von Ausnahmen

10.4 Weiterreichen von Ausnahmen

Java 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.

Stefan Schneider Sun, 01/30/2011 - 17:04

10.5 Der try-with-resources Befehl

10.5 Der try-with-resources Befehl

Dies 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.

Stefan Schneider Sat, 04/01/2017 - 12:09

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
       
  • 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!

Stefan Schneider Sat, 04/02/2011 - 11:33

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);
} }

 

Stefan Schneider Sun, 04/03/2011 - 13:35

10.8 Beispielprogramme aus der Vorlesung

10.8 Beispielprogramme aus der Vorlesung

In 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:

  1. Rechtsklicken und halten  auf dem Projektsymbol (Kaffeetassensymbol) in dem sich die Klasse Konto3 befindet.
  2. Auswahl des letzten Menüpunkts "Properties".
  3. Auswahl der Kategory "Run"
  4. In "Configuration" den "New" Knopf drücken
  5. Im modalen Dialog einen Konfigurationsname wählen (z.Bsp. Konto3Assertion)
  6. Feld "Main Class" belegen in dem man den Knopf rechts davon "Browse" drückt und die gewünschte Klasse im Projekt wählt
  7. Es werden alle Klassen im Projekt angezeigt, die als Startprogramm dienen können. Wählen Sie die Klasse Konto3 aus.
  8. Fügen sie im Textfeld "VM Options" "-ea" um Assertions einzuschalten.
  9. Speichern Sie den Dialog duech Drücken der Taste "OK"

Starten des Programms

  1. Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
  2. Auswahl des letzten Menüpunkts "Set Configuration".
  3. Wählen Sie die im Vorgängerschritt angelegte Konfiguration
  4. Starten Sie das Programm mit Rechtsklicken und halten auf dem Projektsymbol (Kafeetassensymbol) in dem sich die Klasse Konto3 befindet.
  5. 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.

Klassenhierarchie der neuen Ausnahme

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.

Klassenhierarchie mit Ausnahmen

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.

Klassenhierarchie von Ausnahmen

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);
    }
}

 

Stefan Schneider Sun, 03/11/2012 - 17:31

10.9 Lernziele (Ausnahmen)

10.9 Lernziele (Ausnahmen)

Am Ende dieses Blocks können Sie:

  • ...  abschätzen wann Sie im Programmablauf Ausnahmen einsetzen um den Kontrollfluß übersichtlich zu gestalten
  • ...  wissen Sie wie Sie auf Ausnahmen reagieren können ohne das die Anwendung beendet wird
  • ...  die Syntax  von try-catch Blöcken anwenden um Ausnahmen zu behandeln
  • ...  mit Hilfe der Exception-Klasshierarchie entscheiden, welcher try-catch Block beim Auftreten einer Ausnahme ausgeführt wird
  • ...  zwischen checked- und unchecked Exceptions unterscheiden und die entsprechenden Implementierungsmaßnahmen treffen.
  • ...  selbst Ausnahmen werfen
  • ...  selbst Ausnahmeklassen implementieren 
  • ... die folgenden Klassen in ihre Vererbungshierarchie einordnen: Throwable, Error, Exception, RuntimeException
  • ... try-catch Blöcke mit mehren catch Blöcken interpretieren

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Ausnahmen (Exception)

Feedback

Zur Umfrage

QR Code für Umfrage

Stefan Schneider Fri, 01/11/2013 - 16:55

11. Weitere Konzepte

11. Weitere Konzepte Stefan Schneider Sat, 11/17/2018 - 12:00

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:

UML Diagramm mit Java Schnittstellen

alternative Schnittstellenbeschreibung in UML

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
  • 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.

Stefan Schneider Thu, 05/26/2011 - 20:03

11.1.1 Beispiele Schnittstellenanwendung

11.1.1 Beispiele Schnittstellenanwendung

 Serialisierung

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

 Heap Diagramm mit vier Objekten zur Serialisierung
  1. Im Hauptprogramm wird eine Person Urmel angelegt die auf eine Adresse Lummerland zeigt
  2. Die Methode Methode schreiben() schreibt alle von p referenzierten Objekte in eine Datei serialisiert.ser
  3. Die Methode lesen() liest die Person und alle referenzierten Objekte aus der Datei zurück
  4. p1 zeigt nun auf eine neue Person mit eigenen referenzierten Objekt Adresse

 

 Ü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

  1. 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?
  2. Was geschieht beim Übersetzen und Ausführen wenn man den Term "implements Serializable" in der Klasse Adresse oder Person weglässt?
  3. 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?
  4. Was muss man tun wenn man ein Attribut (zBsp. stadt) nicht speichern möchte? Suchen Sie in der Java-Spezifikation oder mit einer Suchmaschine...

 

Stefan Schneider Sun, 03/01/2015 - 12:28

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);
}

 

Stefan Schneider Wed, 03/30/2011 - 13:33

11.1.3 Lösung: Schnittstellen und abstrakte Klassen

11.1.3 Lösung: Schnittstellen und abstrakte Klassen

Lö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;
}
}

 

Stefan Schneider Thu, 02/10/2011 - 10:17

11.1.4 Lernziele (Schnittstellen)

11.1.4 Lernziele (Schnittstellen)

Am Ende dieses Blocks können Sie:

  • den Zusammenhang zwischen "Information Hiding" und Java Schnittstellen erklären
  • die in Java-Schnittstellem vorkommenden Komponenten von Klassen nennen
  • die Unterschiede und Gemeinsamkeiten von Javaklassen und Javaschnittstellen erklären
  • die Syntax einer Javaschnitstelle nennen
  • die UML Notation für Schnittstellen aufzeichnen und anwenden
  • beim Entwurf von Anwendungen Gründe für die Wahl von Vererbung mit Polymorphismus oder die Wahl von Schnittstellen nennen.

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Java Schnittstellen (Interfaces)

Stefan Schneider Fri, 12/28/2012 - 19:51

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:

Vergleich Assertions und Ausnahmen
  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.
Stefan Schneider Wed, 01/19/2011 - 20:28

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.

UML Diagramm Euro

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.
Stefan Schneider Sat, 04/06/2013 - 17:13

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;
    }
}

 

Stefan Schneider Sat, 04/06/2013 - 17:13

11.3.3 Lernziele (Assertions)

11.3.3 Lernziele (Assertions)

Am Ende dieses Blocks können Sie:

  • ... mit Hilfe von Java-Assertions Invarianten der Anwendung implementieren
  • ... die Auswirkung von Annahmen (Assertions) auf die Wartbarkeit von Anwendungen beschreiben
  • ... Javaannahmen (Assertions) zur Laufzeit gezielt an und ausschalten
  • ... den Einsatz von Assertions (Annahmen) abwägen gegen die Verwendung von Ausnahmen (Exceptions)

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Annahmen (Assertions))

Stefan Schneider Wed, 01/09/2013 - 19:16

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:

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):

Stefan Schneider Sat, 11/27/2010 - 16:23

11.3.1 Lernziele

11.3.1 Lernziele

Am Ende dieses Blocks können Sie:

  • .... die Notations eines Dokumentationskommentars von anderen Javakommentaren unterscheiden
  • ... können die Tags zur Dokumentation von Eingabeparametern und Ausgabeparametern nutzen
  • ... können den Dokumentationsgenerator für einfache Klassen aufrufen und kennen die Optionen zum Generieren von zusätzlichen Informationen im Kommentar

Lernzielkontrolle

Sie sind in der Lage die folgenden Fragen zu javadoc zu beantworten:

Stefan Schneider Wed, 10/17/2012 - 17:29

11.4 Packen mit jar

11.4 Packen mit jar

Einfü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.

Die wichtigsten jar Operationen
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.classMain.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

 

Stefan Schneider Tue, 12/14/2010 - 16:40

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:

  • ... Dateien mit Hilfe einer Manifestdatei zu einem jar Archiv packen
  • ... jar Archive erstellen die beim Aufruf von java mit einer automatisch vorkonfigurieten Klasse starten
  • ... jar Archive Packen,  Auslesen und Entpacken
  • ... Dokumentationskommentare von normalen Kommentaren unterscheiden
  • ... die Tags zur Dokumentation der Eingabevariablen und Rückgabewerte benutzen um eine Methode zu dokumentieren
  • ... mit Hilfe der Anwendung javadoc eine html Dokumentation für eine oder mehrere Klassen erzeugen

Lernzielkontrolle

Sie sind in der Lage die Fragen zu jar zu beantworten:

Stefan Schneider Mon, 10/08/2012 - 00:13

12. Begleitende Themen

12. Begleitende Themen

 Java und andere Frameworks

Stefan Schneider Thu, 08/26/2010 - 15:28

12.1 Java API

12.1 Java API

Das 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:

Anonymous (not verified) Thu, 10/20/2011 - 14:14

12.1.1 Konsolen Ein- und Ausgabe

12.1.1 Konsolen Ein- und Ausgabe

Konsolenausgabe 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
Stefan Schneider Thu, 08/26/2010 - 15:45

12.1.2 Swing

12.1.2 Swing

 Die 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.

Stefan Schneider Sun, 01/23/2011 - 12:41

JFrame

JFrame

 Die 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.

JFrame Übersicht
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;
import javax.swing.JLabel; ... JFrame hf = new JFrame();
// 1. Gewünschte Größe setzen
// 1. Parameter: horizontale Größe in Pixel
// 2. Parameter: vertikale Größe
hf.setSize(220,230); // Setzen des Titels
hf.setTitle("Java Anwendung");
// Setzen des Feldes mitdem Text "Mustertext"
hf.add(new JLabel("Mustertext")); // Beim Schliessen wird das Programm beendet
hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Sas JFrame sichtbar machen
hf.setVisible(true);

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.

Stefan Schneider Sun, 01/23/2011 - 12:46

JMenuBar, JMenu, JMenuItem

JMenuBar, JMenu, JMenuItem

 Die 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

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

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

JMenu
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.

Stefan Schneider Sun, 01/23/2011 - 19:22

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) ;
    }
}

Als Download in GitHub.

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);
    }
}

Stefan Schneider Sun, 10/10/2010 - 21:28

12.1.4 Math

12.1.4 Math

 Die 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;
}
Stefan Schneider Mon, 10/11/2010 - 16:48

12.1.5 Systemresourcencheck mit der Klasse Runtime

12.1.5 Systemresourcencheck mit der Klasse Runtime

 Die 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("***  ***");
    }
Stefan Schneider Mon, 10/11/2010 - 12:47

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


 
$ java s2.sort.Lexikographisch SPULE Spüle German PRIMARY
SPULE ist gleich Spüle
$ java s2.sort.Lexikographisch SPULE Spüle German SECONDARY
SPULE vor Spüle
$ java s2.sort.Lexikographisch Schmidt SCHMIDT German PRIMARY
Schmidt ist gleich SCHMIDT
$ java s2.sort.Lexikographisch Schmidt SCHMIDT German SECONDARY
Schmidt ist gleich SCHMIDT
$ java s2.sort.Lexikographisch Schmidt SCHMIDT German TERTIARY
Schmidt vor SCHMIDT

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");
            System.out.println("Syntax: java Kurs2.Sort.Lexikographisch "
                    + "String1 String2 "
                    + "[[German|French|UK|US] [PRIMARY|SECONDARY|TERTIARY]]");

} 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); } } }

Stefan Schneider Tue, 03/29/2011 - 20:32

12.2 Kodierstil

12.2 Kodierstil

Duke auf Motorrad

Es gibt ein paar wichtige Regeln um Code auch in Zukunft noch verstehen zu können. In der Javawelt gelten die folgenden Konventionen:

  • Camelcase verwenden
  • Klassennamen groß schreiben
  • Methodennnamen klein schreiben
  • Variablen klein schreiben
  • Konstanten werden vollständig in Großbuchstaben geschrieben
  • An schwierigen Stellen Kommentare schreiben
  • Code einrücken bei jedem neuen Block
    • Geschweifte Blockklammern {} übereinander scheiben wenn möglich

Siehe auch Wikibooks: Standard für Variablen und Bezeichner

Stefan Schneider Thu, 08/26/2010 - 16:34

12.3 Entwicklungsumgebungen

12.3 Entwicklungsumgebungen

Hier 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

 gedit Screen Shot

bluej.org

Eclipse

Stefan Schneider Thu, 08/26/2010 - 19:53

12.3.1 Intellij

12.3.1 Intellij

Meine persönlichen Preferenz ist aktuell IntelliJ IDEA Community version für Windows und Mac.

Anbei zwei Youtube Videos zur Installation und Konfiguration

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:

Projektliste rechts klick 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. Auswahl git Pull
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. Auswahl von master

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!

 

Stefan Schneider Mon, 01/25/2021 - 16:58

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:

  1. 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
  2. Jede Entwicklungsumgebung die zumindestens Pakete unterstützt ist ausreichend für den Kurs
  3. Es ist durchaus ein gute Idee sich mehrere Entwicklungsumgebungen anszuschauen um die zu wählen die einem am besten gefällt.
  4. 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.

Stefan Schneider Wed, 09/04/2013 - 10:56

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>

 

Stefan Schneider Wed, 11/07/2012 - 17:43

12.4 Java unter Windows

12.4 Java unter Windows

Konfigurieren 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:

Kommanozeilenfenster

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

Ikone Control Panel

 

Öffnen der Systemsteuerung

Klicken Sie dann auf "System and Security"

Systemsteuerung

Klicken Sie auf "System"

Windows Systemsauswahl

 

Klicken Sie auf "Advanced system settings"

Advanced System settings Windows

Ein Dialog mit dem Titel "System Properties" erscheint.

System Properties 

 Wählen Sie hier den Button "Environment Variables"

Environment Windows

Suchen Sie hier den Eintrag "Path" in "System variables" und klicken Sie dann den "Edit" Button

Pfad variable

 

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.

Editieren PATH

 Kontrolle

Bei einem erlfogreich gepflegten Pfad kann man in einem neuen Fenster zum Beispiel die Javaversion mit dem Befehl "java -version" kontrollieren.

Kontrolle 

 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.

Stefan Schneider Sun, 08/07/2011 - 16:40

12.5 Typische Fehler

12.5 Typische Fehler

System.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: ");
System.out.println("John Doe");
System.out.println("****************");
Person: John Doe
****************

aber:

Quellcode Ausgabe
System.out.println("Person: ");
System.out.println("John Doe");
System.out.println("****************");
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.

Stefan Schneider Sun, 12/04/2011 - 14:55

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

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.
Stefan Schneider Tue, 09/01/2020 - 11:34

12.7 Feedback

12.7 Feedback

Feedback der Vorlesung

  Datum 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:

    • Übungszeit 

    • Die Übungen waren sehr interessant, heute konnte mehr mitmachen. 

    • Thema Konstruktoren 

    Ich würde das folgende anders machen:

    • Für mich gehen die Vorlesungen noch zu schnell, ich muss noch zu Hause mehr üben und studieren, aber manche Sachen verstehe ich noch nicht perfekt 
       
    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            
    Stefan Schneider Mon, 11/30/2020 - 11:40

    13. Ressourcen und Quellen

    13. Ressourcen und Quellen

    Duke reading a book

     

    Lernen

    Javabilder

    Javakurse

    Javaprogrammierkurs Dr. Manfred Jackel, Uni Koblenz

    Stefan Schneider Wed, 06/15/2011 - 18:38

    Anonymous (not verified)

    Fri, 11/22/2019 - 16:03

    Ich möchte mich hier für die übersichtliche und nachvollziehbare Darstellung des Themas Programmierung bedanken. Sehr gelungen

    13.1 Quellcode der in der Vorlesung verwendeten Applets

    13.1 Quellcode der in der Vorlesung verwendeten Applets

    Hier 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.
    Stefan Schneider Tue, 09/10/2013 - 12:06

    13.1.2 Klasse s1.block2.BinaerIntApplet

    13.1.2 Klasse s1.block2.BinaerIntApplet

    Starten 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
    */
    }
     

     

    Stefan Schneider Tue, 09/10/2013 - 12:33

    13.1.3 Klasse s1.block2.BinaerFloatApplet

    13.1.3 Klasse s1.block2.BinaerFloatApplet

    Starten 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);
    }
    }

     

    Stefan Schneider Tue, 09/10/2013 - 16:08

    13.1.4 Klasse s1.block2.BinaerCastApplet

    13.1.4 Klasse s1.block2.BinaerCastApplet

    Starten 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);
    }
    }

     

    Stefan Schneider Tue, 09/10/2013 - 19:02

    13.1.5 Klasse s1.block2.IntShiftApplet

    13.1.5 Klasse s1.block2.IntShiftApplet

    Starten 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);
    }
    }

     

    Stefan Schneider Wed, 09/11/2013 - 10:35