Java


08.08.2023https://javastudy.ru/interview/basics-types-operators-arrays
11.08.2021https://javastudy.ru/interview/java-oop
16.01.2020https://javastudy.ru/interview/java-oop2
19.02.2020https://javastudy.ru/interview/java-oop3
17.09.2010http://www.skipy.ru/technics/objCompTh.html
03.07.2021https://javastudy.ru/interview/strings
28.07.2020https://javarush.com/groups/posts/modifikator-static-java
15.12.2020https://javastudy.ru/interview/input-output
01.12.2021https://javastudy.ru/interview/exceptions
15.07.2019https://javarush.com/groups/posts/2315-stiranie-tipov
13.05.2019https://javarush.com/groups/posts/2281-dinamicheskie-proksi
18.01.2024https://habr.com/ru/articles/787166/
28.07.2020https://habr.com/ru/articles/512730/
11.12.2012https://habr.com/ru/post/162017

Dynamic Proxy

Прокси-класс — «надстройка» над оригинальным классом, которая позволяет при необходимости изменить его поведение. Чтобы добавить поведение нам нужен InvocationHandler - перехватчик вызовов. Это специальный интерфейс который позволяет перехватить любые вызовы методов к нашему объекту и добавить нужное нам дополнительное поведение. Нам необходимо сделать собственный перехватчик — то есть, создать класс и реализовать этот интерфейс. Нам нужно реализовать всего один метод интерфейса — invoke. Он делает то что нам нужно — перехватывает все вызовы методов к нашему объекту и добавляет необходимое поведение (здесь мы внутри метода invoke выводим лог в консоль).

Project Valhalla

Нововведение в JVM улучшающее производительность. Каждый объект в Java помимо полезной информации содержит еще и т.н. метаданные (или заголовок/хедер). Даст возможность создавать кастомные примитивные или "value" объекты. Эти объекты должны представлять собой плоскую структуру, подобно массиву int[] а не дерево "указателей".

native

Реализация данного метода выполнена на другом языке (C, C++, ассемблер). Пример - метод hashCode у Object.

Types

Basic types

Примитивные типы. Есть 8 типов деляется на 4 группы:

• целые числа - byte short int long

• числа с плавающей точкой (иначе вещественные) - float double

• логический тип данных - boolean

• символьный тип данных - char

Reference types

Ссылочные типы: Byte Short Integer Long Float Double Boolean Character. У классов-оберток есть ряд характеристик, не свойственных примитивам. У каждого примитивного типа данных есть свой класс-обертка. Ссылочный тип данных, который “оборачивает” своего примитивного младшего брата в Java-объект. Ниже приведены примитивные типы данных и соответствующие им классы обертки

• это классы: при работе с классам-обертками мы работаем с объектами

• данные объекты могут быть null

• предоставляют ряд констант и методов, упрощающих работу с определенным типом данных

Boxing

Процесс преобразования примитивного типа данных в объект его соответствующего типа-обертки.

int primitiveInt = 42;             // Примитивный тип
Integer wrappedInt = primitiveInt; // Боксинг, превращает примитив в объект

Unboxing

Процесс преобразования объекта типа-обертки в соответствующий примитивный тип.

Integer wrappedInt = 42;       // Объект типа Integer
int primitiveInt = wrappedInt; // Анбоксинг, превращает объект в примитив

Object

Object

Все классы являются наследниками суперкласса Object. Это не нужно указывать явно. Объект Object может ссылаться на объект любого другого класса.

Methods

equals сравнивает объекты

hashCode возвращает хэш-код (битовая строка фиксированной длины, целочисленный результат работы метода, которому как входной параметр передан объект)

toString возвращает строковое представление объекта

clone клонирование объекта. Объявлен как protected, нужно переопределить, чтобы воспользоваться. Для этого нужно реализовать интерфейс Cloneable, чтобы соблюсти контракт

finalize вызывается сборщиком мусора когда ссылок на объект больше нет.

getClass возвращает в рантайме класс данного объекта

notify просыпается один поток, который ждет на “мониторе” данного объекта

notifyAll просыпаются все потоки, которые ждут на “мониторе” данного объекта

wait приводит данный поток в ожидание, пока другой поток не вызовет notify или notifyAll методы для этого объекта

wait(long timeout) поток переходит в режим ожидания в течение указанного времени

wait(long timeout, int nanos) приводит данный поток в ожидание, пока другой поток не вызовет notify() или notifyAll() для этого метода, или пока не истечет указанный промежуток времени

hashCode

Это число, битовая строка фиксированной длины, полученная из массива произвольной длины (то есть из объекта), целочисленный результат работы метода, которому в качестве входного параметра передан объект. Вычисление нативное (на C++), используется алгоритм Park Miller, в основе которого random. При каждом запуске приложения у объекта будет разный хеш-код

• для одного и того же входного объекта хеш-код всегда будет одинаковым

• если хеш-коды разные, то и входные объекты гарантированно разные

• если хеш-коды равны, то входные объекты не всегда равны

• количество хеш-кодов ограничено типом int, поэтому хеш-коды разных объектов могут совпадать

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

• какие поля использовать для посчета хеш-кода? - те, которые используются при определении метода equals

• генерируется один раз для каждого объекта при первом вызове метода hashCode, после чего хранится в объекте для последующих вызовов

• число значений hashcode равно диапазону типа int — 2^32

equals

Эквивалентность. Сравнивает содержимое объектов. Стандартная реализация метода equals в Object вернет true лишь в одном случае — когда ссылки указывают на один и тот же объект (адрес объекта в памяти). Содержимое полей не учитывается. Если у двух объектов одного и того же класса содержимое одинаковое, то и хеш-коды должны быть одинаковые. Поэтому, при создании пользовательского класса, принято переопределять методы hashCode() и equals() таким образом, чтобы учитывались поля объекта

• одинаковые объекты — это объекты одного класса с одинаковым содержимым полей

• реализация этого метода для нового класса ложится на плечи разработчика

• при переопределении equals обязательно нужно переопределить hashCode (это даст равенство хеш-кодов для равных обектов, распределено полученное значение будет точно так же, как и исходные данные)

• равные объекты должны возвращать одинаковые хеш-коды

• если переопределить equals без hashCode - классы и методы будут некорректно работать. У объекта HashMap пара, помещенная в Map возможно не будет найдена в ней при обращении, если используется новый экземпляр ключа

• реализация equals должна подчиняться правилам рефлексивности, симметрии, транзитивности и непротиворечивости.

wait

Используется для приостановки выполнения текущего потока до тех пор, пока другой поток не вызовет метод notify или notifyAll на том же объекте монитора. Метод wait вызывается на объекте синхронизации, который должен быть заблокирован (внутри блока synchronized). Когда поток вызывает wait, он освобождает монитор объекта и переходит в состояние ожидания.

// Поток блокируется, пока условие не станет истинным. 
// После вызова wait поток освобождает монитор и переходит в состояние ожидания.
synchronized(object) {
    while (conditionNotMet) {
        object.wait();
    }
    // Продолжение работы после возобновления
}

notify

Используется для пробуждения одного из потоков, ожидающих на том же объекте синхронизации. Этот метод не освобождает монитор объекта, на котором он вызывается; поток, вызвавший notify, остаётся владельцем монитора до тех пор, пока не завершит блок synchronized.

// Метод notify пробуждает один из потоков, ожидающих на объекте object, но не освобождает монитор объекта сразу.
synchronized (object) {
    // Изменение состояния
    object.notify();
}

notifyAll

Пробуждает все потоки, ожидающие на том же объекте синхронизации. Все пробуждённые потоки будут конкурировать за монитор объекта.

// Все потоки, ожидающие на объекте object, будут пробуждены и конкурировать за получение монитора.
synchronized (object) {
    // Изменение состояния
    object.notifyAll();
}

Class

Статическая загрузка классов

После запуска java-приложения все классы которые приложение создает через new (те классы которые импортируются директивой import) будут загружены загрузчиком классов перед запуском приложения.

Динамическая загрузка классов

Возможность загружать классы после старта. После можно создавать экземпляры этого класса через Reflection Api. Вариант является ненадежным с точки зрения целостности программы, так как нужно руками обеспечить попадание этого класса в область в которой его увидит загрузчик классов тогда как при статической загрузке это проверяет компилятор. Однако это дает возможности создавать действительно модульные системы в которых интерфейс и реализация могут быть разделены физически по разным .jar. Классы могут быть загружены и выгружены динамически что позволяет строить гибкие приложения.

Class.forName("path.to.class.ClassName");

Strings

String

Последовательность символов. Все строковые классы — final (от них нельзя унаследоваться). String - immutable final class - созданный объект класса String не может быть изменен.

String name = "Bob";

StringBuffer

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

StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abc").append("def");

StringBuilder

StringBuilder — класс что представляет изменяемую последовательность символов. Класс был введен в Java 5 и имеет полностью идентичный API с StringBuffer. Единственное отличие — StringBuilder не синхронизирован. Это означает, что его использование в многопоточных средах нежелательно. Следовательно, если вы работаете с многопоточностью идеально подходит StringBuffer иначе используйте StringBuilder который работает намного быстрее в большинстве реализаций.

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abc").append("def");

String Pool

Механизм, который позволяет оптимизировать работу с объектами строк, сохраняя их в специальной области памяти, называемой heap. Позволяет уменьшить потребление памяти и улучшить производительность, кэшируя строки и избегая дублирования. Строки в Java неизменяемы (immutable). Это означает, что как только строка создана, её содержимое не может быть изменено. Благодаря этому их можно безопасно кэшировать в пуле.

// Когда строка создается с использованием литералов, например, String s1 = "hello";, JVM проверяет пул на наличие строки "hello".
// Если такая строка уже существует в пуле, переменная s1 будет ссылаться на уже существующий объект. Если нет — строка добавляется в пул.

String s1 = "hello";
String s2 = "hello";  // Будет ссылаться на тот же объект в String Pool

new

Когда строка создается с использованием ключевого слова new, создается новый объект в куче (heap), даже если строка с таким же значением уже существует в пуле строк. Это означает, что объект в куче и объект в строковом пуле не будут ссылаться на один и тот же объект.

String s3 = new String("hello");  // Создаст новый объект в heap

intern

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

String s3 = new String("hello");
String s4 = s3.intern();  // Вернёт ссылку на строку из String Pool
Arrays

Используются для хранения нескольких значений в одной переменной вместо объявления отдельных переменных для каждого значения. Доступ по индексу. Имеют фиксированный размер, не изменить после объявления. Лучше по производительности.

String[] people = { "Bob", "Alice", "John" };
int[] numbers = new int[10];

Serialization

Serializable

Стандартный интерфейс Java. Объекты, реализующие этот интерфейс автоматически сериализуются. Под капотом рефлексия, поэтому медленный. Есть режим ручного управления через методы readObject writeObject.

transient

Игнорировать поле при сериализации/десериализации.

class Book implements Serializable {
    private String name;
    private transient String description;
}

Modifiers

static

Модификатор применяемый к полю блоку методу или внутреннему классу. Данный модификатор указывает на привязку субъекта к текущему классу. static означает что переменная или функция является общей для всех экземпляров этого класса поскольку она принадлежит типу а не самим фактическим объектам. Итак если у вас есть переменная: private static int i = 0; и вы увеличиваете ее (i++) в одном экземпляре изменение будет отражено во всех экземплярах. Если переменная не статическая то у каждого нового объекта данного класса будет своё значение этой переменной меняя которое мы меняем его исключительно в одном объекте.

• статические поля и методы не потокобезопасны (Thread-safe). Это одна из наиболее частых причин возникновения проблем связанных с безопасностью мультипоточного программирования. Учитывая что каждый экземпляр класса имеет одну и ту же копию статической переменной, то такая переменная нуждается в защите — «залочивании» классом. Поэтому при использовании статических переменных, убедитесь, что они должным образом синхронизированы (synchronized), во избежание проблем, например таких как «состояние гонки» (race condition).
• статические методы не переопределяются. всегда вызывается метод родительского класса

В статическом блоке инициализируются статические переменные.

Статический метод может обратиться только к статическим переменным.

Статическим классом может быть только внутренный класс. Данный класс можно наследовать как и он может наследоваться от любого другого класса и имплементировать интерфейс. Статический вложенный класс ничем не отличается от любого другого внутреннего класса за исключением того что его объект не содержит ссылку на создавший его объект внешнего класса. У него есть доступ к приватным статическим переменным внешнего класса.

static class Cat {

    static String name = "Barsik";

    static {
        name = "Oscar";
    }

    public static void rename() {
        name = "Tom";
    }
    
    public static class Car {
       public int km;
  }
}

• Нельзя получить доступ к нестатическим членам класса внутри статического контекста (метода/блока).

public class Counter {
		
		private int count;
		
		public static void main(String args[]) {
			  System.out.println(count); // compile time error
		}
}

final

Запретить наследование для классов. Запретить переопределять методы в подклассах. Сделать переменные неизменяемыми.

final class Cat {

    final String name = "Barsik";

    final void main() {}
}

public

Поля и методы видны другим классам из текущего пакета и из внешних пакетов.

private

Доступен на уровне класса.

protected

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

IO

InputStream OutputStream

Bite-oriented streams. Байтовые потоки. В случае ошибки - IOException.

Reader Writer

Character-oriented streams. Символьные потоки. В случае ошибки - IOException.

BufferedInputStream BufferedOutputStream & BufferedReader BufferedWriter

Классы-надстройки наделяют существующий поток дополнительными свойствами. Буферизируют поток и повышают производительность.

Exceptions

Checked Exceptions

Проверенные (логические) исключения - ошибки при вызове стороннего API, когда мы можем повторить вызов, отменить его или обработать через try-catch. Такие исключения могут быть проверены во время компиляции: ClassNotFoundException IOException SQLException.

Unchecked Exceptions

Непроверенные исключения (runtime exceptions) - являются ошибками программирования и обычно не должны специально обрабатываться: NullPointerException ArrayIndexOutOfBoundsException ClassCastException.

throw

Метод явного создания исключения.

throw new IllegalArgumentException();

throws

Указать все исключения, которые может сгенерировать метод.

public void someMethod() throws NullPointerException, IllegalArgumentException {}

try catch finally

try {
    s = reader.readLine();
} catch (IOException e) {
    System.out.println(e.getMessage());
} finally {
		reader.close();
}

Throwable

Подразделяется на Error (OutOfMemoryError StackOwerFlowError) и Exception (RuntimeException IOException).

Enum

enum

Перечесления. Набор логически связанных констант.

enum Color {
    RED("#FF0000"),
    GREEN("#00FF00"),
    BLUE("#0000FF"), ;

    private String code;

    Color(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

Generics

Стирание типов

Внутри класса не хранится никакой информации о его типе-параметре. Эта информация доступна только на этапе компиляции и стирается (становится недоступной) в runtime. Если ты попытаешься положить объект не того типа в свой List<String>, компилятор выдаст ошибку. Этого как раз и добивались создатели языка, создавая дженерики — проверки на этапе компиляции.

Lambda

Анонимный класс.

// Старый способ:
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Кнопка нажата. Старый способ!");
    }
});

// Новый способ:
button.addActionListener( (e) -> {
    System.out.println("Кнопка нажата. Lambda!");
});
// Старый способ:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}

// Новый способ:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));

// Новый способ с использованием оператора двойного двоеточия ::
list.forEach(System.out::println);

Java version match
Java RuntimeJava
49Java 5
50Java 6
51Java 7
52Java 8
53Java 9
54Java 10
55Java 11
56Java 12
57Java 13
58Java 14
59Java 15
60Java 16
61Java 17
62Java 18
63Java 19
64Java 20
65Java 21
Java. Вопросы на собесе
  1. Как в Java генерируется метод equals?
  1. Расскажи про примитивные и ссылочные типы?
  1. В чем разница между StringBuffer и StringBuilder?
  1. Расскажи про проверенные и непроверенные исключения?
  1. В чем отличие final finally finalize?
  1. Статическая и динамическая загрузка классов?
  1. Что такое стирание типов у дженериков?
  1. Как в Java хранятся строки с одинаковыми значениями?

    Строки кэшируются в специальной области памяти, называемой “строковым пулом” (string pool), чтобы минимизировать создание дубликатов и экономить память.

  1. Основные методы класса Object?

    equals() hashCode() toString() notify() notifyAll() wait() clone() finalize()

  1. Для чего используются методы equals и hashcode у Object?

    Методы equals() и hashCode() используются для сравнения объектов и для оптимизации работы с хэш-структурами, такими как HashMap и HashSet.

  1. Какая реализация у метода hashCode?

    Возвращает Int. Разная в зависимости от JVM. В общем случае, хэшкод основывается на внутреннем адресе объекта в памяти или на использовании генератора псевдослучайных чисел (алгоритм park miller).

  1. Какое максимальное значение у 32-битного Int?

    2^{31} - 1