1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-thinking-in-java-zh

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
10.7 Java 1.1的IO流.md 22 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 11.03.2025 09:15 d56454c

10.7 IO потоки в Java 1.1

На этом этапе многие могут столкнуться с трудностями и задаться вопросом, существует ли другой подход к проектированию IO потоков, требующий большего объёма кода. Кто-то может предложить ещё более экзотический дизайн? На самом деле, Java OnClickListener 1.1 внесла значительные улучшения в библиотеку IO потоков. Первое впечатление большинства людей при виде классов Reader и Writer (так же как у меня) — что они заменяют старые классы InputStream и OutputStream. Однако это не совсем так. Хотя некоторые функции старой библиотеки данных не рекомендуются (использование которых приведёт к появлению предупреждений от компилятора), старые данные всё ещё поддерживаются для обеспечения обратной совместимости, а также:

(1) В старую иерархию были добавлены новые классы, поэтому Sun явно не собирается отказываться от старых потоков данных.

(2) Во многих случаях нам требуется использовать классы из новой структуры вместе со старыми классами. Для достижения этой цели используются "мост" классы:

InputStreamReader преобразует InputStream в Reader, а OutputStreamWriter преобразует OutputStream в Writer. Поэтому часто требуется более сложное упаковывание новых IO потоков по сравнению с старыми. Это также является одним из недостатков декораторного подхода — необходимость платить за дополнительную гибкость.Основной причиной добавления уровней Reader и Writer в Java 1.1 было требование международизации. Старая структура IO-потоков поддерживает только 8-битные байтовые потоки и плохо контролирует 16-битные Unicode-символы. Поскольку Unicode ориентирован на поддержку международизации (внутренний тип char в Java — это 16-битный Unicode), были добавлены уровни Reader и Writer для предоставления поддержки Unicode во всех операциях IO. Кроме того, новый набор библиотек был оптимизирован для скорости и работает быстрее старого.

Как и в других местах книги, я попытаюсь представить общее описание классов, но предполагается, что вы будете использовать онлайн-документацию для получения всех деталей, таких как полный список методов.

1. Инициализация и получение данныхБольшинство классов IO потоков Java 1.0 имеют аналогичные классы в Java 1.1 для обеспечения встроенной поддержки Unicode. Самым простым решением может показаться использование исключительно новых классов и отказ от старых, но на практике это не всегда возможно. Иногда ограничения дизайна библиотеки заставляют нас использовать классы IO потоков Java 1.0. В частности, библиотека java.util.zip, которая была добавлена поверх старой библиотеки потоков, зависит от старых компонентов. Поэтому наиболее разумным подходом является "экспериментальное" использование классов Reader и Writer. Если код не скомпилирован, то можно понять, что необходимо вернуться к использованию старых классов. Ниже приведена таблица, которая суммирует соответствие между источниками и стоками в старой и новой библиотеках.```markdown

Источники и стоки:

Класс Java 1.0

Соответствующий класс Java 1.1

InputStream

Reader конвертор: InputStreamReader

OutputStream

Writer конвертор: OutputStreamWriter

FileInputStream

FileReader

FileOutputStream

FileWriter

StringBufferInputStream

StringReader (нет соответствующего класса)

StringWriter

ByteArrayInputStream

CharArrayReader

ByteArrayOutputStream

CharArrayWriter

PipedInputStream

PipedReader

PipedOutputStream

PipedWriter

Мы заметили, что даже если они не полностью совпадают, но интерфейсы компонентов старого пакета обычно схожи с новыми интерфейсами.

10.7.2 Изменение поведения потока данных

В Java 1.0 данные передаются через "обёртки" (FilterInputStream и FilterOutputStream) для удовлетворения специальных требований. В Java 1.1 потоки ввода/вывода продолжили использовать эту идею, но отказались от подхода, при котором все обёртки производятся от одного базового класса filter. Это может вызвать некоторое замешательство при анализе иерархии классов.

В следующей таблице связи менее детализированы. Разница возникает из-за организации классов: хотя `BufferedOutputStream` является подклассом `FilterOutputStream`, `BufferedWriter` не является подклассом `FilterWriter` (хотя последний является абстрактным классом без подклассов или чего-то похожего, и нет "заполнителя", поэтому нет необходимости его искать). Тем не менее, интерфейсы этих двух классов очень похожи, и очевидно, следует использовать новые версии, где это возможно, а не старые (то есть, за исключением случаев, когда требуется создание `Stream`, вместо которого можно было бы создать `Reader` или `Writer`).```markdown
Фильтры:

Классы Java 1.0:

* `FilterInputStream`
* `FilterReader`
* `FilterOutputStream`
* `FilterWriter` (абстрактный класс без подклассов)
* `BufferedInputStream`
* `BufferedReader` (также имеет метод `readLine()`)
* `BufferedOutputStream`
* `BufferedWriter`
* `DataInputStream`
* `PrintStream`
* `PrintWriter`
* `LineNumberInputStream`
* `LineNumberReader`
* `StreamTokenizer` (использует конструктор, который принимает `Reader`)
* `PushBackInputStream`
* `PushBackReader`

Соответствующие классы Java 1.1:

FilterInputStream FilterReader FilterOutputStream FilterWriter (абстрактный класс без подклассов) BufferedInputStream BufferedReader (есть метод readLine()) BufferedOutputStream BufferedWriter DataInputStream (с использованием DataInputStream, кроме случаев использования readLine(), тогда требуется BufferedReader) PrintStream PrintWriter LineNumberInputStream LineNumberReader StreamTokenizer (конструктор заменяет Reader) PushBackInputStream PushBackReader


Есть явная закономерность: если вам нужен `readLine()`, то не используйте `DataInputStream` (в противном случае вы получите сообщение об ошибке во время компиляции), а вместо этого используйте `BufferedReader`. Однако за исключением этой ситуации, `DataInputStream` остаётся "предпочтительным" членом библиотеки ввода/вывода Java  Yöntemleri 1.1.

Чтобы сделать переход к `PrintWriter` более естественным, он предоставляет конструкторы, которые принимают любой объект типа `OutputStream`. Поддержка форматированного вывода в `PrintWriter` меньше, чем в `PrintStream`; но интерфейсы почти одинаковы.
```## 10.7.3 Неизменённые классы

Очевидно, что дизайнеры библиотеки Java считали некоторые классы без проблем, поэтому они не были изменены и могут продолжать использоваться так же, как раньше:

Java 1.0 классы, которые не имеют соответствия в Java 1.1

DataOutputStream File RandomAccessFile SequenceInputStream


В частности, `DataOutputStream` был не изменён, поэтому для сохранения и получения данных в передаваемом формате следует использовать структуру `InputStream` и `OutputStream`.

## 10.7.4 Пример

Для того чтобы оценить эффект новых классов, давайте рассмотрим, как можно модифицировать соответствующие части примера `IOStreamDemo.java`, чтобы использовать классы `Reader` и `Writer`:

//: NewIODemo.java // Пример типичного использования Java 1.1 IO import java.io.*;

public class NewIODemo { public static void main(String[] args) { try { // 1. Чтение входных данных построчно: BufferedReader in = new BufferedReader( new FileReader(args[0])); String s, s2 = new String(); while ((s = in.readLine()) != null) s2 += s + "\n"; in.close();

  // 1b. Чтение стандартного ввода:
  BufferedReader stdin =
    new BufferedReader(
      new InputStreamReader(System.in));
  System.out.print("Введите строку:");
  System.out.println(stdin.readLine());

## Ввод данных из памяти

### 2. Ввод из памяти
```java
StringReader in2 = new StringReader(s2);
int c;
while ((c = in2.read()) != -1)
    System.out.print((char) c);

3. Форматированный ввод из памяти

try {
    DataInputStream in3 =
        new DataInputStream(new StringBufferInputStream(s2));
    while (true)
        System.out.print((char) in3.readByte());
} catch (EOFException e) {
    System.out.println("Конец потока");
}
```### 4. Нумерация строк и вывод в файл
```java
try {
    LineNumberReader li =
        new LineNumberReader(new StringReader(s2));
    BufferedReader in4 = new BufferedReader(li);
    PrintWriter out1 =
        new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out")));
    while ((s = in4.readLine()) != null)
        out1.println("Строка " + li.getLineNumber() + ": " + s);
    out1.close();
} catch (EOFException e) {
    System.out.println("Конец потока");
}

5. Хранение и восстановление данных

try {
    DataOutputStream out2 =
        new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
    out2.writeDouble(3.14159);
    out2.writeBytes("Это число пи");
    out2.close();
    DataInputStream in5 =
        new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
    BufferedReader in5br = new BufferedReader(new InputStreamReader(in5));
    System.out.println(in5.readDouble());
    System.out.println(in5br.readLine());
} catch (EOFException e) {
    System.out.println("Конец потока");
}

6. Чтение и запись случайного доступа

Чтение и запись случайного доступа аналогичны предыдущим примерам и поэтому здесь они не повторены.

Обработка исключений

catch (FileNotFoundException e) {
    System.out.println("Файл не найден: " + args[1]);
}
catch (IOException e) {
    System.out.println("И/О исключение");
}

```Первая часть была немного сокращена, так как если требуется выполнить только чтение ввода с клавиатуры, достаточно просто обернуть один FileReader в `BufferedReader`. Вторая часть (`1b`) демонстрирует методы для обертывания `System.in`, чтобы читать ввод с консоли. Здесь количество кода увеличилось, поскольку `System.in` является `DataInputStream`, а `BufferedReader` требует параметра типа `Reader`, поэтому используется `InputStreamReader` для преобразования.Во второй части можно заметить, что если есть строка, и вы хотите прочитать данные из неё, то достаточно заменить `StringBufferInputStream` на `StringReader`, остальной код будет таким же.

Третья часть раскрывает ошибку в дизайне нового пакета IO потоков. Если есть строка, и вы хотите прочитать данные из неё, то больше нельзя использовать StringBufferInputStream ни в какой форме. При компиляции кода, использующего StringBufferInputStream, вы получите сообщение "недопустимое использование", которое говорит вам не использовать его. В этом случае лучше использовать StringReader. Однако, если требуется выполнять форматированное чтение из памяти, как показано в третьей части, то необходимо использовать DataInputStream — нет ничего, что могло бы заменить его. Но DataInputStream также требует параметра типа InputStream, поэтому нам приходится использовать недопустимый StringBufferInputStream класс, который компилятор не одобряет. Компилятор также выдаёт сообщение "недопустимое использование", но мы не можем ничего сделать (примечание ②).

Примечание ②: к моменту вашего использования этот баг может уже быть исправлен.Четвертая часть явно представляет собой прямой переход от старых данных потоков к новым, без особых примечаний. В пятой части нас заставили использовать все старые данные потоки, потому что DataOutputStream и DataInputStream требуют их использования, и нет ничего, чем можно было бы их заменить. Однако во время компиляции не возникают какие-либо сообщения "недопустимого использования". Обычно, если данные потоки не рекомендованы, это связано с тем, что их конструкторы генерируют сообщения "недопустимого использования", запрещающие использование всего класса. Однако в случае DataInputStream недопустимым является только использование readLine(), поскольку лучше использовать BufferedReader для readLine(), но для всех остальных форматированных входных данных использовать DataInputStream.Если сравнить пятую часть с небольшой частью в IOStreamDemo.java, можно заметить, что здесь данные записываются перед текстом. Это связано с ошибкой самой Java 1.1, как показано следующим кодом:

//: IOBug.java
// Ошибка ввода-вывода Java 1.1 (и выше?)
import java.io.*;

public class IOBug {
  public static void main(String[] args) 
  throws Exception {
    DataOutputStream out = 
      new DataOutputStream(
        new BufferedOutputStream(
          new FileOutputStream("Data.txt")));
    out.writeDouble(3.14159);
    out.writeBytes("Значение числа π\n");
    out.writeBytes("А это π / 2:\n");
    out.writeDouble(3.14159 / 2);
    out.close();

    DataInputStream in = 
      new DataInputStream(
        new BufferedInputStream(
          new FileInputStream("Data.txt")));
    BufferedReader inbr = 
      new BufferedReader(
        new InputStreamReader(in));
    // Двойные значения, записанные ПЕРЕД строкой текста,
    // восстанавливаются правильно:
    System.out.println(in.readDouble());
    // Чтение строк текста:
    System.out.println(inbr.readLine());
    System.out.println(inbr.readLine());
    // Попытка чтения двойных значений после строки
    // приводит к исключению конца файла:
    System.out.println(in.readDouble());
  }
} ///:~

Показалось, что любое значение, записанное после вызова writeBytes(), невозможно восстановить. Это довольно ограниченная ошибка, надеюсь, она была исправлена к моменту вашего прочтения этой книги. Для проверки исправления запустите приведенную выше программу. Если вы не получили исключение и все значения были корректно распечатаны, значит, ошибка была исправлена.## Класс Redirecting

class Redirecting {
  public static void main(String[] args) {
    try {
      BufferedInputStream in = 
        new BufferedInputStream(
          new FileInputStream(
            "Redirecting.java"));
      // Вызывает сообщение о предотвращении:
      PrintStream out = 
        new PrintStream(
          new BufferedOutputStream(
            new FileOutputStream("test.out")));
      System.setIn(in);
      System.setOut(out);
      System.setErr(out);

Продолжение кода:

      // Дальнейший код программы
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Программа соединяет стандартный ввод с одним файлом и направляет стандартный вывод и ошибки в другой файл. Это ещё один пример сообщения "вопроса". При компиляции с флагом -deprecation получается следующее сообщение:

Примечание: Конструктор java.io.PrintStream(java.io.OutputStream) устарел.

Note: The constructor java.io.PrintStream(java.io.OutputStream) has been deprecated.

Однако методы System.setOut() и System.setErr() требуют использовать объект типа PrintStream, поэтому вызов конструктора PrintStream необходим. Это может показаться странным, поскольку Java 1.1 отказалась от использования этого конструктора, но при этом новые методы были добавлены в библиотеку, требующие использование именно PrintStream. Почему разработчики не использовали вместо него PrintWriter, который является новым и предпочитаемым заменителем? Это действительно вызывает недоумение.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-thinking-in-java-zh.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-thinking-in-java-zh.git
oschina-mirror
wizardforcel-thinking-in-java-zh
wizardforcel-thinking-in-java-zh
master