Философия Java3
Шрифт:
Некоторые люди утверждают, что статические методы со своей семантикой глобальной функции противоречат объектно-ориентированной парадигме; в случае использования статического метода вы не посылаете сообщение объекту, поскольку отсутствует ссылка this. Возможно, что это справедливый упрек, и если вы обнаружите, что используете слишком много статических методов, то стоит пересмотреть вашу стратегию разработки программ. Однако ключевое слово static полезно на практике, и в некоторых ситуациях они определенно необходимы. Споры же о «чистоте ООП» лучше оставить теоретикам.
Очистка: финализация и сборка мусора
Программисты помнят и знают о важности инициализации, но часто забывают о значимости «приборки». Да и зачем,
Все это может создать немало проблем для программистов, особенно для программистов на языке С++, так как они могут спутать метод finalize с деструктором языка С++ — функцией, всегда вызываемой перед разрушением объекта. Но здесь очень важно понять разницу между Java и С++, поскольку в С++ объекты разрушаются всегда (в правильно написанной программе), в то время как в Java объекты удаляются сборщиком мусора не во всех случаях. Другими словами:
ВНИМАНИЕ-:-
1. Ваши объекты могут быть и не переданы сборщику мусора.
2. Сборка мусора не является удалением.
Если программа завершает свою работу и сборщик мусора не удалил ни одного объекта и не освободил занимаемую память, то эта память будет возвращена операционной системе после завершения работы программы. Это хорошо, так как сборка мусора сопровождается весомыми издержками, и если сборщик не используется, то, соответственно, эти издержки не проявляются.
Для чего нужен метод finalize?
Итак, если метод finalize не стоит использовать для проведения стандартных операций завершения, то для чего же он нужен? Запомните третье правило:
ВНИМАНИЕ -
3. Процесс сборки мусора относится только к памяти.
Единственная причина существования сборщика мусора — освобождение памяти, которая перестала использоваться вашей программой. Поэтому все действия, так или иначе связанные со сбором мусора, особенно те, что записаны в методе finalize, должны относиться к управлению и освобождению памяти.
Но значит ли это, что если ваш объект содержит другие объекты, то в finalize они должны явно удаляться? Нет — сборщик мусора займется освобождением памяти и удалением объектов вне зависимости от способа их создания. Получается, что использование метода finalize ограничено особыми случаями, в которых ваш объект размещается в памяти необычным способом, не связанным с прямым созданием экземпляра. Но, если в Java все является объектом, как же тогда такие особые случаи происходят?
Похоже, что поддержка метода finalize была введена в язык, чтобы сделать возможными операции с памятью в стиле С, с привлечением нестандартных механизмов выделения памяти. Это может произойти в основном при использовании методов, предоставляющих способ вызова He– Java-кода из программы на Java. С и С++ пока являются единственными поддерживаемыми языками, но, так как для них таких ограничений нет, в действительности программа Java
После прочтения этого абзаца у вас, скорее всего, сложилось мнение, что метод finalize используется нечасто11. И правда, это не то место, где следует проводить рутинные операции очистки. Но где же тогда эти обычные операции будут уместны?
Очистка — ваш долг
Для очистки объекта его пользователю нужно вызвать соответствующий метод в той точке, где эти завершающие действия по откреплению и должны осуществляться. Звучит просто, но немного протйворечит традиционным представлениям о деструкторах С++. В этом языке все объекты должны уничтожаться. Если объект С++ создается локально (то есть в стеке, что невозможно в Java), то удаление и вызов деструктора происходит у закрывающей фигурной скобки, ограничивающей область действия такого объекта. Если же объект создается оператором new (как в Java), то деструктор вызывается при выполнении программистом оператора С++ delete (не имеющего аналога в Java). А когда программист на С++ забывает вызвать оператор delete, деструктор не вызывается и происходит «утечка» памяти, к тому же остальные части объекта не проходят необходимой очистки. Такого рода ошибки очень сложно найти и устранить, и они являются веским доводом в пользу перехода с С++ на Java.
Java не позволяет создавать локальные объекты — все объекты должны быть результатом действия оператора new. Но в Java отсутствует аналог оператора delete, вызываемого для разрушения объекта, так как сборщик мусора и без того выполнит освобождение памяти. Значит, в несколько упрощенном изложении можно утверждать, что деструктор в Java отсутствует из-за присутствия сборщика мусора. Но в процессе чтения книги вы еще не раз убедитесь, что наличие сборщика мусора не устраняет необходимости в деструкторах или их аналогах. (И никогда не стоит вызывать метод finalize непосредственно, так как этот подход не решает проблему.) Если же потребуется провести какие-то завершающие действия, отличные от освобождения памяти, все же придется явно вызвать подходящий метод, выполняющий функцию деструктора С++, но это уже не так удобно, как встроенный деструктор.
Помните, что ни сборка мусора, ни финализация не гарантированы. Если виртуальная машина Java (Java Virtual Machine, JVM) далека от критической точки расходования ресурсов, она не станет тратить время на освобождение памяти с использованием сборки мусора.
Условие «готовности»
В общем, вы не должны полагаться на вызов метода finalize — создавайте отдельные «функции очистки» и вызывайте их явно. Скорее всего, finalize пригодится только в особых ситуациях нестандартного освобождения памяти, с которыми большинство программистов никогда не сталкивается. Тем не менее существует очень интересное применение метода finalize, не зависящее от того, вызывается ли он каждый раз или нет. Это проверка условия готовности12объекта.
В той точке, где объект становится ненужным — там, где он готов к проведению очистки, — этот объект должен находиться в состоянии, когда освобождение закрепленной за ним памяти безопасно. Например, если объект представляет открытый файл, то он должен быть соответствующим образом закрыт, перед тем как его «приберет» сборщик мусора. Если какая-то часть объекта не будет готова к уничтожению, результатом станет ошибка в программе, которую затем очень сложно обнаружить. Ценность finalize в том и состоит, что он позволяет вам обнаружить такие ошибки, даже если и не всегда вызывается. Единожды проведенная финализация явным образом укажет на ошибку, а это все, что вам нужно.