12 января 2012 г.

Подготовка к собеседованию на java-программиста (уровень Junior)

Недавно в руки попала табличка, по которой в одной замечательной компании рекомендуют собеседовать java-программистов. Опуская моменты специфичные для данной конкретной вакансии попробуем очень вкраце пробежаться по вопросам, которые я бы рекомендовал повторить перед собеседованием на java-программиста. Это описание для уровня junior, описание для уровня middle (staff) смотри тут.



JavaBean definition
JavaBeans — классы в языке Java, написанные по определённым правилам:
1. Класс должен иметь конструктор без параметров, с модификатором доступа public. Такой конструктор позволяет инструментам создать объект без дополнительных сложностей с параметрами.
2. Свойства класса должны быть доступны через get, set и другие методы (так называемые методы доступа), которые должны подчинятся стандартному соглашению об именах. Это легко позволяет инструментам автоматически определять и обновлять содержание bean’ов. Многие инструменты даже имеют специализированные редакторы для различных типов свойств.
3. Класс должен быть сериализуем. Это даёт возможность надёжно сохранять, хранить и восстанавливать состояние bean независимым от платформы и виртуальной машины способом.



Declaring enums
Перечисление (enum) - это тип, значения которого ограничены конечным набором констант.
public enum Modifiers {
    PUBLIC(1), STATIC(2), FINAL(3);

    Modifier(int id) {
        this.id = id;
    }

    private int id;

    public int getId() {
        return id;
    }

    public static String getClassName() {
        return Modifiers.class.getName();
    }
}
Конструктор может быть либо приватным, либо package-private.


Overriding/Overloading
Overriding - изменяет поведение метода суперкласса (не забываем про аннтоацию @Override).
Overloading - дополняет поведение суперкласса новым методом.


String, StringBuilder, StringBuffer
StringBuffer - синхронизирован (работает медленнее), StringBuilder - нет (работает быстрее). String создает новый instance при модификации.


Collections
Контейнеры бывают двух видов:
  1. Коллекции (List, Set, Queue)
  2. Ассоциативный массив (Map)
К популярным реализациям контейнеров стоит отнести: ArrayList, LinkedList, HashSet, TreeSet, LinkedHashSet, HashMap, TreeMap, LinkedHashMap.

В ArrayList и LinkedList элементы хранятся в порядке вставки. Различаются скоростью выполнения и количеством тех или иных операций: ArrayList имеет оптимизированый доступ к элементам списка, но обладает низкой производительностью при добавлении/удалении элементов в середину списка. LinkedList - наоборот.
HashSet обеспечивает наибольшую производительность при выборке элементов. TreeSet хранит элементы отсортированными по возрастанию в порядке сравнения. LinkedHashSet хранит элменты в порядке добавления.
HashMap обеспечивает наибольшую производительность. TreeMap хранит ключи отсортированными. LinkedHashMap хранит ключи в порядке вставки, но обеспечивает скорость HashMap.

Существует такой нечасто используемый контейнер как PriorityQueue. Изначально он ведет себя так же, как и обычный Queue, т.е. методом offer() вы заносите элемент в очередь, а методом peek() (или poll(), или remove()) - достаете. Однако для PriorityQueue вы можете определить Comparator и тогда элементы будут доставаться из контейнера в указанном вами порядке.

Inner classes
Внутренний класс - класс, который объявлен внутри другого класса, например:
// Outer.java
public class Outer {
    Outer() {
        Inner i = new Inner();
        i.innerMethod();
    }

    // Inner class
    private class Inner {
        Inner() {}

        public void innerMethod() {
            // Do something.
        }
    }
}
Анонимный внутренний класс - это класс, который определен в методе другого класса и может быть использован только один раз - там, где он определен. Примером анонимного внутреннего класса часто является реализация обработчика какого-либо события.

Статический и не-статический внутренние классы различаются методом доступа. Если вы хотите получить экземпляр статического класса, то напишете примерно это:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass;
В то время, как при использовании не-статического внутреннего класса нужно было бы сначала инстанцировать экземлпяр outerObject, а потом написать что-то вроде:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

Наследование от внутренних классов
Делается так:
class WithInner {
   class Inner {}
}
public class InheritInner extends WithInner.Inner {
   // !!! InheritInner() {}  - не скомпилится !!
   InheritInner( WithInner wi ) {
      wi.super();
   }
}
Сборщик мусора
Не забудьте повторить все, что знаете про сборщик мусора (например, тут) Вкраце вот как он может работать:
  • схема "подсчет ссылок". Довольно медленный способ реализации СМ - с каждым объектом храниться счетчик ссылок на него. При выходе ссылки из области видимости счетчик уменьшается, при создании ссылки - увеличивается. Когда он равен 0 - объект убирается из памяти. Метод плох тем, что решение задачи циклических ссылок является ресурсоемкой операцией. Возможно поэтому данный метод не применяется ни в одной из JVM
  • схема "остановиться и скопировать". Работа приложения приостанавливается и СМ копирует все объекты на которые есть живые ссылки из одной кучи в другую. Таким образом мы получаем дефрагментированый участок памяти с живыми объектами и кучу с мусором, который можно уничтожить. Недостатками такого подхода являются неэффективное использование памяти и необходимость останавлиать программу и копировать память даже в том случае, если приложение вошло в стабильный режим работы и уже практически не создает и не удаляет объекты
  • схема "пометить и убрать" похожа на предыдущую. СМ сканирует память и помечает неиспользуемые объекты, но не удаляет их сразу. Удаление происходит в конце процесса сканирования. Потом происходит дефрагментация памяти.
На самом деле начиная с JVM 1.2 всё стало намного интереснее, но едва ли вас это будут спрашивать при устройстве на позицию junior.

Список аргументов переменной длины
Реализуется только так:
void printArgs( Object[] args ) {
    for (Object o : args) {
        System.out.println(o);
    }
}

Удачи!

6 комментариев:

  1. void printArgs( Object... args ) {
    for (Object o : args) {
    System.out.println(o);
    }
    }

    Можно и так реализовать "Список аргументов переменной длины"

    ОтветитьУдалить
    Ответы
    1. Дельное замечание. Хотя Ваш вариант является синтаксическим сахаром и во время компиляции преобразуется в тот, что в статье, но выглядит все ж красивее. Да и со словом "только" я явно погорячился.
      Спасибо!

      Удалить
  2. Конструктор enum-а всегда private! Даже если модификатор доступа отсутствует, то компилятор считает его private по умолчанию

    ОтветитьУдалить
  3. Привет:) Я снимаю ролики про вопросы на собеседовании
    https://www.youtube.com/watch?v=Z0JMABjXnww - HashMap
    https://www.youtube.com/watch?v=IyPaSUFrhaM - ArrayList LinkedList

    ОтветитьУдалить
  4. Круто, очень хорошо расписал! Было бы здорово разобрать побольше вопросов, например из списка «327 вопроса на собеседование Java Developer»
    http://becomejavasenior.com/blog/2015/07/01/327-interview-questions-java-developer/

    ОтветитьУдалить

Примечание. Отправлять комментарии могут только участники этого блога.