Разработчики C #, изучающие Java, какие самые большие различия можно упустить? [закрыто]

87

Для разработчиков C #, которые стремятся изучить Java, есть ли какие-либо существенные различия между двумя языками, на которые следует указать?

Может быть, некоторые люди могут подумать, что все будет по-прежнему, но есть некоторые важные аспекты, которые не следует упускать из виду? (или вы действительно можете облажаться!)

Может быть, с точки зрения конструкций ООП, того, как работает сборщик мусора, ссылок, связанных с развертыванием и т. Д.

мрбла
источник
Также stackoverflow.com/questions/295224/… .
Майкл Майерс
Также, вероятно, будут упущены из виду наши собственные «Скрытые возможности C #» stackoverflow.com/questions/9033/hidden-features-of-c
Джон К.

Ответы:

122

Несколько ошибок в голове:

  • В Java нет настраиваемых типов значений (структур), поэтому не ищите их.
  • Перечисления Java сильно отличаются от подхода C # к "именованным числам"; они больше ОО. Их можно использовать с большим эффектом, если вы будете осторожны.
  • byte подписан на Java (к сожалению)
  • В C # инициализаторы переменных экземпляра выполняются раньше, чем конструктор базового класса; в Java они запускаются после него (т.е. непосредственно перед телом конструктора в "этом" классе)
  • В C # методы по умолчанию запечатаны. В Java они по умолчанию виртуальные.
  • Модификатор доступа по умолчанию в C # всегда «самый ограничительный доступ, доступный в текущем контексте»; в Java - это «пакетный» доступ. (Стоит прочитать о конкретных модификаторах доступа в Java.)
  • Вложенные типы в Java и C # работают несколько иначе; в частности, у них разные ограничения доступа, и если вы не объявите вложенный тип, staticон будет иметь неявную ссылку на экземпляр содержащего класса.
Джон Скит
источник
5
+1 Это отличный список для начинающих.
Джим Шуберт
3
@Jon Skeet: +1, и вы, вероятно, пропустили бы Lambda и LINQ до Java 7?
Кб.
1
«В C # инициализаторы переменных экземпляра запускаются до того, как это сделает конструктор базового класса; в Java они запускаются после него (т.е. непосредственно перед телом конструктора в« этом »классе)» - Иээ, не могли бы вы это уточнить? Не уверен, что я действительно понимаю, о чем вы говорите :)
cwap 06
7
@cwap: В C # порядок выполнения - «инициализаторы переменных экземпляра, конструктор базового класса, тело конструктора». В Java это «конструктор суперкласса, инициализаторы переменных экземпляра, тело конструктора». (Инициализаторы переменных экземпляра - это то, что вы получаете, когда присваиваете значение переменной экземпляра в точке объявления.)
Джон Скит,
1
@cwap: Да, я имею в виду - последний инициализатор объекта .
Джон Скит
17

Я удивлен, что никто не упомянул свойства, которые в C # довольно фундаментальны, но отсутствуют в Java. В C # 3 и выше также автоматически реализованы свойства. В Java необходимо использовать методы типа GetX / SetX.

Еще одно очевидное отличие - LINQ и лямбда-выражения в C # 3 отсутствуют в Java.

В Java отсутствуют некоторые другие простые, но полезные вещи, такие как дословные строки (@ ""), перегрузка операторов, итераторы, использующие yield и препроцессор, также отсутствуют в Java.

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

софтведа
источник
10

Отличий много, но мне на ум приходят следующие:

  • Отсутствие перегрузки операторов в Java. Следите за своим instance.Equals (instance2) по сравнению с instance == instance2 (особенно со строками).
  • Привыкайте к интерфейсам, НЕ имеющим префикса I. Часто вместо этого вы видите пространства имен или классы с суффиксом Impl.
  • Блокировка с двойной проверкой не работает из-за модели памяти Java.
  • Вы можете импортировать статические методы без префикса с именем класса, что очень полезно в определенных случаях (DSL).
  • Операторы Switch в Java не требуют значения по умолчанию, и вы не можете использовать строки в качестве меток case (IIRC).
  • Дженерики Java рассердят вас. Дженерики Java не существуют во время выполнения (по крайней мере, в версии 1.5), это трюк компилятора, который вызывает проблемы, если вы хотите отражать общие типы.
Sneal
источник
4
В последний раз я проверял, что строки НЕ перегружали ==, и я не слышал, чтобы он добавлялся для Java 7. Однако они перегружают + для конкатенации строк.
Майкл Мэдсен,
3
Да, сравнение строк с == - очень распространенная ошибка новичков. .equalsэто то, что вам нужно использовать.
Майкл Майерс
Кроме того, я не понимаю комментарий о статических методах. Разве почти все языки не позволяют использовать статические методы или их эквиваленты?
Майкл Майерс
Я почти поддержал это, но я не согласен с дженериками. Мне нравится стирание шрифта.
finnw
2
«Операторы переключения в Java не требуют значения по умолчанию» - и C # тоже.
RenniePet
8

.NET расширил дженерики; Java удалила дженерики.

Разница заключается в следующем: если у вас есть ArrayList<String>объект, в .NET вы можете сказать (во время выполнения), что объект имеет тип ArrayList<String>, тогда как в Java во время выполнения объект имеет тип ArrayList; Stringчасть теряется. Если вы вставляете не Stringобъекты в ArrayList, система не может обеспечить это, и вы узнаете об этом только после того, как попытаетесь извлечь элемент, и приведение не удастся.

Крис Джестер-Янг
источник
1
... хотя стоит добавить, что вы не можете поместить не Stringобъекты в этот список, не выполнив неконтролируемое приведение где-нибудь , и компилятор правильно предупредит вас в этот момент, что компилятор больше не может проверять общие границы. Пока ваш список всегда хранится в переменной List<String>, попытки вставить в него объект, отличный от String, не будут компилироваться.
Анджей Дойл
«объекты, не являющиеся строковыми, в ArrayList, система не может обеспечить это»: обратите внимание, что система действительно применяет это во время компиляции. Однако вы можете обойти это, применив приведение (или не используя дженерики); тогда проверка времени выполнения поймает его, но только при извлечении.
sleske 06
1
@Andrzej, @sleske: Я говорю только о времени выполнения. Допустим, вы получаете объект через отражение и вызываете его addметод (опять же, через отражение). Будет ли система отклонять не Stringобъект сразу или только при приведении результата get?
Крис Джестер-Янг,
@sleske: Но IIRC вам нужно только неявное приведение, чтобы обойти проверку типа.
Ники
@Judah: Я откатил ваше изменение (да, через год), потому что у вас не может быть объектов типа List. У вас могут быть объекты типов, производных от List, например ArrayListи LinkedList. (Это важно использовать Listвместо того, чтобы ArrayListпередавать их, но это не меняет того факта, о котором вы не можете сказать new List().)
Крис Джестер-Янг,
6

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

Шон
источник
1
FWIW, это было учтено при разработке C #, и причина их исключения вполне реальна. Рассмотрим artima.com/intv/handcuffs.html . Дело не в том, что дизайнеры философски настроены против проверенных исключений - скорее, они ждут лучшей техники, предлагающей аналогичные преимущества без всех грубых недостатков.
Greg D
Да-да, очень спорная тема ...
sleske 06
2
Мне нравится тот факт, что в C # нет проверенных исключений, но это был первый пост, который даже указал на разницу, так что +1.
Ник
5

Java имеет автобоксирование для примитивов, а не для типов значений, поэтому, хотя System.Int32[]в C # и Integer[]является массивом значений, это массив ссылок на Integerобъекты, который не подходит для вычислений с более высокой производительностью.

Пит Киркхэм
источник
1
Пит: Это отличный момент, о котором я никогда не знал или, по крайней мере, никогда не рассматривал (будучи разработчиком C # и новичком в Java).
Джим Шуберт
4

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

чашка
источник
3
"не такое уж большое дело". Правильно. Потому что x => x.Fooпротив new Bar() { public void M(SomeType x) { return x.Foo; } }- кого волнует, чтобы код был в 10 раз короче?
Кирк Уолл
2

Встроенная функциональность даты / календаря в Java ужасна по сравнению с System.DateTime. Здесь много информации об этом: Что не так с Java Date & Time API?

Некоторые из них могут быть ошибкой для разработчика C #:

  • Класс Java Date является изменяемым, что может сделать опасными возвращение и передачу дат.
  • Большинство конструкторов java.util.Date устарели. Простое создание экземпляра даты довольно многословно.
  • Я никогда не получал, что класс java.util.Date хорошо взаимодействует с веб-службами. В большинстве случаев даты на обеих сторонах дико преобразовывались в другие дату и время.

Кроме того, Java не имеет всех тех функций, которые предоставляют GAC и сборки со строгими именами. Jar Hell - это термин для обозначения того, что может пойти не так при компоновке / ссылке на внешние библиотеки.

Что касается упаковки / развертывания:

  • бывает сложно упаковать веб-приложения в формат EAR / WAR, которые фактически устанавливаются и запускаются на нескольких различных серверах приложений (Glassfish, Websphere и т. д.).
  • развертывание вашего Java-приложения в качестве службы Windows требует гораздо больше усилий, чем в C #. Большинство рекомендаций, которые я получил для этого, касались платной сторонней библиотеки.
  • Настройка приложения не так проста, как включение файла app.config в ваш проект. Существует класс java.util.Properties, но он не такой надежный, и поиск правильного места для размещения файла .properties может сбивать с толку.
вОрбит
источник
1

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

BFree
источник
1

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

(отредактируйте, например) Пример, B является производным от A (используя синтаксис C #, Java ведет себя так же, как в прошлый раз, но не выдает предупреждения компилятора). Вызывается ли foo от A или foo от B? (A вызывается, что, вероятно, удивляет разработчика, реализовавшего B).

class A 
{
public void foo() {code}
}

class B:A
{
public void foo() {code}
}    

void SomeMethod()
{
A a = new B(); // variable's type is declared as A, but assigned to an object of B.
a.foo();
}
Джим Л
источник
2
Как случайно скрыть метод экземпляра в Java? они либо окончательные - поэтому нет переопределения или newметода для намеренного скрытия - либо не окончательные, поэтому они переопределяются, а не скрываются.
Pete Kirkham
Добавлен пример. Я давно не тестировал его на Java, но вопрос собеседования изначально был задан группой Java из моей предыдущей компании. В C # предупреждение компилятора обычно означает, что люди меняют имя. Я признаю, что это довольно странный случай, но OP, похоже, спрашивал о вещах, которые было бы трудно найти при переключении языков.
Jim L
1
Разница здесь в том, что в Java все методы являются виртуальными (поэтому в Java вызывается B.foo (), потому что Java всегда выполняет динамическую отправку методов), тогда как в C # методы не являются виртуальными по умолчанию (и A.foo () будет называться). Многие программы проверки стиля кодирования для Java скажут вам, что B следует использовать аннотацию @Override.
Уильям Биллингсли
1

В Java нет LINQ, а документация - это ад. Пользовательские интерфейсы на Java сложно разрабатывать, вы теряете все хорошее, что Microsoft дала нам (WPF, WCF и т.д.), но становитесь трудными в использовании, плохо документированными «API».

Полный Тьюринга
источник
0

Единственная проблема, с которой я столкнулся до сих пор при работе с Java, исходящей от C #, - это разные исключения и ошибки.

Например, вы не можете поймать ошибку нехватки памяти с помощью catch (Exception e).

Для получения более подробной информации см. Следующее:

почему-это-java-lang-outofmemoryerror-java-heap-space-not-поймано

Крис Персичетти
источник
Он специально разработан таким образом, чтобы не дать программисту приложений уловить его
finnw
Да, я уверен, что это было так, но со стороны C #, где все, о чем вам нужно беспокоиться, это перехват исключений, бросило меня в тупик :)
Крис Персичетти
0

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

Джейсон слишком крутые сети
источник
0

В ответ на ваш очень прямой вопрос в заголовке:

«Разработчики C #, изучающие Java, какие самые большие различия можно упустить из виду?»

О: Дело в том, что Java работает в Windows значительно медленнее.


источник
4
Я использую 64-разрядную версию OpenSuSE и 64-разрядную версию Windows 7, а Eclipse IDE ("кажется") быстрее в Windows. Ваш ответ основан на личных наблюдениях или на реальных тестах?
Джим Шуберт
0

Самая неприятная для меня разница, когда я перехожу на java, - это объявление строки.

в C # string(большую часть времени) в JavaString

Это довольно просто, но поверьте мне, это заставляет вас терять так много времени, когда у вас есть привычка этого sне делать S!

iChaib
источник