Текст книги "Java: руководство для начинающих (ЛП)"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 20 (всего у книги 36 страниц)
В списке исключений через запятую указываются исключения, которые может генерировать метод.
Возможно, вам покажется странным, что в ряде предыдущих примеров ключевое слово throws не указывалось при генерировании исключений за пределами методов. Дело в том, что исключения, генерируемые подклассом Error или RuntimeException, можно и не указывать в списке оператора throws. Исполняющая система Java по умолчанию предполагает, что метод может их генерировать. А исключения всех остальных типов следует непременно объявить с помощью ключевого слова throws. Если этого не сделать, возникнет ошибка при компиляции.
Пример применения оператора throws уже был представлен ранее в этой книге. Напомним, что при организации ввода с клавиатуры в метод main () потребовалось включить следующее выражение: throws java.io.IOException
Теперь вы знаете, зачем это было нужно. При вводе данных может возникнуть исключение IOException, а на тот момент вы еще не знали, как оно обрабатывается. Поэтому мы и указали, что исключение должно обрабатываться за пределами метода main (). Теперь, ознакомившись с исключениями, вы сможете без труда обработать исключение IOException самостоятельно.
Рассмотрим пример, в котором осуществляется обработка исключения IOException. В методе prompt () отображается сообщение, а затем выполняется ввод символов с клавиатуры. Такой ввод данных может привести к возникновению исключения IOException. Но это исключение не обрабатывается в методе prompt (). Вместо этого в объявлении метода указан оператор throws, т.е. обязанности по обработке данного исключению поручаются вызывающему методу. В данном случае вызывающим является метод main (), в котором и перехватывается исключение. // Применение ключевого слова throws, class ThrowsDemo { // Обратите внимание на оператор throws в объявлении метода. public static char prompt(String str) throws java.io.IOException { System.out.print(str + ": "); return (char) System.in.read() ; } public static void main(String args[]) { char ch; try { // В методе prompt () может быть сгенерировано исключение, // поэтому данный метод следует вызывать в блоке try. ch = prompt("Enter a letter"); } catch(java.io.IOException exc) { System.out.println("I/O exception occurred."); ch = 'X'; } System.out.println("You pressed " + ch); } }
Обратите внимание на одну особенность приведенного выше примера. Класс IOException относится к пакету java. io. Как будет разъяснено в главе 10, в этом пакете содержатся многие языковые средства Java для организации ввода-вывода. Следовательно, пакет java.io можно импортировать, а в программе указать только имя класса IOException. Новые средства обработки исключений, внедренные в версии JDK 7
С появлением версии JDK 7 механизм обработки исключений в Java был значительно усовершенствован благодаря внедрению трех новых средств. Первое из них поддерживает автоматическое управление ресурсами, позволяющее автоматизировать процесс освобождения таких ресурсов, как файлы, когда они больше не нужны. В основу этого средства положена расширенная форма оператора try, называемая оператором try с ресурсами и описываемая в главе 10 при рассмотрении файлов. Второе новое средство называется многократным перехватом, а третье – окончательным или более точным повторным генерированием исключений. Два последних средства рассматриваются ниже.
Многократный перехват позволяет перехватывать два или более исключения одним оператором catch. Как пояснялось ранее, после оператора try можно (и даже принято) указывать два или более оператора catch. И хотя каждый блок оператора catch, как правило, содержит свою особую кодовую последовательность, нередко в двух или более блоках оператора catch выполняется одна и та же кодовая последовательность, несмотря на то, что в них перехватываются разные исключения. Вместо того чтобы перехватывать каждый тип исключения в отдельности, теперь можно воспользоваться единым блоком оператора catch для обработки исключений, не дублируя код.
Для организации многократного перехвата следует указать список исключений в одном операторе catch, разделив их типы оператором поразрядного ИЛИ. Каждый параметр многократного перехвата неявно указывается как final. (По желанию модификатор доступа final можно указать и явным образом, но это совсем не обязательно.) А поскольку каждый параметр многократного перехвата неявно указывается как final, то ему нельзя присвоить новое значение.
В приведенной ниже строке кода показывается, каким образом многократный перехват исключений ArithmeticException и ArraylndexOutOfBoundsException указывается в одном операторе catch. catch(final ArithmeticException | ArraylndexOutOfBoundsException e) {
Ниже приведен краткий пример программы, демонстрирующий применение многократного перехвата исключений. // Применение средства многократного перехвата исключений. // Примечание: для компиляции этого кода требуется JDK 7 // или более поздняя версия данного комплекта, class MultiCatch { public static void main(String args[]) { int a=88, b=0; int result; char chrs[] = { 'А', 'В', 'C' }; for(int i=0; i < 2; i++) { try { if (i == 0) // сгенерировать исключение ArithmeticException result = а / b; else // сгенерировать исключение ArraylndexOutOfBoundsException chrs[5] = 'X'; } // В этом операторе catch организуется перехват обоих исключений, catch(ArithmeticException | ArraylndexOutOfBoundsException е) { System.out.println("Exception caught: " + e); } } System.out.println("After multi-catch."); } }
В данном примере программы исключение ArithmeticException генерируется при попытке деления на нуль, а исключение ArraylndexOutOfBoundsException – при попытке обращения за границы массива chrs. Оба исключения перехватываются одним оператором catch.
Средство более точного повторного генерирования исключений ограничивает этот процесс лишь теми проверяемыми типами исключений, которые генерируются в соответствующем блоке try и не обрабатываются в предыдущем блоке оператора catch, а также относятся к подтипу или супертипу указываемого параметра. И хотя такая возможность требуется нечасто, ничто не мешает теперь воспользоваться ею в полной мере. А для организации окончательного повторного генерирования исключений параметр оператора catch должен быть, по существу, указан как final. Это означает, что ему нельзя присвоить новое значение в блоке catch. Он может быть указан как final явным образом, хотя это и не обязательно. Встроенные в Java исключения
В стандартном пакете java. lang определены некоторые классы, представляющие стандартные исключения Java. Часть из них использовалась в предыдущих примерах программ. Наиболее часто встречаются исключения из подклассов стандартного класса RuntimeException. А поскольку пакет java. lang импортируется по умолчанию во все программы на Java, то исключения, производные от класса RuntimeException, становятся доступными автоматически. Их даже обязательно включать в список оператора throws. В терминологии языка Java такие исключения называют непроверяемыми, поскольку компилятор не проверяет, обрабатываются или генерируются подобные исключения в методе. Непроверяемые исключения, определенные в пакете java.lang, приведены в табл. 9.2, тогда как в табл. 9.3 – те исключения из пакета j ava. lang, которые следует непременно включать в список оператора throws при объявлении метода, если, конечно, в методе содержатся операторы, способные генерировать эти исключения, а их обработка не предусмотрена в теле метода. Такие исключения принято называть проверяемыми. В Java предусмотрен также ряд других исключений, определения которых содержатся в различных библиотеках классов. К их числу можно отнести упоминавшееся ранее исключение IOException.
Таблица 9.2. Непроверяемые исключения, определенные в пакете java.lang Исключение Описание ArithmeticException Арифметическая ошибка, например попытка деления на нуль ArraylndexOutOfBoundsException Попытка обращения за границы массива ArrayStoreException Попытка ввести в массив элемент, несовместимый с ним по типу ClassCastException Недопустимое приведение типов EnumConstNotPresentException Попытка использования нумерованного значения, которое не было определено ранее IllegalArgumentException Недопустимый параметр при вызове метода IllegalMonitorStateException Недопустимая операция контроля, например, ожидание разблокировки потока IllegalStateException Недопустимое состояние среды выполнения или приложения IllegalThreadStateException Запрашиваемая операция несовместима с текущим состоянием потока IndexOutOfBoundsException Недопустимое значение индекса NegativeArraySizeException Создание массива отрицательного размера NullPointerException Недопустимое использование пустой ссылки NumberFormatException Неверное преобразование символьной строки в число SecurityException Попытка нарушить систему защиты StringlndexOutOfBounds Попытка обращения к символьной строке за ее границами TypeNotPresentException Неизвестный тип UnsupportedOperationException Неподдерживаемая операция
Таблица 9.3. Проверяемые исключения, определенные в пакете java.lang Исключение Описание ClassNotFoundException Класс не найден CloneNotSupportedException Попытка клонирования объекта, не реализующего интерфейс Cloneable IllegalAccessException Доступ к классу запрещен InstantiationException Попытка создания объекта абстрактного класса или интер¬фейса InterruptedException Прерывание одного потока другим NoSuchFieldException Требуемое поле не существует NoSuchMethodException Требуемый метод не существует ReflectiveOperationException Суперкласс исключений, связанных с рефлексией (добавлен в версии JDK 7) Создание подклассов, производных от класса Exception
Несмотря на то что встроенные в Java исключения позволяют обрабатывать большинство ошибок, механизм обработки исключений не ограничивается только этими ошибками. В частности, можно создавать исключения для обработки потенциальных ошибок в прикладной программе. Создать исключение несложно. Для этого достаточно определить подкласс, производный от класса Exception, который, в свою очередь, является подклассом, порожденным классом Throwable. В создаваемый подкласс не обязательно включать реализацию каких-то методов. Сам факт существования такого подкласса позволяет использовать его в качестве исключения.
В классе Exception не определены новые методы. Он лишь наследует методы, предоставляемые классом Throwable. Таким образом, все исключения, включая и создаваемые вами, содержат методы класса Throwable. Конечно же, вы вольны переопределить в создаваемом вами классе один или несколько методов.
Ниже приведен пример, в котором создается исключение NonlntResultException. Оно генерируется в том случае, если результатом деления двух целых чисел является дробное число. В классе NonlntResultException содержатся два поля, предназначенные для хранения целых чисел, а также конструктор. В нем также переопределен метод toString (), что дает возможность выводить описание исключения с помощью метода println(). // Применение специально создаваемого исключения. // создать исключение class NonlntResultException extends Exception { int n; int d; NonlntResultException(int i, int j) { n = i; d = j; } public String toString() { return "Result of " + n + " / " + d + " is non-integer."; } } class CustomExceptDemo { public static void main(String args[]) { // В массиве numer содержатся нечетные числа, int numer[] = { 4, 8, 15, 32, 64, 127, 256, 512 }; int denom[] = { 2, 0, 4, 4, 0, 8 }; for(int i=0; i Результат выполнения данной программы выглядит следующим образом: 4 / 2 is 2 Can't divide by Zero! Result of 15 / 4 is non-integer. 32 / 4 is 8 Can't divide by Zero! Result of 127 / 8 is non-integer. No matching element found. No matching element found. Пример для опробования 9.1. Добавление исключений в класс очереди В этом проекте предстоит создать два класса исключении, которые будут использоваться классом очереди, разработанным в примере для опробования 8.1. Эти исключения должны указывать на переполнение и опустошение очереди, а генерировать их будут методы put () и get () соответственно. Ради простоты эти исключения добавляются в класс FixedQueue, но вы можете без труда внедрить их в любые другие классы очереди, разработанные в примере для опробования 8.1. Последовательность действий Создайте файл QExcDemo.java. Определите следующие исключения в файле QExcDemo.java: /* Пример для опробования 9.1. Добавление обработчиков исключений в класс очереди. */ // Исключение, указывающее на переполнение очереди, class QueueFullException extends Exception { int size; QueueFullException(int s) { size = s; } public String toString() { return "nQueue is full. Maximum size is " + size; } } // Исключение, указывающее на опустошение очереди, class QueueEmptyException extends Exception { public String toString() { return "nQueue is empty."; } } Исключение QueueFullException генерируется при попытке поместить элемент в уже заполненную очередь, а исключение QueueEmptyException – в ответ на попытку извлечь элемент из пустой очереди. Измените класс FixedQueue таким образом, чтобы при возникновении ошибки он генерировал исключение. Соответствующий код приведен ниже. Введите этот код в файл QExcDemo.java. // Класс, реализующий очередь фиксированного размера // для хранения символов. class FixedQueue implements ICharQ { private char q[]; // Массив для хранения элементов очереди, private int putloc, getloc; // Индексы размещения и извлечения // элементов очереди. // создать пустую очередь заданного размера public FixedQueue(int size) { q = new char[size+1]; // выделить память для очереди putloc = getloc = 0; } // поместить символ в очередь public void put(char ch) throws QueueFullException { if(putloc==q.length-1) throw new QueueFullException(q.length-1); putloc++; q[putloc] = ch; } // извлечь символ из очереди public char get() throws QueueEmptyException { if(getloc == putloc) throw new QueueEmptyException(); getloc++; return q[getloc]; } } Добавление исключений в класс FixedQueue выполняется в два этапа. Сначала в определении методов get () и put () указывается оператор throws с типом генерируемого исключения. А затем в этих методах организуется генерирование исключений при возникновении ошибок. Используя исключения, можно организовать обработку ошибок в вызывающей части программы наиболее рациональным способом. Как вы помните, в предыдущих версиях рассматриваемой здесь программы выводились только сообщения об ошибках. А генерирование исключений является более профессиональным подходом к разработке данной программы. Для опробования усовершенствованного класса FixedQueue введите в файл QExcDemo.java приведенный ниже исходный код класса QExcDemo. // Демонстрация исключений при обращении с очередью, class QExcDemo { public static void main(String args[]) { FixedQueue q = new FixedQueue(10); char ch; int i; try { // Переполнение очереди. for(i=0; i < 11; i++) { System.out.print("Attempting to store : " + (char) ('A' + i)); q.put((char) (fA' + i)); System.out.println(" – OK"); } System.out.println(); } catch (QueueFullException exc) { System.out.println(exc); } System.out.println(); try { // Попытка извлечь символ из пустой очереди. for(i=0; i < 11; i++) { System.out.print("Getting next char: "); ch = q.get(); System.out.println(ch); } } catch (QueueEmptyException exc) { System.out.println(exc); } } } Класс FixedQueue реализует интерфейс ICharQ, в котором определены методы get () и put (), и поэтому интерфейс ICharQ необходимо изменить таким образом, чтобы в нем отражалось наличие операторов throws. Ниже приведен видоизмененный соответственно код интерфейса ICharQ. Не забывайте о том, что он должен храниться в файле ICharQjava. // Интерфейс очереди для хранения символов с генерированием исключений, public interface ICharQ { // поместить символ в очередь void put(char ch) throws QueueFullException; // извлечь символ из очереди char get() throws QueueEmptyException; } Скомпилируйте сначала новую версию исходного файла IQChar. j ava, а затем исходный файл QExcDemo. java и запустите программу QExcDemo на выполнение. В итоге вы получите следующий результат ее выполнения: Attempting to store A – OK Attempting to store В – OK Attempting to store С – OK Attempting to store D – OK Attempting to store E – OK Attempting to store F – OK Attempting to store G – OK Attempting to store H – OK Attempting to store I – OK Attempting to store J – OK Attempting to store К Queue is full. Maximum size is 10 Getting next char: A Getting next char: В Getting next char: С Getting next char: D Getting next char: E Getting next char: F Getting next char: G Getting next char: H Getting next char: I Getting next char: J Getting next char: Queue is empty. Упражнение для самопроверки по материалу главы 9 Какой класс находится на вершине иерархии исключений? Объясните вкратце, как пользоваться ключевыми словами try и catch? Какая ошибка допущена в приведенном ниже фрагменте кода?// ... vals[18] = 10; catch (ArraylndexOutOfBoundsException exc) { // обработать ошибку } Что произойдет, если исключение не будет перехвачено? Какая ошибка допущена в приведенном ниже фрагменте кода?class A extends Exception { ... class В extends А { ... // ... try { // ... } catch (A exc) { ... } catch (В exc) { ... } Может ли внутренний блок catch повторно генерировать исключение, которое будет обработано во внешнем блоке catch? Блок finally – последний фрагмент кода, выполняемый перед завершением программы. Верно или неверно? Обоснуйте свой ответ. Исключения какого типа необходимо явно объявлять с помощью оператора throws, включаемого в объявление метода? Какая ошибка допущена в приведенном ниже фрагменте кода?class MyClass { // ... } // ... throw new MyClass (); Отвечая на вопрос 3 упражнения для самопроверки по материалу главы 6, вы создали класс Stack. Добавьте в него специальные исключения для реагирования на попытку поместить элемент в переполненный стек и извлечь элемент из пустого стека. Какими тремя способами можно сгенерировать исключение? Назовите два подкласса, производных непосредственно от класса Throwable. Что такое многократный перехват? Следует ли перехватывать в программе исключения типа Error? Глава 10 Ввод-вывод данных Основные навыки и понятия Представление о потоках ввода-вывода Отличия байтовых и символьных потоков Классы для поддержки байтовых потоков Классы для поддержки символьных потоков Представление о встроенных потоках Применение байтовых потоков Использование байтовых потоков для файлового ввода-вывода Автоматическое закрытие файлов с помощью оператора try с ресурсами Чтение и запись двоичных данных Манипулирование файлами с произвольным доступом Применение символьных потоков Использование символьных потоков для файлового ввода-вывода Применение оболочек типов Java для преобразования символьных строк в числа В примерах программ, приводившихся в предыдущих главах, уже применялись отдельные части системы ввода-вывода в Java, в частности метод println (), но делалось это без каких-либо формальных пояснений. Система ввода-вывода основана в Java на иерархии классов, поэтому ее функции и особенности нельзя было представлять до тех пор, пока не были рассмотрены классы, наследование и исключения. А теперь настал черед и для средств ввода-вывода. Приступая к изучению системы ввода вырода Java, приготовьтесь к длительной и кропотливой работе. Система ввода-вывода в Java довольно обширна и содержит немало классов, интерфейсов и методов. Объясняется это, в частности, тем, что в Java, по существу, определены две полноценные системы ввода-вывода: одна – для обмена байтами, другая – для обмена символами. Здесь нет возможности рассмотреть все аспекты ввода-вывода в Java, ведь для этого бы потребовалась отдельная книга. Поэтому в данной главе будут рассмотрены лишь наиболее важные и часто используемые языковые средства ввода-вывода. Правда, элементы системы ввода-вывода в Java тесно взаимосвязаны, и поэтому, уяснив основы, вы легко освоите все остальные свойства этой системы. Прежде чем приступать к рассмотрению системы ввода-вывода, необходимо сделать следующее замечание. Классы, описанные в этой главе, предназначены для консольного и файлового ввода-вывода. Они не применяются для создания графических пользовательских интерфейсов. Поэтому ими не имеет смысла пользоваться при создании оконных приложений. Для графических интерфейсов предусмотрены другие языковые средства. Они будут представлены в главе 14 при рассмотрении апплетов, а также в главе 15, служащей введением в библиотеку Swing. (Swing – это современный набор инструментальных средств, ориентированных на создание графических пользовательских интерфейсов приложений.) Организация системы ввода-вывода в Java на потоках Ввод-вывод в программах на Java осуществляется посредством потоков. Поток – это некая абстракция производства или потребления информации. С физическим устройством поток связывает система ввода-вывода. Все потоки действуют одинаково – даже если они связаны с разными физическими устройствами. Поэтому классы и методы ввода-вывода могут применяться к самым разным типам устройств. Например, методами вывода на консоль можно пользоваться и для вывода в файл на диске. Для реализации потоков используется иерархия классов, содержащихся в пакете java.io. Байтовые и символьные потоки В современных версиях Java определены два типа потоков: байтовые и символьные. (В первоначальных версиях Java были доступны только байтовые потоки, тогда как символьные потоки были реализованы в дальнейшем.) Байтовые потоки предоставляют удобные средства для ввода и вывода байтов. Они используются, например, при чтении и записи двоичных данных. В особенности они полезны для обращения с файлами. А символьные потоки ориентированы на обмен символьными данными. В них применяется кодировка в уникоде (Unicode), а следовательно, программы, в которых используются символьные потоки, легко поддаются локализации на разные языки мира. В некоторых случаях символьные потоки обеспечивают более высокую эффективность по сравнению с байтовыми. Необходимость поддерживать два разных типа потоков ввода-вывода привела к созданию двух иерархий классов (одна для байтовых, другая для символьных данных). Из-за того что число классов достаточно велико, на первый взгляд система ввода-вывода кажется сложнее, чем она есть на самом деле. Но не следует забывать, что функциональные возможности для байтовых потоков дублируются соответствующими средствами для символьных потоков. Следует также иметь в виду, что на самом нижнем уровне все средства ввода-вывода имеют байтовую организацию. А символьные потоки лишь предоставляют удобные и эффективные инструменты для обработки символов. Классы байтовых потоков Для определения байтовых потоков служат две иерархии классов. На их вершине находятся два абстрактных класса: InputStream и OutputStream. В классе InputStream определены свойства, общие для байтовых потоков ввода, а в классе OutputStream – свойства, общие для байтовых потоков вывода. Производными от классов InputStream и OutputStream являются конкретные подклассы, реализующие различные функциональные возможности и учитывающие особенности обмена данными с разными устройствами, например ввода-вывода в файлы на диске. Классы байтовых потоков приведены в табл. 10.1. Не следует пугаться большого количества этих классов: изучив один из них, легко освоить остальные. Таблица 10.1. Классы байтовых потоков Класс байтового потока Описание BufferedlnputStream Буферизованный поток ввода BufferedOutputStream Буферизованный поток вывода ByteArrayInputStream Поток ввода для чтения из байтового массива ByteArrayOutputStream Поток вывода для записи в байтовый массив DatalnputStream Поток ввода с методами для чтения стандартных типов данных Java DataOutputStream Поток вывода с методами для записи стандартных типов данных Java FileInputStream Поток ввода для чтения из файла FileOutputStream Поток вывода для записи в файл FilterlnputStream Подкласс, производный от класса InputStream FilterOutputStream Подкласс, производный от класса OutputStream InputStream Абстрактный класс, описывающий потоковый ввод ObjectInputStream Поток для ввода объектов ObjectOutputStream Поток для вывода объектов OutputStream Абстрактный класс, описывающий потоковый вывод PipedlnputStream Поток конвейерного ввода PipedOutputStream Поток конвейерного вывода PrintStream Поток вывода с методами print () и println () PushbacklnputStream Поток ввода с возвратом прочитанных байтов в поток RandomAccessFile Класс, поддерживающий файловый ввод-вывод с произвольным доступом SequenceInputStream Поток ввода, сочетающий в себе несколько потоков ввода для поочередного чтения данных из них Классы символьных потоков Для определения символьных потоков служат две иерархические структуры классов, на вершине которых находятся абстрактные классы Reader и Writer соответственно. Класс Reader и его подклассы используются для чтения, а класс Writer и его подклассы – для записи данных. Конкретные классы, производные от классов Reader и Writer, оперируют символами в уникоде. Классы, производные от классов Reader и Writer, предназначены для выполнения различных операций ввода-вывода символов. Символьные классы присутствуют в Java параллельно с байтовыми классами. Классы символьных потоков приведены в табл. 10.2. Таблица 10.2. Классы символьных потоков Класс символьного потока Описание BufferedReader Буферизованный поток ввода символов BufferedWriter Буферизованный поток вывода символов CharArrayReader Поток ввода для чтения из символьного массива CharArrayWriter Поток вывода для записи в символьный массив FileReader Поток ввода для чтения символов из файла FileWriter Поток вывода для записи символов в файл FilterReader Класс для чтения символов с фильтрацией FilterWriter Класс для записи символов с фильтрацией InputStreamReader Поток ввода с преобразованием байтов в символы LineNumberReader Поток ввода с подсчетом символьных строк OutputStreamWriter Поток вывода с преобразованием символов в байты PipedReader Поток конвейерного ввода PipedWriter Поток конвейерного вывода PrintWriter Поток вывода с методами print () и println () PushbackReader Поток ввода с возвратом прочитанных символов в поток Reader Абстрактный класс, описывающий потоковый ввод символов StringReader Поток ввода для чтения из символьной строки StringWriter Поток вывода для записи в символьную строку Writer Абстрактный класс, описывающий потоковый вывод символов Встроенные потоки Как вам должно быть уже известно, во все программы на Java автоматически импортируется пакет java. lang. В этом пакете определен класс System, инкапсулирующий некоторые элементы среды выполнения программ. Помимо прочего, в нем содержатся предопределенные переменные in, out и err, представляющие стандартные потоки ввода-вывода. Эти поля объявлены как public, final и static. А это означает, что ими можно пользоваться в любой другой части программы, не ссылаясь на конкретный объект типа System. Переменная System.out ссылается на поток стандартного вывода. По умолчанию этот поток связан с консолью. А переменная System, in ссылается на поток стандартного ввода (по умолчанию с клавиатуры). И наконец, переменная System.err ссылается на поток стандартных сообщений об ошибках, которые по умолчанию выводятся на консоль. По мере необходимости все эти потоки могут быть перенаправлены на другие совместимые устройства ввода-вывода. Поток System.in представляет собой объект типа InputStream, а потоки System.out и System.err – объекты типа PrintStream. Хотя эти потоки обычно используются для чтения и записи символов, они на самом деле являются байтовыми потоками. Дело в том, что эти потоки были определены в первоначальной спецификации Java, где символьные потоки вообще не были предусмотрены. Как станет ясно в дальнейшем, для этих потоков можно по необходимости создать оболочки, превратив их в символьные потоки. Применение байтовых потоков Начнем рассмотрение системы ввода-вывода в Java с байтовых потоков. Как пояснялось ранее, на вершине иерархии байтовых потоков находятся классы InputStream и OutputStream. Методы из класса InputStream приведены в табл. 10.3, а методы из класса OutputStream – в табл. 10.4. При возникновении ошибок в процессе выполнения методы из классов InputStream и OutputStream могут генерировать исключения типа IOException. Методы, определенные в этих двух абстрактных классах, доступны во всех подклассах. Таким образом, они формируют минимальный набор функций ввода-вывода, общих для всех байтовых потоков. Таблица 10.3. Методы, определенные в классе InputStream Метод Описание int available() Возвращает количество байтов, доступных для чтения void close () Закрывает поток ввода. При последующей попытке чтения из потока генерируется исключение IOException void mark(int numBytes) Ставит отметку на текущей позиции в потоке. Отметка доступна до тех пор, пока на будет прочитано количество байтов, определяемое параметром numBytes boolean markSupported() Возвращает логическое значение true, если методы mark() и reset () поддерживаются в вызывающем потоке int read() Возвращает целочисленное представление следующего байта в потоке. Если достигнут конец потока, возвращается значение -1 int read(byte buffer[]) Предпринимает попытку прочитать количество байтов, определяемое выражением buffer, length, в массив buffer и возвращает фактическое количество успешно прочитанных байтов. Если достигнут конец потока, возвращается значение -1 int read(byte buffer[], int offset, int numBytes) Предпринимает попытку прочитать количество байтов, определяемое параметром numBytes, в массив buffer, начиная с элемента buffer[offset]. Если достигнут конец потока, возвращается значение -1 void reset() Устанавливает указатель ввода на помеченной ранее позиции long skip (long numBytes) Пропускает количество байтов, определяемое параметром numBytes, в потоке ввода. Возвращает фактическое количество пропущенных байтов