Сравнение C Sharp и Java

редактировать

В статье сравниваются два языка программирования : C#с Java . Хотя в этой статье уделяется особое внимание языкам и их функциям, при таком сравнении обязательно будут рассмотрены некоторые особенности платформ и библиотек. Для более подробного сравнения платформ см. Сравнение платформ Java и.NET.

C# и Java - похожие языки, которые набираются статически, строго и явно. Оба являются объектно-ориентированными и разработаны с полу- интерпретацией или во время выполнения своевременной компиляцией, и оба являются языками фигурных скобок, например C и C ++.

Содержание

  • 1 Типы
    • 1.1 Единая система типов
    • 1.2 Типы данных
      • 1.2.1 Числовые типы
        • 1.2. 1.1 Целые числа со знаком
        • 1.2.1.2 Целые числа без знака
        • 1.2.1.3 Десятичные числа высокой точности
      • 1.2.2 Расширенные числовые типы
      • 1.2.3 Символы
      • 1.2.4 Встроенные составные типы данных
    • 1.3 Пользовательский тип значения (структура)
    • 1.4 Перечисления
    • 1.5 Делегаты, ссылки на методы
    • 1.6 Поднимаемые (обнуляемые) типы
    • 1.7 Тип с поздней привязкой (динамический)
    • 1.8 Указатели
    • 1.9 Ссылочные типы
    • 1.10 Массивы и коллекции
  • 2 Выражения и операторы
    • 2.1 Упаковка и распаковка
  • 3 Заявления
  • 4 Синтаксис
    • 4.1 Ключевые слова и обратная совместимость
  • 5 Объект -ориентированное программирование
    • 5.1 Частичный класс
    • 5.2 Внутренний и локальный классы
    • 5.3 Событие
    • 5.4 Перегрузка оператора и c онверсии
    • 5.5 Индексатор
    • 5.6 Поля и инициализация
      • 5.6.1 Инициализация объекта
    • 5.7 Удаление ресурсов
    • 5.8 Методы
      • 5.8.1 Методы расширения и методы по умолчанию
      • 5.8.2 Частично
      • 5.8.3 Виртуальные методы
      • 5.8.4 Постоянные / неизменяемые параметры
      • 5.8.5 Генераторные методы
      • 5.8.6 Явная реализация интерфейса
      • 5.8.7 Эталонные (входящие / исходящие) параметры
    • 5.9 Исключения
      • 5.9.1 Проверенные исключения
      • 5.9.2 Try-catch-finally
      • 5.9.3 Окончательные блоки
  • 6 Generics
    • 6.1 Стирание типов по сравнению с повторными универсальными шаблонами
    • 6.2 Миграция совместимость
    • 6.3 Ковариация и контравариантность
  • 7 Функциональное программирование
    • 7.1 Замыкания
    • 7.2 Лямбда-выражения и деревья выражений
  • 8 Метаданные
  • 9 Предварительная обработка, компиляция и упаковка
    • 9.1 Пространства имен и содержимое файлов
    • 9.2 Условная компиляция
  • 10 Потоковые и асинхронные функции
    • 10.1 Параллелизм на основе задач для C #
    • 10.2 Параллелизм на основе задач для Java
  • 11 Дополнительные функции
    • 11.1 Числовой a pplications
    • 11.2 Интегрированный запрос языка (LINQ)
    • 11.3 Собственная со вместимость
    • 11.4 Среда выполнения
  • 12 Примеры
    • 12.1 Ввод / вывод
    • 12.2 Интеграция типов, определяемых библиотекой
    • 12.3 C # делегаты и эквивалентные конструкции Java
    • 12.4 Повышение качества типов
    • 12.5 Взаимодействие с динамическими языками
      • 12.5.1 Использование GraalVM
      • 12.5.2 Традиционный способ
    • 12.6 Последовательность Фибоначчи
      • 12.6.1 Функциональность с потоком Стиль API
  • 13 См. Также
  • 14 Ссылки
  • 15 Внешние ссылки

Типы

Типы данныхJavaC #
Десятичные дроби произвольного размераСправочная информация тип; без операторовСторонняя библиотека
Целые числа произвольного размераТип ссылки; нет операторовДа
МассивыДаДа
Логический тип ДаДа
Символ ДаДа
Комплексные числаСторонняя библиотекаДа
Дата / времяДа; тип ссылкиДа; тип значения
Перечислимые типыДа; тип ссылкиДа; скаляр
Десятичное число высокой точностиНет; но см. «Десятичные дроби произвольного размера» выше128-битный (28 цифр) Тип десятичного числа
IEEE 754 binary32 число с плавающей запятойДаДа
IEEE 754 binary64 число с плавающей запятойДаДа
Поднятые (допускающие значение NULL) ТипНет; но типы оболочкиДа
УказателиНет; только ссылки на методыДа
Типы ссылокДаДа
Целые числа со знаком Да; 8, 16, 32, 64 битДа; 8, 16, 32, 64 бита
Строки Неизменяемый ссылочный тип, Юникод Неизменяемый ссылочный тип, Юникод
Аннотации типовДаДа
Однокорневая (унифицированная) система типов Нет; но типы оболочкиДа
Кортежи Нет; доступно ограниченное количество сторонних производителей.Да
Целые числа без знака Нет; но поддержка некоторых методов.Да; 8, 16, 32, 64 бита
Типы значенийНет; только примитивные типыДа

Единая система типов

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

В Java составные типы синонимами ссылочных типов; методы не являются ссылочным типом класса. В концепции инкапсуляции и методов были отделены требования к методам инкапсуляции, не являющимся ссылочным типом. Однако только ссылочные типы виртуальные методы и специализацию.

Оба языка множество встроенных типов, которые копируются и передаются по значению, а не по ссылке. Java называет эти типы примитивными типами, тогда как в C # они называются простыми типами. Примитивные / простые обычно имеют встроенную поддержку со стороны системы процессора.

Простые типы C # реализуют несколько интерфейсов и, вызывают, вызывают, создают методы для экземпляров типов, даже для литералов. Имена типов C # также являются просто псевдонимами для типов Common Language Runtime (CLR). Тип C # System.Int64в точности совпадает с типом long; единственное отличие состоит в том, что первое - это каноническое имя.NET, а второе - псевдоним C #.

Java не предлагает методов непосредственно для примитивных типов. Вместо этого методы, которые работают с примитивными значениями, через сопутствующие примитивные классы-оболочки . Существует фиксированный набор таких классов-оболочек, каждый из которых является оболочкой для одного из фиксированного набора примитивных типов. Например, тип Java Long- это ссылочный тип , который является оболочкой для примитивного типа long. Однако они не одного типа.

Типы данных

Числовые типы

Целые числа со знаком

И Java, и C # целые числа со знаком с разрядностью 8, 16, 32 и 64 бит. Они используют одно и то же имя / псевдонимы для типов, за исключением 8-битного целого числа, которое называется байтомв Java и sbyte(байт со знаком) в C #.

Целые числа без знака

C # поддерживает беззнаковый в дополнение к целочисленным типам со знаком. Типы без знака - это byte, ushort, uintи ulongдля ширины 8, 16, 32 и 64 бит соответственно. Также поддерживаются беззнаковые арифметические операции с типами. Например, добавление двух целых чисел без знака (uints) по-прежнему дает в результате uint; не длинное целое число со знаком.

В Java нет целочисленных типов без знака. В частности, в Java отсутствует примитивный тип для беззнакового байта. Вместо этого тип байтав Java - это расширенный знак, который является частым источником ошибок и путаницы.

Целые числа без знака были намеренно исключены из Java, потому что Джеймс Гослинг считал, что программисты не поймут, как работает беззнаковая арифметика.

В разработке языков программирования одна из стандартных проблем состоит в том, что язык становится настолько сложным, что никто не может его понять. Один из небольших экспериментов, который я провел, - это расспрашивать людей о правилах беззнаковой арифметики в C. Оказывается, никто не понимает, как работает беззнаковая арифметика в C. Есть несколько очевидных вещей, которые люди понимают, но многие этого не понимают.

Десятичные числа высокой точности

В C # есть тип и буквальная нотация для десятичных чисел высокой точности (28 десятичных цифр) арифметика, подходящая для финансовых и денежных расчетов. В отличие от типов данных floatи double, десятичные дробные числа, такие как 0,1, могут быть представлены точно в десятичном представлении. В представлениях с плавающей запятой и двойными числами часто возникают большие двоичные расширения, которые делают эти представления более подверженными неправильным округлениям.

Хотя в Java нет такого встроенного типа, в библиотеке Java есть десятичный тип произвольной точности. Это не считается языковым типом и не поддерживает обычные арифметические операторы; скорее это ссылочный тип, которым нужно управлять с помощью методов типа. Подробнее о числах произвольного размера / точности ниже.

Расширенные числовые типы

Оба языка представляют библиотекой арифметические типы произвольной точности для целых чисел произвольного размера и десятичной точки расчеты.

Только в Java есть тип данных для вычислений с десятичной точкой произвольной точности. Только C # имеет тип работы с комплексными числами.

. На обоих языках количество операций, которые могут быть выполнены с расширенными числовыми типами, ограничено по со встроенным IEEE 754 с плавающей точкой. точечные типы. Например, ни один из типов произвольного размера не поддерживает квадратный корень или логарифмы.

. C # позволяет интегрировать стандартные, библиотекой, с существующими типами и операторами с помощью пользовательских неявных / явных преобразований и оператора перегрузка. См. Пример в разделе Интеграция типов устанавливаемых библиотекой

Символы

Оба языка имеют собственный тип char(символьный) как простой тип. Хотя тип charможет ввести с помощью преобразования значения charв целочисленное значение перед операцией. Таким образом, результатом побитовой операции в обоих языках является числовой тип, а не символ.

Встроенные составные типы данных

Оба языка обрабатывают строки как (неизменяемые ) объекты ссылочного типа. В обоих языках тип содержит несколько методов для управления строками, синтаксического анализа, форматирования и т. Д. На обоих языках регулярные выражения имеют внешнюю функцию и реализуются в отдельных классах.

Библиотеки обоих языков определяют классы для работы с датами и календарями в разных культурах. Java java.util.Dateявляется изменяемым ссылочным типом, тогда как C # System.DateTimeявляется типом значений структуры. C # обычно определяет тип TimeSpanдля работы с периодами времени. Оба языка арифметику даты и времени в соответствии с различными культурами.

Определяемый через тип значения (структура)

C # позволяет программисту создать определяемые параметры типы значений, используя соответствующее слово struct. В отличие от классов и, как и стандартные примитивы, такие типы значений передаются и присваиваются по значению, а не по ссылке. Они также могут быть частью объекта (в виде поля или в рамке ) или храниться в массиве без использования какого-либо объекта памяти, обычно используется для типов классов.

Первые символы не имеют значения имеют о значении nullи они могут быть введены в конструкцию без инициализации, они всегда поставляются с неявным конструктором по умолчанию. памяти с нуля. Программист может определять дополнительные конструкторы только с одним или аргументами. Типы значений не имеют таблиц виртуальных методов, и из-за этого (и фиксированного объема памяти) они неявно запечатаны. Однако различные значения могут (и часто реализуют) реализовать интерфейсы. Например, встроенные целочисленные типы реализуют несколько интерфейсов .

Помимо встроенных примитивных типов, Java не включает в себя концепцию типов.

Перечисления

Оба языка определяют перечисления, но реализованы принципиально по-разному. Таким образом, перечисления - это одна из областей, где инструменты, разработанные для автоматического перевода кода между двумя языками (например, преобразователи Java в C #), не работают.

В C # перечисления реализованы аналогично C, то есть как оболочки для битовых флагов, реализованные в примитивных целочисленных типах (int, byte, short и т. Д.). Это дает преимущества в производительности и улучшает взаимодействие с скомпилированным кодом C / C ++, но дает меньше функций и может привести к ошибкам, если значений низкого уровня напрямую приводятся к типу перечисления, как это разрешено на языке C #. Следовательно, это рассматривается как синтаксический сахар. Напротив, Java реализует перечисление как полнофункциональный набор экземпляров, требующий больше памяти и не способствующий взаимодействию с кодом C / C ++, но предоставляющий дополнительные функции в отражении и внутреннем поведении. Реализация на каждом языке описана в таблице ниже.

JavaC #
ОпределениеВ Java типом перечисления является класс, а его значениями являются объекты (экземпляры) этого класса. Единственными допустимыми значениями являются те, которые в перечислении. Тип перечисления может объявлять поля , позволяя каждому отдельному перечисляемому значению ссылаться на дополнительные данные, однозначно связанные с этим конкретным значением. Тип перечисления может также объявлять или переопределять методы или реализовывать интерфейсы.Перечисления в C # неявно производятся от типа Enum, который является снова типом значения производная. Набор значений перечисления C # основан на базовом типе, который может быть целочисленным типом со знаком или без знака длиной 8, 16, 32 или 64 бита. Определение перечисления имен для выбранных целочисленных значений. По умолчанию первому имени присваивается значение 0 (ноль), следующие имена назначаются с шагом 1. Любое значение базового примитивного типа является допустимым значением типа перечисления, хотя для его присвоения может потребоваться явное приведение.
Объединениенабора перечислений Java и коллекций карт обеспечения функциональности для объединения перечислений в объединенное значение. Эти специальные коллекции позволяют оптимизировать компилятор для минимизации накладных расходов, связанных с использованием коллекций в механизме комбинирования.C # поддерживает побитовые перечисления, где фактическое значение может быть комбинацией пронумерованных значений, побитовых или соединенных вместе. Методы форматирования и синтаксического анализа, неявно типом, будут пытаться использовать эти значения.

И в C #, и в Java программы могут использовать перечисления в операторе переключения без преобразования в строковый или примитивный целочисленный тип. Однако C # не допускает неявного провала, если оператор case не содержит никакого кода, так как это частая причина труднодоступных ошибок. Отказ должен быть явно объявлен с помощью оператора goto.

Делегаты, ссылки на методы

C # реализует указатели объектно-ориентированных методов в делегатов. Делегат - это особый тип, который может записывать ссылку на метод. Затем эту ссылку можно сохранить в переменном типа делегата или передать методу через параметр делегата для последующего вызова. Делегаты C # включает ковариацию и контравариантность и содержат ссылку на любой совместимый с сигнатурой статический метод, метод экземпляра, анонимный метод или лямбда-выражение.

Делегаты не следует путать сопоставлениями и встроенными функциями.. Эти функции связаны, потому что ссылка на закрывающую / встроенную функцию должна быть зафиксирована в делегат, чтобы быть полезной. Но делегат не всегда упоминается на встроенную функцию; он также может ссылаться на сопряжение статические методы или методы экземпляра. Делегаты составляют основу событий C #, но их также не следует путать с ними.

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

См. Также пример делегатов C # и эквивалентных конструкций Java.

Поднимаемые (обнуляемые) типы

C # позволяет «поднять» значения / примитивные / простые типы, чтобы допустить специальное значение nullв дополнение к собственным значениям типа. Тип повышается добавлением суффикса ?к имени типа; это эквивалентно использованию универсального типа Nullable , где T- это тип, который нужно отменить. Неявно определены преобразования для преобразования между значениями базового и повышенного типов. Поднятый тип можно сравнить с nullили его можно проверить на HasValue. Кроме того, поднятые операторы неявно и автоматически определяются на основе их не поднятого основания, где - за исключением некоторых логических операторов - нулевой аргумент будет распространяться на результат.

Java не поддерживает подъем типов как концепцию, но все встроенные примитивные типы имеют соответствующие типы-оболочки, которые поддерживают значение nullв силу того, что они являются ссылочными типами (классы).

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

В следующем примере показано различное поведение. В C # приподнятый оператор * передает значение операнда null; в Java при распаковке нулевой ссылки возникает исключение.

Не все операторы C # с расширением были определены для безусловного распространения null, если один из операндов null. В частности, логические операторы были расширены для поддержки троичной логики, таким образом сохраняя импеданс с SQL.

. Логические операторы Java не поддерживают троичную логику и не реализованы в библиотека базового класса.

Тип с поздней привязкой (динамический)

В C # есть динамический тип с поздней привязкой, который поддерживает динамический вызов без отражения, взаимодействие с динамическими языками и специальную привязку для (например) документирования объектных моделей. Тип dynamicразрешает доступ к члену динамически во время выполнения, в отличие от статического / виртуального во время компиляции. Механизм поиска членов расширяем за счет традиционного отражения в качестве механизма отката.

Существует несколько вариантов использования типа dynamicв C #:

  • Менее подробное использование отражения: при приведении экземпляра к типу dynamicтакие члены поскольку свойства, методы, события и т. д. могут быть напрямую вызваны в экземпляре без непосредственного использования API отражения.
  • Взаимодействие с динамическими языками: динамический тип поставляется с узловым интерфейсом поддержка реализации динамически типизированных объектов и общей инфраструктуры времени выполнения для эффективного поиска членов.
  • Создание динамических абстракций на лету: например, динамический объект может обеспечить более простой доступ к объектным моделям документов, таким как XML или XHTML документы.

Java не поддерживает тип с поздним связыванием. Варианты использования динамического типа C # имеют разные соответствующие конструкции в Java:

  • Для динамического вызова по имени ранее существовавших типов с поздним связыванием следует использовать отражение.
  • Для взаимодействия с динамическими языками необходимо использовать некоторую форму должен использоваться API совместимости, специфичный для этого языка. На платформе виртуальной машины Java реализовано несколько динамических языков, но не существует общего стандарта для передачи объектов между языками. Обычно это включает в себя некоторую форму отражения или подобного отражению API. В качестве примера использования объектов JavaFX из Java.
  • Для создания и взаимодействия с объектами полностью во время выполнения, например, взаимодействия с абстракцией объектной модели документа, должен использоваться определенный API абстракции.

См. также пример # Взаимодействие с динамическими языками.

Указатели

Java исключает указатели и арифметику указателей в среде выполнения Java. Разработчики языка Java рассудили, что указатели являются одной из основных функций, позволяющих программистам вставлять ошибки в свой код, и предпочли не поддерживать их. Java не позволяет напрямую передавать и получать объекты / структуры в / из базовой операционной системы и, следовательно, не нуждается в моделировании объектов / структур для такого конкретного макета памяти, макетов, которые часто будут включать указатели. Связь Java с базовой операционной системой вместо этого основана на Java Native Interface (JNI), где связь с базовой операционной системой / адаптация к ней осуществляется через внешний клеевой слой.

В то время как C # действительно допускает использование указателей и соответствующей арифметики указателей, разработчики языка C # опасались того, что указатели потенциально могут использоваться для обхода строгих правил доступа к объектам. Таким образом, C # по умолчанию также исключает указатели. Однако, поскольку указатели необходимы при вызове многих собственных функций, указатели разрешены в явном небезопасном режиме. Блоки кода или методы, использующие указатели, должны быть помечены ключевым словом unsafe, чтобы можно было использовать указатели, а компилятору требуется переключатель / unsafe, чтобы разрешить компиляцию такого кода. Сборки, скомпилированные с использованием переключателя / unsafe, помечаются как таковые и могут выполняться только при явном доверии. Это позволяет использовать указатели и арифметику указателей для прямой передачи и получения объектов в / из операционной системы или других собственных API-интерфейсов с использованием внутренней структуры памяти для этих объектов, а также изолировать такой потенциально опасный код в специально доверенных сборках.

Типы ссылок

В обоих языках ссылки являются центральным понятием. Все экземпляры классов даны по ссылке.

Хотя синтаксис языка сам по себе не очевиден, оба языка поддерживают концепцию слабых ссылок. Экземпляр, на который ссылаются только слабые ссылки, имеет право на сборку мусора, как если бы ссылок вообще не было. На обоих языках эта функция предоставляется через связанные библиотеки, хотя на самом деле это основная функция времени выполнения.

Наряду со слабыми ссылками в Java есть мягкие ссылки. Они очень похожи на слабые ссылки, но JVM не освобождает объекты с мягкими ссылками до тех пор, пока не потребуется память.

Типы ссылокJavaC #
Сборка мусора ДаДа
Слабые ссылки ДаДа
Очередь ссылок (взаимодействие со сборкой мусора)ДаДа
Мягкие ссылки ДаДа
Фантомные ссылки ДаНет
Поддержка прокси Да; генерация проксиДа; контексты объектов

Массивы и коллекции

Массивы и коллекции - это концепции, представленные обоими языками.

Массивы и Коллекции JavaC #
Абстрактные типы данных ДаДа
Одномерные, с отсчетом от нуля индексные массивыДаДа
Многомерные массивы, прямоугольные (одиночный массив)NoДа
Многомерные массивы, зубчатые (массивы массивов)ДаДа
Ненулевые массивыNoНекоторые
Унифицированные массивы и коллекцииNoДа
Карты / словари ДаДа
Сортированные словариДаДа
НаборыДаДа
Сортированные наборыДаДа
Списки / векторыДаДа
Очереди / стеки ДаДа
Очередь с приоритетом ДаДа
Сумки / мультимножестваСторонняя библиотекаДа
Коллекции, оптимизированные для параллелизмаДаДа

Синтаксис, используемый для объявления массивов и доступа к ним, идентичен, за исключением того, что в C # добавлен синтаксис для объявления многомерных массивов и управления ими.

JavaC #
Массивы являются неявно прямыми специализациями Object. Они не унифицированы с типами коллекций.Массивы в C # являются неявной специализацией класса System.Array, который реализует несколько интерфейсов коллекций .
Массивы и коллекции полностью разделены без объединения. Массивы не могут передаваться там, где ожидаются последовательности или коллекции (хотя их можно обернуть с помощью Arrays.asList).Массивы могут передаваться там, где ожидаются последовательности (IEnumerables) или интерфейсы коллекций / списков . Однако операции сбора, которые изменяют количество элементов (вставка / добавление / удаление), будут вызывать исключения, поскольку эти операции не поддерживаются массивами.
Оператор forпринимает либо массивы, либо Iterable. Все коллекции реализуют Iterable. Это означает, что такой же короткий синтаксис можно использовать в циклах for.Оператор foreachвыполняет итерацию по последовательности с использованием конкретной реализации метода GetEnumerator, обычно реализуемого с помощью IEnumerableили IEnumerable интерфейс. Поскольку массивы всегда неявно реализуют эти интерфейсы, цикл также будет перебирать массивы.
В обоих языках массивы ссылочных типов ковариантны. Это означает, что массив Stringможет быть назначен переменным Object, поскольку Stringявляется специализацией (присваивается) Object. В обоих языках массивы будут выполнять проверку типа при вставке новых значений, поскольку в противном случае безопасность типов была бы нарушена. Это контрастирует с тем, как общие коллекции были реализованы на обоих языках.
Нет многомерных массивов (прямоугольных массивов), но есть массивы ссылок на массивы (зубчатые массивы ).Многомерные массивы (прямоугольные массивы) и массивы ссылок на массивы (зубчатые массивы ).
нельзя изменить размер массивов (хотя использование метода System.arraycopy ()может допускать многоэтапное изменение размера массива)Размер массивов можно изменять, сохраняя существующие значения с помощью Array.Resize ()метод статического массива (но он может вернуть новый массив).
Реализовано как дооснащение для библиотеки java.utilс дополнительными функциями, такими как структуры данных, такие как наборы и связанные наборы, и имеет несколько алгоритмов для управления элементами коллекции, например поиск самого большого элемента на основе некоторого объекта Comparator, поиск самого маленького элемента, поиск подсписок в списке, изменение содержимого списка, перемешивание содержимого списка, создание неизменяемых версий коллекции, выполняет сортировку и выполняет двоичный поиск.Структура коллекций C # состоит из классов из пространств имен System.Collectionsи System.Collections.Genericс несколькими полезными интерфейсы, абстрактные классы и структуры данных. NET 3.5 добавлено пространство имен System.Linq, которое содержит различные методы расширения для запроса коллекций, такие как Aggregate, All, Average, Distinct, Join, Unionи многие другие. Запросы, использующие эти методы, называются Language Integrated Query (LINQ).

Многомерные массивы в некоторых случаях могут повысить производительность из-за увеличения локальности (поскольку для каждого измерения массива используется одно разыменование указателя, а не одно, как в случае массивов с зубчатыми краями). Однако, поскольку для доступа ко всем элементам массива в многомерном массиве требуется умножение / сдвиг между двумя или более измерениями, это является преимуществом только в сценариях с очень произвольным доступом.

Еще одно отличие состоит в том, что весь многомерный массив можно выделить с помощью одного применения оператора new, в то время как зубчатые массивы требуют циклов и выделения для каждого измерения. Однако Java предоставляет синтаксическую конструкцию для выделения зубчатого массива постоянной длины; затем циклы и множественные выделения выполняются виртуальной машиной и не обязательно должны быть явными на уровне источника.

Оба языка имеют обширный набор типов коллекций, который включает различные упорядоченные и неупорядоченные типы списков, карт / словарей, наборов и т. Д.

Java также поддерживает синтаксис C / C ++:

JavaC #
// Допустимо, поскольку числа - это объект типа short short numbers = new short [100]; // Верно, но непонятно code double values ​​= new double [100];
// Допустимо, так как числа - это объект типа short short numbers = new short [100]; // Не компилируется! двойные значения = новое двойное [100];

Выражения и операторы

Выражения и операторыJavaC #
Арифметические операторыДаДа
Логические операторыДаДа
Побитовые логические операторыДаДа
УсловныйДаДа
Конкатенация строкДаДа
ТранслируетДаДа
Бокс Да; неявныйДа; неявный
Распаковка Да; неявныйДа; явный
Поднятые операторыНет, но см. java.util.OptionalДа
Контроль переполненияNoДа
Строгая оценка с плавающей запятойДа; отказ от участияДа; opt-in
Дословные (здесь-) строкиДа; входит в версию 15Да

Упаковка и распаковка

Оба языка позволяют автоматическую упаковку и распаковку, т.е. они позволяют неявное преобразование между любыми примитивными типами и соответствующими ссылочные типы.. В C # примитивные типы являются подтипами типа Object. In Java this is not true; any given primitive type and the corresponding wrapper type have no specific relationship with each other, except for autoboxing and unboxing, which act as syntactic sugar for interchanging between them. This was done intentionally, to maintain backward compatibility with prior versions of Java, in which no automatic casting was allowed, and the programmer worked with two separate sets of types: the primitive types, and the wrapper (reference) type hierarchy.

This difference has the following consequences. First of all, in C#, primitive types can define methods, such as an override of Object's ToString()method. In Java, this task is accomplished by the primitive wrapper classes.. Secondly, in Java an extra cast is needed whenever one tries to directly dereference a primitive value, as it will not be boxed automatically. The expression ((Integer)42).toString()will convert an integer literal to string in Java while 42.ToString()performs the same operation in C#. This is because the latter one is an instance call on the primitive value 42, while the former one is an instance call on an object of type java.lang.Integer.

Finally, another difference is that Java makes heavy use of boxed types in generics (see below).

Statements

StatementsJavaC#
Loops YesYes
Conditionals YesYes
Flow controlYesYes
Assignment YesYes
Exception controlYesYes
Variable declarationYesYes
Variable type выводДаДа
Детерминированное удаление (ARM-блоки)ДаДа

Синтаксис

Оба языки считаются языками "фигурных скобок" в семействе C / C ++. В целом синтаксис языков очень похож. Синтаксис на уровне операторов и выражений практически идентичен, очевидно, вдохновлен традициями C / C ++. На уровне определения типа (классы и интерфейсы ) существуют некоторые незначительные различия. В Java явно говорится о расширении классов и реализации интерфейсов, в то время как C # делает вывод об этом на основе типов типов, от которых наследуется новый класс / интерфейс.

C # поддерживает больше возможностей, чем Java, что в некоторой степени также очевидно в синтаксисе, который определяет больше ключевых слов и больше правил грамматики, чем Java.

Ключевые слова и обратная совместимость

По мере развития языков разработчики языков для обоих языков сталкивались с ситуациями, когда они хотели расширить языки с помощью новых ключевых слов или синтаксиса. В частности, новые ключевые слова могут нарушить существующий код на уровне исходного кода, т. Е. Старый код может больше не компилироваться, если он представлен компилятору для более поздней версии языка. Разработчики языков стараются избегать таких регрессов. Создатели двух языков пошли разными путями при решении этой проблемы.

Разработчики языка Java по возможности избегали новых ключевых слов, предпочитая вместо этого вводить новые синтаксические конструкции, которые ранее были недопустимы, или повторно использовать существующие ключевые слова в новых контекстах. Таким образом, они не поставили под угрозу обратную совместимость. Пример первого можно найти в том, как цикл forбыл расширен для приема итеративных типов. Пример последнего можно найти в том, как ключевые слова extendsи (особенно) superповторно использовались для определения границ типов, когда в Java 1.5 были введены универсальные шаблоны. Одно время (Java 1.4) было введено новое ключевое слово assert, которое ранее не было зарезервировано в качестве ключевого слова. Это могло сделать ранее действующий код недействительным, если, например, код использовал assertв качестве идентификатора. Разработчики решили решить эту проблему с помощью четырехэтапного решения: 1) Введение переключателя компилятора, который указывает, следует ли использовать Java 1.4 или новее, 2) Только отметка assertв качестве ключевого слова при компиляции как Java 1.4 и позже, 3) По умолчанию используется значение 1.3, чтобы избежать отображения предыдущего (не поддерживающего 1.4 кода) недействительным и 4) Выдавать предупреждения, если используется слово, используемое в режиме Java 1.3, чтобы разработчики могли изменить код.

Разработчики языка C # ввели несколько новых ключевых слов, начиная с первой версии. Однако чтобы определить эти ключевые слова как глобальные ключевые слова, они определяют их как контекстно-зависимые ключевые слова. Это означает, что даже когда они ввели (среди прочего) ключевые слова частичныйи yieldв C # 2.0, использование этих слов в качестве обозначателей по-допустимо, между ними не может быть конфликтом использовать как использовать слово и использовать как контекст. Таким образом, существующий синтаксис C # полностью обратно совместим с исходным кодом, написанным для любой предыдущей версии, без указания используемой языковой версии.

определение словафункция, пример использования
отмечен, снятВ C #, отмеченблоков операторов или выражения могут входящие проверки во время выполнение для арифметическое переполнение.
get, setC # реализует свойства как часть синтаксиса языка с их необязательными запросами getи установить методы доступав качестве альтернативных методов доступа , используемым в Java, которые являются не особенностями языка, шаблоном кодирования, основанным на соглашениях об именах методов.
gotoC # поддерживает слово goto . Иногда это может быть полезно, например, для реализации конечных автоматов или для сгенерированного кода, но обычно рекомендуется использование более структурированного метода потока управления (см. критику оператора goto ). Java не поддерживает оператор goto(но gotoявляется зарезервированным словом). Однако Java поддерживает помеченные операторы breakи continue, когда в особых случаях можно использовать оператор goto.
переключатель (цвет) {case Color.Blue: Console.WriteLine ("Цвет синий"); перемена; case Color.DarkBlue: Console.WriteLine («Цвет темный»); goto case Color.Blue; //...}
lockВ C # слово lockявляется сокращением для синхронизации доступа к блоку кода между потоками (с использованием Monitor), заключен в блок попробуйте... наконец.
out, refC # поддерживает выводы и ссылки . Они позволяют возвращать несколько выходных значений из метода или значения по ссылке.
strictfpJava использует strictfp , чтобы избежать, что операции с плавающей запятой остаются одинаковыми на разных платформах.
переключательВ C # оператор переключатель также работает со строками и постоянным числом. Прохождение разрешено для пустых и операторов возможно через 'goto case' для операторов, безопасный код. Оператор switch в Java работает со строками (начиная с Java 7 ), но не с примитивным типом long, и не работает для всех операторов (за исключением тех, которые имеют 'перерыв').
synchronizedВ сокращенном слове кода Java synchronizedпредставляет собой синхронизацию доступа к блоку между потоками (с использованием Monitor), заключенного в пальца... наконец блок.
выбрасываетJava требует, чтобы каждый метод объявлял проверенные исключения или суперклассы проверенных исключений, которые он может генерировать. Любой метод также может при желании объявить непроверенное исключение, которое он генерирует. В C # такого синтаксиса нет.
public int readItem () выбрасывает java.io.IOException {//...}
с использованиемВ C # с использованиемвызывает Disposeметод (реализованный через интерфейс IDisposable ) объекта, объявленного для выполнения после выполнения блока кода или при возникновении исключения в блоке кода.
// Создаем небольшой файл "test.txt", записываем строку, //... и закрываем ее (даже если возникает исключение) с помощью (StreamWriter file =Writer ("test.txt")) {file.Write ("тест"); }

В Java SE 7 была добавлена ​​аналогичная конструкция, называемая try-with-resources:

try (BufferedReader br = new BufferedReader (новый FileReader (путь))) {return br.readLine (); }

Объектно-ориентированное программирование

И C #, и Java разработаны с нуля как объектно-ориентированные языки с использованием динамической диспетчеризации с синтаксисом, C ++ (C ++, в свою очередь, наследует от C ). Однако ни один из языков не является надмножеством C или C ++.

Ориентация объектаJavaC #
Классы обязательныеобязательные
Интерфейсы ДаДа
Абстрактные классы ДаДа
Уровни доступности элементовДа; общедоступный, пакетный, защищенный, частныйДа; общедоступный, внутренний, защищенный, частный, защищенный внутренний
уровень класса внутренние классы Да; статическийвнутренние классы уровнем развитияДа; все внутренние классы уровня к уровню класса
Внутренние классы уровня экземплярыДаНет
Уровень инструкции (локальный) анонимные классы ДаДа; но без методов
Частичные классы Нет; Сторонняя библиотекаДа
Неявные (предполагаемые) анонимные классыNoДа
Устаревание / устареваниеДаДа
Управление версией с перегрузкойНекоторыеДа
Перечисления могут реализовывать интерфейсы ДаНет
Свойства Нет, но см. JavaBeans specДа
События Предоставляются стандартными библиотекамиВстроенная языковая функция
Перегрузка оператора NoДа
ИндексаторыNoДа
Неявные преобразованияНет; но см. автобокс Да
Явные преобразованияДаДа

Частичный класс

C # позволяет разделить определение класса на несколько исходных файлов с помощью функции, называемой частичными классами. Каждая часть должна быть помечена ключевым словом частичным. Все части должны быть компилятору как часть компиляции. Детали могут ссылаться на элементы из других деталей. Части могут реализовывать интерфейсы, а одна часть может определять базовый класс. Эта функция полезна в сценариях генерации кода (таких как дизайн пользовательского интерфейса (UI)), где генератор кода может предоставить одну часть, а разработчик - другую часть для совместной компиляции. Таким образом, разработчик может редактировать свою часть без риска того, что генератор кода перезапишет этот код позже. В отличие от механизма расширения классов, частичный класс допускает циклические зависимости между его частями, поскольку они гарантированно разрешаются во время компиляции. В Java нет концепции.

Внутренние и локальные классы

Оба языка допускают внутренние классы, в которых класс определен лексически внутри другого класса. Однако в каждом языке эти внутренние классы имеют довольно разную семантику.

В Java, если внутренний класс не объявлен статический, ссылка на экземпляр внутреннего класса несет с собой ссылку на внешний класс. В коде внутреннего класса имеет доступ как к статическим, так и нестатическим внешним классом. Чтобы создать экземпляр нестатического внутреннего класса, необходимо назвать экземпляр охватывающего внешнего класса. Это делается с помощью нового нового-оператора, представленного в JDK 1.3: outerClassInstance.new Outer.InnerClass (). Это можно сделать в любом классе, который имеет ссылку на экземпляр внешнего класса.

В C # внутренний класс концептуально такой же, как и нормальный класс. В некотором смысле внешний класс действует только как пространство имен. Таким образом, код внутреннего класса не может получить доступ к нестатическим данным внешнего класса, если он не делает это через явную ссылку на экземпляр внешнего класса. Программисты могут объявить внутренний класс закрытым, чтобы разрешить доступ к нему только внешнему классу.

Java использует одну возможность, называемые локальными классами или анонимными классами, которые могут быть использованы в телескопах. Обычно они используются для реализации интерфейса только с одним или двумя методами, которые обычно выполняются обработчиками событий. Однако их также можно использовать для переопределения виртуальных методов суперкласса. Методы в этих локальных классах доступ к локальным переменным внешним методом, объявленным final. C # удовлетворяет их сценария использования, предоставляя анонимные делегаты ; см. обработка событий для стабильной информации.

C # также предоставляет функцию, называемую анонимными типами / классами, но она сильно отличается от концепции Java с тем же именем. Это позволяет программисту создать экземпляр класса, предоставив только набор имен для каждого класса, и выражение для инициализации каждого из них. Типы свойств выводятся из типов этих выражений. Эти неявно объявленные классы являются производными непосредственно от объекта.

Событие

Многоадресные делегаты C # используются с событиями. События поддерживают поддержку программирования, событиями, и реализуются эти события наблюдателя . Для поддержки этого существует специальный синтаксис для определения событий в классах и операторы для регистрации, отмены регистрации или комбинирования обработчиков событий.

См. здесь для получения информации о том, как события реализованы в Java.

Перегрузка оператора и преобразования

Перегрузка оператора и преобразования, выполняемое пользователем приведение - это отдельные функции, которые обеспечивают обе системы первоклассными гражданами в системе новых типов. Используя эти функции в C #, были интегрированы такие типы, как Complex и decimal, так что обычные операторы, такие как сложение и умножение, работают с новыми типами. В отличие от C ++, C # ограничивает использование перегрузки операторов, запрещает ее для операторов новый, (), ||, , =и любые варианты составных операторов, таких как + =. Но составные операторы вызывают перегруженные простые операторы, такие как - =, вызывающий -и =.

. Java не включает перегрузку операторов или настраиваемое преобразование.

Индексатор

C # также включает индексаторы, которые можно рассматривать как особый случай перегрузки оператора (например, C ++ operator) или параметризованный получить/ установить свойства. Индексатор - это свойство с именем this, которое использует один или несколько параметров (индексов); индексы могут быть объектами любого типа:

myList [4] = 5; строка name = xmlNode.Attributes ["имя"]; orders = customerMap [theCustomer];

Java не включает индексаторы. Обычный шаблон Java включает в себя написание явных методов получения и установки, в которых программист на C # может использовать индексатор.

Поля и инициализация

Поля и инициализацияJavaC #
Поля ДаДа
Константы ДаДа; но нет поддержки для передаваемых параметров константы
Статические (класс) конструкторы ДаДа
Конструкторы экземпляровДаДа
Финализаторы / деструкторы ДаДа
Инициализаторы экземпляровДаНет; можно моделировать с помощью конструктора экземпляра
Объект инициализация Снизу вверх. (поля и конструкторы)Сверху вниз (поля); снизу вверх (конструкторы)
Инициализаторы объектовДаДа
Инициализаторы коллекцийНет; статические методы varargsДа
Инициализаторы массиваДаДа

Инициализация объекта

Как в C #, так и в Java, поля объекта могут инициализироваться либо инициализаторами переменных (которые могут быть присвоены переменным там, где они указаны), либо конструкторами (специальными подпрограммами, которые выполняются при создании объекта). Кроме того, Java содержит экземпляры инициализаторов, которые предоставляют собой анонимные блоки кода без аргументов, которые запускаются после явного (или неявного) вызова конструктора суперкласса, но до выполнения конструктора.

C # инициализирует поля объекта в следующем порядке при создании объекта:

  1. Производные статические поля
  2. Производный статический конструктор
  3. Производные поля экземпляра
  4. Базовый статические поля
  5. Базовый статический конструктор
  6. Поля базового экземпляра
  7. Конструктор базового экземпляра
  8. Конструктор производного экземпляра

Некоторые из вышеперечисленных полей могут быть неприменимы (например, если объект не имеет статических полей). Стандартные поля - это те, которые используются в прямом классе объекта, а базовое поле - это термин для полей, определенные в одном из суперклассов объекта. Обратите внимание, что представление в классе суперклассов включает в себя все характеристики в классе.

Гарантируется, что любые инициализаторы поля вступают в силу до вызова любых конструкторов, поскольку конструктор экземпляра класса объекта и его суперклассы вызываются после запуска инициализаторов поля. Однако возможная ловушка при инициализации объекта, когда виртуальный метод вызывается из базового конструктора. Переопределенный метод в подклассе может ссылаться на поле, которое определено в подклассе, но это поле может быть не инициализировано, потому что конструктор подкласса, который содержит инициализацию поля, вызывается после конструктора его базового класса.

В Java порядок инициализации следующий:

  1. Вызов другого конструктора (либо класса объекта, либо суперкласса объекта)
  2. Инициализаторы чисел экземпляра и инициализаторы экземпляра (в порядок их появления в исходном коде)
  3. Тело конструктора

Как и в C #, новый объект создается путем вызова конкретного конструктора. Внутри инструкции первая инструкция может быть вызовом конструктора другого конструктора. Если это опущено, вызов конструктора суперкласса без аргументов добавляется неявно компилятором. В противном случае может быть вызван другой способ вызвать конструктор суперкласса. В первом случае вызываемый конструктор снова вызовет другой конструктор (либо из класса объекта, либо из его подкласса), и цепочка рано или поздно вызовом одного из конструкторов суперкласса.

После вызова другого конструктора (который вызывает прямой вызов конструктора суперкласса и т.д., до класса Object) инициализируются переменные экземпляры, воспроизведение в классе объекта. Даже если для некоторых явно не переменные инициализаторы переменные, переменные инициализируются значениями по умолчанию. Обратите внимание, что переменные экземпляры, исполненные в суперклассах, уже инициализированы к этому моменту, потому что они были инициализированы конструктором суперкласса при его вызове (либо кодом конструктора, либо инициализаторами числа, выполненными до кода конструктора, либо неявно значениями по умолчанию). В Java инициализаторы числа выполняются в соответствии с их текстовым порядком в исходном файле.

Наконец, выполнено тело конструктора. Это обеспечивает правильный порядок инициализации, то есть поля базового класса завершают инициализацию до того, как начинается инициализация полей объектного класса.

Есть две возможные потенциальные ловушки при инициализации объекта Java. Во-первых, инициализаторы числа - это выражения, которые могут содержать вызовы методов. Поскольку методы могут ссылаться на любую переменную, определенную в классе, метод, вызываемый в инициализаторе переменной, может ссылаться на переменную, которая определена ниже инициализируемой переменной. Поскольку порядок инициализации соответствует текстовому порядку определений переменных, такая переменная не будет инициализирована значением, предписанным ее инициализатором, и будет содержать значение по умолчанию. Другая потенциальная ловушка - это когда метод, переопределенный в производном классе, вызывается в конструкторе базового класса, что может привести к поведению, которого программист не ожидал бы при создании объекта производного класса. В соответствии с порядком инициализации тело конструктора базового класса выполняется перед оценкой инициализаторов переменных и перед выполнением тела конструктора производного класса. Однако переопределенный метод, вызываемый из конструктора базового класса, может ссылаться на переменные, определенные в производном классе, но они еще не инициализированы значениями, указанными их инициализаторами или установленными в конструкторе производного класса. Последняя проблема также применима к C #, но в менее критичной форме, поскольку в C # методы не могут быть переопределены по умолчанию.

Удаление ресурсов

Оба языка в основном используют сборку мусора как средство освобождения ресурсов памяти, а не явное освобождение памяти. В обоих случаях, если объект содержит ресурсы разных типов, помимо памяти, такие как дескрипторы файлов, графические ресурсы и т. Д., То он должен быть явно уведомлен, когда приложение больше не использует его. Как C #, так и Java предлагают интерфейсы для такого детерминированного удаления, и как C #, так и Java (начиная с Java 7) имеют операторы автоматического управления ресурсами, которые автоматически вызывают методы удаления / закрытия на этих интерфейсах.

Методы

Методы и свойстваJavaC #
Статический импортДаДа
Виртуальный методыВиртуальный по умолчаниюНе виртуальный по умолчанию
Абстрактный ДаДа
УплотнениеДаДа
Явная реализация интерфейсаМетоды по умолчаниюДа
Параметры значения (входные)ДаДа
Справочные (входные / выходные) параметрыNoДа
Выходные (выходные) параметрыNoДа
Постоянные (неизменяемые) параметрыДа; конечныепараметрыДа
Вариативные методы ДаДа
Необязательные аргументыНет; Вместо перегрузки метода или varargsДа
Именованные аргументыNoДа
Генераторные методы NoДа
Расширение / методы по умолчаниюДаДа
Условные методыNoДа
Частичные методыNoДа

Методы расширения и методы по умолчанию

Использование специального указателя this в первом параметре метода, C # разрешает метод действовать так, как если бы это был метод-член типа первого параметра. Это расширение внешнего класса чисто синтаксическое. Метод расширения должен быть объявлен staticи определен внутри чисто статического класса. Этот метод должен подчиняться любому ограничению доступа к члену, как и любой другой метод, внешний по отношению к классу; таким образом, статические методы не могут нарушить инкапсуляцию объекта. «Расширение» активно только в областях, в которые было импортировано пространство имен статического класса хоста.

Начиная с Java 8, в Java есть похожая функция, называемая методами по умолчанию, которые представляют собой методы с телом, объявленным в интерфейсах. В отличие от методов расширения C #, методы Java по умолчанию - это методы экземпляра интерфейса, которые их объявляют. Определение методов по умолчанию в классах, реализующих интерфейс, является необязательным: если класс не определяет метод, вместо него используется определение по умолчанию.

И методы расширения C #, и методы по умолчанию Java позволяют классу переопределять реализацию по умолчанию метода расширения / метода по умолчанию, соответственно. В обоих языках это переопределение достигается путем определения метода в классе, который должен использовать альтернативную реализацию метода.

Правила области действия C # определяют, что если соответствующий метод найден в классе, он имеет приоритет над подходящим методом расширения. В Java предполагается, что любой класс, объявленный для реализации интерфейса с методом по умолчанию, имеет реализации методов по умолчанию, если только класс не реализует сам метод.

Частичные методы

Относящиеся к частичным классам C # позволяет указывать частичные методы внутри частичных классов. Частичный метод - это намеренное объявление метода с несколькими ограничениями на подпись. Ограничения гарантируют, что если определение не предоставляется какой-либо частью класса, то метод и каждый его вызов можно безопасно удалить. Эта функция позволяет коду предоставлять большое количество точек перехвата (например, шаблонный метод GoF ) без дополнительных затрат времени выполнения, если эти точки расширения не используются другой частью класса. во время компиляции. В Java нет соответствующей концепции.

Виртуальные методы

Методы в C # по умолчанию не являются виртуальными и при желании должны быть объявлены виртуальными явно. В Java все нестатические неприватные методы являются виртуальными. Виртуальность гарантирует, что самое последнее переопределение для метода всегда будет вызываться, но при вызове возникают определенные затраты времени выполнения, поскольку эти вызовы не могут быть обычно встроенными и требуют косвенного вызова через таблица виртуальных методов. Однако некоторые реализации JVM, включая эталонную реализацию Oracle, реализуют встраивание наиболее часто называемых виртуальных методов.

Java-методы по умолчанию являются виртуальными (хотя их можно запечатать с помощью модификатора final, чтобы запретить переопределение). Невозможно позволить производным классам определять новый несвязанный метод с тем же именем.

Это означает, что по умолчанию в Java и только при явном включении в C # новые методы могут быть определены в производном классе с тем же именем и подписью, что и в его базовом классе. Когда метод вызывается для ссылки на суперкласс такого объекта, "самая глубокая" переопределенная реализация метода базового класса 'будет вызываться в соответствии с конкретным подклассом объекта, на который ссылаются.

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

Чтобы смягчить это, C # требует, чтобы, если метод предназначен для переопределения унаследованного метода, необходимо указать ключевое слово override. В противном случае метод «скроет» унаследованный метод. Если ключевое слово отсутствует, компилятор выдает предупреждение об этом, которое можно отключить, указав ключевое слово new. Это позволяет избежать проблемы, которая может возникнуть из-за расширения базового класса с помощью не частного метода (т. Е. Унаследованной части пространства имен), сигнатура которого уже используется производным классом. В Java есть аналогичная проверка компилятора в виде аннотации метода @Override, но она не является обязательной, и в ее отсутствие большинство компиляторов не предоставляют комментарии (но метод будет переопределен).

Постоянные / неизменяемые параметры

В Java можно предотвратить переназначение локальной переменной или параметра метода с помощью ключевого слова final. Применение этого ключевого слова к переменной примитивного типа приводит к тому, что переменная становится неизменной. Однако применение finalк переменной ссылочного типа только предотвращает присвоение ей другого объекта. Это не предотвратит изменение данных, содержащихся в объекте. Начиная с C # 7, можно предотвратить переназначение параметра метода с помощью ключевого слова в, однако это ключевое слово нельзя использовать для локальных переменных. Как и в случае с Java, применение вк параметру только предотвращает переназначение параметра другому значению. По-прежнему можно изменять данные, содержащиеся в объекте.

JavaC #
public int addOne (final int x) {x ++; // ОШИБКА: конечную переменную нельзя переназначить return x; } общедоступный список addOne (окончательный список список) {list.add (1); // ОК: все еще можно изменить // конечную переменную (ссылочного типа) return list; }
public int AddOne (в int x) {x ++; // ОШИБКА: параметр только для чтения нельзя переназначить return x; } общедоступный список AddOne (в списке List ) {list.Add (1); // ОК: все еще можно изменить список // только для чтения (ссылочного типа); }

Оба языка не поддерживают важную функцию const-correness, которая существует в C /C ++, которая делает метод постоянным.

Java определяет слово «константа» произвольно как поле static final. Только эти переменные являются переменными, состоящими только из заглавных букв, имена которых разделяются символом подчеркивания . Параметр, имеющий только значение final, не считается константой, хотя это может быть так в случае примитивного типа данных или неизменяемого класса, например a String.

Методы генератора

Любой метод C #, объявленный как возвращающий IEnumerable, IEnumeratorили общие версии этих интерфейсов, могут быть реализованы с использованием yieldсинтаксис. Это форма ограниченных продолжений, генерируемых компилятором, и она может значительно сократить код, необходимый для обхода или генерации последовательностей, хотя вместо этого этот код просто генерируется компилятором. Эта функция также может использоваться для реализации бесконечных последовательностей, например, последовательность чисел Фибоначчи.

В Java нет эквивалентной функции. Вместо этого генераторы обычно определяются путем предоставления специализированной реализации хорошо известной коллекции или итеративного интерфейса, который будет вычислять каждый элемент по запросу. Чтобы такой генератор использовался в выражении для каждого оператора, он должен реализовывать интерфейс java.lang.Iterable.

См. Также пример последовательности Фибоначчи ниже.

Явная реализация интерфейса

C # также имеет явную реализацию интерфейса, которая позволяет классу специально реализовывать методы интерфейса , отдельно от своих собственных методов класса, или предоставлять разные реализации двух методов с одинаковым именем и сигнатурой, унаследованных от двух базовых интерфейсов.

На любом языке, если метод (или свойство в C #) определен с тем же именем и подписью в нескольких интерфейсах, члены будут конфликтовать, когда будет разработан класс, реализующий эти интерфейсы. Реализация по умолчанию реализует общий метод для всех интерфейсов. Если требуются отдельные реализации (потому что методы служат разным целям или потому что возвращаемые значения различаются между интерфейсами) явная реализация интерфейса C # решит проблему, хотя и допускает разные результаты для одного и того же метода в зависимости от текущего приведения объекта. В Java нет другого способа решить эту проблему, кроме рефакторинга одного или нескольких интерфейсов, чтобы избежать конфликтов имен.

Справочные (входящие / исходящие) параметры

Аргументы примитивных типов (например, int, double) в метод передаются по значению в Java, тогда как объекты передаются по ссылке. Это означает, что метод работает с копиями переданных ему примитивов, а не с фактическими переменными. Напротив, реальные объекты в некоторых случаях могут быть изменены. В следующем примере объект String не изменяется. Объект класса «а» изменен.

В C # можно указать ссылку с помощью ключевого слова ref, аналогично C ++ и в некотором смысле C. Эта функция C # особенно полезна, когда кто-то хочет создать метод, который возвращает более одного объекта. В Java попытка вернуть несколько значений из метода не поддерживается, если не используется оболочка, в данном случае с именем «Ref».

JavaC #
class PassByRefTest {static class Ref {T val; Ref (T val) {this.val = val; }} static void changeMe (Ref s) {s.val = "Changed"; } static void swap (Ref x, Ref y) {int temp = x.val; x.val = y.val; y.val = temp; } public static void main (String args) {var a = new Ref (5); var b = новая ссылка (10); var s = new Ref («без изменений»); своп (а, б); changeMe (s); System.out.println ("a =" + a.val + "," + "b =" + b.val + "," + "s =" + s.val); }}
class PassByRefTest {public static void ChangeMe (out string s) {s = "Changed"; } public static void Swap (ref int x, ref int y) {int temp = x; х = у; y = темп; } public static void Main (строковые аргументы) {int a = 5; int b = 10; строка s = "без изменений"; Своп (ссылка a, ссылка b); ChangeMe (out s); System.Console.WriteLine ("a =" + a + "," + "b =" + b + "," + "s =" + s); }}
a = 10, b = 5, s = Changeda = 10, b = 5, s = Changed

Исключения

ExceptionsJavaC #
Проверенные исключенияДаНет
Попробовать- окончательноДаДа

Проверенные исключения

Java поддерживает отмеченные исключения (вместе с непроверенными исключениями). C # поддерживает только непроверенные исключения. Проверенные исключения вынуждают программиста либо объявить исключение, созданное в методе, либо перехватить выброшенное исключение с помощью предложения try-catch.

Проверенные исключения могут способствовать хорошему программированию, гарантируя устранение всех ошибок. Однако Андерс Хейлсберг, главный архитектор языка C #, утверждает, что они были до некоторой степени экспериментом с Java и что они не показали никакой ценности, кроме как в небольших примерах программ.

Одно замечание. в том, что проверенные исключения побуждают программистов использовать пустой блок catch (catch (Exception e) {}), который молча поглощает исключения, вместо того, чтобы позволить исключениям распространяться на процедуру обработки исключений более высокого уровня. Однако в некоторых случаях вместо этого может применяться цепочка исключений , повторно генерируя исключение в исключении оболочки. Например, если объект изменен для доступа к базе данных, а не к файлу, SQLException может быть перехвачено и повторно сгенерировано как IOException , так как вызывающему может не потребоваться знать внутреннюю работу объекта.

Однако не все программисты согласны с такой позицией. Джеймс Гослинг и другие утверждают, что проверенные исключения полезны, и неправильное их использование вызвало проблемы. Бесшумный перехват исключений возможен, да, но необходимо явно указать, что делать с исключением, в отличие от непроверенных исключений, которые по умолчанию не позволяют ничего делать. Его можно игнорировать, но код должен быть написан явно, чтобы игнорировать его.

Try-catch-finally

Между двумя языками также есть различия в обработке try-finallyзаявление. Блок finallyвыполняется всегда, даже если блок tryсодержит операторы передачи управления, такие как throwили return. В Java это может привести к этому неожиданному поведению, если в блоке попыткаостается оператор returnс некоторыми отрезками, а затем выполняется блок наконецпосле также остается оператор returnс соседними. C # решает эту проблему, запрещая любые операторы передачи управления, такие как returnили breakв блоке finally.

Обычной причиной использования блоков try-finallyявляется защита кода управления ресурсами, тем самым гарантируя высвобождение ценных ресурсов в блоке finally. В C # используется оператор usingкак синтаксическое сокращение для этого распространенного сценария, в котором всегда вызывается метод Dispose ()объекта using.

Довольно тонкое различие - момент создания трассировки стека при возникновении исключения. В Java трассировка стека создается в момент создания исключения.

class Foo {Exception up = new Exception (); int foo () выдает исключение {бросить; }}

Исключение в приведенном выше утверждении всегда будет содержать трассировку стека конструктора - независимо от того, как часто вызывается foo. С другой стороны, в C # трассировка стека создается в момент выполнения throw.

class Foo {Exception e = new Exception (); int foo () {попробуйте {бросить е; } catch (исключение e) {бросить; }}}

В приведенном выше коде исключение будет содержать трассировку стека первой строки выброса. При перехвате исключения есть два варианта на случай, если исключение должно быть создано повторно: throwпросто повторно вызовет исходное исключение с исходным стеком, а throw eсоздаст новый стек. след.

Блоки finally

Java позволяет потоку управления покинуть блок finallyоператора try, независимо от того, как он был введен. Это может привести к прекращению выполнения другого оператора потока управления (например, return) в середине выполнения. Например:

int foo () {try {return 0; } наконец {возврат 1; }}

В приведенном выше коде оператор returnв блоке tryзаставляет управление покинуть его, и, таким образом, блок finallyвыполняется до фактического возврат происходит. Однако сам блок finallyтакже выполняет возврат. Таким образом, исходный возврат, который вызвал его ввод, не выполняется, и вышеупомянутый метод возвращает 1, а не 0. Неформально говоря, он пытается вернуть 0, но в итоге возвращает 1.

C # не допускает никаких операторов которые позволяют потоку управления преждевременно покинуть блок finally, за исключением throw. В частности, returnне разрешен вообще, gotoне разрешен, если целевая метка находится за пределами блока finally, и continueи breakне допускаются, если ближайший охватывающий цикл находится за пределами блока finally.

Generics

В области generics два языка демонстрируют внешнее синтаксическое сходство, но у них есть глубокие различия.

GenericsJavaC #
РеализацияСтирание типа Reification
Реализация времени выполненияNoДа
Вариант типа Использование- сайт Объявление-сайт (только для интерфейсов)
Ограничение ссылочного типаДа; неявноеДа
Ограничение значения / примитивного типаNoДа
Ограничение конструктораNoДа (только для конструктора без параметров)
Ограничение подтипа ДаДа
Ограничение супертипа ДаНет
Совместимость с миграциейДаНет

Стирание типов по сравнению с повторными универсальными шаблонами

Универсальные шаблоны в Java - это конструкция только для языка; они реализованы только в компиляторе. Сгенерированные файлы классов включают общие подписи только в форме метаданных (позволяя компилятору компилировать для них новые классы). Среда выполнения ничего не знает о системе универсальных типов; дженерики не являются частью JVM. Вместо этого универсальные классы и методы преобразуются во время компиляции с помощью процесса, называемого стиранием типа . Во время этого компилятор заменяет все универсальные типы их необработанной версией и соответствующим образом вставляет приведение / проверку в клиентский код, в котором используются тип и его методы. Результирующий байт-код не будет содержать ссылок на какие-либо общие типы или параметры (см. Также Generics в Java ).

Спецификация языка Java намеренно запрещает определенное использование универсальных шаблонов; это необходимо для реализации добавления универсальных шаблонов посредством стирания типа и обеспечения совместимости миграции. Исследования по добавленным обобщенным универсальным шаблонам на платформе Java продолжаются, поскольку часть Project Valhalla.

C # опирается на поддержку универсальных шаблонов из предлагаемой системы, то есть это не просто языковая функция. Этот язык является просто интерфейсом для поддержки кросс-языковых универсальных типов в CLR. Во время компиляции универсальных шаблонов проверяется правильность реализации кода универсальных шаблонов. Клиентский код (код, вызывающий универсальные методы / свойства) может безопасно предполагать, что универсальные типы являются безопасными по типу. Это называется овеществлением. Во время выполнения, когда уникальный набор параметров типа для универсального класса / метода / класса используется, загрузчик / проверяющий класс синтезирует конкретный дескриптор класса и сгенерирует реализацию метода впервые. Во время создания реализаций методов все ссылочные типы используются одним типом, как используемые ссылочные типы, используемые для использования одни и те же реализации. Это просто для реализации. Различные наборы типов по-прежнему существуют уникальные дескрипторы типов; их методы будут просто указывать на один и тот же код.

В следующем списке показаны некоторые различия между Java и C # при управлении универсальными шаблонами. Он не является исчерпывающим:

JavaC #
Проверка типов и вводящих преобразование в клиентский код (код, указанный на универсальные шаблоны). По сравнению с неуниверсальным кодом с ручным приведением эти операции будут такими же, но по которому с кодом, проверенным во время компиляции, не требует приведений и проверок во время выполнения, эти операции представляют собой накладные расходы на производительность.Дженерики C # /. NET гарантирует, что проверяются во время выполнения, поэтому дополнительные проверки / приведения не требуются во время выполнения. Следовательно, общий код будет работать быстрее, чем неуниверсальный (или код со стертым типом), который требует при ведении типов неуниверсальных или стертых по типу объектов.
Невозможно использовать примитивные типы в качестве параметров типа; вместо этого разработчик должен использовать тип оболочки, соответствующий типу примитива. Это влечет за собой дополнительные накладные расходы производительности, требуя преобразования упаковки и распаковки, а также давления памяти и сбора мусора, как оболочки будут в куче, а не в стеке.Примитивные типы и типы разрешены в качестве параметров типа в универсальных реализациях. Во время выполнения код будет синтезирован и скомпилирован для каждой уникальной комбинации параметров типа при первом использовании. Обобщения, которые реализованы с типом примитив / значение, не требуют преобразований упаковки / распаковки.
Общие исключения не разрешены, параметр типа не может быть объявлен в предложении catchМожет как определять общее исключение, так и использовать их в предложениях catch
Статические члены являются общими для всех общих реализаций типа erasure все реализации объединены в один класс)Статические члены являются отдельными для каждой общей. Общая реализация - это уникальный класс.
Параметры типа нельзя использовать в объявлениях статических полей / методов или в определениях статических внутренних классовНет ограничений на использование параметров типа
Невозможно создать массив, в котором тип компонента является универсальной реализацией (конкретный параметризованный тип)
Пара tenPairs = новая пара [10]; // ОК
Общая реализация - это гражданин 1-го класса и может инет как любой другой класс; также компонент архитектуры
объект tenPairs = новая пара [10]; // OK
Невозможно создать массив, в котором тип компонента является параметром типа, но допустимо создать массив Объекти выполнить приведение к новому массиву для достижения того же эффекта.
поиск открытого класса {общедоступный V getEmptyValues ​​(ключ K) {return (V) новый объект [0]; // OK}}

Когда параметр универсального типа находится под ограничениями наследования, тип ограничения может быть вместо Object

public class Lookup >{public V getEmptyValues ​​(K key) {return (V) новый Comparable [0]; }}
Параметры типа Наблюдать за факультативными классными классами и общением как любой другой тип общего типа.
открытый класс Lookup {общедоступный V GetEmptyValues ​​(ключ K) {вернуть новый V [0]; // OK}}
Нет литерала класса для реализации универсального типаУниверсальная реализация - это реальный класс.
примерне соответствуют требованиям типа или конкретными универсальными реализациямиОператоры равнои какдля параметров типа так же, как и для любых других тип.
Невозможно создать новые экземпляры, используя параметр типа в качестве типа типаС помощью конструктора универсальные методы или методы универсальных классов могут создать экземпляры классов, которые имеют конструкторы по умолчанию.
Информация о типе стирается во время компиляции. Для обнаружения исходного типа необходимо использовать специальные расширения к отражению.Информация о типах универсальных типов C # полностью сохраняется во время выполнения и обеспечивает полную поддержку отражения и создание универсальных типов.
Отражение нельзя использовать для построения новых общих реализаций. Во время компиляции дополнительный код (приведение типов) вставляется в клиентский код дженериков. Это препятствует созданию новых реализаций позже.Отражение можно использовать для создания новых реализаций для новых комбинаций параметров типа.

C # разрешает универсальные шаблоны непосредственно для примитивных типов. Вместо этого Java позволяет использовать упакованные типы в качестве параметров (например, Listвместо List). За это приходится платить, поскольку все такие значения должны быть упакованы / распакованы в памяти, и все они должны быть размещены в куче. Однако универсальный может быть специализирован с типом примитивного типа в Java, например, Listразрешен. Несколько сторонних библиотек реализовали базовые системы Java с примитивными массивами, чтобы сохранить время выполнения и оптимизацию памяти.

Совместимость с миграцией

Дизайн стирания типов в Java является мотивированным требованием для достижения совместимости миграции - не путать с обратной совместимостью. В частности, исходным требованием было «… должен быть чистым, очевидный путь перемещения для коллекций API, которые введены в платформу Java 2». Это было разработано таким образом, чтобы любые новые универсальные коллекции передавались методам, которые ожидали один из ранее существовавших классов коллекций.

Универсальные шаблоны C # были введены в язык, сохраняя при этом полную обратную совместимость, но не сохраняя полную миграцию совместимость: старый код (до C # 2.0) работает без перекомпиляции в новой среде выполнения с поддержкой универсальных типов. Что касается совместимости, были разработаны новые универсальные классы коллекций и интерфейсы, которые дополняют неуниверсальные коллекции.NET 1.x, а не заменяли. В дополнение к универсальным интерфейсам коллекции новые универсальные классы коллекции реализуют неуниверсальные интерфейсы коллекции, где это возможно. Это предотвращает использование новых общих коллекций с использованием уже используемых методов (не известными универсальными) методами.

Ковариация и контравариантность

Ковариация и контравариантность поддерживаются обоими языками. В Java есть вариант использования сайта, который позволяет одному универсальному классу объявлять члены с использованием как так и контравариантности. В C # есть вариант определения сайта для универсальных интерфейсов и делегатов. Непосредственно для классов вариативность не поддерживается, но поддерживается их реализация вариантных интерфейсов. В C # также есть поддержка ковариации на сайте для методов и делегатов.

Функциональное программирование

Функциональное программированиеJavaC #
Ссылки на методыДаДа
Замыкания Все лямбды не вводят новый уровень области видимости. Все переменные, на есть ссылки, должны быть окончательнымиДа
Лямбда-выражения ДаДа
Деревья выражений NoДа
Общий язык запросовНет; но см. «Эквивалент Java Stream» (Monad) Да
Оптимизация компилятора хвостовой рекурсииNoТолько в x64

Замыкания

Замыкание - это встроенная функция, которая захватывает переменные из его лексический объем.

C # поддерживает замыкания как анонимные методы или лямбда-выражения с полнофункциональной семантикой замыкания.

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

Когда ссылку на метод можно передать для последующего выполнения, возникает проблема, что делать, когда метод имеет ссылки на переменные / параметры в его лексической области видимости. Замыкания C # могут обращаться к любой переменной / параметру из своей лексической области видимости. В анонимных внутренних классах Java разрешены только ссылки на конечные члены лексической области видимости, что требует разработки, какие переменные сделать доступными и в каком состоянии (возможно, потребуется упаковка).

Лямбды и деревья выражений

В C # и Java есть специальные типы встроенных замыканий, называемый лямбда-выражениями. Это анонимные методы: у них есть подпись и тело, но нет имени. Они в основном используются для указания локальных аргументов с функциональным значением в вызовах других методов, в основном связаны с функциональным программированием.

C #, в отличие от Java, позволяет использовать лямбда-функции как способ определения специальных структур данных, называемых деревья выражений. Видны ли они как исполняемая функция или как структура данных, зависит от вывода типа компилятора и от того, какой типовой вариант или им присваивается или предоставляется. Лямбда-выражения и деревья выражений играют ключевую роль в Интегрированный запрос языка (LINQ).

Метаданные

Метаданные JavaC #
Аннотации / атрибуты метаданныхНа основе интерфейса; могут быть созданы пользовательские аннотациина основе классов
Позиционные аргументыНет; если только один аргументДа
Именованные аргументыДаДа
Значения по умолчаниюПри определенииЧерез
Вложенные инициализацииДаДа
СпециализацияNoДа
Условные метаданныеNoДа

Предварительная обработка, компиляция и упаковка

Предварительная обработка, Компиляция и Упаковка JavaC #
Пространства имен ПакетыПространства имен
Содержимое файлаОграниченныйБесплатно
УпаковкаПакетобщедоступная / внутренняя видимость членов пространства имен, которую система сборки преобразует в модули и сборки на уровень CLR
Путь поиска классов / сборкиClassPathКак во время компиляции, так и во время выполнения
Условная компиляцияНет; но см. Apache AntДа
Пользовательские ошибки / предупрежденияДа; AnnotationProcessorДа
Явные областиNoДа

Пространства имен и содержимое файла

В C # пространства имен аналогичны пространствам в C + +. В отличие от имен пакетов в Java, пространство имен никоим образом не привязано к местоположению исходного файла. Хотя не обязательно, чтобы расположение исходного файла Java отражало его код каталогового пакета, это обычная организация.

Оба языка позволяют импортировать классы (например, import java.util. *в Java), позволяя ссылаться на класс, используя только его имя. Иногда классы с одинаковыми именами существуют в нескольких пространствах имен или пакетах. На такие классы можно ссылаться, используя полностью определенные классы или импортируя выбранные классы с другими именами. Для этого Java позволяет импортировать один класс (например, import java.util.List). C # позволяет импортировать классы под новым локальным именем, используя следующий синтаксис: using Console = System.Console. Он также позволяет импортировать специализацию классов в форме с использованием IntList = System.Collections.Generic.List .

Оба языка имеют статический импорт синтаксис, который позволяет использовать краткое имя некоторых или всех статических методов / полей в классе (например, разрешая foo (bar), где foo ()может быть статически импортирован из другого класса). C # имеет синтаксис статического класса (не путать со статическими внутренними классами в Java), который ограничивает класс только статическими методами. В C # 3.0 представлены методы расширения , позволяющие пользователям статически добавлять метод к типу (например, разрешая foo.bar (), где bar ()может быть импортированный метод расширения, работающий с типом foo).

Компилятор Java Sun Microsystems требует, чтобы имя исходного файла соответствовало единственному общедоступному классу внутри, в то время как C # допускает использование нескольких общедоступных классов в одном файле и не накладывает никаких ограничений на файл название. C # 2.0 и более поздние версии позволяют разделить определение класса на несколько файлов с помощью ключевого слова частичноев исходном коде. В Java открытый класс всегда находится в собственном исходном файле. В C # файлы исходного кода и разделение логических данных.

Условная компиляция

В отличие от Java, C # реализует условную компиляцию с использованием директив препроцессора. Он также предоставляет атрибут Conditional для определения методов, которые вызываются только тогда, когда определена данная константа компиляции. Таким образом, утверждения могут быть предоставлены как функция инфраструктуры с помощью метода Debug.Assert (), который оценивается только при определении константы DEBUG. Начиная с версии 1.4, Java предоставляет возможность языка для утверждений, которые по умолчанию отключены во время выполнения, но могут быть включены с помощью переключателя -enableassertionsили -eaпри вызове JVM.

Потоковые и асинхронные функции

Оба языка включают механизмы thread синхронизации как часть синтаксиса языка.

Потоки и Синхронизация JavaC #
ПотокиДаДа
Пул потоков ДаДа
Параллелизм на основе задачДаДа
Семафоры ДаДа
Мониторы ДаДа
Локальные переменные потокаДаДа; ThreadStaticAttribute и ThreadLocal класс

Параллелизм на основе задач для C #

В.NET Framework 4.0 была представлена ​​новая модель программирования на основе задач, которая заменила существующую асинхронную модель на основе событий. API основан на классах Taskи Task. Задачи можно составлять и объединять в цепочки.

По соглашению, каждый метод, возвращающий задачу Task, должен иметь в своем имени постфикс с Async.

общедоступный статический класс SomeAsyncCode {общедоступная статическая задача GetContentAsync () {HttpClient httpClient = new HttpClient (); вернуть httpClient.GetStringAsync ("www.contoso.com"). ContinueWith ((задача) =>{string responseBodyAsText = task.Result; return XDocument.Parse (responseBodyAsText);}); }} var t = SomeAsyncCode.GetContentAsync (). ContinueWith ((задача) =>{var xmlDocument = task.Result;}); t.Start ();

В C # 5 был представлен набор расширений языка и компилятора, чтобы упростить работу с моделью задачи. Эти языковые расширения включали понятие asyncметодов и оператор await, которые заставляют поток программы выглядеть синхронным.

общедоступный статический класс SomeAsyncCode {общедоступная статическая асинхронная задача GetContentAsync () {HttpClient httpClient = new HttpClient (); строка responseBodyAsText = ожидание httpClient.GetStringAsync («www.contoso.com»); вернуть XDocument.Parse (responseBodyAsText); }} var xmlDocument = await SomeAsyncCode.GetContentAsync (); // Задача будет запускаться по вызову с ожиданием.

Из этого синтаксического сахара компилятор C # генерирует конечный автомат, который обрабатывает необходимые продолжения без необходимости думать об этом разработчикам.

Параллелизм на основе задач для Java

Java поддерживает потоки, начиная с JDK 1.0. Java предлагает высокую гибкость для выполнения потоков, часто называемых задачами. Это достигается путем реализации функционального интерфейса (интерфейса java.lang.Runnable), определяющего единственный метод void no-args, как показано в следующем примере:

var myThread = new Thread (() ->{var threadName = Thread.currentThread (). getName (); System.out.println ("Hello" + threadName);}); myThread.start ();

Подобно C #, Java имеет механизм более высокого уровня для работы с потоками. Исполнителимогут выполнять асинхронные задачи, а также управлять группой подпроцессов. Все потоки экземпляра ExecutorServicesобрабатываются в пуле . Этот экземпляр ExecutorServiceбудет повторно использоваться «под капотом» для реванантных задач, поэтому возможно выполнение столько одновременных задач, сколько хочет программист, на протяжении всего жизненного цикла приложения с использованием одного экземпляра службы-исполнителя.

Вот как выглядит первый пример потока с использованием исполнителей:

ExecutorService executor = Executors.newSingleThreadExecutor (); executeor.submit (() ->{var threadName = Thread.currentThread (). getName (); System.out.println ("Привет" + threadName);});

Экземпляр ExecutorServiceтакже поддерживает интерфейс Callable, другой интерфейс с одним методом, такой как Runnable, но подпись содержащегося метода Callableвозвращает значение. Таким образом, лямбда-выражение также должно возвращать значение.

общедоступный статический класс SomeAsyncCode {ExecutorService executor = Executors.newSingleThreadExecutor (); public static Future getContentAsync () {return executor.submit (() ->{HttpRequest httpReq = HttpRequest.newBuilder ().uri (новый URI ("https://www.graalvm.org")).build (); return HttpClient.newHttpClient ().send (httpReq, BodyHandlers.ofString ()).body ();}); }} var webPageResult = SomeAsyncCode.getContentAsync (). get ();

Вызов метода get()блокирует текущий поток и ожидает завершения вызываемого объекта перед возвратом значения (в данном примере - содержимого веб-страницы):

Дополнительные функции

Числовые приложения

Для адекватной поддержки приложений в области математических и финансовых вычислений существует несколько языковых функций.

Java strictfp ключевое слово позволяет выполнять строгие вычисления с плавающей запятой для области кода. Строгие вычисления с плавающей запятой требуют, чтобы даже если платформа обеспечивает более высокую точность вычислений, промежуточные результаты должны быть преобразованы в одинарные / двойные. Это гарантирует, что строгие вычисления с плавающей запятой возвращают точно такой же результат на всех платформах. Без строгой плавающей точки реализация платформы может использовать более высокую точность для промежуточных результатов во время вычислений. C # позволяет реализации для данной аппаратной архитектуры всегда использовать более высокую точность для промежуточных результатов, если они доступны, то есть C # не позволяет программисту дополнительно принудительно использовать промежуточные результаты для использования потенциально более низкой точности single / double.

Хотя арифметика с плавающей запятой в Java в значительной степени основана на IEEE 754 (Стандарт для двоичной арифметики с плавающей запятой), некоторые функции не поддерживаются даже при использовании модификатора strictfp, например, флаги исключения и направленные округления, возможности, предусмотренные стандартом IEEE Standard 754 (см. Критика Java, арифметика с плавающей запятой ).

C # предоставляет встроенный десятичный тип, который имеет более высокую точность (но меньший диапазон), чем двойной Java / C #. Тип decimal - это 128-битный тип данных, подходящий для финансовых и денежных расчетов. Десятичный тип может представлять значения в диапазоне от 1,0 × 10 до приблизительно 7,9 × 10 с 28–29 значащими цифрами. В структуре используется перегрузка операторов C #, чтобы можно было управлять десятичными числами с помощью таких операторов, как +, -, * и /, как и других примитивных типов данных.

Типы BigDecimal и BigInteger , поставляемые с Java, позволяют представлять десятичные и целые числа с произвольной точностью, соответственно. Стандартная библиотека Java не имеет классов для работы с комплексными числами.

Типы BigIntegerи Complex, предоставляемые с C #, позволяют представлять и управлять целыми числами произвольной точности и комплексными числами соответственно. В структурах используется перегрузка операторов C #, так что экземплярами можно управлять с помощью таких операторов, как +, -, *и /, как и других примитивных типов данных. В стандартной библиотеке C # нет классов для работы с числами с плавающей запятой произвольной точности (см. программное обеспечение для арифметики произвольной точности ).

C # может помочь математическим приложениям с операторами с установленными без отметки, которые позволяют включать или отключать проверку времени выполнения для арифметического переполнения для область кода.

Интегрированный языковой запрос (LINQ)

C # s Интегрированный языковой запрос (LINQ) - это набор функций, предназначенных для совместной работы, позволяющих выполнять запросы на языке и это отличительная черта C # и Java.

LINQ состоит из следующих функций:

  • Методы расширения позволяют расширять существующие интерфейсы или классы новыми методами. Реализации могут быть общими или интерфейс может иметь специальную реализацию.
  • Лямбда-выражения позволяют функционально выражать критерии.
  • Деревья выражений позволяют конкретной реализации захватывать лямбда-выражение как абстрактное синтаксическое дерево, а не исполняемый блок. Это может быть использовано реализациями для представления критериев на другом языке, например в форме SQL, где предложение, как, например, в случае с Linq, LINQ to SQL.
  • Анонимные типы и вывод типов поддерживают захват и работу с типом результата запроса. Запрос может как присоединяться, так и проецироваться на источники запроса, что может привести к типу результата, который не может быть назван.
  • Выражения запроса для поддержки синтаксиса, знакомого пользователям SQL.
  • Типы, допускающие значение NULL (повышенные), чтобы обеспечить лучшее соответствие с поставщиками запросов, которые поддерживают типы, допускающие значение NULL, например, SQL.

Родное взаимодействие

Родное взаимодействиеJavaC #
Межъязыковое взаимодействие Да (с GraalVM, Nashorn, CORBA, JNI или JNA )Да; C # был разработан для этого
Внешние / собственные методыДаДа
Маршаллинг Требуется внешний склеивающий кодДа; метаданные контролируются
Указатели и арифметикаДаДа
Собственные типыДаДа
Буферы фиксированного размераNoДа
Явное выделение стекаNoДа
Адрес-изNoДа
Закрепление объекта (привязка переменной к адресу) NoДа

Функция Java Native Interface (JNI) позволяет программам Java вызывать код, отличный от Java. Однако JNI требует код вызывается в соответствии с несколькими соглашениями и накладывает ограничения на используемые типы и имена. Это означает, что часто требуется дополнительный уровень адаптации между устаревшим кодом и Java. Этот код адаптации должен быть написан на языке, отличном от Java. язык, часто C или C ++. Java Native Access (JNA) позволяет упростить вызов машинного кода, который требует только написания кода Java, но требует снижения производительности.

Кроме того, сторонние библиотеки предоставляют мосты Java- Component Object Model (COM), например, JACOB (free ) и J -Integra для COM (проприетарный ).

Вызов платформы.NET (P / Invoke ) предлагает те же возможности, разрешая вызовы из C # на то, что Microsoft называет un управляемым кодом. С помощью атрибутов метаданных программист может точно контролировать, как параметры и результаты упорядочиваются, избегая, таким образом, внешнего связующего кода, необходимого для эквивалентного JNI в Java. P / Invoke обеспечивает почти полный доступ к процедурным API (таким как Win32 или POSIX), но ограниченный доступ к библиотекам классов C ++.

Кроме того,.NET Framework также предоставляет мост.NET-COM, разрешающий доступ к COM-компонентам, как если бы они были первоклассными объектами.NET.

C # также позволяет программисту отключать обычную проверку типов и другие функции безопасности CLR, что затем позволяет использовать переменные-указатели. При использовании этой функции программист должен пометить код с помощью ключевого слова unsafe. JNI, P / Invoke и «небезопасный» код являются одинаково опасными функциями, открывая возможные дыры в безопасности и нестабильность приложений. Преимущество небезопасного управляемого кода перед P / Invoke или JNI заключается в том, что он позволяет программисту продолжать работать в знакомой среде C # для выполнения некоторых задач, которые в противном случае потребовали бы вызова неуправляемого кода. Сборка (программа или библиотека), использующая небезопасный код, должна быть скомпилирована с помощью специального переключателя и будет отмечена как таковая. Это позволяет средам выполнения принимать особые меры предосторожности перед выполнением потенциально опасного кода.

Среда выполнения

Java (язык программирования) разработан для выполнения на платформе Java через среду выполнения Java (JRE). Платформа Java включает виртуальную машину Java (JVM) и общий набор библиотек. Изначально JRE была разработана для поддержки интерпретируемого выполнения с окончательной компиляцией в качестве опции. Большинство сред JRE исполняют полностью или хотя бы частично скомпилированные программы, возможно, с адаптивной оптимизацией. Компилятор Java создает байт-код Java . После выполнения байт-код загружается средой выполнения Java и либо интерпретируется напрямую, либо компилируется в машинные инструкции, а затем выполняется.

C # разработан для выполнения в Common Language Runtime (CLR). CLR предназначена для выполнения полностью скомпилированного кода. Компилятор C # создает инструкции Common Intermediate Language. После выполнения среда выполнения загружает этот код и компилирует в машинные инструкции целевой архитектуры.

Примеры

Ввод / вывод

Пример, иллюстрирующий, как копировать текст по одной строке за раз из одного файла в другой, используя оба языка.

JavaC #
import java.nio.file. *; class FileIOTest {public static void main (String args) выдает исключение {var lines = Files.readAllLines (Paths.get ("input.txt")); Files.write (Paths.get ("output.txt"), строки); }}
с использованием System.IO; class FileIOTest {public static void Main (string args) {var lines = File.ReadLines ("input.txt"); File.WriteAllLines ("output.txt", строки); }}
Примечания к реализации Java: метод
  • Files.readAllLinesвозвращает список строк с содержимым текстового файла, Files также имеет метод readAllBytes, возвращает массив строк.
  • Метод Files.writeзаписывает массив байтов или в выходной файл, указанный объектом Path.
  • Метод Files.writeтакже заботится о буферизации и закрытии вывода stream.
Примечания к реализации C #:
  • Метод ReadLinesвозвращает перечислимый объект, который после перечисления будет читать файл по одной строке за раз.
  • WriteAllLinesметод принимает перечислимое и извлекает строку за раз и записывает ее, пока не закончится перечисление.
  • Базовый модуль чтения автоматически выделяет буфер, поэтому нет необходимости явно вводить буферизованный поток.
  • WriteAllLines автоматически закрывает выходной поток, даже в случае аварийного завершения.

Интеграция типов, используемых библиотекой

C # все определяет используемые типами интеграции, используемые типами интеграции, используя настраиваемые неявные / явные преобразования и перегрузку операторов, показанные в следующем примере:

JavaC #
var bigNumber = new BigInteger («123456789012345678901234567890»); var answer = bigNumber.multiply (новый BigInteger ("42")); var square = bigNumber.multiply (bigNumber); var sum = bigNumber.add (bigNumber);
var bigNumber = BigInteger.Parse ("123456789012345678901234567890"); var answer = bigNumber * 42; var square = bigNumber * bigNumber; var sum = bigNumber + bigNumber;

Делегаты C # и эквивалентные конструкции Java

JavaC #
// класс целевого класса Target {public boolean targetMethod (String arg) {// делаем что-то return true; }} // использование void doSomething () {// создание цели с помощью целевого метода var target = new Target (); // захватить ссылку на метод Function ivk = target :: targetMethod; // вызывает метод boolean result = ivk.apply ("argumentstring"); }
// выбираем класс class Target {public bool TargetMethod (string arg) {// что-то делаем return true; }} // использование void DoSomething () {// создание цели с помощью целевого метода var target = new Target (); // захват делегата для последующего вызова Func dlg = target.TargetMethod; // вызов делегата bool result = dlg ("argumentstring"); }

Изменение типа

JavaC #

В Java нет этой функции, хотя аналогичный эффект возможен с Необязательнымклассом

var a = Необязательно. Оф (42); var b = Optional.empty (); // orElse (0) возвращает 0, если значение b равно нулю var c = a.get () * b.orElse (0);
int? а = 42; int? б = ноль; // c получит нулевое значение // потому что * отменяется и один из операндов имеет значение null int? с = а * Ь;

Взаимодействие с динамическими языками

В этом примере показано, как можно использовать Java и C # для создания и вызова экземпляра класса, реализованного на другом языке программирования. Класс «Глубокая мысль» реализован с использованием языка программирования Ruby и представляет собой простой калькулятор, который умножает два входных значения (aи b), когда Вызывается метод Calculate. В дополнение к традиционному способу, Java имеет GraalVM, виртуальную машину, способную запускать любой реализованный язык программирования.

JavaC #

Использование GraalVM

Контекст polyglot = Context.newBuilder (). AllowAllAccess (правда).build (); // Значение Ruby rubyArray = polyglot.eval ("ruby", "[1,2,42,4]"); int rubyResult = rubyArray.getArrayElement (2).asInt (); // Значение Python pythonArray = context.eval ("python", "[1,2,42,4]"); int pythonResult = pythonArray.getArrayElement (2).asInt (); // Значение JavaScript jsArray = polyglot.eval ("js", "[1,2,42,4]"); int jsResult = jsArray.getArrayElement (2).asInt (); // Значение R rArray = polyglot.eval ("R", "c (1,2,42,4)"); int rResult = rArray.getArrayElement (2).asInt (); // LLVM (в данном случае C, но может быть C ++, Go, Basic и т. Д.) Source source = Source.newBuilder ("llvm", new File ("C_Program.bc")). Build (); Значение cpart = polyglot.eval (источник); cpart.getMember ("основной"). execute ();

Традиционный способ

// инициализируем движок var invocable = new ScriptEngineManager (). GetEngineByName ("jruby"); var rubyFile = новый FileReader ("Deepoughtt.rb"); engine.eval (фр);
// инициализируем движок var runtime = ScriptRuntime.CreateFromConfiguration (); динамические глобалы = время выполнения.Глобальные; runtime.ExecuteFile («Глубокая мысль.rb»);
// создаем новый экземпляр калькулятора «Глубокая мысль» var calc = globals.Deepoughtt. @ New (); // устанавливаем входные значения калькулятора calc.a = 6; calc.b = 7; // вычисляем результат var answer = calc.Calculate ();
// создаем новый экземпляр калькулятора «Глубокая мысль» var calcClass = engine.eval («Глубокая мысль»); var calc = invocable.invokeMethod (calcClass, «новый»); // установить входные значения калькулятора invocable.invokeMethod (calc, "a =", 6); invocable.invokeMethod (calc, "b =", 7); // вычисляем результат var answer = invocable.invokeMethod (calc, "Calculate");

Примечания для реализации Java:

  • Имена средств доступа Ruby генерируются из имени атрибута с суффиксом =. При назначении значений разработчики Java должны использовать имя метода доступа Ruby.
  • Динамические объекты с иностранного языка не являются первоклассными объектами, поскольку ими нужно управлять через API.

Примечания по реализации C # :

  • Объекты, возвращаемые из свойств или методов динамическихобъектов, сами относятся к типу динамический. Когда используется вывод типа (ключевое слово var), переменные calc и answer выводятся динамически / с поздним связыванием.
  • Динамические объекты с поздними границами являются первоклассными гражданами, которые могут быть управляются с использованием синтаксиса C #, даже если они были созданы на внешнем языке.
  • new- зарезервированное слово. Префикс @позволяет использовать ключевые слова в качестве идентификаторов.

Последовательность Фибоначчи

Этот пример показывает, как последовательность Фибоначчи может быть реализована с использованием двух языков. Версия C # использует преимущества методов генератора C #. Версия Java использует преимущества интерфейса и методов Stream. И в Java, и в C # примерах используется стиль KR для форматирования кода классов, методов и операторов.

JavaC #
// Последовательность Фибоначчи Stream.generate (new Supplier () {int a = 0; int b = 1; public Integer get () {int temp = a; a = b; b = a + temp; return temp;}}). limit (10).forEach (System.out :: println);
// Последовательность Фибоначчи public IEnumerable Fibonacci () {int a = 0; int b = 1; while (истина) {yield return a; (а, б) = (б, а + б); }}
// выводим 10 первых чисел Фибоначчи foreach (var it in Fibonacci (). Take (10)) {Console.WriteLine (it); }
Примечания для версии Java:
  • Интерфейс Java 8 Stream - это последовательность элементов, поддерживающих последовательные и параллельные агрегатные операции.
  • Метод generate возвращает бесконечный последовательный неупорядоченный поток, где каждый элемент создается предоставленным поставщиком.
  • Метод limit возвращает поток, состоящий из элементов этого потока, усеченных до длины не более maxSize.
  • Метод forEach выполняет действие для каждого элемента этого потока это действие может быть лямбда-выражением или ссылкой на метод.

Функционал со стилем Stream API

Вышеупомянутый алгоритм можно записать еще более согласованно, используя Stream.iterate. Метод итерации получает начальный параметр и функцию, определяющую, что делать для каждой итерации. В этом случае начальным значением может быть класс записи с двумя начальными значениями алгоритма и соответствующим им преобразованием на каждой итерации.

пара записей (int x, int y); Stream.iterate (new Pair (0,1), p ->new Pair (py (), px () + py ())).limit (10).map (p ->px ()).forEach (System.out :: println);
Примечания для версии C #:
  • Метод определен как возвращающий экземпляры интерфейса IEnumerable, что позволяет клиентскому коду повторяться, запрашивает следующий номер след.
  • Ключевое слово yieldпреобразует метод в метод генератора.
  • Оператор yield returnвозвращает следующий номер и создает продолжение, так что последующие вызовы языка MoveNextинтерфейс IEnumerableбудут продолжать выполнение из следующего оператора со всеми локальными переменными без изменений.
  • Присвоение кортежей позволяет избежать необходимости создавать и использовать временную переменную при обновлении значений числа aи b.

См. Также

Ссылки

Внешние ссылки

Последняя правка сделана 2021-05-15 08:00:30
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте