Java

  1. Single Responsibility Principle (Принцип единственной обязанности)
  2. Open Closed Principle (Принцип открытости/закрытости)
  3. Liskov’s Substitution Principle (Принцип подстановки Барбары Лисков)
  4. Interface Segregation Principle (Принцип разделения интерфейса)
  5. Dependency Inversion Principle (Принцип инверсии зависимостей)

6eec38

Model-View-Controller (MVC, «Модель-Представление-Контроллер», «Модель-Вид-Контроллер») — схема разделения данных приложения, пользовательского интерфейса и управляющей логики на три отдельных компонента: модель, представление и контроллер — таким образом, что модификация каждого компонента может осуществляться независимо[1].

Стратегия (англ. Strategy) — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляциикаждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путём определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.

Наблюдатель (англ. Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents). Создает механизм у класса, который позволяет получать экземпляру объекта этого класса оповещения от других объектов об изменении их состояния, тем самым наблюдая за ними[2].

ArrayList

Список, реализованный на основе массива.

Доступ к произвольному элементу по индексу за постоянное время. Минимум расходов на хранение. Доступ к элементу по индексу —> одно время. Запись при расширении массива значительно больше. При превышении размера массива создаётся новый с размером (n * 3) / 2 + 1. Недостаток — вставка / удаление в середину списка. В 90% случаев быстрее и экономичнее. Сжатие размера — trimToSize();

LinkedList.

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

Доступ к любому элементу осуществляется за линейное время, доступ к первому и последнему — за константное. Предпочтителен, когда идёт вставка / удаление в середине списка.

HashMap

Состоит из баскетов (корзин). Одна корзина — одна пара ключ-значение. При добавлении вычисляется hashcode ключа, на основании которого вычисляется номер корзины. Если такой ключ уже есть — он заменяется. CRUD за константное время. Изначальное количество корзин — 16. Максимальный диапазон int.

HashSet и TreeSet

Множества, в которых хранятся значения. В TreeSet они упорядочены в виде красно-чёрного дерева. В HashSet ключом выступает сам элемент. При добавлении в TreeSet нескольких элементов по порядку они будут распределены по дереву, которое само себя балансирует.

Вложенные классы (Nested Class) — статические вложенные классы. Внутренние классы (Inner Class) — подмножество Nested Class, нестатические вложенные классы.

Виды:

  • Классы-члены
  • Локальные классы
  • Анонимные классы

Внутренние классы инкапсулируют в себе какие-то функции, например, поиск пользователя по параметрам.

Классы-члены.

Пример работы и объявления класса:

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();
      }
  }
}
Переопределение (override)

Переопределение используется тогда, когда вы переписываете (переделываете, переопределяете) УЖЕ существующий метод.

Исключения — не пробрасываются.

Перегрузка (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);
Dependency Injection — имплементация принципа Inversion of Control — когда объекты не создают свои зависимости, а получают их.

Поддержка Аспектно-Ориентированного Программирования.

Аспе́ктно-ориенти́рованное программи́рование (АОП) — парадигма программирования, основанная на идее разделения функциональности для улучшения разбиения программы на модули.

В стандартном коде для создания класса используется создание объекта из Java-кода, например:

Cat cat  = new Cat();

Spring создаёт бин (POJO, объект) из внешнего файла, таким образом, управление классами происходит не из Java-кода, а из внешнего xml-файла. Это и есть IoC. Зависимости (бины, их свойства) внедряются из внешних файлов — это и есть Dependency Injection.

synchronized имеет два важных момента: это гарантия того, что только один поток выполняет секцию кода в один момент времени (взаимоисключение или mutex), и также гарантия того, что данные, изменённые одним потоком, будут видны всем другим потокам (видимость изменений).

volatile проще, нежели синхронизация и подходит только для контроля доступа к одиночному экземпляру или переменной примитивного типа: int, boolean… Когда переменная объявлена как volatile, любая запись её будет осуществляться прямо в память, минуя кеш. Также как и считываться будет прямо из памяти, а не из всевозможного кеша. Это значит, что все потоки будут «видеть» одно и то же значение переменной одновременно.

JavaScript. TypeScript.

bind();

Метод 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
Strict Mode — нововведение, применённое в 5 версии ECMAScript, уберегающая разработчика от опасных частей языка и снижающая вероятность ошибки.

use strict добавляется вначале скрипта для всего кода и вначале функции для функции.

919885af

Замыкание – это функция вместе со всеми внешними переменными, которые ей доступны.

  1. Все переменные и параметры функций являются свойствами объекта переменных LexicalEnvironment. Каждый запуск функции создает новый такой объект. На верхнем уровне им является «глобальный объект», в браузере – window.
  2. При создании функция получает системное свойство [[Scope]], которое ссылается на LexicalEnvironment, в котором она была создана.
  3. При вызове функции, куда бы её ни передали в коде – она будет искать переменные сначала у себя, а затем во внешних LexicalEnvironment с места своего «рождения».
При первом проходе компилятор получает все объявления переменных, все идентификаторы. При этом никакой код не выполняется, методы не вызываются. При втором проходе собственно происходит выполнение.

console.log(foo);   // undefined
var foo = "Tom";

 

var c = a * b;
var a = 7;
var b = 3;
console.log(c); // NaN
var — переменная, область видимости — вся функция, до объявления — undefined.

let — переменная, область видимости — только {…}, до объявления их нет.

const — аналог переменной final.

Promise – это специальный объект, который содержит своё состояние. Вначале pending(«ожидание»), затем – одно из: fulfilled («выполнено успешно») или rejected(«выполнено с ошибкой»).

promiseInit

На promise можно навешивать коллбэки двух типов:

  • onFulfilled – срабатывают, когда promise в состоянии «выполнен успешно».
  • onRejected – срабатывают, когда promise в состоянии «выполнен с ошибкой».

Способ использования, в общих чертах, такой:

  1. Код, которому надо сделать что-то асинхронно, создаёт объект promise и возвращает его.
  2. Внешний код, получив promise, навешивает на него обработчики.
  3. По завершении процесса асинхронный код переводит promise в состояние fulfilled (с результатом) или rejected (с ошибкой). При этом автоматически вызываются соответствующие обработчики во внешнем коде.
Аналог функционального интерфейса в Java.

var sum = (num1, num2) => num1 + num2;
// эквивалент
var sum = function(num1, num2) { return num1 + num2; };

React