...

среда, 21 августа 2013 г.

[Из песочницы] Подготовка к экзамену Oracle Java SE 7 Programmer II (1Z0-804)



Приветствую уважаемых хабражителей и 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 при реализации интерфейса, или неверный тип исключения при переопределении метода, или отсутствие доступа.

  • Встречались несколько вопросов, где присутствуют одинаковые названия переменных или методов. Самый простой случай — shadowing переменной у наследника, и использование переменной исходного класса и наследника в исходном и переопределяемом методе соответственно. Так же есть вопросы, где класс реализует два разных интерфейса с одноименными методами и переменными. И аналогичные примеры с одинаковыми названиями во вложенных классах. Проверяйте это всё самостоятельно на «живом коде», подмешивая в них различные this, super, ((ClassName)obj).var, разные исключения в одноименные методы и т.п.

  • Из статического контекста нельзя обращаться к нестатическому, в т.ч. запрещены обращения к this и super.


Исключения





  • Очень важно помнить базовую иерархию исключений. На нижеприведенной диаграмме красным отмечены т.н. 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 и т.п.) и для String. Это означает, что изменение значения параметра внутри процедуры не затронет переданную извне переменную. Для всех остальных — работает передача по ссылке (классов, массивов и др.). Т.е изменения внутри процедуры сохраняются в переданном извне объекте. Явно задать как передавать параметр — по ссылке или по значению — нельзя.

  • При переопределении метода можно задать тип результата как подкласс результата у базового метода (в суперклассе), но не наоборот!

  • При переопределении метода он:

    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 — уже реализуют этот интерфейс. Использование:

  • Интерфейс Comparator — реализуется вне класса, объекты которого будут сравниваться. Основное отличие от Comparable:

    1) можно создать несколько видов (классов) независимых сортировок;

    2) используется если нужно отсортировать объекты чужого класса, в котором сравнение не реализовано.

  • Примеры использования:




    • 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

  • 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

  • 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.

  • И обязательно разберитесь со всеми нюансами интерфейса Closeable и его использование в «try-with-resource»


Разное





  • Выражение x instanceof Y — не скомпилируется, если x и Y принадлежат разной иерархии классов.

  • Но x instanceof SomeInterface компилируется. Даже встретился вопрос, в котором объявлялся пустой интерфейс I1 и независимый класс C2, между которыми было соотношение C2 is-a I1.

  • И еще instanceof не работает с обобщениями.

  • Помните определения:

    coupling — связанность. Это степень, в которой каждый программный модуль использует другие модули.

    cohesion — связность. Это степень внутренней взаимосвязи между частями одного модуля. Или еще можно определить как меру сфокусированности класса на своих прямых задачах.

  • Почему-то встречается очень много вопросов на тему паттернов проектирования (может кто расскажет что в них такого необычайного?). Честно признаюсь, у меня здесь остались некоторые пробелы, т.к. нигде толком не нашел внятных материалов (википедия как то хромает). Вот тут более-менее написано про DAO. И вообще, считаю хороший программист должен сам придумывать паттерны, пригодные конкретно для его проекта, а не следовать везде подобным «рекомендациям». Короче singleton, DAO и factory на экзамене лучше знать на зубок.

  • Нужно помнить основные аргументы утилит командной строки javac (-cp, -d, -source), java (-ea, -da, -cp) и утилиты jar.

  • Что не встречалось на экзамене:


    • Не было кода с использованием всевозможных Executors. Был только один вопрос по теории.

    • Thread Pools. Вообще не было.

    • Кода с использованием подклассов RowSet. Только теория.




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


Заключение




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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий