Source Code
197 subscribers
30 photos
3 files
80 links
Download Telegram
#java

Что такое атомарность в Java?

Как можно определить атомарность?

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

При использовании метода nonAtomic существует вероятность того, что какой-то поток обратится к array[0] в тот момент, когда array[0] не проинициализирован, и получит неожиданное значение. При использовании probablyAtomic (при том условии, что массив сначала заполняется, а уже потом присваивается. Такого быть не должно: array всегда содержит либо null, либо проинициализированный массив, но в array[0] не может содержаться что-то, кроме 1. Эта операция неделима, и она не может примениться наполовину, как это было с nonAtomic - только либо полностью, либо никак, и весь остальной код может спокойно ожидать, что в array будет либо null, либо значения, не прибегая к дополнительным проверкам.

Кроме того, под атомарностью операции зачастую подразумевают видимость ее результата всем участникам системы, к которой это относится (в данном случае - потокам); это логично, но, на мой взгляд, не является обязательным признаком атомарности.

Почему это важно?

Атомарность зачастую проистекает из бизнес-требований приложений: банковские транзакции должны применяться целиком, билеты на концерты заказываться сразу в том количестве, в котором были указаны, и т.д.
#Spring #backEnd #java
Spring: @Component против @Bean

@Component и @Bean делают две совершенно разные вещи, и их не следует путать.

@Component (а также @Service и @Repository ) используются для автоматического обнаружения и автоматической настройки бобов с помощью сканирования classpath. Существует неявное отображение one-to-one между аннотированным классом и Бобом (то есть один боб на класс). Управление проводкой при таком подходе довольно ограничено, поскольку оно носит чисто декларативный характер.

@Bean используется для явного объявления одного компонента, а не позволяет Spring делать это автоматически, как описано выше. Он отделяет объявление Боба от определения класса.
#java
Немного о наследовании и ООП

Какие методы можно переопределять?
- Все методы, которые не являются static, final или private.

this - ссылка на текущий объект
super - не то же что и this. Это ключевое слово всего лишь уведомляет компилятору, что нужно вызвать из суперкласса.
keyword super означает обращение к объекту родительского класса.

С помощью keyword super можно обращаться как к методам, так и к переменным родительского класса.

Невозможно использование super в static методах и переменных.

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

При обращении к методу родительского класса, выражение super не обязательно должно быть первой строкой тела метода.

❗️ Конструктор - инициализатор, некий набор инструкций, который для вновь созданного объекта задает некоторое начальное состояние.
Ключевое слово new - вызывается конструктор, который инициализирует поля.
Конструктор не является членом класса, то есть он не наследуется, по этой причине, мы и используем ключевое слово super для его вызова из суперкласса.
К любому члену класса мы можем обратиться через "точку", к полям, к методом. К конструктору мы обратиться не сможем.

Возможно будет продолжение в сторону Reflection API. Кому интересно, накидайте лайков.
#java
Кастинг ссылочных типов данных

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

Кастинг из дочернего класса в супер класс происходит автоматически - Upcasting.
Кастинг из супер класса в дочерний класс НЕ происходит автоматически - Downcasting.

Если между классами/интерфейсами нет IS-A взаимоотношения, компилятор не допустит кастинг.

Даже если компилятор допустил кастинг, выскочит runtime exception, если объект, который мы делаем каст на самом деле не принадлежит классу, на который мы его делаем каст.
#java
О методе equals()

Если вы перезаписываете метод equals, всегда используйте в его параметре тип данных Object.

Правильно перезаписанный метод equals должен обладать следующими свойствами:
Симметричность - для non-null ссылочных переменных a и b, a.equals(b) возвращает true тогда и только тогда, когда b.equals(a) возвращает true.

Рефлексивность - для non-null ссылочных типов переменной a, a.equals(a) всегда должно возвращать true.

Транзитивность - для non-null ссылочных переменных a, b, c если a.equals(b) и b.equals(c) возвращает true, то a.equals(c) тоже должно возвращать true.

Постоянство - для non-null ccылочных переменных переменных a и b, неоднократный вызов a.equals(b) должен возвращать или только true или только false.

Для non-null ссылочной переменной a, a.equals(null) всегда должно возвращать false.
#java
Non-access modifiers

transient. Переменные класса с ключевым словом transient не сериализуются.

native. Методы с ключевым словом native реализованы не на Java. В своем описании они не имеют тела и заканчиваются как абстрактные методы символом ";".

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

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

strictfp. Ключевое слово strictfp в методах и классах ограничивает точность вычислений с float и double по стандарту IEEE.
#java
Checked/Unchecked exceptions
Раньше уже был пост на эту тему, но в этом попытаюсь объяснить более простыми словами + дополнить + повторение материала не помешает)

Сабклассы RuntimeExceptions = unchecked exceptions
Runtime исключения бывают в коде, в котором присутствуют ошибочные выражения. Т.е. в выбросе данных исключений виноват программист. Компилятор НЕ в состоянии проверить возможность выброса runtime исключений.
❗️Runtime исключения можно не объявлять и не обрабатывать, но при желании, можно сделать и то, и другое.

Сабклассы Exceptions = checked exceptions
Checked исключения указывают на часть кода, которая находится за пределами непосредственного контроля программой и которая может являться причиной выброса исключений. Они, как правило, возникают при взаимодействии вашей программы с внешними источниками (работа с файлами, с БД, с сетью), из-за которых могут возникать проблемы. Компилятор всегда проверяет возможность выброса checked исключений.
❗️Checked исключения всегда должны быть или объявлены и/или обработаны.
#java
Важные моменты в исключениях

Очередность catch блоков очень важна. Компилятор не пропустит код, если исключение "супер класс" будет стоять перед исключением "дочерний класс".

Если в части кода, которая не находится в блоке try или в блоке try выбрасывается исключение, то соответствующая оставшаяся часть кода уже не обрабатывается.

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

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

finally блок выполняется в том случае, если в try блоке или в catch блоке имеется return statement.

finally блок не выполнится только в том случае, если вы прекращаете работу программы с помощью System.exit в try или в catch блоке или же, если происходит "крушение" JVM или, например, операционной системы.

Если return statement имеется и в catch блоке и в finally блоке, то аутпутом метода будет возвращаемое значение из finally блока.

(1) Про это позже будет пост
#java
Встроенные функциональные интерфейсы

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

https://telegra.ph/Vstroennye-funkcionalnye-interfejsy-06-11
#java #design
Почему композиция лучше наследования?

GoF
: «Предпочитайте композицию наследованию класса».

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

Проблема хрупкого базового класса

Одним из основных критериев хорошей архитектуры является слабое зацепление (loose coupling), которое характеризует степень взаимосвязи между программными модулями. Не зря слабое зацепление входит в перечень паттернов GRASP, описывающих базовые принципы для распределения ответственности между классами.

Слабое зацепление имеет массу преимуществ!

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

Появляется возможность параллельной разработки слабозацепленных модулей без риска нарушить их функционирование.

Логика работы класса становится более очевидной, легче становится использовать класс правильно и по назначению, и сложно использовать – неправильно.
Java_Core_Slides.pdf
1.1 MB
#java

PDF файл со всей теорией Java Core
#java #testing
Для чего вообще нужно тестирование?

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

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

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

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

Ну вот я проверил, что все правильно работает, для чего мне оставлять тысячи линий кода в проекте, если все и так работает?
Тесты часто вам помогают убедиться, что новая фича ничего старого не поломала. При хорошем деплойе проходят все тесты и, если что-то пойдет не так, то поблагодарите тестам, что это не случилось на проде и к вам домой не едет CEO.

Пишите много тестов.
#java
Сохранение данных в постоянной памяти, используя Preferences.

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

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

Вот пример кода (Читайте комментарии):
import java.util.prefs.Preferences;

class PreferenceTest {
//Created node for storage
private final Preferences prefs = Preferences.userRoot().node(this.getClass().getName());

public void setPreference() {
//Keys
final String key1 = "key1";
final String key2 = "key2";
final String key3 = "key3";

//Here we define variables with default values
System.out.println(prefs.getBoolean(key1, true));
System.out.println(prefs.get(key2, "Hello World"));
System.out.println(prefs.getInt(key3, 50));

prefs.putBoolean(key1, false);
prefs.put(key2, "Bye bye");
prefs.putInt(key3, 0);

//You should write key and default value for getting present value
System.out.println(prefs.getInt(key3, 50));

prefs.remove(key1);
}

public static void main(String[] args) {
PreferenceTest test = new PreferenceTest();
test.setPreference();
}
}

Запустите программу дважды. Значение «key1» должно быть записано со значением по умолчанию (true) в командную строку, так как значение preference было удалено в конце метода. Значение «key2» и «key3» должно было измениться после первого вызова.

Useful link - https://spec-zone.ru/RU/Java/Docs/7/api/java/util/prefs/Preferences.html