|
Язык и архитектура Java
Язык и архитектура Java
Цишевский В.
Введение
Преамбула
На сегодняшний день создание программного обеспечения представляет собой чрезвычайно тяжелое занятие. Трудности связаны с разнообразием архитектур машин, операционных систем, графических оболочек и т.д.. Кроме того, ваши приложения должны работать в распределенных системах. Стремительный рост технологий, связанных с Интернетом, WWW и "электронной коммерцией", дополнительно усложняют эту задачу. Модный ныне объектно-ориентированный подход сам по себе не решает этих проблем, более того, часто привносит новые.
Предлагаемый фирмой Sun Microsystems подход, а именно система программирования на основе языка Java(TM) обладает следующими характеристиками:
язык программирования объектно-ориентирован, в то же время довольно прост для освоения
цикл разработки приложений сокращен за счет того, что система построена на основе интерпретатора
приложение получается автоматически переносимым между множеством платформ и операционных систем
за счет встроенной системы сборки мусора программист освобождается от необходимости явного управления памятью
в интерактивном графическом приложении удается достичь высокой производительности (быстрого отклика на ввод пользователя) за счет встроенной в систему многопотоковости
приложение легко сопровождается и модифицируется, т.к. модули могут быть загружены с сети
в приложения встроена система безопасности, не допускающая незаконного доступа и проникновения вирусов
Основы проекта Java. Немного истории
Целью проекта было создание небольшой, надежной, переносимой и распределенной системы реального времени. Исходно в качестве языка планировалось использование языка программирования С++, но постепенно связанные с ним трудности привели к необходимости создания нового языка. Система должна была вобрать в себя лучшие черты из множества современных систем программирования -- Eiffel, Smalltalk, Objective C, Cedar/Mesa и т. д..
Стремительный рост сетевых технологий привел к необходимости нового взгляда на процесс создания и распределения приложений. Современные приложения должны быть безопасны, высокопроизводительны, работать в распределенной среде на множестве машин различной архитектуры.
Требования к переносимости заставили отказаться от традиционного способа создания и доставки бинарных файлов, содержащих машинные коды и, следовательно, привязанных к определенной платформе. Сегодня, чтобы выжить в этих джунглях из архитектур процессоров, операционных систем и графических оболочек, приложение должно быть нейтрально к архитектуре и динамически настраиваемо.
Созданная система разработки Java удовлетворяет всем этим требованиям, а следовательно:
проста, поэтому может быть использована широким кругом разработчиков
объектно ориентирована, что соответствует современному взгляду на программирование
поддерживает многопотоковость, что позволяет выполнять несколько задач одновременно
интерпретируема, что обеспечивает переносимость и улучшает динамические свойства
Чтобы не быть голословным, рассмотрим каждое из этих свойств по отдельности.
Свойства Java, краткий обзор
Java проста, объектно ориентированна и знакома
Система Java создана на основе *простого* языка программирования, техника использования которого близка к общепринятой и обучение которому не требует значительных усилий.
Java как язык программирования является объектно ориентированной с момента основания. Кроме того программист с самого начала обеспечивается набором *стандартных* библиотек, обеспечивающих функциональность от стандартного ввода/вывода и сетевых протоколов до графических пользовательских интерфейсов. Эти библиотеки легко могут быть расширены.
Несмотря на то, что язык С++ был отвергнут, синтаксис языка Java максимально приближен к синтаксису С++. Это делает язык знакомым широкому кругу программистов. В то же время из языка были удалены многие свойства, которые делают С++ излишне сложным для пользования, не являясь абсолютно необходимыми. В результате язык Java получился более простым и органичным, чем С++.
Надежность и безопасность
Java существенно облегчает создание надежного программного обеспечения. Кроме исчерпывающей проверки на этапе компиляции, система предусматривается анализ на этапе выполнения. Сам язык спроектирован так, чтобы вырабатывать у программиста привычку писать "правильно". Модель работы с памятью, в которой исключено использование указателей, делает невозможными целый класс ошибок, характерных для С и С++.
В силу того, что Java предназначена для работы в распределенной среде, безопасность становится чрезвычайно важной проблемой. Требования безопасности определяют многие черты как языка, так и реализации всей системы.
Независимость от архитектуры и переносимость.
Компилятор Java производит байт-коды, т.е. модули приложения имеют архитектурно-независимый формат, который может быть проинтерпретирован на множестве разнообразных платформ. Это уже не исходные тексты, но еще не платформно-зависимые машинные коды.
Следующий шаг -- "замораживание" стандарта на формат основных встроенных типов данных. Программа, созданная на одной платформе, работает на всех остальных.
Этот стандарт фиксирован в документе, описывающем Java Virtual Machine. Стандарт может быть реализован на любой аппаратно-программной платформе, поддерживающей многопотоковость.
Производительность
Схема работы системы и набор байт-кодов виртуальной машины Java таковы, что позволяют достичь высокой производительности на этапе выполнения программы:
анализ кодов на соблюдение правил безопасности производится один раз до запуска кодов на выполнение, в момент выполнения таких проверок уже не нужно, и коды выполняются максимально эффективно
работа с базовыми типами максимально эффективна, для операций с ними зарезервированы специальные байт-коды
методы в классах не обязательно связываются динамически
автоматический сборщик мусора работает отдельным фоновым потоком, не замедляя основную работу программы, но в то же время обеспечивая своевременный возврат свободной памяти в систему
стандарт предусматривает возможность написания критических по производительности участков программы в машинных кодах
Интерпретируемый, многопотоковый и динамический
Интерпретируемая природа языка позволяет сделать фазу линкования простой, инкрементальной и, следовательно, быстрой. Это резко сокращает цикл разработки и тестирования программных фрагментов.
Многопотоковость позволяет выполнять в рамках одного приложения несколько задач одновременно. Это становится особенно актуально в современных распределенных приложениях, когда процессы сетевого обмена могут идти одновременно и асинхронно. При этом программа продолжает реагировать на ввод информации пользователем без неприятных задержек.
Многопотоковость поддерживается на уровне языка -- часть примитивов синхронизации встроена в систему реального времени, а библиотека содержит базовый класс Thread. К тому же системные библиотеки написаны thread-safe, т.е. все они могут быть использованы в многопотоковых приложениях.
Система обеспечивает динамическую сборку программы. Классы подгружаются по мере необходимости, причем загружены они могут быть с любой точки сети, что позволяет сделать внесение изменений в приложения прозрачным для пользователя. Пользователь может быть уверен, что всегда работает со свежей версией приложения.
Базовая система Java
Опыт показывает, что отсутствие стандартных базовых библиотек для языка С++ чрезвычайно затрудняет работу с ним. В силу того, что любое нетривиальное приложение требует наличия некоторого набора базовых классов, разработчикам приходится пользоваться различными несовместимыми между собой библиотеками или писать свой собственный вариант такого набора. Все это затрудняет как разработку, так и дальнейшую поддержку приложений, затрудняет стыковку приложений, написанных разными людьми.
Полная система Java включает в себя готовый набор библиотек, который можно разбить на следующие пакеты:
java.lang -- базовый набор типов, отраженных в самом языке. Этот пакет обязательно входит в состав любого приложения. Содержит описания классов Object и Class, а также поддержку многопотоковости, исключительных ситуаций, оболочку для базовых типов, а также некоторые фундаментальные классы.
java.io -- потоки и файлы произвольного доступа. Аналог библиотеки стандартного ввода-вывода системы UNIX. Поддержка сетевого доступа (sockets, telnet, URL) содержится в пакете java.net.
java.util -- классы-контейнеры (Dictionary, HashTable, Stack) и некоторые другие утилиты. Кодирование и декодирование. Классы Date и Time.
java.awt -- Abstract Windowing Toolkit, архитектурно-независимый оконный интерфейс, позволяющий запускать интерактивные оконные Java-приложения на любой платформе. Содержит базовые компоненты интерфейса, такие как события, цвета, фонты, а также основные оконные элементы -- кнопки, scrollbars и т.д..
Результат -- новый подход к распределенным вычислениям
Каждая из перечисленных характеристик по отдельности может быть найдена в уже существующих программных пакетах. Новым является соединение их в стройную непротиворечивую систему, которая должна стать всеобщим стандартом.
Java -- просто и знакомо
На сегодняшний день наиболее популярными языками программирования являются С и С++. Из них двоих лишь С++ претендует на объектную ориентацию. Характеристики этого языка складывались в ходе длинной истории его развития, причем довольно хаотично, каждое новое свойство не отменяло всех предыдущих. Стандарт языка до сих пор не зафиксирован, т.к. новые свойства продолжают появляться по сей день. В результате С++ стал бесконечно сложным и избыточным -- одну и ту же операцию возможно реализовать на языке множеством способов.
Java представляет собой новую точку отсчета в программном обеспечении. Разработчики языка взяли за основу С++, затем методично удалили из него черты, которые:
делают невозможным контроль безопасности приложений
не являются абсолютно необходимыми, чаще мешают программисту, чем облегчают его задачу
являются источником наиболее трудно и поздно распознаваемых ошибок
В то же время в языке Java полностью сохранен "дух" программирования на С++, опытным С++ программистам потребуется одна-две недели на освоение самого языка, а огромный объем программного обеспечения, уже созданного с использованием С++, может быть адаптирован под новый язык относительно легко.
Основные свойства языка программирования Java
Встроенные (примитивные) типы данных
В языке Java, так же как и в С++, существует набор встроенных типов данных, которые (так же как и в С++) не являются объектами. Набор их также сходен с набором базовых типов С++ за некоторыми исключениями.
Numeric
Характерным отличием от С++ является то, что бинарное представление чисел отныне фиксировано:
целые числа: 8-бит byte, 16-бит short, 32-бит int, 64-бит long. Все числа со знаком, ключ unsigned из языка удален.
числа с плавающей точкой. 32-бит float, 64-бит double. Представление должно соответствовать стандарту IEEE 754.
Character
Отличаются от С++ как синтаксисом, так и представлением. Тип character есть 16-разрядное число без знака (диапазон 0-65,535). Кодировка соответствует стандарту Unicode. В силу того, что эта кодировка в идеале должна охватывать все существующие в мире языки, это представление должно облегчить локализацию приложений.
Boolean
Этот тип данных не выделен в С++, однако неявно присутствует практически во всех программах. В Java тип называется boolean, может принимать значения true и false и не может (в отличие от С++) быть преобразован в другой тип.
Операторы
Добавлен новый оператор >>> логического сдвига вправо (т.к. нет беззнаковых целых чисел). Встроенная операция слияния строк (оператор +).
Массивы
В отличие от С++ массивы в Java являются полноценными объектами с определенным runtime представлением. Декларация:
Point myPoints[];
резервирует ссылку на массив, а не место под реальный объект. Сам массив может быть затем создан выполнением
myPoints = new Point[10];
а его элементы заполнены операцией типа:
myPoints[2] = new Point();
Размер массива может быть получен во время выполнения программы:
howMany = myPoints.length;
Значение индекса проверяется при каждом обращении, при ошибке возбуждается исключительная ситуация.
Указатели полностью исключены из языка вместе с целой категорией трудноуловимых ошибок "замедленного действия". К тому же наличие указателей противоречит требованиям безопасности и усложняет реализацию сборщика мусора.
Strings
Строки в Java являются полноценными объектами. Они делятся на текстовые константы (Strings) и модифицируемые строки (StringBuffer). Компилятор позволяет явно определять текстовые литералы в программе подобно тому, как это делается в С++
String hello = "Hello world!";
Ссылка hello инициируется объектом класса String на основе представления "Hello world!" в кодировке Unicode.
Оператор "+" может быть применен к строкам, например
System.out.println("There are" + num + "characters in the file.");
Multi-Level Break
В Java отсутствует выражение goto. Анализ С/С++ текстов показал, что подавляющее число случаев использования этого оператора связано с необходимостью выхода из вложенного цикла. Для отработки таких ситуаций в Java перед началом блока может ставиться метка, а инструкции break и continue также могут сопровождаться меткой, на которую должен быть осуществлен переход. Например:
test:
for(int i = 0; i < 10; i++)
for(int j = 0; j < 10; j++)
if( i > 3)
break test;
Управление памятью, сборка мусора
Необходимость явно управлять памятью в С/С++ программах всегда была большой занозой для программистов. Мало того, что сами программы изобиловали вызовами функции free или операторами delete, непосредственно к логике программы отношения не имеющими. Ошибки, связанные с неосвобождением памяти или наоборот, с удалением уже однажды удаленных объектов, относятся к категории ошибок наиболее трудных для обнаружения и исправления.
Java полностью снимает эту заботу с программиста. Автоматический сборщик мусора обязан быть встроен в run-time системы. Память объектов, на которые больше нет ссылок, в конце концов возвращается в систему. Опыт показывает, что несмотря на относительную сложность сборщиков мусора, производительность системы в целом может оказаться не меньше, а часто и больше, чем при явном освобождении памяти программой.
Сборка мусора в фоновом режиме
Одно из преимуществ того, что Java-приложения многопотоковые, заключается в том, что сборка мусора может производиться в фоновом потоке. Этот поток имеет меньший приоритет выполнения, чем остальные, поэтому система всегда готова ответить на действия пользователя, отсутствуют "периоды молчания", в которые производится только сборка мусора. С другой стороны, паузы в операциях пользователя сборщик мусора может использовать для своей работы, обеспечивая наличие свободной памяти в моменты, когда это необходимо.
Встроенная синхронизация потоков
Java поддерживает многопотоковость не только на уровне библиотек, но и на уровне самого языка, что значительно облегчает построение приложений, надежно работающих в многопотоковом режиме.
Свойства, присутствующие в С и С++, и удаленные из Java
Конструкция typedef, препроцессор
Конструкция typedef была унаследована С++ из С. Из Java она выброшена совсем.
Необходимость в макропроцессоре также во многом отпала при написании программ на С++. Почти все, для чего использовались макрорасширения, можно было сделать более элегантным и надежным образом, используя конструкции самого языка.
Система неявно поощряла создание каждым программистом своего собственного подмножества языка, неизвестного остальному миру. По мере разрастания кодов увеличивается тот смысловой контекст, в котором компилятор интерпретирует каждую строку программы. Уже в проектах среднего размера существенно возрастает нагрузка на компилятор, не говоря уже о нагрузке на память программиста.
Единственная оставшаяся важная функция препроцессора -- позволить включение в программу файлы-заголовки с описаниями классом. Эта операция может быть выполнена более просто и эффективно, если позволить компилятору читать подготовленные бинарные файлы с описанием классов. Последний путь был выбран при создании языка Java.
Все эти соображения позволили полностью исключить необходимость использования текстового препроцессора в языке Java.
Struct и union
Структуры не имеют смысла в Java, их роль полностью выполняют классы. Использование конструкций типа union для типизованных объектов также больше не нужно -- язык позволяет определить тип объекта при исполнении программы.
Функции
В этом смысле Java чисто объектно-ориентированная система. Функции и процедуры, не привязанные к контексту какого-либо объекта, больше не присутствуют в системе. В ситуации, когда функция логически не привязана к определенному экземпляру класса, она может быть создана как метод самого класса (т.е. иметь тип static).
Множественное наследование
Последовательная реализация концепции множественного наследования в С++ привела к существенным сложностям как в создании компиляторов, так и в использовании его (множественного наследования) в программах. В качестве альтернативы Java использует понятие интерфейса определяющего набор методов, которые должны быть определены в классе, реализующем этот интерфейс. Интерфейс может также содержать определение некоторых констант.
То, чего интерфейс содержать |