Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Effective Java TM.doc
Скачиваний:
3
Добавлен:
28.09.2019
Размер:
2.11 Mб
Скачать

Глава 7 Общие вопросы программирования

Данная глава посвящена обсуждению основных элементов языка Java. В ней рассматриваются интерпретация локальных переменных, использование библиотек и различных типов данных, а также две выходящие за рамки языка возможности: отражение (reflection) и машинно-зависимые методы (native method). Наконец, обсуждаются оптимизация и соглашения по именованию.

Сводите к минимуму область видимости локальных переменных

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

Язык программирования указывает, что локальные переменные должны декла­рироваться в начале блока. И программисты продолжают придерживаться этого по­рядка, хотя от него уже нужно отказываться. Напомним, что язык программирования Java позволяет объявлять переменную в любом месте, где может стоять оператор.

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

132

к расширению области видимости локальной переменной приводит не только слишком ранее, но и слишком позднее ее декларирование. Область видимости локаль­ной переменной начинается в том месте, где она декларируется, и заканчивается с завершением блока, содержавшего эту декларацию. Если переменная декларирована за пределами блока, где она используется, то она остается видимой и после того, как программа выйдет из этого блока. Если переменная случайно была использована до или после области, в которой она должна была применяться, последствия могут быть катастрофическими.

Почти каждая декларация локальной переменной должна содержать инициа­лизатор. Если у вас недостаточно информации для правильной инициализации пере­менной, вы должны отложить декларацию до той поры, пока она не появится. Исключение из этого правила связано с использованием операторов tгу / catch. Если для инициализации переменной применяется метод, инициирующий появление обраба­тываемого исключения, то инициализация переменной должна осуществляться внутри блока try. Если переменная должна использоваться за пределами блока try, деклари­ровать ее следует перед блоком try, там, где она еще не может быть "правильно ини­циализирована" (статья 35).

Цикл предоставляет уникальную возможность для сужения области видимости переменных. Цикл for позволяет объявлять переменные цикла (loop variabIe), ограни­чивая их видимость ровно той областью, где они нужны. (Эта область состоит из соб­ственно тела цикла, а также из предшествующих ему полей инициализации, проверки и обновления.) Следовательно, если после завершения цикла значения его переменных не нужны, предпочтение следует отдавать циклам for, а не while.

Представим, например, предпочтительную идиому для организации цикла по не­коей коллекции:

for (Iterator i = с.iterator(); i.hasNext(); ) {

doSomething(i.next()); }

для пояснения, почему данный цикл for предпочтительнее более очевидного цикла while, рассмотрим следующий фрагмент кода, в котором содержатся два цикла while и одна ошибка:

Iterator i = c.iterator();

while (i.hasNext()) {

doSomething(i.next()); }

Iterator i2 = c2.1terator();

while (i.hasNext()) { // Ошибкаl

doSomethingElse(i2.next()); }

133

Второй цикл содержит ошибку копирования фрагмента программы: инициализи­руется новая переменная цикла i2, но используется старая i, которая, к сожалению, остается в поле видимости. Полученный код компилируется без замечаний и выпол­няется без инициирования исключительных ситуаций, только вот делает не то, что нужно. Вместо того чтобы организовывать итерацию по с2, второй цикл завершается немедленно, создавая ложное впечатление, что коллекция с2 пуста. И поскольку про­грамма ничего об этой ошибке не сообщает, та может оставаться незамеченной долгое время.

Если бы аналогичная ошибка копирования была допущена при применении цикла for, полученный код не был бы даже скомпилирован. для той области, где располага­ется второй цикл, переменная первого цикла уже была бы за пределами видимости:

for (Iterator i = с. iterator(); i. hasNext(); ) {

doSomething(i.next()); }

// Ошибка компиляции - символ i не может быть идентифицирован

for (Iterator i2 = c2.iterator(); i.hasNext();, ) {

doSomething(i2.next()); }

Более того, если вы пользуетесь идиомой цикла for, уменьшается вероятность того, что вы допустите ошибку копирования, поскольку нет причин использовать в двух этих циклах различные названия переменных. Эти циклы абсолютно независимы, а потому нет никакого вреда от повторного применения названия для переменной цикла. На самом деле это даже стильно.

Идиома цикла for имеет еще одно преимущество перед идиомой цикла while, хотя и не столь существенное. Идиома цикла for короче на одну строку, что помогает при редактировании уместить содержащий ее метод в окне фиксированного размера и повышает удобство чтения.

Приведем еще одну идиом)\ цикла для про смотра списка, которая минимизирует область видимости локальных переменных:

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

// с произвольным доступом

for (int i = 0, n = list.size(); i < n; i++) {

doSomething(list.get(i)); }

Эта идиома полезна для реализаций интерфейсов List с произвольным доступом, та­ких как ArrayList и Vector, поскольку для таких списков она, скорее всего, работает быстрее, чем приведенная выше "предпочтительная идиома". По поводу этой идиомы важно заметить, что в ней используются две переменные цикла: i и n, и обе имеют

134

абсолютно правильную область видимости. Вторая переменная важна для производи­тельности идиомы. Без нее метод size пришлось бы вызывать при каждом Проходе цикла, что отрицательно сказалось бы на производительности. Если вы уверены, что список действительно предоставляет произвольный доступ, пользуйтесь этой идиомой. В противном случае производительность цикла будет падать в квадратичной зависи­мости от размера списка.

Похожие идиомы есть и для других задач с циклами, например:

for (int i = 0, n = expensiveComputation(); i < n; i++) {

doSomething(i) ; }

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]