Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Die Funktionsbibliothek
10 Threads und nebenläufige Programmierung
11 Raum und Zeit
12 Datenstrukturen und Algorithmen
13 Dateien und Datenströme
14 Die eXtensible Markup Language (XML)
15 Grafische Oberflächen mit Swing
16 Grafikprogrammierung
17 Netzwerkprogrammierung
18 Verteilte Programmierung mit RMI und Web-Services
19 JavaServer Pages und Servlets
20 Applets
21 Midlets und die Java ME
22 Datenbankmanagement mit JDBC
23 Reflection und Annotationen
24 Logging und Monitoring
25 Sicherheitskonzepte
26 Java Native Interface (JNI)
27 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Stichwort
Download: - ZIP, ca. 12,5 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> /Java-Insel | vor >>
Java ist auch eine Insel von Christian Ullenboom Programmieren mit der Java Standard Edition Version 6
Java ist auch eine Insel 7., aktualisierte Auflage geb., mit DVD (November 2007) 1.492 S., 49,90 Euro Galileo Computing ISBN 978-3-8362-1146-8 |
| 6 Eigene Klassen schreiben
|  | 6.1 Eigene Klassen mit Eigenschaften deklarieren
|  |  | 6.1.1 Attribute deklarieren
|  |  | 6.1.2 Methoden deklarieren
|  |  | 6.1.3 Die this-Referenz
|  | 6.2 Privatsphäre und Sichtbarkeit
|  |  | 6.2.1 Für die Öffentlichkeit: public
|  |  | 6.2.2 Paketsichtbar
|  |  | 6.2.3 Kein Public Viewing – Passwörter sind privat
|  |  | 6.2.4 Wieso nicht freie Methoden und Variablen für alle?
|  |  | 6.2.5 Privat ist nicht ganz privat: Es kommt darauf an, wer’s sieht
|  |  | 6.2.6 Zugriffsmethoden für Attribute deklarieren
|  |  | 6.2.7 Setter und Getter nach der JavaBeans-Spezifikation
|  | 6.3 Statische Methoden und statische Attribute
|  |  | 6.3.1 Warum statische Eigenschaften sinnvoll sind
|  |  | 6.3.2 Statische Eigenschaften mit static
|  |  | 6.3.3 Statische Eigenschaften über Referenzen nutzen?
|  |  | 6.3.4 Warum die Groß- und Kleinschreibung wichtig ist
|  |  | 6.3.5 Statische Eigenschaften und Objekteigenschaften
|  |  | 6.3.6 Statische Variablen zum Datenaustausch
|  |  | 6.3.7 Statische Blöcke als Klasseninitialisierer
|  | 6.4 Konstanten und Aufzählungen
|  |  | 6.4.1 Konstanten über öffentliche statische finale Variablen
|  |  | 6.4.2 Eincompilierte Belegungen der Klassenvariablen
|  |  | 6.4.3 Typ(un)sicherere Aufzählungen
|  |  | 6.4.4 Aufzählungen mit enum
|  | 6.5 Objekte anlegen und zerstören
|  |  | 6.5.1 Konstruktoren schreiben
|  |  | 6.5.2 Der Standard-Konstruktor
|  |  | 6.5.3 Parametrisierte und überladene Konstruktoren
|  |  | 6.5.4 Konstruktor nimmt ein Objekt vom eigenen Typ an (Copy-Konstruktor)
|  |  | 6.5.5 Einen anderen Konstruktor der gleichen Klasse aufrufen
|  |  | 6.5.6 Initialisierung der Objekt- und Klassenvariablen
|  |  | 6.5.7 Finale Werte im Konstruktor und in statischen Blöcken setzen
|  |  | 6.5.8 Exemplarinitialisierer (Instanzinitialisierer)
|  |  | 6.5.9 Ihr fehlt uns nicht – der Garbage-Collector
|  |  | 6.5.10 Implizit erzeugte String-Objekte
|  |  | 6.5.11 Private Konstruktoren, Utility-Klassen, Singleton, Fabriken
|  | 6.6 Assoziationen zwischen Objekten
|  |  | 6.6.1 Unidirektionale 1:1-Beziehung
|  |  | 6.6.2 Bidirektionale 1:1-Beziehungen
|  |  | 6.6.3 Unidirektionale 1:n-Beziehung
|  | 6.7 Vererbung
|  |  | 6.7.1 Vererbung in Java
|  |  | 6.7.2 Spielobjekte modelliert
|  |  | 6.7.3 Einfach- und Mehrfachvererbung
|  |  | 6.7.4 Sichtbarkeit protected
|  |  | 6.7.5 Konstruktoren in der Vererbung und super
|  |  | 6.7.6 Automatische und explizite Typanpassung
|  |  | 6.7.7 Das Substitutionsprinzip
|  |  | 6.7.8 Typen mit dem binären Operator instanceof testen
|  |  | 6.7.9 Methoden überschreiben
|  |  | 6.7.10 Mit super an die Eltern
|  |  | 6.7.11 Kovariante Rückgabetypen
|  |  | 6.7.12 Array-Typen und Kovarianz
|  |  | 6.7.13 Zusammenfassung zur Sichtbarkeit
|  | 6.8 Dynamisches Binden
|  |  | 6.8.1 Unpolymorph bei privaten, statischen und finalen Methoden
|  |  | 6.8.2 Polymorphie bei Konstruktoraufrufen
|  |  | 6.8.3 Finale Klassen
|  |  | 6.8.4 Nicht überschreibbare (finale) Methoden
|  | 6.9 Abstrakte Klassen und abstrakte Methoden
|  |  | 6.9.1 Abstrakte Klassen
|  |  | 6.9.2 Abstrakte Methoden
|  | 6.10 Schnittstellen
|  |  | 6.10.1 Deklarieren von Schnittstellen
|  |  | 6.10.2 Implementieren von Schnittstellen
|  |  | 6.10.3 Markierungsschnittstellen
|  |  | 6.10.4 Ein Polymorphie-Beispiel mit Schnittstellen
|  |  | 6.10.5 Die Mehrfachvererbung bei Schnittstellen
|  |  | 6.10.6 Keine Kollisionsgefahr bei Mehrfachvererbung
|  |  | 6.10.7 Erweitern von Interfaces – Subinterfaces
|  |  | 6.10.8 Vererbte Konstanten bei Schnittstellen
|  |  | 6.10.9 Schnittstellenmethoden, die nicht implementiert werden müssen
|  |  | 6.10.10 Abstrakte Klassen und Schnittstellen im Vergleich
|  | 6.11 Geschachtelte (innere) Klassen, Schnittstellen, Aufzählungen
|  |  | 6.11.1 Statische innere Klassen und Schnittstellen
|  |  | 6.11.2 Mitglieds- oder Elementklassen
|  |  | 6.11.3 Lokale Klassen
|  |  | 6.11.4 Anonyme innere Klassen
|  |  | 6.11.5 this und Vererbung
|  | 6.12 Generische Datentypen
|  |  | 6.12.1 Einfache Klassenschablonen
|  |  | 6.12.2 Einfache Methodenschablonen
|  |  | 6.12.3 Umsetzen der Generics, Typlöschung und Raw-Types
|  |  | 6.12.4 Einschränken der Typen
|  |  | 6.12.5 Generics und Vererbung, Invarianz
|  |  | 6.12.6 Wildcards
|  | 6.13 Die Spezial-Oberklasse Enum
|  |  | 6.13.1 Methoden auf Enum-Objekten
|  |  | 6.13.2 enum mit eigenen Konstruktoren und Methoden
|  | 6.14 Dokumentationskommentare mit JavaDoc
|  |  | 6.14.1 Einen Dokumentationskommentar setzen
|  |  | 6.14.2 Mit javadoc eine Dokumentation erstellen
|  |  | 6.14.3 HTML-Tags in Dokumentationskommentaren
|  |  | 6.14.4 Generierte Dateien
|  |  | 6.14.5 Dokumentationskommentare im Überblick
|  |  | 6.14.6 JavaDoc und Doclets
|  |  | 6.14.7 Veraltete (deprecated) Klassen, Konstruktoren und Methoden
## 6.8 Dynamisches Binden Bei der Vererbung haben wir eine Form der Ist-eine-Art-von-Beziehung, sodass die Unterklassen immer auch vom Typ der Oberklassen sind. Die sichtbaren Methoden, die die Oberklassen besitzen, existieren somit auch in den Unterklassen. Der Vorteil bei der Spezialisierung ist, dass die Oberklasse eine einfache Implementierung vorgeben und eine Unterklasse diese überschreiben kann. Wir hatten das bisher bei toString() gesehen. Doch nicht nur die Spezialisierung ist aus Sicht des Designs interessant, sondern auch die Bedeutung der Vererbung. Bietet eine Oberklasse eine sichtbare und nicht finale Methode an, so wissen wir immer, dass alle Unterklassen diese Methode haben werden, egal, ob sie die Methode überschreiben oder nicht. Wir werden gleich sehen, dass dies zu einem der wichtigsten Konstrukte in objektorientierten Programmiersprachen führt. Da jede Klasse Eigenschaften von java.lang.Object erbt, lässt sich auf jedem Objekt die toString()-Methode aufrufen. Sie soll in unseren Klassen GameObject und Room wie folgt implementiert sein: Listing 6.67 com/tutego/insel/game/vf/GameObject.java, GameObject
    class GameObject
    {
      String name;

      @Override public String toString()
      {
        return String.format( "GameObject[name=%s]", name );
      }
    }
Listing 6.68 com/tutego/insel/game/vf/Room.java, Room
    public class Room extends GameObject
    {
      int size;

      @Override public String toString()
      {
        return String.format( "Room[name=%s, size=%d]", name, size );
      }
    }
Die Unterklassen GameObject und Room überschreiben die toString()-Methode aus Object. Bei einem toString() auf einem GameObject kommt nur der Name in der Kennung, und bei einem toString() auf einem Room-Objekt kommen Name und Größe in die String-Repräsentation. Es fehlen noch einige kleine Testzeilen, die drei Räume aufbauen. Alle rufen die toString()-Funktion auf den Räumen auf, wobei der Unterschied darin besteht, dass die verweisende Referenzvariable alle Typen von Room durchgeht: Ein Room ist ein Room, ein Room ist ein GameObject, und ein Room ist ein Object. Listing 6.69 com/tutego/insel/game/vf/Playground.java, main()
    Room rr = new Room();
    rr.name = "Affenhausen";
    rr.size = 7349944;
    System.out.println( rr.toString() );

    GameObject rg = new Room();
    rg.name = "Affenhausen";
    System.out.println( rg.toString() );

    Object ro = new Room();
    System.out.println( ro.toString() );
Jetzt ist die spannendste Frage in der gesamten Objektorientierung folgende: Was passiert bei dem Methodenaufruf toString()?
    Room[name=Affenhausen, size=7349944]
    Room[name=Affenhausen, size=0]
    Room[name=null, size=0]
Die Ausgabe ist leicht zu verstehen, wenn wir berücksichtigen, dass der Compiler nicht die gleiche Weisheit besitzt wie die Laufzeitumgebung. Vom Compiler würden wir erwarten, dass er jeweils das toString() in Room, aber auch in GameObject – die Ausgabe wäre nur der Name – und toString() aus Object aufruft – dann wäre die Kennung die kryptische. Doch führt die Laufzeitumgebung die Anweisungen aus, und nicht der Compiler. Da dem im Programmtext vereinbarten Variablentypen nicht zu entnehmen ist, welche Implementierung der Methode toString() aufgerufen wird, sprechen wir von später dynamischer Bindung, kurz dynamischer Bindung. Erst zur Laufzeit (das ist spät, im Gegensatz zur Übersetzungszeit) wählt die Laufzeitumgebung dynamisch die entsprechende Objektmethode aus – passend zum tatsächlichen Typ des aufrufenden Objekts. Die virtuelle Maschine weiß, dass hinter den drei Variablen immer ein Raum-Objekt steht, und ruft daher das toString() vom Room auf. Die dynamische Bindung ist eine Anwendung von Polymorphie. Obwohl Polymorphie mehr ist als dynamisches Binden, wollen wir beide Begriffe synonym verwenden. Wichtig ist, dass eine Methode überschrieben wird; von einer Funktion – nennen wir sie zufälligDa() – in beiden Unterklassen GameObject und Room hätten wir nichts, da sie nicht in Object definiert ist. Dann hätten die Klassen nur rein »zufällig« diese Methode, aber die Ober- und Unterklassen verbindet nichts. Wir nutzen daher ausdrücklich die Gemeinsamkeit, dass GameObject, Player und weitere Unterklassen toString() aus Object erben. Ohne sie gäbe es kein Bindeglied, und folglich bietet die Oberklasse immer eine Methode an, die Unterklassen überschreiben können. Würden wir eine neue Unterklasse von Object schaffen und toString() nicht überschreiben, so würde die Laufzeitumgebung toString() in Object finden, aber die Methode gäbe es auf jeden Fall; entweder die Original-Methode oder die überschriebene Variante. #### Implementierung von System.out.println(Object) Werfen wir einen Blick auf ein Programm, das dynamisches Binden noch deutlicher macht. Die print() und println()-Methoden sind so überladen, dass sie jedes beliebige Objekt annehmen und dann die String-Repräsentation ausgeben: Listing 6.70 java/io/PrintStream.java, Skizze von println()
    public void println( Object x )
    {
      String s = (x == null) ? "null" : x.toString();
      print( s );
      newLine();
    }
Eine println()-Funktion besteht aus drei Teilen: Als Erstes wird die String-Repräsentation eines Objektes erfragt – hier findet sich die Polymorphie –, dann wird dieser String an print() weitergegeben, und newLine() produziert abschließend den Zeilenumbruch. Der Compiler hat überhaupt keine Idee, was x ist; es kann alles sein, denn alles ist ein java.lang.Object. Statisch lässt sich aus dem Argument x nichts ablesen, und so muss die Laufzeitumgebung entscheiden, an welche Klasse der Methodenaufruf geht. Das ist das Wunder der dynamischen Bindung. Eclipse zeigt bei der Tastenkombination + eine Typhierarchie an, standardmäßig die Oberklassen und bekannten Unterklassen. ### 6.8.1 Unpolymorph bei privaten, statischen und finalen Methoden Obwohl Methodenaufrufe in Java in der Regel dynamisch gebunden sind, gibt es bei privaten, statischen und finalen Methoden eine Ausnahme; sie können nicht überschrieben werden und sind daher auch nicht polymorph gebunden. Sehen wir uns das an einer privaten Funktion an: Listing 6.71 NoPolyWithPrivate.java
    class NoPolyWithPrivate
    {
      public static void main( String[] args )
      {
        Unter unsicht = new Unter();
        System.out.println( unsicht.bar() );   // 2
      }
    }

    class Ober
    {
      private int furcht()
      {
        return 2;
      }

      int bar()
      {
        return furcht();
      }
    }

    class Unter extends Ober
    {
      // Überschreibt nicht, daher kein @Override
      public int furcht()
      {
        return 1;
      }
    }
Der Compiler meldet bei der Funktion furcht() in der Unterklasse keinen Fehler. Für den Compiler ist es in Ordnung, wenn es eine Methode in der Unterklasse gibt, die den gleichen Namen wie eine private Methode in der Oberklasse trägt. Das ist auch gut so, denn private Implementierungen sind ja ohnehin geheim und versteckt. Die Unterklasse soll von den privaten Methoden in der Oberklasse gar nichts wissen. Statt von Überschreiben sprechen wir hier von Überdecken. Die Laufzeitumgebung macht etwas Erstaunliches für unsicht.bar(). Die Funktion bar() wird aus der Oberklasse geerbt. Wir wissen, dass in bar() aufgerufene Funktionen normalerweise dynamisch gebunden werden, das heißt, dass wir eigentlich bei furcht() in Unter landen müssten, da wir ein Objekt vom Typ Unter haben. Bei privaten Methoden ist das aber anders, da sie nicht vererbt werden. Wenn eine aufgerufene Methode den Modifizierer private trägt, wird nicht dynamisch gebunden, und unsicht.bar() bezieht sich bei furcht() auf die Methode aus Ober.
    System.out.println( unsicht.bar() );   // 2
Anders wäre es, wenn bei furcht() der Sichtbarkeitsmodifizierer public wäre; wir würden dann die Ausgabe 1 bekommen. Dass private und statische Funktionen nicht überschrieben werden, ist ein wichtiger Beitrag zur Sicherheit. Falls nämlich Unterklassen interne private Methoden überschreiben könnten, wäre dies eine Verletzung der inneren Arbeitsweise der Oberklasse. In einem Satz: Private Methoden sind nicht in den Unterklassen sichtbar und werden daher nicht verdeckt oder überschrieben. Andernfalls könnten private Implementierungen im Nachhinein geändert werden, und Oberklassen wären nicht mehr sicher davor, dass tatsächlich ihre eigenen Funktionen benutzt werden. Schauen wir, was passiert, wenn wir in der Methode bar() über die this-Referenz auf ein Objekt vom Typ Unter casten:
    int bar()
    {
      return ((Unter)(this)).furcht();
    }
Dann wird ausdrücklich diese furcht() aus Unter aufgerufen, was jedoch kein typisches objektorientiertes Konstrukt darstellt, da Oberklassen ihre Unterklassen im Allgemeinen nicht kennen. bar() in der Klasse Ober ist somit unnütz. ### 6.8.2 Polymorphie bei Konstruktoraufrufen Dass ein Konstruktor der Unterklasse zuerst den Konstruktor der Oberklasse aufruft, kann die Initialisierung der Variablen in der Unterklasse stören. Schauen wir uns erst Folgendes an:
    class Rausschmeisser extends Muckityp
    {
      String was = "Ich bin ein Rausschmeisser";
    }
Wo wird nun die Variable was initialisiert? Wir wissen, dass die Initialisierungen immer im Konstruktor vorgenommen werden, doch gibt es ja noch gleichzeitig ein super() im Konstruktor. Da die Spezifikation von Java Anweisungen vor super() verbietet, muss die Zuweisung hinter dem Aufruf der Oberklasse folgen. Das Problem ist nun, dass ein Konstruktor der Oberklasse früher aufgerufen als Variablen in der Unterklasse initialisiert wurden. Wenn es die Oberklasse nun schafft, auf die Variablen der Unterklasse zuzugreifen, wird der erst später gesetzte Wert fehlen. Der Zugriff gelingt tatsächlich, doch nur durch einen Trick, da eine Oberklasse (etwa Muckityp) nicht auf die Variablen der Unterklasse zugreifen kann. Wir können aber in der Oberklasse genau jene Methode der Unterklasse aufrufen, die die Unterklasse aus der Oberklasse überschreibt. Da Methodenaufrufe dynamisch gebunden werden, kann eine Methode den Wert auslesen. Listing 6.72 Rausschmeisser.java
    class Muckityp
    {
      Muckityp()
      {
        wasBinIch();
      }

      void wasBinIch()
      {
        System.out.println( "Ich weiß es noch nicht :-(" );
      }
    }

    public class Rausschmeisser extends Muckityp
    {
      String was = "Ich bin ein Rausschmeisser";

      @Override
      void wasBinIch()
      {
        System.out.println( was );
      }

      public static void main( String[] args )
      {
        Muckityp bb = new Muckityp();
        bb.wasBinIch();
        Rausschmeisser bouncer = new Rausschmeisser();
        bouncer.wasBinIch();
      }
    }
Die Ausgabe ist nun folgende:
    Ich weiß es noch nicht :-(
    Ich weiß es noch nicht :-(
    null
    Ich bin ein Rausschmeisser
Das Besondere an diesem Programm ist die Tatsache, dass überschriebene Methoden – hier wasBinIch() – dynamisch gebunden werden. Diese Bindung gibt es auch dann schon, wenn das Objekt noch nicht vollständig initialisiert wurde. Daher ruft der Konstruktor der Oberklasse Muckityp nicht wasBinIch() von Muckityp auf, sondern wasBinIch() von Rausschmeisser. Wenn in diesem Beispiel ein Rausschmeisser-Objekt erzeugt wird, dann ruft Rausschmeisser mit super() den Konstruktor von Muckityp auf. Dieser ruft wiederum die Methode wasBinIch() in Rausschmeisser auf, und er findet dort keinen String, da dieser erst nach super() gesetzt wird. Schreiben wir den Konstruktor von Rausschmeisser einmal ausdrücklich hin:
    public class Rausschmeisser extends Muckityp
    {
      String was;
      Rausschmeisser()
      {
        super();
        was = "Ich bin ein Rausschmeisser";
      }
    }
Die Konsequenz, die sich daraus ergibt, ist folgende: Dynamisch gebundene Methodenaufrufe über die this-Referenz sind in Konstruktoren potenziell gefährlich und sollten deshalb vermieden werden. Vermeiden lässt sich das, indem der Konstruktor nur private Methoden aufruft, da diese nicht dynamisch gebunden werden. Wenn der Konstruktor eine private Methode in seiner Klasse aufruft, dann bleibt es auch dabei. ### 6.8.3 Finale Klassen Soll eine Klasse keine Unterklassen bilden, werden Klassen mit dem Modifizierer final versehen. Dadurch lässt sich vermeiden, dass Unterklassen Eigenschaften nachträglich verändern können. Ein Versuch, von einer finalen Klasse zu erben, führt zu einem Compilerfehler. Dies schränkt zwar die objektorientierte Wiederverwendung ein, wird aber aufgrund von Sicherheitsaspekten in Kauf genommen. Eine Passwortüberprüfung soll zum Beispiel nicht einfach überschrieben werden können. In der Java-Bibliothek gibt es eine Reihe finaler Klassen, von denen wir einige bereits kennen: String, StringBuffer, StringBuilder Wrapper-Klassen: Integer, Double, ... Math System Font, Color
Tipp Eine protected Eigenschaft in einer final deklarierten Klasse ergibt wenig Sinn, da ja keine Unterklasse möglich ist, die diese Methode oder Variable nutzen kann. Daher sollte die Eigenschaft dann paketsichtbar sein (protected enthält ja paketsichtbar), oder gleich private oder public.
### 6.8.4 Nicht überschreibbare (finale) Methoden In der Vererbungshierarchie möchte ein Designer in manchen Fällen verhindern, dass Unterklassen eine Methode überschreiben und mit neuer Logik implementieren. Das verhindert der zusätzliche Modifizierer final an der Methodendeklaration. Da Methodenaufrufe immer dynamisch gebunden werden, könnte ein Aufrufer unbeabsichtigt in der Unterklasse landen, was finale Methoden vermeiden. Dazu ein Beispiel. Das GameObject speichert einen Namen intern im protected Attribut name und erlaubt Zugriff nur über einen Setter/Getter. Die Methode setName() testet, ob der Name ungleich null ist und mindestens ein Zeichen enthält. Diese Methode soll final sein, denn eine Unterklasse könnte diese Zugriffsbeschränkungen leicht ausheben und selbst die protected-Variable name beschreiben, auf die die Unterklasse Zugriff hat. Listing 6.73 com/tutego/insel/game/vg/GameObject.java, GameObject
    class GameObject
    {
      protected String name;

      public String getName()
      {
        return name;
      }

      public final void setName( String name )
      {
        if ( name != null && name.length() >= 0 )
          this.name = name;
      }
    }
Bei dem Versuch, in einer Unterklasse die Funktion zu überschreiben, meldet der Compiler einen Fehler. Listing 6.74 com/tutego/insel/game/vg/Player.java, Player
    public class Player extends GameObject
    {
      @Override
      public void setName( String name )
                  ^ Cannot override the final method from GameObject
      {
        this.name = name;
      }
    }
Hinweis Auch private Methoden können final sein, aber private Methoden lassen sich ohnehin nicht überschreiben (sie werden überlagert), sodass final überflüssig ist.
### Ihr Kommentar Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.
Name |
E-Mail |
Ihr Kommentar |
<< zurück | <top> | vor >>

Copyright © Galileo Press 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

[Galileo Computing]
Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de