Вопросы по Java на собеседовании (1)

1. Принципы объектно-ориентированного программирования (ООП)
2. Class и его свойства
3. Методы базового суперкласса Object
4. Отношения между классами : ассоциация, агрегация и композиция
5. Конструкторы класса
6. Абстрактный класс
7. Статический класс и его свойства
8. Модификаторы свойств класса
9. Использование this и super
10. Сигнатура метода
11. Переопределение и перегрузка методов
12. Инициализация статических полей
13. Использование оператора instanceof
14. Использование модификатора final
15. Порядок инициализации статических полей потомка и его предка
16. Определения понятия «интерфейса»
17. Вложенные и внутренние классы
18. Аннотации классов
19. Использование метода finalize
20. Отличие final, finally, finalize

Вопросы и ответы для собеседование по Java, Содержание.
Вопросы и ответы для собеседование по Java, часть 2.
Вопросы и ответы для собеседование по Java, часть 3.
Вопросы и ответы для собеседование по Java, часть 4.
Вопросы и ответы для собеседование по Java, часть 5.
Вопросы и ответы для собеседование по Java, часть 6.

1. Принципы объектно-ориентированного программирования (ООП)

Объе́ктно-ориенти́рованное программи́рование (ООП) — это методология программирования, основанная на представлении программного обеспечения в виде совокупности объектов, каждый из которых является экземпляром определенного класса. Базовой основой любого класса является Object, включающий определенный набор методов.

Основные принципы ООП : абстракция, инкапсуляция, наследование, полиморфизм.

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

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

Инкапсуляция (encapsulation) — это свойство объединения данных и методов в одном классе, позволяющее отделить внутреннее представление от внешнего (интерфейса). Инкапсуляция позволяет закрыть доступ к полям и методам класса другим объектам, либо предоставить доступ к полям класса через свои методы. Можно сказать, что, с точки зрения java, инкапсуляция – это «сокрытие реализации».

При использовании ООП желательно использовать непрямой доступ к свойствам (полям) какого-либо класса из методов других классов. Для этого в классе необходимо предусмотреть соответствующие методы.

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

Полиморфизм (polymorphism) — это свойство позволяет использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Полиморфные объекты наследуют один и тот же базовый класс; не обязательно, что базовый класс является родителем, он может «быть предком в третьем колене».

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

Подробное описание и примеры ООП представлены здесь.

2. Class и его свойства

Класс (Class) – это шаблон описания одного или нескольких объектов. Объект представляет экземпляр класса. Шаблон (класс) включает свойства, определяемые характеристиками объектов (полями объекта) и методами их управления. Поля класса определяют, например, такие характеристики объекта, как вес, размер, цвет, площадь и т.д. Методы класса позволяют «оперировать» его полями – определять значение (методы set), выдавать значение (методы get) и т.д.

Class может наследовать свойства другого класса. Первоосновой любого класса является суперкласс Object. Пример класса :

public class Rectangle extends Shape
{
    public Rectangle() {
        // ...
    }
    @Override
    public void draw() {
        // ...
    }
    @Override
    public String toString() {
        return "Rectangle[width:" + width + 
                      ", height:" + height + "]";
    }
}

В примере представлен класс прямоугольника Rectangle, наследующий свойства класса (фигуры) Shape, и включающий переопределенные методы draw базового класса Shape и toString суперкласса Object. Метод toString возвращает описанные в Shape свойства класса width и height в текстовом виде. При описании свойств класса был использован модификатор public.

Более подробное описание класса с примерами представлено здесь.

3. Методы суперкласса Object

Object является базовым суперклассом для всех остальных объектов/классов Java, т.е. каждый класс наследует свойства Object. Соответственно все классы наследуют следующие методы суперкласса Object :

public final native Class getClass(); возвращает в run-time класс данного объекта;
public native int hashCode(); возвращает hash-код;
public boolean equals(Object obj); сравнивает текущий клас с объектом obj;
protected native Object clone() throws CloneNotSupportedException; клонирование объекта;
public String toString(); возвращает строковое представление объекта;
public final native void notify(); возобновление работа потока, находящегося в ожидании освобождения «монитора» данного объекта;
public final native void notifyAll(); возобновление работы всех потоков, находящихся в ожидании освобождения «монитора» данного объекта;
public final native void wait(long timeout) throws InterruptedException; поток переходит в режим ожидания в течение указанного времени timeout;
public final void wait(long timeout, int nanos) throws InterruptedException; переход потока в режим ожидания в течение указанного времени timeout;
public final void wait() throws InterruptedException; перевод потока в ожидание, пока другой поток не вызовет notify() или notifyAll() методы для этого объекта;
protected void finalize() throws Throwable; вызывается сборщиком мусора, когда garbage collector определил, что ссылок на объект больше нет.

Подробное описание с примерами использования методов hashCode и equals представлено в разделе безопасности. Примеры использования методов многопочного программирования wait, notify, notifyAll c описанием класса Thread (поток) и интерфейса Runnable можно увидеть здесь. Метод finalize описан ниже.

4. Отношения между классами : ассоциация, агрегация и композиция

Ассоциация - это отношение, при котором объекты одного типа определенным образом связаны с объектами другого типа. Так объект одного типа может содержать или использовать объект другого типа. Например, игрок Player играет в определенной команде Team. В этом случае класс Player будет связан отношением ассоциации с классом Team. Таким образом, идея ассоциации достаточно простая — два объекта могут быть между собой связаны и это надо как-либо образом описать.

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

Агрегация определяет отношение одного объекта к другому. Так, например, студент может посещать дополнительные факультативные занятия.

Композиция определяет более «жесткое отношение», при котором один объект может быть только частью другого объекта. Например Машина и Двигатель : двигатель может существовать отдельно без машины, но он не может быть в двух или трех машинах одновременно, в отличии от студента, который может посещать различные факультативные занятия.

5. Конструкторы класса

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

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

6. Абстрактный класс

Класс может быть объявлен абстрактным при помощи ключевого словом 'abstract'. Абстрактный класс не предполагает создания экземпляров, а может быть использован только как базовый класс. Таким образом, абстрактные классы реализуют на практике один из принципов ООП — полиморфизм. Абстрактный класс может как содержать, так и не содержать абстрактные методы. Абстрактный метод базового класса должен быть переопределен для его неабстрактных потомков; в базовом классе абстрактный метод только описывается.

Пример абстрактного класса представлен здесь.

7. Статический класс и его свойства

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

В java можно использовать статический импорт, применяемый для импорта статических членов класса или интерфейса. Так, например, для импорта статических методов Math.pow() и Math.sqrt() можно использовать в секции import следующий код :

import static java.lang.Math.sqrt;
import static java.lang.Math.pow;
...
// После статического импорта вместо
hypot = Math.sqrt(Math.pow(side1, 2) + Math.pow(side2, 2));

// можно использовать следующее выражение
hypot = sqrt(pow(side1, 2) + pow(side2, 2));

Статический импорт позволяет использовать вызов методов без наименования класса в префиксе.

8. Модификаторы свойств класса

Модификаторы класса позволяют определить уровень доступа к свойству класса. В java существуют следующие модификаторы доступа :

public (открытый) : поле и метод доступны всем;
protected (защищённый) : поле и метод доступны всем классам пакета и наследникам;
private (закрытый) : поле и метод доступны только методам этого класса;
без модификатора : default, friendly, доступ по умолчанию только внутри своего собственного пакета.

Примечание : конструктор класса также может иметь модификатор. Если класс имеет единственный конструктор с модификатором private, то невозможно создать объект данного класса. От такого класса нельзя наследоваться. При попытке наследования будет выдаваться ошибка :
There is no default constructor available in nameClass
А при попытке создать объект этого класса:
nameClass() has private access in nameClass

Можно ли обойти отмеченное выше Примечание? Можно, если в этом классе создать метод, который возвращает экземляр данного класса. Пример :

public class TestPrivate 
{
    private static TestPrivate instance;

    private TestPrivate() {
        System.out.println("Object 'TestPrivate' is created");
    }

    public static TestPrivate getTestPrivate() {
        if (instance == null)
            instance = new TestPrivate();
        return instance;
    }
}

А теперь, создаем объект класса, у которого единственный конструктор с модификатором private. Конечно, new TestPrivate() здесь не пройдет, но решить данную задачу с использованием внутреннего статического метода можно.

public class Test1
{
    public static void main(String[] args) 
    {
        TestPrivate test1 = TestPrivate.getTestPrivate();
        TestPrivate test2 = TestPrivate.getTestPrivate();
        System.out.println("" + (test1 !=  null));
        System.out.println("" + (test2 !=  null));
    }
}

9. Использование this и super

Чтобы обратиться к свойству текущего класса используют this, а к свойству базового класса — super. Так, например, в следующем примере класс Dog наследует свойства класса Animal и переопределяет метод eat(). Для обращения к переопределенному методу eat() своего класса используется вызов this.eat() в методе thisEat. Но, чтобы обратиться из наследника Dog к методу eat() базового класса Animal используется вызов super.eat() в методе superEat.

public class Animal 
{
    public void eat() {
        System.out.println("animal eat");
    }
}
//------------------------------------------- 
public class Dog extends Animal 
{
    @Override
    public void eat() {
        System.out.println("Dog eat");
    }
    public void thisEat() {
        this.eat();
    }
 
    public void superEat() {
        super.eat();
    }
}

Более подробное описание наследования и пример использования this и super представлено здесь.

10. Сигнатура метода

Сигнатура метода (method signature) включает наименование метода с параметрами. При этом порядок параметров методов имеет значение. В сигнатуру метода не входят модификаторы, возвращаемое значение и вызываемые методом исключения. Также в сигнатуры не включены операнды synchronized, native и аннотация метода.

11. Переопределение и перегрузка методов

Если в иерархии классов сигнатура метода класса-наследника совпадает с сигнатурой метода родительского класса, то метод подкласса переопределяет метод базового класса. Вызов переопределённого метода из своего подкласса всегда ссылается на версию, определённую подклассом, а версия метода родительского класса будет скрыта. Чтобы вызвать «переопределенный» метод родительского класса следует использовать super.

В одном java-классе можно создавать несколько методов с одинаковыми наименованиями, но разными сигнатурами. Создание метода с тем же именем, но с другим набором параметров называется перегрузкой. На основе фактических параметров JVM определяет какой из перегруженных методов класса должен выполняться при вызове.

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

12. Инициализация статических полей

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

class Foo {
    // инициализация в месте объявления 
    public static final int SIZE = 32;

    public static List<Integer> ABC;
    // static block
    static {
        // инициализация в статическом блоке
        abc = new ArrayList<Integer>();
        for (int i = 0; i c < 20; ++i) {
            abc.add (new Integer(i));
        }
    }
}

Динамический блок является дополнением к конструктору. В следующем коде роль динамического блока играет процедура init :

public class Car 
{
    public Car () {
        init();
    }
    private void init() {
        System.out.println("Процедура init()");
    }
}

Статические блоки в java выполняются до выполнения конструктора. С их помощью инициализируются статические поля.

13. Использование оператора instanceof

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

String str = "Hello";
int    i   = 0;
String gstr;

if (str instanceof java.lang.String) {
    // сообщение будет выведено в консоль, 
    // т.к. выражение будет true
    System.out.println("str is String");
}

if (i instanceof Integer) {
    // сообщение будет выведено в консоль, 
    // т.к. i будет упакована в Integer
    System.out.println("i is Integer");
}

if (gstr instanceof java.lang.String) {
    // gstr не инициализирована и поэтому проверка 
    // вернет false для null
    System.out.println("gstr is a String");
}

14. Использование модификатора final

Модификатор final может быть применен к полям, методам или классам. Смысл в его применении зависит от сущности, к которой он применен :

  1. Класс с модификатором final не может иметь наследников.
  2. Метод с модификатором final не может быть переопределен в классах наследниках.
  3. Поле помеченное при помощи слова final не может изменить свое значение после инициализации. Инициализируется поле либо при описании, либо в конструкторе, либо в статическом или динамическом блоке.
  4. Значение локальных переменных, а также параметров метода помеченных при помощи слова final не могут быть изменены после присвоения.

15. Порядок инициализации статических полей потомка и его предка

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

16. Определения понятия «интерфейса»

Ключевое слово «интерфейс» (interface) используется для описания полностью абстрактного класса. В описании интерфейса определяются статические поля и методы без тела (кода). Описание методов интерфейса выполняется в классе, реализующим данный интерфейс.

Реализация интерфейса «определяет поведение» класса. Т.е. именно так должны выглядеть все классы, которые реализуют данный интерфейс : включать статические поля и методы. Таким образом, любой класс, реализующий конкретный интерфейс, «знает» только те статичесие поля и методы, которые представлены в интерфейсе, но не более этого (наследование и реализация в несколько поколений расширяет диапазон статических полей и методов наследника).

Пример описания интерфейса SomeInterface и его реализации в классе SomeClass :

public interface SomeInterface
{
    String COMPANY = "Рога и копыта";
	
    void  methodName();
}

class SomeClass implements SomeInterface
{
    @Override
    void  methodName() {
        System.out.println("SomeClass.methodName() ...");
    }
}

По умолчанию все поля, описанные в интерфейсе автоматически являются статическими (static) и неизменными (final). Все методы и переменные неявно объявляются как public. Начиная с 8-ой версии Java в интерфейсе можно объявлять static методы, но они должны включать тело метода.

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

Описание и пример использования интерфейса представлено здесь.

17. Вложенные и внутренние классы

Вложенный класс (nested classes) представляет собой класс, который объявлен внутри объявления другого класса. Вложенные классы могут быть статическими и нестатическими. Нестатические вложенные классы имеют и другое название — внутренние классы (inner classes).

Внутренние Java классы делятся на три вида :

  • внутренние классы-члены;
  • локальные классы;
  • анонимные классы.

Внутренние классы-члены (member inner classes) ассоциируются не с самим внешним классом, а с его экземпляром. Такие классы имеют доступ ко всем полям и методам внешнего класса.

Локальные классы (local classes) определяются в блоке java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно внутри статических и нестатических блоков инициализации.

Анонимный класс (anonymous class) — это локальный класс без имени.

Из внутреннего нестатического класса можно обратиться к нестатическому полю внешнего класса с использованием наименования внешнего класса и оператора this. Например, OuterClass.this.fieldName.

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

Описание и пример использования вложенных (внутренних) классов представлено здесь.

18. Аннотации классов

Аннотация («annotation») в языке Java – это специальная форма метаданных, которая может быть добавлена в исходный код. Аннотированы могут быть пакеты, классы, методы, переменные и параметры. Аннотации стали доступны в самом языке начиная с 5-ой версии Java. В 6-ой версии Java аннотации были интегрированы в компилятор javac.

Имеются встроенные широко используемые аннотации, которые отслеживаются средой разработки IDE и применяются к методу класса :

  • @Override - проверка переопределения метода. IDE вызывает предупреждение компиляции, если метод не найден в родительском классе;
  • @Deprecated - IDE отмечает, что метод устарел и вызывает предупреждение компиляции, если метод используется.
  • @SuppressWarnings - аннотация указывает IDE подавить предупреждения компиляции.

Аннотации, применяемые к другим аннотациям :

  • @Retention - определяет режим хранения аннотации (в исходном коде или в скомпилированном классе);
  • @Documented - отмечает аннотацию для включения в документацию;
  • @Target - отмечает аннотацию как ограничивающую (какие элементы java-аннотации могут быть к ней применены);
  • @Inherited - отмечает, что аннотация может быть расширена подклассами аннотируемого класса.

Подробнее об аннотациях можно прочитать здесь.

19. Использование метода finalize

Метод finalize предназначен для автоматического освобождения системных ресурсов, занимаемых объектом. Это может быть удобным, чтобы постоянно не помнить, что, например, необходимо закрыть соединение с каким-либо ресурсом.

Можно использовать finalize для чистки данных. Но, во-первых, нет гарантии, что он будет вызван, если где-то в коде осталась ссылка на объект. Ну, и во-вторых, нет гарантии на то, что метод будет вызван сразу. Это связано с тем, что если в объекте переопределен метод finalize, то он вызывается не сразу, а помещается в очередь, которая обрабатывается специально созданным для этого потоком. Следует отметить, что в очередь на «финализацию» попадают только те объекты, в которых переопределен метод finalize.

Порядок очистки объектов

Cборщик мусора Garbage Collector не вызывает методы finalize() напрямую (по крайней мере, в HotSpot 1.6), а только добавляет соответствующие объекты в специальный список, вызывая статический метод java.lang.ref.Finalizer.register(Object). Объект класса Finalizer формирует двусвязный список ссылок на объекты, для которых необходимо вызвать finalize(). Т.е. в списке хранятся ссылки на следующий и предыдущий finalize().

Непосредственный вызов методов finalize() происходит в отдельном потоке «Finalizer» (java.lang.ref.Finalizer.FinalizerThread), который создаётся при запуске виртуальной машины, а точнее в статической секции при загрузке класса Finalizer. Методы finalize() вызываются последовательно в том порядке, в котором были добавлены в список сборщиком мусора. Соответственно, если какой-то finalize() зависнет, то он подвесит поток «Finalizer», но не сборщик мусора. Таким образом, если объект не имеет метода finalize(), то он будут исправно удаляться, а вот объекты с методом finalize будут увеличивать очередь до тех пор, пока «не отвиснет» поток «Finalizer», не завершится приложение или не кончится память.

Есть вероятность того, что метод finalize() не будет вызван совсем. Это может произойти в том случае, когда сборщик мусора будет готов вызвать метод объекта, но программа завершила свою работу.

20. Отличие final, finally, finalize

У этих операторов общим является только корень final, и то, что они являются зарезервированными словами в Java.

final — модификатор, применяющийся к классам, методам, переменным.

finally является частью конструкции try-catch-finally, где играет роль блока, который выполняется независимо от событий происходящих в try-catch и выполняется в любом случае.

finalize является методом класса Object.

Вопросы и ответы для собеседование по Java, Содержание.
Вопросы и ответы для собеседование по Java, часть 2.
Вопросы и ответы для собеседование по Java, часть 3.
Вопросы и ответы для собеседование по Java, часть 4.
Вопросы и ответы для собеседование по Java, часть 5.
Вопросы и ответы для собеседование по Java, часть 6.

  Рейтинг@Mail.ru