Вопросы для собеседования Java-разработчика
Какие типы данных в Java?
В Java существует 8 простых типов данных:
1. byte
- 8-битное целое число со знаком (-128 до 127)
2. short
- 16-битное целое число со знаком (-32,768 до 32,767)
3. int
- 32-битное целое число со знаком (-2,147,483,648 до 2,147,483,647)
4. long
- 64-битное целое число со знаком (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
5. float
- 32-битное число с плавающей точкой (1.4E-45 до 3.4028235E+38)
6. double
- 64-битное число с плавающей точкой (4.9E-324 до 1.7976931348623157E+308)
7. char
- 16-битный символ Unicode ('\u0000' до '\uffff')
8. boolean
- логическое значение (true или false)
Также в Java есть объектные типы данных, которые являются экземплярами классов, и могут хранить некоторые данные и иметь методы. Объекты могут хранить данные разных типов, даже простых типов данных. Например, объект типа Integer может хранить целое число типа int.
Разница между простыми типами данных и объектами заключается в способе хранения данных и доступе к ним. Простые типы данных хранятся в стеке, в то время как объекты - в куче. Объекты также могут иметь методы для обработки своих данных, тогда как простые типы данных этого не могут.
Что такое JVM, JDK, JRE?
JVM, JDK и JRE - это три основных понятия в мире Java-разработки. JVM (Java Virtual Machine) - виртуальная машина Java , которая выполняет Java-байткод. Все программы на Java компилируются в байткод, который может быть выполнен на любой платформе, на которую установлена JVM. JDK (Java Development Kit) - это пакет разработчика Java , который включает в себя всё необходимое для разработки Java-приложений, включая компилятор javac, библиотеки классов, документацию, примеры кода и JVM. JRE (Java Runtime Environment) - это пакет для запуска Java-приложений, который включает в себя JVM, библиотеки классов и другие необходимые компоненты для запуска Java-приложений. Кратко говоря, если вы планируете разработку Java-приложений, то вам нужна JDK. Если же вы планируете только запускать Java-приложения, то вам достаточно установить JRE, которая включает в себя JVM.
Зачем используют JVM?
JVM (виртуальная машина Java) — важнейший компонент языка программирования Java. Это абстрактная машина, предоставляющая среду выполнения, в которой может выполняться скомпилированный код Java. Вот несколько причин, почему JVM важна и широко используется в разработке программного обеспечения: 1. Переносимость: код Java можно написать один раз и запустить на любой платформе, на которой установлена JVM, независимо от базового оборудования и операционной системы. Это делает Java-программы легко переносимыми и уменьшает количество кода, необходимого для конкретной платформы. 2. Управление памятью: JVM управляет распределением памяти и автоматически освобождает неиспользуемую память посредством сборки мусора. Это освобождает разработчиков от утомительной и чреватой ошибками задачи ручного управления памятью. 3. Безопасность. Поскольку JVM выполняет код Java в изолированной среде, это предотвращает причинение вреда базовой системе вредоносным кодом. Это делает Java популярным выбором для создания безопасных и надежных приложений. 4. Производительность: JVM создана для оптимизации выполнения кода Java и использует передовые методы, такие как своевременная компиляция, для достижения высокой производительности. В целом, JVM играет критическую роль в языке программирования Java, предоставляя многочисленные преимущества, которые делают его популярным выбором для создания надежных, безопасных и переносимых приложений.
Что такое bytecode?
Java-программы компилируются в байт-код, который может быть распространен и загружен на любой машине, на которой установлено соответствующее окружение выполнения Java. После того, как байт-код загружается в виртуальную машину, он транслируется в машинный код и исполняется. Это позволяет программам Java быть переносимыми между различными платформами без необходимости перекомпилировать их на каждой платформе.
Что такое Reflection?
Рефлексия (Reflection) - это механизм получения данных о программе во время её выполнения (runtime). В Java Reflection осуществляется с помощью Java Reflection API, состоящего из классов пакетов java.lang
и java.lang.reflect
.
Возможности Java Reflection API:
- Определение класса объекта;
- Получение информации о модификаторах класса, полях, методах, конструкторах и суперклассах;
- Определение интерфейсов, реализуемых классом;
- Создание экземпляра класса;
- Получение и установка значений полей объекта;
- Вызов методов объекта;
- Создание нового массива.
Какие модификаторы доступа есть в Java?
1. private
: члены класса доступны только внутри класса.
2. default
, package-private
, package level
: видимость класса/членов класса только внутри пакета. Является модификатором доступа по умолчанию - специальное обозначение не требуется.
3. protected
: члены класса доступны внутри пакета и в наследниках.
4. public
: класс/члены класса доступны всем.
Последовательность модификаторов по возрастанию уровня закрытости: public
, protected
, default
, private
.
Во время наследования возможно изменения модификаторов доступа в сторону большей видимости (для поддержания соответствия принципу подстановки Барбары Лисков).
Зачем нужен модификатор static?
static
— модификатор, применяемый к полю, блоку, методу или внутреннему классу. Данный модификатор указывает на привязку субъекта к текущему классу.
Что такое статический класс?
Это вложенный класс, объявленный с использованием ключевого слова static
. К классам верхнего уровня модификатор static
неприменим.
По сути статический вложенный класс ничем не отличается от любого другого внутреннего класса за исключением того, что его объект не содержит ссылку на создавший его объект внешнего класса.
Тем не менее, благодаря этому статический класс наиболее похож на обычный не вложенный, ведь единственное различие состоит в том, что он упакован в другой класс. В некоторых случаях для нас это преимущество, так как с него у нас есть доступ к приватным статическим переменным внешнего класса.
Пример вложенного статического класса:
public class Vehicle {
public static class Car {
public int km;
}
}
Создание экземпляра данного класса и задание значения внутренней переменной:
Vehicle.Car car = new Vehicle.Car();
car.km = 90;
Для использования статических методов/переменных/класса нам не нужно создавать объект данного класса.
Что такое статические поля?
При создании объектов класса для каждого объекта создается своя копия нестатических обычных полей. А статические поля являются общими для всего класса. Поэтому они могут использоваться без создания объектов класса.
Например, создадим статическую переменную:
public class Program{
public static void main(String[] args) {
Person tom = new Person();
Person bob = new Person();
tom.displayId(); // Id = 1
bob.displayId(); // Id = 2
System.out.println(Person.counter); // 3
// изменяем Person.counter
Person.counter = 8;
Person sam = new Person();
sam.displayId(); // Id = 8
}
}
class Person{
private int id;
static int counter=1;
Person(){
id = counter++;
}
public void displayId(){
System.out.printf("Id: %d \n", id);
}
}
Класс Person
содержит статическую переменную counter, которая увеличивается в конструкторе и ее значение присваивается переменной id
. То есть при создании каждого нового объекта Person
эта переменная будет увеличиваться, поэтому у каждого нового объекта Person
значение поля id
будет на 1 больше чем у предыдущего.
Так как переменная counter
статическая, то мы можем обратиться к ней в программе по имени класса:
System.out.println(Person.counter); // получаем значение
Person.counter = 8; // изменяем значение
Консольный вывод программы:
Id = 1
Id = 2
3
Id = 8
Что такое статические константы?
Также статическими бывают константы, которые являются общими для всего класса.
public class Program{
public static void main(String[] args) {
double radius = 60;
System.out.printf("Radisu: %f \n", radius); // 60
System.out.printf("Area: %f \n", Math.PI * radius); // 188,4
}
}
class Math{
public static final double PI = 3.14;
}
Стоит отметить, что на протяжении всех предыдущих тем уже активно использовались статические константы. В частности, в выражении:
System.out.println("hello");
out
как раз представляет статическую константу класса System
. Поэтому обращение к ней идет без создания объекта класса System
.
Что такое статические инициализаторы?
Статические инициализаторы предназначены для инициализации статических переменных, либо для выполнения таких действий, которые выполняются при создании самого первого объекта. Например, определим статический инициализатор:
public class Program{
public static void main(String[] args) {
Person tom = new Person();
Person bob = new Person();
tom.displayId(); // Id = 105
bob.displayId(); // Id = 106
}
}
class Person{
private int id;
static int counter;
static{
counter = 105;
System.out.println("Static initializer");
}
Person(){
id=counter++;
System.out.println("Constructor");
}
public void displayId(){
System.out.printf("Id: %d \n", id);
}
}
Статический инициализатор определяется как обычный, только перед ним ставится ключевое слово static
. В данном случае в статическом инициализаторе мы устанавливаем начальное значение статического поля counter
и выводим на консоль сообщение.
В самой программе создаются два объекта класса Person
. Поэтому консольный вывод будет выглядеть следующим образом:
Static initializer
Constructor
Constructor
Id: 105
Id: 106
Стоит учитывать, что вызов статического инициализатора производится после загрузки класса и фактически до создания самого первого объекта класса.
Что такое статические методы?
Статические методы также относятся ко всему классу в целом. Например, в примере выше статическая переменная counter
была доступна извне, и мы могли изменить ее значение вне класса Person
. Сделаем ее недоступной для изменения извне, но доступной для чтения. Для этого используем статический метод:
public class Program{
public static void main(String[] args) {
Person.displayCounter(); // Counter: 1
Person tom = new Person();
Person bob = new Person();
Person.displayCounter(); // Counter: 3
}
}
class Person{
private int id;
private static int counter = 1;
Person(){
id = counter++;
}
// статический метод
public static void displayCounter(){
System.out.printf("Counter: %d \n", counter);
}
public void displayId(){
System.out.printf("Id: %d \n", id);
}
}
Теперь статическая переменная недоступна извне, она приватная. А ее значение выводится с помощью статического метода displayCounter
. Для обращения к статическому методу используется имя класса: Person.displayCounter()
.
При использовании статических методов надо учитывать ограничения: в статических методах мы можем вызывать только другие статические методы и использовать только статические переменные.
Вообще методы определяются как статические, когда методы не затрагиют состояние объекта, то есть его нестатические поля и константы, и для вызова метода нет смысла создавать экземпляр класса. Например:
public class Program{
public static void main(String[] args) {
System.out.println(Operation.sum(45, 23)); // 68
System.out.println(Operation.subtract(45, 23)); // 22
System.out.println(Operation.multiply(4, 23)); // 92
}
}
class Operation {
static int sum(int x, int y){
return x + y;
}
static int subtract(int x, int y){
return x - y;
}
static int multiply(int x, int y){
return x * y;
}
}
В данном случае для методов sum
, subtract
, multiply
не имеет значения, какой именно экземпляр класса Operation
используется. Эти методы работают только с параметрами, не затрагивая состояние класса. Поэтому их можно определить как статические.