Java: fill the gaps
12.9K subscribers
7 photos
215 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Почему так?
Чтобы обеспечить однозначность и приоритет классов JDK. При такой схеме невозможно создать свой класс с именем java.lang.String.

Почему для классов JDK используется 2 загрузчика?
Bootstrap работает на уровне виртуальной машины и загружает необходимый минимум классов на старте приложения. Extension ClassLoader - java объект, который загружает дополнительные модули JDK по мере необходимости.

Почему для отложенной загрузки классов нужны разные ClassLoader?
Для безопасности классов JDK. Cуществует 4 модификатора доступа - private, protected, public и default. Последний ещё называют "доступ по умолчанию". Он даёт доступ к классам и методам того же пакета. Если мы назовём класс java.lang.Smth, он сможет работать с классами из пакета java.lang. Поэтому класс получает доступ к default полям другого класса только если:
🔸У него совпадает название пакета.
🔸Загружен тем же ClassLoader'ом.

JDK классы используют Bootstrap/Extension загрузчик, а наш java.lang.Smth - Application загрузчик, поэтому внутренние классы JDK недоступны классу Smth.

Что изменилось в java 9?
Схема взаимодействия загрузчиков осталась той же. Поскольку JDK библиотеки (rt.jar, tools.jar) теперь разбиты на модули, работа с ними поменялась:
1️⃣ Изменились внутренние классы и методы загрузчиков. Проекты, которые используют загрузчики классов напрямую, столкнулись с проблемами совместимости.
2️⃣ Extension ClassLoader переименован в Platform ClassLoader. Теперь он загружает классы из модулей JDK и сторонних библиотек, одобренных Java Community: JDBC, JMS, JAX-RS и т.д.
Домашний проект: от выбора темы до пункта в резюме.

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

Что писать?
Необязательно придумывать стартап, который изменит мир. Но если идей совсем нет:
1️⃣ Реализуйте с нуля текущий рабочий проект. Это очень удобно:
Знакомая предметная область
Понятные задачи
Можно обсудить с коллегами непонятные места и текущие решения

2️⃣ Если рабочий проект надоел или не нравится, напишите свою версию популярной системы: Tinder, Яндекс.Маркет, «Кто хочет стать миллионером?» и тд. Помните, Вы пишете не "очередной велосипед", а тренируетесь и изучаете лучшие практики.
Понятный функционал
Современные подходы и технологии
Детали реализации оригинала не всегда доступны, поэтому не с чем сравнить результат

Как работать над проектом?
Здесь Вы сами себе проджект-менеджер:
▫️Опишите функции до начала работы. Ориентируйтесь на текущие навыки и добавьте небольшой челлендж
▫️Декомпозируйте и отсортируйте задачи
▫️Составьте план
▫️Поставьте нестрогие дедлайны
▫️Двигайтесь от простого к сложному. Напишите основу на чистой java, добавьте maven/gradle, затем Spring, Spring Boot, базу данных и другие технологии
▫️Пишите тесты, это поможет найти узкие места
▫️Напишите эмулятор запросов, нагрузите приложение и изучите профайлеры

Нужно ли добавлять проект в резюме?
Как хотите🙂 Это большой бонус, если:
🔸Вы только начинаете карьеру разработчика.
🔸Домашний проект больше соответствует вакансии, чем текущее место работы.
🔸Вы хотите показать область своих интересов. Это полезно при собеседовании в компанию с большим количеством проектов.

Как подготовить проект к показу?
Прежде чем добавлять ссылку на проект в резюме, сделайте 2 несложных шага:
1️⃣ Добавьте файл README.md в корневую директорию. Иногда это единственное, на что посмотрит посторонний человек. Не пожалейте времени, тщательно опишите:
▪️О чём проект
▪️Основные функции
▪️Архитектуру (нарисуйте схему)
▪️Используемые технологии

2️⃣ Проработайте структуру проекта - дайте модулям и классам понятные имена. Функции, указанные в README, должно быть легко найти в коде.

Эти простые действия помогут сориентироваться в проекте и лучше оценить Вашу работу.
🥂
Forwarded from Java Developer
​​Java 25 лет!

Java, выпущенная в 1995 году под руководством Джеймса Гослинга, используется на 3 миллиардах устройств по всей планете, от мобильного телефона и PC, до холодильника и компьютера в автомобиле.

Более 10 миллионов разработчиков пишут на Java и других мощных языках, которые используют JVM: Kotlin, Scala, Groovy, Jython, JRuby, Clojure.

Поздравляю всех причастных: разработчиков, тестировщиков, менеджеров, аналитиков! Java — это сила!

Мы не знаем точно, что будет дальше в мире технологий, но Java явно войдет в историю. Быть причастным к этому и кодить на одном языке с массой крутых разработчиков очень приятно! С 25-летием, родная

@java_developer
Best practices: как проверять входные данные.

Запросы пользователей и ответы сторонних систем иногда приходят некорретными или в неправильном формате. Проверить данные можно по-разному, посмотрим разные варианты на простом примере:
String info(Request req) {
return req.toString(); }

Для нормальной работы параметр req не должен быть пустым. Мы можем:
1️⃣ Оставить как есть: тогда JVM выбросит NullPointerException(NPE)
2️⃣ Добавить проверку и самим бросить исключение:
if (req==null) throw new NPE();
3️⃣ Добавить проверку и вернуть null:
if (req==null) return null;
4️⃣ Добавить проверку и вернуть Optional.empty():
if (req==null) return Optional.empty();

Оценим их с точки зрения производительности и дизайна.

Начнём с производительности. Запустим каждый вариант миллион раз:
▪️14 мс - JVM исключение
▪️2 мс - явный выброс исключения
▪️0,03 мс - null
▪️0,03 мс - Optional.empty()

Что это значит?
1️⃣ Создание исключения гораздо дольше, чем выход из метода. Время уходит на создание стек-трейса - чем он глубже, тем хуже производительность. Стек-трейс из сервиса на Spring Boot собирается в 50-100 раз дольше, чем стек-трейс из метода main.

2️⃣ Если исключение явно создаётся в коде, JVM переиспользует ранее собранный стэк-трейс. Так получается выигрыш в 5-10 раз, но разница с вариантом без исключения всё ещё внушительна.

По этим цифрам легко решить, что исключения - удар по производительности. 10 лет назад была тенденция избегать исключений любой ценой. Например, возвращать из метода пару (результат, код_ошибки).

В 2020 это уже не актуально. Если взаимодействие в системе не построено на исключениях, то это капля в море относительно других операций. В примере сверху исключение создаётся миллион раз подряд - такого в продакшене обычно нет.
Исключения делают код чище:
🔹Понятное возвращаемое значение
🔹В части throws явно обозначены непредвиденные ситуации
🔹Логика метода и обработка ошибок разделены

Теперь сравним создание исключения и возврат null с точки зрения дизайна.
Исключение подразумевает обработку ошибки, например:
🔸Показать пользователю сообщение.
🔸Прекратить работу с файлом.
🔸Поменять формат данных.

Optional.empty(), null или объект по умолчанию возвращается, когда реагировать на ситуацию необязательно. Например, когда данные быстро устаревают. Возвращаемый тип Optional удобнее использовать: не нужно смотреть в документации, возвращает ли функция пустой результат, и как он обозначается: null, -1 или 0.
Что вернёт метод?
Что вернёт метод?
Anonymous Poll
26%
try
51%
finally
23%
Ошибка компиляции
Что вернётся из try-catch-finally?

Вопросы такого типа популярны на собеседованиях. Поведение try-catch-finally в спецификации описывается через 24 предложения "если". Логика запутанная, и легко допустить ошибку.

Чтобы решать такие задачки, воспользуйтесь следующей моделью:
Результатом блока try-catch-finally могут быть 3 варианта:
🔸Ничего
🔸Возврат значения
🔸Исключение
Представим, что результат записывается в переменную result. В начале работы там "ничего".

Блоки обрабатываются в строгом порядке:
1️⃣ try
2️⃣ catch
3️⃣ finally
На каждом шаге переменная result может быть перезаписана. Итоговое значение result и будет результатом выполнения. Особый случай — при вызове System.exit(0) выполнение прекращается сразу же .

Разберём пример из опроса:
▫️try установит результат на "вернуть try".
▫️Блока catch нет.
▫️В finally результат меняется на "вернуть finally".
Итог: вернётся "finally"

Другой пример:
try { throw new NPE(); }
catch (SecurityEx e)
{ return "ex"; }
finally {}

▫️В блоке try результатом станет "NPE".
▫️Блок catch пропускается, т.к нет подходящего типа исключения.
▫️В блоке finally результат не переопределяется.
Итог: выброс NPE.

Этот пример важен, потому что такие ошибки часто встречаются на практике. Блок finally - не оберег от исключений. Если результат "исключение" дальше не переопределён, он пробрасывается в вышестоящий метод.
Что вернёт метод?
Stream API: ускоряемся🚀

Stream API помогает писать выразительный код, который быстро работает. Несложными действиями можно повысить скорость ещё больше:

1️⃣ Добавить parallel().
Для поддержки параллельности нужны дополнительные ресурсы, и прирост скорости заметен только если в коллекции много элементов. Если:
N – количество элементов,
Q – количество операций над каждым элементом,
то при N*Q>10000 можно смело добавлять parallel()

2️⃣ Добавить или удалить sorted(), unordered().
🔸sorted()
: отсортировать стрим и добавить свойство «отсортирован».
🔸unordered(): выставить свойство «порядок не важен».
Источник данных и методы sorted, unordered, dictinct,... определяют свойства стрима: конечный размер, уникальность, отсортированные значения и другие. Благодаря этому некоторые методы оптимизируют работу. Если порядок не важен, параллельная обработка будет быстрее. Если список уже отсортирован, удаление повторяющихся элементов distinct() займёт меньше времени. Иногда наоборот — дополнительные ограничения замедляют работу.
Другие специфичные примеры.

3️⃣ Пользоваться специальными методами.
Чем меньше операций, тем быстрее всё работает. Проверить, что ни один элемент не удовлетворяет условию можно так:
filter(...).findFirst().isPresent()
а можно короче, понятнее и быстрее:
noneMatch(...)

Иногда стримы даже не нужны, много полезных методов есть в интерфейсе Collections. Например, максимальный элемент в списке быстрее найти через Collections.max(...), чем с использованием стрима.
Больше примеров

4️⃣ Объединить однотипные операции.
Чаще это касается сложной фильтрации:
filter(1).filter(2).filter(3)
можно ускорить в 2-4 раза, если объединить условия:
filter(1 & 2 & 3)
3
IntelliJ IDEA: быстрый поиск.

Двойное нажатие Shift - универсальный шорткат для поиска. Он поможет:
Найти класс, метод или файл.
Открыть настройки: Tab Size, Code style, ...
Выполнить команду:
Optimize imports,
Push
(в репозиторий),
Show history,
Rename,
Presentation Mode

...и многие другие. Просто нажмите Shift-Shift, начните набирать слово и выберите подходящий вариант из списка.
👍21
Скомпилируется ли этот код?
Система типов и проверка аргументов.

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

Первый вопрос: когда происходит проверка? В зависимости от ответа типизация может быть:
🔸Cтатической
🔸Динамической

При статической тип указателя навсегда закрепляется при создании:
String str;
Ассоциированный объект имеет тот же тип или производный. Т.к типы известны заранее, проверка аргументов происходит во время компиляции.
Отлавливается много ошибок.
Быстро работает в рантайме.
Предсказуемое поведение.
Легко тестировать.

При динамической типизации указатель не имеет типа. Во время работы программы в переменную можно записать любые значения:
value = "а"
value = 4
Аргументы проверяются во время вызова метода. Такие программы в разы короче и легко адаптируются к изменениям.

Java является языком со статической типизацией.

Второй вопрос, на который отвечает система типов: что делать, если метод принимает тип А, но приходит объект типа Б? Можно:
▪️Бросить ошибку.
▪️Преобразовать Б в тип А.

У языка сильная типизация, если допустимо мало преобразований. Если много, то типизация слабая. Речь идёт о неявных преобразованиях, которые делает компилятор или исполняющая среда.
Пример: 5L + 1
Язык Ruby не разрешает складывать числа разных типов, а java приведёт единицу к типу long и выполнит сложение. Выражение
5 + true
в java вызовет ошибку компиляции. JavaScript спокойно переведёт true в единицу.
Компилятор java может перевести:
- подкласс в суперкласс
- long в int и наоборот
- int в Integer и наоборот
и так далее. Некоторые правила работают в одних условиях, некоторые в других. В целом java считается языком с сильной типизацией.

Вернёмся к вопросу перед постом. Метод id ожидает аргумент типа Long, а передаётся примитивный int. В правилах преобразований нет перехода int → Long, поэтому будет ошибка компиляции. Достичь цели можно в 2 шага:
int → long
long → Long
Компилятор может выполнить только один шаг, второе преобразование делает программист. Поэтому подходящий ответ:
(long) intValue
Java 8-11: новые методы String.

Чтобы не писать велосипеды и не добавлять в проект лишние библиотеки, посмотрите на новые методы в классе String:

🔸Соединить строки через разделитель:
String.join("-","1","2");
// 1-2
Можно использовать со списком строк:
List<String> list = ...;
String.join("-", list);

🔸Убрать пробелы и служебные символы
▪️с начала строки:
str.stripLeading();
▪️в конце строки:
str.stripTrailing();
▪️с обеих сторон:
str.strip();
В классе уже присутствует метод trim(), который тоже стирает неподходящие символы. strip() корректнее определяет недопустимые символы, в том числе экзотические виды пробелов ('\u00A0', '\u2007', '\u202F').

🔸Проверить, что строка пуста:
str.isBlank()
На замену StringUtils.isBlank() из библиотеки Apache Commons.

🔸Создать стрим из строки. Можно разделить текст
▪️по линиям: str.lines()
▪️по символам: str.chars()

🔸Продублировать строку:
"<td>".repeat(3);
// <td><td><td>
Изучаете новую технологию? Не забывайте про контекст.

В интернете полно статей о языках программирования, фреймворках и библиотеках. Большинство из них показывают синтаксис на простых, но часто нереалистичных примерах. Этой информации хватает для собеседований, докладов и расширения кругозора. Однако для продуктивной работы важен не только синтаксис, но и контекст использования. Он включает в себя ответы на вопросы:
🔷Какую проблему решает Х?
🔷Какие альтернативы для решения этой же проблемы?
🔷В каких условиях Х лучше, чем альтернативы?
🔷За счёт чего Х лучше?

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

Рассмотрим пример. Любой курс по многопоточности начинается с понятия потока и изучения класса Thread. Обычно автор описывает жизненный цикл потока, доступные методы и показывает пример создания потока с интерфейсами Runnable и Callable.

Можно ли на основе только этих знаний решать задачи? Можно, но вряд способ будет оптимальным. Посмотрим на класс Thread поближе и попробуем ответить на вопросы выше:

Какие задачи можно решать в отдельных потоках?
🔸Обработка большого количества данных
🔸Слабосвязанные подзадачи

Есть ли альтернативы для этих задач?
Обрабатывать данные можно с помощью ForkJoinPool. Для выполнения подзадачи в другом потоке есть ExecutorService. Можно использовать виртуальные потоки: Kotlin continuations, библиотеку Quasar и другие.

Когда ручное управление потоком лучше, чем варианты выше?
Сложно сказать. ForkJoinPool и ExecutorService эффективнее используют ресурсы и предоставляют высокоуровневый интерфейс, поэтому ими легко пользоваться. Виртуальные потоки лучше работают с маленькими задачами и блокирующими вызовами.

Класс Thread появился в java 1.2. Это низкоуровневый инструмент, в 2020 году с ним решаются только очень специфичные задачи. Для популярных задач появилось много специализированных классов и фреймворков.

Обзорные статьи и доклады - это стартовые точки при изучении технологий. Нужно двигаться к пониманию, как и когда это применять на практике. Как это сделать? Лёгкого пути нет, нужно по крупицам собирать общую картину из разных источников и, конечно, читать посты на @java_fillthegaps💪
👍6
Что из нижеперечисленного вызовет ошибку компиляции?