Источник: Medium Перед вами подборка из 33 достаточно сложных вопросов по основам языка Java, которые могут задать кандидату на позицию Junior Java Developer или Middle Java Developer. Кофе-брейк #187. 33 сложных вопроса на собеседовании по основам языка Java - 1

1. Бывают ли операторы import в скомпилированном коде?

Нет. Они встречаются только в исходном коде. Это простое удобство, позволяющее избежать префикса имен классов с именами пакетов.

2. Замедляет ли импорт подстановочных знаков производительность программы?

Нет. Компилятор определяет, какие классы нужны, и импортирует только эти классы. Оператор import не влияет на эффективность класса во время выполнения. Это личное предпочтение, использовать ли импорт подстановочных знаков.

3. В чем разница между типами данных short и char?

Оба тесно связаны, оба они имеют размер 16 бит. Основное отличие состоит в том, что тип short может хранить как положительные, так и отрицательные числа, тогда как тип char может хранить только положительные числа, включая 0.

4. Работает ли расширенный цикл (enhanced-for-loop) для итераторов?

Нет. Он работает только с массивами и коллекциями. Причина, по которой он не поддерживает итераторы, заключается в том, что сам расширенный цикл (enhanced-for-loop) добавляется, чтобы избежать зацикливания коллекции с использованием итераторов. Подробнее читайте в JSR-201 и Enhanced for loop specification draft.

5. Почему метод finalize() устарел в Java?

Потому что нет гарантии, что метод finalize() будет вызван JVM. Все зависит от той или иной реализации JVM. Метод finalize() может создать проблему и снизить производительность.

6. Чем классы CopyOnWrite отличаются от других классов Concurrent, таких как ConcurrentHashMap?

Эти классы создают копию коллекции каждый раз, когда в коллекции добавляется, удаляется или изменяется ссылка, а затем обновляют исходную ссылку на коллекцию, чтобы она указывала на копию. Как правило, эти классы используются, чтобы итератор не видел изменений в коллекции.

7. В чем разница между методами strip() и trim() внутри класса String?

Оба эти метода используются для удаления пробелов в начале и в конце строки. Метод trim() поддерживает только символы ASCII, тогда как метод strip(), появившийся в Java 11, поддерживает Unicode и все остальные типы кодирования.

8. Что такое инициализатор экземпляра (instance initializer)?

Инициализатор экземпляра — это блок кода внутри класса, который можно использовать для инициализации его членов. Он будет вызываться каждый раз при создании объекта класса и перед вызовом конструктора. Вот наглядный пример:

class Animal {
    int legs;

    {
        this.legs = 10;
        System.out.println("Instance initializer block");
    } // Этот блок называется инициализатор экземпляра.

    public Animal() {
        System.out.println("Constructor");
    }
}
Вывод:
Instance initializer block Constructor

9. Какая реальная польза от “блока инициализации экземпляра”, если мы можем инициализировать переменные экземпляра в конструкторе?

В классе с несколькими конструкторами вам придется повторять код в каждом конструкторе. С инициализатором экземпляра вы можете просто написать код один раз, и он будет выполняться независимо от того, какой конструктор используется для создания объекта. Инициализаторы экземпляров также полезны в анонимных внутренних классах, которые вообще не могут объявлять никаких конструкторов.

10. Что делает метод fillInStackTrace() в классе Throwable?

Метод fillInStackTrace() сбрасывает информацию о трассировке стека в throwable и начинает новую трассировку стека с информацией о текущих методах. Пример без fillinStackTrace():

void methodOne() throws Throwable {
    methodTwo();
}

void methodTwo() throws Throwable {
    methodThree();
}

void methodThree() throws Throwable {
    throw new Exception("Error occurred");
}

public static void main(String[] args) {
    try {
        new Test().methodOne();
    } catch (Throwable e) {
        e.printStackTrace();
    }
}
Вывод:
java.lang.Exception: Error occurred at com.test.Test.methodThree(Test.java:14) at com.test.Test.methodTwo(Test.java:10) at com.test.Test.methodOne(Test.java:6) at com.test.Test.main(Test.java:19)
Пример с fillInStackTrace():

void methodOne() throws Throwable {
    try {
        methodTwo();
    } catch (Throwable e) {
        throw e.fillInStackTrace();
    }
}

void methodTwo() throws Throwable {
    methodThree();
}

void methodThree() throws Throwable {
    throw new Exception("Error occurred");
}

public static void main(String[] args) {
    try {
        new Test().methodOne();
    } catch (Throwable e) {
        e.printStackTrace();
    }
}
Вывод:
java.lang.Exception: Error occurred at com.test.Test.methodOne(Test.java:9) at com.test.Test.main(Test.java:23)

11. Что такое дедупликация строк?

Дедупликация строк помогает нам экономить память, занимаемую повторяющимися объектами String в приложениях Java. Это уменьшает объем памяти, занимаемой объектами String в памяти кучи Java, за счет того, что повторяющиеся или идентичные значения String совместно используют один и тот же массив символов.

12. Можно ли вызвать статический метод интерфейса внутри реализованного класса с помощью ключевого слова super?

Нет. Код не скомпилируется.

interface A {
    public static void print() {
        System.out.println("Hello World");
    }
}

class MyClass implements A {

    public void hello() {
        super.print(); // Ошибка компиляции!!!
    }
}
Однако вы можете вызвать статический метод класса внутри унаследованного класса с ключевым словом super.

class A {
    public static void print() {
        System.out.println("Hello World");
    }
}

class MyClass extends A {

    public void hello() {
        super.print(); // Работает!
    }
}

13. Можно ли создать анонимный класс из класса final?

Нет. Создание анонимного класса аналогично наследованию класса. Следовательно, если мы не можем наследовать класс final, то мы не можем создать и анонимный класс.

final class Bird {
    
}

class MyClass {

    public static void main(String[] args) {
        Bird b = new Bird() {}; // Ошибка компиляции!!!
    }
}

14. Принимают ли операторы Switch нулевые значения?

Нет. Если вы передаете null point оператору switch, то появится исключение NullPointerException.

15. Могут ли перечисления (enums) иметь публичный конструктор?

Нет. Внутри перечислений (enums) разрешены только частные конструкторы.

16. В чем разница между исключением (exception) и ошибкой (error)?

java.lang.Error — это ошибки, которые обычно невозможно обработать. Как правило, они относятся к катастрофическим сбоям, например, к нехватке системных ресурсов. Вот некоторые примеры: java.lang.OutOfMemoryError, java.lang.NoClassDefFoundError и java.lang.UnSupportedClassVersionError. С другой стороны, java.lang.Exception — это ошибки, которые могут быть обнаружены и обработаны, например, IOException, ArithmeticException и так далее.

17. Когда оператор instanceof выдает ошибку компиляции?

  • Если он не может проверить примитивы.
  • Если переменная не инстанцирована.
  • Если объект не находится в той же ветке наследования.

class A {
}

class B {
}

class C extends A {
}

class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a instanceof B); // Ошибка компиляции
        System.out.println(a instanceof C); // Работает
        System.out.println(a instanceof A); // Работает
        
        C c = new C();
        System.out.println(c instanceof A); // Работает, потому что C расширяет A
    }
}

18. Что такое вычисление по короткой схеме (short circuit evaluation)?

Вычисление по короткой схеме позволяет не оценивать правую часть выражений AND и OR, когда общий результат можно предсказать по значению в левой части.

int a = 3, b = 2;
boolean c = false;

c = (a > b && ++b == 3); // c — true,   b — 3
c = (a > b && ++b == 3); // c — false,  b — 3

c = (a > b || ++b == 3); // c — false,  b — 4
c = (a < b || ++b == 3); // c — true,   b — 4

c = (a < b | ++b == 3);  // c — true,   b — 5

19. Можно ли использовать в конструкторе и this(), и super()?

Нет!

class Car extends Vehicle {
    
    public Car() {
        this(20);
        super(10); // Ошибка компиляции!!!
    }

    public Car(int x) {
        super(x);
    }
}

20. Какой вывод будет у приведенной ниже программы?


class Test {

    static int myMethod() {
        try {
            throw new RuntimeException();
        } catch (Exception e) {
            System.out.println("Error");
            return 3;
        } finally {
            return 2;
        }
    }
    
    public static void main(String[] args) {
        System.out.println(myMethod());
    }
}
Ответ: сначала появится Error, а затем 2. Всегда выполняется блок finally.

21. Почему оператор switch не поддерживает long, float, boolean и double?

  • Числа с плавающей запятой (floating) неточны. Оператор switch используется для выполнения точного совпадения. Следовательно, выполнение точного сопоставления чисел с плавающей запятой не всегда хорошая идея.

  • boolean не поддерживается просто потому, что это можно легко сделать с помощью оператора if.

  • long не поддерживается, потому что оператор switch реализован с использованием кода инструкций виртуальной машины tableswitch или lookupswitch внутри JVM. Они работают только с 32-битными кодами операций (целыми числами), тогда как long составляет 64 бита (8 байтов).

22. Что такое WeakHashMap?

WeakHashMap — одна из трех реализаций карты специального назначения (special-purpose map) в Java. Его ключи относятся к типу WeakReference, что позволяет map стать garbage-collected, если программа больше не ссылается на ее ключ. Это полезно при реализации “реестровых” (registry-like) структур данных или просто реализации кэша в памяти, где запись исчезает, или если ее ключ больше недоступен ни одному потоку. 23. Что такое IdentityHashMap?Интерфейс Map требует использования метода equals() при сравнении ключей. IdentityHashMap проверяет, равны ли объекты, используя знак == вместо вызова метода equals(), а вместо использования hashCode() для вычисления хэш-кода он использует System.identityHashCode().

24. Что такое ConcurrentHashMap?

ConcurrentHashMap является усовершенствованием HashMap, поскольку мы знаем, что при работе со Threads в приложении HashMap не является хорошим выбором. Причина в том, что HashMap с точки зрения производительности уже не соответствует современным требованиям.

25. Что такое LinkedHashMap?

LinkedHashMap хранит свои элементы в порядке вставки и обеспечивает быстрый доступ с помощью хеширования. Кроме того, он поддерживает порядок элементов в порядке LRU (Least recently used first — Сначала наименее использовавшиеся), как показано ниже.

LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);
linkedHashMap.put(0, "a");
linkedHashMap.put(1, "b");
linkedHashMap.put(2, "c");

System.out.println(linkedHashMap);  // {0=a, 1=b, 2=c}
System.out.println(linkedHashMap);  // {0=a, 1=b, 2=c}

linkedHashMap.get(0);
System.out.println(linkedHashMap); // {1=b, 2=c, 0=a}

26. В чем разница между ConcurrentHashMap и Hashtable?

Hashtable использует одну блокировку для всех данных. ConcurrentHashMap использует несколько блокировок на уровне сегмента (по умолчанию 16) вместо уровня объекта, то есть всей Map. Блокировка ConcurrentHashMap применяется только для обновлений. В случае извлечения это обеспечивает полный параллелизм. Извлечения отражают результаты самых последних завершенных операций обновления. Таким образом, чтение может происходить очень быстро, в то время как запись выполняется с блокировкой.

27. Какие четыре типа ссылок доступны в Java?

Java предоставляет четыре типа ссылок. Это StrongReference, WeakReference, SoftReference и PhantomReference.

28. В чем разница между методом isInstance() и оператором instanceof?

Оператор instanceof и метод isInstance() используются для проверки класса объекта. Основное отличие между ними возникает, когда мы хотим динамически проверять класс объекта. В этом случае метод isInstance() будет работать, но мы не можем сделать это с помощью оператора instanceof.

29. Можем ли мы переопределить статические методы?

Нет!

30. В чем заключается переопределение ковариантного метода (co-variant method)?

Эта функция впервые появилась в Java 5. Она означает, что подкласс может иметь сигнатуру метода переопределенного метода в качестве типа производного класса.

class Fruit {
}

class Apple extends Fruit {
}

class Basket {
    
    public Fruit getFruit() { // return type is Fruit
        return new Fruit();
    }
}


class AppleBasket extends Basket {
    
    @Override
    public Apple getFruit() { // return type is Apple
        return new Apple();
    }
}

31. В чем разница между hashCode() и System.identityHashCode(obj)?

Метод hashcode() возвращает hashCode из переопределенного метода класса, System.identityHashCode() возвращает hashcode из класса Object.

class Person {
    int id;

    public Person(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return this.id;
    }
}

class Test {
    public static void main(String[] args) {
        Person p = new Person(123);

        System.out.println(p.hashCode()); // возвращает 123
        System.out.println(System.identityHashCode(p)); // возвращает 918221580 …
    }
}

32. Почему дженерики не поддерживают примитивы?

Потому что JVM во время выполнения стирает тип дженериков и преобразует их в тип класса объекта. Этот процесс известен как стирание типа (Type erasure). Примитивы не могут быть напрямую преобразованы в Object. Вот пример:

List<Car> cars = new ArrayList<Cars>();

// Во время выполнения этот код будет преобразован в
List cars = new ArrayList(); // теперь это просто список необработанных объектов

33. Можем ли мы перегрузить метод, который принимает общие параметры, как показано ниже?


class  Test <T, W> { 

    public  void  print (T t) { 
    } 

    public  void  print (W w) { 
    } 
}
Ответ: Нет. Это вызовет ошибку компиляции из-за стирания типа. Как вы уже знаете, JVM стирает тип дженерика во время выполнения и преобразует его в тип объекта. Метод при выполнении выглядит следующим образом:

public void print(Object t) {
}

public void print(Object w) {
}
Поскольку сигнатура обоих методов одинакова, перегрузка невозможна.