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

Что такое инкапсуляция?
В общем случае в разных языках программирования термин «инкапсуляция» относится к одной или обеим одновременно следующим нотациям:
– механизм языка, позволяющий ограничить доступ одних компонентов программы к другим;
– языковая конструкция, позволяющая связать данные с методами, предназначенными для обработки этих данных.
ссылка 1

Какие типы ссылок существуют в Java?
Strong reference
Weak Reference
Soft Reference
Phantom Reference
Soft-ссылка, в отличие от weak-ссылки, сможет отложить процесс удаления обьекта до тех пор, пока не появится острая нехватка памяти. Учитывая это отличие soft-ссылки от weak, первая больше подходит для кэшей, а weak для метаданных.
ссылка 1
ссылка 2

Как GarbageCollector очищает ссылки, которые ссылаются друг на друга?
Java GC рассматривает объекты “мусор”, если они недоступны через цепочку, начинающуюся с корня коллекции мусора, поэтому эти объекты будут собраны. Даже если объекты могут указывать друг на друга, чтобы сформировать цикл, они все еще мусор, если они отрезаны от корня.
ссылка 1

Временная сложность алгоритма:
В информатике временна́я сложность алгоритма определяет время работы, используемое алгоритмом, как функции от длины строки, представляющей входные данные. Она обычно выражается с использованием нотации «O» большое.
«O» большое и «o» малое — математические обозначения для сравнения асимптотического поведения (асимптотики) функций.
Линейная сложность подразумевает рост времени выполнения алгоритма пропорционально размеру входных данных. (пример: обход массива)
Константная временная сложность не зависит от размера входных данных. (пример: доступ по индексу к определенному элементу массива)
ссылка 1
ссылка 2

Методы сортировки:
Методы сортировки делятся на две группы: устойчивые и неустойчивые. К устойчивым сортировкам принадлежат:
– Пузырьковая Оn2
– Слиянием O(n logn)
– Вставкой On2
– С помощью двоичного дерева O(n logn)
К неустойчивым:
– Сортировка выбором On2
– Сортировка расчёской On2
– Сортировка Шелла On4/3
– Пирамидальная сортировка O(n logn)
– Быстрая (или Хоара) O(n logn)
ссылка 1

Какая сортировка используются в джаве?
java.util.Arrays.sort(prim[], …) — для сортировки массивов примитивов prim (int, byte, char, и т.д.). Использует алгоритм быстрой сортировки (quick sort).
java.util.Arrays.sort(T[], …) и java.util.Arrays.sort(Object[], …) — для сортировки массивов комплексных объектов произвольного типа T или Object. Использует алгоритм сортировки слиянием (merge sort).
java.util.Collections.sort() — для сортировки коллекций. Использует алгоритм сортировки слиянием (merge sort).
Начиная с 8-ки сортировка слиянием объявлена устаревшей и заменена на TimSort
ссылка 1
ссылка 2
ссылка 3

Всегда ли хеш код String кешируется и вычсляется только один раз?
Не всегда. Если в результате подсчета хеш кода впервые получается 0, то для этого значения хеш код будет пересчитан в следующий раз. (см. реализацию метода hashCode в String)

Какой размер boolean в Java и почему именно такой?
Минимальный размер этого типа 1 байт. Сделано это для удобства размещения значения в памяти и работе с ним. Например, в могопоточной среде.

Why does 128==128 return false but 127==127 return true when converting to Integer wrappers?
To save on memory, Java ‘reuses’ all the wrapper objects whose values fall in the following ranges:
– All Boolean values (true and false)
– All Byte values
– All Character values from \u0000 to \u007f (i.e. 0 to 127 in decimal)
– All Short and Integer values from -128 to 127.
ссылка 1

TreeMap vs Hashmap:
HashMap makes absolutely no guarantees about the iteration order. It can (and will) even change completely when new elements are added.
TreeMap will iterate according to the “natural ordering” of the keys according to their compareTo() method (or an externally supplied Comparator). Additionally, it implements the SortedMap interface, which contains methods that depend on this sort order.
LinkedHashMap will iterate in the order in which the entries were put into the map
ссылка 1
ссылка 2

Для чего нужнен loadFactor в HashMap?
float loadFactor – коэффициент заполнения HashMap. Равен отношению числа хранимых элементов в таблице к её размеру. Является мерой заполнения таблицы элементами, при превышении которой происходит автоматической перехеширование.
Другими словами, когда кол-во элементов в бакете начинает превышать (по умолчанию 75%) от всего количества элементов в таблице, HashMap производит автоматическую оптимизацию. А именно: добавляет еще один бакет и производит перехеширование (переразмещение) всех элементов таблицы. Поэтому, это очень плохо сказывается в том случае, если hashCode ключа всегда постоянный. HashMap не только вырождается в список, но еще присутствует постоянный рост бакетов и перехеширования элементов при добавлении нового.
ссылка 1
ссылка 2

Откуда взялся метод getLast в LinkedList? Ведь в List его нет.
LinkedList имплементирует сразу два интерфейса List и Deque. Другими словами, LinkedList – это и список, и очередь, и двухсторонняя очередь одновременно. Метод getLast пришёл как-раз из Deque.

Что такое Natural ordering?
The natural ordering of two objects a and b is the outcome of a.compareTo(b), i.e. what those objects themselves think which one is smaller, greater or equal to the other object. Normally a.compareTo(a) == 0 and if a.compareTo(b) <> 0 then b.compareTo(a) >< 0. The entire thing melts away when you think of integer numbers, e.g. 3 < 4, so 4 > 3 and 3 == 3
natural ordering ты сам реализуешь в Comparable. Посмотри, Collections.sort. Там два метода: один реализует natural ordering класса, второй – ты создаешь компаратор. А какой из них ты будешь использовать – твое личное дело.
ссылка 1
ссылка 2

Что такое красно-черное дерево?
Красно-чёрное дерево — это одно из самобалансирующихся двоичных деревьев поиска, гарантирующих логарифмический рост высоты дерева от числа узлов и быстро выполняющее основные операции дерева поиска: добавление, удаление и поиск узла.
ссылка 1
ссылка 2

Что такое самобалансирующееся двоичное дерево поиска?
Двоичное дерево поиска — это двоичное дерево, для которого выполняются следующие дополнительные условия (свойства дерева поиска):
1. Оба поддерева — левое и правое — являются двоичными деревьями поиска.
2. У всех узлов левого поддерева произвольного узла X значения ключей данных меньше, нежели значение ключа данных самого узла X.
3. У всех узлов правого поддерева произвольного узла X значения ключей данных больше либо равны, нежели значение ключа данных самого узла X.
Очевидно, данные в каждом узле должны обладать ключами, на которых определена операция сравнения меньше.
ссылка 1

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

Что такое сбалансированное дерево?
В этой главе будет рассматриваться другая разновидность бинарных поисковых деревьев – AVL-деревья или сбалансированные двоичные деревья с минимальным временем поиска по дереву. Это свойство AVL-деревьев обеспечивается их сбалансированностью, которая одновременно усложняет алгоритмы для вставки узлов в дерево и их последующего удаления.
ссылка 1

Java 8:
Java 8 (codename: Spider) was released on March 18, 2014, and included some features that were planned for Java 7 but later deferred.
Work on features was organized in terms of JDK Enhancement Proposals (JEPs).
JSR 335, JEP 126: Language-level support for lambda expressions (officially, lambda expressions; unofficially, closures) under Project Lambda and default methods (virtual extension methods) which allow the addition of methods to interfaces without breaking existing implementations. There was an ongoing debate in the Java community on whether to add support for lambda expressions. Sun later declared that lambda expressions would be included in Java and asked for community input to refine the feature. Supporting lambda expressions also allows the performance of functional-style operations on streams of elements, such as MapReduce-inspired transformations on collections. Default methods allow an author of an API to add new methods to an interface without breaking the old code using it. Although it was not their primary intent, default methods also allow multiple inheritance of behavior (but not state).
JSR 223, JEP 174: Project Nashorn, a JavaScript runtime which allows developers to embed JavaScript code within applications
JSR 308, JEP 104: Annotation on Java Types
Unsigned Integer Arithmetic
JSR 337, JEP 120: Repeating annotations
JSR 310, JEP 150: Date and Time API
JEP 178: Statically-linked JNI libraries
JEP 153: Launch JavaFX applications (direct launching of JavaFX application JARs)
JEP 122: Remove the permanent generation
Java 8 is not supported on Windows XP but as of JDK 8 update 25, it can still be installed and run under Windows XP. Previous updates of JDK 8 could be run under XP, but had to be installed after a forced installation by directly unzipping files from the installation executable.
From October 2014, Java 8 was the default version to download from the official website. “Oracle will not post further updates of Java SE 8 to its public download sites for commercial use after September 2018”.
ссылка 1
ссылка 2
ссылка 3

Допустим есть выражение IntStream.of(1, 7, 9).filter(x -> x < 5).map(x -> x + 1).limit(2).forEach(System.out::print). Какой метод итерирует коллекцию?
У стримов есть некоторые особенности. Одна из них: обработка не начнётся до тех пор, пока не будет вызван терминальный оператор. В данном случае это forEach.
ссылка 1
ссылка 2

Чем интерфейсы в Java 8 отличаются от абстрактых классов?
Во-первых, интерфейсы по-прежнему не могут хранить состояние, в отличие от тех же абстрактных классов. Хотя теперь, они напротив могут задавать некое дефолтное поведение. Во-вторых, ограничение на кол-во прямых родителей / наследников у классов как было единицей, так и осталось. Но вот интерфейсов то мы всегда могли имплементить сколь угодно много. И вот он – самый главный чит восьмерки – теперь у нас есть возможность практически полноценного множественного наследования!
ссылка 1
ссылка 2

Что будет если класс имплементирует два интерфейса с одним и тем же методом?
Если это не дефолтный метод – то ничего страшного. Метод будет импелементирован в классе и полиморфно будет вызываться на каждом из интерфейсов.

Что такое Execution Service?
До Java 5 для организации работы с несколькими потоками приходилось использовать сторонние имплеменации пулинга или писать свой. С появлением ExecutorService такая необходимость отпала.
ExecutorService исполняет асинхронный код в одном или нескольких потоках. Создание инстанса ExecutorService’а делается либо вручную через конкретные имплементации (ScheduledThreadPoolExecutor или ThreadPoolExecutor), но проще будет использовать фабрики класса Executors. Например, если надо создать пул с 2мя потоками, то делается это так:
ExecutorService service = Executors.newFixedThreadPool(2);
Если требуется использовать кэширующий пул потоков, который создает потоки по мере необходимости, но переиспользует неактивные потоки (и подчищает потоки, которые были неактивные некоторое время), то это задается следующим образом:
ExecutorService service = Executors.newCachedThreadPool();
ссылка 1
ссылка 2
ссылка 3

Как остановить поток в джаве?
Все потоки нужно прерывать при помощи метода interrupt().
В самом потоке, который возможно будет прерван – нужно устанавливать проверки isInterrupted() во всех ключевых точках (где это необходимо) и обрабатывать соответственно.
Вообще в java doc не рекомендуют пользоваться deprecated методами (такими как Thread.stop()). Метод Thread.stop() убивает поток не обрабатывая и самое главное –
поток может быть «убит» во время выполнения операции, обрыв которой на полуслове оставит некоторый объект в неправильном состоянии, что приведет к появлению трудноотлавливаемой и случайным образом возникающей ошибке
ссылка 1
ссылка 2

Что такое ForkJoinPool в Java?
Благодаря ForkJoinPool можно в небольшом количестве потоков выполнить существенно большее число задач. Это достигается путём так называемого work-stealing’а, когда спящая задача на самом деле не спит, а выполняет другие задачи. Можно выделить две фазы работы с ForkJoin. Сначала форкаем подзадачи, а затем их джойним. Между этими двумя фазами и происходит работа над выполнением всех подзадач.
ссылка 1

Что такое атомарные операции и может ли volatile обеспечить атомарность?
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
ссылка 1
HOLLYWAR DETECTED:
ссылка 2

++long атомарна или нет?
В Java атомарными являются операции чтения/записи всех примитивных типов данных за исключением типов long и double, поскольку эти типы данных занимают два машинных слова, и операции чтения/записи являются составными операциями из двух атомарных операций над старшими и младшими битами числа соответственно.
ссылка 1
ссылка 2

Что такое семафор?
Семафоры представляют еще одно средство синхронизации для доступа к ресурсу. В Java семафоры представлены классом Semaphore из пакета java.util.concurrent.
Для управления доступом к ресурсу семафор использует счетчик, представляющий количество разрешений. Если значение счетчика больше нуля, то поток получает доступ к ресурсу, при этом счетчик уменьшается на единицу. После окончания работы с ресурсом поток освобождает семафор, и счетчик увеличивается на единицу. Если же счетчик равен нулю, то поток блокируется и ждет, пока не получит разрешение от семафора.
ссылка 1

Что такое Long pooling?
Выглядит это примерно следующим образом:
1) Клиент отсылает на сервер обычный ajax-запрос
2) Сервер, вместо того, чтобы быстро обработать этот запрос и отправить ответ клиенту, запускает цикл, в каждой итерации которого следит за возникновением событий (другой клиент добавил запись или удалил).
3) При возникновении события сервер генерирует ответ и отсылает его клиенту, таким образом завершая запрос.
4) Клиент, получив ответ от сервера, запускает обработчик события и параллельно отправляет очередной «длинный» запрос серверу.
ссылка 1
ссылка 2

Метод транзакции в спринге:
При декларативном способе описания транзакционного метода в спринге используется аспектная аннотация.
При программном способе описания – используется особый бин который отвечает за транзакцию.
ссылка 1
ссылка 2
ссылка 3

В чем практический смысл Spring aliasing?
Два бина с одним именем в spring создать нельзя. Однако можно перекрыть первый бин вторым при помощи alias. К примеру, у тебя есть две базы данных. Одна тестовая, а другая промышленная. Тебе не хочется каждый раз при тесте менять название полей в бинах, которые используют базу. Тут на помощь и приходит alias. Плюс при переименованиях бинов alias используется для обратной совместимости. Вещь редко используется, но всё же используется.
ссылка 1

Отличия свойств margin и padding
Создавать промежутки между элементами можно и тем, и другим способом, но если padding – это отступ от содержимого до края блока, то margin – это расстояние от одного блока до другого, межблоковое пространство.
ссылка 1

Расскажите про уровни изолированности в базах данных:
-Read uncommitted (чтение незафиксированных данных)
-Read committed (чтение фиксированных данных)
-Repeatable read (повторяемость чтения)
-Serializable (упорядочиваемость)
ссылка 1
ссылка 2
ссылка 3

Как устроена лямбда внутри?
В Oracle JRE 8 metafactory динамически генерирует Java класс, используя ObjectWeb Asm, который и создает класс-реализацию функционального интерфейса. К созданному классу могут быть добавлены дополнительные поля, если лямбда-выражение захватывает внешние переменные. Этот похоже на анонимные классы Java, но есть следующие отличия:

  • Анонимный класс генерируется компилятором Java.
  • Класс для реализации лямбда-выражения создается JVM во время выполнения.

ссылка 1

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

Почему пароль предпочтительней хранить в массиве символов а не строке?
ссылка 1

Включаем спринговые логи

Spring как и любой другой сложный фреймворк умеет сам себя логировать. Иногда нужно уметь смотреть что там внутри происходит. Поэтому давайте попробуем разобраться, как можно заставить его говорить больше, чем он умеет по умолчанию.
Поставляется Spring со встроенным логгером JCL. По сути, это ранний аналог sf4j. Не сам логгер, а обёртка над различными имплементациями. Отсюда наша задача сводится к настройке JCL и стандартного Java логгера. Вполне, думаю, возможно подключить и другие реализации, но это всё зависит от конкретной задачи. Наша же заключается в том, чтобы Spring куда нибудь вывел дебажную информацию о себе. Поэтому Java логгера будет достаточно.
Итак. Задача сводится к следующим трём пунктам:
1) Нам нужно сказать JCL о том, чтобы он подключил Java логгер
2) Настроить Java логгер
3) Получить логи
Давайте теперь постараемся всё это реализовать:
1) Реализуется очень просто. Нужно всего лишь положить файл commons-logging.properties в корень нашего класспаса. Содержимое его должно быть:

Но, если копнуть глубже, и посмотреть внутрь JCL, то мы увидим, что это лишне, поскольку он сам по умолчанию использует Java логгер.
2) Создадим теперь файл для настроек Java логгера. Положите его в любое место и добавьте следующее содержимое:

Обратите внимание на строку 7. С её помощью можно получать логи из того пакета, который именно мы захотим.
3) Теперь чтобы это всё заработало нужно Java логгеру сказать, чтобы он настроился из того файла, который мы только что создали. Это можно сделать через системную переменную:

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

Вот вроде и всё. С таким подходом можно получить логи любого фреймворка, который юзает JCL по умолчанию.

Полезная ссылка

Операторы Inner Join и Outer (left, right, full) Join в SQL (Oracle)

Ключевое слово join в SQL используется при построении select выражений. Инструкция Join позволяет объединить колонки из нескольких таблиц в одну. Объединение происходит временное и целостность таблиц не нарушается. Существует три типа join-выражений:

  • inner join;
  • outer join;
  • cross join;

В свою очередь, outer join может быть left, right и full (слово outer обычно опускается).

В качестве примера (DBMS Oracle) создадим две простые таблицы и сконструируем для них SQL-выражения с использованием join.

В первой таблице будет хранится ID пользователя и его nick-name, а во второй – ID ресурса, имя ресурса и ID пользователя, который может этот ресурс администрировать.

Содержимое таблиц пусть будет таким:

Конструкция join выглядит так:

Где join_type – тип join-выражения, table_name – имя таблицы, которая присоединяется к результату, condition – условие объединения таблиц.

Кострукция join располагается сразу после select-выражения. Можно использовать несколько таких конструкций подряд для объединения соответствующего кол-ва таблиц. Логичнее всего использовать join в том случае, когда таблица имеет внешний ключ (foreign key).

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

Результат будет таким:

В случае с left join из главной таблицы будут выбраны все записи, даже если в присоединяемой таблице нет совпадений, то есть условие condition не учитывает присоединяемую (правую) таблицу. Пример:

Результат выполнения запроса:

Результат показывает все ресурсы и их администраторов, вне зависимотсти от того есть они или нет.

Right join отображает все строки удовлетворяющие правой части условия condition, даже если они не имеют соответствия в главной (левой) таблице:

А результат будет следующим:

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

Full outer join (ключевое слово outer можно опустить) необходим для отображения всех возможных комбинаций строк из нескольких таблиц. Иными словами, это объединение результатов left и right join.

А результат будет таким:

Некоторые СУБД не поддерживают такую функциональность (например, MySQL), в таких случаях обычно используют объединение двух запросов:

Наконец, cross join. Этот тип join еще называют декартовым произведением (на английском – cartesian product). Настоятельно рекомендую использовать его с умом, так как время выполнения запроса с увеличением числа таблиц и строк в них растет нелинейно. Вот пример запроса, который аналогичен cross join:

Конструкция Join (в сочетании с другими SQL конструкциями, например, group by) часто встречается при программировании под базы данных. Думаю, эта статья будет вам полезна.

Кстати, для проверки своих знаний в области баз данных (и в частности Oracle) рекомендую воспользоваться этим сайтом онлайн тестирования – Тесты по базам данных.

Источник

Использование ThreadLocal переменных

Введение

Вы уже наверно знаете, что поля классов в java бывают статические и не статические. Любое поле класса без модификатора static принадлежит объекту данного класса и создается каждый раз когда создается новый экземпляр класса. Статические переменные(помеченные модификатором static) не принадлежат экземпляру класса и существует всегда в единственном экземпляре независимо от того, сколько экземпляров класса было создано. Появившийся в java 1.2 класс java.lang.ThreadLocal по сути предоставляет нам ещё одну область жизни объектов, ThreadLocal предоставляет абстракцию над переменными локальными по отношению к потоку испольнения java.lang.Thread. ThreadLocal переменные отличаются от обычных переменных тем, что у каждого потока свой собственный, индивидуально инициализируемый экземпляр переменной, доступ к которой он получает через методы get() или set().

Я в своей практике встречался с четырьмя основными целями применения ThreadLocal переменных:
1. Упрощение API.
2. Cинтаксический сахар.
3. Кеширование непотокобезопасных(non thread safe) ресурсов.
4. Уменьшение области конкуренции между потоками(lock striping).

Упрощение API с помощью ThreadLocal.

Допустим Вы разрабатываете JEE веб приложение, после прохождения аутентификации на странице логина информация о пользователе запоминается в http сессии, и Вам в любой точке кода может понадобится информация о пользователе от которого пришел http запрос.
Наверняка Вы не захотите всю логику помещать в сервлеты и JSP, Вы выделети в приложение несколько слоев(бизнесс логика, доступ к данным и.т.д.), но после распределния ответсвенности по слоям у Вас может возникнуть проблема с тем, что не в каждой точке кода будет доступ к Http сессии, соответсвенно не везде можно будет узнать от какого пользователя пришел запрос.
Встает вопрос? а как проектировать свой API? Добавлять в каждый метод каждого класса дополнительный параметр представляющий данные пользователе?

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

Итак начнем с класса для реализации потокобезопсного контеста пользователя:

Как мы знаем сервера приложений могут выполнять тысячи запросов одновременно и на первый взгляд такой код не потоко безопасен. Ведь действительно мы запоминаем текущего пользователя в статической переменной, а статическая переменная одна на весь класс, и вроде бы как паралельные http запросы должны перетирать данные друг друга.
Но вся фишка ThreadLocal заключается в том что имея всего одну ThreadLocal переменную, мы можем иметь различное значение для каждого из потоков, то есть один поток никогда не прочтет, удалит или не перезатрет данные присвоенные другим потоком. Таким образом несмотря на разделяемую статическую переменную код выше потоко-безопасен.Хранилище аутентификацционных данных написано, теперь нужно написать и сконфигурировать в web.xml слушателя входящих HTTP запросов, который бы при поступлении запроса присоеденяя данные о пользователе к потоку обработки а по завершении обработки запроса, очищал бы эту информацию.

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

Конечно же функционал реализованный выше редко когда придется писать самому, есть множество библиотек связанных с security в которых это уже реализовано, например spring-security, стоит только иметь в виду что все они будут точно также работать посредством ThreadLocal. Построение API вокруг ThreadLocal широко используется в java enterprise edition и используется не только для ассоциирования контекста безопасности с потоком, но и для других вещей как например транзакции, открытые JPA сессии. Так же хочу заметить что использование ThreadLocal в JEE окружении сопряжено с возникновением многих проблемам и без глубокого понимания платформы jee, многопоточности и механизма загрузки классов от использования ThreadLocal в JEE лучше отказаться. Проблемы порождаемые ThreadLocal переменными в JEE окружении описаны в конце статьи.

2. Cинтаксический сахар или программирование на языке.

ThreadLocal можно использовть для добавления синтаксического сахара в язык java и многие библиотеки этим пользуются, например mybatis:

Код получился легко читаемым, как видно функции BEGIN, SELECT и.т.д. не вызываются ни на одном объекте, то есть они статические и за счет статического импорта появившегося в java 5, вызов таких функций можно осуществлять без префикса класса. Потоко-безопасность достигается за счет того что каждый поток выполнения имеет собственный экземпляр билдера запросов. Конечно следует ожидать, что c появлением лямбд в java 8, использование ThreadLocal в качестве синтаксического сахара потеряет свою актуальность

Кеширование непотокобезопасных(non thread safe) ресурсов.

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

Класс использовался как кастомный адаптер для даты в JAXB. Вроде бы простой маленьки класс и в нём негде ошибится, однако есть одно но, класс java.text.SimpleDateFormat не является потоко безопасным, параллельные потоки должны либо синхронизировать доступ к инстансу объекта данного класса, либо отказаться от разделения одного инстанса SimpleDateFormat.
То есть просто создать один экземпляр формата и запомнить в статической переменной нельзя, иначе мы получим мусор на выходе если форматировать даты паралельно из нескольких потоков. Честно говоря в приложении рассчитаном на входящий поток данных 5 тысяч входящих документов в секунду, ни генерировать мусор создавая каждый раз новый экземпляр формата, ни тем более создавать бутылочное горлышко в виде synchronized блоков мне не хотелось, и поскольку стояло жесткое требование по максимуму отказаться от библиотек не входящих в j2se, то есть нельзя было использовать сторонние реализации форматеров то код выше превратился в следующее:

Как видно обеспечено кеширование объектов DateFormat без синхронизации. В статье Java Best Practices – DateFormat in a Multithreading Environmentприведены результаты бенчмарков показывающих что такой подход позволяет увеличить производительность парсинга дат до 8 раз по сравнению с созданием каждый раз нового экземпляра формата.

Сужение области конкуренции между потоками(lock striping).

Lock striping техника представления сложного объекта, к которому осуществляется конкуретный доступ в виде отдельных маленьких частей, каждую часть такого объекта можно менять без блокировки целого объекта. Например техника lock striping применена в CuncurrentHashMap – вся коллекция разбита на регионы, и треды при модификации не конкурируют за всю коллекцию целиком, они конкурируют за её отдельные регионы, таким образом острота конкуренции снижается.

Прежде всего для этого параграфа хотелось бы сразу поместить disclaimer и cсылку на эту презентацию
java8 новинки в java.util.concurrent, в презентации авторитетные специалисты в области java оптимизации в квалификации которых не возникает ни каких сомнений, крайне не рекомендуют использовать ThreadLocal для сuncurrent оптимизаций.

Однако пока java8 ещё не зарелизена, а в продакшн энтерпрайз приложений java8 попадет вообще не скоро, то пример использования ThreadLocal я всё же опубликую.

И так представим высоконагруженное приложение с тысячами рабочих потоков. Потоки занимаются тем что обрабатывают пачки входящих документов и нам бы хотелось собирать некоторую статистику о работе приложения, а конкретно количество обработанных документов. Но при этом мы бы хотели чтобы сбор статистики обходился нам бесплатно, и не вносил бы лишних нагрузку в приложение.
Какие сть варианты решения:
Выполнять запрос SELECT COUNT(*) FROM TABLE в базу данных – на таких объемах не очень удачное решение.
Заводить AtomicLong – поможет на небольшом количестве потоков, но не на тысячах.

Итак применим ThreadLocal. Общее количество обработанных сервером документов можно представить как сумму обработанных документов каждым рабочим потоком. То есть к каждому треду можно поставить соответсвии число, и он будет увеличивать это число вообще без конкуренции с каким либо другим потоком, то есть как мы и хотели сбор статистики обходится нам бесплатно.

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

Пять секретов… многопоточного Java-программирования

Немногие Java™-разработчики могут позволить себе игнорировать многопоточное программирование и поддерживающие его библиотеки платформы Java, но еще меньше тех, кто располагает временем для глубинного изучения потоков. Вместо этого мы изучаем их спонтанно, добавляя в свой инструментарий новые рекомендации и приемы, когда нуждаемся в них. Это не лучший путь, хотя он и позволяет построить и запустить достойное приложение. Понимание потоковых механизмов компилятора Java и JVM поможет вам написать более эффективный, качественный и быстродействующий код Java.
В этой статье из цикла Пять секретов я расскажу о некоторых тонких аспектах многопоточного программирования с применением синхронизированных методов, volatile-переменных и атомарных классов. В частности, мы обсудим взаимодействие некоторых из этих конструкций с JVM и компилятором Java и влияние различных взаимодействий на производительность Java-приложения.

1. Синхронизированный метод или синхронизированный блок?

Развить навыки по этой теме
Этот материал — часть knowledge path для развития ваших навыков. Смотри Стать Java-программистом
Иногда приходится решать, синхронизировать ли вызов метода целиком или только потокобезопасное подмножество этого метода. В таких ситуациях полезно знать, что когда Java-компилятор преобразует исходный код в байт-код, он по-разному обрабатывает синхронизированные методы и синхронизированные блоки.
Когда JVM выполняет синхронизированный метод, исполняемый поток определяет, что в структуре method_info этого метода установлен флаг ACC_SYNCHRONIZED, а затем автоматически блокирует объект, вызывает метод и разблокирует объект. В случае исключительной ситуации поток автоматически снимает блокировку.
При синхронизации же блока кода метода встроенная в JVM поддержка блокировки объекта и обработки исключений не используется, и эта функциональность должна быть явно прописана в байт-коде. Если вы прочтете байт-код метода с синхронизированным блоком, то увидите более десятка дополнительных операций для управления этой функциональностью. В листинге 1 показаны вызовы для создания синхронизированного метода и синхронизированного блока.
Листинг 1. Два подхода к синхронизации

Метод synchronizedMethodGet() генерирует следующий байт-код:

А вот байт-код метода synchronizedBlockGet():

Для создания синхронизированного блока понадобилось 16 строк байт-кода, тогда как для синхронизации метода достаточно пяти.

2. Переменные ThreadLocal

Когда нужно сохранить один экземпляр переменной для всех экземпляров класса, используются переменные-члены статического класса. Когда же нужно сохранить по экземпляру переменной для каждого потока, следует использовать локальные переменные потока. Переменные ThreadLocal отличаются от обычных переменных тем, что в каждом потоке есть свой собственный индивидуально инициализированный экземпляр переменной, к которому он обращается посредством методов get() или set().
Допустим, вы разрабатываете многопоточный трассировщик кода, цель которого ― уникально идентифицировать путь каждого потока в вашем коде. Задача в том, чтобы скоординировать несколько методов из нескольких классов между несколькими потоками. Без ThreadLocal это было бы трудно сделать. Пришлось бы в начале исполнения потока создавать уникальный маркер для его идентификации в трассировщике, а затем передавать этот маркер каждому методу, встречающемуся по пути.
ThreadLocal все упрощает. В начале исполнения поток инициализирует локальную переменную потока, а затем обращается к ней из каждого метода в каждом классе с уверенностью, что переменная содержит сведения о трассировке только для текущего исполняемого потока. После исполнения поток может передать сведения о своем пути объекту управления, отвечающему за хранение всех путей.
Использование ThreadLocal имеет смысл, когда нужно хранить экземпляры переменных для каждого потока.

3. Volatile-переменные

По моей оценке, примерно половине всех Java-разработчиков известно о наличии в языке Java ключевого слова volatile. Из них лишь около 10% знают, что оно означает, и еще меньше ― как его эффективно использовать. Вкратце, определение переменной с ключевым словом volatile означает, что значение этой переменной может изменяться другими потоками. Чтобы как следует понять, что делает ключевое слово volatile, полезно разобраться, как потоки обрабатывают обычные переменные.
В целях повышения производительности спецификация языка Java допускает сохранение в JRE локальной копии переменной для каждого потока, который на нее ссылается. Такие «локальные» копии переменных напоминают кэш и помогают потоку избежать обращения к главной памяти каждый раз, когда требуется получить значение переменной.
Но давайте посмотрим, что происходит в следующем сценарии: запускаются два потока, и один из них считывает переменную A как 5, а второй ― как 10. Если значение переменной А изменилось с 5 на 10, то первый поток не узнает об изменении и будет хранить неправильное значение A. Однако если переменная А помечена как volatile, то когда бы поток не считывал значение A, он будет обращаться к главной копии A и считывать ее текущее значение.
Локальный кэш потока имеет смысл в том случае, если переменные в ваших приложениях не будут изменяться извне. Если это не так, то знать, что делает ключевое слово volatile, очень полезно.

4. Volatile- или синхронизированные переменные?

Если переменная объявлена как volatile, это означает, что она может изменяться разными потоками. Естественно ожидать, что JRE обеспечит ту или иную форму синхронизации таких volatile-переменных. JRE действительно неявно обеспечивает синхронизацию при доступе к volatile-переменным, но с одной очень большой оговоркой: чтение volatile-переменной и запись в volatile-переменную синхронизированы, а неатомарные операции ― нет.
Это означает, что следующий код не является потокобезопасным:

Предыдущий оператор можно записать и так:

Другими словами, если volatile-переменная обновляется таким образом, что ее значение считывается, изменяется, и ей “под капотом” присваивается новое значение, то результатом будет непотокобезопасная операция, выполняемая между двумя синхронными операциями. Остается решить, использовать ли явную синхронизацию или же полагаться на поддержку автоматической синхронизации volatile-переменных в JRE. Наилучший подход зависит от обстоятельств: если новое значение volatile-переменной зависит от ее текущего значения (как при операции инкремента), то нужна явная синхронизация, чтобы эта операция была потокобезопасной.

5. Атомарные корректоры полей

При увеличении или уменьшении значения примитива в многопоточной среде гораздо выгоднее использовать один из новых атомарных классов из пакета java.util.concurrent.atomic, чем писать свой собственный синхронизированный блок кода. Атомарные классы гарантируют выполнение определенных операций, таких как увеличение и уменьшение, обновление или добавление значения, потокобезопасным способом. В перечень атомарных классов входят классы AtomicInteger, AtomicBoolean, AtomicLong, AtomicIntegerArray и т.п.
Проблема использования атомарных классов состоит в том, что все операции класса, включая get, set и семейство операций get-set, оказываются атомарными. Это означает, что операции read и write, которые не изменяют значения атомарной переменной, ― это не просто важные, а синхронизированные операции read-update-write. Если требуется более детальное управление развертыванием синхронизированного кода, то обходной путь заключается в использовании атомарного корректора полей.
Использование атомарных обновлений
Атомарные корректоры полей, такие как AtomicIntegerFieldUpdater, AtomicLongFieldUpdater и AtomicReferenceFieldUpdater, по сути, представляют собой оболочку volatile-поля. Они используются внутри библиотек Java-классов. Эти корректоры не нашли широкого применения в коде приложений, но избегать их нет никаких причин.
В листинге 2 приведен пример класса, использующего атомарные корректоры для изменения названия книги, которую кто-то читает.
Листинг 2. Класс Book

Класс Book – это простой объект Java (POJO) с единственным полем: name.
Листинг 3. Класса MyObject

Класс MyObject в листинге 3 предоставляет свое свойство whatAmIReading обычным образом, с помощью методов get и set, но метод set делает нечто особенное. Вместо того чтобы просто присвоить свою внутреннюю ссылку Book указанному объекту Book (что достигается с помощью кода, закомментированного в листинге 3), он использует AtomicReferenceFieldUpdater.

AtomicReferenceFieldUpdater

В Javadoc класс AtomicReferenceFieldUpdater определяется следующим образом:
Основанная на отражении (reflection-based) утилита, которая обеспечивает атомарное обновление указанных опорных volatile-полей указанных классов. Этот класс предназначен для использования в атомарных структурах данных, где несколько опорных полей одного и того же узла независимо подлежат атомарному обновлению.
В листинге 3 класс AtomicReferenceFieldUpdater создается с помощью вызова его статического метода newUpdater, принимающего три параметра:
класс объекта, содержащий поле (в данном случае, MyObject);
класс объекта, подлежащий атомарному обновлению (в данном случае, Book);
имя поля, подлежащего атомарному обновлению.
Реальная выгода здесь заключается в том, что метод getWhatImReading выполняется без всякой синхронизации, в то время как setWhatImReading выполняется как атомарная операция.
В листинге 4 показано, как использовать метод setWhatImReading() с гарантией правильного изменения значения.
Листинг 4. Пример атомарного обновления

Подробнее об атомарных классах см. в разделе Ресурсы.

Заключение

Многопоточное программирование всегда сложно, но по мере развития платформы Java некоторые из его задач упрощаются. В этой статье я раскрыл пять секретов создания многопоточных приложений на платформе Java, включая разницу между методами синхронизации и синхронизированными блоками кода, важность использования переменных ThreadLocal, сохраняющихся в каждом потоке, малоизвестное ключевое слово volatile (и опасность опоры на volatile при решении задач синхронизации) и краткий экскурс в тонкости атомарных классов. Подробнее см. в разделе Ресурсы.

Ресурсы

  • Оригинал статьи: 5 things you didn’t know about … multithreaded Java programming.
  • Пять секретов…: цикл статей, содержащих полезные советы по Java-программированию.
  • Java Concurrency in Practice (Brian Goetz, et. al. Addison-Wesley, 2006): удивительная способность Брайана объяснять сложные концепции простым языком делает эту книгу необходимой в библиотеке любого Java-программиста.
  • Code Tracing (Steven Haines, InformIT, август 2010 г.): подробнее о трассировке кода с помощью переменных ThreadLocal.
  • Java bytecode: Understanding bytecode makes you a better programmer(Peter Haggar, developerWorks, июль 2001 г.): введение в малоизвестные области байткода, включая приведенный выше пример, иллюстрирующий разницу между синхронизированными методами и синхронизированными блоками.
  • Java theory and practice: Going atomic (Brian Goetz, developerWorks, ноябрь 2004 г.): о том, как атомарные классы позволяют разрабатывать высокомасштабируемые неблокирующие алгоритмы на языке Java.
  • Java theory and practice: Concurrency made simple (sort of) (Brian Goetz, developerWorks, ноябрь 2002 г.): экскурс в пакет java.util.concurrent.
  • 5 things you didn’t know about … java.util.concurrent, Part 1 (Ted Neward, developerWorks, май 2010 г.): о пяти классах параллельных коллекций, которые модифицируют стандартные классы коллекций для нужд параллельного программирования.

Источник

Как работает ConcurrentHashMap

В октябре на хабре появилась замечательная статья про работу HashMap. Продолжая данную тему, я собираюсь рассказать о реализации java.util.concurrent.ConcurrentHashMap.
Итак, как же появился ConcurrentHashMap, какие у него есть преимущества и как он был реализован.

Предпосылки к созданию ConcurrentHashMap

До появления в JDK 1.5 реализации ConcurrentHashMap, существовало несколько способов описания хэш-таблиц.
Первоначально в JDK 1.0 был клас Hashtable. Hashtable — потокобезопасная и легкая в использовании реализация хэш-таблицы. Проблема HashTable заключалась, в первую очередь, в том, что при доступе к элементам таблицы производилась её полная блокировка. Все методы Hashtable были синхронизированными. Это являлось серьёзным ограничением для многопоточной среды, поскольку плата за блокировку всей таблицы была очень большой.
В JDK 1.2 на помощь Hashtable пришёл HashMap и его потокобезопасное представление — Collections.synchronizedMap. Причин для такого разделения было несколько:

  • Не каждый программист и не каждое решение нуждались в использовании потокобезопасной хэш-таблицы
  • Программисту необходимо было дать выбор, какой вариант ему удобно использовать

Таким образом, c JDK 1.2 список вариантов реализации хэш-карт в Java пополнился ещё двумя способами. Однако эти способы не избавили разработчиков от появления в их коде race conditions, которые могли привести к появлению ConcurrentModificationException. В данной статье подробнее рассказано о возможных причинах их появления в коде.
И вот, в JDK 1.5 наконец появляется более производительный и масштабируемый вариант.

ConcurrentHashMap

К моменту появления ConcurrentHashMap Java-разработчики нуждались в следующей реализации хэш-карты:

  • Потокобезопасность
  • Отсутствие блокировок всей таблицы на время доступа к ней
  • Желательно, чтобы отсутствовали блокировки таблицы при выполнении операции чтения

Doug Lea представляет вариант реализации такой структуры данных, которая включается в JDK 1.5.
Какие же основные идеи реализации ConcurrentHashMap?

1. Элементы карты

В отличие от элементов HashMap, Entry в ConcurrentHashMap объявлены как volatile. Это важная особенность, также связанная с изменениями в JMM. Ответ Doug Lea о необходимости использования volatile и возможных race condition можно прочитать здесь.

2. Хэш-функция

В ConcurrentHashMap также используется улучшенная функция хэширования.
Напомню, какой она была в HashMap из JDK 1.2:

Версия из ConcurrentHashMap JDK 1.5:

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

3. Сегменты

Карта делится на N различных сегментов (16 по умолчанию, максимальное значение может быть 16-битным и представлять собой степень двойки). Каждый сегмент представляет собой потокобезопасную таблицу элементов карты.
Между хэш-кодами ключей и соответствующими им сегментами устанавливается зависимость на основе применения к старшим разрядам хэш-кода битовой маски.
Вот как в карте хранятся элементы:

Рассмотрим, что же представляет из себя класс сегмента:

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

4. ConcurrencyLevel

Данный параметр влияет на использование картой памяти и количество сегментов в карте.
Посмотрим на создание карты и на то, как влияет заданный в качестве парамента конструктора concurrencyLevel:

Количество сегментов будет выбрано как ближайшая степень двойки, большая чем concurrencyLevel. Ёмкость каждого сегмента, соответственно, будет определяться как отношение округлённого до ближайшей большей степени двойки значения ёмкости карты по умолчанию, к полученному количеству сегментов.
Очень важно понимать две следующие вещи. Занижение concurrencyLevel ведёт к тому, что более вероятны блокировки потоками сегментов карты при записи. Завышение показателя ведёт к неэффективному использованию памяти.

Как же выбрать concurrencyLevel?

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

Оценки масштабируемости

На javamex можно найти статью о сравнении масштабируемости synchronizedMap и ConcurrentHashMap:
image
Как видно из графика, между 5 и 10 миллионами операций доступа к карте заметно серьёзное расхождение, что обуславливает эффективность применения ConcurrentHashMap в случае с высоким количеством хранимых данных и операций доступа к ним.

Итого

Итак, основные преимущества и особенности реализации ConcurrentHashMap:

  • Карта имеет схожий с hashmap интерфейс взаимодействия
  • Операции чтения не требуют блокировок и выполняются параллельно
  • Операции записи зачастую также могут выполняться параллельно без блокировок
  • При создании указывается требуемый concurrencyLevel, определяемый по статистике чтения и записи
  • Элементы карты имеют значение value, объявленное как volatile

В заключение хотелось бы сказать, что ConcurrentHashMap должен применяться грамотно, с предварительной оценкой соотношения чтения и записи в карту. Также по-прежнему имеет смысл использовать HashMap в программах, где нет множественного доступа от нескольких потоков к хранимой карте.
Спасибо за ваше внимание!

Полезные ресурсы по параллельным коллекциям в Java и, в частности, по работе ConcurrentHashMap:

  1. www.ibm.com/developerworks/ru/library/j-jtp07233/index.html
  2. stas-blogspot.blogspot.com/2010/08/concurrenthashmap-revealed.html
  3. www.codercorp.com/blog/java/why-concurrenthashmap-is-better-than-hashtable-and-just-as-good-hashmap.html

Источник

Откуда растут ноги у hashCode

Опять на собеседованиях по Java спрашивают про hashCode и equals? А кто из собеседующих сам ответит на вопрос, как вычисляется Object.hashCode() и System.identityHashCode()? Насколько дорог вызов этих методов? Как их можно ускорить в HotSpot JVM? Держу пари, едва ли кто даст правильный ответ. Разве что, кто прочитает эту статью.

Существует распространенное заблуждение, что Object.hashCode возвращает адрес объекта в памяти. Когда-то давно, наверное, так оно и было. Например, Dalvik VM до сих пор использует адрес объекта, сдвинутый на 3 бита вправо. Однако такая реализация неудачна: во-первых, последовательно выделяемые объекты будут иметь последовательные хеш-коды; во-вторых, сборщик мусора может передвигать объекты в памяти, меняя их адреса.

Так получилось, что на прошлой неделе я дважды столкнулся с темой вычисления хеш-кода, что и побудило меня написать заметку. Сначала попался на глаза крайне несправедливо заминусованный комментарий. Именно SSSurkv совершенно верно предположил, что для вычисления Object.hashCode используется генератор случайных чисел.
– Как так? – спросите вы. – Ведь хеш-код объекта должен оставаться постоянным в течение жизни приложения.

Все верно. Встроенный хеш-код генерируется лишь один раз для каждого объекта при первом вызове метода hashCode(), после чего сохраняется в заголовке объекта для последующих вызовов. Но для первого раза используется именно random! Убедитесь сами, заглянув в исходники OpenJDK (функция get_next_hash).

Вероятно, я бы забыл про этот случай, если бы на днях не столкнулся с реальной проблемой в реальном проекте. Профилируя приложение, среди горячих методов я неожиданно увидел IdentityHashMap.put(), который, на мой взгляд, реализован довольно эффективно. Узким местом оказался System.identityHashCode(), на который IdentityHashMap полагается. Причем медленным был только первый вызов identityHashCode на объекте. Второй и последующие вызовы, как мы теперь знаем, берут сохраненное значение из заголовка.

Но нет худа без добра. Дело в том, что в HotSpot можно выбирать реализацию Object.hashCode с помощью ключа командной строки -XX:hashCode=n (где n от 0 до 5).
0 – Park-Miller RNG (по умолчанию)
1 – f(адрес, глобальное_состояние)
2 – константа 1
3 – последовательный счетчик
4 – адрес объекта
5 – Thread-local Xorshift
Наиболее адекватным, на мой взгляд, является последний – он дает неплохое равномерное распределение, используя только битовые операции, и, что важно для конкурентных алгоритмов, не трогает глобальные переменные.
Так, всего лишь добавив ключ JVM -XX:hashCode=5, я магическим образом ускорил свой алгоритм на 30%! Почему этот вариант до сих пор не сделали дефолтным, остается загадкой…

Напоследок забавный факт: хотспотовский hashCode никогда не вернет 0, так как 0 считается признаком того, что хеш-код для данного объекта еще не генерировался:
if (value == 0) value = 0xBAD ;

Надеюсь, теперь, когда вы узнали всю правду о hashCode, вы сможете не только удивить коллег на собеседовании, но и сделать свои алгоритмы еще эффективнее.

Источник

Класс Object в Java

Класс Object, из пакета java.lang, находится на вершине дерева иерархии классов. Каждый класс является потомком, прямым или косвенным, класса Object. Любой использованный или написанный нами класс наследует методы этого класса. Благодаря этому мы можем сослаться на объект, тип которого нам не известен.

Исходный код данного класса из OpenJDK можно посмотреть тут.


Разберемся по порядку с методами этого класса.

Обычно, для того, чтобы JVM нашла нативные функции, они должны быть названы определенным образом. Например для java.lang.Object.registerNatives, соответствующая функция на С должна называться Java_java_lang_Object_registerNatives. Используя JNI функцию registerNatives можно именовать наши С функции как захочется.

Код на С :

Следует заметить, что Object.getClass нет в этом списке. Это значит что он будет вызываться со стандартным именем Java_java_lang_Object_getClass.
Регистрация нативных функций также полезна если мы встраиваем Java в программу на С и хотим ссылаться на функции своего приложения, а не на функции из общей библиотеки или хотим привязать нативные методы к другим функциям на С.

Далее идет статичный конструктор.


Этот метод нельзя переопределить.
Возвращает класс объекта, методы которого можно использовать для получения информации об этом классе, например его имя (getSimpleName()), его суперкласс (getSuperClass()), и реализуемых интерфейсах (getInterfaces()).
Например, следующий метод выведет имя класса объекта:


Возвращает хэш-код объекта. Этот метод поддерживается в интересах хэш-таблиц (таких как HashMap, например).

По умолчанию, если два объекта равны, их хэш-коды должны быть также равны. Если переопределить метод equals(), реализация метода hashCode() у Object перестает быть корректной. Поэтому, если мы переопределяем метод equals(), мы должны переопределить метод hashCode().

В доках Oracle написано: нативный метод реализован так, что возвращает адрес объекта в памяти.
В openJDK 7 для HotSpot JVM он основан на генераторе случайных чисел. Смотреть метод get_next_hash.

Но также можно выбрать другой режим, в том числе и адрес в памяти.

С помощью ключа командной строки -XX:hashCode=n (где n от 0 до 5).
0 – Park-Miller RNG (по умолчанию)
1 – f(адрес, глобальное_состояние)
2 – константа 1
3 – последовательный счетчик
4 – адрес объекта
5 – Thread-local Xorshift


Метод сравнивает два объекта и возвращает true, если они равны. Метод equals(), реализованный в классе Object использует тождественный оператор (==), чтобы определить, являются ли два объекта равными. Для примитивных типов данных это корректно. Для объектов метод проверяет только то, ссылки указывают на один и тот-же объект.
Всегда надо переопределять метод equals(), если сравнение тождественным оператором не подходит для объекта.
Переопределив метод equals() обязывает разработчика переопределить метод hashCode().


Если класс или один из его суперклассов реализуют интерфейс Cloneable, то можно использовать метод clone() для создания копии существующего объекта.
Реализация этого метода у Object проверяет, реализует ли объект у которого clone() был вызван интерфейс Cloneable(). Если нет, то выбрасывается  CloneNotSupportedException. Если да, то создается объект того же класса, как исходный и его поля инициализируется теми же значениями.
Для некоторых классов стандартное поведение метода clone() работает отлично. Но если объект содержит ссылку на внешний объект, то возможно потребуется переопределить поведение метода clone(). В противном случае изменение внешнего объекта в одном классе приведет к его изменению в другом (полученном клонированием) классе.


Возвращает строковое представление объекта. При переопределении метода рекомендуется возвращать краткий но информативный результат. Для класса Object метод toString() возвращает строку, содержащую имя класса, экземпляром которого является объект и шестнадцатеричное представление хэщ-кода объекта, разделенные символом “@”.


Методы notify(), notifyAll(), wait() объекта участвуют в синхронизации независимо запущенных потоков в программе.

Класс Object предоставляет callback метод finalize(), который может вызываться для объекта когда он становится “мусором”. Реализация finalize() у Object не делает ничего. Его можно переопределить для очистки, нарпимер свободных ресурсов.

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

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

Краткая выдержка про использование finalize (Джошуа Блох):
1. finalize() можно использовать только в двух случаях:
1.1. Проверка/подчистка ресурсов с логированием
1.2. При работе с нативным кодом, который не критичен к утечке ресурсов
2. finalize() замедляет работу GC по очистке объекта в 430 раз
3. finalize() может быть не вызван

Источник

Jar Explorer

Хочу вашему вниманию представить удобную утилиту для работы с Java-библиотеками известными под расширениями jar, war, ear, apk.

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

Java Decompiler – небольшая, на “сях” написанная утилитка, причём действительно с хорошим декомпайлером, но меня в ней не устраивает следующее:

  1. Падает. Очень мне неприятно, когда программа вдруг без всяких причин закрывается в процессе работы. Отвык я уже от такого.
  2. Если ты вдруг решил открыть Java класс в папке где находятся другие классы или джарники, то декомпайлер пытается открыть, загрузить и, самое неприятное, связать все эти файлы между собой. Это занимает кучу времени и моих нервов.
  3. Ну и также название. Из названия можно предположить что автор позиционирует программу всё-таки как декомпайлер а не проводник.

Остальные, такие как: sf Jar Explorer, javalite/jar-explorerBndtoolsJarSpyjarzilla сплошное разочарование для меня. Одна не умеет декомпилить, другая только под Linux третья ещё что-то… Вообщем нет ни одной типа “взял и работай”. Вот поэтому и случился Jar Explorer.

С помощью этой утилиты можно:

  1. Быстро просматривать содержимое JAR, WAR, EAR, ZIP, APK. А также вложенных Java библиотек.
  2. Декомпилить классы с помощью встроенного декомпайлера одним нажатием на класс.
  3. Менять содержимое Java библиотеки. Можно быстро поправить содержимое любого текстового, xml и других файлов.
  4. Также можно перетягивать файлы и менять(обновлять) таким образом Java библиотеку.

 

Так все-таки чем перехватывать исключения в Java

Несколько лет назад я, будучи новичком, помню, на практических шишках пришёл к выводу, что если хватать все исключения Throwable-ом, то будет мне счастье. Но тогда же “умными” сеньёрами мне было настучено по рукам. Потому что так Фулер (тот что Мартин) какой-то то там сказал или просто карго-культ. Вот так надо и больше никак! Ну да ясный код с ними. Я же, когда мне было надо, один хрен юзал где Throwable, а где Exception. Вообщем мне хватало. Карго-последователи утверждают, якобы, что ровными руками написать код который бы генерил исключения неотлавливаемые через Exception – невозможно.

Но вот недавно натолкнули меня коллеги на следующий код:

Вообщем, идея простая. У нас есть код, который надо выполнить (строка 7). В этом коде простреливает такой вот эрор java.lang.ExceptionInInitializerError, полученный, кстати, весьма честно – стреляем рантайм исключение из статического блока при инициализации класса:

untitled

Получается что чхать хотел этот зверёк на Exception и может быть перехвачен только Throwable. Вот так вот.