Интернет магазин китайских планшетных компьютеров |
||
Компьютеры - Сравнение C Sharp и Java24 апреля 2011Оглавление: 1. Сравнение C Sharp и Java 2. Реализации 3. Использование
В этой статье сравниваются языки программирования C# и Java два очень похожих между собой современных языка со сборкой мусора и компиляцией при выполнении с C-подобным синтаксисом. Настоящая страница даёт обзор сходства и различия этих однотипных языков. Читателю следует избегать искушения «вести счёт», а вместо этого обратить внимание на причины, по которым было сделано то или иное решение. ЯзыкСинтаксисОба языка используют в качестве синтаксической основы язык программирования C. В частности, от него унаследованы без изменений:
Всё это приводит к тому, что программы на Java и C# внешне на первый взгляд выглядят чрезвычайно похоже на C-программы. В обоих языках сделаны однотипные расширения и дополнения по отношению к C, в частности, расширен алфавит и введён собственный синтаксис, поддерживающий пакеты, импорт описаний, определение единиц компиляции. Синтаксических различий также достаточно.
В целом синтаксис C# несколько объёмнее и богаче, чем Java, в частности, там присутствуют такие особенности, как возможность разделения описания одного класса на несколько модулей, описание свойств, возможность управления виртуальностью методов. Механизм работы с динамическими данными и сборка мусораОба языка реализуют одну модель работы с динамическими данными: объекты создаются динамически с помощью конструкции И в Java, и в C# есть сильные и слабые ссылки на объекты. Оба языка поддерживают методы-финализаторы. Из-за неопределённости момента удаления объекта финализаторы не могут использоваться для освобождения системных ресурсов, занятых объектом, что вынуждает создавать дополнительные методы для «очистки» объекта и вызывать их явно. C# содержит в стандартной библиотеке интерфейс // DisposableClass реализует интерфейс IDisposable и описывает его метод Dispose class DisposableClass : IDisposable { public void Dispose { // ... Здесь освобождаются занятые экземпляром ресурсы } } using ) { // ... Код, использующий объект obj } // ... Здесь для объекта obj гарантированно уже вызван метод Dispose В Java подобной конструкции нет и очистка объектов может быть выполнена только вручную: class AnyClass { void clear { // ... Здесь находится код очистки } } AnyClass obj = new AnyClass; try { // ... код, использующий объект obj } finally { obj.clear; // - явный вызов метода очистки объекта по завершении его использования } В Java 7 добавлена конструкция «try-with-resources», обеспечивающая автоматическую очистку полностью аналогично C#: try ) { return br.readLine; } При выходе из блока try все объекты, которым присвоено значение в его заголовке, будут очищены. Обязательное условие классы этих объектов должны реализовывать системный интерфейс java.lang.AutoCloseable. Java позволяет зарегистрировать слушателя, который будет получать сообщения, когда ссылка подвергается сборке мусора, что даёт улучшение производительности WeakHashMap. C# позволяет отменить выполнение финализатора для данного объекта методом Объектные средстваОба языка объектно-ориентированные, с синтаксисом, унаследованным от C++, но значительно переработанным. Код и данные могут описываться только внутри классов. ИнкапсуляцияВ Java модификатор protected в описании, помимо доступа из классов-потомков, разрешает доступ из всех классов, входящих в тот же пакет, что и класс-владелец. В C# для объектов, которые должны быть видны в пределах сборки введён отдельный модификатор internal, а protected сохраняет свой изначальный смысл, взятый из C++ доступ только из классов-потомков. Допускается комбинировать internal и protected тогда получится область доступа, соответствующая protected в Java. Внутренние классыОба языка позволяют определить класс внутри класса. Внутренние классы Java имеют доступ к нестатическим членам родительского класса, то есть «знают о this»; кроме того, внутри методов можно определять локальные классы, имеющие доступ по чтению к локальным переменным, и безымянные локальные классы, которые фактически позволяют создавать экземпляры объектов и интерфейсов, перекрывающие методы своего класса. На этом механизме в Java-программах может строиться обработка событий. Подход C# более напоминает C++. Внутренние классы в C# имеют доступ только к статическим членам внешнего класса, а для доступа к нестатическим членам нужно явно указывать экземпляр внешнего класса. Локальные внутренние классы в C# не поддерживаются, обработка событий в нём не требует таких классов, поскольку строится на других механизмах. МетодыВ обоих языках методы определяются через функции класса. Тело метода располагается внутри описания класса. Поддерживаются статические методы, абстрактные методы. В C# также есть явная реализация методов интерфейса, что позволяет классу реализовывать методы интерфейса отдельно от собственных методов или давать разные реализации одноимённых методов, принадлежащих двум разным интерфейсам. В C# примитивные типы и структуры передаются по значению, остальные типы передаются по ссылке. C# также поддерживает явное описание передачи параметров по ссылке. При использовании out компилятор контролирует наличие в методе присваивания значения. В Java параметры метода передаются только по значению, но поскольку для экземпляров классов передаётся ссылка, ничто не мешает изменить в методе экземпляр, переданный через параметр. Виртуальность методовC# копирует концепцию виртуальных методов C++: виртуальный метод должен быть явно объявлен с ключевым словом В Java, наоборот, все открытые методы, кроме статических, являются виртуальными, а переопределить метод так, чтобы механизм виртуальности не включился, невозможно. Метод всегда виртуально перекрывает метод базового класса с теми же именем и сигнатурой, если он есть. Ключевое слово Подход Java синтаксически проще, он гарантирует, что всегда вызывается метод именно того класса, к которому относится объект. С другой стороны, виртуальность действительно нужна не всегда, а накладные расходы на вызов виртуальных методов несколько больше, поскольку эти вызовы обычно не проходят инлайн-подстановку и требуют дополнительного обращения к таблице виртуальных методов. Виртуальность всех методов потенциально небезопасна: если программист по ошибке объявит метод, который уже есть в базовом классе, не имея намерения его перекрывать, а просто не обратив внимания на то, что такой метод уже есть, то новый метод перекроет одноимённый метод в базовом классе, хотя это и не входит в намерения разработчика. В C# подобная ошибка тоже возможна, но компилятор выдаст предупреждение, что перекрывающий метод объявлен без Типы данныхПримитивные типыОба языка поддерживают идею примитивных типов, и оба для трансляции примитивных типов в объектные обеспечивают их автоматическое «заворачивание» в объекты и «разворачивание». У C# имеется больше примитивных типов, чем у Java, за счёт беззнаковых целых типов, имеющихся парно ко всем знаковым, и специального типа В Java отказались от большинства беззнаковых типов ради упрощения языка. Одна из известных проблем с такими типами сложность определения типа результата арифметических операций над двумя аргументами, один из которых является знаковым, другой беззнаковым. Независимо от того, какие правила в отношении подобных операций примет язык, в некоторых ситуациях это приведёт к ошибкам» даст в результате не −10000, а 55536). СтруктурыC# позволяет создавать пользовательские типы-значения, используя ключевое слово В Java для того, чтобы от класса нельзя было наследоваться, его можно объявить финальным Перечислимые типыПеречислимые типы в C# происходят от примитивных целочисленных типов. Допустимым значением перечислимого типа является любое значение лежащего в его основе примитивного, хотя для его присваивания может потребоваться явное приведение типа. Это позволяет комбинировать значения перечислимого типа побитовой операцией «или», если они являются битовыми флагами. В Java перечислимые типы являются классами, их значения, соответственно объектами. Тип-перечисление может иметь методы, реализовывать интерфейсы. Единственные допустимые значения типа те, что перечислены в объявлении. Для комбинации их вместе как флагов требуется специальный объект набора перечислений. Возможно задавать разные реализации методов для каждого значения. Массивы и коллекцииМассивы и коллекции тоже получили выражение в синтаксисе обоих языков, благодаря особой разновидности цикла В Java могут быть объявлены, строго говоря, только одномерные массивы. Многомерный массив в Java массив массивов. В C# есть как настоящие многомерные массивы, так и массивы массивов, которые в C# обычно называются «неровными», или «ступенчатыми». Многомерные массивы всегда «прямоугольные», в то время как массивы массивов могут хранить строки разной длины. Многомерные массивы ускоряют доступ к памяти, а неровные массивы работают медленнее, но экономят память, когда не все строки заполнены. Многомерные массивы требуют для своего создания лишь один вызов оператора Параметризованные типыВ обоих языках типы могут быть параметризованными, что поддерживает парадигму обобщённого программирования. Синтаксически определение типов достаточно близко в обоих языках оно унаследовано от шаблонов C++, хотя и с некоторыми модификациями. Шаблоны Java являются чисто языковой конструкцией и реализованы лишь в компиляторе. Компилятор заменяет все обобщённые типы на их верхние границы и вставляет соответствующее приведение типов в те места, где используется шаблон. В результате получается байт-код, который не содержит ссылок на обобщённые типы и их параметры. Такая техника реализации обобщённых типов называется затиранием типов. Это означает, что информация об исходных обобщённых типах во время выполнения недоступна, и обусловливает некоторые ограничения, такие как невозможность создавать новые экземпляры массивов из аргументов обобщённого типа. Среда выполнения Java не знакома с системой обобщённых типов, вследствие чего новым реализациям JVM понадобились лишь минимальные обновления для работы с новым форматом классов. C# пошёл другим путём. Поддержка обобщённости была интегрирована в саму виртуальную среду выполнения, впервые появившись в .NET 2.0. Язык здесь стал лишь внешним интерфейсом для доступа к этим возможностям среды. Как и в Java, компилятор производит статическую проверку типов, но в дополнение к этому JIT производит проверку корректности во время загрузки. Информация об обобщённых типах полностью присутствует во время выполнения и позволяет полную поддержку рефлексии обобщённых типов и создание их новых реализаций. Подход Java требует дополнительных проверок во время выполнения, не гарантирует, что клиент кода будет следовать соответствию типов, и не обеспечивает рефлексии для обобщённых типов. Java не позволяет специализировать обобщённые типы примитивными, в то время как C# обеспечивает обобщение как для ссылочных типов, так и для типов-значений, включая примитивные. Вместо этого Java предлагает использование завёрнутых примитивных типов в качестве параметров, но это даётся ценой дополнительного выделения динамической памяти. Как в Java, так и в C# специализации обобщённого типа на разных ссылочных типах дают одинаковый код , но для C# среда выполнения динамически генерирует оптимизированный код при специализации на типах-значениях, что позволяет их хранить и извлекать из контейнеров без операций за- и разворачивания. Обработка событийJava требует от программиста ручной реализации шаблона наблюдателя, хоть и обеспечивает некоторый синтаксический сахар в виде анонимных вложенных классов, что позволяет определить тело класса и тут же создать его экземпляр в одной точке кода. C# предоставляет обширную поддержку событийного программирования на уровне языка, включая делегаты .NET, мультикастинг, специальный синтаксис для задания событий в классах, операции для регистрации и разрегистрации обработчиков события, ковариантность делегатов и анонимные методы с полным набором семантики замыкания. Замыкания предлагаются к включению в Java SE 7. Эти замыкания, как делегаты в C#, имели бы полный доступ ко всем локальным переменным в данной области видимости, а не только доступ для чтения к переменным, помеченным словом Перегрузка операцийC# включает перегрузку операций и задаваемое пользователем приведение типов, знакомые программирующим на C++. C# её поддерживает с некоторыми ограничениями, обеспечивающими логическую целостность, что при осторожном использовании помогает сделать код более лаконичным и читаемым. Java не включает перегрузку операций во избежание злоупотреблений ею и для поддержания простоты языка. СвойстваC# поддерживает концепцию «свойств» псевдополей класса, к которым обеспечивается полностью контролируемый доступ путём создания методов для извлечения и записи значения поля. Описания свойств производятся с помощью конструкций C# также включает так называемые индексаторы, которые можно считать особым случаем перегрузки операций, или параметризованными свойствами. Индексатор это свойство с именем myList = 5; string name = xmlNode.Attributes; orders = customerMap; Использование свойств не одобряется некоторыми авторитетными программистами. В частности, Джеффри Рихтер пишет:
Согласно общепринятому в C# стилю именования, имена свойств визуально отличаются от полей тем, что начинаются с прописной буквы. Условная компиляцияC#, в отличие от Java, поддерживает условную компиляцию с использованием директив препроцессора. В нём также есть атрибут Java версий 1.4 и выше включает в язык возможность проверки допущений, включаемую во время выполнения. Кроме того, конструкции if с константными условиями могут разворачиваться на этапе компиляции. Существуют сторонние реализации препроцессоров для Java, они используются преимущественно при разработке приложений для мобильных устройств. Пространства имён, сборки, пакетыC# использует пространства имён, напоминающие одноимённый механизм C++. Каждый класс относится к некоторому пространству имён, те классы, для которых пространство имён не указано явно, относятся к безымянному пространству имён по умолчанию. Пространства имён могут быть вложенными друг в друга. В Java имеются пакеты, отчасти похожие на пространства имён C#. Пакеты могут быть вложенными, каждый описываемый класс относится к некоторому пакету, при отсутствии явного указания пакета класс относится к глобальному безымянному пакету. В обоих языках для обращения к объекту, объявленному в другом пространстве имён или пакете, нужно объявить в программе требуемый пакет как используемый. Обращение к объекту производится через квалифицированное имя, в качестве квалификатора используется имя пакета. Если требуется обращение к объекту без квалификации, программный модуль должен содержать директиву разыменования: using в C# и import в Java. В C# пространства имён никак не связаны с компилированными модулями. Несколько сборок могут содержать одно и то же пространство имён, в одной сборке может объявляться несколько пространств имён, не обязательно вложенных. Модификаторы области видимости C# никак не связаны с пространствами имён. В Java объявленные в одном пакете классы по умолчанию образуют единый компилированный модуль. Модификатор области видимости по умолчанию ограничивает область видимости полей и методов класса пределами пакета. В Java структура файлов и каталогов исходных текстов пакета по умолчанию связана со структурой пакета пакету соответствует каталог, входящим в него подпакетам подкаталоги этого каталога, файлы исходных текстов располагаются в каталогах, соответствующих пакету или подпакету, в который они входят. Таким образом, дерево исходных текстов повторяет структуру пакета. В C# местонахождение файла с исходным текстом никак не связано с его пространством имён. Расположение исходного текста в файлахВ C# классы могут располагаться в файлах произвольным образом. Имя файла исходного кода никак не связано с именами определяемых в нём классов. Допускается расположить в одном файле несколько общедоступных классов. Начиная с версии 2.0, C# позволяет также разбить класс на два и более файла. Последняя особенность активно используется визуальными средствами построения интерфейса: часть класса, в которой находятся поля и методы, управляемые конструктором интерфейса, выделяются в отдельный файл, чтобы не загромождать автоматически генерируемым кодом файл, непосредственно редактируемый программистом. В Java каждый файл может содержать только один общедоступный класс, причём Java требует, чтобы имя файла совпадало с именем этого класса, что исключает путаницу в именах файлов и классов. Более того, согласно рекомендуемому Sun соглашению об оформлении кода, размер файла исходного кода не должен превышать 2000 строк кода, поскольку в файле большего размера труднее разбираться. Большой размер файла также считается признаком плохого проектного решения. ИсключенияОба языка поддерживают механизм обработки исключений, синтаксически оформленный совершенно одинаково: в языке имеется оператор генерации исключения Java поддерживает проверяемые исключения: программист должен явно указать для каждого метода типы исключений, которые метод может выбросить, эти типы перечисляют в объявлении метода после ключевого слова C# проверяемые исключения не поддерживает. Их отсутствие является сознательным выбором разработчиков. Андерс Хейлсберг, главный архитектор C#, считает, что в Java они были в какой-то степени экспериментом и себя не оправдали. Вопрос о пользе проверяемых исключений дискуссионный. Подробнее см. статью Обработка исключений. Параллельное программированиеВ целом механизмы параллельного программирования в C# аналогичны тем, что предоставляет Java, различие состоит в деталях реализации. В обоих случаях имеется библиотечный класс Thread, реализующий понятие «потока». Java предоставляет два способа создания собственных потоков: либо путём расширения класса Thread, либо путём реализации интерфейса Runnable. В обоих случаях программист должен определить наследуемый метод run, содержащий тело потока код, который будет в нём выполняться. C# вместо этого использует механизм делегатов: для создания потока создаётся экземпляр стандартного класса Thread, которому передаётся в виде параметра конструктора делегат, содержащий метод тело потока. В обоих языках есть возможность создать синхронно исполняемый блок кода; в Java это делается с помощью оператора synchronized, в C# оператором lock. В Java имеется также возможность объявлять синхронные методы, используя модификатор synchronized в заголовке описания метода. Такие методы при исполнении блокируют свой объект-хозяин. Аналогичная возможность в .NET реализуется с помощью атрибута реализации метода MethodImplAttribute MethodImplOptions.Synhronized, но, в отличие от Java, эта возможность формально не является частью языка C#. В обоих языках доступны также идентичные средства синхронизации, основанные на отправке и ожидании сигнала от одного потока к другому. В Java это методы notify, notifyAll и wait, в C# методы Pulse, PulseAll, Wait. Различие состоит лишь в том, что в Java эти методы реализуется в классе Object, поэтому для синхронизации не требуется никаких дополнительных библиотек, а в C# эти методы реализованы как статические в отдельном библиотечном классе Monitor. В C# стандартная библиотека содержит также несколько дополнительных примитивов синхронизации параллельного исполнения потоков: мьютексы, семафоры, синхронизирующие таймеры. С версии 1.5 в JDK SE включены пакеты java.util.concurrent, java.util.concurrent.atomic и java.util.concurrent.locks содержащие исчерпывающий набор средств для реализации параллельных вычислений. Низкоуровневый кодJava Native Interface позволяет программам вызывать из Java низкоуровневые, системно-зависимые функции. Как правило, JNI используется при написании драйверов. При написании JNI-библиотек разработчик должен использовать специальный API, предоставляемый бесплатно. Выпускаются также специализированные библиотеки для взаимодействия Java с COM. В своё время MS ввела поддержку COM-объектов в Java на уровне языка в своей JVM, что послужило причиной отзыва Sun Microsystems лицензии на выпуск JVM у MS. Технология Platform Invoke, реализованная в .NET, позволяет вызывать из C# внешний код, который Microsoft называет неуправляемым. Через атрибуты в метаданных программист может точно управлять передачей параметров и результатов, избегая таким образом необходимости дополнительного кода адаптации. P/Invoke предоставляет почти полный доступ к процедурным API, но не даёт прямого доступа к библиотекам классов C++. .NET Framework предоставляет также мост между .NET и COM, позволяя обращаться к COM-компонентам так, как если бы они были родными объектами .NET, что требует дополнительных усилий программиста при использовании COM-компонент со сложными нетривиальными интерфейсами. В этих случаях приходится прибегать к unsafe коду или другим обходным путям. C# разрешает ограниченное использование указателей, которые проектировщики языков зачастую считают опасными. Подход C# в этом деле требование ключевого слова C# также позволяет программисту отключить нормальную проверку типов и другие возможности безопасности CLR, разрешая использование переменных-указателей при условии применения ключевого слова Просмотров: 4740
|