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

от Уикипедия, свободната енциклопедия

Синтаксис[1][редактиране | редактиране на кода]

И двата езика се считат за „curly brace“ (т.е. използват фигурни скоби) програмни езици от фамилията на C и C++. Като цяло синтаксисът им е много сходен, а на ниво „statement“ (твърдения) и „expressions“ (изрази) е почти идентичен, очевидно „вдъхновен“ от традицията на C++. При дефинирането на типове (класове и интерфейси) съществуват някои незначителни различия. В Java имплементирането на интерфейси и разширяването (extend) на класовете се извършва изрично (explicit), докато в C# това се подразбира от типа, наследен от новия клас/интерфейс.

C# поддържа повече функции от Java, което до известна степен е видно и от синтаксиса му, който включва повече ключови думи и повече правила, отколкото Java.

Ключови думи и обратна съвместимост[редактиране | редактиране на кода]

Тъй като езиците еволюират, „дизайнерите“ и на двата езика постоянно се изправят пред ситуации, от които се вижда, че езиците трябва да бъдат разширени с нови ключови думи или синтаксис. Това обаче крие и опасности. Принципно новите ключови думи могат да „счупят“ вече написаният код, т.е. код, написан на по-стара версия на езика, няма да се компилира от компилатора на по-нова версия (няма обратна съвместимост). Дизайнерите на C# и Java, в стремежа си да избегнат такива регресии, подхождат по различен начин при разрешаването на този проблем.

Дизайнерите на Java се опитват да избягват колкото може повече нови ключови думи, предпочитайки да представят нови синтактични конструкции или да преизползват съществуващи ключови думи в нов контекст. По този начин те не застрашават обратната съвместимост. Пример за това е начинът, по който for-цикълът беше разширен, за да може да приема iterable типове. Друг пример е преизползването на ключовите думи „extends“ и „super“ за специфицирането на типовете, когато „generics“ са въведени в Java 1.5.

Дизайнерите на C# са въвели няколо нови ключови думи от представянето на първата версия на езика. Обаче, вместо да се дефинират като глобални, те са въведени като „контекстно-чувствителни“. Това означава, че след въвеждането например на „partial“ и „yield“ като ключови думи в C# 2.0, употребата им като идентификатори все още продължава да бъде валидна, според контекста разбира се. Така настоящият синтаксис на C# е напълно съвместим със сорс кода, написан на предишните версии на езика.

Ключова дума Приложение
checked, unchecked В C# блоковете код и изразите, дефинирани като checked, могат run-time да следят за препълване на типовете данни.
get, set C# имплементира свойствата (property) на обектите като част от синтаксиса на езика, заедно със средствата за достъп до тях (get, set),

като алтернатива на методите за достъп, използвани в Java.

goto C# поддържа ключовата дума goto. Това в някои случаи би било полезно, но използването на този оператор не се препоръчва. В Java goto конструкцията не съществува, но goto е резервирана дума. Все пак Java поддържа конструкциите break и continue, които в определени ситуации могат да се използват по същия начин като goto
switch(color)
{
 case Color.Blue:
 Console.WriteLine("Color is blue"); break;
 case Color.DarkBlue:
 Console.WriteLine("Color is dark");
 goto case Color.Blue;
 // ...
}
lock В C# с lock се осъществява синхронизиран достъп да блокове код през нишки (threads), обвити в try...finally блок.
out, ref C# поддържа изходящи (out) и референтни (ref) параметри. Това позволява връщането на множество стойности от един метод, или достъпа до стойности по референция.
strictfp Java използва strictfp за да гарантира, че резултатите от работата с числа с плаваща запетая ще останат същите за различни платформи.
switchcase В C# switchcase конструкцията работи също със string и long. Пропадането е позволено за празните case (т.е. без код), а за тези, които съдържат някакъв код, е възможно с „goto case“. Преди Java 7 switchcase конструкцията не е работела с „String“, нито с „long“, но пропадането е позволено за всички case-ове, с изключение на тези, които завършват с break.
throws Java изисква всеки метод да декларира проверените (checked) изключения (exceptions), които може да хвърли.
public int readItem() throws java.io.IOException
{
 // ...
}
using В C# използването на using е причина да се изпълни метода Dispose (които е имплементиран чрес интерфейса IDisposable) на декларирания обект след като код-блокът е приключил изпълнението си или е хвърлил изключение (exception).
// Create a small file "test.txt", write a string,
// ... and close it (even if an exception occurs)
using (StreamWriter file = new StreamWriter("test.txt"))
{
 file.Write("test");
}
В Java SE 7 е добавена подобна конструкция, наречена try-with-resources :
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
 return br.readLine();
}

Обектно-ориентирано програмиране[2][3][редактиране | редактиране на кода]

Както C#, така и Java са изначално проектирани като обектно–ориентирани езици, със синтаксис, сходен на синтаксиса на C++ (който от своя страна произлиза от C). Не може обаче да се каже, че двата езика са съвкупност от C и C++.

Обектно ориентиране Java C#
Класове Задължителни Задължителни
Интерфейси Да Да
Абстрактни класове Да Да
Нива на достъп до член-променливите Да: public, package, protected, private Да: public, internal, protected, private, protected

internal

Вмъкнати класове (inner classes) Да Да
Анонимни класове Да Да (без методи)
Частични класове Не Да
Събития (events) Поддържани от стандартните библиотеки Вградени в езика
Овърлоудване на оператори Не Да
Индексатори Не Да
Косвена (implicit) конверсия Не Да
Изрична (explicit) конверсия Да Да

Частични класове[редактиране | редактиране на кода]

C# позволява дефиницията на класовете да бъде разделена на няколко части на различни места в сорс кода или в различни изходни файлове посредством съществуването на т. нар. частични класове. В този случай всяка част трябва да бъде обозначена с ключовата дума „partial“. Тези части се събират по време на компилация, което позволява изходния код да е идентичен с този на нечастичните (целите) класове. Различните части имат достъп до елементи от други части на класа. Възможно е една част да дефинира базовия клас, а останалите да имплементират интерфейси.

Основната причина за въвеждането на частични класове е улесняване разработката и имплементирането на генератори на код, например графични/визуални дизайнери. Без частичните класове, генераторите трябва да управляват и вмъкват генерирания от тях код на различни места в оригиналния сорс код. Това поражда редица проблеми както с ефективността на компилатора така и с риск от грешки и повреждане на крайния сорс код. За да се избегне това, генераторите създават своя код в отделни файлове или в грубо дефинирани частични класове и компилаторът минава през фаза на „пред-компилация“ (обединяване на сегментите) преди да продължи с нормалната си работа.

За разлика от механизмите за разширяване на класовете (class extensions) частичният клас позволява „циркулираща“ зависимост между отделните части, тъй като е гарантирано, че те ще бъдат достъпни по време на компилация. Java от своя страна не поддържа тази концепция.

Вмъкнати и локални класове[редактиране | редактиране на кода]

И двата езика позволяват създаването на вмъкнати (вътрешни) класове, като в тези случаи класът е дефиниран лексикално вътре в друг клас. За всеки от езиците обаче тези класове имат различна семантика.

В Java, освен ако вътрешният клас не бъде деклариран като статичен, референцията към него представлява и референция към външния клас. Като резултат кодът във вътрешния клас има достъп както до статичните, така и до нестатичните членове на външния клас. За да бъде създадена инстанция на нестатичен вътрешен клас, тя трябва да бъде именувана като инстанция на обгръщащия го външен клас. Това може да бъде направено посредством въведения с JDK 1.3 оператор „new“: outerClassInstance.new Outer.InnerClass(). Това може да бъде направено във всеки клас, който има референция към инстанция на външния клас.

В C#[4] вмъкнатият клас принципно е почти същият като обикновения клас. В известен смисъл външният клас играе ролята на „namespace“. Следователно кодът във вътрешния клас няма достъп до нестатичните членове на външния клас, освен ако не използва изрична референция към инстанцията на външния клас. Вътрешният клас може да бъде деклариран с ключовата дума „private“, за да се ограничи достъпът до него. Така деклариран, той може да бъде достъпен единствено от външния клас.

Друг тип е локалният (или анонимен) клас, който представлява клас, дефиниран в тялото на метод (функция). И C#, и Java позволяват създаването на такива класове.

В Java[5] тези класове се използват предимно за имплементиране на интерфейси със само един или два метода, които обикновено представляват някакви манипулатори на събития (event handlers), но могат да бъдат използвани също и за пренаписване (override) на т. нар. виртуални методи или суперкласове. Методите в тези локални класове имат достъп до променливите, декларирани като константи (с ключовата дума „final“) във външния метод.

В C# също се поддържат анонимните класове (типове), но с известни разлики от Java. Такъв клас в C# може да бъде инстанциран, само като му се подадат като свойства (properties) множество от имена и съответен израз за инициализирането им. Типовете на тези „properties“ се подразбират от типа на израза, който е подаден.

Овърлоудване на оператори и конверсия[редактиране | редактиране на кода]

Овърлоудването (претоварването) на операторите и дефинираното от потребителя „кастване“ (cast – преобразуване на типа на данните) са отделни функционалности, чиято цел е да се позволи на нови типове данни да бъдат използвани свободно, както всички други типове. Чрез използването им в C# са интегрирани типовете като „complex“ и „decimal“, така че обикновените оператори за събиране, умножение, деление и т.н. да могат свободно да работят с тях. За разлика от C++, в C# претоварването на операторите е ограничено, а за операторите „new“, „()“, „| |“, „&&“, „=“ и за някои вариации на съставните оператори („+=“, „-=“ и т.н.) дори забранено. Важно е обаче да се отбележи, че съставният оператор ще извика поотделно унарните оператори, които може вече да са овърлоуднати (напр. „+=“ извиква „+“ и „=“).

Java не поддържа овърлоудването на операторите, нито „custom“ конверсията, с цел да се предотврати злоупотребата с тази функционалност, както и за да се запази „простотата“ на езика.

Индексатори[4][редактиране | редактиране на кода]

C# поддържа т.нар. индексатори, които могат да се разглеждат като специфичен случай на овърлоудване на оператори, когато се използват в обектно-ориентираното програмиране (с оператора [ ] се извикват елементите на колекция по индекс, но в случая се използва за индексация на обекти). Индексаторите се имплементират по подобен начин на свойствата (properties).

Индексаторите представляват синтактическо средство, позволяващо създаването на клас, структура или интерфейс, достъп до които може да се осъществява по индекс подобно на масивите (с оператора "[ ]"). За да се дефинира такъв индексатор, се използва ключовата дума „this“.

public int this[int index] // Indexer declaration
{
 // get and set accessors
}

В Java такива индексатори не съществуват. По индекс (с оператора "[ ]") могат да се получат само и единствено елементите на масив.

Полета и инициализация[3][редактиране | редактиране на кода]

Полета и инициализация Java C#
Полета Да Да
Константи Да Да
Статични конструктори Да Да
Деструктори/финализатори Да Да
Инициализация на обектите Отдолу-нагоре (полета и конструктори) Отгоре-надолу (полета); отдулу-нагоре (конструктори)
Обектни инициализатори Не Да
Инициализатори на колекции Не: могат да бъдат симулирани Да
Инициализатори на масиви Да Да

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

В C#, когато създадем обект, неговите полета се инициализират в следния ред:

  1. Наследени статични полета
  2. Наследени инстанции на полета
  3. Базови статични полета
  4. Базови инстанции на полета

Някои от горните полета може да не са приложими, напр. ако обектът няма статични полета. Наследени полета са тези, които са дефинирани директно в класа на съответния обект (производен клас), докато базови се наричат полетата, дефинирани в суперкласа на обекта.

Гарантирано е, че всяко инициализиране на поле поражда ефект преди да бъде извикан който и да е конструктор, тъй като инстанциите на конструкторите както в производния клас, така и в суперкласа се извикват след това. Въпреки това съществува потенциална опасност при инициализацията на обекти чрез извикване на виртуалния метод от базовия конструктор. Презаписаният в подкласа метод може да реферира поле, което е дефинирано в този подклас, но това поле може да не е било инициализирано, защото конструкторът в подкласа, който инициализира полетата, се извиква след конструктора от базовия му клас.

В Java редът за инициализация е следният:

  1. Извикване на друг конструктор (или от производния клас, или от суперклас)
  2. Инстанции на инициализатори на променливи и инстанции на инициализатори (в реда, в който се появяват в кода)
  3. Изпълнение на самото тяло на конструктора

Както в C#, така и в Java създаваме нов обект, като извикваме конструктор. Самият конструктор от своя страна може да извиква друг конструктор, така докато се извика най-накрая конструктор от суперкласа на обекта. След изпълнението на тялото на конструктора дефинираните променливи се инициализират. Ако някои променливи не са изрично дефинирани, им се присвояват стойности по подразбиране.

В Java има две основни потенциални опасности при инициализацията на обекти. Първо, инициализаторите на променливи са изрази, които могат да съдържат в себе си извикването на метод. Тъй като методите могат да използват всяка променлива, дефинирана в класа, извиканият метод има достъп до променлива, дефинирана по-надолу, преди още да е била инициализарана. Тъй като редът за инициализация съответства на реда на дефиниране на променливите, в този случай променливата няма да бъде инициализирана с нужната стойност, а със стойност по подразбиране. Другият „капан“ е когато метод, който е презаписан в наследения клас се извиква от конструктора на базовия клас. Това може да доведе до неочаквано поведение на обектите, създадени от наследения клас.

Освобождаване на ресурсите (garbage collection)[редактиране | редактиране на кода]

Както C#, така и Java използват т.нар. система за почистване на паметта (garbage collection) виж Система за почистване на паметта (компютърни науки) като средство за управление на ресурсите на паметта. В противен случай тези ресурси би трябвало да се управляват ръчно (като в C и C++). И двата езика поддържат интерфейси за такова „автоматично“ управление на паметта.

Методи[3][редактиране | редактиране на кода]

Методи и свойства Java C#
Статично въвеждане Да Да
Виртуални методи Виртуални по подразбиране Не Виртуални по подразбиране
Абстрактни методи Да Да
Запечатващи Да Да
Изрично изпълнение на интерфейс Методи по подразбиране Java 8 Да
Входни параметри (стойности) Да Да
Референтни параметри Не Да
Изходни параметри Не Да
Константи Да, Окончателни параметри Не
Variadic methods Да Да
Допълнителни аргументи Не Да
Именувани аргументи Не Да
Генеративени методи Не Да
Разширени/по подразбиране методи Да Да
Условни методи Не Да
Частични методи Не Да

Разширени методи и методи по подразбиране[редактиране | редактиране на кода]

С помощта на специалн обозначение на първия параметър на метод, C # позволява методът да се действа, както ако бяха метод член на вида на първия параметър. Това разширение на външния клас е чисто синтактично. Методът за удължаване трябва да бъде обявена за статичен и определен в рамките на чисто статичен клас. Методът не трябва да се подчиняват на никакви ограничения за достъп по този начин статичните методи не могат да счупят обектното капсулиране. Разширението е активно само в рамките на обхват, където името на статичния класа домакин е бил внесен.

След Java 8, Java има подобна функция, наречена методи по подразбиране, чиято структутра наподобява тази на интерфейс. За разлика от С# разширените методи, Java методите по подразбиране са отделен метод на интерфейса, който ги декларира. Определяне на методи по подразбиране в класове, реализиращи интерфейса е по желание: Ако класът не определя метода, се използва определението по подразбиране.

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

С# правилата за обхват дефинират: ако в даден метод се открие съвпадение с класа, то той има предимство пред удължения метод. В Java всеки клас обявен като интерфейс с метод по подразбиране, приема да изпълнява метода по подразбиране, освен ако клас изпълнява самия метод.

Частичен/непълен/ метод[редактиране | редактиране на кода]

Във връзка с частични класове C# позволява частичните методи, да бъдат определяни в рамките на частичните класове. Частиченят метод е декларация на метод с редица ограничения. Тези ограничения гарантират, че ако всяка част от класа не отговаря на дефиницията, а след това и на метода, могат безопасно да бъдат изтрити. Тази функция позволява на код, да има голям брой точки на, без да заплаща по време на работа над главата, ако тези удължители точки не се използват от друг клас, част по време на компилация. Java е без съответното понятие.

Виртуални методи[редактиране | редактиране на кода]

По подразбиране методи в C # не са виртуални, и при желание трябва изрично да бъдат декларирани като виртуални. В Java,[5] всички нестатични, нечастни методи са виртуални. Някои JVM реализации, включително препоръчани от Oracle реализации, най-често изпълняват операции използвайки виртуални методи.

Java методите са виртуални по подразбиране (въпреки че те могат да бъдат „запечатани“ с помощта на крайния модификатор). Няма начин дефинираните класове да наследят, несвързан метод със същото име.

Това означава, че по подразбиране в Java, и само когато изрично се позволява в C #, нови методи могат да бъдат дефинирани в извлечени клас със същото име и сигнатура, като в базовия клас. Когато методът се извиква по референция на суперкласа, той ще бъде извикан според специфичния подклас на обекта.

В някои случаи, когато подклас представя метод със същото име и сигнатура като метод, който вече присъства в базовия клас, могат да възникнат проблеми. В Java, това ще означава, че методът на извлечения клас имплицитно ще замени метода в базовия клас, въпреки че може да не е целта на дизайна на двата класа.

За да се смекчи това, в C# се изисква, ако един метод трябва да замени наследствен метод, трябва да се уточни, ключовата дума за замяна. В противен случай, методът ще „скрие“ наследения метод. Ако ключовата дума отсъства, компилаторът предупреждава, това може да бъде спряно чрез нова ключова дума. Така се избягват проблеми, които могат да възникнат ако базов клас се опита да презапише метод който се наследява, чийто свойства вече се използват от вече наследения клас. Java има подобна компилираща проверка под формата на презаписващ метод с коментар, но това не е задължително, и при липса, повечето компилатори няма да предоставят коментар (но методът ще бъде презаписан).

Константи/Неизменими параметри[редактиране | редактиране на кода]

В Java, е възможно използваните вече параметри да се превърнат в методи, с помощта на ключовата дума final. Език C# не разполага с тази функционалност.

Java C#
/**
 * Getter for a value from the value array
 * @param index
 * @return Requested value or -1, if the index is out of bounds.
 */
 public short getValue(final short index) {
 if (index < values.length) {
 index++; // ERROR: The final local variable INDEX cannot be assigned.
 // It must be blank and not using a compound assignment
 return values[index];
 }
 else return -1;
 }
Няма еквивалент в C#

Тази липсваща функция има само ограничено приложение. За примитивните видове, изпълняващи се по стойност, модифицираната стойност на преминалия във вътрешността на метода параметър не се променя. По този начин, за примитивните видове, при преминаване на даден параметър като окончателен (последен) се предотвратява промяна на стойността на този параметър. За преминали обекти, крайния (последния) параметър предотвратява, да бъде присвоен друг обект.

И двата езика не поддържат съществена характеристика на константна коректност(const­correctness), която съществува в езики като C и C ++, което прави един метод постоянен (константа).

Интересното е, че Java дефинира думата „константа“ произволно като статично окончателно поле. Само тези променливи, се именуват (дефинират) с главни букви с разделител – долна черта. Само последният параметър не се разглежда като константа, това се отнася и за примитивните типове данни или неизменимите клас, от тип string.

Генеративни методи[редактиране | редактиране на кода]

Всяки метод в C# обявен и връщащ IEnumerable, IEnumerator или обща версия на тези интерфейси може да се реализира с помощта на ”yeild” синтаксис. Това е ограничена форма на компютърно-генерирани разширения и може драстично да редуцира кода, въпреки че този код е генериран от компилатора. Функцията може да се използва за прилагане на безкрайни редици (поредици), например редицата на Фибоначи.

Java не разполага с еквивалентна функция. Вместо генератори обикновено се използват специализирани имплементации на добре познати колекции или интерфейси, който ще изчислят всеки елемент по поръчка. За да се използва такъв генератор за всяка изявление, той трябва да изпълнява интерфейса java.lang.Iterable.

Виж също редицата на Фибоначи.

Изричнно изпълнение на интерфейс[редактиране | редактиране на кода]

C # има и изпълнение изрично интерфейс, който позволява на един клас да специфично прилагат методи за интерфейс, се разделят на собствените си методи на клас, или за предоставяне на различни приложения за два метода със същото име и подпис, наследена от две базови интерфейси.

Във всеки език, ако даден метод (свойство в C #) е дефиниран със същото име и сиганура в множество интерфейси, членовете ще се сблъскват, когато един клас е предназначен да реализира тези интерфейси. Изпълнението по подразбиране ще приложи общ метод за всички интерфейси. Ако са необходими отделни приложения (защото методите наистина изпълняват отделни задачи, или защото връщат различни стойности между отделните интерфейси) в C# чрез изрично изпълнение на даден интерфейс, ще се реши проблема, макар и връщащ различни резултати за един и същи метод, в зависимост от текущото състояние на обекта. В Java няма начин да се реши този проблем, освен чрез рефакториране на един или повече интерфейси, за да се избегне съвпадението на имената.

Референция / Входни/изходни параметри[редактиране | редактиране на кода]

Аргументите на примитивните типове (например int, double) в метод се предават по стойност в Java. Обектите се предават чрез препратка (референция). Това означава, че методът работи върху „копия“ на примитивите данни, вместо върху действителните стойности на променливите. Но от друга страна, в някои случаи действителните обекти могат да бъдат променяни.

В C#, е възможно да се извиква (прилага) референция с ключовата дума „ref“, подобно на C++ и донякъде на С. Тази функция на C# е особено полезна, когато искаме да създадем метод, който връща повече от един обект. В Java не се поддържа опцията да бъдат върнати няколко стойности от един метод.

Изключения (грешки)[редактиране | редактиране на кода]

Изключения (грешки) Java C#
Проверени изключения Да Не
Try­-catch­-finally Да Да

Проверени изключения[редактиране | редактиране на кода]

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

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

Може да се смята като критика, че проверените изключения насърчават програмистите да използват празен „catch“ блок, който мълчаливо преглъща изключенията, вместо да позволи те да се разпространят, и да се обработят на по-високо ниво. В някои случаи, обаче, верижните изключения могат да се прилагат, вместо преизхвърляне на изключения. Например, за един обект де се промени достъпът до базата данни, вместо на файла, SQLException могат да бъдат уловени и изхвърлени като IOException, тъй като извикващият може да не се нуждае да знае вътрешната изработка на обекта.

Въпреки това, не всички програмисти са съгласни с тази позиция, за Джеймс Гослинг и други поддръжници на идеята за проверка на изключенията, това е добра идея и ако се пропускат, възниква проблем. „Мълчаливо“ обработване (хващане) на изключенията е възможно, но трябва изрично да се каже какво точно трябва да се направи с възникналото изключение, за разлика от непроверени изключения, които по подразбиране позволяват да не се направи нищо. Това може да бъде игнорирано, но трябва да се напише код.

Try­ – catch – ­finally блок[редактиране | редактиране на кода]

Има и разлики между двата езика в използването на try-catch.Finally блок винаги се изпълнява, дори ако „try“ блокът съдържа контролирани преминавания, като „throw“ или „return“. В Java, това може да доведе до неочаквано поведение, ако „try“ блокът връща някаква стойност, а след това „finally“ блока, който се изпълнява последен, също връща различна стойност. C # решава този проблем, като се забраняват всякаки контролирани преминавания, като „ретурн“ или „brake“ в „finally“ блока.

Често срещана причина за използване на try-finally блокове е да „охранява“ ресурсите на кода, като по този начин се гарантира освобождаването на ценни ресурси в „finally“ блока.

Малка разлика е моментът, когато се създава „stack trace“, при възникване (изхвърляне) на изключение. В Java, „stack trace“ се създава когато възникне изключение.

Изключенията винаги ще съдържат „stack trace“ конструктор без значение колко често се извиква. От друга страна в C #, „stack trace“ се създава когато „throw“ се изпълнява.

В кода по-горе, изключението ще съдържа „stack trace“ на първия throwline. Когато хващането (обработването) на изключение, има два варианта, в случай на изключение трябва отново да се изхвърли: „throw“ отново ще изхрърли „оригиналното“ изключение с оригиналния „stack“, докато изхвърлянето несъздаде нов „stack trace“.

Generics[редактиране | редактиране на кода]

В областта на „generics“ двата езика имат сходна синтактичен прилика, но те ​​имат основните разлики.

Generics Java C#
Изпълнение Типово изтриване Reification
Реализация по време на работа Не Да
Референтен тип / ограничен Да; косвено Да
Стойност/примитивен тип / ограничен Не Да
Конструктор / ограничен Не Да
Подтип / ограничен Да Да
Супертип / ограничен Да Не
Стойност/примитивен тип / подкрепа Не Да
Миграция – съвместимост Да Не

Типове[редактиране | редактиране на кода]

Типове Данни Java C#
Обща система от типове (Common Type System) Само на обвиващите (wrapper) типове. Да.
Целочислени типове данни със знак (Signed) Да. Могат да бъдат 8, 16, 32 и 64 битови. Да; Могат да бъдат 8, 16, 32 и 64 битови.
Целочислени типове данни без знак (Unsigned) Не, но някои методи го поддържат. Да; Могат да бъдат 8, 16, 32 и 64 битови.
Символен тип (char/Character) Да. Да.
Дата / Време Да; Референтен тип. Разположен е в пакета java.time. Да. Стойностен тип.
Реален 32-битов тип с плаваща запетая с единична

точност – точност до 7 десетични знака (float)

Да. Да.
Булев тип (bool/boolean) Да. Да.
Реален 128-битов тип с плаваща запетая

- точност до 28 – 29 десетични знака (decimal)

Не. Да, с точност до 28 знака.
BigDecimal – Реален тип с неограничен размер Референтен тип. Разположен е в пакета java.math. Само от външни библиотеки.
Низове Неизменен референтен тип. Може да съдържа

всички символи от Unicode таблицата. Разположен е

в пакета java.lang.

Неизменен референтен тип. Може да съдържа

всички символи от Unicode таблицата. Разположен

е в класа System.String.

BigInteger – Целочислен тип с неограничен размер Референтен тип. Разположен е в пакета java.math. Да.
Комплексни числа Само от външни библиотеки. Да.
Референтни типове Да. Да.
Масиви Да. Да.
Стойностни типове Не. Само примитивни типове. Да.
Анотации Да. Да.
Енумерации Да. Референтен тип. Разположен е в пакета java.lang. Да. Скаларен тип.
[6] Само на обвиващите (wrapper) типове. Да.
Tuples – функции, които представляват вложени

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

Само от външни библиотеки. Да.
Указатели Използват се чрез референции. Да.

Обща система от типове[редактиране | редактиране на кода]

Двата езика са статично типизирани и класово-базирано обектно-ориентирани. В Java, особеното в примитивните типове е това, че не са обектно-ориентирани, тъй като са вградени в езика на най-ниско ниво. Те също нямат общ прародител с референтни типове. Референтините типове в Java произлизат от общ коренов тип. C#

има обща типова система, в която всички типове (с изключението на несигурните указатели) произлизат от общ коренов тип. Следователно, всички типове имплементират методите на този общ коренов тип и разширените методи, дефиниращи типа object, се прилагат на всички типове, дори на примитивните литерали int и делегатите. За разлика от Java, това позволява на C# да поддържа обекти с капсулация, които не са референтни.

В Java, сложните (съставни) типове са синоними с референтните типове; методите не могат да бъдат дефинирани към тип, който не е класов референтен тип. В C#, концепциите за методи и капсулация били премахнати от изискванията за референция, за да може по този начин един тип да може да поддържа методи и капсулация, без да е необходимо да е референтен тип. Само референтните типове поддържат виртуални методи и специализация на базовия клас.

И двата езика поддържат определен брой вградени типове, които са копирани и подавани като стойности, отколкото като референтни типове. В Java, тези типове се наричат примитивни типове, а в C# – прости типове. Примитивните/простите типове имат поддръжка от процесора.

Примитивните/прости типове в C# имплементират по няколко интерфейса и следователно предлагат определен брой методи директно при инстанцията на типовете – дори и на литералите. Имената на типовете в C# наподобяват псевдоними за типовете на виртуалната машина на .NET – Common Language Runtime (CLR). Типът System.Int64 в C# е абсолютно еквивалентен на типа long; единствената разлика е, че System.Int64 е истинското му .NET име, а в C# се използва чрез псевдоним (long).

За разлика от C#, Java не предлага директни методи на примитивните типове. Вместо това, в Java съществуват т.нар. обвиващи се типове, съдържащи в себе си определен брой методи. Примерно, в типът Long е референтен тип, който обгръща примитивния тип long.

Типове данни[редактиране | редактиране на кода]

Числени методи[редактиране | редактиране на кода]

Целочислени типове със знак[редактиране | редактиране на кода]

И C#, и Java поддържат знакови типове с битови дължини от 8, 16, 32 и 64 бита. Те използват същите имена / псевдоними за типовете, с изключение на 8-битовия целочислен тип, който в C# е под името sbyte (знаков байт / signed byte), а в Java – byte.

Целочислени типове без знак[редактиране | редактиране на кода]

В C#, като има знакови, така и беззнакови типове. Беззнаковите типове са byte, ushort, uint, ulong за битови дължини 8, 16, 32 и 64. Аритметични операции върху беззнакови типове също е позволено. Примерно, ако съберем две числа от тип ulong, резултата пак ще даде число от ulong.

Java не включва беззнакови типове. Фактически, в Java липсва примитивен тип за беззнаков байт. Освен това, в Java съществува т.нар. „sign extended byte“, което означава, увеличение на битовете на тип, като запазва знака на числото и често пъти това довежда по бъгове и объркване.

Беззнаковите типове били напълно отхвърлени от Java, поради причината, че Джейнс Гослинг е вярвал, че програмистите не биха разбрали как беззнаковата аритметика работи.

„В разработването на програмния език, един от стандартните проблеми е, че езика расте толкова сложно, че никой не би го разбрал. Един от малките експерименти, които съм пробвал е да питам хората за правилата на беззнаковата аритметика в С. Оказва се, че никой не разбира как работи беззнаковата аритметика в С. Има някои очевидни неща, които хората разбират, но доста хора не го разбират.“

Джеймс Гослинг

Високо-прецизен реален тип с плаваща запетая[редактиране | редактиране на кода]

В C# съществува реален тип (с точност до 28 десетични знака) за висока аритметична прецизност, наречен decimal. Използва се най-вече за финансови и валутни сметки. С decimal, при закръгляне на дроби, шансовете да бъде изгубена прецизност е минимална (и дори никаква), докато при float и double, шансът е много по-голям.

Докато в Java, такъв вграден тип не съществува, обаче в библиотеката му е включен тип за голяма прецизност. Той не е приет за официален тип на Java и не поддържа основните аритметични оператори; по-скоро е референтен тип, който може да бъде манипулиран чрез методите, които са му включени.

Символи[редактиране | редактиране на кода]

И двата езика включват типа char като прост тип. Въпреки че типа може да пъде използван с побитови операции, символния тип също може да бъде преобърнат в целочислен тип и обратно.

Вградени сложни типове данни[редактиране | редактиране на кода]

И двата езика се отнасят към низовете като неизменен референтен тип. И двата езика типа съдържа методи за манипулиране на низ, парсване, форматиране и др. И в двата езика регулярните изрази са приети за отделни функционалности и биват имплементирани чрез отделни класове от стандартната библиотека на езика.

Библиотеките и на двата езика включват класове за работа с дати и календари за различни култури. В Java, java.util.Date е изменим референтен тип, докато System.DateTime е структурнен стойностен тип в C#. C# отделно има включен тип, наречен TimeSpan за работа с периоди. И с двата езика може да се извършват аритметични сметки с дати и време.

Бележки[редактиране | редактиране на кода]

  1. C Sharp синтаксис
  2. Обектно-ориентирано програмиране
  3. а б в Comparison of C Sharp and Java
  4. а б „Въведение в програмирането със C#“
  5. а б „Въведение в програмирането с Java“
  6. Нулеви типове