Что напечатает следующий код?
public classPoint { private final int x; private final int y; publicPoint (int x, int y) { this.x = x; this.y = y; } public booleanequals (Point other) { if (this == other) return true; return this.x == other.x && this.y == other.y; } public inthashCode () { 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 classPoint { private final int x; private final int y; publicPoint (int x, int y) { this.x = x; this.y = y; } // getters@Override public booleanequals (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 classColourPoint extendsPoint { private final String colour; publicColourPoint (int x, int y, String colour) { super(x, y); this.colour = colour; } // getters@Override public booleanequals (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); } }
Симметрия
Что будет выведено в консоль?
classA {} classB extendsA {} classC extendsB {} classD extendsC {} public classLoader { public static voidmain (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
Что будет выведено в консоль?
classSuperBase { public int i = 3; public voidfoo (Object o) { System.out.println("Object " + i); } public voidfoo (String s) { System.out.println("String " + i); } } classBase extendsSuperBase { publicBase () { i = 5; } public static voidmain (String[] args) { SuperBase sb = new Base(); Object o = ""; sb.foo(o); sb.foo(""); } }
Object 5 String 5
Каким будет результат кода?
classParent {} classDeriveOne extendsParent {} classDeriveTwo extendsParent {} Parent p = new Parent(); DeriveOne d1 = new DeriveOne(); DeriveTwo d2 = new DeriveTwo(); d1 = (DeriveOne) d2;
Ошибка компиляции.
Компилятору сразу понятно, что невозможно кастировать d2 к классу DeriveOne, т.к. d2 не является наследником DeriveOne
Есть класс
public class Sample<T> { T data; public Sample(T data) { this.data = data; } }
Его используют так, что не так? что смущает?
public class App { public Sample method(Integer value1, String value2) {//1 var sample1 = new Sample<Integer>(value1); if (value2 != null) { var sample2 = (Sample) sample1;//2 sample2.data = value2; return sample2; } return sample1; } }
Первое 1 и 2 - не параметризированны
Использование сырого типа лишает вас преимуществ дженериков и типобезопасности.
Зачем вообще строка 2? Можно удалить
Если хочется поумничать про проблемы:
Типобезопасность: Переменной типа Sample<Integer> присваивается значение типа Sample<String>,
что может приводить к ошибкам в runtime.
Дженерики в JAVA предназначены для обеспечения типобезопасности, и данная конструкция его нарушает.
Неконсистентность данных: Объект Sample создается с одним типом данных (Integer),
но позже его содержимое перезаписывается другим типом данных (String).
Это может привести к путанице и сложности в поддержке кода.
Нарушенные принципы SOLID:
- OCP: Код не открыт для расширения и изменение поведения метода требует изменения существующего кода,
причём делает это небезопасно.
- LSP: нарушен, поскольку использование объекта с различными видами данных не ведет себя предсказуемо.
Cигнатура выглядит очень странно? Надо задать вопрос, что мы хотим?
Скажут обернуть второе если есть (первое типа есть всегда)
Тут надо сказать "мухи отдельно", давайте оборачивать отдельно и выбирать во что обернуть то же, и родить подобный код с перегрузкой метода
public Sample<Integer> wrap(Integer value1) { return new Sample<>(value1); } public Sample<String> wrap(String value2) { return new Sample<>(value2); } public Sample<?> method(Object value1, String value2) { return value2 != null ? wrap(value2) : wrap(value1); }
После это, глядя на это, можно спросить - а будут ли использоваться другие типы?
Если да, то можно продолжить "шалить" и написать типизированный метод с использованием дженериков
public <T> Sample<T> method(T value) { return new Sample<>(value); }
Будет ли использоваться индекс в запросе?
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.
CREATE index ON post (owner_id, likes_count); SELECT * FROM post WHERE owner_id = ?;
Да, индекс будет использоваться в запросе.
В данном запросе имеется условие owner_id = ?, которое соответствует первой колонке составного индекса
(owner_id, likes_count).
Индексы могут использоваться даже если условие соответствует только первой колонке составного
индекса.
CREATE index ON post (owner_id, likes_count); SELECT * FROM post WHERE likes_count = ?;
Нет, индекс не будет использован в этом запросе.
Поскольку условие фильтрации затрагивает только likes_count, а индекс начинается с owner_id, то индекс
(owner_id, likes_count) не будет полезен.
Составной индекс может быть использован, только если условия фильтрации затрагивают первый столбец
(или первый и второй) в индексе.
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)
или выбрать наиболее селективный индекс для начала поиска и затем использовать другой индекс для
дальнейшей фильтрации.
CREATE index ON post (owner_id); CREATE index ON post (likes_count); SELECT * FROM post WHERE owner_id = ? OR likes_count = ?;
Здесь ответ может зависеть от конкретной СУБД и её возможностей оптимизации. В некоторых случаях, особенно для сложных OR-условий, использование индексов может быть ограниченным.
Обычно:
- Некоторые СУБД могут использовать индексы для обеих колонок при запросах с OR-условиями, выполняя
два индексных поиска и объединяя результаты.
- Другие СУБД могут выбрать полный скан таблицы, если считают, что это будет более эффективно по
времени.
Таким образом, хотя технически оба индекса потенциально могут быть использованы,
всё зависит от подсистемы оптимизации запросов конкретной СУБД.
В большинстве случаев конкретная база данных будет принимать решение, исходя из своих алгоритмов
оптимизации.
classVolatileExample { private static booleanrunning = true;// Не использовано volatile public static voidmain (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)
classReorderingExample { private intx = 0; private booleanflag = false;// тут нужен volatile public voidwriter () { x = 42;// (1) flag = true;// (2) } public voidreader () { if (flag) {// (3) System.out.println(x);// (4) } } public static voidmain (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 для флага
Как правильно создать синглтон
classSingleton { private static Singletoninstance ;// Тут еще нужно вкрячить volatile privateSingleton () {// Private constructor } public static SingletongetInstance () { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
Наиболее распространенный способ — «Double-Checked Locking»
volatile тут нужен, что бы мы отдали полностью созданный и инициализированный объект, иначе может быть ситуация что ссылка на объект опубликована, а объект еще не создан, или объект создан, но его поля еще не проинициализированны.
classUnsafePublication { private Objectresource ;// нужен volatile что бы решить проблему publicUnsafePublication () { new Thread(() -> { resource = new Object(); }).start(); } public ObjectgetResource () { return resource; } public static voidmain (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, но еще не все поля проинициализированы
У нас есть следующие таблицы.
Требуется вывести id людей городов в которых нет мостов.
CREATE TABLE town ( id bigserial PRIMARY KEY, name text ); CREATE TABLE person ( id bigserial PRIMARY KEY, name text, town_id bigint not null REFERENCES town (id) ); CREATE TABLE bridge ( id bigserial PRIMARY KEY, name text, town_id bigint not null REFERENCES town (id) );
SELECT p.id FROM person p LEFT JOIN bridge b on p.town_id = b.town_id WHERE b.id IS NULL;
Быстро даром не бывает - значит что тут будет использовано много памяти
Тут надо понять что одной структурой не обойтись
Определившись со структурами подумать или вспомнить
как удалить из массива за O(1)
Мой вариант кода (уже с оптимизацией и типизацией)
Public classRandomSet<T> { private final HashMap<T, Integer>map = new HashMap<>(); private final List<T>list = new ArrayList<>(); private final Randomrandom = new Random(); public booleanadd (T element) { int size = list.size(); return map.computeIfAbsent(element, t -> { list.add(element); return list.size() - 1; }) == size; } public booleancontains (T element) { return map.containsKey(element); } public booleanremove (T element) { Integer removeIndex = map.remove(element); if (removeIndex != null) { int lastIndex = list.size() - 1; T lastElement = list.get(lastIndex); list.set(removeIndex, lastElement); if (removeIndex > 0) map.put(lastElement, removeIndex); list.remove(lastIndex); return true; } return false; } public TgetRandom () { return list.get(random.nextInt(list.size())); } }