-
Як створити незмінний об'єкт у Java? Перерахуйте всі переваги
Незмінний клас – це клас, стан якого може бути змінено після створення. Тут станом об'єкта по суті вважаються значення, що зберігаються в екземплярі класу, будь то примітивні типи або типи посилань.
Для того щоб зробити клас незмінним, необхідно виконати такі умови:
- Не надавайте сеттери або методи, які змінюють поля або об'єкти, що посилаються на поля. Сетери мають на увазі зміну стану об'єкта, а це те, чого ми хочемо тут уникнути.
- Зробіть всі поля
final
таprivate
. Поля, позначеніprivate
, будуть недоступними зовні класу, а позначення їхfinal
гарантує, що ви не зміните їх випадково. - Не дозволяйте субкласам перевизначати методи. Найпростіший спосіб це зробити – оголосити клас як
final
. Фіналізовані класи Java не можуть бути перевизначені. - Завжди пам'ятайте, що ваші екземпляри змінних можуть бути змінюваними або незмінними. Визначте їх та повертайте нові об'єкти з скопійованим вмістом для всіх змінних об'єктів (посилальні типи). Змінні змінні (примітивні типи) можуть бути безпечно повернені без додаткових зусиль.
Також, вам необхідно пам'ятати наступні переваги незмінних класів. Можливо, вони знадобляться вас на співбесіді. Незмінні класи:
- легко конструювати, тестувати та використовувати
- автоматично потокобезпечні та не мають проблем синхронізації
- не вимагають конструктора копіювання
- дозволяють виконати «ліниву ініціалізацію» хешкода і кешувати значення, що повертається
- не вимагають захищеного копіювання, коли використовуються як поле
- роблять хороші
Map
ключі таSet
елементи (ці об'єкти не повинні змінювати стан, коли знаходяться в колекції) - роблять свій клас постійним, одного разу створивши його, а він не потребує повторної перевірки
- завжди мають «атомарність по відношенню до збою» (failure atomicity, термін застосував Джошуа Блох): якщо незмінний об'єкт кидає виняток, він ніколи не залишиться у небажаному чи невизначеному стані.
Подивіться приклад, написаний у цьому пості .
-
У Java передача за значенням чи за посиланням?
Java специфікація свідчить, що це Java передається за значенням. Немає такого поняття, як «передача за посиланням» Java. Ці умови пов'язані з викликом методів і передачі змінних, як параметрів методу. Добре, примітивні типи завжди передаються за значенням без будь-якої плутанини. Проте, концепція має бути зрозумілою у контексті параметра методу складних типів.
У Java, коли ми передає посилання складного типу як будь-який параметр методу, завжди адресаа пам'яті копіюється в нову змінну змінну крок за кроком. Подивіться на зображення:
У наведеному прикладі, біти адресаи першого екземпляра копіюються інший посилальної змінної, в результаті чого обидві посилання вказують на одну ділянку пам'яті, де зберігається об'єкт. Пам'ятайте, що привласнивши друге посилання null, ви не надасте null першому посиланні. Але зміна стану об'єкта з однією змінною, що посилається, буде відображено і в іншому посиланні.
Подробиці дивіться тут .
-
Яке застосування блоку
finally
? Чи гарантує цей блок виконання свого коду? Колиfinally
блок не викликається?Блок
finally
завжди викликається, якщоtry
є блок. Це гарантує, що блокfinally
викликається навіть якщо трапляється несподіваний виняток. Алеfinally
є більш корисним, ніж просто для обробки винятків – цей блок дозволяє виконати чищення коду, який випадково обійшов черезreturn
,continue
абоbreak
. Розміщення коду, що очищає, в блокfinally
завжди є хорошою практикою, навіть коли не очікується жодних винятків.Якщо віртуальна машина завершує роботу під час виконання блоку
try
абоcatch
, тоді блокfinally
не буде виконано. Аналогічно, якщо нитка, виконуючи блокtry
абоcatch
, буде перервана або вбита, блокfinally
не буде виконаний, навіть не дивлячись на те, що програма продовжує працювати. -
Чому існує два класи
Date
, один вjava.util package
інший вjava.sql
?java.util.Date
представляє дату та час, аjava.sql.Date
представляє лише дату. Доповненнямjava.sql.Date
є класjava.sql.Time
, який представляє тільки час.Клас
java.sql.Date
є субкласом (розширенням) класуjava.util.Date
. Отже, що змінилося вjava.sql.Date
:toString()
формує інше уявлення рядка: yyyy-mm-dd- статичний метод
valueOf(String)
створює дату з рядка з вказаним вище поданням - виключені гетери та сеттери для годин, хвабон та секунд
Клас
java.sql.Date
використовується в JDBC і призначений, щоб не мати складову часу, тобто години, хвабони, секунди та мілісекунди повинні бути нульовими… але це не є обов'язковим для класу. -
Поясніть маркери.
Шаблон інтерфейсу-маркера – це шаблон проектування в комп'ютерних науках, який використовується мовами програмування, які надають інформацію про об'єкти під час виконання . Це надає спосіб асоціації метаданих класу, де мова не має явної підтримки таких метаданих . Java для цього використовуються інтерфейси без вказівки методів.
Хорошим прикладом застосування інтерфейсу-маркера Java є інтерфейс
Serializable
. Клас реалізує цей інтерфейс для вказівки, що йогоtransient
дані можуть бути записані в потік байтів або на файлову систему.Головною проблемою інтерфейсу-маркера є те, що інтерфейс визначає угоду для класів, що її реалізують, і ця угода успадковується всіма субкласами. Це означає, що ви не зможете "де-реалізувати" маркер. У наведеному прикладі, якщо ви створите субклас, який ви не хотіли б серіалізувати (можливо тому, що він перебуває в минущому (transient) стані), ви повинні вдатися до явного кидання
NotSerializableException
. -
Чому метод
main()
оголошено якpublic static void
?Чому public? Метод
main
має модифікатор доступуpublic
, тому він може бути доступний скрізь і для будь-якого об'єкта, який захоче використовувати цей метод для запуску програми. Тут я не кажу, що JDK/JRE мають подібну нагоду, оскільки java.exe або javaw.exe (для windows) використовують Java Native Interface (JNI) виклик для запуску методу, тому вони можуть викликати його в будь-якому випадку, незалежно від модифікатора доступу .Чому це? Давайте припустимо, що ми метод
main
не статичний. Тепер для виклику будь-якого методу вам необхідний екземпляр класу. Правильно? Java дозволяє мати перевантажені конструктори, це ми всі знаємо. Тоді який із них має бути використаний, і звідки візьмуться параметри для перевантаженого конструктора?Чому void? Немає застосування для значення, що повертається у віртуальній машині, яка фактично викликає цей метод. Єдине, що програма захоче повідомити процесу, що викликав, - це нормальне або ненормальне завершення. Це вже можливо використовуючи
System.exit(int)
. Чи не нульове значення має на увазі ненормальне завершення, інакше все в порядку. -
У чому різниця між створенням рядка як
new()
і літералом (за допомогою подвійних лапок)?Коли ми створюємо рядок, використовуючи
new()
, вона створюється в хіпі і також додається в пул рядків, у той же час рядок, створений за допомогою літералу, створюється тільки в пулі рядків.Вам необхідно ознайомитися з поняттям пула рядків глибше, щоб відповісти на це або подібні запитання. Моя порада - як слід вивчіть клас String і пул рядків .
-
Як працює метод
substring()
класуString
?Як і в інших мовах програмування, рядки Java є послідовністю символів. Цей клас більше схожий на службовий клас для роботи з цією послідовністю. Послідовність символів забезпечується наступною змінною:
/** The value is used for character storage. */ /** Значение используется для хранения символов */ private final char value[]; Для доступа к этому массиву в различных сценариях используются следующие переменные
/** The offset is the first index of the storage that is used. */ /** Смещение – это первый индекс используемого хранабоща. */ private final int offset; /** The count is the number of characters in the String. */ /** Счет – это количество символов в строке. */ private final int count;
Щоразу, коли ми створюємо підрядок від існуючого екземпляра рядка, метод
substring()
лише встановлює нові значення зміннихoffset
таcount
. Внутрішній масив символів не змінюється. Це можливе джерело витоку пам'яті, якщо методsubstring()
використовувати необережно:Початкове значення
value[]
не змінюється. Тому якщо ви створите рядок довжиною 10000 символів і створите 100 підрядків з 5-10 символами в кожному, всі 101 об'єкти будуть містити один і той же символьний масив довжиною 10000 символів. Це без сумніву марнотратство пам'яті.Цього можна уникнути, змінивши код таким чином:
замінити
original.substring(beginIndex)
наnew String(original.substring(beginIndex))
, деoriginal
– вихідний рядок.Примітка перекладача: я не можу сказати до якої версії Java це застосовно, але на даний момент у Java 7 цей пункт статті не актуальний. Метод substring()
викликає конструктор класуnew String(value, beginIndex, subLen)
, що у свою чергу звертається до методуArrays.copyOfRange(value, offset, offset+count)
. Це означає, що у нас буде щоразу нове значення змінноїvalue[]
, що містить наше нове кількість символів. -
Поясніть роботу
HashMap
. Як вирішено проблему дублікатів?Більшість з вас, напевно, погодиться, що
HashMap
найбільш улюблена тема для дискусій на інтерв'ю в даний час. Якщо хтось попросить мене розповісти «Як працюєHashMap
?», я просто відповім: «За принципом хешування». Так просто, як це є.Отже, хешування по суті є способом призначити унікальний код будь-якої змінної/об'єкта після застосування будь-якої формули/алгоритму до своїх властивостей.
Визначення картки (
Map
) таке: «Об'єкт, який прив'язує ключі до значень». Дуже просто, правда? Отже,HashMap
містить власний внутрішній класEntry
, який має вигляд:static class Entry implements Map.Entry { final K key; V value; Entry next; final int hash; …//More code goes here }
Коли хтось намагається помістити пару ключ-значення в
HashMap
, відбувається таке:- Насамперед об'єкт ключа перевіряється на
null
. Якщо ключnull
, значення зберігається у позиціюtable[0]
. Тому що хешкодnull
завжди 0. - Потім, наступним кроком обчислюється хеш значення викликаючи у змінної ключа свій метод
hashCode()
. Цей хеш використовується для обчислення індексу в масиві для зберігання об'єктаEntry
. Розробники JDK чудово розуміли, що методhashCode()
може бути погано написаний і може повертати дуже велике чи дуже маленьке значення. Для вирішення цієї проблеми вони ввели іншийhash()
метод і передають хешкод об'єкту цьому методу для приведення цього значення до діапазону розміру індексу масиву. - Тепер викликається метод
indexFor(hash, table.length)
для обчислення точної позиції для зберігання об'єктаEntry
. - Наразі головна частина. Як ми знаємо, два неоднакові об'єкти можуть мати однакове значення хешкода, як два різні об'єкти зберігатимуться в однаковому розташуванні в архіві [називається кошиком]?
Відповідь –
LinkedList
. Якщо пам'ятаєте, класEntry
має властивість “next”. Ця властивість завжди вказує на наступний об'єкт у ланцюзі. Така поведінка дуже схожа наLinkedList
.Отже, у разі збігів хешкод, об'єкти Entry зберігаються у формі
LinkedList
. Коли об'єктEntry
необхідно розмістити на конкретному індексі,HashMap
перевіряє, чи існує на цьому місці інший об'єктEntry
? Якщо там немає запису, наш об'єкт збережеться у цьому місці.Якщо в нашому індексі вже знаходиться інший об'єкт, перевіряється його поле
next
. Якщо воно рівнеnull
, наш об'єкт стає наступним вузлом вLinkedList
. Якщо next не дорівнюєnull
, ця процедура повторюється, доки знайдено полеnext
рівнеnull
.Що буде, якщо ми додамо інше значення ключа, що дорівнює доданому раніше? Логічно, що вона має замінити старе значення. Як це відбувається? Після визначення індексу позиції для об'єкта
Entry
, пробігаючи поLinkedList
розташованому на нашому індексі,HashMap
викликає методequals()
для значення ключа для кожного об'єктаEntry
. Всі ці об'єктиEntry
маютьLinkedList
однакове значення хешкода, але методequals()
перевірятиме на справжню рівність. Якщо ключ.equals(k)
буде true , тоді обидва сприйматимуться як однаковий об'єкт. Це викликає заміну лише об'єкта-значення всередині об'єктаEntry
.У такий спосіб
HashMap
забезпечує унікальність ключів. - Насамперед об'єкт ключа перевіряється на
-
Відмінності між інтерфейсами та абстрактними класами?
Це дуже поширене питання, якщо ви проходите співбесіду з програмістом рівня junior. Найбільш значущі відмінності наведені нижче:
- В інтерфейсах Java змінні апріорі
final
. Абстрактні класи можуть містити неfinal
змінні. - Інтерфейс Java беззастережно не може мати реалізації. Абстрактний клас може мати екземпляри методів, що реалізують базову поведінку.
- Складові інтерфейсу повинні бути
public
. Анотація клас може мати модифікатори доступу на будь-який смак. - Інтерфейс має бути реалізований ключовим словом
implements
. Абстрактний клас має бути розширений за допомогою ключового слова extends . - Java клас може реалізовувати безліч інтерфейсів, але може успадковуватися тільки від одного абстрактного класу.
- Інтерфейс повністю абстрактний і не може мати екземплярів. Абстрактний клас також може мати примірників класу, але може бути викликаний, якщо існує метод
main()
. - Абстрактний клас трохи швидше за інтерфейс, тому що інтерфейс передбачає пошук перед викликом будь-якого перевизначеного методу в Java. У більшості випадків це незначна відмінність, але якщо ви пишите критичний час додаток, вам необхідно врахувати і цей факт.
- В інтерфейсах Java змінні апріорі
-
Коли ви перевизначаєте методи
hashCode()
таequals()
?Методи
hashCode()
іequals()
визначені у класуObject
, який є батьківським класом для всіх об'єктів Java. З цієї причини всі об'єкти Java успадковують базову реалізацію цих методів.Метод
hashCode()
використовується для отримання унікального значення integer для цього об'єкта. Це значення використовується визначення розташування кошика, коли об'єкт необхідно зберігати у структурі даних на кшталтHashTable
. За замовчуванням методhashCode()
повертає ціле чисельне подання адресаи пам'яті, де зберігається об'єкт.Метод
equals()
, як передбачає назву, використовується для простої еквівалентності об'єктів. Базова реалізація методу полягає у перевірці посилань двох об'єктів для перевірки їхньої еквівалентності.Зверніть увагу, що зазвичай необхідно перевизначати метод
hashCode()
щоразу, коли перевизначено методequals()
. Це необхідно для підтримки загальної угоди методуhashCode
, в якому говориться, що рівні об'єкти повинні мати рівні хешкоди.Метод equals() повинен визначати рівність відносин (він має бути зворотним, симетричним та транзитивним). На додаток, він повинен бути стійким (якщо об'єкт не змінювався, метод повинен повертати те саме значення). Крім того,
o.equals(null)
завжди має повертати false .hashCode()
повинен бути також стійким (якщо об'єкт не змінювався за умовами методуequals()
, він повинен продовжувати повертати те саме значення.Відношення між двома методами таке: завжди, якщо
a.equals(b)
, тодіa.hashCode()
має бути таким самим, як іb.hashCode()
.
Andrey
26 рівень
Java Core. Запитання до співбесіди, ч. 1
Для тих, хто вперше чує слово Java Core, це фундаментальні основи мови. З цими знаннями вже можна сміливо йти на стажування/інтернатуру. Наведені питання допоможуть вам освіжити знання перед співбесідою, або почерпнути щось нове. Для отримання практичних навичок займайтеся на JavaRush .
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ