Добрый день! Представляю вашему вниманию список вопросов к собеседованию Java Backend, которые я оформлял на протяжении около 2х лет.
Вопросы разбиты по темам: core, collections, concurrency, io, exceptions, которые задают основные направления хода технического собеседования. Звездочками отмечен субъективный (с точки зрения автора) уровень сложности вопроса, в сноске спойлера — краткий ответ на вопрос. Ответ представляет для интервьювера правильное направления развития мысли кандидата.
Опросник не претендует на роль исчерпывающего средства оценки технических навыков кандидата, это скорее еще одна компиляция вопросов и тем по Java и сопуствующим технологиям, принципам и концепциям разработки, коих в сети десятки. Она преследует цель собрать большое число технических вопросов, возникающих на собеседованиях, в удобном для читателей Хабра формате. Некоторые ответы следует воспринимать как мнемоники, «размечивающие» пространство поиска, так что глубже копать нужно уже в документации.
Также не стоит обращать внимание на слишком большое кол-во списков, так как это все-таки не вольное переложение авторских знаний с примесью литературной экспреcсии (что на хабре котируется и собирает аудиторию, особенно в пятницу). Этот список я составил для самого себя как способ структурировать основные тематики и типичные вопросы с собеседований, так что все дополнения и правки только приветствуются.
Это также не руководство к действию — не надо закидывать бедных кандидатов всеми вопросами из списка.
Секция Core:
- Назвать методы Object. (*)
Краткий ответ- toString()
- equals()
- hashCode()
- wait()
- notify()
- notifyAll()
- finalize() — deprecated в Java 9+
- getClass()
Про toString(), equals(), hashCode() и их контракт знать нужно обязательно
- Что такое string-pool? В чем отличие cоздания строки через new от литерала? Что такое String.intern()? (*)
Краткий ответ
string-pool — структура в памяти, хранящая массив всех строк-литералов программы.
String.intern(), соответственно, вернет строку из пула, при наличии таковой. Полезно при сравнениях вида:
new String("hello").intern() == new String("hello").intern()
Т.к без интернирования пришлось бы сравнивать строки через equals, что может быть медленнее при наличии длинных строк. В данном случае возвращается ссылка на один и тот же объект строки из пула, и проверка проходит с true. - Почему хранить пароль предпочтительнее в char[]/byte[], а не в String? (**)
Краткий ответ- Строка в виде литерала сразу раскрывает пароль, плюс она всегда хранится в string-пуле
- byte[]/char[] возможно сбросить после использования, и удалить все ссылки на него
- Привести пример плохой реализации hashCode() (*)
Краткий ответМетод, возвращающий константу, или значения хэшкодов с неравномерным распределением, приводящим к коллизиям - Примитивы, врапперы. Package/unpackage (boxing/unboxing). (*)
Краткий ответ- Типы примитивы не создаются в куче, их жизненный цикл ограничен жизненным циклом стек-фрейма
- Package — создание типа-обертки в хипе для аналогичного типа-примитива, например при объявлении аргумента как Integer, и при передаче int в качестве аргумента. Unpackage — обратная операция
- Сравнение по == и по equals (*)
Краткий ответ- Сравнение по "==" — сравнение по ссылкам
- Сравнение по «equals» — если переопределен equals, то это сравнение эквивалентности объектов по их полям, если нет — по ссылкам на объекты
- Свойства, которым должен удовлетворять equals (**)
Краткий ответ- Рефлексивность: a==a
- Симметричность: a==b, b==a
- Транзитивность: a==b, b==c, a==c
- Консистентность: Множественные вызовы equals должны возвращать один и тот же результат
- Отличия String/StringBuilder/StringBuffer (**)
Краткий ответ- String — иммутабельный байтовый массив
- StringBuilder — helper-класс для построения строк, не предоставляет гарантий синхронизации
- StringBuffer — то же, что и StringBuilder, с synchronized методами
- Приведите пример нарушения симметрии equals (**)
Краткий ответ- Создать класс Point2D c полями x,y: double
- Унаследовать от него класс ColoredPoint2D c доп. полем color
- a: Point2D
- b: ColoredPoint2D
- a.equals(b), !b.equals(a)
- Interface vs Abstract Class. (*)
Краткий ответ- Интерфейс есть средство наследования API, абстрактный класс — средство наследования реализации
- Через интерфейсы возможно осуществлять множественное наследование, абстрактный класс можно наследовать в одном экземпляре.
- В интерфейсе нет возможности определить поля и конструкторы
- override vs overload (*)
Краткий ответ- override — возможность переопределениия поведения метода в типах-потомках
- overload — возможность переопределять метод с одним именем, но разным набором аргументов
- Как в Java сделать утечку памяти? (**)
Краткий ответ- Используя самописный класс стека, при выполнении операции pop() не присваивать предыдущей ссылке значение null.
- Также можно неверно использовать HashMap вместо WeakHashMap для кэширования чего-нибудь большого, например картинок ваших товаров, пользователей и.т.д в. Т.к ссылки на ключи сильные (strong references), значения по этим ключам будут висеть в хипе до
морковкиного заговеньяследующей перезагрузки jvm процесса или удаления ключа из мапы и обнуления ссылки на него. Вообще, кэширование — тема для отдельного разговора - Также, статья (но староватая)
- Как вернуть псевдо-случайную последовательность целых чисел/чисел с плавающей запятой? (**)
Краткий ответ - В чем проблемы Random? (**)
Краткий ответRandom возвращает псевдо-случайную числовую последовательность, основанную на линейном конгруэнтном методе и seed'е, основанном на timestamp'е создания j.u.Random.
Соотвественно, зная время создания, можно предсказать такую последовательность. Такой генератор является детерминированным, и криптографически нестойким. Для исправления этого лучше использовать SecureRandom - GC и различные его виды в JVM. Какой объект считать достижимым. Как происходит сборка мусора (своими словами).(**)
Краткий ответВиды GC:
- Serial Stop the World
- Parallel
- CMS (В чем недостаток по сравнению с Parallel?)
- G1 (Назвать отличие от CMS)
- Shenandoah
Если объект является достижимым из стека или статической области, то он не поддается сборке мусора - Java 8: стримы, функциональные интерфейсы, Optional (**)
Краткий ответStream — интерфейс, предоставляющий функциональные возможности обработки коллекций (filter, map, reduce, peek)
Операции на стримах делятся на терминальные и нетерминальные. Нетерминальные операции модифицируют pipeline операций над коллекцией, при этом не изменяя саму коллекцию, терминальные (например, collect) — проводят действия pipeline'а, возвращают результат и закрывают Stream.
FunctionalInterface — аннотация, предоставляющая возможность использовать лямбды на месте интерфейсов (например, при передаче лямбды в качестве аргумента в метод)
Optional — интерфейс, предохраняющий пользовательский код от nullable ссылок. Оборачивает исходный nullable объект, и предоставляет возможность понять, хранит ли non-nullable объект или нет. - Java 8: Что такое capturing/non-capturing lambda (**)
Краткий ответ- capturing lambda захватывает локальные переменные/аргументы/поля объекта из внешнего скоупа
- non-capturing lambda — не захватывает контекст внешнего скоупа, не инстанцируется каждый раз при использовании
- Новые возможности Java 9 — 11 (**)
Краткий ответ- Новые методы в String
- Java 9: Модульность
- Java 9: Методы в Objects: requireNonNullElse() и requireNonNullElseGet()
- Java 9: List.of(), Set.of(), Map.of(), Map.ofEntries()
- Java 9: Optional.ifPresentOrElse(), Optional.stream()
- Java 10: var type-inference
- Java 11: Files.readString(), Files.writeString()
- Java 11: Local-Variable Syntax for Lambda Parameters — выведение типов у var-аргументов в лямбда-параметрах
- Java 11: JEP 321: HTTP Client
Можно как бонус назвать какие-нибудь:
- JEP 328: Flight Recorder
- JEP 335: Deprecate the Nashorn JavaScript Engine
- JEP 320: Remove the Java EE and CORBA Modules
но это совершенно необязательно, покажет лишь вашу въедливость при чтении JDK'шных Release Notes :) - Swing: рассказать про EDT, как им пользоваться (**)
Краткий ответEDT — тред в котором производится обработка пользовательских действий на UI: движение курсора, нажатие клавиш, скролл, drag'n'drop и.т.д. Соотвественно, все «тяжелые» по времени и ресурсам операции нужно выносить в отдельный worker-тред (SwingUtils.invokeLater(...)
), чтобы не фризить EDT. - Swing: перечислить все виды Layout, которые знаете (**)
Краткий ответ - Generics: В чем преимущество, как работают? Что такое type-erasure? В чем отличие от шаблонов C++? (**)
Краткий ответ- Типы дженерики обеспечивают параметрический полиморфизм, т.е выполнение идентичного кода для различных типов. Типичный пример — коллекции, итераторы
- type-erasure — это стирание информации о типе-параметре в runtime. Таким образом, в байт-коде мы увидим List, Set вместо List<Integer>, Set<Integer>, ну и type-cast'ы при необходимости
- В отличие от дженериков в Java, в С++ шаблоны в итоге приводят к компиляции метода или типа для каждого специфицированного типа параметра (специализация шаблона). Да простят меня здесь адепты С++.
- Generics: Метод принимает ссылку на List<Parent>. Child наследуется от Parent. Можно ли в метод передать List<Child>? (**)
Краткий ответВ типе аргумента нужно указать List<? extends Parent>
- Generics: Ковариантность/контравариантность. Спросить про принцип PECS как бонус
Краткий ответ- Ковариантность — List<? extends T>, если B extends T, то и List<B> extends List<T>
- Контраваринтность — List<? super T>, если B super T, то и List<B> super List<T>
- PECS — Producer-Extends-Consumer-Super, метод отдаёт ковариантный тип, принимает контравариантный (прим. автора — последнее интуитивно не очень понятно)
- Регионы памяти в JVM (**)
Краткий ответJava 8: Metaspace, Old Generation, Young Generation (Eden Space/Survivor Space), Stack, Constant Pool, Code Cache, GC Area.
- Hard-references, weak references, soft-references, phantom-references (***)
Краткий ответ- Hard-references — стандартные ссылки на объекты, которые становится eligible for collection после недостижимости из root set
- Weak-references — объекты могут быть удалены при наличии слабой ссылки на него в любое время
- Soft-references — объекты могут удалятся GC при недостатке памяти
- Phantom-references — объекты не доступны напрямую по ссылкам, перед удалением помещаются в очередь на удаление. Нужны для более безопасной финализации ссылок (вместо finalize)
- Рассказать про classloader'ы и их иерархию. Из за чего, например, может возникать NoClassDefFoundError, NoSuchMethodError? (***)
Краткий ответИерархия classloader'ов
- Bootstrap
- System
- Application
- NoClassDefFoundError может возникнуть, если нужной библиотеки с этим классом нет в classpath
- NoSuchMethodError может возникнуть из-за несовместимости ваших библиотек, если зависимая библиотека A вызывает метод из старой версии библиотеки B, но в classpath есть более новая версия библиотеки B, c другой сигнатурой этого метода
- Какими способами можно сконструировать объект в Java? (**)
Краткий ответ- Через конструктор
- Через статический factory-method
- Через паттерн Builder
- Как идентифицируется класс в Java? (**)
Краткий ответПо его FQDN и classloader'у
- Bytecode: назовите какие-нибудь инструкции и опишите их (**).
Краткий ответЗдесь только краткий список команд:
- aload
- aconst
- astore
* Попросить описать принцип действия стековой машины, как бонус. Допустим, на примере вызова метода.
- Bytecode: invokevirtual, invokestatic, invokespecial — когда используются?
Краткий ответ- invokevirtual — вызовы методов (в Java все методы виртуальные)
- invokestatic — вызовы статических методов
- invokespecial — вызовы конструкторов и приватных методов
Секция Concurrency:
- synchronized. wait/notify/notifyAll. Как есть примитивы аналоги из пакета j.u.c? (**)
Краткий ответДальше тезисы:
- synchronized — ключевое слово, обозначающее скоуп критической секции. Можно ставить напротив объявления метода, или в виде блока в коде.
- wait() — ожидание треда до тех пор, пока он не будет разбужен другим тредом через notify/notifyAll.
- У wait() есть перегруженные версии с таймаутами.
- Тред ставится в wait-set на объекте
- Перед вызовом wait() нужно захватить монитор на данном объекте (через synchronized)
- Магия wait() — он отпускает лок на мониторе объекта после вызова, так чтобы в дальнейшем другой тред мог захватить монитор и вызвать notify/notifyAll
- notify() — будит один из ожидающих тредов, но Важно! — лок на объекте не отпускает, т.е ожидающий тред разбужен будет, но с ожиданием входа в критическую секцию объекта (т.к как будто остановился на synchronized). Так что если после notify есть тяжелые операции, это затормозит ожидающий тред, т.к тред с notify еще не отпустил монитор
- notifyAll() — будут разбужены все треды в wait-set, но при этом далее между тредами происходит contention («сражение») за монитор
- Тред на wait() может быть разбужен также через interrupt, или через spurious wake-up, или по таймауту
- Так что условие выполнения, которого ожидает тред, проверяется в цикле while, а не в if
- Примитив-аналог — Condition
- volatile. happens-before. (**)
Краткий ответ- Ключевое слово volatile устанавливает отношение happens-before над операциями записи-чтения на поле
- Таким образом, операции чтения из читающих тредов будут видеть эффекты записи пишущих тредов.
- В частности, решается проблема double checked locking. Для double/long типов есть проблема атомарности, она решается через атомики
- AtomicInteger, AtomicLong, AtomicBoolean, AtomicDouble (**)
Краткий ответ- Атомики предоставляют возможность изменения переменной в нескольких потоках без эффекта гонок.
- Например, 10 тредов инкрементят AtomicInt = 0, основной тред ждет их выполнения через countdown-latch, далее проверка атомика должна показать 10.
- Основной механизм под капотом атомиков — цикл cas (compare-and-set). На примере increment:
- Читаем старое значение
- Перед set'ом проверяем старое значение, если оно не изменилось, сетаем старое + 1
- Если изменилось, в след. итерации получаем «новое» старое, далее см. п. 1
- Редкий вопрос — как поймать exception из другого треда? (***)
Краткий ответЗарегистрировать Thread.UncaughExceptionHandler - ReentrantLock (**)
Краткий ответПримитив синхронизации, с помощью которого можно установить границы критической секции. Тред, перед входом в критическую секцию должен сделать захват c операциейlock()
, после выхода из крит. секции — сделатьunlock()
. Другой тред в это время ожидает на lock'е (можно указывать таймаут ожидания), либо может проверить доступность черезtryLock()
.
ReentrantLock обязательно нужно освобождать (такое кол-во раз, сколько раз он был захвачен), в противном случае будет thread starvation у других тредов, ожидающих у границы критической секции.
ReentrantLock может быть «честным» (fairness = true), тогда приоритет отдается тредам, ждущих на нем наибольшее кол-во времени, но это вроде как уменьшает производительность
lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() }
- Countdown Latch/Cyclic Barrier (**)
Краткий ответCountdownLatch («защелка») — примитив синхронизации, с помощью которого, например, основной thread может ожидать выполнения работы остальных N тредов. Треды, выполняющие работу, выполняют countDown() на защелке, основной тред ожидает на операции await(). Когда счетчик достигает нуля, основной тред продолжает работу.
Для синхронизации N тредов (все ждут всех) и переиспользования используется CyclicBarrier, ему также можно указывать действие (через Runnable), выполняемое после синхронизации всех-со-всеми - ThreadLocal (**)
Краткий ответКласс, предоставляющий доступ к операциям get/set в области видимости треда. Под капотом содержит кастомную реализацию мапы со слабыми ссылками на ключи-треды. Каждый тред имеет доступ только к своим данным. - Создание singleton? (**)
Краткий ответ- Наивным способом, с проверкой на null статического поля
- Double checked locking (объяснить проблемы double checked locking)
- Простой — инициализация статического поля, или через enum, т.к ленивая инициализация thread-safe по-умолчанию
- Способы запустить поток? (***)
Краткий ответ- Переопределить Thread#run(), запустить через Thread#start()
- new Thread(Runnable).start()
- Через ExecutorService, используя utility-класс Executors
- ConcurrentHashMap (**)
Краткий ответConcurrentHashMap имеет разные реализации в 1.7 и 1.8 (что стало для меня неожиданностью).
Раньше параллелизм основывался на идее сегментирования хэштаблицы на основе заданного уровня параллелизма.
Начиная с Java 8 — это единый массив бакетов с lock-free (локинг на первой ноде бакета с cas-циклом) и конкурентным ресайзингом.
Частичная реализация ConcHashMap (аля Java 8) с нуля от kuptservol - ConcurrentSkipListMap
Краткий ответLock-free структура данных, хранящая упорядоченный набор элементов, с O(log N) временем доступа/удаления/вставки и weakly-consistent итераторами. Под капотом содержит структуру SkipList, предоставляющую собой слои связных списков, от верхнего к нижнему, элементы верхнего списка ссылаются на элементы нижнего списка под ними. Вероятность попадания элемента при вставке в самый нижний список — 1.0, далее она равняется p (либо 1/2, либо 1/4 как правило) — вероятности попадания элемента из нижнего списка в верхний. Таким образом, на самом верхнем будет вставлено минимальное кол-во элементов. Skiplist — вероятностная структура данных. Подробнее и доходчиво про skiplist описано здесь. Полезна, если стоит задача отсортировать поток событий, одновременно читаемый несколькими тредами, которым нужно делать срез по временному интервалу. Более медленные операции по сравнению с ConcurrentHashMap
- Thread states (**)
Краткий ответ- NEW
- RUNNABLE
- BLOCKED(monitor lock)
- WAITING(Thread.join)
- TERMINATED
- Deadlocks, условия наступления, как избежать: (***)
Краткий ответ- Условия наступления — эксклюзивность доступа к ресурсам, наличие циклов в графе ожиданий ресурсов, отсуствие таймаутов на ожидание
- Как избежать — задать порядок доступа к ресурсам. Всегда обращение в порядке либо Thread1->Thread2, либо Thread2->Thread1
- ThreadPoolExecutor — описать механизм работы, св-ва, частности (fixed threadpool, scheduled, single thread executor) (***)
Краткий ответThreadPoolExecutor — средство контроля исполнения параллельных задач, задействует один из свободных тредов в общем пуле, или ставит задание в очередь, если таковых нет, или достигнуты определенные условия (ниже)
Основными св-вами ThreadPoolExecutor являются corePoolSize и maxPoolSize. Если текущее количество тредов в пуле < corePoolSize — новый тред будет создаваться в независимости от того, есть ли в пуле незанятые треды. В промежутке между corePoolSize и maxPoolSize тред будет создаваться в том случае, если заполнена очередь задач, и удаляться спустя keepAliveTime. Если кол-во тредов стало >= maxPoolSize — новые треды не создаются, а задачи ставятся в очередь.
Есть возможность регулировать поведение очереди:
- Direct handoffs: немедленная передача задачи тредпулу. Нет понятия очереди задачи. Если свободных тредов нет — кидается exception. Применимо при неограниченных maxPoolSize, но нужно понимать проблему при быстрых записях и медленных чтениях, что может спровоцировать непомерное потребление ресурсов
- Unbounded queues: очередь без ограничений. Задачи будут добавляться в нее при превышении corePoolSize, при этом maxPoolSize будет игнорироваться. Unbounded queues имеют проблемы потребления ресурсов при больших нагрузках, но сглаживают рост тредов при пиках.
- Bounded queues: очередь с ограничениями. Задачи будут добавляться в очередь до достижения некоего capacity. Для достижения наилучшей производительности нужно понимать размеры corePoolSize и самой очереди и чем можно пожертвовать — перфомансом (малый corePoolSize и большая очередь), или же памятью (ограниченная очередь, большой corePoolSize)
Частности ThreadPoolExecutor:
- ScheduleThreadPoolExecutor — применяется для периодичных по времени задач
- fixed thread pool — частность ScheduleThreadPoolExecutor'а с настроенным corePoolSize и unbounded queue
- single thread executor — тредпулл, c сorePoolSize = 1, гарантирующий последовательное выполнение задач из очереди
Секция Collections:
- Рассказать про java.util.collection. (*)
Краткий ответ- Iterable — реализуют коллекции, по которым можно проитерироваться
- Сollection — общий интерфейс для коллекций
- List (стандартная реализация ArrayList) — список с массивом элементов, с возможностью случайного доступа элемента по индексу за O(1), вставкой/удалением со сложностью O(n)
- Set (стандартная реализация HashSet) — мн-во элементов без дубликатов. Нет возможности доступа по индексу, есть вставка и удаление за O(1)
- Map (стандартная реализация HashMap) — мн-во пар элементов «ключ-значение». Доступ по ключу/добавление/удаление за O(1) при оптимальном случае, O(n) — при вырожденном
Обзор по коллекциям от JournalDev - Устройство ArrayList, LinkedList, HashMap, HashSet. Когда следует использовать. Контракт equals/hashcode для Map, Set (*)
Краткий ответОбзор по коллекциям от JournalDev - Итератор по коллекции, его св-ва и интерфейс (*)
Краткий ответ- Может только один раз проходить по коллекции. Для прохождения в двух направлениях есть ListIterator
- Если в foreach цикле структурно модифицировать коллекцию, при последующем обращению к элементу (неявно через итератор) получим ConcurrentModificationException (fail-fast)
- hasNext(), next() — основные методы
- Hashtable vs HashMap (*)
- Java 8,11: новые методы в Map (**)
Краткий ответ- Java 8: compute
- Java 8: computeIfAbsent
- Java 8: computeIfPresent
- Java 8: forEach
- Java 8: putIfAbsent
- Java 11: factory-методы of()
- LinkedHashMap, зачем он нужен (**)
Краткий ответ- Позволяет сохранять порядок вставки пар key-value в Map
- Каждый entry содержит помимо hash, value, next (следующий элемент в бакете) также поля, указывающие на предыдущий и следующий элементы относительно порядка вставки
- Устройство TreeMap (**)
Краткий ответ- Cбалансированное красно-черное дерево
- Реализует интерфейс NavigableMap, что позволяет возвращать из него элементы, больше (меньше) указанного, либо range элементов, находящийся в определенных границах
- Какой контракт Comparator должен соблюдать?
Краткий ответБыть согласованным с equals() - Есть ли способ сделать enum ключом Map? (**)
Краткий ответEnumMap — массив, по размеру соотвествующий кол-ву элементов в enum'е. Индекс элемента массива соотвествуют ordinal'у из enum'а - Расскажите про CopyOnWriteArrayList/CopyOnWriteHashSet (**)
Краткий ответ- СopyOnWriteArrayList — иммутабельный list, при добавлении/апдейте/удалении элементов из которого пользователь получает новую модифицированную копию данного списка
- СopyOnWriteHashSet — иммутабельный set, при добавлении/апдейте/удалении элементов из которого пользователь получает новую модифицированную копию данного set'а
- IdentityHashMap — когда используется? (**)
Краткий ответIdentityHashMap — используется, только если нужно проверять идентичность двух ссылок, а не эквивалентность двух объектов по ним. Например, если нужно отслеживать уже посешенные ноды в графе, или строить карту объекты-прокси. IdentityHashMap представляет из себя не классическую хэштаблицу со связанными списками, это linear probing map (бонус за объяснение работы linear probing) - Интерфейсы Queue/Deque и их реализации (***)
Краткий ответ- Queue — коллекция, предоставляющая возможности упорядочения элементов в порядке вставки согласно принципу FIFO. Поддерживает два набора операций добавления/удаления/взятия элемента с конца — с возвращением спец. значения при исключительной ситуации (пустая или заполненная очередь), и с киданием exception'а
- Deque — коллекция, предоставляющая возможность вставки значений в начала и в конец, позволяющая организовать очереди по принципам FIFO/LIFO. Предпочтительна для реализации стэка вместо Stack. Ниже приведен набор операций для Deque:
- BlockingQueue — очередь, блокирующая операции чтения take при пустой очереди или операции записи put при полной очереди. Есть наборы операций и с неблокирующей семантикой. Ниже определен полный список операций чтения/записи.
- ArrayBlockingQueue — кольцевой bounded-buffer с внутренним capacity и fairness policy. Все операции чтения и записи защищены внутренним lock'ом (j.u.c.l.ReentrantLock), неявно связанным с двумя condition'ами — notFull, notEmpty. Соотвественно, если пишущий тред, захвативший общий lock, застрял на condition'е notFull, он неявно освобождает lock, чтобы читающий тред мог освободить очередь, и сигнализировать producer'у о том, что можно продолжать. Ровно и наоборот, читающий тред также захватывает общий lock, застревает на notEmpty, неявно освобождает lock, чтобы пишущий тред мог положить новый элемент и просигналить о непустой очереди. Т.е семантика такая же, как и у synchronized/wait/notify/notifyAll. Fairness = true позволяет предотвратить ситуации thread starvation для консьюмеров и продюсеров, но снизит производительность.
- LinkedBlockingQueue — очередь, в которой элементы добавляются в связанный список, основанная на two-lock queue, описанного в статье Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms by Maged M. Michael and Michael L. Scott.
Секция IO:
- InputStream, OutputStream и их buffered версии (*)
Краткий ответДалее, для краткости InputStream — is, OutputStream — os
is — побайтное чтение из сокета/файла/строки/другого байтового массива
os — побайтная запись в сокет/файл/другой байтовый массив
Buffered-версии нужны для оптимизации чтения/записей через отдельный буффер - Зачем нужен Reader? (*)
Краткий ответReader позволяет указать Charset при чтении - Serializable, serialVersionUID (*)
Краткий ответКлассы, чьи объекты подвергаются сериализации/десериализации должны реализовывать marker интерфейс Serializable (и иметь статическое поле serialVersionUID для указании при сериализации, с какой версией класса данный объект был сериализован. Если serialVersionUID из сериализованного представления не совпадает c serialVersionUID класса «на том конце провода» — то кидается exception)
На практике, уже довольно редко используется, т.к тем же Jackson/GSON не обязательно наличие данного интерфейса для сериализации - try-with-resources. AutoCloseable (*)
Краткий ответtry-with-resources — краткая замена стандартному try..catch..finally. Закрывает ресурс после выхода из секции try-with-resources. Ресурс должен имплементить интерфейс AutoCloseable.
«Ресурс» в данном контексте — это класс, представляющий cобой соединение/cокет/файл/поток
try (InputStream is = new FileInputStream("/path/to/file.txt")) { ... }
Секция Exceptions:
- Отличие checked-exception/unchecked-exception. Error, Exception, RuntimeException (*)
Краткий ответ- Checked exceptions (проверяемые исключения). В JDK представлены классом Exception. Исключения, которые нельзя проигнорировать, их обязательно нужно обрабатывать, либо специфицировать в сигнатуре метода, для обработки выше. Как правило, считаются дурным тоном, т.к код со мн-вом конструкций try..catch плохо читабелен, к тому же добавление новых пробрасываемых исключений в сигнатуре метода может сломать контракт вызова у пользователей данного метода.
- Unchecked exceptions (непроверяемые исключения). В JDK это класс RuntimeException. Можно игнорировать, не требуют обработки через try..catch, или указания в сигнатуре через throws. Минус такого подхода — у вызывающей стороны нет никакого понимания, как обрабатывать ситуацию, когда под капотом «рванет»
- Error — ошибки, кидаемые JVM в результате нехватки памяти (OutOfMemoryError), переполнения стэка (StackOverflowError) и.т.д
Полезные ссылки:
- Утечки памяти — статья на TopTal.com
- Частичная реализация ConcHashMap (аля Java 8) с нуля от kuptservol
- Структура SkipList
- Обзор коллекций от JournalDev
- Обзор коллекций от vedenin1980
- Обзор пакета java.util.concurrency (Java 7) от Melnosta
- Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms by Maged M. Michael and Michael L. Scott (для сильных духом)
BugM
Лучше алгоритмы на доске спрашивайте. По этим вопросам можно взять человека который потратил пару дней на подготовку к ним, и не взять всех остальных.
В вопросах в основном мелочи которые при необходимости гуглятся за 15 минут, но наизусть не помнятся если совсем недавно именно с ними не работал.
Sm1le291
Вы предлагаете другую крайность, мало кто в состоянии на доске написать алгоритмы, если заранее не готовился. Опять же проверите готовился ли человек к алгоритмам, которые никогда не будет использовать на практике
BugM
Вы fizzbuzz или определение простое число или нет (любым способом) на доске не напишите? Не Гугл же.
Это гораздо лучше чем спрашивать «Generics: Ковариантность/контравариантность.» большая часть разработчиков слов то таких не знает. Ибо не надо. А если понадобится то быстрогугл сразу решает пробелму.
sshikov
Проблему непонимания ковариантности гугл не решает. Насчет «не надо» — ну в общем да, не особо часто.
BugM
Определение из Вики:
Чтобы найти прочитать и понять 5 минут достаточно. В работе, разговорах итд не используется никогда. И через неделю-месяц это определение забудется за ненадобностью.
И там все в списке примерно такое. «Назвать методы Object» equals и hashCode я назову сразу. toString немного подумав. Остальное только по документации или исходникам JDK. Ибо оно не используется практически никогда.
sshikov
>Чтобы найти прочитать и понять 5 минут достаточно.
Не совсем согласен. Во-первых, увы не всем достаточно, а главное, оно может и появляется, какое-то понимание, но очень краткосрочное.
>И через неделю-месяц это определение забудется за ненадобностью.
Ну я собственно этоже самое и имел в виду, когда говорил, что гугление не решает проблему понимания этой темы. Потому что настоящее понимание позволяет такое же определение написать/вывести, исходя по большей части из «здравого смысла».
BugM
Представим себе собеседование. Судя по формулировкам на нем прямо так и спросят. А расскажите о Ковариантность/контравариантность в дженериках Джавы. Тут любой кто не помнит определение будет в тупике и больше чем «Эээ. А что эти слова значат?» Выдавить из себя не сможет. В итоге собеседование завалено, хотя человек просто не знает определение и слов. Которые не используются в работе.
И опять таки остальное ровно такое же.
«Почему хранить пароль предпочтительнее в char[]/byte[], а не в String? (**)»
Вот прямо так ведь и спросят. Я честно затруднюсь ответить. На мой вкус хуже. Потому что граблей на которые можно наступить заметно больше, а плюсов нет.
И даже после их «правильного» ответа
я все еще затрудняюсь. И скорее всего буду спорить. Если у зломышленника есть доступ к памяти процесса в котором пароли проверяются, то уже спасать пароли поздно. Пароли точно утекли. Исходим из этого.
Защита «Строка в виде литерала сразу раскрывает пароль, а переменная password четко видимая в любом отладчике видимо не раскрывает?» как-то по детски выглядит.
sshikov
>Которые не используются в работе.
Вы никогда List что-ли не пишете? А как только вы его написали, и ежели у вашего MySuperObject есть наследники, вы должны понимать, что у вас имеет место, ко/контра либо инвариантность. А если не понимаете — вероятность накосячить весьма велика. Названий можете не знать, не вопрос. Но реально не понимать разницу, хотя бы на интуитивном уровне...? А аббревиатуру LSP вы тоже никогда не видели что-ли? Я если что не конкретно про кого-то лично, пусть будет абстрактный разработчик в вакууме.
Ну то есть, я могу себе представить деятельность, где это не нужно совсем. И легко могу представить разработчика, который вообще не понимает generics, при этом достаточно долго и сравнительно успешно работает. Но могу и наоборот. В принципе, второй случай — это наверное скорее разработка фреймворка. А первый — использование.
>И опять таки остальное ровно такое же.
Ну, про остальное в целом — скорее согласен.
BugM
Особенность джавы в том что это работает в одну сторону. А мы о Джаве говорим. И соответвенно эти определения получаются чисто академическими. Так что, что как и кого наследует в шаблонах расскажу без проблем. А вот про вариативности извините, не смогу. Они были в лучше случае на каком-то курсе и успешно забылись за ненадобностью. Соответвенно вопрос на собеседовании в котором будут они звучать вызовет замешательство и непонимание.
Мне даже вики не помогла.
sshikov
>Так что, что как и кого наследует в шаблонах расскажу без проблем. А вот про вариативности извините, не смогу.
Так этож одно и тоже, по сути. То есть, фактически вы не знаете названий для этого?
LSP — это принцип подстановки Лисков. Ну то есть, я опять же вполне допускаю вариант, что не зная ничего этого вы можете успешно много лет работать. Но только до какого-то предела — примерно пока вам не потребуется написать свою «коллекцию».
BugM
Если по русски "Наследники в Override функциях не должны делать ничего неожиданного." Я даже больше сказать могу. Весь код должен работать самым ожидаемым способом. Не надо форматировать диск в функции getValue() оверрайдит она что-то или нет не важно.
Это действительно такая странная и редкая вещь вещь что ее надо знать и обсуждать отдельно? Вроде обычный здравый смысл.
sshikov
То что вы говорите — это совсем не про ко/контравариантность.
BugM
Это про написание нормального кода. Высказанное обычным языком. Без терминов которые не используются в реальности. Ну и максимально близко к теме.
Начали с одного из предлагаемых вопросов. Где предполагалось что пришедший на собеседование знает что означает слово ковариантность и выдаст определение из учебника. И моего возражения что это очень странный вопрос для собеседования.
Neikist
Я вот слова эти знаю, даже знаю примерно какую ситуацию они описывают, но вечно путаю какое из них что значит ибо они черт побери почти одинаково написаны/звучат. А на практике я их встречал только в документации да книгах, в разговорах с коллегами ни разу не слышал.