Roman Ivanov 06 сентября 2021

☕ Основы Java за 30 минут: самоучитель для начинающих

В этой статье мы рассмотрим основные особенности языка Java, познакомимся с его философией и разберемся с причинами его популярности, а также напишем пару простеньких программ, чтобы изучить его синтаксис и подход к написанию кода.
☕ Основы Java за 30 минут: самоучитель для начинающих
Дисклаймер
Сразу хочу вас предупредить, чтобы стать настоящим java разработчиком с нуля вам нужно будет потратить в разы больше его времени. Как преподаватель java в Сбер Университете, по своему опыту могу сказать, чтобы точно получить хорошую работу на изучение языка и его инфраструктуры вы должны потратить 5-6 месяцев – при этом заниматься минимум 40 часов в неделю.

Начинаем!

История языка и предпосылки к лидерству

Почему Java так популярен? Во-первых это старый язык по меркам сферы ИТ. Ему уже более 25 лет. За это время было написано огромное количество приложений и библиотек, сформировано, наверное, самое многочисленное комьюнити программистов, которое поможет найти ответ на любой вопрос. Во-вторых java создавался с прицелом на высокую надежность: в то время доминировали языки C и C++, которые обладали высоким порогом входа – нужно было внимательно следить за каждой строчкой. Легко можно было выстрелить себе в ногу, неправильно использовав множественное наследование, не почистив правильно память и т.д.

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

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

Одной из киллер-фич был подход к исполнению кода. Он не компилировался напрямую в бинарники. Компилятор создавал на основе исходного кода байт-код, который уже в свою очередь с помощью специального приложения – JAVA машины исполнялся на компьютере. Такой подход давал ряд существенных преимуществ: программист мог запустить один и тот же код по разными операционными системами, процессорами вообще без каких-либо изменений!

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

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

Недавно java перешла на новый релизный цикл – каждая новая версия выходит через полгода. Это позволяет быстрее доставлять новые возможности в язык и практически полностью сократить отставание языка от других jvm языков.

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

Подготовка окружения
В первую очередь нам надо скачать JDK – это та самая виртуальная машина, а также набор утилит, которые позволят собирать код. После чего мы уже можем начинать программировать в любимом редакторе. Если у вас такового не имеется, могу порекомендовать скачать и установить – IntelliJ IDEA Community. Это самый популярный редактор исходного кода на Java, базовая версия которого распространяется бесплатно.

После того как мы скачали все и запустили редактор, приступим к изучению.

Объекты и методы в java

В java все является объектом. Что такое объект? Это некоторая сущность в которой описано ее состояние с помощью каких-либо переменных, а также описано ее поведение с помощью функций. Рассмотрим все на примере из реального мира.

У нас есть автомобиль, у него много характеристик. Мы абстрагируемся от них, нас интересует только два параметра: текущая скорость и максимальная скорость. В мире объектно-ориентированного программирования этот подход так и называется – абстракция. Какое поведение мы ожидаем от автомобиля? Всего два – начать движение и остановиться. Напишем этот код на java в файле с именем Car.java:

Car.java
        public class Car {
   int speed;
   int maxSpeed;
  
   void start(){}
   void stop(){}
}

    

Здесь мы описали шаблон будущего объекта – класс. У него есть две переменные состояния – speed и maxSpeed, а также две функции, которые описывают поведение объекта.

Теперь приложение нужно запустить, для этого Java надо подсказать, где находится точка входа в него. Для этого существует особое соглашение – нужно добавить в описание любого объекта метод:

        public static void main(String[] args) {
  
}

    

Именно, тот код, который мы в нем укажем и начнет выполняться.

Выполним теперь в консоли следующие команды:

        javac Car.java
java Car
    

Либо, что в разы проще, просто нажимаем зеленую стрелочку около метода и редакторе:

☕ Основы Java за 30 минут: самоучитель для начинающих
☕ Подтянуть свои знания по Java вы можете на нашем телеграм-канале «Библиотека Java для собеса»

Наше приложение запустилось, но ничего не произошло, потому что мы не добавили никакой логики в метод. Добавим простой вывод на консоль текста:

        public static void main(String[] args) {
   System.out.println("Hello");
}

    

Результат выполнения виден на консоли.

Отмечу, что каждый файл может содержать только один публичный класс. То есть ключевое слово public вместе со словом class.

Ключевое слово public – это так называемый модификатор доступа. Оно определяет уровень доступности этого класса/метода/переменной из других частей программы.

Имя файла должно совпадать с именем класса, включая регистр, и иметь расширение .java. Ключевое слово static, говорит компилятору о том, что данный метод/переменная принадлежит именно шаблону объекта, то есть классу, а не конкретному объекту.

Пока не будем особо на это заострять внимание.

Пакеты в java

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

☕ Основы Java за 30 минут: самоучитель для начинающих

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

Это так же позволяет легко импортировать чужие классы в свой код:

Import.java
        package ru.proglib;

import java.util.Random;  // импортируем описание класса из пакета java.util.
public class Import {
   public static void main(String[] args) {
       var random = new Random();
       System.out.println(random.nextInt());  // выводим на экран случайное число
   }
 }

    

В нашем пакете нет определения класса Random, поэтому я воспользовался ключевым словом import, чтобы его добавить в нашу программу. Теперь я могу с ним работать. Используя ключевое слово new я создаю на основе класса объект random, который могу использовать в дальнейшем коде. Далее у объекта я вызываю метод, nextInt который описывает следующее поведение объекта: объект возвращает из метода натуральное число, которое произвольно каким-то образом у себя генерирует. Мы не знаем как именно это происходит – мы знаем только то, что в результате вызова этого метода мы получим какое-то целое число типа int. В объектно ориентированном программировании этот прием называется инкапсуляцией – когда объект внутри себя, основываясь на своем состоянии генерирует некий результат, при этом пользователь данного метода не знает как это работает под капотом.

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

🧩☕ Интересные задачи по Java для практики можно найти на нашем телеграм-канале «Библиотека задач по Java»

Создание объектов

В коде у нас была следующая строка:

        Random random = new Random();
    

Что она делает?

Здесь мы с помощью ключевого слова new создаем новый объект из его шаблона. Мы указываем, что в своем коде мы будем использовать имя random для обращения к этому объекту. В последней строке мы вызываем метод nextInt который приводит к тому, что в нашу программу возвращается какое-то число, после чего это же число мы передаем в метод println который уже выводит его на экран.

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

Car.java
        public class Car {
   int speed;
   int maxSpeed;

   void start() {
       System.out.println("Я начал ехать");
   }

   void stop() {
       System.out.println("Я остановился");
   }

   public static void main(String[] args) {
       var myCar = new Car();
       myCar.start();
       myCar.stop();
   }
}

    

Запустите код и посмотрите, что будет выведено на консоль.

Теперь вернемся к нашим переменным состояния объекта.

Примитивные типы в java

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

Ключевое слово Тип Пример
boolean true или false (8-bit число) true
byte 8-bit число 123
short 16-bit число 123
int 32-bit число 123
long 64-bit число 123L
float 32-bit число 123.0f
double 64-bit число 123.0
char 16-bit число 'a'

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

Поэтому появилась такая битовая таблица различных чисел. В нашем случае мы будем использовать числа типа int, которые могут быть описаны 32 битами в памяти компьютера. То есть наши числа будут лежать в промежутке -2,147,483,648 (-2^31) to 2,147,483,647 (2^31-1).

Конструкторы объектов

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

Car.java
        public class Car {
   int speed;
   int maxSpeed;

   public Car(int speed, int maxSpeed) {
       this.speed = speed;
       this.maxSpeed = maxSpeed;
       System.out.println("Объект готов");

   }

   void start() {
       System.out.println("Я начал ехать");
       System.out.println(speed);
   }

   void stop() {
       System.out.println("Я остановился");
       System.out.println(maxSpeed);
   }

   public static void main(String[] args) {
       var myCar = new Car(100, 500);
       myCar.start();
       myCar.stop();
   }
}

    

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

Если вы запустите приложение, то увидите, что кроме строк на экран выводятся и числа – как раз те, которые мы передали в конструкторе. Теперь наш объект инициализирован – у него есть какое-то внутреннее состояние.

Ссылки vs примитивные типы

Теперь рассмотрим ключевое различие между ссылками на объект, которые мы использовали для работы с объектами и примитивными типами. Для ссылок мы можем написать так:

        var myCar = new Car(100, 500);
myCar = null;
    

Мы приравняли наш указатель на объект к ключевому слову null, которое говорит виртуальной машине, что по данному указателю уже нельзя обращаться к объекту, то есть вызов myCar.start(); приведет к ошибке. Что произойдет с нашим объектом, который мы создали? В виртуальной машине java запускается сборщик мусора, который обнаружит, что данный объект живет без какой либо ссылки и удалит его из памяти – то есть сотрет его в оперативной памяти.

Но подобное не работает с примитивными типами:

        int x = null;
    

Подобный код вызовет ошибку.

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

Строки описываются классом String. Посмотрим какие исключения для них есть:

        var str = "Я остановился";
var srt2 =  new String("Я остановился");

    

Это единственный класс, который мы можем создать без оператора new.

Кроме того, строки можно складывать друг с другом или другими примитивными типами, но не вычитать, делить и т.п.:

        System.out.println("Я начал ехать" + "в направлении М1");
System.out.println("Я начал ехать" + "в направлении М1 со скоростью " + speed);

    

Добавьте данные строчки в наш код и вы увидите, что все прекрасно работает. Но повторюсь, что подобное исключение сделано только для одного класса – String, потому что строки очень часто используются.

Операторы в java

Раз мы упомянули операторы, давайте посмотрим какие предлагает java.

Унарный оператор – это оператор, для работы которого требуется только один операнд, или переменная, часто выполняют простые операции.

Оператор Описание Пример
! Инвертирует логическое значение булевой функции !true будет равно false
+ либо - Указывает на знак числа -123
++ Добавляет к числу единицу var i = 5; i++; //i будет равно 6
-- Отнимает от числа единицу var i = 5; i--; //i будет равно 4

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

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

Оператор Описание Пример
+ Cложение var i = 5; var k = 6; System.out.println(i + k)
- Вычитание var i = 5; var k = 6; System.out.println(i – k)
* Умножение var i = 5; var k = 6; System.out.println(i * k)
/ Деление var i = 5; var k = 6; System.out.println(i / k)
% Взятие по модулю var i = 15; var k = 6; System.out.println(i % k)

Если мы пишем сложное математическое выражение, то лучше пользоваться скобками:

var x = 5 * (6 – 8);

Завершить секцию с операторами я хотел бы таблицей с условными операторами, результат работы которых true либо false:

Оператор Описание Пример
== Сравнение на равенство var i = 5; var k = 6; System.out.println(i == k)
< либо <= Меньше, меньше либо равно var i = 5; var k = 6; System.out.println(i < k)
> либо >= Больше, больше либо равно var i = 5; var k = 6; System.out.println(i >= k)
&& Логическое И. В обоих частях должно быть true, чтобы оператор вернул true System.out.println(true && true)
|| Логическое ИЛИ. Хотя бы в одной части должно быть true, чтобы оператор вернул true System.out.println(false || true)

Мы познакомились с самыми популярными операторами в java, настало время их использовать.

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

Добавим новый метод, который будет это делать:

        void setSpeed(int speed){
   this.speed = speed;
}

    

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

Условные выражения

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

        void setSpeed(int speed) {
   if (speed < maxSpeed) {
       this.speed = speed;
   }
}

    

В круглых скобках мы помещаем условие, которое должно вернуть либо true либо false, а в фигурных мы добавляем тот код, который будет выполнен если условие правдиво.

Так же, если условие вернуло false мы можем добавить с помощью ключевого слова else еще один блок кода, который выполняем в таком случае:

        void setSpeed(int speed) {
   if (speed < maxSpeed) {
       this.speed = speed;
   }
   else {
       System.out.println("Вы передали слишком большую скорость");
   }
}

    

Циклы

Что делать, если мы хотим повторять какой-то блок кода много раз? Если вместо if написать while у нас получится самый простой цикл, который будет выполняться до тех пор пока выражение в круглых скобках истинно или не произойдет прерывания цикла с помощью ключевого слова break, либо пока программа не завершиться, например из другого потока. Выглядеть это будет так:

        public static void main(String[] args) {
   var myCar = new Car(100, 500);
   var i = 0;
   while (i < 10){
       myCar.start();
       i++;
   }
}

    

В данном случае, мы создаем некую примитивную переменную i изначально равную 0. В цикле у нас есть условие того, что она меньше 10, если это не так, то код в фигурных скобках выполняться не будет. В них же мы увеличиваем значение i на единицу, если бы мы этого не делали, то выполнялся бы цикл вечно. Запустите программу и посмотрите, что будет выведено на экран и сколько раз.

Для того, чтобы отделить ту логику работы цикла от нашего кода был создан цикл for.

Найдите различия:

        for (var i = 0; i < 10; i++) {
   myCar.start();
}

    

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

если не передать никаких условий, то получится бесконечный цикл:

        for( ; ; ) {
   System.out.println("Hello World");
}

    

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

        for( ; ; ) {
   System.out.println("Hello World");
   break;
}

    

Сообщение будет выведено на консоль и на этом месте цикл остановится.

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

В своем проекте мы создали только один автомобиль, давайте теперь создадим несколько и поместим их в какое-то хранилище.

        var myCar = new Car(100, 500);
var myCar2 = new Car(10, 50);
var garage = new Car[2];

garage[0] = myCar;
garage[1] = myCar2;

for (Car car : garage) {
   car.start();
}

    

В примере выше мы создали два автомобиля, потом создали массив из указателей на объекты класса Car размером 2 и положили в него указатели на наши объекты. Как видно, отсчет ячеек массива начинается с 0. После чего мы использовали специальную модификацию цикла for для массивов и коллекций, который позволяет пройтись по всем элементам и совершить с ними какую-то логику.

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

Перепишем наш код с помощью коллекций:

        var myCar = new Car(100, 500);
var myCar2 = new Car(10, 50);
var garage = new ArrayList<Car>();

garage.add(myCar);
garage.add(myCar2);

for (Car car : garage) {
   car.start();
}

    

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

Для объявления коллекции мы написали так:

        var garage = new ArrayList<Car>();
    

Что это значит? Здесь мы говорим, что будем использовать коллекцию на основе массива и что в нашей коллекции будут лежать объекты типа Car.

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

Коллекция типа HashSet (множество) не позволит положить в себя два одинаковых значения, сравните выводы:

        var garage = new HashSet<Car>();

garage.add(myCar);
garage.add(myCar);
garage.add(myCar2);

for (Car car : garage) {
   car.start();
}

    

А потом замените первую строку

        var garage = new ArrayList<Car>();

    

Кроме списка так же популярна коллекция Map, она позволяет присваивать объектам ключи и потом получать эти объекты по уникальному ключу:

        var myCar = new Car(100, 500);
var myCar2 = new Car(10, 50);
var garage = new HashMap<String, Car>();

garage.put("Мой авто", myCar);
garage.put("Мой второй авто", myCar2);

garage.get("Мой второй авто").start();

    

Я указал строку в качестве ключа – очень частая практика. И положил в коллекцию два своих объекта. Потом я по ключу достал свой объект и вызвал его метод.

Могу сказать, что коллекции – это то, чем вы будете пользоваться всегда в своей работе, поэтому их надо знать. Здесь я рассказал лишь о трех из них, которые используются в 99% случаев, при том не рассказав какие дополнительные методы они несут в себе.

Исключения

Чтобы произошло, если бы я написал свой запрос так:

        garage.get("второй авто").start();
    

То на консоли увидел бы следующее:

        Exception in thread "main" java.lang.NullPointerException
	at ru.proglib.Car.main(Car.java:42)

    

Возникла исключительная ситуация и программа прервала свою работу. Так как метод get вернул null о котором мы говорили ранее. Соответственно у нас не было объекта на котором мы могли бы вызвать метод. Для предотвращения таких ситуаций был придумал блок try/catch. Посмотрим как он помог бы решить нашу проблему:

        try {
   garage.get("второй авто").start();
}catch (NullPointerException exception){
   System.out.println("В словаре нет автомобиля с таким ключем");
}

    

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

Вывод

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

Дополнительные материалы:

  • Очень рекомендую ознакомится с заметкой ТОП-10 лучших книг по Java для программистов ( https://proglib.io/p/java-books-2019). В ней вы найдете поборку из прекрасных книг по java.
  • Если необходимо узнать какую-то информацию о бзовых возможностях java, то лучше официально документации ничего нет – https://docs.oracle.com/en/java/javase/16/
  • А если вам нужно усваивать материал через видео, то нет ничего лучше, чем лекции, которые читает Java Champion.

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Продуктовый аналитик
Екатеринбург, по итогам собеседования
Продуктовый аналитик в поддержку
по итогам собеседования
DevOps
Санкт-Петербург, от 150000 RUB до 400000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ