Java
- Single Responsibility Principle (Принцип единственной обязанности)
- Open Closed Principle (Принцип открытости/закрытости)
- Liskov’s Substitution Principle (Принцип подстановки Барбары Лисков)
- Interface Segregation Principle (Принцип разделения интерфейса)
- Dependency Inversion Principle (Принцип инверсии зависимостей)
Стратегия (англ. Strategy) — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляциикаждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путём определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.
Наблюдатель (англ. Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents). Создает механизм у класса, который позволяет получать экземпляру объекта этого класса оповещения от других объектов об изменении их состояния, тем самым наблюдая за ними[2].
Список, реализованный на основе массива.
Доступ к произвольному элементу по индексу за постоянное время. Минимум расходов на хранение. Доступ к элементу по индексу —> одно время. Запись при расширении массива значительно больше. При превышении размера массива создаётся новый с размером (n * 3) / 2 + 1. Недостаток — вставка / удаление в середину списка. В 90% случаев быстрее и экономичнее. Сжатие размера — trimToSize();
LinkedList.
Классический связный список, основанный на объектах с ссылками между ними.
Доступ к любому элементу осуществляется за линейное время, доступ к первому и последнему — за константное. Предпочтителен, когда идёт вставка / удаление в середине списка.
HashMap
Состоит из баскетов (корзин). Одна корзина — одна пара ключ-значение. При добавлении вычисляется hashcode ключа, на основании которого вычисляется номер корзины. Если такой ключ уже есть — он заменяется. CRUD за константное время. Изначальное количество корзин — 16. Максимальный диапазон int.
HashSet и TreeSet
Множества, в которых хранятся значения. В TreeSet они упорядочены в виде красно-чёрного дерева. В HashSet ключом выступает сам элемент. При добавлении в TreeSet нескольких элементов по порядку они будут распределены по дереву, которое само себя балансирует.
Виды:
- Классы-члены
- Локальные классы
- Анонимные классы
Внутренние классы инкапсулируют в себе какие-то функции, например, поиск пользователя по параметрам.
Классы-члены.
Пример работы и объявления класса:
Users.Query query = users.createQuery(); query.setCreationDate(date); List users = query.list();
Если конструктор public, то
Users users = new Users(); Users.Query query = users.new Query();
Локальные классы.
Локальные классы (local classes) определяются в блоке Java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно внутри статических и нестатических блоков инициализации.
Пример:
public class LocalClasses { public static void main(String[] args) { //локальный класс Lists lists = new Lists(); } }
Анонимные классы.
Класс без имени, объект без ссылки на него.
list.add(new Integer(10));
Использование анонимных классов оправдано во многих случаях, в частности когда:
- тело класса является очень коротким;
- нужен только один экземпляр класса;
- класс используется в месте его создания или сразу после него;
- имя класса не важно и не облегчает понимание кода.
Java Heap (куча) используется Java Runtime
для выделения памяти под объекты и JREклассы. Создание нового объекта также происходит в куче. Здесь работает сборщик мусора: освобождает память путем удаления объектов, на которые нет каких-либо ссылок. Любой объект, созданный в куче, имеет глобальный доступ и на него могут ссылаться с любой части приложения.
Стэк
Стековая память в Java работает по схеме LIFO (Последний-зашел-Первый-вышел). Всякий раз, когда вызывается метод, в памяти стека создается новый блок, который содержит примитивы и ссылки на другие объекты в методе. Как только метод заканчивает работу, блок также перестает использоваться, тем самым предоставляя доступ для следующего метода.
Размер стековой памяти намного меньше объема памяти в куче.
- Куча используется всеми частями приложения в то время как стек используется только одним потоком исполнения программы.
- Всякий раз, когда создается объект, он всегда хранится в куче, а в памяти стека содержится ссылка на него. Память стека содержит только локальные переменные примитивных типов и ссылки на объекты в куче.
- Объекты в куче доступны с любой точки программы, в то время как стековая память не может быть доступна для других потоков.
- Управление памятью в стеке осуществляется по схеме
LIFO
. - Стековая память существует лишь какое-то время работы программы, а память в куче живет с самого начала до конца работы программы.
- Мы можем использовать
-Xms
и-Xmx
опции JVM, чтобы определить начальный и максимальный размер памяти в куче. Для стека определить размер памяти можно с помощью опции-Xss
. - Если память стека полностью занята, то Java Runtime бросает
java.lang.StackOverflowError
, а если память кучи заполнена, то бросается исключениеjava.lang.OutOfMemoryError: Java Heap Space
. - Размер памяти стека намного меньше памяти в куче. Из-за простоты распределения памяти (
LIFO
), стековая память работает намного быстрее кучи.
Пример работы стека:
public class StackHeap { public static void main(String[] args) { System.out.println("Цикл i вошёл в стек"); for (int i = 0; i < 5; i++) { System.out.println("Цикл j вошёл в стек"); for (int j = 0; j < 5; j++) { System.out.print(j + " "); } System.out.println(); System.out.println("Цикл j вышел из стека"); } System.out.println("Цикл i вышел из стека"); } }
Типы памяти:
Eden Space — все создаваемые объекты.
Survival Space — пережили хотя бы одну уборку.
Tenured (Old) Generation (heap) — Здесь скапливаются долгоживущие объекты (крупные высокоуровневые объекты, синглтоны, менеджеры ресурсов и проч.). Когда заполняется эта область, выполняется полная сборка мусора (full, major collection), которая обрабатывает все созданные JVM объекты.
Типы ссылок:
Strong Reference — все ссылки, созданные обычным путём. Пока есть ссылка, объект не будет удалён.
Soft Reference — объект удаляется GC при нехватке памяти.
SoftReference sApi = new SoftReference(new StreamApi());
Weak Reference — объект удаляется при ближайшем запуске GC.
Phantom Reference — если GC видит что объект доступен только через цепочку phantom-ссылок, то он его удалит из памяти. После нескольких запусков GC. Альтернатива финализации. После фантомизации объекта он проживёт как повезёт.
finalize() — метод, который помогает при сборке мусора. Метод, который вызывается перед тем, как объект будет уничтожен сборщиком мусора.Не должен быть использован для освобождения ресурсов вне оперативной памяти, потому что Java имеет лимитированное количество этих ресурсов.
public class ExampleClass{ private MyResource myRes; protected void finalize(){ if (null != myRes){ myRes.close(); } } }
Переопределение используется тогда, когда вы переписываете (переделываете, переопределяете) УЖЕ существующий метод.
Исключения — не пробрасываются.
Перегрузка (overload)
Перегрузка метода заключается в следующем — вы создаете метод с таким же именем, но с другим набором параметров. Например, в классе может быть несколько методов с названием summa, но с разным набором парметров.
Исключения — пробрасываются.
- Аннотации, применяемые к исходному коду:
-
- @Override — проверяет, переопределён ли метод. Вызывает ошибку компиляции, если метод не найден в родительском классе или интерфейсе;
- @Deprecated — отмечает, что метод устарел и не рекомендуется к использованию. Предполагается, что по каким-то причинам этот метод пока оставлен, но будет удалён в будущих версиях. Вызывает предупреждение компиляции, если метод используется;
- @SuppressWarnings — указывает компилятору подавить предупреждения компиляции, определённые в параметрах аннотации;
- @SafeVarargs — указывает, что никакие небезопасные действия, связанные с параметром переменного количества аргументов, недопустимы. Применяется только к методам и конструкторам с переменным количеством аргументов, которые объявлены как static или final.
- Аннотации, применяемые к другим аннотациям:
-
- @Retention — определяет, как отмеченная аннотация может храниться — в коде, в скомпилированном классе или во время работы кода;
- @Documented — отмечает аннотацию для включения в документацию;
- @Target — отмечает аннотацию как ограничивающую, какие элементы аннотации могут быть к ней применены;
- @Inherited — отмечает, что аннотация может быть расширена подклассами аннотируемого класса;
Получение аннотации:
Class<?> c = ob.getClass(); Method m = c.getMethod("myMeth"); My anno = m.getAnnotation(My.class);
Собственные аннотации:
@interface My{ String str(); int val(); }
//Аннотирование метода. @My(str = "Пример аннотации", val = 100) public static void myMeth() { // ...
//без дженерика List list = new ArrayList(); list.add(10); String s = (String) list.get(0); //ClassCastException System.out.println(s); //с дженериком List list1 = new ArrayList<>();
WildCards:
List<?> list2 = Arrays.asList(new User("Stepan", 10), new NewUser("Ivan", 20), 10); list2.forEach(System.out::println);
List<? extends User> list2 = Arrays.asList(new User("Stepan", 10), new NewUser("Ivan", 20)); list2.forEach(System.out::println);
Поддержка Аспектно-Ориентированного Программирования.
Аспе́ктно-ориенти́рованное программи́рование (АОП) — парадигма программирования, основанная на идее разделения функциональности для улучшения разбиения программы на модули.
В стандартном коде для создания класса используется создание объекта из Java-кода, например:
Cat cat = new Cat();
Spring создаёт бин (POJO, объект) из внешнего файла, таким образом, управление классами происходит не из Java-кода, а из внешнего xml-файла. Это и есть IoC. Зависимости (бины, их свойства) внедряются из внешних файлов — это и есть Dependency Injection.
volatile проще, нежели синхронизация и подходит только для контроля доступа к одиночному экземпляру или переменной примитивного типа: int, boolean… Когда переменная объявлена как volatile, любая запись её будет осуществляться прямо в память, минуя кеш. Также как и считываться будет прямо из памяти, а не из всевозможного кеша. Это значит, что все потоки будут «видеть» одно и то же значение переменной одновременно.
JavaScript. TypeScript.
Метод bind() используется преимущественно для того, чтобы вызвать функцию с явным указанием значения this. Другими словами, bind() позволяет нам указать, ссылка на какой объект будет значением this, когда функция будет вызвана, и вызвать эту функцию.
var users = { data: [ {name: 'John Smith'}, {name: 'Ellen Simons'} ], showFirst: function (event) { console.log(this.data[0].name); } } $("button").click(users.showFirst.bind(users)); //даём понять, что в console.log(this.data[0].name); this = users, а не "button", как было бы без bind();
Каррирование — инструмент, позволяющий вызывать функцию с меньшим количеством параметров.
// Определим функцию от трех переменных function greet(gender, age, name) { // if a male, use Mr., else use Ms. var salutation = gender === "male" ? "Mr. " : "Ms. "; if (age > 25) { return "Hello, " + salutation + name + "."; } else { return "Hey, " + name + "."; } } // C помощью bind() мы можем получать функции от меньшего числа переменных var greetAnAdultMale = greet.bind(null, "male", 45); greetAnAdultMale("John Hartlove"); // "Hello, Mr. John Hartlove." var greetAYoungster = greet.bind(null, "", 16); greetAYoungster("Alex"); // "Hey, Alex." greetAYoungster("Emma Waterloo"); // "Hey, Emma Waterloo."
call() и apply()
Методы apply() и call() — два метода объекта Function, которые позволяют явно установить значение this для функции.
function.call(object,…)
== — сравнение через числовое приведение типа.
alert( null > 0 ); // false, т.к. null преобразовано к 0 alert( null >= 0 ); // true, т.к. null преобразовано к 0 alert( null == 0 ); // false, в стандарте явно указано, что null равен лишь undefined
Также: значения null
и undefined
при ==
равны друг другу и не равны ничему ещё. А при операторах больше/меньше происходит приведение null
к 0
, а undefined
к NaN
.
Строки сравниваются по значениям unicode.
alert( 'а' > 'Я' ); // true
use strict добавляется вначале скрипта для всего кода и вначале функции для функции.
- Все переменные и параметры функций являются свойствами объекта переменных
LexicalEnvironment
. Каждый запуск функции создает новый такой объект. На верхнем уровне им является «глобальный объект», в браузере –window
. - При создании функция получает системное свойство
[[Scope]]
, которое ссылается наLexicalEnvironment
, в котором она была создана. - При вызове функции, куда бы её ни передали в коде – она будет искать переменные сначала у себя, а затем во внешних
LexicalEnvironment
с места своего «рождения».
console.log(foo); // undefined var foo = "Tom";
var c = a * b; var a = 7; var b = 3; console.log(c); // NaN
let — переменная, область видимости — только {…}, до объявления их нет.
const — аналог переменной final.
pending
(«ожидание»), затем – одно из: fulfilled
(«выполнено успешно») или rejected
(«выполнено с ошибкой»).
На promise
можно навешивать коллбэки двух типов:
onFulfilled
– срабатывают, когдаpromise
в состоянии «выполнен успешно».onRejected
– срабатывают, когдаpromise
в состоянии «выполнен с ошибкой».
Способ использования, в общих чертах, такой:
- Код, которому надо сделать что-то асинхронно, создаёт объект
promise
и возвращает его. - Внешний код, получив
promise
, навешивает на него обработчики. - По завершении процесса асинхронный код переводит
promise
в состояниеfulfilled
(с результатом) илиrejected
(с ошибкой). При этом автоматически вызываются соответствующие обработчики во внешнем коде.
var sum = (num1, num2) => num1 + num2; // эквивалент var sum = function(num1, num2) { return num1 + num2; };