Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 95 96 97 98 99 100 101 102 103 ... 132

Резюме

В этой главе вы убедились в том, что язык Java предоставляет неплохую поддержку низкоуровневых массивов фиксированного размера. Такие массивы отдают предпочтение производительности перед гибкостью. В исходной версии Java низкоуровневые массивы фиксированного размера были абсолютно необходимы — не только потому, что проектировщики Java решили включить в язык примитивные типы (также по соображениям быстродействия), но и потому, что поддержка контейнеров в этой версии была крайне ограниченной.

В последующих версиях Java поддержка контейнеров была значительно улучшена. Сейчас контейнеры превосходят массивы во всех отношениях, кроме быстродействия, хотя производительность контейнеров была значительно улучшена. С добавлением автоматической упаковки и параметризации хранение примитивов в контейнерах требует меньших усилий и способствует дальнейшему переходу с низкоуровневых массивов на контейнеры. Так как параметризация открыла доступ к типизованным контейнерам, в этом отношении массивы тоже утратили исходные преимущества.

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

Все эти факторы показывают, что при программировании для последних версий Java следует отдавать предпочтение контейнерам перед массивами. На массивы следует переходить только при критичных требованиях по быстродействию — и только в том случае, если переход принесет ощутимую пользу.

Система

ввода/вывода

Java

Создание хорошей системы ввода/вывода является одной из труднейших задач разработчика языка. Доказательством этого утверждения служит множество подходов, используемых при разработке систем ввода/вывода.

Основная сложность состоит в том, что необходимо учесть все возможные ситуации. Это не только наличие множества источников и приемников данных, с которыми необходимо поддерживать связь (файлы, консоль, сетевые соединения), но и реализации различных форм этой связи (последовательный доступ, произвольный, буферизованный, двоичный, символьный, построчный, пословный и т. д.).

Разработчики библиотеки Java решили начать с создания огромного количества классов. Вообще говоря, в библиотеке ввода/вывода Java так много классов, что потеряться в них проще простого (парадоксально, но сама система ввода/вывода Java в действительности не нуждается в таком количестве классов). Потом, после выхода первой версии языка Java, в библиотеке ввода/вывода последовали значительные изменения: к ориентированным на посылку и прием байтов классам добавились основанные на Юникод классы, работающие с символами. В JDK 1.4 классы nio (от сочетания «new I/O», «новый ввод/вывод») призваны улучшить производительность и функциональность. В результате, чтобы понять общую картину ввода/вывода в Java и начать использовать ее, вам придется изучить порядочный ворох классов. Вдобавок не менее важно понять и изучить эволюцию библиотеки ввода/вывода, несмотря на вашу очевидную реакцию: «Избавьте меня от истории! Просто покажите, как работать с библиотекой!» Если не уяснить причины изменений, проведенных в библиотеке ввода/вывода, вскоре мы запутаемся в ней и не сможем твердо аргументировать сделанный нами выбор в пользу того или иного класса.

В этой главе мы познакомимся с различными классами, отвечающими за ввод/вывод в библиотеке Java, а также научимся использовать их.

Класс File

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

Имя класса File весьма обманчиво: легко подумать, что оно всегда ссылается на файл, но это не так. Класс File может представлять как имя определенного файла, так, имена группы файлов, находящихся в каталоге. Если класс представляет каталог, его метод list() возвращает массив строк с именами всех файлов. Использовать в данной ситуации массив (а не более гибкий контейнер) очень удобно: количество файлов в каталоге фиксировано, как и размер массива, а если понадобится узнать имена файлов в другом каталоге, достаточно создать еще один объект File. Следующий раздел покажет, как использовать этот класс в совокупности с тесно связанным с ним интерфейсом FilenameFilter.

Список каталогов

Предположим, вы хотите получить содержимое каталога. Объект File позволяет получить этот список двумя способами. Если вызвать метод list() без аргументов, то результатом будет полный список файлов и каталогов (точнее, их названий), содержащихся в данном каталоге. Но, если вам нужен ограниченный список — например, список всех файлов с расширением .java, — используйте «фильтр», то есть класс, который описывает критерии отбора объектов File.

Рассмотрим пример. Заметьте, что полученный список без всяких дополнительных усилий сортируется (по алфавиту) с помощью метода j a va. u ti L. Array. sort() и объекта String.CASE_INSENSITIVE_ORDER:

//: io/DirList java

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

// {Параметры- "D *.java"}

import java.util regex.*.

import java io.*.

import java util *,

public class DirList {

public static void main(String[] args) { File path = new File(" "). StringC] list, if(args length == 0)

list = path listO.

else

list = path list(new DirFilter(args[0])); Arrays sortdist. String CASE_INSENSITIVE_ORDER). for(String dirltem list)

System.out.printin(dirltem).

class DirFiIter implements FilenameFilter { private Pattern pattern; public DirFilter(String regex) {

pattern = Pattern compile(regex);

}

public boolean accept(File dir. String name) { return pattern matcher(name) matchesO;

}

} /* Output DirectoryDemo java DirList java DirList2 java DirList3 java III ~

Класс DirFilter реализует интерфейс FilenameFilter. Посмотрите, как просто выглядит этот интерфейс:

public interface FilenameFilter {

boolean accept(File dir. String name).

}

Это показывает, что данный тип объекта должен поддерживать метод с именем accept(), который вызывается методом list() с целью определения того, какие имена файлов должны включаться в выходной список, а какие нет. Перед нами один из примеров паттерна «стратегия»: list() реализует базовую функциональность, a FilenameFilter предоставляет алгоритм, необходимый для работы list(). Так как метод list() принимает в качестве аргумента объект Filename-Filter, ему можно передать любой объект любого класса, лишь бы он реализовы-вал интерфейс FilenameFilter (даже во время выполнения). Таким образом легко изменять результат работы метода list(). Целью данного паттерна является обеспечение гибкости в поведении кода.

Метод accept() получает объект File, представляющий собой каталог, в котором был найден данный файл, и строку с именем файла. Помните, что метод list() вызывает accept() для каждого файла, обнаруженного в каталоге, чтобы определить, какие из них следует включить в выходной список — в зависимости от возвращаемого значения accept() (значение типа boolean).

Метод accept() использует объект регулярного выражения matcher, чтобы посмотреть, соответствует ли имя файла выражению regex. Метод list() возвращает массив.

Безымянные внутренние классы

Описанный пример идеально подходит для демонстрации преимуществ внутренних классов (описанных в главе 10). Для начала создадим метод filter(), который возвращает ссылку на объект FilenameFilter:

// io/DirList java

II Использование безымянных внутренних классов

II {Параметры "D * java"}

import java util regex *,

import java io *.

import java util *,

public class DirList {

public static void main(String[] args) { File path = new FileC" ").

Stri ng[] list. продолжение & if(args length == 0)

list = path listO.

else

list = path list(new Di rFi1ter(args[0])): Arrays.sort(1i st, Stri ng.CASE_INSENSITIVE_ORDER); for(String dirltem . list)

System out println(dirltem);

class DirFiIter implements FilenameFilter { private Pattern pattern, public Di rFilter(String regex) {

pattern = Pattern.compile(regex);

}

public boolean accept(File dir. String name) { return pattern.matcher(name).matchesО;

}

} /* Output. DirectoryDemo.java DirList.java DirList2.java Di rLi st3.java *///:-

Заметьте, что аргумент метода filter() должен быть неизменным (final). Это необходимо для того, чтобы внутренний класс смог получить к нему доступ даже за пределами области определения аргумента.

Несомненно, структура программы улучшилась хотя бы потому, что объект FilenameFilter теперь неразрывно связан с внешним классом DirList2. Впрочем, можно сделать следующий шаг и определить безымянный внутренний класс как аргумент метода list(), в результате чего программа станет еще более компактной:

//: io/Dirl_ist3.java

// Создание безымянного внутреннего класса "на месте".

// {Параметры: "D.*.java"}

import java.util.regex.*;

import java.io.*;

import java.util.*;

public class DirList3 {

public static void main(final String[] args) { File path = new File(V); String[] list; if(args.length == 0)

list = path.listO;

else

list = path.list(new FilenameFilterО {

private Pattern pattern = Pattern.compile(args[0]); public boolean accept(File dir, String name) { return pattern.matcher(name).matches();

}

}):

Arrays.sort(1i st. Stri ng.CASE_INSENSITIVE_ORDER); for(String dirltem : list)

System out println(dirltem).

}

} /* Output. DirectoryDemo.java DirList java DirList2.java DirList3.java */// ~

На этот раз неизменным (final) объявлен аргумент метода main(), так как безымянный внутренний класс использует параметр командной строки (args[0]) напрямую.

1 ... 95 96 97 98 99 100 101 102 103 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий