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