Java и другие языки программирования. Системное и прикладное программирование
Язык программирования Java был создан в рамках проекта корпорации Sun Microsystems по созданию компьютерных программно-аппаратных комплексов нового поколения. Первая версия языка была официально опубликована в 1995 году. С тех пор язык Java стал стандартом де-факто, вытеснив за десять лет языки C и C++ из многих областей программирования. В 1995 году они были абсолютными лидерами, но к 2006 году число программистов, использующих Java, стало заметно превышать число программистов, использующих C и C++, и составляет более четырёх с половиной миллионов человек. А число устройств, в которых используется Java, превышает полтора миллиарда.
Как связаны между собой языки C, C++, JavaScript и Java? Что между ними общего, и в чём они отличаются? В каких случаях следует, а в каких не следует их применять? Для того чтобы ответить на этот вопрос, следует сначала остановиться на особенностях программного обеспечения предыдущих поколений и о современных тенденциях в развитии программного обеспечения.
Первоначально программирование компьютеров шло в машинных кодах. Затем появились языки ASSEMBLER, которые заменили команды процессоров мнемоническими сокращениями, гораздо более удобными для человека, чем последовательности нулей и единиц. Их принято считать языками программирования низкого уровня (то есть близкими к аппаратному уровню), так как они ориентированы на особенности конкретных процессоров. Именно поэтому программы, написанные на языках ASSEMBLER, нельзя было переносить на компьютеры с другим типом процессора - процессоры имели несовместимые наборы команд. То есть они были непереносимы на уровне исходного кода (source code).
Программы, написанные в машинных кодах, то есть в виде последовательности ноликов и единиц, соответствующих командам процессора и необходимым для них данным, нет необходимости как-то преобразовывать. Их можно скопировать в нужное место памяти компьютера и передать управление первой команде программы (задать точку входа в программу).
Программы, написанные на каком-либо языке программирования, сначала надо перевести из одной формы (текстовой) в другую (двоичную, то есть в машинные коды). Процесс такого перевода называется трансляцией (от английского translation – “перевод”, “перемещение”). Не обязательно переводить программу из текстовой формы в двоичные коды, возможен процесс трансляции с одного языка программирования на другой. Или из кодов одного типа процессора в коды другого типа.
Имеется два основных вида трансляции – компиляция и интерпретация.
При компиляции первоначальный набор инструкций однократно переводится в исполняемую форму (машинные коды), и в последующем при работе программы используются только эти коды.
При интерпретации во время каждого вызова необходимых инструкций каждый раз сначала происходит перевод инструкций из одной формы (текстовой или двоичной) в другую – в исполняемые коды процессора используемого компьютера. И только потом эти коды исполняются. Естественно, что интерпретируемые коды исполняются медленнее, чем скомпилированные, так как перевод инструкций из одной формы в другую обычно занимает в несколько раз больше времени чем выполнение полученных инструкций. Но интерпретация обеспечивает большую гибкость по сравнению с компиляцией, и в ряде случаев без неё не обойтись.
В 1956 году появился язык FORTRAN – первый язык программирования высокого уровня (то есть не ориентированный на конкретную аппаратную реализацию компьютера). Он обеспечил переносимость программ на уровне исходных кодов, но довольно дорогой ценой. Во-первых, быстродействие программ, написанных на FORTRAN, было в несколько раз меньше, чем для ассемблерных. Во-вторых, эти программы занимали примерно в два раза больше места в памяти компьютера, чем ассемблерные. И, наконец, пришлось отказаться от поддержки особенностей периферийных устройств – общение с “внешним миром” пришлось ограничить простейшими возможностями, которые в программе одинаково реализовывались для ввода-вывода с помощью перфокарточного считывателя, клавиатуры, принтера, текстового дисплея и т.д. Тем не менее, языки программирования высокого уровня постепенно вытеснили языки ASSEMBLER, поскольку обеспечивали не только переносимость программ, но и гораздо более высокую их надёжность, а также несоизмеримо более высокую скорость разработки сложного программного обеспечения. FORTRAN до сих пор остаётся важнейшим языком программирования для высокопроизводительных численных научных расчётов.
Увеличение аппаратных возможностей компьютеров (количества памяти, быстродействия, появления дисковой памяти большого объёма), а также появление разнообразных периферийных устройств, привело к необходимости пересмотра того, как должны работать программы. Массовый выпуск компьютеров потребовал унификации доступа из программ к различным устройствам. Возникла идея, что из программы можно обращаться к устройству без учёта особенностей его аппаратной реализации. Это возможно, если обращение к устройству идёт не напрямую, а через прилагающуюся программу – драйвер устройства (по-английски driver означает “водитель”). Появились операционные системы - наборы драйверов и программ, распределяющих ресурсы компьютера между разными программами. Соответственно, программное обеспечение стало разделяться на системное и прикладное. Системное программное обеспечение – непосредственно обращающееся к аппаратуре, прикладное – решающее какие-либо прикладные задачи и использующее аппаратные возможности компьютера не напрямую, а через вызовы программ операционной системы. Прикладные программы стали приложениями операционной системы, или, сокращённо, приложениями (applications). Этот термин означает, что программа может работать только под управлением операционной системы. Если на том же компьютере установить другой тип операционной системы, программа-приложение первой операционной системы не будет работать.
Требования к прикладным программам принципиально отличаются от требований к системным программам. От системного программного обеспечения требуется максимальное быстродействие и минимальное количество занимаемых ресурсов, а также возможность доступа к любым необходимым аппаратным ресурсам. От прикладного – максимальная функциональность в конкретной предметной области. При этом быстродействие и занимаемые ресурсы не имеют значения до тех пор, пока не влияют на функциональность. Например, нет совершенно никакой разницы, реагирует программа на нажатие клавиши на клавиатуре за одну десятую или за одну миллионную долю секунды. Правда, на первоначальном этапе создания прикладного программного обеспечения даже прикладные по назначению программы были системными по реализации, так как оказывались вынуждены напрямую обращаться к аппаратуре.
Язык C был создан в 1972 году в одной из исследовательских групп Bell Laboratories при разработке операционной системы Unix. Сначала была предпринята попытка написать операционную систему на ASSEMBLER, но после появления в группе новых компьютеров пришлось создать платформонезависимый язык программирования высокого уровня, с помощью которого можно было бы писать операционные системы. Таким образом, язык C создавался как язык для создания системного программного обеспечения, и таким он остаётся до сих пор. Его идеология и синтаксические конструкции ориентированы на максимальную близость к аппаратному уровню реализации операций – в той степени, в какой он может быть обеспечен на аппаратно-независимом уровне. При этом главным требованием была максимальная скорость работы и минимальное количество занимаемых ресурсов, а также возможность доступа ко всем аппаратным ресурсам. Язык C является языком процедурного программирования, так как его базовыми конструкциями являются подпрограммы. В общем случае подпрограммы принято называть подпрограммами-процедурами (откуда и идёт название “процедурное программирование”) и подпрограммами-функциями. Но в C имеются только подпрограммы-функции. Обычно их называют просто функциями.
Язык C произвёл настоящую революцию в разработке программного обеспечения, получил широкое распространение и стал промышленным стандартом. Он до сих пор применяется для написания операционных систем и программирования микроконтроллеров. Но мало кто в полной мере осознаёт причины его популярности. В чём они заключались? - В том, что он смог обеспечить необходимую функциональность программного обеспечения в условиях низкой производительности компьютеров, крайней ограниченности их ресурсов и неразвитости периферийных устройств! При этом повторилась та же история, что и с FORTRAN, но теперь уже для языка системного программирования. Переход на язык программирования высокого уровня, но с минимальными потерями по производительности и ресурсам, дал большие преимущества.
Большое влияние на развитие теории программирования дал язык PASCAL, разработанный в 1974 году швейцарским профессором Никлаусом Виртом. В данной разработке имелось две части. Первая состояла в собственно языке программирования PASCAL, предназначенном для обучения идеям структурного программирования. Вторая заключалась в идее виртуальной машины. Никлаус Вирт предложил обеспечить переносимость программ, написанных на PASCAL, за счёт компиляции их в набор команд некой абстрактной P-машины (P- сокращение от PASCAL), а не в исполняемый код конкретной аппаратной платформы. А на каждой аппаратной платформе должна была работать программа, интерпретирующая эти коды. Говорят, что такая программа эмулирует (то есть имитирует) систему команд несуществующего процессора. А саму программу называют виртуальной машиной.
В связи с ограниченностью ресурсов компьютеров и отсутствием в PASCAL средств системного программирования этот язык не смог составить конкуренцию языку C, так как практически всё промышленное программирование вплоть до середины последней декады двадцатого века по реализации было системным. Идеи P-машины были в дальнейшем использованы и значительно усовершенствованы в Java.
Развитие теории и практики программирования привело к становлению в 1967-1972 годах нового направления – объектного программирования, основанного на концепциях работы с классами и объектами. Оно обеспечило принципиально новые возможности по сравнению с процедурным. Были предприняты попытки расширения различных языков путём введения в них конструкций объектного программирования. В 1982 году Бьерном Страуструпом путём такого расширения языка C был создан язык, который он назвал “C с классами”. В1983 году после очередных усовершенствований им был создан первый компилятор языка C++. Два плюса означают “C с очень большим количеством добавлений”. C++ является надмножеством над языком C – на нём можно писать программы как на “чистом C”, без использования каких-либо конструкций объектного программирования. В связи с этим, а также дополнительными преимуществами объектного программирования, он быстро приобрёл популярность и стал промышленным стандартом, сначала “де факто”, а потом и “де юре”. Так что в настоящее время C++ является базовым языком системного программирования. Длительное время он использовался и для написания прикладных программ. Но, как мы уже знаем, требования к прикладным программам совпадают к требованиям к системным только в том случае, когда быстродействие компьютера можно рассматривать как низкое, а ресурсы компьютера – малыми. Кроме этого, у языков C и C++ имеется ещё два принципиальных недостатка: а) низкая надёжность как на уровне исходного кода, так и на уровне исполняемого кода; б) отсутствие переносимости на уровне исполняемого кода. С появлением компьютерных сетей эти недостатки стали очень существенным ограничивающим фактором, поскольку вопросы безопасности при работе в локальных, и, особенно, глобальных сетях приобретают первостепенную значимость.
В 1995 году появились сразу два языка программирования, имеющие в настоящее время огромное значение –Java, разработанный в корпорации Sun, и JavaScript, разработанный в небольшой фирме Netscape Communication, получившей к тому времени известность благодаря разработке браузера Netscape Navigator.
Java создавался как универсальный язык, предназначенный для прикладного программирования в неоднородных компьютерных сетях как со стороны клиентского компьютера, так и со стороны сервера. В том числе – для использования на тонких аппаратных клиентах (устройствах малой вычислительной мощности с крайне ограниченными ресурсами). При этом скомпилированные программы Java работают только под управлением виртуальной Java-машины, поэтому они называются приложениями Java. Синтаксис операторов Java практически полностью совпадает с синтаксисом языка C, но, в отличие от C++, Java не является расширением C – это совершенно независимый язык, со своими собственными синтаксическими правилами. Он является гораздо более сильно типизированным по сравнению с C и C++, то есть вносит гораздо больше ограничений на действия с переменными и величинами разных типов. Например, в C/C++ нет разницы между целочисленными числовыми, булевскими и символьными величинами, а также адресами в памяти. То есть, например, можно умножить символ на булевское значение, из которого вычтено целое число, и разделить результат на адрес! В Java введён вполне разумный запрет на почти все действия такого рода.
Язык JavaScript создавался как узкоспециализированный прикладной язык программирования HTML-страниц, расширяющий возможности HTML, и в полной мере отвечает этим потребностям до сих пор. Следует подчеркнуть, что язык JavaScript не имеет никакого отношения к Java. Включение слова “Java” в название JavaScript являлось рекламным трюком фирмы Netscape Communication. Он также C-образен, но, в отличие от Java, является интерпретируемым. Основное назначение JavaScript – программное управление элементами WWW-документов. Языки HTML и XML позволяют задавать статический, неизменный внешний вид документов, и с их помощью невозможно запрограммировать реакцию на действия пользователя. JavaScript позволяет ввести элементы программирования в поведение документа. Программы, написанные на JavaScript, встраиваются в документы в виде исходных кодов (сценариев) и имеют небольшой размер. Для упрощения работы с динамически формируемыми документами JavaScript имеет свободную типизацию – переменные меняют тип по результату присваивания. Поэтому программы, написанные на JavaScript, гораздо менее надёжны, чем написанные на C/C++, не говоря уж про Java.
Java, JavaScript и C++ являются объектно-ориентированными языками программирования, и все они имеют C-образный синтаксис операторов. Но как объектные модели, так и базовые конструкции этих языков (за исключением синтаксиса операторов), в этих языках принципиально различны. Ни один из них не является версией или упрощением другого – это совсем разные языки, предназначенные для разных целей. Итак, Java- универсальный язык прикладного программирования, JavaScript – узкоспециализированный язык программирования HTML-документов, C++ - универсальный язык системного программирования.
В 2000 году в корпорации Microsoft была разработана платформа .Net (читается “дотнет”, DotNet– в переводе с английского “точка Net” ). Она стала альтернативой платформе Java и во многом повторяла её идеи. Основное различие заключалось в том, что для этой платформы можно использовать произвольное количество языков программирования, а не один. Причём классы .Net оказываются совместимы как в целях наследования, так и по исполняемому коду независимо от языка, используемого для их создания. Важнейшим языком .Net стал Java-образный язык C# (читается “Си шарп”). Фактически, C# унаследовал от Java большинство особенностей - динамическую объектную модель, сборку “мусора”, основные синтаксические конструкции. Хотя и является вполне самостоятельным языком программирования, имеющим много привлекательных черт. В частности, компонентные модели Java и C# принципиально отличаются.
Java стал первым универсальным C-образным языком прикладного программирования, что обеспечило лёгкость перехода на этот язык большого числа программистов, знакомых с C и C++. А наличие средств строгой проверки типов, ориентация на работу с компьютерными сетями, переносимость на уровне исполняемого кода и поддержка платформонезависимого графического интерфейса, а также запрет прямого обращения к аппаратуре обеспечили выполнение большинства требований, предъявлявшихся к языку прикладного программирования. Чем больше становятся быстродействие и объём памяти компьютеров, тем больше потребность в разделении прикладного и системного программного обеспечения. Соответственно, для прикладных программ исчезает необходимость напрямую обращаться к памяти и другим аппаратным устройствам компьютера. Поэтому среди прикладных программ с каждым годом растёт доля программного обеспечения, написанного на Java и языках .Net. Но как по числу программистов, так и по числу устройств, использующих соответствующие платформы, Java в настоящее время лидирует с большим отрывом.