Пять задач, которые приходится решать при трудоустройстве начинающим Java-разработчикам в 2022 году

Хабр, привет! Меня зовут Даниил Пилипенко, я программный директор факультета backend-разработки направления «Программирование» Skillbox, директор центра подбора IT-специалистов SymbioWay. Сегодня разберём практические задачи, с которыми могут столкнуться Java-разработчики junior-уровня на собеседовании. Такого рода задачи мы часто видим на собеседованиях наших клиентов и коллег, а также сами даём соискателям.

a3481373075ce565f0a31c080e29727a.jpeg

Чего хочет работодатель

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

Примеры задач 

Задача 1. Написать код, выполняющий какую-то несложную задачу. Здесь может быть, например, классический FizzBuzz, задача на сжатие или переворачивание строки. Работодателю здесь важно понять, как соискатель владеет основами синтаксиса языка и может ли писать код сразу чисто. Удивительно, но многие кандидаты испытывают значительные сложности при выполнении задач такого рода.

Одна из задач, которую мы даём соискателям: написать код, который выводит числа от 0 до 1000, которые делятся на 3, но не делятся на 5, и сумма цифр в которых меньше десяти. Задача часто вызывает совершенно немыслимые трудности у людей, утверждающих, что их уровень middle или даже senior. Вот несколько примеров решений такой задачи кандидатами на собеседованиях:

//Пример 1
public class TestClass {

   public static final int MAX_LIMIT = 1000;

   public static void main(String[] args) {

       for(int i = 0; i < MAX_LIMIT; i++) {
           boolean enabled = true;
           if (i % 3 != 0) {
               enabled = false;
           }

           if (i % 5 == 0) {
               enabled = false;
           }

           if (!testSum(i)) {
               enabled = false;
           }

           if (enabled == true) {
               System.out.println("Число: " + i);
           }
       }
   }

   public static boolean testSum(int in) {
       int res = 0;
       while (in > 0) {
           res += in % 10;
           in = in / 10;
       }
       return res < 10;
   }
}
//Пример 2
public class Test {
   public static void main(String[] args) {
       for (int i = 0; i<1000; i ++) {
           if ( i%3 == 0 && i%5!= 0) {

           }
       }
   }

   public int returnNumber( int i) {
       int t;
           if (i >0){
           returnNumber(i/10);
          
       }
   }
}
//Пример 3
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class TestTask {
   public static List getForNumber() {
       List list = IntStream.range(0, 1000).boxed()
               .filter(n -> n % 3 == 0 && n % 5 != 0)
               .collect(Collectors.toList());
       return list;
   }

   public static int sumOfNubers(List list) {
       for (Integer n : list) {
           int summ;
           while(n>1) {
               summ += n % 10;
               n /= 10;
           }
       }
       return 0;
   }
}

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

С помощью задач такого типа мы проверяем уровень соискателя: как много он писал код сам, а также косвенно — сталкивался ли с «грязным» кодом и рефакторил ли его. По такому заданию можно косвенно увидеть, как быстро и «чисто» (понятно и поддерживаемо) специалист будет писать собственный код.

Задача 2. Создать веб-приложение на фреймворке Spring, которое будет соответствовать определённой спецификации. Спецификация может быть дана в текстовом виде или в виде отдельного Swagger-файла. Например, реализовать backend для анонимного онлайн-чата со следующей спецификацией:

  • GET /init — запрос инициализации, который по ID сессии отвечает, авторизован ли текущий пользователь или нет.

  • POST /auth — запрос, регистрирующий и авторизующий пользователя. Он добавляет пользователя в базу данных и привязывает к текущей сессии.

  • Формат запроса:

  • name — имя пользователя

  • sex — пол пользователя, M или F

  • Формат ответа:

    {
    
       "result": true
    
    }
  • GET /users — запрос, возвращающий список пользователей в чате в порядке от самого свежего, который зарегистрировался недавно, до самого старого.

  • Запрос без параметров

  • Формат ответа:

    {
       "result": true,
       "data": [
           {
               "id": 567,
               "name": "Alex Kurnikov",
               "male": "M"
           },
           ...
       ]
    }
    
  • GET /messages — запрос, возвращающий ленту сообщений чата от самого нового до самого старого с постраничной навигацией.

  • Формат запроса:

  • offset — сдвиг от самого последнего до самого раннего, от 0, по умолчанию равен 0

  • limit — количество сообщений, которые нужно вывести, по умолчанию — 100

  • Формат ответа:

    {
    
       "result": true,
    
       "count": 100,
    
       "data": [
    
           {
    
               "id": 46273,
    
               "time": "18:30 30.01.2022",
    
               "authorId": 567,
    
               "message": "Some HTML text, may me tagged"
    
           },
    
           ...
    
       ]
    
    }
  • POST /messages — запрос, создающий новое сообщение от имени текущего пользователя. Форматы запроса и ответа спроектируйте самостоятельно на ваше усмотрение на основе имеющейся документации.

С помощью таких задач мы и работодатели проверяют сразу несколько навыков:

  • умение правильно проектировать и создавать веб-приложения на фреймворке Spring в соответствии с принятой структурой;

  • понимание принципов клиент-серверного взаимодействия (знание HTTP и стандарта REST);

  • умение работать с ORM-системой (обычно Hibernate) и базой данной, умение проектировать структуру этой базы;

  • умение писать код на Java и владение базовыми навыками при работе с этим языком программирования, в частности, знание ООП, умение работать со строками и коллекциями, лямбда-выражениями и Stream API, привычку их применять при необходимости.

Задача 3. Написать SQL-запрос. Например, задача может быть такой. У вас есть две таблицы:

employee — сотрудники компании

  • id

  • department_id

  • work_start_date

  • name

  • salary

department — отделы, в которых работают сотрудники

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

Верным ответом на такую задачу будет запрос:

SELECT

   e.id,

   e.name

FROM employee e

LEFT JOIN department d ON d.id = e.department_id

WHERE

   e.work_start_date >= '2021-06-01' AND

   e.salary < 100000 AND

   d.id IS NULL

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

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

Задача № 4. Решить заданную проблему устно. Например, рассказать, как предотвращать взаимные блокировки (deadlock) в многопоточных приложениях, или объяснить, как код, работающий с коллекцией, сделать потокобезопасным.

Здесь обычно требуется сначала рассказать о самой проблеме — в чём она состоит и почему её вообще следует решать, а затем объяснить, как именно это можно сделать. И чем понятнее будет ваш рассказ и чем логичнее будут ваши рассуждения, тем лучше.

При этом не обязательно решать задачу до конца идеально. Того, что вы думаете в правильном направлении, будет вполне достаточно для засчитывания ответа как верного. Ведь в реальности невозможно знать и помнить всё, гораздо важнее умение находить решения, понимать, куда «копать», и ставить эксперименты.

Задача № 5. Задача или серия вопросов на понимание принципов и паттернов ООП. Например, создать класс, объект которого может быть представлен только в единственном экземпляре (по сути, реализовать паттерн Singleton).

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

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

Как интерпретируют результаты

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

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

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

Когда мы подбираем специалистов уровня junior в нашу команду или кому-то из работодателей, мы проверяем три блока навыков:

  • Понимание базовых концепций (синтаксис, коллекции, чистота кода, ООП, создание приложений на Spring, HTTP, REST и SQL).

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

  • Уровень обучаемости — скорость и качество запоминания и дальнейшего внедрения в свою работу best practices и рекомендаций, которые мы даём новичку.

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

© Habrahabr.ru