Как стать автором
Обновить

Подготовка к экзамену Oracle Java SE 7 Programmer II (1Z0-804)

Время на прочтение 14 мин
Количество просмотров 80K

Приветствую уважаемых хабражителей и Java-программистов!
Cтатья посвящена подготовке к сдаче экзамена Oracle Java SE7 Professional с кодовым номером 1Z0-804. Про это на Хабре уже было написано множество постов (например здесь, здесь, тут, здесь, здесь, тут, тут, и вот тут), поэтому постараюсь не повторяться и дополнить заметками о том что наиболее часто встречалось, важными нюансами, которые на мой взгляд были пропущены или недостаточно хорошо освещены в указанных статьях, и вообще в общедоступной литературе (сразу отмечу, что материал не претендует на полноту, здесь я лишь старался обозначить каверзные вопросы с экзамена и лаконично изложить некоторые сложные вещи). Так же поделюсь своими соображениями насчет того, по каким материалам лучше готовиться. С первого раза экзамен сдать не получилось, поэтому начал сохранять для себя различные заметки, где записывал всё что мне казалось сложным или трудно-запоминаемым. Которыми теперь и решил с вами поделится. Заранее прошу проявить понимание, если вы вдруг заметите ошибку, недочёт или очепятку — пишите в комментарии.


Общая информация


Экзамен сдавал в Москве, стоит 150$, длится 2,5 часа, при себе иметь 2 документа. 90 вопросов, 65% проходной балл, т.е. неправильно ответить можно примерно на 30 вопросов (не уверен что за каждый вопрос дают одинаковое кол-во баллов). При необходимости — повторная пересдача только через 2 недели. Регистрация на экзамен на сайте Pearson VUE, там же выбирается тестинговый центр и дата, оплата по карте (как то раз платил в самом центре налом, но это выходило дороже). В этот раз ездил в ACET(Американский Центр Образования и Тестирования), м. Октябрьская. На сдаче тут всё серьёзно — вплоть до выворачивания карманов и запрета брать с собой любые вещи (телефон понятно, но вот например воду — как то слишком). Выходить из кабинета разрешается, но «под расписку», и экзамен при этом не останавливается. Результаты присылают минут через 30 на почту (или на аккаунт на Oracle Certview, где отображаются все экзамены и полученные сертификаты). Бумажный сертификат приходит в среднем в течение 1.5-2 мес (Московская обл.).

Рекомендации по подготовке


Ну конечно же программировать, программировать и снова программировать! И шутка это только отчасти. Поскольку Java я начал изучать недавно, и практического опыта было мало, то поначалу пытался подготовиться в основном по книжкам и различным тестам, но всё это быстро вылетает из головы, и обязательно нужна практика, чтобы всё уложилось и главное не забывалось. Для подготовки пользовался следующими материалами:
  • Книжка Java SE 6 Programmer Practice Exams (Bert Bates, Kathy Sierra, 2011 г). По сути сборник тестов и рекомендаций. Правда она немного устаревшая и по 6 версии, но в остальном тесты и объяснения к ним довольно близки к реальным вопросам. Единственный минус — тут недостаёт следующих тем: NIO.2, JDBC и Concurrency (включая Executor-ы и Fork-Join Framework). Ошибок в ответах не обнаружено, что приятно.
  • Тесты Enthuware. Стоят порядка 10$ за 1 экзамен, и скачивается соответствующая программка. Охватывает все темы экзамена, хотя некоторых теоретических вопросов, встретившихся на экзамене, там нет. Изредка встречаются ошибки, так что если не уверены в показанном ответе — обязательно проверяйте. В целом, этот тест оставил положительное впечатление, т.к. по характеру «уловок» многие вопросы были схожи с экзаменационными, есть разбивка по темам и самих вопросов очень много (полагаю порядка 300+).
  • Официальный туториал от OraclePreparation for Java Programmer Language Certification Programmer Level II Exam. Это коварная штука, на которую я наткнулся изначально, будучи уверенным что уж тут должно быть всё и полностью… И вот провалил тест. А реально его можно использовать скорее как оглавление по темам и используемым в экзамене классам, да и то с оговорками. Во-первых, полнота информации и полнота и сложность примеров, в сравнении с экзаменом, оставляет желать лучшего. Во вторых, некоторые топики (например JDBC, NIO2) раздуты тут до лишней детальности, чего на экзамене я не встретил. В-третьих, тут нету теории паттернов (а Oracle эту тему как оказалось очень любит — разбирайтесь досконально). Ну и еще по мелочи, вроде некоторых моментов теории дизайна классов и потоков я тут тоже не видел (о том что нужно знать — напишу ниже).
  • Различные приложения-тесты под Android. На мой взгляд наиболее полезными/удобными оказались SCJPv1 и SCJP Champ (хотя и устаревшими).
  • Ну и конечно же различная литература по Java, кому какая больше нравится, я пользовался электронной Java 7 — The Complete Reference (Herbert Schildt), Философия Java (Брюс Эккель), официальной документацией и гуглом.
  • В принципе в Интернетах еще есть много всяческих тестов, и дорогих, не очень, и вообще бесплатных, но поскольку гарантий на объективность тут никаких — было решено выбрать золотую середину и на ней остановиться.


Основные темы


Дизайн классов, конструкторы, доступ


  • Одна из важных тем, по которой часто встречаются подвохи на экзамене. Уровень доступа к переменным и методам класса, перегрузки и переопределения, всевозможные конструкторы в конструкторе и т.п.
  • Необходимо четко знать порядок инициализации различных частей класса, в т.ч. в случае наследования. Пример:
    class SuperTest
    {
        static { System.out.print("1"); }
        { System.out.print("3"); }    
        public SuperTest() { System.out.print("4"); }
    }
    
    public class Test extends SuperTest
    {
        static { System.out.print("2"); }
        { System.out.print("5"); }    
        public Test() { System.out.print("6"); }
    
        public static void main(String[] args) { new Test(); }
    }
    

    Данный фрагмент напечатает 123456. Если менять местами статические и динамические блоки инициализации, ничего не изменится. В случае наличия нескольких блоков одного типа, они выполняются последовательно.
  • Встречаются различные «игры» с конструктором без аргументов (или конструктором по умолчанию), который создаётся Java автоматически, если не задан явно. Нужно помнить, что если в пустом классе создан конструктор_с_аргументами, то конструктор_по_умолчанию уже создаваться не будет, а это означает, что если создать наследника и не указать явно какой конструктор суперкласса вызывать, то Java будет пытаться вызываться конструктор_по_умолчанию (которого у нас нету), что вызовет ошибку компиляции.
  • Нужно помнить важную особенность модификатора protected (разрешает доступ из того-же пакета и наследникам). Если наследник находится в другом пакете, он не имеет доступа к protected-члену через ссылку на объект суперкласса (base_class.var), но может обращаться к protected-полям суперкласса через цепочку наследования (super.var) либо через ссылку на объект этого же класса (extended_class.var, this.var).
  • Часто любят забывать модификатор public при реализации интерфейса, или неверный тип исключения при переопределении метода, или отсутствие доступа.
  • Из статического контекста нельзя обращаться к нестатическому, в т.ч. запрещены обращения к this и super.
  • Встречались несколько вопросов, где присутствуют одинаковые названия переменных или методов. Самый простой случай — shadowing переменной у наследника, и использование переменной исходного класса и наследника в исходном и переопределяемом методе соответственно. Так же есть вопросы, где класс реализует два разных интерфейса с одноименными методами и переменными. И аналогичные примеры с одинаковыми названиями во вложенных классах. Проверяйте это всё самостоятельно на «живом коде», подмешивая в них различные this.var, super.var, ((ClassName)obj).var, разные исключения в одноименные методы и т.п.


Исключения


  • Очень важно помнить базовую иерархию исключений. На нижеприведенной диаграмме красным отмечены т.н. unckecked exceptions (не требующие обработки), желтый — checked (требующие обработки). Собственно на картинке как раз показан тот необходимый минимум, который нужно знать «на зубок», т.к. каверзные вопросы встречаются довольно часто.


  • Если вызываемое исключение не обрабатывается в блоке catch (или блок catch отсутствует) и есть блок finally, то сперва выполнится блок finally, а затем сгенерируется исключение.
  • Конструкция method() throws SomeCheckedException {… } не обязывает метод вызывать внутри исключение SomeCheckedException (checked-типа). Но при этом вызов этого метода воспринимается как требующий обработки (в try… catch или throws). Пример для класса Exception: mechod() throws Exception не обязывает вызывать (throw) его внутри метода (в т.ч. метод может быть пустой), но при это внешний вызов mechod() так же требует обработки исключения.
  • Внутри try {...} catch (ExceptionClass e) {...} исключение ExceptionClass должно «вызываться», но только в случае если оно типа checked! Иначе будет ошибка компиляции: «exception is never thrown in body of corresponding try statement».
  • Классы исключений в блоке catch, перечисляемые через вертикальную черту («или»), не должны лежать на одной ветви иерархии наследования. К примеру, можно написать catch (ClassNotFoundException | IOException e), а вот catch(IOException | FileNotFoundException e) — не скомпилируется.


Методы и параметры


  • Передача параметров по ссылке / значению. Помните, что в Java параметры в процедуры передаются по значению (т.е. когда изменение значения параметра внутри процедуры не затронет переданную извне переменную), но только с оговоркой. Для простых типов (int, float, ...) это верно. Для объектов же (в т.ч. массивов), происходит передача ссылки, а значит изменения в объекте будут видны в вызывающей процедуре. А вот для обёрток простых типов (Int, Float, ...) и String, вследствие отсутствия API изменения внутреннего состояния, передача их в качестве параметра будет аналогична простым типам.
  • При переопределении метода можно задать тип результата как подкласс результата у базового метода (в суперклассе), но не наоборот!
  • При переопределении метода он:
    a) может вызвать «более узкое» исключение (подкласс);
    б) не может вызвать более широкое исключение (суперкласс);
    в) может вообще не вызывать исключений.


Коллекции


Главное из того что нужно знать: синтаксис, иерархия классов и интерфейсов коллекций, работа с элементами, поиск/сортировка, работа методов equals() и hashCode(). Основные коллекции, которые встретились мне на экзамене: интерфейсы Set, Map, List, Deque, SortedMap, классы ArrayList, CopyOnWriteArrayList, ArrayDeque, TreeMap, TreeSet, HashMap, HashSet. Для поиска элемента в коллекции Java использует методы equals() и hashCode(). Для нового класса, по умолчанию, метод equals() выполняет сравнение объектов (т.е. указателей на них, т.к. наследуется от Object), поэтому если вы используете собственный класс элемента в коллекции, то необходимо переопределить этот метод, чтобы Java могла найти вставленный ранее элемент. Метод hashCode() необходим Java для поиска необходимого бакета c элементами (для хеш-коллекций). Соответственно, если метод не определен и бакет не найден, то и элемент не найден. Так же желательно знать всевозможные варианты синтаксиса создания коллекций:
// Нельзя:
List<> m = new ArrayList();  // error!
List<> m = new ArrayList<String>();  // error!
List<?> m = new ArrayList<?>(); // error!
List<String> m = new ArrayList<?>(); // error!
// Можно:
HashMap m = new LinkedHashMap<>();
HashMap<String, String> m = new LinkedHashMap();
HashMap<String, String> m = new LinkedHashMap<>();
HashMap<String, ?> m = new LinkedHashMap();
HashMap<?, ?> m = new LinkedHashMap<String, String>();


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


  • Set — Интерфейс. Все реализации выполняют хранение набора объектов. Дубликаты запрещаются, но в случае добавления дубликата — исключение не вызывается (если вставляется дубликат, метод add() возвращает false). Реализации:
    • HashSet — Неупорядоченный набор. Разрешает вставлять null. Нет поддержки многопоточности.
    • TreeSet — Упорядоченй HashSet. Нет поддержки многопоточности.
  • Map — Интерфейс, реализации выполняют хранение пары <key,value>. Все реализации запрещают дубликаты:
    • HashMap — Реализует хранение элементов в виде хеш-таблицы. Перезаписывает дубликаты новыми значениями. Метод put() возвращает null, если нет ключа key, и прежнее значение value, если ключ уже есть). Разрешает null для key и value, в случае дублирования key — замещает его новым value без вызова исключений. Нет поддержки многопоточности. Порядок обхода не гарантирован.
    • Hashtable — null запрещены для key и value (в этом случае будет NullPointerException в обеих случаях). Потоко-безопасная. В остальном работа аналогична HashMap.
    • TreeMap — аналог HashMap, только упорядоченная, т.е. при добавлении автоматически сортирует элементы по ключу (т.е. при обходе элементы будут отсортированы). Устроена по принципу красно-черного дерева.

  • List — Интерфейс, реализации хранят списки объектов. Разрешает дубликаты и null. Реализации:
    • ArrayList — Динамический массив. Нет поддержки многопоточности.
    • Vector — Динамический массив. Потоко-безопасный.
    • LinkedList — Двусвязный список. Нет поддержки многопоточности. Так же реализует интерфейс Deque.

  • Queue — Реализации этого интерфейса построены по принципу очереди (FIFO) для хранения объектов. Null разрешены. Метод add() помещает в конец очереди.
    • Deque — Реализации объединяют функциональность очереди (FIFO) и стека (LIFO). Метод add() аналогичен методу add() для Queue, помещает в конец очереди. Методы push() и addFirst() равносильны и помещают элемент в начало очереди (на вершину стека).


Дополнительную информацию по сравнению коллекций можно почитать тут:


Поиск и сортировка коллекций. Интерфейсы Comparable и Comparator


  • Это тоже одна из «популярных» тем, на экзамене было ~5 вопросов про поиск/сортировку.
  • Выучите в точности как работают методы Arrays.binarySearch и Collections.binarySearch, проверьте на примерах, т.к. в на мой взгляд документации написано маловато. Что возвращают, какие исключения вызывают, как работают с «сырыми» типами и т.п.
  • Аналогично для методов Collections.sort и Arrays.sort.
  • Интерфейс Comparable — используется для класса, чьи экземпляры объектов можно/нужно сравнивать. Все «обертки» простых типов — Char, Integer и т.д., а так же String — уже реализуют этот интерфейс. Использование:
    • Для элемента коллекции необходимо реализовать метод public int compareTo(T obj);
    • При создании коллекции указать новый класс элемента
      TreeSet<ElementNewClass> myColl = new TreeSet<ElementNewClass>);

  • Интерфейс Comparator — реализуется вне класса, объекты которого будут сравниваться. Основное отличие от Comparable:
    1) можно создать несколько видов (классов) независимых сортировок;
    2) используется если нужно отсортировать объекты чужого класса, в котором сравнение не реализовано.
    • Основной метод интерфейса: public int compare(T obj1, T obj2);
    • Использование — при создании коллекции указать реализацию компаратора:
      new TreeSet<String>(myComparator);

  • Примеры использования:
    • Collections.sort(List<T> arg1, Comparator<? super T> arg2);
    • Arrays.sort(T[] arg1, Comparator<? super T> arg2);

  • Методы sort(), reverse() и binarySearch() класса Collections НЕ работают с Set-ами (при этом исключение не вызывается)


Внутренние классы


  • Outer class — класс верхнего уровня. Файл компилируется с именем Outside.class. Разрешенные модификаторы доступа: [default], public
  • Вложенные классы. Делятся на staic nested classes (статические) и inner classes (все остальные):
    • Static nested class (или interface) — Определен в контексте верхнего класса, статический. Может обращаться к статическим членам внешнего класса. Экземпляры внутреннего и внешнего классов могут создаваться независимо (синтаксис создания: OuterClass.StaticNestedClass obj = new OuterClass.StaticNestedClass();). Файл компилируется с именем: Outside$Inside.class. Разрешенные модификаторы доступа: ВСЕ.
    • Member class — Определен в контексте верхнего класса, не статический. Экземпляр может быть создан только после создания экземпляра внешнего класса. Файл компилируется с именем: Outside$Inside.class. Разрешенные модификаторы доступа: ВСЕ.
    • Local class — Определяется внутри блока кода, и виден только внутри этого блока. Может обращаться только к final-переменным внешнего класса и final-параметрам методов.Файл компилируется с именем: Outside$1$Inside.class. Разрешенный модификатор доступа: только [default].
    • Anonymous class — Аналог local class, только безымянный. Файл компилируется с именем: Outside$1.class


Так же следует помнить:
  • non-static inner класс не может иметь static-методы;
  • non-static inner класс может иметь static-переменные (но только final!);
  • static nested класс может иметь static и non-static переменные;


Работа со строками, регулярные выражения


  • Помните, что объекты String — неизменяемы, StringBuilder — изменяемы, StringBuffer — тоже изменяемы и еще потоко-безопасны.
  • StringBuilder и StringBuffer могут конкатенироваться через '+' только со String, и не могут друг с другом и другими простыми типами (в отличие от String, который может конкатенироваться с простыми типами).
  • Тщательно выучите регулярные выражения, тут часто встречаются подвохи. Например, в строковом литерале для Pattern-a указывают только один обратный слеш ("\s"), что вызывает ошибку компиляции.
  • Помните про «жадность» квантификатора *, к примеру для строки «abcd bla-bla-bla abcd» выражение «a.*cd» будет соответствовать всей строке.
  • Так же не забывайте про, что методы класса Matcher (find и group) начинают поиск следующего совпадения с позиции последней найденной группы, таким образом для «ababa» и выражения «aba» будет найдено только одно совпадение.
  • Методы String.split(), String.replaceAll() и Scanner.useDelimeter() работают с регулярными выражениями.
  • Помните основы форматирования строк: System.out.printf(«Hello java %03d!», 7); Была одна каверзная задачка про порядок параметров printf, рекомендую разобраться: System.out.printf(«1:%3$s 2:%2$s 3:%s»,1,2,3);


Потоки


Еще одна «излюбленная» тема, где нужно быть очень внимательным.
  • Кратко о главном. synchronized method() {… } — блокирует для других потоков текущий и все остальные синхронизированные методы экземпляра объекта (вторую часть легко забыть). A если метод объявлен как static, то блокируются все synchronized-методы класса.
  • И аналогично: syncronized (obj) {… } — блокирует:
    1) все обращения других потоков к блоку внутри syncronized на данном объекте obj;
    2) все другие блоки syncronized с тем-же объектом obj.
  • Встречаются неправильные формы реализации интерфейса Runnable, например private void run() {… }, public int run() {… }, или public void run(Runnable r) {… } и т.п.
  • Нельзя указывать synchrnozed для методов интерфейса и абстрактного класса, а для методов enum — можно.
  • После завершения потока, нельзя его снова запускать, т.е. Thread.start() вызовет IllegalThreadStateException.
  • Помните отличия Runnable от Callable (последний возвращает значение и может вызывать исключение).
  • Нужно знать теорию (определения):
    1) deadlock — «мертвая» блокировка, тут, надеюсь, вопросов нет;
    2) stravation — когда поток не может долго получить доступ к ресурсу из за того, что он долго занят другим потоком (более «жадным» или с более высоким приоритетом);
    3) livelock — возникает в случае, если обменивающиеся или ожидающие друг друга потоки слишком заняты, чтобы эффективно продолжать работу. Т.е. они периодически освобождают друг для друга ресурсы на слишком короткое время, по аналогии как 2 человека в коридоре не могут разминуться, синхронно шагая то вправо, то влево;
    4) race condition — ошибка проектирования многопоточной системы, при которой результат выполнения зависит от порядка выполнения потоков (т.е. непредсказуем).


Обёрточные типы и методы с переменным числом аргументов


  • Widening — это автоматическое расширение простого Java типа к более широкому, например byte -> int. Либо расширение к более общему классу, например Byte -> Object.
  • Boxing — это автоматическое «оборачивание» примитивного типа Java в соответствующий ему объектный тип, например: long -> Long. Соответственно Long называется обёрточным (wrapper) типом для long.
  • Unboxing — обратная операция: Long -> long

Существуют следующие правила для компилятора:
  • Невозможен widening от одного оберточного типа к другому (например Byte -> Int) (IS-A fails);
  • Запрещен widening, затем boxing (int не может стать Long);
  • Но разрешается boxing, затем widening (int -> Integer -> Object);
  • Методы с переменным числом аргументов можно сочетать с widening и boxing;
  • В этом случае приоритет у компилятора будет следующий (по убыванию приоритета): widening, boxing, var-args;
  • По отдельности boxing и var-args так же могут использоваться при перегрузке методов.


Нововведения 7 версии


  • Fork-Join Framework. Нужно знать минимальные базовые принципы построения кода с его использованием. Встретилась пара вопросов с интерфейсами RecursiveTask/RecursiveAction (был аналог примера с рядом Фибоначчи из документации) и один теоретический вопрос на тему как разбить задачу на две.
  • NIO2. Тут нужно хорошо знать следующее:
    — маску поиска файлов PathMatcher («glob:*.jpg») и принцип обхода дерева файлов (Files.walkFileTree);
    — класс Path (особенно методы getName, subpath, relativize);
    — класс Files (методы copy, move + StandardCopyOption, newBufferedReader, newBufferedWriter, setAttribute, getAttribute);
    — класс WatchService.
  • И обязательно разберитесь со всеми нюансами интерфейса AutoCloseable и его использование в «try-with-resource» (полагаю достаточно знать: где и когда объект, объявленный внутри скобок try() доступен, когда освобождается, что будет если произойдет исключение и как его отделить от остальных вероятных исключений).


Разное


  • Выражение x instanceof Y — не скомпилируется, если x и Y принадлежат разной иерархии классов.
  • Но x instanceof SomeInterface компилируется. Вообще на экзамене нет разницы между «is-a» для классов и для интерфейсов, т.е. «like-a» приравнивается к «is-a».
  • И еще instanceof не работает с обобщениями.
  • Помните определения:
    coupling — связанность. Это степень, в которой каждый программный модуль использует другие модули.
    cohesion — связность. Это степень внутренней взаимосвязи между частями одного модуля. Или еще можно определить как меру сфокусированности класса на своих прямых задачах.
  • Почему-то встречается очень много вопросов на тему паттернов проектирования (может кто расскажет что в них такого необычайного?). Честно признаюсь, у меня здесь остались некоторые пробелы, т.к. нигде толком не нашел внятных материалов (википедия как то хромает). Вот тут более-менее написано про DAO. И вообще, считаю хороший программист должен сам придумывать паттерны, пригодные конкретно для его проекта, а не следовать везде подобным «рекомендациям». Короче singleton, DAO и factory на экзамене лучше знать на зубок.
  • Нужно помнить основные аргументы утилит командной строки javac (-cp, -d, -source), java (-ea, -da, -cp) и утилиты jar.
  • Что не встречалось на экзамене:
    • Вопросов про garbage collection. Хотя во всех вышеперечисленных тестах его очень «любили».
    • Не было кода с использованием всевозможных Executors. Был только один вопрос по теории.
    • Thread Pools. Вообще не было.
    • Кода с использованием подклассов RowSet. Только теория.

    Понятно, что если мне не попалась какая-то тема, это не означает что её в вопросах нет. Но может кому-то упростит и без того ёмкую подготовку. Подозреваю что Oracle периодически обновляет набор вопросов, чтобы подобные посты не делали жизнь слаще. Так что дерзайте пока свежее :)


Заключение


Конечно, осталось много материала про который я не написал (перечисления, работа с файлами, JDBC, Localizarion, ResourceBundle и вероятно что-то еще). По этим темам предлагаю разобраться самостоятельно, т.к. особых сложностей на мой взгляд они не представляют. И конечно всем удачи на экзамене!
Теги:
Хабы:
+18
Комментарии 23
Комментарии Комментарии 23

Публикации

Истории

Работа

Java разработчик
356 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн