Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 18 - Character-Streams

## 18.2 Ausgabe-Streams

- 18.2 Ausgabe-Streams
  - 18.2.1 Die abstrakte Klasse Writer
  - 18.2.2 Auswahl des Ausgabegerätes
    - OutputStreamWriter und FileWriter
    - StringWriter und CharArrayWriter
  - 18.2.3 Schachteln von Ausgabe-Streams
    - BufferedWriter
    - PrintWriter
    - FilterWriter

### 18.2.1 Die abstrakte Klasse Writer

Basis aller sequentiellen Ausgaben ist die abstrakte Klasse `Writer` des Pakets `java.io`, die eine Schnittstelle für stream-basierte Ausgaben zur Verfügung stellt:

    protected Writer()

    public void close()
    public void flush()

    public void write(int c)
    public void write(char[] cbuf)
    abstract public void write(char[] cbuf, int off, int len)
    public void write(String str)
    public void write(String str, int off, int len)
| java.io.Writer

Neben einem parameterlosen Konstruktor definiert sie die Methoden `close` und `flush` und eine Reihe überladener `write`-Methoden. Die Bedeutung des Konstruktors liegt darin, den Ausgabestrom zu öffnen und für einen nachfolgenden Aufruf von `write` vorzubereiten, bevor er schließlich mit `close` wieder geschlossen wird. In der Grundform erwartet `write` einen einzigen `int`-Parameter und schreibt diesen als Byte in den Ausgabestrom. Daneben gibt es weitere Varianten, die ein Array von Bytes oder ein `String`-Objekt als Parameter erwarten und dieses durch wiederholten Aufruf der primitiven `write`-Methode ausgeben. Durch Aufruf von `flush` werden eventuell vorhandene Puffer geleert und die darin enthaltenen Daten an das Ausgabegerät weitergegeben.

|  | Von abgeleiteten Klassen wird erwartet, daß sie mindestens die Methoden `flush` und `close` sowie `write(char, int, int)` überlagern. Aus Gründen der Performance ist es aber oftmals sinnvoll, auch die übrigen `write`-Methoden zu überlagern. |  |
Hinweis

### 18.2.2 Auswahl des Ausgabegerätes

Als abstrakte Basisklasse kann `Writer` nicht instanziert werden. Statt dessen gibt es eine Reihe konkreter Klassen, die aus `Writer` abgeleitet wurden und deren Aufgabe es ist, die Verbindung zu einem konkreten Ausgabegerät herzustellen oder Filterfunktionen zu übernehmen. Tabelle 18.1 gibt eine Übersicht dieser abgeleiteten Klassen.

Klasse | Bedeutung
`OutputStreamWriter` | Basisklasse für alle Writer, die einen Character-Stream in einen Byte-Stream umwandeln
`FileWriter` | Konkrete Ableitung von `OutputStreamWriter` zur Ausgabe in eine Datei
`FilterWriter` | Abstrakte Basisklasse für die Konstruktion von Ausgabefiltern
`PrintWriter` | Ausgabe aller Basistypen im Textformat
`BufferedWriter` | Writer zur Ausgabepufferung
`StringWriter` | Writer zur Ausgabe in einen `String`
`CharArrayWriter` | Writer zur Ausgabe in ein Zeichen-Array
`PipedWriter` | Writer zur Ausgabe in einen `PipedReader`

Tabelle 18.1: Aus Writer abgeleitete Klassen

In den folgenden Unterabschnitten werden die Klassen `OutputStreamWriter`, `FileWriter`, `StringWriter` und `CharArrayWriter` erläutert. Die Klassen `BufferedWriter`, `PrintWriter` und `FilterWriter` sind Thema des nächsten Abschnitts.

|  | Die Klasse `PipedWriter` soll hier nicht erläutert werden. In Abschnitt 22.4.4 findet sich jedoch ein Beispiel zur Anwendung der Klassen `PipedInputStream` und `PipedOutputStream`, das auf `PipedReader` und `PipedWriter` übertragbar ist. |  |
Hinweis

#### OutputStreamWriter und FileWriter

Die Klasse `OutputStreamWriter` ist die Basisklasse für alle Writer, die eine Konvertierung zwischen Character- und Byte-Streams vornehmen. Sie enthält ein Objekt des Typs `CharToByteConverter` (aus dem undokumentierten Paket `sun.io`), das die Konvertierung der Ausgabezeichen bei allen schreibenden Zugriffen vornimmt. Als übergeordnete Basisklasse ist `OutputStreamWriter` für uns allerdings nicht so interessant wie die daraus abgeleitete Klasse `FileWriter`, die die Ausgabe in eine Datei ermöglicht. Sie implementiert die abstrakten Eigenschaften von `Writer` und bietet vier zusätzliche Konstruktoren, die es erlauben, eine Datei zu öffnen:

    public FileWriter(String fileName)
      throws IOException

    public FileWriter(String fileName, boolean append)
      throws IOException

    public FileWriter(File file)
      throws IOException

    public FileWriter(FileDescriptor fd)
| java.io.FileWriter

Am einfachsten kann eine Datei göffnet werden, indem der Dateiname als `String`-Parameter `fileName` übergeben wird. Falls `fileName` eine bereits vorhandene Datei bezeichnet, wird sie geöffnet und ihr bisheriger Inhalt gelöscht, andernfalls wird eine neue Datei mit diesem Namen angelegt. Sukzessive Aufrufe von `write` schreiben weitere Bytes in diese Datei. Wird zusätzlich der Parameter `append` mit dem Wert `true` an den Konstruktor übergeben, so werden die Ausgabezeichen an die Datei angehängt, falls sie bereits existiert.

Das folgende Programm erstellt eine Datei hallo.txt und schreibt die Zeile »Hallo JAVA« in die Datei:

    001 /* Listing1801.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1801
    006 {
    007   public static void main(String[] args)
    008   {
    009     String hello = "Hallo JAVA\r\n";
    010     FileWriter f1;
    011
    012     try {
    013       f1 = new FileWriter("hallo.txt");
    014       f1.write(hello);
    015       f1.close();
    016     } catch (IOException e) {
    017       System.out.println("Fehler beim Erstellen der Datei");
    018     }
    019   }
    020 }
| Listing1801.java

Listing 18.1: Erstellen einer Datei

Fast alle Methoden der Klassen `OutputStreamWriter` und `FileWriter` deklarieren die Ausnahme `IOException`, die als allgemeine Fehleranzeige im Paket `java.io` verwendet wird. `IOException` kann von einer Vielzahl von Methoden ausgelöst werden und hat dabei teilweise sehr unterschiedliche Bedeutungen. So bedeutet sie beim Aufruf des Konstruktors, daß es nicht möglich war, die Datei anzulegen, was wiederum eine ganze Reihe von Gründen haben kann. Beim Aufruf von `write` signalisiert sie einen Schreibfehler, und bei `close` zeigt sie einen nicht näher spezifizierten I/O-Fehler an.

In unserem Beispiel wurden alle Ausnahmen dieses Typs von einer einzigen `try`-`catch`-Anweisung behandelt. Falls eine differenziertere Fehlererkennung nötig ist, macht es Sinn, für die unterschiedlichen I/O-Operationen verschiedene `try`-`catch`-Anweisungen vorzusehen.

|  | Die anderen Konstruktoren von `FileWriter` erwarten ein `File`-Objekt bzw. ein Objekt vom Typ `FileDescriptor`. Während wir auf die Klasse `FileDescriptor` nicht näher eingehen werden, ist ein `File` die Repräsentation einer Datei im Kontext ihres Verzeichnisses. Wir werden später in Kapitel 21 darauf zurückkommen. |  |
Hinweis

#### StringWriter und CharArrayWriter

Die Klassen `StringWriter` und `CharArrayWriter` sind ebenfalls aus `Writer` abgeleitet. Im Gegensatz zu `FileWriter` schreiben sie ihre Ausgabe jedoch nicht in eine Datei, sondern in einen dynamisch wachsenden `StringBuffer` bzw. in ein Zeichenarray.

`StringWriter` besitzt zwei Konstruktoren:

    public StringWriter()

    public StringWriter(int initialSize)
| java.io.StringWriter

Der parameterlose Konstruktor legt einen `StringWriter` mit der Standardgröße eines `StringBuffer`-Objekts an, während der zweite Konstruktor es erlaubt, die initiale Größe selbst festzulegen. Wie schon erwähnt, wächst der interne Puffer automatisch, wenn fortgesetzte Aufrufe von `write` dies erforderlich machen. Die schreibenden Zugriffe auf den Puffer erfolgen mit den von `Writer` bekannten `write`-Methoden.

Für den Zugriff auf den Inhalt des Puffers stehen die Methoden `getBuffer` und `toString` zur Verfügung:

    public StringBuffer getBuffer()

    public String toString()
| java.io.StringWriter

Die Methode `getBuffer` liefert den internen `StringBuffer`, während `toString` den Puffer in einen `String` kopiert und diesen an den Aufrufer liefert.

Die Klasse `CharArrayWriter` arbeitet ähnlich wie `StringWriter`, schreibt die Zeichen allerdings in ein Character-Array. Analog zu `StringWriter` wächst dieses automatisch bei fortgesetzten Aufrufen von `write`. Die Konstruktoren haben dieselbe Struktur wie bei `StringWriter`, und auch die dort verfügbaren `write`-Methoden stehen allesamt hier zur Verfügung:

    public CharArrayWriter()

    public CharArrayWriter(int initialSize)
| java.io.CharArrayWriter

Auf den aktuellen Inhalt des Arrays kann mit Hilfe der Methoden `toString` und `toCharArray` zugegriffen werden:

    public String toString()

    public char[] toCharArray()
| java.io.CharArrayWriter

Zusätzlich stehen die Methoden `reset` und `size` zur Verfügung, mit denen der interne Puffer geleert bzw. die aktuelle Größe des Zeichen-Arrays ermittelt werden kann. Durch Aufruf von `writeTo` kann des weiteren der komplette Inhalt des Arrays an einen anderen `Writer` übergeben und so beispielsweise mit einem einzigen Funktionsaufruf in eine Datei geschrieben werden:

    public void reset()

    public int size()

    public void writeTo(Writer out)
      throws IOException
| java.io.CharArrayWriter

### 18.2.3 Schachteln von Ausgabe-Streams

Wie eingangs erwähnt, ist es in Java möglich, Streams zu schachteln. Dazu gibt es die aus `Writer` abgeleiteten Klassen `BufferedWriter`, `PrintWriter` und `FilterWriter`, deren wesentlicher Unterschied zu `Writer` darin besteht, daß sie als Membervariable einen zusätzlichen `Writer` besitzen, der im Konstruktor übergeben wird. Wenn nun die `write`-Methode eines derart geschachtelten Writers aufgerufen wird, gibt sie die Daten nicht direkt an den internen `Writer` weiter, sondern führt zuvor erst die erforderlichen Filterfunktionen aus. Damit lassen sich beliebige Filterfunktionen transparent realisieren.

#### BufferedWriter

Diese Klasse hat die Aufgabe, Stream-Ausgaben zu puffern. Dazu enthält sie einen internen Puffer, in dem die Ausgaben von `write` zwischengespeichert werden. Erst wenn der Puffer voll ist oder die Methode `flush` aufgerufen wird, werden alle gepufferten Ausgaben in den echten Stream geschrieben. Das Puffern der Ausgabe ist immer dann nützlich, wenn die Ausgabe in eine Datei geschrieben werden soll. Durch die Verringerung der `write`-Aufrufe reduziert sich die Anzahl der Zugriffe auf das externe Gerät, und die Performance wird erhöht.

`BufferedWriter` besitzt zwei Konstruktoren:

    public BufferedWriter(Writer out)

    public BufferedWriter(Writer out, int size)
| java.io.BufferedWriter

In beiden Fällen wird ein bereits existierender `Writer` übergeben, an den die gepufferten Ausgaben weitergereicht werden. Falls die Größe des Puffers nicht explizit angegeben wird, legt `BufferedWriter` einen Standardpuffer an, dessen Größe für die meisten Zwecke ausreichend ist. Die `write`-Methoden von `BufferedWriter` entsprechen denen der Klasse `Writer`.

Zusätzlich gibt es eine Methode `newLine`, mit der eine Zeilenschaltung in den Stream geschrieben werden kann. Diese wird dem System-Property `line.separator` entnommen und entspricht damit den lokalen Konventionen.

Das nachfolgende Beispiel demonstriert die gepufferte Ausgabe einer Reihe von Textzeilen in eine Datei buffer.txt:

    001 /* Listing1802.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1802
    006 {
    007   public static void main(String[] args)
    008   {
    009      Writer f1;
    010      BufferedWriter f2;
    011      String s;
    012
    013      try {
    014        f1 = new FileWriter("buffer.txt");
    015        f2 = new BufferedWriter(f1);
    016        for (int i = 1; i <= 10000; ++i) {
    017          s = "Dies ist die " + i + ". Zeile";
    018          f2.write(s);
    019          f2.newLine();
    020        }
    021        f2.close();
    022        f1.close();
    023     } catch (IOException e) {
    024       System.out.println("Fehler beim Erstellen der Datei");
    025     }
    026   }
    027 }
| Listing1802.java

Listing 18.2: Gepufferte Ausgabe in eine Datei

Dieses Beispiel erzeugt zunächst einen neuen `Writer` `f1`, um die Datei buffer.txt anzulegen. Dieser wird anschließend als Parameter an den `BufferedWriter f2` übergeben, der dann für den Aufruf der Ausgaberoutinen verwendet wird.

|  |  |  | Die etwas umständliche Verwendung zweier Stream-Variablen läßt sich vereinfachen, indem die Konstruktoren direkt beim Aufruf geschachtelt werden. Das ist die unter Java übliche Methode, die auch zukünftig bevorzugt verwendet werden soll: |  |
Tip

    001 /* Listing1803.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1803
    006 {
    007   public static void main(String[] args)
    008   {
    009     BufferedWriter f;
    010     String s;
    011
    012     try {
    013       f = new BufferedWriter(
    014          new FileWriter("buffer.txt"));
    015       for (int i = 1; i <= 10000; ++i) {
    016         s = "Dies ist die " + i + ". Zeile";
    017         f.write(s);
    018         f.newLine();
    019       }
    020       f.close();
    021     } catch (IOException e) {
    022       System.out.println("Fehler beim Erstellen der Datei");
    023     }
    024   }
    025 }
| Listing1803.java

Listing 18.3: Schachteln von Writer-Konstruktoren

#### PrintWriter

Die stream-basierten Ausgaberoutinen in anderen Programmiersprachen bieten meistens die Möglichkeit, alle primitiven Datentypen in textueller Form auszugeben. Java realisiert dieses Konzept über die Klasse `PrintWriter`, die Ausgabemethoden für alle primitiven Datentypen und für Objekttypen zur Verfügung stellt. `PrintWriter` besitzt folgende Konstruktoren:

    public PrintWriter(Writer out)

    public PrintWriter(Writer out, boolean autoflush)
| java.io.PrintWriter

Der erste Konstruktor instanziert ein PrintWriter-Objekt durch Übergabe eines Writer-Objekts, auf das die Ausgabe umgeleitet werden soll. Beim zweiten Konstruktor gibt zusätzlich der Parameter `autoflush` an, ob nach der Ausgabe einer Zeilenschaltung automatisch die Methode `flush` aufgerufen werden soll.

Die Ausgabe von primitiven Datentypen wird durch eine Reihe überladener Methoden mit dem Namen `print` realisiert. Zusätzlich gibt es alle Methoden in einer Variante `println`, bei der automatisch an das Ende der Ausgabe eine Zeilenschaltung angehängt wird. `println` existiert darüber hinaus parameterlos, um lediglich eine einzelne Zeilenschaltung auszugeben. Damit stehen folgende `print`-Methoden zur Verfügung (analog für `println`):

    public void print(boolean b)
    public void print(char c)
    public void print(char[] s)
    public void print(double d)
    public void print(float f)
    public void print(int i)
    public void print(long l)
    public void print(Object obj)
    public void print(String s)
| java.io.PrintWriter

Das folgende Beispiel berechnet die Zahlenfolge 1 + 1/2 + 1/4 + ... und gibt die Folge der Summanden und die aktuelle Summe unter Verwendung mehrerer unterschiedlicher `print`-Routinen in die Datei zwei.txt aus:

    001 /* Listing1804.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1804
    006 {
    007   public static void main(String[] args)
    008   {
    009     PrintWriter f;
    010     double sum = 0.0;
    011     int nenner;
    012
    013     try {
    014       f = new PrintWriter(
    015           new BufferedWriter(
    016           new FileWriter("zwei.txt")));
    017
    018       for (nenner = 1; nenner <= 1024; nenner *= 2) {
    019         sum += 1.0 / nenner;
    020         f.print("Summand: 1/");
    021         f.print(nenner);
    022         f.print("   Summe: ");
    023         f.println(sum);
    024       }
    025       f.close();
    026     } catch (IOException e) {
    027       System.out.println("Fehler beim Erstellen der Datei");
    028     }
    029   }
    030 }
| Listing1804.java

Listing 18.4: Die Klasse PrintWriter

Das Programm verwendet Methoden zur Ausgabe von Strings, Ganz- und Fließkommazahlen. Nach Ende des Programms hat die Datei zwei.txt folgenden Inhalt:

    Summand: 1/1   Summe: 1
    Summand: 1/2   Summe: 1.5
    Summand: 1/4   Summe: 1.75
    Summand: 1/8   Summe: 1.875
    Summand: 1/16   Summe: 1.9375
    Summand: 1/32   Summe: 1.96875
    Summand: 1/64   Summe: 1.98438
    Summand: 1/128   Summe: 1.99219
    Summand: 1/256   Summe: 1.99609
    Summand: 1/512   Summe: 1.99805
    Summand: 1/1024   Summe: 1.99902

|  |  |  | Das vorliegende Beispiel realisiert sogar eine doppelte Schachtelung von `Writer`-Objekten. Das `PrintWriter`-Objekt schreibt in einen `BufferedWriter`, der seinerseits in den `FileWriter` schreibt. Auf diese Weise werden Datentypen im ASCII-Format gepuffert in eine Textdatei geschrieben. Eine solche Schachtelung ist durchaus üblich in Java und kann in einer beliebigen Tiefe ausgeführt werden. |  |
Tip

#### FilterWriter

Wie bereits mehrfach angedeutet, bietet die Architektur der `Writer`-Klassen die Möglichkeit, eigene Filter zu konstruieren. Dies kann beispielsweise durch Überlagern der Klasse `Writer` geschehen, so wie es etwa bei `PrintWriter` oder `BufferedWriter` realisiert wurde. Der offizielle Weg besteht allerdings darin, die abstrakte Klasse `FilterWriter` zu überlagern. `FilterWriter` besitzt ein internes `Writer`-Objekt `out`, das bei der Instanzierung an den Konstruktor übergeben wird. Zusätzlich überlagert es drei der vier `write`-Methoden, um die Ausgabe auf `out` umzuleiten. Die vierte `write`-Methode (`write(String)`) wird dagegen nicht überlagert, sondern ruft gemäß ihrer Implementierung in `Writer` die Variante `write(String, int, int)` auf.

Soll eine eigene Filterklasse konstruiert werden, so ist wie folgt vorzugehen:

- Die Klasse wird aus FilterWriter abgeleitet.
- Im Konstruktor wird der Superklassen-Konstruktor aufgerufen, um out zu initialisieren.
- Die drei write-Methoden werden separat überlagert. Dabei wird jeweils vor der Übergabe der Ausgabezeichen an die Superklassenmethode die eigene Filterfunktion ausgeführt.

Anschließend kann die neue Filterklasse wie gewohnt in einer Kette von geschachtelten `Writer`-Objekten verwendet werden.

Wir wollen uns die Konstruktion einer Filterklasse anhand der folgenden Beispielklasse `UpCaseWriter` ansehen, deren Aufgabe es ist, innerhalb eines Streams alle Zeichen in Großschrift zu konvertieren:

    001 /* Listing1805.java */
    002
    003 import java.io.*;
    004
    005 class UpCaseWriter
    006 extends FilterWriter
    007 {
    008   public UpCaseWriter(Writer out)
    009   {
    010     super(out);
    011   }
    012
    013   public void write(int c)
    014   throws IOException
    015   {
    016     super.write(Character.toUpperCase((char)c));
    017   }
    018
    019   public void write(char[] cbuf, int off, int len)
    020   throws IOException
    021   {
    022     for (int i = 0; i < len; ++i) {
    023       write(cbuf[off + i]);
    024     }
    025   }
    026
    027   public void write(String str, int off, int len)
    028   throws IOException
    029   {
    030     write(str.toCharArray(), off, len);
    031    }
    032 }
    033
    034 public class Listing1805
    035 {
    036    public static void main(String[] args)
    037    {
    038       PrintWriter f;
    039       String s = "und dieser String auch";
    040
    041       try {
    042          f = new PrintWriter(
    043              new UpCaseWriter(
    044              new FileWriter("upcase.txt")));
    045          //Aufruf von außen
    046          f.println("Diese Zeile wird schön groß geschrieben");
    047          //Test von write(int)
    048          f.write('a');
    049          f.println();
    050          //Test von write(String)
    051          f.write(s);
    052          f.println();
    053          //Test von write(String, int, int)
    054          f.write(s,0,17);
    055          f.println();
    056          //Test von write(char[], int, int)
    057          f.write(s.toCharArray(),0,10);
    058          f.println();
    059          //---
    060          f.close();
    061       } catch (IOException e) {
    062          System.out.println("Fehler beim Erstellen der Datei");
    063       }
    064    }
    065 }
| Listing1805.java

Listing 18.5: Konstruktion einer eigenen FilterWriter-Klasse

Im Konstruktor wird lediglich der Superklassen-Konstruktor aufgerufen, da keine weiteren Aufgaben zu erledigen sind. Die drei `write`-Methoden werden so überlagert, daß jeweils zunächst die Ausgabezeichen in Großschrift konvertiert werden und anschließend die passende Superklassenmethode aufgerufen wird, um die Daten an den internen `Writer` `out` zu übergeben.

Der Test von `UpCaseWriter` erfolgt mit der Klasse `Listing1805`. Sie schachtelt einen `UpCaseWriter` innerhalb eines `FileWriter`- und eines `PrintWriter`-Objekts. Die verschiedenen Aufrufe der `write`- und `println`-Methoden rufen dann jede der vier unterschiedlichen `write`-Methoden des `UpCaseWriter`-Objekts mindestens einmal auf, um zu testen, ob die Konvertierung in jedem Fall vorgenommen wird. Die Ausgabe des Programms ist:

    DIESE ZEILE WIRD SCHÖN GROß GESCHRIEBEN
    A
    UND DIESER STRING AUCH
    UND DIESER STRING
    UND DIESER

|  |  |  | Wenn man sich die Implementierung der `write`-Methoden von `UpCaseWriter` genauer ansieht, könnte man auf die Idee kommen, daß sie wesentlich performanter implementiert werden könnten, wenn nicht alle Ausgaben einzeln an `write(int)` gesendet würden. So scheint es nahezuliegen, beispielsweise `write(String, int, int)` in der folgenden Form zu implementieren, denn die Methode `toUpperCase` existiert auch in der Klasse `String`: |  |
Warnung

    super.write(str.toUpperCase(), off, len);

Die Sache hat nur leider den Haken, daß `String.toUpperCase` möglicherweise die Länge des übergebenen Strings verändert. So wird beispielsweise das "ß" in ein "SS" umgewandelt (an sich lobenswert!) und durch die unveränderlichen Konstanten `off` und `len` geht ein Zeichen verloren. Der hier beschrittene Workaround besteht darin, grundsätzlich die Methode `Character.toUpperCase` zu verwenden. Sie kann immer nur ein einzelnes Zeichen zurückgeben und läßt damit beispielsweise ein "ß" bei Umwandlung unangetastet.

Als interessante Erkenntnis am Rande lernen wir dabei, daß offensichtlich `String.toUpperCase` und `Character.toUpperCase` unterschiedlich implementiert sind. Ein Blick in den Quellcode der Klasse `String` bestätigt den Verdacht und zeigt zwei Sonderbehandlungen, eine davon für das "ß", wie folgender Auszug aus dem Quellcode des JDK 1.1 belegt (im JDK 1.2 sieht es ähnlich aus):

    for (i = 0; i < len; ++i) {
       char ch = value[offset+i];
       if (ch == '\u00DF') { // sharp s
          result.append("SS");
          continue;
       }
       result.append(Character.toUpperCase(ch));
    }

Im Umfeld dieser Methode sollte man also mit der nötigen Umsicht agieren. Das Gegenstück `lowerCase` hat diese Besonderheit übrigens nicht.

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de