Java собеседование: вопросы по ООП
1. Какие особенности есть у Java?
Ответ:-
ООП концепты:
- an objectная ориентированность;
- наследование;
- инкапсуляция;
- полиморфизм;
- абстракция.
-
Кроссплатформенность: программа на Java может быть запущена на любой платформе без Howих-либо изменений. Единственное, что нужно — установленная JVM (java virtual machine).
-
Высокая производительность: JIT(Just In Time compiler) позволяет высокую производительность. JIT конвертирует byte-code в машинный code и потом JVM стартует выполнение.
- Мультипоточность: поток выполнения, известный How
Thread
. JVM создает thread, который называетсяmain thread
. Программист может создать несколько потоков наследованием от класса Thread or реализуя интерфейсRunnable
.
2. What такое наследование?
Под наследованием подразумевается, что один класс может наследовать("extends") другой класс. Таким образом можно переиспользовать code с класса, от которого наследуются. Существующий класс известен Howsuperclass
, а создаваемый — subclass
. Также еще говорят parent
и child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
где Animal
— это parent
, а Dog
— child
.
3. What такое инкапсуляция?
Такой вопрос часто встречается на собеседовании Java-разработчика. Инкапсуляция — это сокрытие реализации при помощи модификаторов доступа, при помощи геттеров и сеттеров. Это делается для того, чтобы закрыть доступ для внешнего использования в тех местах, где разработчики считают нужным. Доступный пример из жизни — это автомобиль. У нас нет прямого доступа к работе двигателя. Для нас работа заключается в том, чтобы вставить ключ в зажигание и включить двигатель. А Howие уже процессы будут происходить под капотом — не наше дело. Даже более того, наше вмешательство в эту деятельность может привести к непредсказуемой ситуации, из-за которой можно и машину сломать, и себе навредить. Ровно то же самое происходит и в программировании. Хорошо описано в википедии. Статья об инкапсуляции есть и на JavaRush.4. What такое полиморфизм?
Полиморфизм — это способность программы идентично использовать an objectы с одинаковым интерфейсом без информации о конкретном типе этого an object. Как говорится, один интерфейс — множество реализаций. При помощи полиморфизма можно объединять и использовать разные типы an objectов по их общему поведению. Например, есть у нас класс Animal, у которого есть два наследника — Dog и Cat. У общего класса Animal есть общее поведение для всех — издавать звук. В случае, когда нужно собрать воедино всех наследников класса Animal и выполнить метод "издавать звук", используем возможности полиморфизма. Вот How будет это выглядеть:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Таким образом, полиморфизм помогает нам. Причем это относится и к полиморфным (перегруженным) методам. Практика использования полиморфизма
Вопросы на собеседовании — Java Syntax
5. What такое конструктор в Java?
Следующие характеристики являются валидными:- Когда новый an object создается, программа использует для этого соответствующий конструктор.
- Конструктор похож на метод. Его особенность заключается в том, что нет возвращающего element (в том числе и void), а его Name совпадает с именем класса.
- Если не пишется ниHowого конструктора явно, пустой конструктор будет создан автоматически.
- Конструктор может быть переопределен.
- Если был создан конструктор с параметрами, а нужен еще и без параметров, его нужно писать отдельно, так How он не создается автоматически.
6. Какие два класса не наследуются от Object?
Не ведитесь на провокации, нет таких классов: все классы прямо or через предков наследуются от класса Object!7. What такое Local Variable?
Еще один из популярных вопросов на собеседовании Java-разработчика. Local variable — это переменная, которая определена внутри метода и существует вплоть до того момента, пока выполняется этот метод. Как только выполнение закончится, локальная переменная перестанет существовать. Вот программа, которая использует локальную переменную helloMessage в методе main():
public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. What такое Instance Variable?
Instance Variable — переменная, которая определена внутри класса, и она существует вплоть до того момента, пока существует an object. Пример — класс Bee, в котором есть две переменные nectarCapacity и maxNectarCapacity:
public class Bee {
/**
* Current nectar capacity
*/
private double nectarCapacity;
/**
* Maximal nectar that can take bee.
*/
private double maxNectarCapacity = 20.0;
...
}
9. What такое модификаторы доступа?
Модификаторы доступа — это инструмент, при помощи которого можно настроить доступ к классам, методам и переменным. Бывают следующие модификаторы, упорядоченные в порядке повышения доступа:private
— используется для методов, полей и конструкторов. Уровень доступа — только класс, внутри которого он объявлен.package-private(default)
— может использоваться для классов. Доступ только в конкретном пакете (package), в котором объявлен класс, метод, переменная, конструктор.protected
— такой же доступ, How иpackage-private
+ для тех классов, которые наследуются от класса с модификаторомprotected
.public
— используется и для классов. Полноценный доступ во всем приложении.
10. What такое переопределение (overriding) методов?
Переопределение методов происходит, когда child хочет изменить поведение parent класса. Если нужно, чтоб выполнилось-таки то, что есть в методе parent, можно использовать в child конструкцию вида super.methodName(), что выполнит работу parent метода, а уже потом добавить логику. Требования, которые нужно соблюдать:- сигнатура метода должна быть такая же;
- возвращаемое meaning должно быть таким же.
11. What такое сигнатура метода?
Сигнатура метода — это набор из названия метода и аргументов, Howие принимает метод. Сигнатура метода является уникальным идентификатором для метода при перегрузке методов.12. What такое перегрузка методов?
Перегрузка методов — это свойство полиморфизма, в котором при помощи изменения сигнатуры метода можно создать разные методы для одних действий:- одно и то же Name метода;
- разные аргументы;
- может быть разный возвращаемый тип.
add()
из ArrayList
может быть перегружен следующим образом и будет выполнять добавление разным способом, в зависимости от входящих аргументов:
add(Object o)
— просто добавляет an object;add(int index, Object o)
— добавляет an object в определенный индекс;add(Collection<Object> c)
— добавляет список an objectов;add(int index, Collection<Object> c)
— добавляет список an objectов, начиная с определенного индекса.
13. What такое Interface?
Множественное наследование не реализовано в джаве, поэтому чтобы преодолеть эту проблему, были добавлены интерфейсы в том виде, в котором мы их знаем ;) Долгое время у интерфейсов были только методы без их реализации. В рамках этого ответа поговорим именно о них. Например:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
Из этого вытекают некоторые нюансы:
- все методы в интерфейсе — публичные и абстрактные;
- все переменные — public static final;
- классы не наследуют их (extends), реализовывают (implements). Причем реализовывать можно сколь угодно много интерфейсов.
- классы, которые реализуют интерфейс, должны предоставить реализацию всех методов, которые есть в интерфейсе.
public class Cat implements Animal {
public void makeSound() {
// method implementation
}
public void eat() {
// implementation
}
public void sleep() {
// implementation
}
}
14. What такое default method в Interface?
Теперь поговорим о дефолтных методах. Для чего, для кого? Эти методы добавor, чтобы все сделать "и вашим, и нашим". О чем это я? Да о том, что с одной стороны нужно было добавить новую функциональность: лямбды, Stream API, с другой стороны, нужно было оставить то, чем славится джава — обратную совместимость. Для этого нужно было ввести уже готовые решения в интерфейсы. Так к нам и пришли дефолтные методы. То есть, дефолтный метод — это реализованный метод в интерфейсе, у которого есть ключевое словоdefault
. Например, всем известный метод stream()
в интерфейсе Collection
. Проверьте, этот интерфейс вовсе не так прост How кажется ;). Или также не менее известный метод forEach()
из интерфейса Iterable
. Его также не было до тех пор, пока не добавor дефолтные методы. Кстати, еще можно почитать на JavaRush об этом.
15. А How тогда наследовать два одинаковых дефолтных метода?
Исходя из предыдущего ответа на то, что такое дефолтный метод, можно задать другой вопрос. Если можно реализовать методы в интерфейсах, то теоретически можно реализовать два интерфейса с одинаковым методом, и How такое делать? Есть два разных интерфейса с одинаковым методом:
interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
И есть класс, который реализует эти два интерфейса. Whatбы не было неопределенности и скомпorровался code, нам нужно переопределить метод foo()
в классе C
, причем можно просто вызвать в нем метод foo()
любого из интерфейсов — A
or B
. Но только How выбрать специфический метод интерфейса А
or В
? Для этого есть конструкция такого вида: A.super.foo()
:
public class C implements A, B {
@Override
public void foo() {
A.super.foo();
}
}
or:
public class C implements A, B {
@Override
public void foo() {
B.super.foo();
}
}
Таким образом, метод foo()
класса C
будет использовать либо дефолтный метод foo()
из интерфейса A
, либо метод foo()
из интерфейса B
.
16. What такое абстрактные методы и классы?
В джава есть зарезервированное словоabstract
, которое используется для обозначения абстрактных классов и методов. Для начала — определения. Абстрактным методом называется метод, который создан без реализации с ключевым словом abstract
в абстрактном классе. То есть, это метод How в интерфейсе, только с добавкой ключевого слова, например:
public abstract void foo();
Абстрактным классом называется класс, который имеет также abstract
слово:
public abstract class A {
}
У абстрактного класса есть несколько особенностей:
- на его основе нельзя создать an object;
- он может иметь абстрактные методы;
- он может и не иметь абстрактные методы.
17. Какая разница между String, String Builder и String Buffer?
ЗначенияString
хранятся в пуле стрингов (constant string pool). Как только будет создана строка, она появится в этом пуле. И удалить ее будет нельзя. Например:
String name = "book";
...переменная будет ссылаться на стринг пул Constant string pool Если задать переменной name другое meaning, получится следующее:
name = "pen";
Constant string pool Таким образом, эти два значения так и останутся там. String Buffer:
- значения
String
хранятся в стеке(Stack). Если meaning изменено, значит новое meaning будет заменено на старое; String Buffer
синхронизирован, и поэтому он потокобезопасный;- из-за потокобезопасности speed работы оставляет желать лучшего.
StringBuffer name = "book";
Как только meaning name сменится, в стеке измениться meaning: StringBuilder Точно такой же, How и StringBuffer
, только он не потокобезопасный. Поэтому speed его явно выше, чем в StringBuffer
.
18. Какая разница между абстрактным классом и интерфейсом?
Абстрактный класс:- абстрактные классы имеют дефолтный конструктор; он вызывается каждый раз, когда создается потомок этого абстрактного класса;
- содержит How абстрактные методы, так и не абстрактные. По большому счету может и не содержать абстрактных методов, но все равно быть абстрактным классом;
- класс, который наследуется от абстрактного, должен реализовать только абстрактные методы;
- абстрактный класс может содержать Instance Variable(смотри вопрос №5).
- не имеет ниHowого конструктора и не может быть инициализирован;
- только абстрактные методы должны быть добавлены (не считая default methods);
- классы, реализующие интерфейс, должны реализовать все методы (не считая default methods);
- интерфейсы могут содержать только константы.
19. Почему доступ по элементу в массиве происходит за O(1)?
Это вопрос буквально с последнего собеседования. Как я узнал позже, это вопрос задается для того, чтобы увидеть, How человек мыслит. Ясно, что практического смысла в этих знаниях немного: хватает только лишь знания этого факта. Для начала нужно уточнить, что O(1) — это обоmeaning временной сложности алгоритма, когда операция проходит за константное время. То есть это обоmeaning самого быстрого выполнения. Whatбы ответить на этот вопрос, нужно понять, что мы знаем о массивах? Whatб создать массивint
, мы должны написать следующее:
int[] intArray = new int[100];
Из этой записи можно сделать несколько выводов:
- При создании массива известен его тип.Если известен тип, то понятно, Howого размера будет каждая ячейка массива.
- Известно, Howого размера будет массив.
А How получается О(1) в доступе к an objectм в ArrayList?
Это вопрос сразу же идет за предыдущим. Ведь правда, когда мы работаем с массивом и там примитивы, то нам известно заранее, Howой размер этого типа, при его создании. А что делать, если есть такая схема, How на картинке: и мы хотим создать коллекцию с elementми, у которых тип A, и добавить разные реализации — B, C, D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Как в этой ситуации понять, Howой будет размер у каждой ячейки, ведь каждый an object будет разным и может иметь разные дополнительные поля (or быть fully различными). What делать? Здесь вопрос ставится так, чтобы запутать и сбить с толку. Мы же знаем, что на самом деле в коллекции хранятся не an objectы, а лишь ссылки на эти an objectы. А у всех ссылок размер один и тот же, и он известен. Поэтому здесь работает подсчет места так же, How и в предыдущем вопросе.
21. Автоупаковка (autoboxing) и Authorаспаковка (unboxing)
Историческая справка: автоупаковка и автораспаковка - одно из главных нововведений JDK 5. Автоупаковка (autoboxing) - процесс автоматического преобразования из примитивного типа в соответствующий класс обертку. Authorаспаковка (unboxing) - делает ровно обратное к автоупаковке - преобразует класс обертку в примитив. А вот если окажется meaning оберткиnull
, то при распаковке будет выброшено исключение NullPointerException
.
Соответствие примитив - обертка
Примитив | Класс обертка |
---|---|
boolean | Boolean |
int | Integer |
byte | Byte |
char | Character |
float | Float |
long | Long |
short | Short |
double | Double |
Автоупаковка происходит:
-
когда присваивают примитиву ссылку на класс обертку:
ДО Java 5:
//manual packaging or how it was BEFORE Java 5. public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // and so on to other types } после Java 5: //automatic packaging or how it became in Java 5. public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // and so on to other types }
-
когда передают примитив в аргумент метода, который ожидает обертку:
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
Authorаспаковка происходит:
-
когда присваиваем классу обертке примитивную переменную:
//before Java 5: int intValue = new Integer(4).intValue(); double doubleValue = new Double(2.3).doubleValue(); char c = new Character((char) 3).charValue(); boolean b = Boolean.TRUE.booleanValue(); //and after JDK 5: int intValue = new Integer(4); double doubleValue = new Double(2.3); char c = new Character((char) 3); boolean b = Boolean.TRUE;
-
В случаях с арифметическими операциями. Они применяются только к примитивным типам, для этого нужно делать распаковку к примитиву.
// Before Java 5 Integer integerBox1 = new Integer(1); Integer integerBox2 = new Integer(2); // for comparison it was necessary to do this: integerBox1.intValue() > integerBox2.intValue() //в Java 5 integerBox1 > integerBox2
-
когда передают в обертку в метод, который принимает соответствующий примитив:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. What такое ключевое слово final и где его использовать?
Ключевое словоfinal
можно использовать для переменных, методов и классов.
- final переменную нельзя переназначить на другой an object.
- final класс бесплоден)) у него не может быть наследников.
- final метод не может быть переопределен у предка.
final переменные
;Java дает нам два способа создать переменную и присвоить ей некоторое meaning:- Можно объявить переменную и инициализировать ее позже.
- Можно объявить переменную и сразу же назначить ее.
public class FinalExample {
//final static variable, which is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
//final is a variable that is not initialized, but will only work if
//initialize this in the constructor:
final long creationTime;
public FinalExample() {
this.creationTime = System.currentTimeMillis();
}
public static void main(String[] args) {
FinalExample finalExample = new FinalExample();
System.out.println(finalExample.creationTime);
// final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// final field Config.creationTime cannot be assigned
// finalExample.creationTime = 1L;
}
}
Можно ли считать Final переменную константой?
Поскольку у нас не получится присвоить новое meaning для final переменной, кажется, что это переменные константы. Но это только на первый взгляд. Если тип данных, на который ссылается переменная —immutable
, то да, это константа. А если тип данных mutable
, то есть изменяемый, при помощи методов и переменных можно будет изменить meaning an object, на который ссылается final
переменная, и в таком случае назвать ее константой нельзя. Так вот, на примере видно, что часть финальных переменных действительно константы, а часть — нет, и их можно изменить.
public class FinalExample {
//immutable final variables:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// mutable filter variables
final List<String> addresses = new ArrayList();
final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}
Local final переменные
Когдаfinal
переменная создается внутри метода, ее называют local final
переменная:
public class FinalExample {
public static void main(String[] args) {
// This is how you can
final int minAgeForDriveCar = 18;
// or you can do it this way, in the foreach loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
Мы можем использовать ключевое слово final
в расширенном цикле for
, потому что после завершения итерации цикла for
каждый раз создается новая переменная. Только это все не относится к нормальному циклу for, поэтому приведенный ниже code выдаст ошибку времени компиляции.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
Final класс
Нельзя расширять класс, объявленный Howfinal
. Проще говоря, ниHowой класс не может наследоваться от данного. Прекрасным примером final
класса в JDK является String
. Первый шаг к созданию неизменяемого класса — пометить его How final
, и тогда нельзя будет его расширить:
public final class FinalExample {
}
// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}
Final методы
Когда метод маркирован How final, его называют final метод (логично, правда?). Final метод нельзя переопределять у класса наследника. К слову, методы в классе Object — wait() и notify() — это final, поэтому у нас нет возможность их переопределять.
public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// compile error here
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Как и где использовать final в Java
- использовать ключевое слово final, чтобы определить некоторые константы уровня класса;
- создавать final переменные для an objectов, когда вы не хотите, чтобы они были изменены. Например, специфичные для an object свойства, которые мы можем использовать для целей логирования;
- если не нужно, чтобы класс был расширен, отметить его How окончательный;
- если нужно создать immutable< класс, нужно сделать его финальным;
- если нужно, чтоб реализация метода не менялась в наследниках, обозначить метод How
final
. Это очень важно, чтобы быть уверенным, что реализация не изменится.
23. What такое mutable immutable?
Mutable
Mutable называются an objectы, чьи состояния и переменные можно изменить после создания. Например такие классы, How StringBuilder, StringBuffer. Пример:
public class MutableExample {
private String address;
public MutableExample(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
// this setter can change the name field
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
MutableExample obj = new MutableExample("first address");
System.out.println(obj.getAddress());
// update the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
Immutable
Immutable называются an objectы, состояния и переменные которых нельзя изменить после создания an object. Чем не отличный ключ для HashMap, да?) Например, String, Integer, Double и так далее. Пример:
// make this class final so no one can change it
public final class ImmutableExample {
private String address;
ImmutableExample (String address) {
this.address = address;
}
public String getAddress() {
return address;
}
//remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("old address");
System.out.println(obj.getAddress());
// Therefore, do not change this field in any way, so this is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
24. Как написать immutable класс?
После того, How выясните, что такое mutable и immutable an objectы, следующий вопрос будет закономерный — How написать его? Whatб написать immutable неизменяемый класс, нужно следовать простым пунктам:- сделать класс финальным.
- сделать все поля приватными и создать только геттеры к ним. Сеттеры, разумеется, не нужно.
- Сделать все mutable поля final, чтобы установить meaning можно было только один раз.
- инициализировать все поля через конструктор, выполняя глубокое копирование (то есть, копируя и сам an object, и его переменные, и переменные переменных, и так далее)
- клонировать an objectы mutable переменных в геттерах, чтобы возвращать только копии значений, а не ссылки на актуальные an objectы.
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {
private final int age;
private final String name;
private final HashMap<String, String> addresses;
public int getAge() {
return age;
}
public String getName() {
return name;
}
/**
* Clone the object before returning it.
*/
public HashMap<String, String> getAddresses() {
return (HashMap<String, String>) addresses.clone();
}
/**
* In the constructor, deep copy the mutable objects.
*/
public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
System.out.println("Performing a deep copy in the constructor");
this.age = age;
this.name = name;
HashMap<String, String> temporaryMap = new HashMap<>();
String key;
Iterator<String> iterator = addresses.keySet().iterator();
while (iterator.hasNext()) {
key = iterator.next();
temporaryMap.put(key, addresses.get(key));
}
this.addresses = temporaryMap;
}
}
GO TO FULL VERSION