Перейти к основному содержимому

Вопросы для собеседования по системе типов Java

· 8 мин. чтения

1. Введение

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

2. Вопросы

Q1. Опишите место класса объекта в иерархии типов. Какие типы наследуются от объекта, а какие нет? Наследуются ли массивы от объекта? Можно ли присвоить лямбда-выражение объектной переменной?

java.lang.Object находится на вершине иерархии классов в Java. Все классы наследуются от него либо явно, либо неявно (когда в определении класса опущено ключевое слово extends ), либо транзитивно через цепочку наследования.

Однако есть восемь примитивных типов, которые не наследуются от Object , а именно: boolean , byte , short , char , int , float , long и double .

Согласно Спецификации языка Java, массивы также являются объектами. Они могут быть назначены ссылке на объект , и для них могут быть вызваны все методы объекта .

Лямбда-выражения нельзя назначать непосредственно переменной объекта, поскольку объект не является функциональным интерфейсом. Но вы можете назначить лямбду переменной функционального интерфейса, а затем назначить ее переменной объекта (или просто назначить ее переменной объекта, одновременно приведя ее к функциональному интерфейсу).

Q2. Объясните разницу между примитивными и ссылочными типами.

Ссылочные типы наследуются от верхнего класса java.lang.Object и сами являются наследуемыми (за исключением конечных классов). Примитивные типы не наследуются и не могут быть подклассами.

Значения аргументов с примитивным типом всегда передаются через стек, что означает, что они передаются по значению, а не по ссылке. Это имеет следующий смысл: изменения, внесенные в значение примитивного аргумента внутри метода, не распространяются на фактическое значение аргумента.

Примитивные типы обычно хранятся с использованием базовых аппаратных типов значений.

Например, для хранения значения int может использоваться 32-битная ячейка памяти. Ссылочные типы вводят накладные расходы на заголовок объекта, который присутствует в каждом экземпляре ссылочного типа.

Размер заголовка объекта может быть весьма значительным по сравнению с размером простого числового значения. Вот почему в первую очередь были введены примитивные типы — для экономии места на служебных объектах. Недостатком является то, что не все в Java технически является объектом — примитивные значения не наследуются от класса Object .

Q3. Опишите различные типы примитивов и объем памяти, который они занимают.

Java имеет 8 примитивных типов:

  • boolean — логическое значение true / false . Размер логического значения не определяется спецификацией JVM и может различаться в разных реализациях.
  • byte — 8-битное значение со знаком,
  • short — 16-битное значение со знаком,
  • char — беззнаковое 16-битное значение,
  • int — 32-битное значение со знаком,
  • long — подписанное 64-битное значение,
  • float — 32-битное значение с плавающей запятой одинарной точности, соответствующее стандарту IEEE 754,
  • double — 64-битное значение с плавающей запятой двойной точности, соответствующее стандарту IEEE 754.

Q4. В чем разница между абстрактным классом и интерфейсом? Каковы варианты использования одного и другого?

Абстрактный класс — это класс с модификатором abstract в его определении. Его нельзя создать, но можно создать подкласс. Интерфейс — это тип, описываемый ключевым словом interface . Его также нельзя реализовать, но можно реализовать.

Основное различие между абстрактным классом и интерфейсом заключается в том, что класс может реализовывать несколько интерфейсов, но расширять только один абстрактный класс.

Абстрактный класс обычно используется в качестве базового типа в какой-либо иерархии классов и обозначает основное назначение всех классов, которые от него наследуются.

Абстрактный класс также может реализовывать некоторые базовые методы, необходимые во всех подклассах. Например, большинство коллекций карт в JDK наследуются от класса AbstractMap , который реализует многие методы, используемые подклассами (например, метод equals ).

Интерфейс определяет некоторый контракт, с которым соглашается класс. Реализованный интерфейс может обозначать не только основное назначение класса, но и некоторые дополнительные контракты.

Например, если класс реализует интерфейс Comparable , это означает, что экземпляры этого класса можно сравнивать, какова бы ни была основная цель этого класса.

Q5. Каковы ограничения для членов (полей и методов) типа интерфейса?

Интерфейс может объявлять поля, но они неявно объявляются как public , static и final , даже если вы не указываете эти модификаторы. Следовательно, вы не можете явно определить поле интерфейса как private . По сути, интерфейс может иметь только поля-константы, а не поля-экземпляры.

Все методы интерфейса также неявно являются общедоступными . Они также могут быть (неявно) abstract или default .

Q6. В чем разница между внутренним классом и статическим вложенным классом?

Проще говоря, вложенный класс — это класс, определенный внутри другого класса.

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

Вот пример внутреннего класса — вы можете видеть, что он может получить доступ к ссылке на экземпляр внешнего класса в форме конструкции OuterClass1.this :

public class OuterClass1 {

public class InnerClass {

public OuterClass1 getOuterInstance() {
return OuterClass1.this;
}

}

}

Чтобы создать такой внутренний класс, вам нужно иметь экземпляр внешнего класса:

OuterClass1 outerClass1 = new OuterClass1();
OuterClass1.InnerClass innerClass = outerClass1.new InnerClass();

Статический вложенный класс совсем другой. Синтаксически это просто вложенный класс с модификатором static в его определении.

На практике это означает, что этот класс может быть создан как любой другой класс, без привязки его к какому-либо экземпляру окружающего класса:

public class OuterClass2 {

public static class StaticNestedClass {
}

}

Чтобы создать экземпляр такого класса, вам не нужен экземпляр внешнего класса:

OuterClass2.StaticNestedClass staticNestedClass = new OuterClass2.StaticNestedClass();

Q7. Есть ли в Java множественное наследование?

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

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

Q8. Что такое классы-оболочки? Что такое автобокс?

Для каждого из восьми примитивных типов в Java существует класс-оболочка, который можно использовать для обертывания примитивного значения и использования его как объекта. Эти классы, соответственно, Boolean , Byte , Short , Character , Integer , Float , Long и Double . Эти оболочки могут быть полезны, например, когда вам нужно поместить примитивное значение в общую коллекцию, которая принимает только ссылочные объекты.

List<Integer> list = new ArrayList<>();
list.add(new Integer(5));

Чтобы избежать необходимости вручную преобразовывать примитивы туда и обратно, компилятор Java обеспечивает автоматическое преобразование, известное как автоупаковка/автораспаковка.

List<Integer> list = new ArrayList<>();
list.add(5);
int value = list.get(0);

Q9. Опишите разницу между equals() и ==

Оператор == позволяет сравнивать два объекта на предмет «одинаковости» (т. е. того, что обе переменные ссылаются на один и тот же объект в памяти). Важно помнить, что ключевое слово new всегда создает новый объект, который не будет передавать равенство == с любым другим объектом, даже если они имеют одинаковое значение:

String string1 = new String("Hello");
String string2 = new String("Hello");

assertFalse(string1 == string2);

Также оператор == позволяет сравнивать примитивные значения:

int i1 = 5;
int i2 = 5;

assertTrue(i1 == i2);

Метод equals() определен в классе java.lang.Object и поэтому доступен для любого ссылочного типа. По умолчанию он просто проверяет, что объект тот же, с помощью оператора ==. Но обычно это переопределяется в подклассах, чтобы обеспечить конкретную семантику сравнения для класса.

Например, для класса String этот метод проверяет, содержат ли строки одинаковые символы:

String string1 = new String("Hello");
String string2 = new String("Hello");

assertTrue(string1.equals(string2));

Q10. Предположим, у вас есть переменная, которая ссылается на экземпляр типа класса. Как проверить, что объект является экземпляром этого класса?

Вы не можете использовать ключевое слово instanceof в этом случае, потому что оно работает только в том случае, если вы предоставляете фактическое имя класса как литерал.

К счастью, в классе Class есть метод isInstance , который позволяет проверить, является ли объект экземпляром этого класса:

Class<?> integerClass = new Integer(5).getClass();
assertTrue(integerClass.isInstance(new Integer(4)));

Q11. Что такое анонимный класс? Опишите вариант его использования.

Анонимный класс — это одноразовый класс, который определяется в том же месте, где необходим его экземпляр. Этот класс определен и создан в одном и том же месте, поэтому ему не нужно имя.

До Java 8 вы часто использовали анонимный класс для определения реализации интерфейса с одним методом, например Runnable . В Java 8 вместо отдельных интерфейсов абстрактных методов используются лямбда-выражения. Но у анонимных классов все еще есть варианты использования, например, когда вам нужен экземпляр интерфейса с несколькими методами или экземпляр класса с некоторыми дополнительными функциями.

Вот как вы можете создать и заполнить карту:

Map<String, Integer> ages = new HashMap<String, Integer>(){{
put("David", 30);
put("John", 25);
put("Mary", 29);
put("Sophie", 22);
}};