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:

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

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

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.

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.

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.

5.2 Objektorientierung in Java

Duke in 3D

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

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.

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

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

class Addressbuch {
   private static Addressbuch myInstance;

   private Addressbuch() {
      // Initialisiere Addressbuch
      }

   public static Addressbuch getAddressbuch() {
      if (myInstance == null) myInstance = new Addressbuch();
     return myInstance; 
   }
}
...
Addressbuch ab = getAddressbuch ();
...

Mit dieser Implementierung wird beim ersten Anfordern eines Addressbuchs genau einmal ein Addressbuch angelegt.

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.

5.3 Übungen

Duke als Boxer

5.3.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.3.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:

(a + ib) + (c + id) = (a + c) + i(b + d)
(a + ib) - (c + id) = (a - c) + i(b - d)
(a + ib) * (c + id) = (a*c - b*d) + i(a*d + b*c)
(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:

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

}

}
 

5.4 Lösungen

5.4.1 Vektorrechnung

public class Vektor {

   private double x;
    privatedouble 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 skalarproduct(Vektor v) {
        x = x * v.x;
        y = y * v.y;
        normalisierung();
    }
     public void drucken() {
     System.out.println("(" + x + "," + y + ")");
     }

}

public class Main {

    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.skalarproduct(c);
        b.drucken();
    }
}

5.4.2 Komplexe Zahlen

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.4.3 Übung: Modellierung der Datenkapselung

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

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

5.4.4 Lösung: Statische Klassenattribute

Klasse Flugzeug

Erweitert um die statische Variable

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

}

5.4.5 Überladene Methoden

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

}

Klasse FlugzeugTest

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

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(17);
pad.gate1.einsteigen(2);
pad.gate2.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.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.4.6 Fuhrparkmanagement

Klasse Kraftwagen

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

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 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, int 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 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

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

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

 

5.5 Lernziele

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
  • ...Klassvariablen 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 beantworten: