{
  "assessmentTests": {
    "java_test": {
      "name": "Тест Java",
      "desc": "30 сценарных вопросов по основному синтаксису, ООП, коллекциям, generics, исключениям и параллелизму — узнайте, соответствует ли ваша Java тому, что объявления о вакансиях имеют в виду под компетентностью в Java.",
      "recommendation": "Ваш профиль навыков Java",
      "results": {
        "beginner": {
          "name": "Начинающий",
          "desc": "Вы можете писать работающие классы и методы, но упущенные вами вопросы сосредоточены вокруг идентификации и значений по умолчанию, а не синтаксиса — String == vs .equals, почему new String(\"x\") никогда не переиспользует пул строк, что числовое поле по умолчанию против поля-обёртки. Это не о плохом знании Java; это специфичные для Java правила, которые обманывают людей, выучивших язык методом проб и ошибок, а не из понимания того, как ссылки и примитивы на самом деле работают под капотом. На работе они важны, потому что каждое из них — место, где код компилируется, запускается и молча делает неправильное.",
          "recommendation": "Начните с трёх вещей в этом порядке: почему == при сравнении двух объектов String сравнивает ссылки, а не символы (и .equals — это то, что вы почти всегда хотите), как int overflow молча переходит вместо выброса исключения, и почему неинициализированное поле Integer по умолчанию null, а поле int по умолчанию 0. Официальные учебники Java от Oracle и Baeldung оба рассматривают все три с запустимыми примерами."
        },
        "intermediate": {
          "name": "Средний уровень",
          "desc": "Вы удобно справляетесь с кодом приложения каждый день — классы, коллекции, прямолинейный поток управления — и не будете замедлены рутинной работой. Разница между здесь и Продвинутым в основном в том, что происходит, когда правила Java взаимодействуют с параллелизмом и generics: статический метод, разрешённый по объявленному типу вместо ожидаемого объекта, порядок итерации HashMap, на который вы молчаливо положились, ConcurrentModificationException при удалении элемента посредине цикла. Это баги, которые проходят при быстром прочтении и появляются только под определённым условием выполнения.",
          "recommendation": "Сосредоточьтесь на том, как статический взгляд компилятора на ваш код отличается от того, что работает: разрешение перегрузки и скрывание статического метода по объявленному типу вместо типа выполнения, почему HashMap не даёт гарантию порядка итерации, и почему изменение списка во время итерации выбрасывает ConcurrentModificationException. Затем стирание generics, так как это обманывает людей, которые уже знают коллекции отдельно."
        },
        "advanced": {
          "name": "Продвинутый",
          "desc": "Это уровень, который большинство объявлений о вакансиях имеют в виду под «сильная Java». Вы читаете класс со статическими блоками, блоками инициализации экземпляров и несколькими конструкторами и можете предсказать точный порядок их запуска, вы знаете, почему блок finally может молчаливо отбросить значение return блока try, и вы обращаетесь к try-with-resources вместо ручного finally-close, потому что знаете гарантию порядка, которую она даёт. То, что отделяет эту категорию от вершины — параллельная и интерфейсная сторона работы: что synchronized на самом деле блокирует, что volatile делает и не гарантирует, и как разрешаются конфликты методов по умолчанию.",
          "recommendation": "Углубитесь в части, которые защищают код, который касаются другие потоки и другие интерфейсы: разница между тем, что блокирует synchronized метод экземпляра против synchronized статического метода, почему volatile даёт видимость, но не атомарность для counter++, и как Java вынуждает вас вручную разрешить diamond конфликта методов по умолчанию. Материал Baeldung и Java Concurrency in Practice по Java Memory Model — естественная следующая остановка для обоих."
        },
        "expert": {
          "name": "Эксперт",
          "desc": "Вы набрали максимум во всех разделах — основной синтаксис и типы, ООП механика и область видимости, коллекции и generics, исключения, параллелизм и идиомы. На практике это означает, что вам можно дать класс незнакомца и объяснить, почему происходит ошибка компиляции, исключение выполнения или молчаливо неправильное значение, а не просто что синтаксис говорит, что должно произойти — это более сложный и ценный навык. На этом уровне сам язык редко является ограничивающим фактором; ограничение обычно — дизайн параллелизма или форма данных под ним.",
          "recommendation": "Возвраты теперь в дизайне и диагностике: чтение дампа потока перед предположением, что зависание — это deadlock, выбор между synchronized, java.util.concurrent блокировками и atomics как компромисс вместо значения по умолчанию, и дизайн stream-pipeline, который остаётся ленивым по причине, а не случайно. Если вас отбирают на роль, опишите баг параллелизма, как упущенное happens-before ребро или ConcurrentModificationException, которую вы нашли в production, вместо названия функций Java — это демонстрирует рассуждение, не только словарный запас."
        }
      },
      "questions": [
        {
          "question": "Вы создаете два объекта String: String a = new String(\"cat\"); String b = new String(\"cat\"); На что вычисляется a == b?",
          "options": [
            {
              "icon": "",
              "label": "true — литералы String с одинаковыми символами всегда являются одним объектом"
            },
            {
              "icon": "",
              "label": "true — new String(...) переиспользует пул строк, поэтому идентичный текст всегда общий один объект"
            },
            {
              "icon": "",
              "label": "Ошибка компиляции — == нельзя использовать для сравнения объектов String"
            },
            {
              "icon": "",
              "label": "false — new String(...) всегда выделяет новый объект в heap, поэтому == сравнивает две разные ссылки, хотя .equals(b) вернул бы true"
            }
          ]
        },
        {
          "question": "Integer x = 100; Integer y = 100; System.out.println(x == y); затем Integer x2 = 200; Integer y2 = 200; System.out.println(x2 == y2); Что выводят две строки?",
          "options": [
            {
              "icon": "",
              "label": "true затем true — объекты Integer с автоупаковкой всегда кешируются независимо от значения"
            },
            {
              "icon": "",
              "label": "false затем false — == на объектах Integer всегда сравнивает ссылки, поэтому равные значения никогда не совпадают"
            },
            {
              "icon": "",
              "label": "true затем false, но только потому что 200 переполняет byte — граница кеша не связана с -128..127"
            },
            {
              "icon": "",
              "label": "true затем false — автоупакованные значения Integer от -128 до 127 кешируются и общие, поэтому 100 переиспользует один объект, но 200 выходит за границы кеша и упаковывается в два отдельных объекта"
            }
          ]
        },
        {
          "question": "int max = Integer.MAX_VALUE; System.out.println(max + 1); Что происходит?",
          "options": [
            {
              "icon": "",
              "label": "Выбрасывает ArithmeticException при переполнении целого числа"
            },
            {
              "icon": "",
              "label": "Выводит Integer.MAX_VALUE снова, потому что Java фиксирует максимум типа"
            },
            {
              "icon": "",
              "label": "Выводит Integer.MIN_VALUE — арифметика int молчаливо оборачивается при переполнении вместо выброса"
            },
            {
              "icon": "",
              "label": "Это ошибка компиляции — компилятор обнаруживает переполнение заранее"
            }
          ]
        },
        {
          "question": "System.out.println(0.1 + 0.2 == 0.3); Что выводит это и почему?",
          "options": [
            {
              "icon": "",
              "label": "false — 0.1, 0.2 и 0.3 не могут быть представлены точно в двоичной плавающей точке, поэтому сумма содержит ошибку округления, которая делает ее не совпадающей с 0.3"
            },
            {
              "icon": "",
              "label": "true — Java округляет арифметику double до ближайшего представимого десятичного перед сравнением"
            },
            {
              "icon": "",
              "label": "true — сложение двух double всегда точно для значений с двумя десятичными цифрами"
            },
            {
              "icon": "",
              "label": "Выбрасывает исключение, потому что == не определен для double в Java"
            }
          ]
        },
        {
          "question": "Класс объявляет два поля экземпляра без инициализатора: int count; и Integer total; До запуска конструктора, каковы их значения по умолчанию?",
          "options": [
            {
              "icon": "",
              "label": "Оба по умолчанию 0, потому что Integer автоупаковывается в int при инициализации поля"
            },
            {
              "icon": "",
              "label": "count равно 0 и total равно 0, упакованное автоматически при первом обращении"
            },
            {
              "icon": "",
              "label": "count равно 0 и total равно null — примитивные числовые поля по умолчанию равны нулю, но неинициализированное поле-обертка ссылается на null как любая другая ссылка на объект"
            },
            {
              "icon": "",
              "label": "Оба по умолчанию null до явного присваивания, потому что Java не имеет неявных числовых значений по умолчанию"
            }
          ]
        },
        {
          "question": "Цикл работает 1000 раз, каждый раз выполняя result += \"x\"; на String result. Что на самом деле происходит под капотом на каждой итерации?",
          "options": [
            {
              "icon": "",
              "label": "Внутренний массив символов существующего объекта String расширяется на месте"
            },
            {
              "icon": "",
              "label": "Java автоматически объединяет конкатенации и выделяет только один финальный объект String"
            },
            {
              "icon": "",
              "label": "Это компилируется в единственный вызов StringBuilder.append(), общий для всех 1000 итераций, без дополнительного объекта, выделяемого на итерацию"
            },
            {
              "icon": "",
              "label": "На каждой итерации создается совершенно новый объект String и result переприсвоивается на него — предыдущий объект String отбрасывается, потому что String неизменяемый и += не может его изменить на месте"
            }
          ]
        },
        {
          "question": "final List<String> names = new ArrayList<>(); Какое из этих утверждений верно?",
          "options": [
            {
              "icon": "",
              "label": "names.add(\"Ana\") компилируется и работает нормально — final только предотвращает переприсваивание самой ссылки names, не мутирование объекта, на который она указывает"
            },
            {
              "icon": "",
              "label": "names.add(\"Ana\") — ошибка компиляции, потому что final делает сам список неизменяемым"
            },
            {
              "icon": "",
              "label": "Список потокобезопасен для конкурентных записей, потому что он был объявлен final"
            },
            {
              "icon": "",
              "label": "final на локальной переменной не имеет эффекта, если только тип также не объявлен неизменяемым"
            }
          ]
        },
        {
          "question": "int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println(a == b); System.out.println(Arrays.equals(a, b)); Что выводят две строки?",
          "options": [
            {
              "icon": "",
              "label": "false затем true — == сравнивает ссылки массива (два разных объекта массива), а Arrays.equals сравнивает элементы"
            },
            {
              "icon": "",
              "label": "true затем true — массивы с идентичным содержимым всегда один объект в Java"
            },
            {
              "icon": "",
              "label": "false затем false — Arrays.equals работает только для объектных массивов, а не примитивных int массивов"
            },
            {
              "icon": "",
              "label": "true затем false — == на массивах сравнивает содержимое, а Arrays.equals избыточен"
            }
          ]
        },
        {
          "question": "void show(Object o) { print(\"Object\"); } void show(String s) { print(\"String\"); } Object ref = \"hello\"; show(ref); Какая перегрузка выполняется и почему?",
          "options": [
            {
              "icon": "",
              "label": "show(String) выполняется, потому что Java проверяет фактический класс объекта во время выполнения перед выбором перегрузки"
            },
            {
              "icon": "",
              "label": "show(Object) выполняется — разрешение перегрузки решается во время компиляции с использованием объявленного типа переменной, а не фактического типа объекта во время выполнения"
            },
            {
              "icon": "",
              "label": "Это ошибка компиляции — Object не может быть передан где существует более специфичная перегрузка"
            },
            {
              "icon": "",
              "label": "Оба метода выполняются, по одному разу, потому что Java разрешает перегрузки, пытаясь каждый матч"
            }
          ]
        },
        {
          "question": "Класс Base имеет static void greet() { print(\"Base\"); }. Класс Derived extends Base и также объявляет static void greet() { print(\"Derived\"); }. Вы пишете: Base ref = new Derived(); ref.greet(); Что выводит?",
          "options": [
            {
              "icon": "",
              "label": "Derived — статические методы переопределяются как методы экземпляров, следуя типу фактического объекта во время выполнения"
            },
            {
              "icon": "",
              "label": "Это ошибка компиляции — статические методы не могут быть вызваны через ссылку на экземпляр"
            },
            {
              "icon": "",
              "label": "Base — статические методы не полиморфны; вызов одного через ссылку разрешается по объявленному типу ссылки во время компиляции, а не типу объекта во время выполнения"
            },
            {
              "icon": "",
              "label": "И Base и Derived выводят, потому что вызов разрешается и скрытой и скрывающей версией"
            }
          ]
        },
        {
          "question": "Внутри метода вы пишите int count = 0; затем пытаетесь обратиться к count изнутри лямбды, переданной другому методу. Что должно быть верно о count, чтобы это скомпилировалось?",
          "options": [
            {
              "icon": "",
              "label": "count должно быть effectively final — никогда не переприсвоено где-либо после начального значения — потому что лямбда захватывает снимок, а не живую ссылку на изменяемую локальную переменную"
            },
            {
              "icon": "",
              "label": "Ничего — любая локальная переменная может быть свободно прочитана и переприсвоена изнутри лямбды"
            },
            {
              "icon": "",
              "label": "count должно быть объявлено volatile, чтобы лямбда всегда видела его последнее значение"
            },
            {
              "icon": "",
              "label": "count должно быть полем, не локальной переменной — лямбды не могут захватывать локали вообще"
            }
          ]
        },
        {
          "question": "void rename(StringBuilder sb) { sb = new StringBuilder(\"new\"); } вызывается как StringBuilder original = new StringBuilder(\"old\"); rename(original); Какой original после вызова?",
          "options": [
            {
              "icon": "",
              "label": "Становится \"new\" — объекты передаются по ссылке в Java, поэтому переприсваивание параметра изменяет переменную вызывающего тоже"
            },
            {
              "icon": "",
              "label": "Становится \"new\" только для изменяемых типов как StringBuilder, но не для неизменяемых типов"
            },
            {
              "icon": "",
              "label": "По-прежнему \"old\" — Java передает саму ссылку по значению, поэтому переприсваивание параметра внутри метода только переводит локальную копию ссылки, оставляя исходный вызывающего нетронутым"
            },
            {
              "icon": "",
              "label": "Выбрасывает исключение во время выполнения, потому что sb был переприсвоен внутри метода"
            }
          ]
        },
        {
          "question": "Класс имеет блок статической инициализации, блок инициализации экземпляра и конструктор в таком порядке исходного кода. Вы создаете два объекта этого класса один за другим. В каком порядке они выполняются?",
          "options": [
            {
              "icon": "",
              "label": "Блок static выполняется ровно один раз при первой загрузке класса; затем для каждого объекта блок экземпляра выполняется и затем выполняется тело конструктора"
            },
            {
              "icon": "",
              "label": "Все три выполняются заново в порядке исходного кода для каждого создаваемого объекта"
            },
            {
              "icon": "",
              "label": "Конструктор выполняется первым для каждого объекта, затем блок экземпляра, затем блок static один раз в конце"
            },
            {
              "icon": "",
              "label": "Блок static выполняется один раз на объект, прямо перед блоком экземпляра"
            }
          ]
        },
        {
          "question": "Класс имеет void log(String s) и void log(String... args). Вы вызываете log(\"hi\"). Какой выполняется?",
          "options": [
            {
              "icon": "",
              "label": "log(String... args) — перегрузки varargs всегда предпочтены когда оба применимы"
            },
            {
              "icon": "",
              "label": "Это ошибка компиляции — вызов неоднозначен между двумя перегрузками"
            },
            {
              "icon": "",
              "label": "log(String s) — когда перегрузка с фиксированной арностью точно совпадает, Java всегда предпочитает ее перегрузке varargs, которая используется только как последняя мера"
            },
            {
              "icon": "",
              "label": "Та, что объявлена первой в исходном файле"
            }
          ]
        },
        {
          "question": "Первая строка конструктора вызывает this(0); для делегирования другому конструктору в том же классе. Где разрешено это появляться?",
          "options": [
            {
              "icon": "",
              "label": "Где угодно в теле конструктора, пока он выполняется перед тем, как объект вернется"
            },
            {
              "icon": "",
              "label": "Только как очень первое выражение конструктора — this() (или super()) должно быть первой строкой, и конструктор не может вызвать и this() и super()"
            },
            {
              "icon": "",
              "label": "Только как последнее выражение после завершения всей инициализации полей"
            },
            {
              "icon": "",
              "label": "Только в конструкторах явно отмеченных как делегирующие, используя ключевое слово delegate"
            }
          ]
        },
        {
          "question": "Вам нужен список, в который будут очень часто вставляться элементы в начало, а произвольный доступ для чтения редкий. Какая комбинация это правильный структурный выбор и почему?",
          "options": [
            {
              "icon": "",
              "label": "ArrayList — его смежный backing array делает каждую операцию, включая вставку в начало, быстрее чем связанная структура"
            },
            {
              "icon": "",
              "label": "Они работают идентично, потому что оба реализуют интерфейс List с одинаковыми гарантиями времени"
            },
            {
              "icon": "",
              "label": "LinkedList — вставка в начало это O(1) потому что она просто переписывает узел, в то время как ArrayList должна сдвинуть каждый существующий элемент на одно, делая вставку в начало O(n)"
            },
            {
              "icon": "",
              "label": "LinkedList, потому что он поддерживает произвольный доступ в O(1) в то время как ArrayList нет"
            }
          ]
        },
        {
          "question": "Вы вставляете пять записей в простой HashMap в определенном порядке, затем итерируете по нему с циклом for-each. В каком порядке выходят записи?",
          "options": [
            {
              "icon": "",
              "label": "В том же порядке, в котором были вставлены записи, поскольку Java maps всегда сохраняют порядок вставки"
            },
            {
              "icon": "",
              "label": "Никакой гарантированный порядок — порядок итерации HashMap зависит от размещения bucket хеша, а не порядка вставки, и может даже измениться между запусками; LinkedHashMap — это то, что сохраняет порядок вставки"
            },
            {
              "icon": "",
              "label": "Отсортирован по ключу автоматически, так же как TreeMap себя ведет"
            },
            {
              "icon": "",
              "label": "Обратный порядок вставки, потому что HashMap внутри использует стек"
            }
          ]
        },
        {
          "question": "Сколько null ключей может одновременно держать простой java.util.HashMap и сколько null значений?",
          "options": [
            {
              "icon": "",
              "label": "Нет null ключей и нет null значений не разрешены ни в какой реализации Map"
            },
            {
              "icon": "",
              "label": "Один null ключ максимум, и любое число null значений — HashMap допускает один null ключ, в отличие от Hashtable, которая не позволяет ни один"
            },
            {
              "icon": "",
              "label": "Неограниченно null ключей и неограниченно null значений, так как null рассматривается как любой другой ключ"
            },
            {
              "icon": "",
              "label": "Один null ключ и одно null значение максимум, оба ограничены одним"
            }
          ]
        },
        {
          "question": "Вы итерируете List с циклом for-each и вызываете list.remove(item) прямо на списке изнутри тела цикла, на некоторых но не всех элементах. Что происходит?",
          "options": [
            {
              "icon": "",
              "label": "Это работает правильно и удаляет ровно предназначенные элементы"
            },
            {
              "icon": "",
              "label": "Это молчаливо пропускает элемент после удаленного, но иначе завершается без ошибки"
            },
            {
              "icon": "",
              "label": "Это выбрасывает IndexOutOfBoundsException как только цикл достигает старого размера списка"
            },
            {
              "icon": "",
              "label": "Это выбрасывает ConcurrentModificationException — изменение структуры списка напрямую в то время как неявный итератор его обходит обнаруживается и отклоняется; Iterator.remove() должен быть использован вместо"
            }
          ]
        },
        {
          "question": "Во время выполнения, учитывая List<String> list, что вы на самом деле можете определить о его параметре дженерика через reflection или instanceof?",
          "options": [
            {
              "icon": "",
              "label": "Вы можете вызвать list.getElementType() чтобы получить String.class во время выполнения"
            },
            {
              "icon": "",
              "label": "instanceof List<String> компилируется и правильно проверяет тип элемента"
            },
            {
              "icon": "",
              "label": "JVM сохраняет параметр типа как скрытые метаданные доступные через list.getGenericType()"
            },
            {
              "icon": "",
              "label": "Ничего — информация о типе генерика стирается во время компиляции, поэтому во время выполнения объект — это просто List, и нет способа вернуть был ли это List<String> или List<Integer>"
            }
          ]
        },
        {
          "question": "switch (day) { case 1: case 2: print(\"Weekday\"); break; case 6: print(\"Saturday\"); case 7: print(\"Sunday\"); break; default: print(\"?\"); } Если day равно 6, что выводит?",
          "options": [
            {
              "icon": "",
              "label": "Только Saturday — каждый блок case в switch всегда останавливается после своего собственного print выражения"
            },
            {
              "icon": "",
              "label": "Только Sunday — совпадение начинается с case 6 но только последний совпадающий label перед break выполняется"
            },
            {
              "icon": "",
              "label": "Ничего не выводит, потому что day 6 не имеет совпадающего case со своим собственным break прямо под ним"
            },
            {
              "icon": "",
              "label": "Saturday затем Sunday — case 6 не имеет break, поэтому выполнение переходит в код следующего case перед попаданием в следующий break"
            }
          ]
        },
        {
          "question": "Класс Product нужен один очевидный \"естественный\" порядок сортировки по цене, плюс возможность также сортировать по имени или по уровню запасов в разных местах в кодовой базе. Какая комбинация это правильный дизайн?",
          "options": [
            {
              "icon": "",
              "label": "Реализуйте Comparable<Product> три отдельных раза, один на упорядочивание, и позвольте вызывающему выбрать какой compareTo выполняется"
            },
            {
              "icon": "",
              "label": "Реализуйте Comparable<Product> для естественного упорядочивания по цене, и напишите отдельные экземпляры Comparator<Product> для сортировок по имени и уровню запасов используемые где-либо еще"
            },
            {
              "icon": "",
              "label": "Используйте только Comparator для каждого упорядочивания, включая цену, потому что Comparable не имеет преимущества здесь"
            },
            {
              "icon": "",
              "label": "Используйте только Comparable добавляя три перегруженных метода compareTo, один на упорядочивание"
            }
          ]
        },
        {
          "question": "Метод вызывает new FileReader(path), который объявляет throws IOException. IOException extends Exception, а не RuntimeException. Что вы должны сделать чтобы это скомпилировалось?",
          "options": [
            {
              "icon": "",
              "label": "Ничего — компилятор только принуждает это для исключений что extends RuntimeException"
            },
            {
              "icon": "",
              "label": "Либо catch IOException в try/catch либо объявите throws IOException на вашем собственном методе — checked исключения должны быть обработаны или распространены явно, в отличие от unchecked RuntimeException подклассов"
            },
            {
              "icon": "",
              "label": "Оберните вызов в try/catch для RuntimeException, потому что IOException автоматически unchecked в современной Java"
            },
            {
              "icon": "",
              "label": "Объявите метод как static — статические методы освобождены от обработки checked исключений"
            }
          ]
        },
        {
          "question": "try (Resource a = new Resource(\"A\"); Resource b = new Resource(\"B\")) { ... } использует два ресурса реализующие AutoCloseable. Если блок try завершается нормально, в каком порядке они закрываются?",
          "options": [
            {
              "icon": "",
              "label": "A закрывается первым, затем B, совпадая порядку объявления"
            },
            {
              "icon": "",
              "label": "B закрывается первым, затем A — try-with-resources закрывает ресурсы в обратном порядке чем они были объявлены"
            },
            {
              "icon": "",
              "label": "Оба закрываются одновременно, потому что try-with-resources параллелизирует очистку"
            },
            {
              "icon": "",
              "label": "Только B закрывается автоматически — A все равно должен быть закрыт вручную в блоке finally"
            }
          ]
        },
        {
          "question": "int getValue() { try { return 1; } finally { return 2; } } Что вернет вызов getValue()?",
          "options": [
            {
              "icon": "",
              "label": "1 — возвращаемое значение блока try уже зафиксировано перед запуском finally, поэтому finally не может его изменить"
            },
            {
              "icon": "",
              "label": "Это выбрасывает исключение во время выполнения, потому что метод не может вернуться из двух мест"
            },
            {
              "icon": "",
              "label": "2 — return выражение внутри finally переопределяет и заменяет любой return уже в процессе из блока try, отбрасывая значение 1 полностью"
            },
            {
              "icon": "",
              "label": "Это ошибка компиляции — finally не разрешено содержать return выражение"
            }
          ]
        },
        {
          "question": "try { ... } catch (IOException e) { ... } catch (FileNotFoundException e) { ... }, и FileNotFoundException extends IOException. Что происходит когда вы пытаетесь это скомпилировать?",
          "options": [
            {
              "icon": "",
              "label": "Ошибка компиляции — блок catch FileNotFoundException недостижим потому что более ранний более общий catch IOException уже совпадает каждому FileNotFoundException"
            },
            {
              "icon": "",
              "label": "Это компилируется нормально, и более специфичный блок FileNotFoundException выполняется когда именно этот тип выбрасывается"
            },
            {
              "icon": "",
              "label": "Это компилируется нормально, и оба catch блока выполняются в порядке для FileNotFoundException"
            },
            {
              "icon": "",
              "label": "Это нормально во время компиляции но выбрасывает ошибку во время выполнения в первый раз когда FileNotFoundException действительно возникает"
            }
          ]
        },
        {
          "question": "Класс имеет synchronized метод экземпляра process() и synchronized статический метод configure(). Что специфично каждый блокирует?",
          "options": [
            {
              "icon": "",
              "label": "process() блокирует монитор конкретного экземпляра объекта он вызывается на, в то время как configure() блокирует монитор самого объекта Class — общий каждый экземпляр"
            },
            {
              "icon": "",
              "label": "Оба блокируют одну единственную глобальную блокировку для всей JVM, независимо от экземпляра или класса"
            },
            {
              "icon": "",
              "label": "process() блокирует объект Class, и configure() блокирует какой бы экземпляр это не вызвал"
            },
            {
              "icon": "",
              "label": "Ни один на самом деле не блокирует ничего пока synchronized блок также не используется внутри тела метода"
            }
          ]
        },
        {
          "question": "Поле объявлено volatile int counter = 0;, и несколько потоков запускают counter++ на нем одновременно. Предотвращает ли volatile потерю обновлений здесь?",
          "options": [
            {
              "icon": "",
              "label": "Да — volatile делает каждую операцию на поле атомарной, включая increments"
            },
            {
              "icon": "",
              "label": "Нет — volatile только гарантирует что reads видят последний write через потоки (видимость); counter++ это read-modify-write с несколькими шагами, и volatile ничего не делает чтобы сделать те шаги атомарными"
            },
            {
              "icon": "",
              "label": "Да, но только для int и long полей специфично, из-за как JVM обрабатывает 64-bit значения"
            },
            {
              "icon": "",
              "label": "Нет, и volatile также не гарантирует видимость для примитивных типов как int"
            }
          ]
        },
        {
          "question": "Интерфейс A и интерфейс B каждый объявляют default метод describe(). Класс реализует и A и B и не переопределяет describe() сам. Что происходит?",
          "options": [
            {
              "icon": "",
              "label": "Компилятор автоматически выбирает версию интерфейса A, потому что она указана первой в clausule implements"
            },
            {
              "icon": "",
              "label": "Обе версии выполняются, одна за другой, всякий раз когда describe() вызывается"
            },
            {
              "icon": "",
              "label": "Это нормально во время компиляции, но выбрасывает AmbiguousMethodException в первый раз когда describe() вызывается"
            },
            {
              "icon": "",
              "label": "Ошибка компиляции — когда два интерфейса способствуют одному default методу, реализующий класс должен переопределить его сам чтобы разрешить неоднозначность, потому что Java не угадает какой вы имели в виду"
            }
          ]
        },
        {
          "question": "list.stream().filter(x -> x > 0).map(x -> x * 2); написано но результат никогда не присвоен терминальной операции как .collect() или .forEach(). Что на самом деле происходит когда эта строка выполняется?",
          "options": [
            {
              "icon": "",
              "label": "Ничего с элементами списка не происходит — filter и map это ленивые промежуточные операции что только описывают конвейер; без терминальной операции ничего этого конвейера никогда не выполняется"
            },
            {
              "icon": "",
              "label": "Каждый элемент фильтруется и отображается немедленно, точно как если бы была вызвана терминальная операция"
            },
            {
              "icon": "",
              "label": "Только filter выполняется немедленно; map отложен пока не появится терминальная операция"
            },
            {
              "icon": "",
              "label": "Это выбрасывает IllegalStateException, потому что конвейер потока требует терминальную операцию чтобы скомпилироваться"
            }
          ]
        }
      ],
      "optionOrderVersion": "0154f9576054ad6f"
    }
  }
}
