EQUALS

Теория

Что напечатает следующий код?

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public boolean equals(Point other) {
        if (this == other) return true;

        return this.x == other.x && this.y == other.y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }
}

Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
Point p3 = new Point(2022, 2023);

System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));

Set<Point> set = new HashSet<>();
set.add(p1);

System.out.println(set.contains(p2));
System.out.println(set.contains(p3));

true false false false

Метод boolean equals(Point other) не переопределяет Object.equals, а перегружает его.
Плюс не определен метод hashcode(), что осложняет нахождение объекта в HashSet.


Какое свойство equals нарушается в этом случае? (если нарушается)

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // getters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Point)) return false;

        Point other = (Point) o;

        return x == other.x && y == other.y;
    }
}

public class ColourPoint extends Point {
    private final String colour;

    public ColourPoint(int x, int y, String colour) {
        super(x, y);
        this.colour = colour;
    }

    // getters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ColourPoint)) return false;

        ColourPoint other = (ColourPoint) o;

        return this.getX() == other.getX() &&
               this.getY() == other.getY() &&
               this.colour.equals(other.colour);
    }

}

Симметрия

INSTANCEOF

Что будет выведено в консоль?

class A {}
class B extends A {}
class C extends B {}
class D extends C {}

public class Loader {
    public static void main(String[] args) {
        B b = new C();
        A a = b;
        if (a instanceof A) System.out.println("A");
        if (a instanceof B) System.out.println("B");
        if (a instanceof C) System.out.println("C");
        if (a instanceof D) System.out.println("D");
    }
}

A B C

Method Overloading (Перегрузка методов)

Что будет выведено в консоль?

class SuperBase {
    public int i = 3;

    public void foo (Object o) {
        System.out.println("Object " + i);
    }
    public void foo (String s) {
        System.out.println("String " + i);
    }
}

class Base extends SuperBase {
    public Base() {
        i = 5;
    }

    public static void main(String[] args) {
        SuperBase sb = new Base();
        Object o = "";
        sb.foo(o);
        sb.foo("");
    }
}

Object 5 String 5

Inheritance (наследование)

Теория

Каким будет результат кода?

class Parent {}
class DeriveOne extends Parent {}
class DeriveTwo extends Parent {}
Parent p = new Parent();
DeriveOne d1 = new DeriveOne();
DeriveTwo d2 = new DeriveTwo();
d1 = (DeriveOne) d2;

Ошибка компиляции.

Компилятору сразу понятно, что невозможно кастировать d2 к классу DeriveOne, т.к. d2 не является наследником DeriveOne

Index DB

Будет ли использоваться индекс в запросе?

1.

CREATE index ON post (owner_id);
SELECT *
FROM post
WHERE likes_count > ?
  AND owner_id = ?;

Да, индекс будет использоваться в запросе.

В данном запросе имеется условие owner_id = ?, которое соответствует индексу, созданному на owner_id.
Индекс на owner_id может помочь улучшить производительность запроса, делая выборку более быстрой.
Условие likes_count > ? может быть вторичным, но главный фильтрующий критерий здесь — это owner_id.

2.

CREATE index ON post (owner_id, likes_count);
SELECT *
FROM post
WHERE owner_id = ?;

Да, индекс будет использоваться в запросе.

В данном запросе имеется условие owner_id = ?, которое соответствует первой колонке составного индекса (owner_id, likes_count).
Индексы могут использоваться даже если условие соответствует только первой колонке составного индекса.

3.

CREATE index ON post (owner_id, likes_count);
SELECT *
FROM post
WHERE likes_count = ?;

Нет, индекс не будет использован в этом запросе.

Поскольку условие фильтрации затрагивает только likes_count, а индекс начинается с owner_id, то индекс (owner_id, likes_count) не будет полезен.
Составной индекс может быть использован, только если условия фильтрации затрагивают первый столбец (или первый и второй) в индексе.

4.

CREATE index ON post (owner_id);
CREATE index ON post (likes_count);
SELECT *
FROM post
WHERE owner_id = ?
  AND likes_count = ?;

Да, индекс будет использоваться в запросе.

В этом случае оба индекса (owner_id и likes_count) могут быть использованы.
СУБД может использовать оба индекса для более эффективного выполнения запроса.
Например, некоторые системы управления базами данных могут выполнить пересечение индексов (bitmap index merge)
или выбрать наиболее селективный индекс для начала поиска и затем использовать другой индекс для дальнейшей фильтрации.

5.

CREATE index ON post (owner_id);
CREATE index ON post (likes_count);
SELECT *
FROM post
WHERE owner_id = ?
   OR likes_count = ?;

Здесь ответ может зависеть от конкретной СУБД и её возможностей оптимизации. В некоторых случаях, особенно для сложных OR-условий, использование индексов может быть ограниченным.

Обычно:
- Некоторые СУБД могут использовать индексы для обеих колонок при запросах с OR-условиями, выполняя два индексных поиска и объединяя результаты.
- Другие СУБД могут выбрать полный скан таблицы, если считают, что это будет более эффективно по времени.
Таким образом, хотя технически оба индекса потенциально могут быть использованы,
всё зависит от подсистемы оптимизации запросов конкретной СУБД.
В большинстве случаев конкретная база данных будет принимать решение, исходя из своих алгоритмов оптимизации.

JMM

1.

class VolatileExample {
    private static boolean running = true; // Не использовано volatile

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int count = 0;
            while (running) {
                count++;
            }
            System.out.println("Stopped at count: " + count);
        });

        thread.start();
        Thread.sleep(1000);

        running = false;
        thread.join();
        System.out.println("Main thread completed.");
    }
}

Что выведет программа в конце работы?

В таком виде программа не закончится.

т.к. не использовано volatile, while (running) может быть преобразовано в while (true)


2.

class ReorderingExample {
    private int x = 0;
    private boolean flag = false;  // тут нужен volatile

    public void writer() {
        x = 42;  // (1)
        flag = true;  // (2)
    }

    public void reader() {
        if (flag) {  // (3)
            System.out.println(x);  // (4)
        }
    }

    public static void main(String[] args) {
        ReorderingExample example = new ReorderingExample();

        Thread writerThread = new Thread(example::writer);
        Thread readerThread = new Thread(example::reader);

        writerThread.start();
        readerThread.start();
    }
}

Что выведет программа в конце работы?

0, 42 или ничего

0 - потому-что оптимизатор может поменять местами строки 1 и 2

Что сделать что бы получить ожидаемое?

Добавить volatile для флага


3.

Как правильно создать синглтон

class Singleton {
    private static Singleton instance; // Тут еще нужно вкрячить volatile

    private Singleton() {
        // Private constructor
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Наиболее распространенный способ — «Double-Checked Locking»

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

статейка на тему


4.

class UnsafePublication {
    private Object resource; // нужен volatile что бы решить проблему

    public UnsafePublication() {
        new Thread(() -> {
            resource = new Object();
        }).start();
    }

    public Object getResource() {
        return resource;
    }

    public static void main(String[] args) throws InterruptedException {
        UnsafePublication unsafe = new UnsafePublication();
        Thread.sleep(100);
        if (unsafe.getResource() != null) {
            System.out.println("Resource is initialized");
        } else {
            System.out.println("Resource is null");
        }
    }
}

Что тут не так?

Небезопасная публикация

Можем отдать не полностью проинициализированный объект, т.е. объект не null, но еще не все поля проинициализированы

статья на тему