Укр
Выбор первого языка программирования — обзор Java

Выбор первого языка программирования — обзор Java

  • 10 марта, 2016

На сегодняшний день самым популярным языком программирования является Java. При этом он продолжает набирать обороты.

Попытаемся разобраться почему Java выбирается в современных проектах в качестве основного языка программирования.

Си-подобный синтаксис*

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

Java и С++ не единственные, кто обладает подобным синтаксисом. Прародителем C++ был язык C (Си), потому все языки использующие подобный синтаксис называются си-подобными, и их существует великое множество (Wikipedia: C-family).

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

Плюсы:

  • популярен

Минусы:

  • требует привыкания

* Синтаксис — это набор правил, которым должен соответствовать текст программы. По-простому: то, как выглядит программа. Более сложно - на вики.

Язык общего назначения

По своему назначению языки программирования бывают:

  • специальные (решающие задачи в определённой отрасли. Например, в математике Fortran, в базах данных SQL);
  • общего назначения (решающие широкий спектр задач в любых отраслях. Например С++, Java, C#).

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

Плюсы:

  • универсален, решает любые задачи

Минусы:

  • в отдельных отраслях уступает (по скорости работы и удобству использования) специализированным языкам

Объектно-ориентированный

Все компьютерные программы пытаются описать процессы нашего мира в командах процессора. Поскольку компьютер ничего не знает об объектах нашего мира, программисту всегда приходилось «переводить» всё на язык цифр и инструкций. Так было до 70-х годов, пока не появилась объектно ориентированная парадигма программирования. С её помощью оказалось возможным программирование на уровне объектов из нашего мира, а не на уровне цифр из мира компьютерного.

Например, вот так выглядит описание двух столов в не-объектном виде:

  • длина первого стола: 1200 мм
  • ширина первого стола: 500 мм
  • высота первого стола: 1000 мм
  • длина второго стола: 1300 мм
  • ширина второго стола: 600 мм
  • высота второго стола: 1100 мм

Между параметрами нет никакой связи, кроме их названий. Можно легко допустить ошибку в попытке посчитать площадь стола: умножить длину ПЕРВОГО стола на ширину ВТОРОГО.

Пример объектно-ориентированного описания:

Первый стол:

  • длина: 1200 мм
  • ширина: 500 мм
  • высота: 1000 мм

Второй стол:

  • длина: 1300 мм
  • ширина: 600 мм
  • высота: 1100 мм

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

Программы, написанные на объектно-ориентированных языках, выполнялись медленней. Это было основной причиной, почему эта парадигма стала популярной лишь в 90-х, когда «железо» стало к этому готово.

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

Плюсы:

  • ООП используется во многих других языках программирования
  • широко использует абстракции
  • легко моделировать объекты реального мира

Минусы:

  • требует дополнительного изучения и развития абстрактного мышления

Функциональный

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

  1. Оптимизировать программу;
  2. Увеличить частоту процессора.

С оптимизацией имеем следующие проблемы:

  • оптимизировать приходится ВСЕ программы;
  • существует логический лимит, дальше которого оптимизировать невозможно.

Ускорение процессора решает эти проблемы, и ускоряет выполнение сразу всех программ.

Благодаря закону Мура мир развивался вторым путём — ускорял процессоры. Закон гласил, что каждые два года частота процессоров обязана удваиваться. Но однажды закон Мура перестал работать и частота перестала расти. И мы перестали получать необходимое нам ускорение программ. Во всём сразу обвинили Мура, сказали, что он всё не так понял, и в закон внесли поправку: не частота удваивается, а количество транзисторов!

Таким образом последние годы частота процессоров не растёт, а растет количество транзисторов. Транзисторы расходуются на дополнительные ядра процессоров. Но если посмотреть на выше упомянутые два способа ускорения программ — там нет пункта о добавлении ядер. Чтобы программу можно было ускорить с помощью дополнительных ядер — она должна быть способна выполняться независимыми частями.

Но сделать это не так-то просто: все куски программ зависят друг от друга, зависят от общих данных (особенно если речь идёт об объектно-ориентированном программировании).

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

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

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

Существуют полностью функциональные языки (Haskel, Erlang) и гибридные — сочетающие в себе объектно-ориентированный и функциональный подходы (Scala, C++).

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

Плюсы:

  • доступны новые приёмы программирования
  • легко создаются программы, способные использовать одновременно несколько ядер процессора

Минусы:

  • требует дополнительного изучения и развития функционального (математического) мышления

Типизация: строгая, статическая, явная

Строгая типизация

Судя из названия, если типизация строгая — то существуют какие-то правила, нарушать которые нельзя.

Если типизация НЕ строгая (например как в JavaScript), это не значит что правил нет. Правила есть, при чём есть ещё дополнительные правила, которые вступают в силу, когда нарушаются основные.

Пример (необходимо понимать что строки и числа — это разные типы данных):

Сложение двух чисел и двух строк, не зависимо от модели типизации, везде происходит одинаково:

5 + 5 = 10

«5» + «5» = «55»

Разница будет когда мы (возможно по ошибке) попытаемся сложить число и строку:

5 + «5» = ? (10 или «55»)

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

Не строго типизированный начнёт хитрить: он прибегнет к дополнительному набору правил на случай не соответствия типов. В зависимости от этих правил будет выбран один из вариантов:

  1. Строка «5» станет числом,и результат будет 10 (сложение чисел)
  2. Число «5» станет строкой,и результат будет «55» (конкатенация строк)

Получается, что при не строгой типизации, по ошибке мы можем попытаться выполнить операцию над несовместимыми типами данных, и в зависимости от «дополнительных правил» получить разный результат: 10 или «55».

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

Плюсы:

  • нет дополнительных правил автоматического преобразования типов: их не нужно знать и нет ошибок неправильного преобразования
  • быстро приходит понимание типизации

Минусы:

  • все необходимые преобразования приходится выполнять явно

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

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

Естественно, что в наши дни никто не пишет код в блокноте, и не компилирует из командной строки — всё это умеют делать современные инструменты разработки IDE (IDEA, Eclipse, NetBeans). И все указанные IDE активно пользуются тем, что типизация статическая, и проверяют ошибки ещё до того, как мы попытаемся код скомпилировать.

Ещё одним плюсом статической типизации является то, что IDE способна подсказывать, какие методы и свойства есть у объекта. Это очень сильно упрощает и разработку, и обучение.

Плюсы:

  • об ошибках несовместимых типов мы узнаём во время компиляции (с использованием IDE во время написания кода), а не во время выполнения программы
  • зная тип данных IDE может подсказывать, какие операции с ним можно выполнить (очень полезно, особенно на начальных этапах знакомства с языком)

Минусы:

  • необходимость определять типы самостоятельно

Явная типизация

Для того, чтобы объявить переменную в Java, необходимо явно указать её тип. Например, переменная i целочисленного типа объявляется так:

int i = 5;

При этом на самом деле Java умеет вывести тип переменной i и без нашего int. Достаточно было бы написать:

var i = 5;

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

Плюсы:

  • нет

Минусы:

  • необходимость указывать типы явно (микро-проблема)

Кросс-платформенный

Для того, чтобы написанное вами приложение могло запускаться на разных платформах (Linux, Windows, Android), оно должно быть преобразовано (скомпилированно) в совместимые с этой платформой процессорные инструкции.

Java декларирует, что для её программ этого не требуется. За счёт чего это достигается?

Код, написанный на Java, компилируется не в инструкции процессора (как у большинства компилируемых языков), а в специальный байт-код. Этот байт-код запускается на Java-машине — специальной программе, которая умеет преобразовать байт-код в процессорные инструкции.

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

Плюсы:

  • программы могут работать на разных системах

Минусы:

  • страдает производительность (незначительно)

Сборщик мусора

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

Так вот Java решает эту проблему с помощью сборщика мусора. Вы просто не должны думать о ненужных объектах, думайте только о нужных! А о не нужных позаботится сборщик. Сам. Без напоминаний.

Попутно сборщик решает ещё одну проблему — фрагментацию памяти. Если в двух словах, суть проблемы в следующем: есть кусок памяти на 10 ячеек, 5 из них заняты и 5 свободны. Вы хотите записать целый объект на 5 ячеек в память — но не можете сделать этого. Т.к. свободные ячейки расположены не подряд, а три вначале, и две в конце.

Плюсы:

  • не нужно следить за объектами, и принимать решение об очистке памяти
  • нет проблемы фрагментации памяти

Минусы:

  • страдает производительность

Hello world

Обзор языка был бы не полным без классического примера самой простой программы:

public class MyFirstClass {

public static void main(String[] args) {

System.out.println("Hello world!");

}

}

Из-за многословности этого, казалось бы, элементарного действия считают, что «порог входа» в Java слишком велик, и это отпугивает многих потенциальных пользователей этого языка. Действительно, hello world на Java окунает в ООП, заставляя задуматься, что такое класс. Почему он паблик? Зачем статик? Однако, на самом деле достаточно относиться первое время к этому как к магии и продолжать изучение этого замечательного языка.

И со временем Вам будут открыты все таинства заклинаний Java :)