Топ-50 Java Core питань та відповідей на співбесіді. Частина 1
Collections
25. Що мають на увазі під Collections в Java?
Collection – це фреймворк, створений для збереження та маніпуляції об'єктами. Використовується для виконання таких операцій:- пошук;
- сортування;
- маніпуляція;
- додавання;
- видалення.
java.util
пакеті.
26. Які класи та інтерфейси доступні у Collection фреймворку?
Інтерфейси:- Collection;
- List;
- Set;
- Map;
- Sorted Set;
- Sorted Map;
- Queue.
- Lists:
- ArrayList;
- LinkedList;
- Vector(deprecated).
- Sets:
- HashSet;
- LinkedHashSet;
- TreeSet.
- Maps:
- HashMap
- TreeMap
- HashTable (deprecated)
- LinkedHashMap
- Queue
- Priority Queue.
27. Що мається на увазі під sorted та ordered у колекціях?
Ordered (упорядкування):
Це означає, що елементи, які зберігаються в колекції, ґрунтуються на значеннях, доданих до колекції. Таким чином, ми можемо перебирати значення з колекції у певному порядку. Тобто це означає, що в елементів колекції є свій специфічний порядок, згідно з яким вони розташовані. Для кращого розуміння колекція, яка не впорядкована (ordered), зберігає елементи в довільному порядку. Наприклад, Set.Sorted (відсортований):
Це означає, що група елементів відсортована колекції на основі даних елемента колекції. Тобто не лише колекція впорядкована (ordered), а ще й порядок елементів залежить від їх значень. Цей порядок може змінюватися, якщо відсортувати інше значення елемента.28. Які колекції з List інтерфейсом? Як відбувається робота з List?
Значення елементів у аркуш базуються на їхньому індексі — вони впорядковані за індексом. Повторення елементів дозволено (тобто можна додати той самий об'єкт у колекцію кілька разів, і це буде нормально).ArrayList:
Найпоширеніша колекція. По суті, це масив з розміром, що динамічно розширюється. Робота з управління розміром масиву лежить на колекції. Для нас важливо зрозуміти, що в більшості випадків це те, що потрібно використовувати. особливості:- швидкий перебір та швидкий пошук за індексом;
- колекцію впорядковано за індексом, але не сортовано;
- реалізує RandomAccess інтерфейс;
- повільне додавання до середини списку.
public class A {
public static void main(String[] args) {
ArrayList names = new ArrayList<>();
names.add("John");
names.add("John");
names.add("Roman");
names.add("Ivan");
}
}
>> output
[John, John, Roman, Ivan]
У висновку видно, що це елементи, що повторюються. Вони виведені у порядку, в якому їх записали. Що ще почитати? Так море інформації навіть виходити з JavaRush не потрібно:
Linked List:
Це колекція, де кожен елемент має посилання на попередній і наступний елементи. За цими посиланнями можна переходити від одного елемента до іншого. При додаванні елемента просто змінюються посилання на попередній та наступний елементи:- елементи пов'язані один з одним, тобто реалізовано двозв'язковий перелік;
- загальна швидкість роботи помітно нижче, ніж у ArrayList;
- відмінний вибір для великої кількості вставок та видалень у середину масиву;
- реалізує інтерфейси списків Queue і Deque, тому має їх методи для роботи.
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("One");
linkedList.add("Two");
linkedList.add("Three");
29. Розкажіть про колекцію Map та її реалізацію?
Map – це колекція ключ-значення (key-value). Є унікальний ключ та значення, яке відповідає цьому значенню. Використовуєтьсяequals()
і hashcode()
методи визначення унікальності ключа.
HashMap:
- не відсортований та не впорядкований;
- використовують якщо не важливі порядок та сортування;
- підтримує ключ null.
public class CollectionExample {
public static void main(String[] args) {
HashMap positions = new HashMap<>();
positions.put("junior", "Ivan");
positions.put("middle", "Roman");
positions.put("senior", "Vasily");
positions.put("team lead", "Anton");
positions.put("arthitect", "Andrew");
positions.put("senior", "John");
System.out.println(positions);
}
}
// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}
Ключі завжди унікальні, тому записано лише один senior.
LinkedHashMap:
- підтримує порядок вставки;
- повільніше, ніж HashMap;
- очікується, що ітерація швидше, ніж у HashMap.
public class CollectionExample {
public static void main(String[] args) {
LinkedHashMap<String, String> positions = new LinkedHashMap<>();
positions.put("junior", "Ivan");
positions.put("middle", "Roman");
positions.put("senior", "Vasily");
positions.put("team lead", "Anton");
positions.put("arthitect", "Andrew");
positions.put("senior", "John");
System.out.println(positions);
}
}
// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}
TreeMap:
Реалізація карти, яка зберігає записи відсортованими відповідно до природного порядку їх ключів або, що краще, з використанням компаратора, якщо він надається в конструкторі при створенні карти. Приклад:-
Без компаратора
public class CollectionExample { public static void main(String[] args) { TreeMap<Integer, String> positions = new TreeMap<>(); positions.put(1, "Ivan"); positions.put(3, "Roman"); positions.put(2, "Vasily"); positions.put(10, "Anton"); positions.put(7, "Andrew"); positions.put(1, "John"); System.out.println(positions); } } // вывод в консоль // {1=John, 2=Vasily, 3=Roman, 7=Andrew, 10=Anton}
-
З компаратором
public class CollectionExample { public static void main(String[] args) { //используем реализацию Strategy Pattern'a и добавим компаратор: TreeMap<Integer, String> positions = new TreeMap<>(Comparator.reverseOrder()); positions.put(1, "Ivan"); positions.put(3, "Roman"); positions.put(2, "Vasily"); positions.put(10, "Anton"); positions.put(7, "Andrew"); positions.put(1, "John"); System.out.println(positions); } } // вывод в консоль // {10=Anton, 7=Andrew, 3=Roman, 2=Vasily, 1=John}
30. Розкажіть про колекцію Set та її реалізацію?
Set - це безліч унікальних елементів, і це її головна особливість. Тобто Set не допускає повторення тих самих елементів. Тут важливо, щоб у об'єктів, які додаються, було реалізовано методequals
.
HashSet:
- не відсортований та не впорядкований. Під капотом там HashMap із заглушкою для значення. Подивіться самі;)
- використовує hashCode для додавання об'єктів;
- варто використовувати, коли потрібно мати унікальні об'єкти та їх порядок не важливий.
public class CollectionExample {
public static void main(String[] args) {
HashSet<String> positions = new HashSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [senior, middle, team lead, architect, junior]
Тут видно, що елемент "junior", який двічі доданий, присутній тільки в одиничному екземплярі. І порядок не такий самий, як при додаванні.
LinkedHashSet:
- упорядкована версія HashSet;
- підтримує двозв'язковий перелік всіх елементів;
- використовувати його, коли потрібна впорядкованість при ітерації.
public class CollectionExample {
public static void main(String[] args) {
LinkedHashSet<String> positions = new LinkedHashSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [senior, middle, team lead, architect, junior]
TreeSet:
- одна із двох сортованих колекцій;
- використовує структуру червоно-чорного дерева та гарантує, що елементи будуть у зростаючому порядку;
- під капотом це TreeMap із заглушкою на значеннях. А елементами TreeSet є ключі до TreeMap (також подивіться ;)).
public class CollectionExample {
public static void main(String[] args) {
TreeSet<String> positions = new TreeSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [architect, junior, middle, senior, team lead]
Exceptions
31. Що таке Exception?
Exception це проблема, яка може виникнути в runtime. Це виняткова ситуація, яка виникає з якихось причин. Діаграма успадкування винятків виглядає так (потрібно знати її назубок ;) ): На діаграмі видно, що загалом усі винятки поділяються на дві групи – exceptions та error. Error — використовуються JVM для відображення помилок, після яких робота програми вже не має сенсу. Наприклад, StackOverFlowError, яка говорить, що стек заповнений і програма вже не може працювати. Exception- Винятки, які генеруються програмно в коді. Є різні винятки, що перевіряються та неперевірювані, але головне, що вони є, і їх можна перехопити та продовжити роботу програми. Exceptions, у свою чергу, ще поділяються на тих, хто успадковується від RuntimeException та інших спадкоємців Exception. В рамках цього питання інформації достатньо. Про те, що таке виключення, що перевіряються / неперевірені, поговоримо нижче.32. Як JVM обробляє винятки?
Як це працює? Як тільки десь створюється виняток, runtime створює Exception Object (позначимо як ExcObj). У ньому зберігається вся необхідна для роботи інформація - саме виняток, який викликався і місцем, де це сталося. СтворенняExcObj
та передача у runtime є ніщо інше як “викидання виключення”. ExcObj
містить методи, якими можна дійти місце створення винятку. Це безліч методів називається Call Stack. Далі, runtime система шукає метод у Call Stack, котрий зможе обробити наш виняток. Якщо він таки знаходить відповідний оброблювач, тобто тип виключення збігається з типом оброблювача, все добре. Якщо не знаходить, то runtime передає все в default exception handler, який готує відповідь та завершує роботу. Як це виглядає наочно:
/**
* Пример, в котором показываются две опции — когда находится обработчик для исключения и когда нет.
*/
class ThrowerExceptionExample {
public static void main(String[] args) throws IllegalAccessException {
ThrowerExceptionExample example = new ThrowerExceptionExample();
System.out.println(example.populateString());
}
/**
* Здесь происходит перехват одного из возможных исключений — {@link IOException}.
* А вот второй будет пробрасываться дальше вверх по вызову.
*/
private String populateString() throws IllegalAccessException {
try {
return randomThrower();
} catch (IOException e) {
return "Caught IOException";
}
}
/**
* Здесь две опции: або бросается {@link IOException} або {@link IllegalAccessException}.
* Выбирается случайным образом.
*/
private String randomThrower() throws IOException, IllegalAccessException {
if (new Random().nextBoolean()) {
throw new IOException();
} else {
throw new IllegalAccessException();
}
}
}
У нашому випадку CallStack схематично матиме вигляд:
randomThrower() => populateString() => main(String[] args)
Є дві опції: випадковим чином буде викинуто один чи інший виняток. Для IOException все ок, якщо буде згенеровано воно, результатом роботи буде: "Caught IOException"
. А от якщо буде другий виняток, обробника на якого немає, програму буде зупинено з таким висновком:
Exception in thread "main" java.lang.IllegalAccessException
at ThrowerExceptionExample.randomThrower(CollectionExample.java:38)
at ThrowerExceptionExample.populateString(CollectionExample.java:24)
at ThrowerExceptionExample.main(CollectionExample.java:15)
33. Як програмістам обробляти винятки?
У питаннях вище вже використовувалися ті чи інші ключові слова для роботи з винятками, тепер потрібно поговорити про них докладніше. Які ключові слова є?- try
- catch
- throw
- throws
- finally
try-catch-finally
- це конструкція, за допомогою якої можна правильним чином перехопити та обробити виняток.try
— можливо лише один раз, у ньому і відбувається логіка;catch
— блок, який приймає якийсь тип виключення, може бути безліч. Наприклад, у блоці try генеруватиметься кілька винятків, які ніяк не пов'язані один з одним;finally
- "Нарешті" і цей блок. Це блок, який виконається у будь-якому випадку, незалежно від того, що робиться у try, catch.
try {
// сюда передают тот код, который может вызвать исключение.
} catch (IOException e) {
// первый catch блок, который принимает IOException и все его подтипы(потомки).
// Например, нет файлу при чтении, выпадает FileNotFoundException, и мы уже соответствующе
// обрабатываем это.
} catch (IllegalAccessException e) {
// если нужно, можно добавить больше одного catch блока, в этом нет проблем.
} catch (OneException | TwoException e) {
// можно даже объединять несколько в один блок
} catch (Throwable t) {
// а можно сказать, что мы принимаем ВСЁ))))
} finally {
// этот блок может быть, а может и не быть.
// и он точно выполнится.
}
Уважно вчитайтесь в опис прикладу і буде все ясно)
34. throw і throws в Java
throw
throw
використовують у разі, коли потрібно явно створити новий виняток. Застосовують його для створення та викидання винятків користувача. Наприклад, винятки, пов'язані з валідацією. Зазвичай для валідації успадковуються RuntimeException
. Приклад:
// пример пробрасывания исключения
throw new RuntimeException("because I can :D");
Важливо, що використовувати цю конструкцію можна тільки тим, що успадковується Throwable
. Тобто не можна сказати так:
throw new String("як тебе такое, Илон Маск?");
Далі робота потоку обривається і починається пошук оброблювача, який зміг би обробити його. Коли не знаходить, йде до методу, який викликав його, і так пошук йтиме вгору по рядку викликів поки або не знайде відповідний обробник, або залишить роботу програми. Дивимося:
// Пример, который демонстрирует работу throw
class ThrowExample {
void willThrow() throws IOException {
throw new IOException("Because I Can!");
}
void doSomething() {
System.out.println("Doing something");
try {
willThrow();
} catch (IOException e) {
System.out.println("IOException was successfully handled.");
}
}
public static void main(String args[]) {
ThrowExample throwExample = new ThrowExample();
throwExample.doSomething();
}
}
Якщо запустити програму, отримаємо такий результат:
Doing something
IOException was successfully handled.
throws
throws
— механізм, з якого метод може викидати одне чи більше винятків. Додаються вони через кому. Дивимося як це легко і просто:
private Object willThrow() throws RuntimeException, IOException, FileNotFoundException
Причому важливо відзначити, що можуть бути як виключення, що таки не перевіряються. Зрозуміло, що неперевірені винятки можна і не додавати в throws
, але правила хорошого тону говорять про інше. Якщо це перевіряються, то використовуючи метод, який їх генерує, потрібно якось його обробити. Є два варіанта:
- Написати
try-catch
з відповідним і вище за успадкуванням винятком. - Використовувати
throws
так само, щоб ця проблема була вже в когось іншого :D
35. Checked і Unchecked винятки Java
B Java є два типи винятків - checked і unchecked.Checked винятки:
Це винятки, що перевіряються під час компіляції. Якщо якийсь код у методі під час виключення видає checked виняток, метод зобов'язаний або обробити його за допомогою , абоtry-catch
прокинути його далі. (для нас це не важливо) і зберігає її назад.
class CheckedImageExample {
public static void main(String[] args) {
File imageFile = new File("/users/romankh3/image.png");
BufferedImage image = ImageIO.read(imageFile);
updateAndSaveImage(image, imageFile);
}
private static void updateAndSaveImage(BufferedImage image, File imageFile) {
ImageIO.write(image, "png", imageFile);
}
}
Такий код компілюватися не буде, тому що статичні методи ImageIO.read()
і ImageIO.write()
викидають виняток IOException, яке є checked (перевіреним) і має бути обробленим відповідно. Тут дві опції, які ми вже обговорабо вище: або використати try-catch
, або прокинути далі. Для кращого засвоєння зробимо і так, і так. Тобто в методі updateAndSave
просто прокинемо, а вже в головному методі скористаємося try-catch
:
class CheckedImageExample {
public static void main(String[] args) {
File imageFile = new File("/users/romankh3/image.png");
try {
BufferedImage image = ImageIO.read(imageFile);
updateAndSaveImage(image, imageFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void updateAndSaveImage(BufferedImage image, File imageFile) throws IOException {
ImageIO.write(image, "png", imageFile);
}
}
Unchecked винятки:
Це винятки, які на етапі компіляції не перевіряються. Тобто, метод може генерувати RuntimeException, а компілятор не нагадає якимось чином це обробити. Як показано нижче, у нас усі, хто успадковуються від RuntimeException та Error є неперевіреними. Розглянемо таку Java-програму. Код чудово компілюється, але при запуску видає винятокArrayIndexOutOfBoundsException
. Компілятор дозволяє компілювати його, тому що ArrayIndexOutOfBoundsException
є неперевіреним винятком. Звичайна ситуація з масивом, яка може бути:
class CheckedImageExample {
public static void main(String[] args) {
int[] array = new int[3];
array[5] = 12;
}
}
Результатом буде:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at main(CheckedImageExample.java:12)
До речі, ви вже помітабо, що Java короткі імена ніхто не дає? Чим більше тим краще. Він, Spring Framework, при цьому встиг дуже сильно: візьми тільки який-небудь BeanFactoryPostProcessor клас)))
36. Що таке try-with-resources?
Це механізм, з якого потрібно правильно закривати всі ресурси. Як-то не зрозуміло, так?) Спершу, що таке ресурс... Ресурс — це об'єкт, після роботи з яким потрібно закрити його, тобто викликати методclose()
. Ресурсом називаються всі об'єкти, які реалізують інтерфейс AutoClosable
, який, у свою чергу, реалізує інтерфейс Closeable
. Для нас важливо зрозуміти, що все InputStream
є OutpuStream
ресурсами і їх потрібно правильно і успішно вивільняти. Ось якраз для цього і потрібно використовувати try-with-resource
конструкцію. Ось як вона виглядає:
private void unzipFile(File zipFile) throws IOException {
try(ZipInputStream zipOutputStream = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry zipEntry = zipOutputStream.getNextEntry();
while (zipEntry != null) {
}
}
}
private void saveZipEntry(ZipEntry zipEntry) {
// логика сохранения
}
На цьому прикладі ресурс — це ZipInputStream
, після роботи з яким потрібно буде закрити його. І щоб не думати про те, що необхідно викликати спосіб close()
, ми просто визначаємо цю змінну в блоці try, як показано в прикладі і в рамках цього блоку виконуємо все потрібне. Що робить приклад? Він розархівує архів zip. Для цього потрібно скористатися InputStream
тим. Визначати можна більше однієї змінної, розділяють їх крапкою з комою. А в чому проблема? Але ж можна використовувати finally
блок, можливо, скажете ви. Ось стаття , в якій детально описуються проблеми з цим підходом. Також у ній описується весь перелік невдач, які можуть осягнути того, хто знехтує використанням цієї конструкції. Рекомендую до прочитання ;) У завершальній частині— питання/відповіді на тему Multithreading. Мій профіль на GitHub
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ