Рубрики
Без рубрики

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

Узнайте ответы на распространенные вопросы интервью Java

Автор оригинала: Patryk Kucharz.

1. введение

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

2. Основные вопросы по языку Java для начинающих

Q1. Передаются ли данные по ссылке или по значению в Java?

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

  1. Передача по значению – это означает, что мы передаем копию объекта в качестве параметра в метод.
  2. Передача по ссылке – это означает, что мы передаем ссылку на объект в качестве параметра в метод.

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

Когда мы передаем примитивы методу, его значение копируется в новую переменную. Когда дело доходит до объектов, значение ссылки копируется в новую переменную. Таким образом, мы можем сказать, что Java-это строго pass-by-value язык.

Мы можем узнать больше об этом в одной из наших статей: Pass-By-Value как механизм передачи параметров в Java .

Q2. В чем разница между импортом и статическим импортом?

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

import java.util.ArrayList; //specific class
import java.util.*; //all classes in util package

Мы также можем использовать их для импорта общедоступных вложенных классов заключающего класса:

import com.baeldung.A.*

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

Существует также статический импорт, который позволяет нам импортировать статические члены или вложенные классы:

import static java.util.Collections.EMPTY_LIST;

В результате мы можем использовать статическую переменную EMPTY_LIST без добавления полного имени класса, т. Е. Так, как если бы оно было объявлено в текущем классе.

Q3. Какие Модификаторы доступа Доступны в Java и какова их цель?

В Java есть четыре модификатора доступа:

  1. частный
  2. по умолчанию (пакет)
  3. защищенный
  4. общественный

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

В отличие от модификатора private , мы можем применить модификатор default ко всем типам членов класса и к самому классу. Мы можем применить по умолчанию видимость, вообще не добавляя модификатор доступа. Если мы используем по умолчанию видимость, наш класс или его члены будут доступны только внутри пакета нашего класса. Мы должны иметь в виду, что модификатор доступа по умолчанию не имеет ничего общего с ключевым словом default .

Аналогично модификатору default , все классы в одном пакете могут получить доступ к членам protected . Более того, модификатор protected позволяет подклассам получать доступ к защищенным членам суперкласса, даже если они не находятся в одном пакете. Мы не можем применить этот модификатор доступа к классам, только к членам класса.

Модификатор public можно использовать вместе с ключевым словом класса и всеми членами класса. Он делает классы и членов класса доступными во всех пакетах и всеми классами.

Мы можем узнать больше в статье Модификаторы доступа Java.

Q4. Какие Другие Модификаторы Доступны в Java и какова их цель?

В Java доступно еще пять модификаторов:

  • статический
  • окончательный
  • абстрактный
  • синхронизировано
  • летучий

Они не контролируют видимость.

Прежде всего, мы можем применить ключевое слово static к полям и методам. Статические поля или методы являются членами класса, в то время как нестатические являются членами объекта . Члены класса не нуждаются в вызове какого-либо экземпляра. Они вызываются с именем класса вместо имени ссылки на объект. В этой статье более подробно рассматривается ключевое слово static .

Затем у нас есть ключевое слово final . Мы можем использовать его с полями, методами и классами. Когда final используется в поле, это означает, что ссылка на поле не может быть изменена. Таким образом, он не может быть переназначен другому объекту. Когда final применяется к классу или методу, это гарантирует, что этот класс или метод не может быть расширен или переопределен. Ключевое слово final более подробно описано в этой статье .

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

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

Последнее ключевое слово, которое мы собираемся обсудить, – это volatile . Мы можем использовать его только вместе с полями экземпляра. Он объявляет, что значение поля должно быть считано и записано в основную память – в обход кэша процессора. Все операции чтения и записи для изменчивой переменной являются атомарными. Ключевое слово volatile подробно описано в этой статье .

Q5. В чем разница между JDK, JRE и JVM?

JDK расшифровывается как Java Development Kit , который представляет собой набор инструментов, необходимых разработчикам для написания приложений на Java. Существует три типа сред JDK:

  • Standard Edition – комплект разработки для создания портативных настольных или серверных приложений
  • Enterprise Edition – расширение стандартной версии с поддержкой распределенных вычислений или веб-служб
  • Micro Edition – платформа для разработки встраиваемых и мобильных приложений

В JDK включено множество инструментов, которые помогают программистам писать, отлаживать или поддерживать приложения . Наиболее популярными являются компилятор ( javac ), интерпретатор ( java ), архиватор ( jar ) и генератор документации ( javadoc ).

JRE – это Среда выполнения Java . Это часть JDK, но она содержит минимальную функциональность для запуска Java-приложений . Он состоит из виртуальной машины Java , основных классов и вспомогательных файлов. Например, у него нет никакого компилятора.

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

Для получения дополнительной информации давайте проверим разницу между JVM, JRE и статьей JDK.

Q6. В чем разница между Стеком и кучей?

Есть две части памяти, где все переменные и объекты хранятся в JVM. Первый-это стек , а второй – куча .

Стек – это место, где JVM резервирует блоки для локальных переменных и дополнительных данных . Стек представляет собой структуру LIFO (последний вход-первый выход). Это означает, что всякий раз, когда вызывается метод, новый блок зарезервирован для локальных переменных и ссылок на объекты. Каждый новый вызов метода резервирует следующий блок. Когда методы завершают свое выполнение, блоки освобождаются в обратном порядке, в котором они были запущены.

Каждый новый поток имеет свой собственный стек.

Мы должны знать, что в стеке гораздо меньше места в памяти, чем в куче. И когда стек заполнен, JVM выдаст StackOverflowError . Это может произойти, когда есть плохой рекурсивный вызов, и рекурсия идет слишком глубоко.

Каждый новый объект создается на Java h eap , который используется для динамического распределения . Существует g сборщик мусора , который отвечает за удаление неиспользуемых объектов, которые разделены на молодые (детские) и старые пространства. Доступ к памяти к куче происходит медленнее, чем доступ к стеку. JVM выдает OutOfMemoryError , когда куча заполнена.

Более подробную информацию о стековой памяти и пространстве кучи мы можем найти в статье Java.

Q7. В чем разница между интерфейсами Сравнения и компаратора?

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

Во-первых, давайте посмотрим на интерфейс Comparable :

public interface Comparable {
    int compareTo(T var1);
}

Мы должны реализовать этот интерфейс классом, объекты которого мы хотим отсортировать.

Он имеет метод compareTo() и возвращает целое число. Он может возвращать три значения: -1, 0 и 1, что означает, что этот объект меньше, равен или больше сравниваемого объекта.

Стоит отметить, что переопределенные сравнение T0() метод должен соответствовать равняется() метод.

С другой стороны, мы можем использовать интерфейс Comparator . Он может быть передан в методы sort() интерфейса Collection или при создании экземпляров отсортированных коллекций. Вот почему он в основном используется для создания одноразовой стратегии сортировки.

Более того, это также полезно, когда мы используем сторонний класс, который не реализует сопоставимый интерфейс.

Как и метод compareTo () , переопределенные методы compare() должны соответствовать методу equals () , но они могут дополнительно разрешать сравнение с нулями.

Давайте посетим статью Comparator и Comparable в Java для получения дополнительной информации.

Q8. Что такое Тип void и когда Мы Его используем?

Каждый раз, когда мы пишем метод на Java, он должен иметь возвращаемый тип. Если мы хотим, чтобы метод не возвращал значения, мы можем использовать ключевое слово void .

Мы также должны знать, что существует класс Void . Это класс-заполнитель, который может использоваться, например, при работе с универсальными файлами. Класс Void не может быть ни создан, ни расширен.

Q9. Каковы методы класса объектов и что они делают?

Важно знать, какие методы содержит класс Object и как они работают. Это также очень полезно, когда мы хотим переопределить эти методы:

  • clone() – возвращает копию этого объекта
  • equals() – возвращает true , когда этот объект равен объекту, переданному в качестве параметра
  • finalize() – сборщик мусора вызывает этот метод во время очистки памяти
  • getClass() – возвращает класс выполнения этого объекта
  • hashCode() – возвращает хэш – код этого объекта. Мы должны знать, что он должен соответствовать методу equals()
  • notify() – отправляет уведомление одному потоку, ожидающему монитора объекта
  • notifyAll() – отправляет уведомление всем потокам, ожидающим монитора объекта
  • toString() – возвращает строковое представление этого объекта
  • wait() – существует три перегруженных версии этого метода. Это заставляет текущий поток ждать указанное количество времени, пока другой поток не вызовет notify() или notifyAll() для этого объекта.

Q10. Что Такое Перечисление и как Мы можем Его использовать?

Enum – это тип класса, который позволяет разработчикам указывать набор предопределенных значений констант. Чтобы создать такой класс, мы должны использовать ключевое слово enum . Давайте представим себе перечисление дней недели:

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY 
}

Для перебора всех констант мы можем использовать метод static values () . Более того, перечисления позволяют нам определять такие элементы, как свойства и методы, такие как обычные классы.

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

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

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

Q11. Что такое КУВШИН?

JAR – это ярлык для Java-архива . Это архивный файл, упакованный с использованием формата ZIP-файла. Мы можем использовать его для включения файлов классов и вспомогательных ресурсов, необходимых для приложений. Он имеет множество функций:

  • Безопасность – мы можем подписывать файлы JAR цифровой подписью
  • Сжатие – при использовании JAR мы можем сжимать файлы для эффективного хранения
  • Переносимость – мы можем использовать один и тот же файл JAR на нескольких платформах
  • Управление версиями – Файлы JAR могут содержать метаданные о файлах, которые они содержат
  • Запечатывание – мы можем запечатать пакет в файле JAR. Это означает, что все классы из одного пакета должны быть включены в один и тот же файл JAR
  • Расширения – мы можем использовать формат файла JAR для упаковки модулей или расширений для существующего программного обеспечения

Q12. Что такое исключение NullPointerException?

Исключение NullPointerException , вероятно, является наиболее распространенным исключением в мире Java. Это непроверенное исключение и, таким образом, расширяет RuntimeException . Мы не должны пытаться справиться с этим.

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

  • вызов метода нулевой ссылки
  • установка или получение поля нулевой ссылки
  • проверка длины ссылки на нулевой массив
  • установка или получение элемента ссылки на нулевой массив
  • метание null

Q13. Каковы два типа приведения в Java? Какое Исключение Может Быть Вызвано При Приведении? Как Мы Можем Этого Избежать?

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

Апкастинг очень прост, так как мы всегда можем это сделать. Например, мы можем передать экземпляр String в тип Object :

Object str = "string";

В качестве альтернативы мы можем понизить переменную. Это не так безопасно, как передача, поскольку она включает в себя проверку типа. Если мы неправильно приведем объект, JVM выдаст ClassCastExcpetion во время выполнения. К счастью, мы можем использовать ключевое слово instanceof для предотвращения недопустимого приведения:

Object o = "string";
String str = (String) o; // it's ok

Object o2 = new Object();
String str2 = (String) o2; // ClassCastException will be thrown

if (o2 instanceof String) { // returns false
    String str3 = (String) o2;
}

Мы можем узнать больше о приведении типов в этой статье .

3. Основные вопросы по языку Java для продвинутых программистов

Q1. Почему String является неизменяемым классом?

Мы должны знать, что объекты String обрабатываются JVM иначе, чем другие объекты . Одно из отличий заключается в том, что объекты String являются неизменяемыми. Это означает, что мы не можем изменить их, как только мы их создали. Есть несколько причин, по которым они так себя ведут:

  1. Они хранятся в пуле string , который является специальной частью памяти кучи. Он отвечает за экономию большого пространства.
  2. Неизменность класса String гарантирует, что его хэш-код не изменится. Благодаря этому Строки могут эффективно использоваться в качестве ключей в коллекциях хэширования. Мы можем быть уверены, что не будем перезаписывать какие-либо данные из-за изменения хэш-кодов.
  3. Их можно безопасно использовать в нескольких потоках. Ни один поток не может изменить значение объекта String , поэтому мы получаем потокобезопасность бесплатно.
  4. Строки являются неизменяемыми, чтобы избежать серьезных проблем с безопасностью. Конфиденциальные данные, такие как пароли, могут быть изменены ненадежным источником или другим потоком.

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

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

Привязка в Java – это процесс связывания вызова метода с соответствующим телом метода. В Java можно выделить два типа привязки: статическую и динамическую.

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

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

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

Q3. Что такое JIT?

JIT расшифровывается как “как раз вовремя”. Это компонент JRE, который запускается во время выполнения и повышает производительность приложения. В частности, это компилятор, который запускается сразу после запуска программы.

Это отличается от обычного компилятора Java, который компилирует код задолго до запуска приложения. JIT может ускорить работу приложения различными способами.

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

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

Q4. Что такое отражение в Java?

Отражение-очень мощный механизм в Java. Отражение-это механизм языка Java, который позволяет программистам исследовать или изменять внутреннее состояние программы (свойства, методы, классы и т.д.) Во время выполнения. Пакет java.lang.reflect предоставляет все необходимые компоненты для использования отражения.

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

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

При работе с модулями начиная с Java 9 мы должны знать, что по умолчанию мы не можем использовать отражение классов, импортированных из другого модуля. Чтобы разрешить другим классам использовать отражение для доступа к закрытым членам пакета, мы должны предоставить разрешение “Отражение”.

В этой статье более подробно рассматривается отражение Java.

Q5. Что такое загрузчик классов?

Загрузчик классов /является одним из самых важных компонентов в Java. Это часть JRE.

Проще говоря, classloader отвечает за загрузку классов в JVM. Мы можем выделить три типа загрузчиков классов:

  • Загрузчик классов начальной загрузки – он загружает основные классы Java. Они находятся в каталоге /jre/lib
  • Extension classloader – он загружает классы, расположенные в /jre/lib/ext или в пути, определенном свойством java.ext.dirs
  • Системный загрузчик классов – он загружает классы в путь к классам нашего приложения

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

Более подробная информация о загрузчиках классов содержится в статье Загрузчики классов в Java.

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

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

Динамическая загрузка класса относится к ситуации, когда мы не можем предоставить определение класса во время компиляции. Тем не менее, мы можем сделать это во время выполнения. Чтобы создать экземпляр класса, мы должны использовать метод Class.forName() :

Class.forName("oracle.jdbc.driver.OracleDriver")

Q7. Какова цель сериализуемого интерфейса?

Мы можем использовать интерфейс Serializable , чтобы включить сериализуемость класса, используя API сериализации Java. Сериализация-это механизм сохранения состояния объекта в виде последовательности байтов, в то время как десериализация-это механизм восстановления состояния объекта из последовательности байтов. Сериализованный вывод содержит состояние объекта и некоторые метаданные о типе объекта и типах его полей.

Мы должны знать, что подтипы сериализуемых классов также сериализуемы. Однако, если мы хотим сделать класс сериализуемым, но его супертип несериализуем, мы должны сделать две вещи:

  • реализуйте Сериализуемый интерфейс
  • убедитесь, что конструктор без аргументов присутствует в суперклассе

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

Q8. Есть ли деструктор в Java?

В Java сборщик мусора автоматически удаляет неиспользуемые объекты, чтобы освободить память. Разработчикам не нужно отмечать объекты для удаления, что может привести к ошибкам. Поэтому разумно, что в Java нет доступных деструкторов.

В случае, если объекты содержат открытые сокеты, открытые файлы или подключения к базе данных, сборщик мусора не сможет восстановить эти ресурсы . Мы можем освободить ресурсы в методе close и использовать синтаксис try-finally для последующего вызова метода до Java 7, например классы ввода-вывода FileInputStream и FileOutputStream . Начиная с Java 7, мы можем реализовать интерфейс Автоклавируемый и использовать оператор try-with-resources для написания более короткого и чистого кода . Но вполне возможно, что пользователи API забывают вызвать метод close , поэтому метод finalize и класс Cleaner появляются, чтобы действовать в качестве защитной сети. Но, пожалуйста, имейте в виду, что они не эквивалентны деструктору.

Это не гарантирует, что как метод finalize , так и Cleaner класс будут запущены быстро. У них даже нет возможности запустить до выхода JVM. Хотя мы могли бы вызвать System.runFinalization , чтобы предложить JVM запустить finalize методы любых объектов, ожидающих завершения, это все еще недетерминировано.

Кроме того, метод finalize может вызвать проблемы с производительностью, взаимоблокировки и т.д. Мы можем найти дополнительную информацию, просмотрев одну из наших статей: Руководство по методу finalize в Java .

Начиная с Java 9 , класс Cleaner добавляется для замены метода finalize | из-за его недостатков. В результате мы лучше контролируем поток, который выполняет очистительные действия.

Но спецификация java указывает на поведение очистителей во время System.exit является специфичной для реализации, и Java не дает никаких гарантий, будут ли вызваны действия по очистке или нет.