Выбрать главу

Все является объектом

Если бы мы говорили на другом языке, то и мир воспринимали бы по-другому.

Людвиг Витгенштейн (1889-1951)

Хотя язык Java основан на С++, он является более «чистокровным» объект- но-ориентированным языком.

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

Язык программирования Java подразумевает, что вы занимаетесь только объектно-ориентированным программированием. А это значит, что прежде, чем начать с ним работать, нужно «переключиться» на понятия объектно-ориенти- рованного мира (если вы уже этого не сделали). Выгода от этого начального усилия — возможность программировать на языке, который по простоте изуче¬ния и использования превосходит все остальные языки ООП. В этой главе мы рассмотрим основные компоненты Java-программы и узнаем, что в Java (почти) все является объектом.

Для работы с объектами используются ссылки

Каждый язык программирования имеет свои средства манипуляции данными. Иногда программисту приходится быть постоянно в курсе, какая именно мани¬пуляция производится в программе. Вы работаете с самим объектом или же с каким-то видом его косвенного представления (указатель в С или в С++), требующим особого синтаксиса?

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

вы обращаетесь со всем как с объектом, идентификатор, которым вы манипу¬лируете, на самом деле представляет собой ссылку на объект . Представьте себе телевизор (объект) с пультом дистанционного управления (ссылка). Во время владения этой ссылкой у вас имеется связь с телевизором, но при переключе¬нии канала или уменьшении громкости вы распоряжаетесь ссылкой, которая, в свою очередь, манипулирует объектом. А если вам захочется перейти в другое место комнаты, все еще управляя телевизором, вы берете с собой «ссылку», а не сам телевизор.

Также пульт может существовать сам по себе, без телевизора. Таким обра¬зом, сам факт наличия ссылки еще не означает наличия присоединенного к ней объекта. Например, для хранения слова или предложения создается ссылка String:

String s;

Однако здесь определяется только ссылка, но не объект. Если вы решите по¬слать сообщение s, произойдет ошибка, потому что ссылка s на самом деле ни к чему не присоединена (телевизора нет). Значит, безопаснее всегда инициа¬лизировать ссылку при ее создании:

String s = "asdf";

В данном примере используется специальная возможность Java: инициали¬зация строк текстом в кавычках. Обычно вы будете использовать более общий способ инициализации объектов.

Все объекты должны создаваться явно

Когда вы определяете ссылку, желательно присоединить ее к новому объекту. В основном это делается при помощи ключевого слова new. Фактически оно оз¬начает: «Создайте мне новый объект». В предыдущем примере можно написать:

String s = new StringC"asdf"):

Это не только значит «предоставьте мне новый объект String», но также ука¬зывает, как создать строку посредством передачи начального набора символов.

Конечно, кроме String, в Java имеется множество готовых типов. Важнее то, что вы можете создавать свои собственные типы. Вообще говоря, именно созда¬ние новых типов станет вашим основным занятием при программировании на Java, и именно его мы будем рассматривать в книге.

Где хранятся данные

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

1. Регистры. Это самое быстрое хранилище, потому что данные хранятся прямо внутри процессора. Однако количество регистров жестко ограни¬чено, поэтому регистры используются компилятором по мере необходи¬мости. У вас нет прямого доступа к регистрам, вы не сможете найти и ма¬лейших следов их поддержки в языке. (С другой стороны, языки С и С++ позволяют порекомендовать компилятору хранить данные в регистрах.)

2. Стек. Эта область хранения данных находится в общей оперативной па¬мяти (RAM), но процессор предоставляет прямой доступ к ней с исполь¬зованием указателя стека. Указатель стека перемещается вниз для выде¬ления памяти или вверх для ее освобождения. Это чрезвычайно быстрый и эффективный способ размещения данных, по скорости уступающий только регистрам. Во время обработки программы компилятор Java дол¬жен знать жизненный цикл данных, размещаемых в стеке. Это ограниче¬ние уменьшает гибкость ваших программ, поэтому, хотя некоторые дан¬ные Java хранятся в стеке (особенно ссылки на объекты), сами объекты Java не помещаются в стек.

3. Куча. Пул памяти общего назначения (находится также в RAM), в кото¬ром размещаются все объекты Java. Преимущество кучи состоит в том, что компилятору не обязательно знать, как долго просуществуют находя¬щиеся там объекты. Таким образом, работа с кучей дает значительное преимущество в гибкости. Когда вам нужно создать объект, вы пишете код с использованием new, и память выделяется из кучи во время выпол¬нения программы. Конечно, за гибкость приходится расплачиваться: вы¬деление памяти из кучи занимает больше времени, чем в стеке (даже если бы вы могли явно создавать объекты в стеке, как в С++).

4. Постоянная память. Значения констант часто встраиваются прямо в код программы, так как они неизменны. Иногда такие данные могут разме¬щаться в постоянной памяти (ROM), если речь идет о «встроенных» сис¬темах.

5. Не-оперативная память. Если данные располагаются вне программы, они могут существовать и тогда, когда она не выполняется. Два основных примера: потоковые объекты (streamed objects), в которых объекты пред¬ставлены в виде потока байтов, обычно используются для посылки на другие машины, и долгоживущие (persistent) объекты, которые запомина¬ются на диске и сохраняют свое состояние даже после окончания работы программы. Особенностью этих видов хранения данных является воз¬можность перевода объектов в нечто, что может быть сохранено на дру¬гом носителе информации, а потом восстановлено в виде обычного объ¬екта, хранящегося в оперативной памяти. В Java организована поддержка легковесного (lightweight) сохранения состояния, а такие механизмы, как JDBC и Hibernate, предоставляют более совершенную поддержку сохра¬нения и выборки информации об объектах из баз данных.

Особый случай: примитивные типы

Одна из групп типов, часто применяемых при программировании, требует осо¬бого обращения. Их можно назвать «примитивными» типами (табл. 2.1). При¬чина для особого обращения состоит в том, что создание объекта с помощью new — особенно маленькой простой переменной — недостаточно эффективно, так как new помещает объекты в кучу. В таких случаях Java следует примеру языков С и С++. То есть вместо создания переменной с помощью new создается «автоматическая» переменная, не являющаяся ссылкой. Переменная напрямую хранит значение и располагается в стеке, так что операции с ней гораздо произ¬водительнее.

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