Статьи

Подготовка к собеседованию на Java: 15 вопросов по собеседованию на Java

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

Однако может быть легко забыть все входы и выходы языка, такого как Java, потому что, проще говоря, мы не имеем дело с такими вопросами, как «Каким типом памяти управляет JVM?» и «Опишите полиморфизм на примере». на ежедневной основе.

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

Сегодня мы рассмотрим вопросы интервью и ответы, связанные с:

  • Java экосистема
  • Java классы
  • Интерфейсы
  • наследование
  • Многопоточность
  • Управление памятью
  • Коллекции
  • Обработка исключений
  • Сериализация
  • одиночка

Давайте начнем!

Q1: Что подразумевается под Java как независимый от платформы?

Ява работает по принципу: пиши один раз и беги куда угодно. Как только Java-программа написана, она компилируется в так называемый байтовый код, который затем можно запустить на любой виртуальной машине Java или JVM.

Компиляция в байт-код — это магия взаимодействия Java. Различные операционные системы и аппаратные архитектуры имеют JVM, разработанные специально для себя, и все JVM могут запускать один и тот же байт-код. Поэтому, если вы напишите Java-программу для Linux, она будет бесперебойно работать на JVM, разработанной для операционной системы Windows, что делает код независимым от базового оборудования и ОС.

Q2: объясните концепции JRE, JDK и JVM

  • JRE (Java Runtime Environment) включает виртуальную машину Java и стандартные API Java (базовые классы и вспомогательные файлы). JRE содержит достаточно для выполнения приложения Java, но недостаточно для его компиляции.
  • JDK (Java Development Kit) — это JRE плюс компилятор Java и набор других инструментов для компиляции и отладки кода. JRE состоит из библиотек платформы Java, виртуальной машины Java (JVM), плагина Java и Java Web Start для запуска приложений Java. JRE как автономный не содержит компиляторов и инструментов отладки. Если вам нужно разрабатывать Java-программы, вам нужен полный Java SDK. JRE недостаточно для разработки программ. Только полный Java SDK содержит компилятор Java, который превращает ваши исходные файлы .java в файлы байт-кода .class.
  • JVM (виртуальная машина Java) — это реализация спецификации, подробно описывающая поведение, ожидаемое от JVM. Любая реализация, которая соответствует спецификации JVM, должна иметь возможность запускать код, скомпилированный в байт-код Java, независимо от языка, на котором код был изначально написан. На языке программирования Java весь исходный код сначала пишется в виде простых текстовых файлов, заканчивающихся расширением .java. Эти исходные файлы затем компилируются в файлы .class компилятором javac. Файл .class не содержит код, свойственный вашему процессору; вместо этого он содержит байт-коды — машинный язык виртуальной машины Java. Затем инструмент запуска Java запускает ваше приложение с экземпляром виртуальной машины Java.

Q3: Как бы вы отметили пакет сущностей как частный в Java?

Там нет явного модификатора для пакета private. При отсутствии какого-либо модификатора переменные класса или члена являются закрытыми для пакета. Участник, помеченный как закрытый пакет, виден только внутри его собственного пакета. Рассмотрим класс ниже.

Пакет private является несколько более широкой формой private. Приятной особенностью package-private является то, что вы можете использовать его для предоставления доступа к методам, которые вы в противном случае считали бы закрытыми для классов модульного теста. Таким образом, если вы используете вспомогательные классы, которые не имеют никакого другого применения, кроме как для того, чтобы ваши публичные классы делали то, что нужно клиентам, имеет смысл сделать их закрытыми, так как вы хотите максимально упростить работу для пользователей библиотеки.

Q4: Почему вы должны избегать метода finalize () в классе Object? Какие есть альтернативы?

Класс Object предоставляет метод обратного вызова finalize (), который может быть вызван для объекта, когда он становится мусором. Реализация объекта finalize () ничего не делает — вы можете переопределить finalize () для очистки, такой как освобождение ресурсов.

Метод finalize () может вызываться системой автоматически, но когда он вызывается или даже если он вызывается, является неопределенным. Таким образом, вы не должны полагаться на этот метод, чтобы сделать вашу очистку для вас. Например, если вы не закрываете файловые дескрипторы в своем коде после выполнения ввода-вывода и ожидаете, что finalize () закроет их для вас, у вас могут закончиться файловые дескрипторы.

Вот несколько альтернатив:

  • Идиома try-with-resources может использоваться для очистки объектов. Это требует реализации интерфейса AutoCloseable.
  • Использование PhantomReference для очистки при сборке мусора
  • Использование класса Cleaner для выполнения действий по очистке.
  • Реализуйте метод close (), который выполняет очистку и документирует вызов метода.

Вопрос 5: Можете ли вы изменить содержимое окончательного массива, как показано в фрагменте кода ниже?

1
2
final int[] array = new int[5];
array[0] = 1;

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

1
2
final int[] array = new int [5]
array = new int[10];

Тем не менее, следующий код будет работать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class FinalArrayExample {
  final int[] array = new int[5];
   
  // allowed
  void changeArrayContents(int i, int val) {
    array[i] = val;
  }
   
  // not allowed and will not compile
  /*
   
  void changeArray() {
    array = new int [10]
     
  }*/
 
}

Q6: Объясните разницу между интерфейсом и абстрактным классом? Когда вы должны использовать один или другой?

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

Интерфейс — это полностью «абстрактный класс», который используется для группировки связанных методов с пустыми телами.

Ниже приведены четыре основных различия между абстрактными классами и интерфейсами:

  • Абстрактный класс может иметь конечные переменные, статические переменные или переменные-члены класса, тогда как интерфейс может иметь только переменные, которые являются окончательными и статическими по умолчанию.
  • Абстрактный класс может иметь статические, абстрактные или неабстрактные методы. Интерфейс может иметь статические, абстрактные или стандартные методы.
  • Члены абстрактного класса могут иметь различную видимость частного, защищенного или открытого. Принимая во внимание, что в интерфейсе все методы и константы являются открытыми.
  • Класс может расширять только другой класс, но он может реализовывать несколько интерфейсов. Аналогично, интерфейс может расширять несколько интерфейсов. Интерфейс никогда не реализует класс или интерфейс.

Используйте абстрактный класс, когда подклассы совместно используют состояние или используют общую функциональность. Или вам нужно объявлять нестатические, не финальные поля или вам нужны модификаторы доступа, кроме public.

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

Q7: Что такое полиморфизм? Можете привести пример?

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

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

Классическим примером полиморфизма является класс Shape . Мы получаем классы Circle , Triangle и Rectangle из родительского класса Shape , который предоставляет абстрактный метод draw (). Производные классы предоставляют свои пользовательские реализации для метода draw() . Теперь очень легко визуализировать различные типы фигур, содержащиеся в одном массиве, вызывая метод draw() для каждого объекта. Это избавляет нас от создания отдельных методов рисования для каждой фигуры, например drawTriangle() , drawCircle() и т. Д.

Q8: основной метод может быть перегружен?

Да, основной метод, который является статическим, может быть перегружен. Но только public static void main(String[] args) будет использоваться, когда JVM запускает ваш класс, даже если вы укажете один или два аргумента командной строки. Однако программно можно вызывать перегруженные версии основного метода.

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

Мы можем передать переменное количество аргументов методу, используя функцию varargs. Ниже приведен пример передачи нескольких аргументов одного типа в метод.

1
2
3
4
5
public void childrenNames(string... names) {
   for(int i= 0; i < names.length; i++)
   system.out.println(names[i]);
 
}
  • За именем типа следуют три точки, пробел, а затем имя переменной.
  • Переменная varargs обрабатывается как массив.
  • Переменная varargs должна появляться последней в сигнатуре метода.
  • Как следствие вышесказанного, в сигнатуре метода может быть только один varargs.

Вышеуказанный метод может быть вызван следующим образом: Вызов метода Varargs

1
2
3
childrenNames();
childrenNames("Jane");
childrenNames("Jane", "Tom", "Peter");

В10: Может ли семафор выступать в качестве мьютекса?

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

Это приводит нас к понятию «собственность». Мьютекс принадлежит потоку, получающему его, до тех пор, пока он не освободит его, тогда как для семафора нет понятия собственности.

Нужна переподготовка по многопоточности? Прочтите эту статью «Многопоточность и параллелизм Java: что нужно знать, чтобы взломать старшее инженерное интервью».

В11: Объясните интерфейс Externalizable

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

Интерфейс Externalizable расширяет интерфейс Serializable и предоставляет два метода для сериализации и десериализации объекта, writeExternal() и readExternal() .

В12: Если блок кода выдает более одного исключения, как его можно обработать?

Множество типов исключений, генерируемых фрагментом кода, могут обрабатываться несколькими предложениями блока catch, за которыми следует блок try. Пример фрагмента обработки исключений представлен ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
oid process(int val)  {
   try {
        if (val == 1)
            //checked exception
            throw new FileNotFoundException();
        
        if (val == 2)
            // runtime exception
            throw new NullPointerExxception();
         
        if (val == 3)
            // error exception
            throw new StackOverflowError
             
   } catch (RuntimeException re) {
            // catches all unchecked  exceptions
             
   } catch (Exception e) {
            // catches all checked exceptions
             
   } catch (Error err) {
            // catches all errors
    
   }
    
}

В13: Если бы вы использовали набор, как бы вы определили между HashSet и TreeSet?

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

Поэтому, если вы хотите сохранить порядок, лучше использовать TreeSet, поскольку он хранит ключи в порядке возрастания, а не в порядке их вставки. Это не потокобезопасно. Однако имейте в виду, что TreeSet не является потокобезопасным, тогда как HashSet — безопасным.

Q14: Какими несколькими способами вы можете улучшить объем памяти приложения Java?

Вот три ключевых шага, которые вы можете предпринять, чтобы улучшить объем памяти:

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

В15: Каков наилучший способ реализации одноэлементного класса?

Лучший способ реализовать синглтон согласно Джошу Блоху — это использовать тип enum для синглтона. Поскольку Java гарантирует, что когда-либо создается только один экземпляр перечисления, одноэлементный класс, реализованный через перечисления, защищен от атак отражения и сериализации.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
class Demonstration {
    public static void main( String args[] ) {
        Superman superman = Superman.INSTANCE;
        superman.fly();
    }
}
 
enum Superman {
    INSTANCE;
 
    private final String name = "Clark Kent";
    private String residence = "USA";
 
    public void fly() {
        System.out.println("I am flyyyyinggggg ...");
    }
}

Получение мастерства

В этом посте много было сказано о языке программирования Java, начиная от экосистемы Java (вопрос 1) и заканчивая многопоточностью (вопрос 10) и исключениями (вопрос 12). Это типы вопросов об интервью Java, которые вы можете ожидать. Лучше всего использовать материал, изложенный выше, в качестве руководства по темам, которые вы хотите изучить, и типам вопросов, которые вы можете ожидать.

Однако материал здесь просто царапает поверхность. Есть еще много концепций, которые необходимо пересмотреть или исследовать, например объектно-ориентированное программирование, статические переменные и перегрузка методов.

Счастливого обучения! Дом

Опубликовано на Java Code Geeks с разрешения образовательного партнера нашей программы JCG . Смотрите оригинальную статью здесь: Подготовка к собеседованию на Java: 15 вопросов на собеседовании на Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.