Наследование и классы-коллекции
p align="left">boolean add (Object obj) -- добавляет элемент obj в конец коллекции; возвращает false, если такой элемент в коллекции уже есть, а коллекция не допускает повторяющиеся элементы; возвращает true, если добавление прошло удачно; boolean addAll (Collection coll) -- добавляет все элементы коллекции coll в конец данной коллекции; void clear() -- удаляет все элементы коллекции; boolean contains (Object obj) -- проверяет наличие элемента obj в коллекции; boolean containsAll (Collection coll) -- проверяет наличие всех элементов коллекции coll в данной коллекции; boolean isEmpty() -- проверяет, пуста ли коллекция; iterator iterator () -- возвращает итератор данной коллекции; boolean remove (Object obj) -- удаляет указанный элемент из коллекции; возвращает false, если элемент не найден, true, если удаление прошло успешно; boolean removeAll (Collection coll) -- удаляет элементы указанной коллекции, лежащие в данной коллекции; boolean retainAll (Collection coll) -- удаляет все элементы данной коллекции, кроме элементов коллекции coll; int size () -- возвращает количество элементов в коллекции; Object [] toArray() -- возвращает все элементы коллекции в виде массива; Objectn toArray(Object[] a) -- записывает все элементы коллекции в массив а, если в нем достаточно места. Интерфейс List Интерфейс List из пакета java.util, расширяющий интерфейс Collection, описывает методы работы с упорядоченными коллекциями. Иногда их называют последовательностями (sequence). Элементы такой коллекции пронумерованы, начиная от нуля, к ним можно обратиться по индексу. В отличие от коллекции Set элементы коллекции List могут повторяться. Класс Vector -- одна из реализаций интерфейса List. Интерфейс List добавляет к методам интерфейса Collection методы, использующие индекс index элемента: void add(int index, Object obj) -- вставляет элемент obj в позицию index; старые элементы, начиная с позиции index, сдвигаются, их индексы увеличиваются на единицу; boolean addAll(int index, Collection coll) -- вставляет все элементы коллекции coll; Object get(int index) -- возвращает элемент, находящийся в позиции index; int indexOf(Object obj) -- возвращает индекс первого появления элемента obj в коллекции; int lastindexOf (Object obj) -- возвращает индекс последнего появления элемента obj в коллекции; Listiterator listiterator () -- возвращает итератор коллекции; Listiterator listiterator (int index) -- возвращает итератор конца коллекции от позиции index; Object Set (int index, Object obj) -- заменяет элемент, находящийся в позиции index, элементом obj; List subListUnt from, int to) -- возвращает часть коллекции от позиции from включительно до позиции to исключительно. Интерфейс Set Интерфейс Set из пакета java.util, расширяющий интерфейс Collection, описывает неупорядоченную коллекцию, не содержащую повторяющихся элементов. Это соответствует математическому понятию множества (Set). Такие коллекции удобны для проверки наличия или отсутствия у элемента свойства, определяющего множество. Новые методы в интерфейс Set не добавлены, просто метод add () не станет добавлять еще одну копию элемента, если такой элемент уже есть в множестве. Этот интерфейс расширен интерфейсом SortedSet. Интерфейс SortedSet Интерфейс SortedSet из пакета java.util, расширяющий интерфейс Set, описывает упорядоченное множество, отсортированное по естественному порядку возрастания его элементов или по порядку, заданному реализацией интерфейса Comparator. Элементы не нумеруются, но есть понятие первого, последнего, большего и меньшего элемента. Дополнительные методы интерфейса отражают эти понятия: Comparator comparator () -- возвращает способ упорядочения коллекции; Object first()-- возвращает первый, меньший элемент коллекции; SortedSet headSet (Object toElement) -- возвращает начальные, меньшие элементы до элемента toElement исключительно; Object last () -- возвращает последний, больший элемент коллекции; SortedSet subSet(Object fromElement, Object toElement) -- возвращает подмножество коллекции от элемента fromElement включительно до элемента toElement исключительно; SortedSet tailSet (Object fromElement) -- возвращает последние, большие элементы коллекции от элемента fromElement включительно. Интерфейс Map Интерфейс Map из пакета java.util описывает коллекцию, состоящую из пар "ключ -- значение". У каждого ключа только одно значение, что соответствует математическому понятию однозначной функции или отображения. Такую коллекцию часто называют еще словарем (dictionary) или ассоциативным массивом (associative array). Обычный массив -- простейший пример словаря с заранее заданным числом элементов. Это отображение множества первых неотрицательных целых чисел на множество элементов массива, множество пар "индекс массива - элемент массива". Класс HashTable -- одна из реализаций интерфейса мар. Интерфейс Map содержит методы, работающие с ключами и значениями: boolean containsKey (Object key) -- проверяет наличие ключа key; boolean containsValue (Object value) -- проверяет наличие значения value; Set entrySet () -- представляет коллекцию в виде множества, каждый элемент которого -- пара из данного отображения, с которой можно работать методами вложенного интерфейса Map.Entry; Object get (Object key) -- возвращает значение, отвечающее ключу key; Set keySet() -- представляет ключи коллекции в виде множества; Object put(Object key, Object value) -- добавляет пару "key-- value", если такой пары не было, и заменяет значение ключа key, если такой ключ уже есть в коллекции; void putAll (Map m) -- добавляет к коллекции все пары из отображения m; Collection values() -- представляет все значения в виде коллекции. В интерфейс Mар вложен интерфейс Map.Entry, содержащий методы работы с отдельной парой. Интерфейс SortedMap Интерфейс SortedMap, расширяющий интерфейс Map, описывает упорядоченную по ключам коллекцию мар. Сортировка производится либо в естественном порядке возрастания ключей, либо, в порядке, описываемом в интерфейсе Comparator. Элементы не нумеруются, но есть понятия большего и меньшего из двух элементов, первого, самого маленького, и последнего, самого большого элемента коллекции. Эти понятия описываются следующими методами: Comparator comparator () -- возвращает способ упорядочения коллекции; Object firstKey() -- возвращает первый, меньший элемент коллекции; SortedMap headMap(Object toKey) -- возвращает начало коллекции до элемента с ключом toKey исключительно; Object lastKey() -- возвращает последний, больший ключ коллекции; SprtedMap subMap (Object fromKey, Object toKey) -- возвращает часть коллекции от элемента с ключом fromKey включительно до элемента с ключом toKey исключительно; SortedMap tallMap (Object fromKey) -- возвращает остаток коллекции от элемента fromKey включительно. Вы можете создать свои коллекции, реализовав рассмотренные интерфейсы. Это дело трудное, поскольку в интерфейсах много методов. Чтобы облегчить эту задачу, в Java API введены частичные реализации интерфейсов -- абстрактные классы-коллекции. Абстрактные классы-коллекции Эти классы лежат в пакете java.util, Абстрактный класс AbstractGollection реализует интерфейс Collection, но оставляет нереализованными методы iterator (), size (). Абстрактный класс AbstractList реализует интерфейс List, но оставляет нереализованным метод get() и унаследованный метод size() Этот класс позволяет реализовать коллекцию спрямым доступом к элементам, подобно массиву Абстрактный класс AbstractSequentialList реализует интерфейс List, но оставляет нереализованным метод listiterator(int index) и унаследованный метод size(). Данный класс позволяет реализовать коллекции с последовательным доступом к элементам с помощью итератора Listiterator Абстрактный класс AbstractSet реализует интерфейс Set, но оставляет нереализованными методы, унаследованные от AbstractCollection Абстрактный класс AbstractMap реализует интерфейс Map, но оставляет нереализованным метод entrySet (), Наконец, в составе Java API есть полностью реализованные классы-коллекции помимо уже рассмотренных классов Vector, Stack, Hashtable и Properties, Это классы ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, WeakHashMap , Для работы с этими классами разработаны интерфейсы Iterator, Listiterator, Comparator И классы Arrays И Collections. Перед тем как рассмотреть использование данных классов, обсудим понятие итератора. Интерфейс Iterator В 90-х годах было решено заносить данные в определенную коллекцию, скрыв ее внутреннюю структуру, а для работы с данными использовать методы этой коллекции. В частности, задачу обхода возложили на саму коллекцию. В Java API введен интерфейс Iterator, описывающий способ обхода всех элементов коллекции. В каждой коллекции есть метод iterator(), возвращающий реализацию интерфейса Iterator для указанной коллекции. Получив эту реализацию, можно обходить коллекцию в некотором порядке, определенном данным итератором, с помощью методов, описанных в интерфейсе Iterator и реализованных в этом итераторе. Подобная техника использована в классе StringTokenizer. В интерфейсе Iterator описаны всего три метода: o логический метод hasNext () возвращает true, если обход еще не завершен; o метод next() делает текущим следующий элемент коллекции и возвращает его в виде объекта класса Object; o метод remove() удаляет текущий элемент коллекции. Можно представить себе дело так, что итератор -- это указатель на элемент коллекции. При создании итератора указатель устанавливается перед первым элементом, метод next() перемещает указатель на первый элемент и показывает его. Следующее применение метода next() перемещает указатель на второй элемент коллекции и показывает его. Последнее применение метода next() выводит указатель за последний элемент коллекции. Метод remove(), пожалуй, излишен, он уже не относится к задаче обхода коллекции, но позволяет при просмотре коллекции удалять из нее ненужные элементы. Пример. Использование итератора вектора Vector v = new Vector(); String s = "Строка, которую мы хотим разобрать на слова."; StringTokenizer st = new StringTokenizer(s, " \t\n\r,."); while (st.hasMoreTokens()){ // Получаем слово и заносим в вектор. v.add(st.nextToken()); // Добавляем в конец вектора } System.out.print*Ln(v.firstElement(}); // Первый элемент System.out.println(v.lastElement()); // Последний элемент v.SetSize(4); // Уменьшаем число элементов v.add("собрать."); // Добавляем в конец укороченного вектора v.Set(3, "опять"); // Ставим в позицию 3 for (int i = 0; i < v.sizeO; i++) // Перебираем весь вектор System.out.print(v.get(i) + "."); System.out.println(}; Iterator it = v.Iterator (); // Получаем итератор вектора try{ while(it.hasNext()) // Пока в векторе есть элементы, System.out.println(it.next()); // выводим текущий элемент }catch(Exception e){} Интерфейс Listlterator Интерфейс ListIterator расширяет интерфейс Iterator, обеспечивая перемещение по коллекции как в прямом, так и в обратном направлении. Он может быть реализован только в тех коллекциях, в которых есть понятия следующего и предыдущего элемента и где элементы пронумерованы. В интерфейс ListIterator добавлены следующие методы: void add (Object element) -- добавляет элемент element перед текущим элементом; boolean hasPrevious() -- возвращает true, если в коллекции есть элементы, стоящие перед текущим элементом; int nextindex() -- возвращает индекс текущего элемента; если текущим является последний элемент коллекции, возвращает размер коллекции; Object previous() -- возвращает предыдущий элемент и делает его текущим; int previous index() -- возвращает индекс предыдущего элемента; void Set (Object element) -- заменяет текущий элемент элементом element; выполняется сразу после next() или previous(). Как видите, итераторы могут изменять коллекцию, в которой они работают, добавляя, удаляя и заменяя элементы. Чтобы это не приводило к конфликтам, предусмотрена исключительная ситуация, возникающая при попытке использования итераторов параллельно "родным" методам коллекции. Именно поэтому в следующем примере действия с итератором заключены в блок try(){}-- catch(){}. Пример с использованием итератора ListIterator. Vector v = new Vector(); String s = "Строка, которую мы хотим разобрать на слова."; StringTokenizer st = new StringTokenizer(s, " \t\n\r,."); while (st.hasMoreTokens()){ // Получаем слово и заносим в вектор v.add(st.nextToken()); // Добавляем в конец вектора } ListIterator lit = v.listlterator(); // Получаем итератор вектора // Указатель сейчас находится перед началом вектора try{ while(lit.hasNext()) // Пока в векторе есть элементы System.out.println(lit.next()); // Переходим к следующему // элементу и выводим его // Теперь указатель за концом вектора. Пройдем к началу while (lit.hasPrevious())System.out.println(lit.previous()); } catch (Exception e) {} Посмотрим теперь, какие возможности предоставляют классы-коллекции Java2. Классы, создающие списки Класс ArrayList полностью реализует интерфейс List и итератор типа Iterator. Класс ArrayList очень похож на класс Vector, имеет тот же набор методов и может использоваться в тех же ситуациях. В классе ArrayList три конструктора; ArrayList()-- создает пустой объект; ArrayList(Collection coll) -- создает объект, содержащий все элементы коллекции coll; ArrayList (int initCapacity) -- создает пустой Объект емкости initCapacity. Единственное отличие класса ArrayList от класса Vector заключается в том, что класс ArrayList не синхронизован. Это означает что одновременное изменение экземпляра этого класса несколькими подпроцессами приведет к непредсказуемым результатам. Сравнение элементов коллекций Интерфейс Comparator описывает два метода сравнения: int compare (Object obji, Object obj2) -- возвращает отрицательное число, если obj1 в каком-то смысле меньше obj2; нуль, если они считаются равными; положительное число, если obj1 больше obj2. Этот метод сравнения обладает свойствами тождества, антисимметричности и транзитивности; boolean equals (Object obj) -- сравнивает данный объект с объектом obj, возвращая true, если объекты совпадают в каком-либо смысле, заданном этим методом. Для каждой коллекции можно реализовать эти два метода, задав конкретный способ сравнения элементов, и определить объект класса SortedMap вторым конструктором. Элементы коллекции будут автоматически отсортированы в заданном порядке. Классы, создающие множества Класс HashSet полностью реализует интерфейс Set и итератор типа Iterator. Класс HashSet используется в тех случаях, когда надо хранить только одну копию каждого элемента. В классе HashSet четыре конструктора: HashSet() -- создает пустой объект с показателем загруженности 0,75; HashSet (int capacity) -- создает пустой объект с начальной емкостью capacity и показателем загруженности 0,75; HashSet (int capacity, float loadFactor) -- создает пустой объект с начальной емкостью capacity и показателем загруженности loadFactor; HashSet (Collection coll) -- создает объект, содержащий все элементы коллекции coll, с емкостью, равной удвоенному числу элементов коллекции coll, но не менее 11, и показателем загруженности 0,75. Упорядоченные множества Класс TreeSet полностью реализует интерфейс SortedSet и итератор типа Iterator. Класс TreeSet реализован как бинарное дерево поиска, значит, его элементы хранятся в упорядоченном виде. Это значительно ускоряет поиск нужного элемента. Порядок задается либо естественным следованием элементов, либо объектом, реализующим интерфейс сравнения Comparator. Этот класс удобен при поиске элемента во множестве, например, для проверки, обладает ли какой-либо элемент свойством, определяющим множество. В классе TreeSet четыре конструктора: TreeSet() -- создает пустой объект с естественным порядком элементов; TreeSet (Comparator с) -- создает пустой объект, в котором порядок задается объектом сравнения с; TreeSet (Collection coll) -- создает объект, содержащий все элементы коллекции coll, с естественным порядком ее элементов; TreeSet (SortedMap sf) -- создает объект, содержащий все элементы отображения sf, в том же порядке. Действия с коллекциями Коллекции предназначены для хранения элементов в удобном для дальнейшей обработки виде. Очень часто обработка заключается в сортировке элементов и поиске нужного элемента. Эти и другие методы обработки собраны в класс Collections. Методы класса Collections Все методы класса Collections статические, ими можно пользоваться, не создавая экземпляры классу Collections Как обычно в статических методах, коллекция, с которой работает метод, задается его аргументом. Сортировка может быть сделана только в упорядочиваемой коллекции, реализующей интерфейс List. Для сортировки в классе Collections есть два метода: static void sort (List coll) -- сортирует в естественном порядке возрастания коллекцию coll, реализующую интерфейс List; static void sort (List coll, Comparator c) -- сортирует коллекцию coll в порядке, заданном объектом с. После сортировки можно осуществить бинарный поиск в коллекции: static int binarySearch(List coll, Object element) -- отыскивает элемент element в отсортированной в естественном порядке возрастания коллекции coll и возвращает индекс элемента или отрицательное число, если элемент не найден; отрицательное число показывает индекс, с которым элемент element был бы вставлен в коллекцию, с обратным знаком; static int binarySearchfList coll, Object element, Comparator c) -- то же, но коллекция отсортирована в порядке, определенном объектом с. Четыре метода находят наибольший и наименьший элементы в упорядочиваемой коллекции: static Object max (Collection coll) -- возвращает наибольший в естественном порядке элемент коллекции coll; static Object max (Collection coll, Comparator c) -- то же в порядке,заданном объектом с; static Object min (Collection coll) -- возвращает наименьший в естественном порядке элемент коллекции coll; static Object min(Collection coll, Comparator c) -- то же в порядке, заданном объектом с. Два метода "перемешивают" элементы коллекции в случайном порядке: static void shuffle (List coll) -- случайные числа задаются по умолчанию; static void shuffle (List coll, Random r) -- случайные числа определяются объектом r. Метод reverse (List coll) меняет порядок расположения элементов на обратный. Метод copy (List from, List to) копирует коллекцию from в коллекцию to. Метод fill (List coll, Object element) заменяет все элементы существующей коллекции coll элементом element. Приложение 4. Работа с датами и временемРабота с датами и временем Методы работы с датами и показаниями времени собраны в два класса: Calendar и Date из пакета java.util. Объект класса Date хранит число миллисекунд, прошедших с 1 января 1970 г. 00:00:00 по Гринвичу. Это "день рождения" UNIX, он называется "Epoch". Класс Date удобно использовать для отсчета промежутков времени в миллисекундах. Получить текущее число миллисекунд, прошедших с момента Epoch на той машине, где выполняется программа, можно статическим методом System.currentTimeMillis() В классе Date два конструктора. Конструктор Date() заносит в создаваемый объект текущее время машины, на которой выполняется программа, по системным часам, а конструктор Date(long millisec) -- указанное число. Получить значение, хранящееся в объекте, можно методом long getTime(), установить новое значение -- методом setTime(long newTime). Три логических метода сравнивают отсчеты времени: boolean after (long when) -- возвращает true, если время when больше данного; boolean before (long when) -- возвращает true, если время when меньше данного; boolean after (Object when) -- возвращает true, если времена when -- объекта класca Date и данное совпадают. Еще два метода, сравнивая отсчеты времени, возвращают отрицательное число типа int, если данное время меньше аргумента when; нуль, если времена совпадают; положительное число, если данное время больше аргумента when: int compareTo(Date when); int compareTo(Оbject when) -- если when не относится к объектам класса Date, создается исключительная ситуация. Преобразование миллисекунд, хранящихся в объектах класса Date, в текущее время и дату производится методами класса Calendar. Класс Calendar Класс Calendar -- абстрактный, в нем собраны общие свойства календарей: юлианского, григорианского, лунного. В Java API пока есть только одна его реализация -- подкласс GregorianCalendar. Поскольку Сalendar -- абстрактный класс, его экземпляры создаются четырьмя статическими методами по заданной локали и/или часовому поясу: Calendar getlnstance() Calendar getlnstance(Locale loc) Calendar getlnstance(TimeZone tz) Calendar getlnstance(TimeZone tz, Locale loc) Для работы с месяцами определены целочисленные константы от JANUARY до DECEMBER, для работы с днями недели -- константы MONDAY до SUNDAY. Первый день недели можно узнать методом int getFirstDayOfWeek(), a установить -- методом setFirstDayOfWeek(int day), например: setFirstDayOfWeek(Calendar.MONDAY) Остальные методы позволяют просмотреть время и часовой пояс или установить их. Представление даты и времени Различные способы представления дат и показаний времени можно осуществить методами, собранными в абстрактный класс DateFormat и его подкласс SimpleDateFormat из пакета Java. text. Класс DateFormat предлагает четыре стиля представления даты и времени: стиль SHORT представляет дату и время в коротком числовом виде: 27.04.01 17:32; в локали США: 4/27/01 5:32 РМ; стиль MEDIUM задает год четырьмя цифрами и показывает секунды: 27.04.2001 17:32:45; в локали США месяц представляется тремя буквами; стиль LONG представляет месяц словом и добавляет часовой пояс: 27 апрель 2001 г. 17:32:45 GMT+03.-00; стиль FULL в русской локали таков же, как и стиль LONG; в локали США добавляется еще день недели. Есть еще стиль DEFAULT, совпадающий со стилем MEDIUM. При создании объекта класса SimpieDateFormat можно задать в конструкторе шаблон, определяющий какой-либо другой формат, например: SimpieDateFormat sdf = new SimpieDateFormat("dd-MM-yyyy hh.mm"); System.out.println(sdf.format(new Date())); Получим вывод в таком виде: 27-07-2004 17.32. Приложение 5. Файловый ввод/вывод.Поскольку файлы в большинстве современных операционных систем понимаются как последовательность байтов, для файлового ввода/вывода создаются байтовые потоки с помощью классов FileInputStream и FileOutputStream. Очень много файлов содержат тексты, составленные из символов. Несмотря на то, что символы могут храниться в кодировке Unicode, эти тексты чаще всего записаны в байтовых кодировках. Поэтому и для текстовых файлов можно использовать байтовые потоки. В таком случае со стороны программы придется организовать преобразование байтов в символы и обратно. Чтобы облегчить это преобразование, в пакет java.io введены классы FileReader и FileWriter. Они организуют преобразование потока: со стороны программы потоки символьные, со стороны файла -- байтовые. Это происходит потому, что данные классы расширяют классы InputStreamReader и OutputstreamWriter, соответственно, значит, содержат "переходное кольцо" внутри себя. Несмотря на различие потоков, использование классов файлового ввода/вывода очень похоже. В конструкторах всех четырех файловых потоков задается имя файла в виде строки типа String или ссылка на объект класса File. Конструкторы не только создают объект, но и отыскивают файл и открывают его. Например: FileInputStream fis = new FileInputStream("C:\\PrWr.Java"); FileReader fr = new FileReader("D:\\jdkl.3\\src\\PrWr.Java"); При неудаче выбрасывается исключение класса FileNotFoundException, но конструктор класса FileWriter выбрасывает более общее исключение IOException. После открытия выходного потока типа FileWriter или FileOutputStream содержимое файла, если он был не пуст, стирается. Для того чтобы можно было делать запись в конец файла, и в том и в другом классе предусмотрен конструктор с двумя аргументами. Если второй аргумент равен true, то происходит дозапись в конец файла, если false, то файл заполняется новой информацией. Например: FileWriter fw = new FileWriter("c:\\8.txt", true); FileOutputStream fos = new FileOutputStream("D:\\samples\\newfile.txt"); Сразу после выполнения конструктора можно читать файл: fis.read(); fr.read(); или записывать в него: fos.write((char)с); fw.write((char)с); По окончании работы с файлом поток следует закрыть методом close(). Преобразование потоков в классах FileReader и FileWriter выполняется по кодовым таблицам установленной на компьютере локали. Для правильного ввода кириллицы надо применять FileReader, a нe FileInputStream. Если файл содержит текст в кодировке, отличной от локальной кодировки, то придется вставлять "переходное кольцо" вручную, как это может делаться для консоли, например: InputStreamReader isr = new InputStreamReader(fis, "KOI8_R")); Байтовый поток fis определен выше. Получение свойств файла В конструкторах классов файлового ввода/вывода, описанных в предыдущем разделе, указывалось имя файла в виде строки. При этом оставалось неизвестным, существует ли файл, разрешен ли к, нему доступ, какова длина файла. Получить такие сведения можно от предварительно созданного экземпляра класса File, содержащего сведения о файле. В конструкторе этого класса File(String filename) указывается путь к файлу или каталогу, записанный по правилам операционной системы. Конструктор не проверяет, существует ли файл с таким именем, поэтому после создания объекта следует это проверить логическим методом exists(). Класс File содержит около сорока методов, позволяющих узнать различные свойства файла или каталога. Прежде всего, логическими методами isFile(), isDirectory() можно выяснить, является ли путь, указанный в конструкторе, путем к файлу или каталогу. Для каталога можно получить его содержимое -- список имен файлов и подкаталогов-- методом list(), возвращающим массив строк String[]. Можно получить такой же список в виде массива объектов класса File[] методом listFiles(). Если каталог с указанным в конструкторе путем не существует, его можно создать логическим методом mkdir(). Этот метод возвращает true, если каталог удалось создать. Логический метод mkdirs() создает еще и все несуществующие каталоги, указанные в пути. Логические методы canRead(), canwrite() показывают права доступа к файлу. Файл можно переименовать логическим методом renameTo(File newName) или удалить логическим методом delete(). Эти методы возвращают true, если операция прошла удачно. Если файл с указанным в конструкторе путем не существует, его можно создать логическим методом createNewFile(), возвращающим true, если файл не существовал, и его удалось создать, и false, если файл уже существовал. Несколько методов getxxx() возвращают имя файла, имя каталога и другие сведения о пути к файлу. Эти методы полезны в тех случаях, когда ссылка на объект класса File возвращается другими методами и нужны сведения о файле. Наконец, метод toURL() возвращает путь к файлу в форме URL. Буферизованный ввод/вывод Операции ввода/вывода по сравнению с операциями в оперативной памяти выполняются очень медленно. Для компенсации в оперативной памяти выделяется некоторая промежуточная область -- буфер, в которой постепенно накапливается информация. Когда буфер заполнен, его содержимое быстро переносится процессором, буфер очищается и снова заполняется информацией. Классы файлового ввода/вывода не занимаются буферизацией. Для этой цели есть четыре специальных класса BufferedXxx. Они присоединяются к потокам ввода/вывода как "переходное кольцо", например: BufferedReader br = new BufferedReader(isr); BufferedWriter bw = new BufferedWriter(fw); Потоки isr и fw определены выше. Данная программа читает текстовый файл, написанный в кодировке СР866, и записывает его содержимое в файл в кодировке Cp1251. При чтении и записи применяется буферизация. import java.io.*; class DOStoWindows{ public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream d:\\dos.txt, "Cp866")); BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream d:\\windows.txt, "Cp1251")); int с = 0; while ((c = br.read()) != -1) bw.write((char)c); br.close(); bw.close(); System.out.println("Все OK."); } } Приложение 6. Обработка исключений Исключительные ситуации (exceptions) могут возникнуть во время выполнения (runtime) программы, прервав ее обычный ход. К ним относится деление на нуль, отсутствие загружаемого файла, отрицательный или вышедший за верхний предел индекс массива, переполнение выделенной памяти и масса других неприятностей, которые могут случиться в самый неподходящий момент. В объектно-ориентированных языках программирования принят такой подход. При возникновении исключительной ситуации исполняющая система создает объект определенного класса, соответствующего возникшей ситуации, содержащий сведения о том, что, где и когда произошло. Этот объект передается на обработку программе, в которой возникло исключение. Если программа не обрабатывает исключение, то объект возвращается обработчику по умолчанию исполняющей системы. Обработчик поступает очень просто: выводит на консоль сообщение о произошедшем исключении и прекращает выполнение приложения. Блоки перехвата исключенияМы можем перехватить и обработать исключение в программе. При описании обработки применяется бейсбольная терминология. Говорят, что исполняющая система или программа "выбрасывает" (throws) объект-исключение. Этот объект "пролетает" через всю программу, появившись сначала в том методе, где произошло исключение, а программа в одном или нескольких местах пытается (try) его "перехватить" (catch) и обработать. Обработку можно сделать полностью в одном месте, а можно обработать исключение в одном месте, выбросить снова, перехватить в другом месте и обрабатывать дальше. Для того чтобы попытаться (try) перехватить (catch) объект-исключение, надо весь код программы, в котором может возникнуть исключительная ситуация, охватить оператором try{} catch() {}. Каждый блок catch(){} перехватывает исключение только одного типа, того, который указан в его аргументе. Но можно написать несколько блоков catch(){} для перехвата нескольких типов исключений. После блоков перехвата может быть вставлен еще один, необязательный блок finally(). Он предназначен для выполнения действий, которые надо выполнить обязательно, чтобы ни случилось. Все, что написано в этом блоке, будет выполнено и при возникновении исключения, и при обычном ходе программы, и даже если выход из блока try{} осуществляется оператором return. Если в операторе обработки исключений есть блок finally{}, то блок catch () {} может отсутствовать, т. е. можно не перехватывать исключение, но при его возникновении все-таки проделать какие-то обязательные действия. Пустой блок catch (){}, в котором между фигурными скобками нет ничего, даже пробела, тоже считается обработкой исключения и приводит к тому, что выполнение программы не прекратится. Часть заголовка метода throws То обстоятельство, что метод не обрабатывает возникающее в нем исключение, а выбрасывает (throws) его, следует отмечать в заголовке метода служебным словом throws и указанием класса исключения: private static void f(int n) throws ArithmeticException{ System.out.println(" 10 / n = " + (10 / n)) ; } Исключение ArithmeticException выпадает, например, при делении на ноль.Дело в том, что спецификация JLS делит все исключения на проверяемые (checked), те, которые проверяет компилятор, и непроверяемые (unchecked). При проверке компилятор замечает необработанные в методах и конструкторах исключения и считает ошибкой отсутствие в заголовке таких методов и конструкторов пометки throws. Именно для предотвращения подобных ошибок вставляются в листинги блоки обработки исключений. Так вот, исключения класса RuntimeException и его подклассов, одним из которых является ArithmeticException, непроверяемые, для них пометка throws необязательна. Еще одно большое семейство непроверяемых исключений составляет класс Error и его расширения. Почему компилятор не проверяет эти типы исключений? Причина в том, что исключения класса RuntimeException свидетельствуют об ошибках в программе, и единственно разумный метод их обработки -- исправить исходный текст программы и перекомпилировать ее. Что касается класса Error, то эти исключения очень трудно локализовать и на стадии компиляции невозможно определить место их появления. Напротив, возникновение проверяемого исключения показывает, что программа недостаточно продумана, не все возможные ситуации описаны. Такая программа должна быть доработана, о чем и напоминает компилятор. Если метод или конструктор выбрасывает несколько исключений, то их надо перечислить через запятую после слова throws. Оператор throw Этот оператор очень прост: после слова throw через пробел записывается объект класса-исключения. Достаточно часто он создается прямо в операторе throw, например: throw new ArithmeticException(); Оператор можно записать в любом месте программы. Он немедленно выбрасывает записанный в нем объект-исключение и дальше обработка этого исключения идет как обычно, будто бы здесь произошло деление на нуль или другое действие, вызвавшее исключение класса ArithmeticException. Итак, каждый блок catch() и перехватывает один определенный тип исключений. Если требуется одинаково обработать несколько типов исключений, то можно воспользоваться тем, что классы-исключения образуют иерархию. Таким образом, перемещаясь по иерархии классов-исключений, мы можем обрабатывать сразу более или менее крупные совокупности исключений. Рассмотрим подробнее иерархию классов-исключений. Иерархия классов-исключений Все классы-исключения расширяют класс Throwable -- непосредственное расширение класса Object. У класса Throwable и у всех его расширений по традиции два конструктора: · Throwable () -- конструктор по умолчанию; · Throwable (String message) -- создаваемый объект будет содержать произвольное сообщение message. Записанное в конструкторе сообщение можно получить затем методом getMessage (). Если объект создавался конструктором по умолчанию, то данный метод возвратит null. Метод toString() возвращает краткое описание события. Три метода выводят сообщения обо всех методах, встретившихся по пути "полета" исключения: · printstackTrace() -- выводит сообщения в стандартный вывод, как правило, это консоль; · printStackTrace(PrintStream stream) -- выводит сообщения в байтовый поток stream; · printStackTrace(PrintWriter stream) -- выводит сообщения в символьный поток stream. У класса Throwable два непосредственных наследника -- классы Error и Exception. Они не добавляют новых методов, а служат для разделения классов-исключений на два больших семейства -- семейство классов-ошибок (error) и семейство собственно классов-исключений (exception). Классы-ошибки, расширяющие класс Error, свидетельствуют о возникновении сложных ситуаций в виртуальной машине Java. Их обработка требует глубокого понимания всех тонкостей работы JVM. Ее не рекомендуется выполнять в обычной программе. Не советуют даже выбрасывать ошибки оператором throw. He следует делать свои классы-исключения расширениями класса Error или какого-то его подкласса. Имена классов-ошибок, по соглашению, заканчиваются словом Error. Классы-исключения, расширяющие класс Exception, отмечают возникновение обычной нештатной ситуации, которую можно и даже нужно обработать. Такие исключения следует выбросить оператором throw. Классов-исключений очень много, более двухсот. Они разбросаны буквально по всем пакетам J2SDK. В большинстве случаев вы способны подобрать готовый класс-исключение для обработки исключительных ситуаций в своей программе. При желании можно создать и свой класс-исключение, расширив класс Exception или любой его подкласс. Среди классов-исключений выделяется класс RuntimeException -- прямое расширение класса Exception. В нем и его подклассах отмечаются исключения, возникшие при работе JVM, но не столь серьезные, как ошибки. Их можно обрабатывать и выбрасывать, расширять своими классами, но лучше доверить это JVM, поскольку чаще всего это просто ошибка в программе, которую надо исправить. Особенность исключений данного класса в том, что их не надо отмечать в заголовке метода пометкой throws. Имена классов-исключений, по соглашению, заканчиваются словом Exception. Порядок обработки исключений Блоки catch () {} перехватывают исключения в порядке написания этих блоков. Это правило приводит к интересным результатам. try{ // Операторы, вызывающие исключения }catch(Exception e){ // Какая-то обработка }catch(RuntimeException re){ // Никогда не будет выполнен! } Второй блок не будет выполняться, поскольку исключение типа RuntimeException является исключением общего типа Exception и будет перехватываться предыдущим блоком catch () {}.
Страницы: 1, 2
|