Skip to content

Instantly share code, notes, and snippets.

@isRuslan
Created November 22, 2018 12:23
Show Gist options
  • Save isRuslan/1f310297cc4eb847e3ffaf90bc9018e2 to your computer and use it in GitHub Desktop.
Save isRuslan/1f310297cc4eb847e3ffaf90bc9018e2 to your computer and use it in GitHub Desktop.
Опросник на java джуниора
Думаю, очень круто было бы в начале предупредить, что знание тонкостей языка не так важно,
как умение размышлять и делать разумные предположения
1. Immutable strings
String a = "hello ";
String b = a;
a = a + "world!";
System.out.println(b);
Я не помню, как хранятся строки и являются ли они примитивным типом.
Но думаю, что выведется "hello "
Для переменной a будет создана новая ссылка
"Верно. Строки - не примитивы. Все примитивы с маленькой буквы."
"Присвоение [String b = a] - копирование ссылки"
2. List vs Arraylist vs Linkedlist
В чем разница?
Когда стоит использовать какой?
Частое чтение по индексу?
Частое добавление в конец?
Частое добавление в начало?
Частое добавление в середину?
Как итерироваться по LinkedList?
Что такое List непонятно. Думаю что базовый интерфейс, который даже инстанцировать нельзя "Да"
Пара других – это отсылка внутренней реализации – массив или список. "Да"
Частое чтение по индексу? – Array "Да"
Частое добавление в конец? – Может быть пофиг.
Но надо учитывать, что добавление в массив будет приводить к повторному выделению памяти.
"Да, но в великом большинстве случаев ArrayList будет эфективнее по всем параметрам"
Частое добавление в начало? – Тут точно список "Да"
Частое добавление в середину? – Список "Да"
Как итерироваться по LinkedList? Указатель на next должен быть. Наверное ещё можно Set из него получить
"Нет. Указатель на next скрыт от тебя в реализации."
"Плюс, не понятно как можно было бы итерировать абстарктный List, "
"если бы способ итерации зависел от имплементации"
"Самый распространенный способ итерации - foreach loop"
" for (Integer elem : list) { "
" System.out.println(elem); "
" } "
"Он крутой, но им нельзя удалять элементы"
"Он реально - синтаксический сахор над Iterator"
" List<Integer> list = new LinkedList<>(); "
" Iterator<Integer> iterator = list.iterator(); "
" while (iterator.hasNext()) { "
" Integer next = iterator.next(); "
" System.out.println(next); "
" } "
"Iterator умеет iterator.remove() почти всегда :)"
"foreach работает, если коллекция (List в данном случае) implements Iterable<T>"
"Ну и третий способ через Stream API (c java 8) - это правильная стандартная реализация наших болтсов"
" List<Integer> list = new LinkedList<>(); "
" list.forEach(System.out::println); "
"Set получить можно, если туда перекопировать все елементы из листа с помощью set.addAll(list)"
"Но итерировать по сету нужно точно так же :)"
"Еще на всякий случай добавлю, что LinkedList - пролинкован в обе стороны"
"Т.е. эффективно итерировать его можно в обе стороны"
3. Equals
public class Fruit {
private String name;
private int price;
public Fruit(String name, int price) {
this.name = name;
this.price = price;
}
}
Fruit a = new Fruit("banana", 37);
Fruit b = a;
Fruit c = new Fruit("banana", 37);
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(c));
Что выведет код?
Как сделать так, чтобы логически одинаковые фрукты были равны?
true, "Да"
false, "Да"
true, "Нет, попался :)"
"Если в классе не определить метод equals, то берется equals первого родителя, у которого он есть"
"В данном случае это Object"
"Для Object equals() - это тоже просто проверка ссылки"
В первых друх случаях мы сравниваем ссылки, а не объекты
Хм. Можно какое-то внутреннее хранилище для уникальных объектов создать, например хешмапу с ключём из имени и цены.
Если элемента в мапе нет, то кладём его туда
Если есть, то возвращаем ссылку на то, что нашли
"Можно, но слишком сложно"
"Правильный ответ - определить метод equals()"
"который будет возвращать true, если цена и имя равны"
"PS В нашем коде мы так не делаем, потому что у нас есть ru.yandex.misc.lang.DefaultObject"
"который магически через рефлексию (и скорее всего не очень эффективно по производительности) определяет этот метод"
"PSS Вместе с equals() обычно определяют еще и hashCode() потому что у них тесно связаные контракты"
"Но это уже совсем другая история с умными словами типа рефлексивность, "
"симметричночть, транзитивность и принцип подстановки Лискова"
4. Sort (Comarable/Comparator)
public class Person {
public String name;
public int age;
}
Отсортировать List<Person> by age desc
Желательно 2 способа
Тут мне надо в книжку смотреть...
Догадка
List<Person> sortedList = originalList.sortBy((p1, p2) -> p1.age - p2.age)
"Догадка почти правильная :)"
"Первый способ такой:"
" originalList.sort((a, b) -> a.age - b.age);
"или без лямбды
" originalList.sort(new Comparator<Person>() {
" @Override
" public int compare(Fruit a, Fruit b) {
" return a.age - b.age;
" }
" });
"или с вспомогательным компаратором
" originalList.sort(Comparator.comparingInt(a -> a.age));
"Это все - одно и то же"
"Важно, что он in-place"
"Но можно и иначе"
После доки
Collections.sort(mArrayList, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p2.age == p1.age ? 0 : p2.age - p1.age / Math.abs(p2.age - p1.age);
}
});
" это как без лямбды, но с какими-то очень странными извращениями
" я их пока не понял
" скинь плз доку, где ты это вычитал
"Второй важный способ - заимлементить Comparable<Person>
" class Person implements Comparable<Person> {
" public String name;
" public int age;
"
" @Override
" public int compareTo(Person o) {
" return this.age - o.age;
" }
" }
"Тогда можно делать просто так:
" Collections.sort(list);
5. Default constructor & initialization
public class Person {
public String name;
public int age;
}
Person p = new Person();
"Если ни одного конструктора не определено, то дефолтный создастся сам"
System.out.println(p.name);
"Если не определять фвно значения полей, они задаются дефолтами типа 0, false, null"
System.out.println(p.age);
Person p1 = new Person("John", 12);
"Такой конструктор сам не создается - будет ошибка компеляции"
Что выведет код?
Какой-то он странный. Информации о конструкторе нет, поэтому может вывести всё, что угодно
Если конструктор не определён, то вообще с ошибкой упадёт
Может вывести дефолтные значения, пустую строку и 0
6. Remove from collection by predicate
Удалить из List<Person> тех, у кого name на G
(Желательно in-place)
Тоже надо книжку смотреть.
"Писал про Iterator в задачке #2 - он может удалять
"фильтер тоже ок, но он не in-place"
" List<Person> filteredList = originalList.stream()"
" .filter(p -> p.name.startsWith("G")).collect(Collectors.toList());
" ну и другой вариант не in-place - пробежаться и положить в новый список только правильных людей
7. Unboxing
for (Integer i = 0; i <= Integer.MAX_VALUE; i++) {
System.out.println(i);
}
Какие проблемы есть в этом коде?
Что-нибудь про unboxing
"Да - unboxing и boxing будут происходить на каждой итерации"
"Если просто поменять Integer на int, то будет работать в 700 раз быстрее (1400мс -> 2мс)"
"Но намного важнее проблема в том, что цикл бесконечный :)
"любой Integer <= Integer.MAX_VALUE
8. Exceptions
Какие бывают исключения?
Какие нужно ловить, а какие нет?
Зачем нужен блок finally?
Когда блок finally не выполняется?
Например так?
try {
System.exit(1);
// или выдернуть провод из розетки
} finally {
System.out.println("finally");
}
Какие бывают исключения? – тут у меня пробел в знаниях. Буду исправлять
"Ожидаемый ответ: checked и unchecked (runtime)"
"Более полный ответ: "
# Throwable
# / \
# Error Exception
# / / \
# ... RuntimeException \
# / ...
# ...
" Наследников RuntimeException не нужно ловить или декларировать в сигнатуре (unchecked)
" Наследнико Exception (но не RuntimeException), нужно ловить или декларировать в сигнатуре (checked)
" Error - совсем системные ошибки, которые почти всегда не восстанавливаемые (типа краша JVM)
Какие нужно ловить, а какие нет? – ??
Зачем нужен блок finally? – должен выполняться в любом случае
и при успешном выполнении блок внутри try и при catch "Да"
Когда блок finally не выполняется? – ??
" Например так
" try {
" System.exit(1);
" // или выдернуть провод из розетки
" } finally {
" System.out.println("finally");
" }
Так в исключениях у меня сплошные проблемы
9. Casts & compilation time
class Vehicle {};
class Car extends Vehicle {};
class Lexus extends Car {};
class Vegetable {};
Vehicle a = new Vehicle();
Vehicle b = new Car();
Car c = new Vegetable();
Lexus d = new Lexus();
c = b;
c = (Car) b;
b = d;
d = a
d = (Lexus) a;
Где ошибки?
Какие на этапе компиляции, какие в рантайме?
Car c = new Vegetable(); – упадёт при компиляции Эти типы нельзя приводить друг к другу "Да"
Дальше примеры с c кажутся странными и я их игнорирую "Задачу я поставил плохо :( подумаю еще"
d = a; – упадёт на компиляции. Vehicle нельзя привести к родительскому типу. "Да"
d = (Lexus) a; – вот это наверное в рантайме грохнется "Да"
10. Abstract class
Что такое абстрактный класс?
Что будет, если создать инстанс абстрактного класса (new)
Почему?
Когда лучше использовать интерфейсы?
Класс который описывает некоторый интерфейс и предоставляет реализацию части методов "Да"
Нельзя его инстанцировать, потому-то он и абстрактный. "Да"
Только наследника получится сделать, определив недостающие методы "Да"
Декомпозиция и вот это всё, когда хотим один и тот же класс использовать в разных полезных методах.
Для меня это способ замены множественного наследования
"Такой ответ я бы тоже принял. "
"Но можно просто сказать: например, когда хотим сразу 2 интерфейса имплементить - "
"с абстрактными классами так нельзя"
11. Not lazy method args
static int foo() {
System.out.println("foo");
return 1;
}
static int bar(boolean a, int b) {
if (a) {
return b;
}
return 0;
}
public static void main(String[] args) {
int res = bar(false, foo());
System.out.println(res);
}
Что выведет данный код?
foo
1
"Нет
#foo
#0
"Это не так важно, но ты попал мимо смысла задачи"
"Это проверка на наш недавний баг с Option.when(), про который рассказывал Руслан"
"Кто-то может подумать, что если false, и результат foo() не будет использован, "
"то и выполнятся он не будет"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment