Quizz for interview Flashcards
Перечислите методы класса Object
equals() hashCode() toString() getClass() notify() notifyAll() wait() wait(long timeOut) wait(long timeOut, int nanos)
Зачем нужны методы equals & hashCode?
Метод equals() для определения способа сравнения двух объектов.
Метод hashCode() для ускорения процесса сравнения: если хеши объектов не равны, значит полное сравнение делать не нужно.
Что будет, если переопределить equals, но не переопределить hashCode?
Будет нарушен контракт между equals() и hashCode() :
1) Если два объекта равны, тогда они должны иметь одинаковые хеш-коды.
2) Если два объекта имеют одинаковые хеш-коды, то они могут быть не равны между собой.
В случае, если hashCode() не переопределен, то HashMap HashSet будут работать не верно - вставлять одинаковые ключи или не находить существующие.
Зачем нужны методы wait, notify, notifyAll?
Все методы используются в механизме многопоточности Java.
wait() - вызывается нитью в синхронизированном блоке у объекта-монитора. В результате вызова нить,которая его осуществила становиться в режим ожидания и отпускает мютекс объекта-монитора
notify() - вызывается у монитора и снимает с wait’а одну случайную нить
notifyAll() - снимает с паузы все заснувшие нити
Как правильно клонировать объект?
- Переопределение метода clone() и реализация интерфейса Cloneable();
- Использование конструктора копирования;
- Использовать для клонирования механизм сериализации
Зачем нужен метод finalize() и как он работает?
Перед удалением объекта сборщик мусора может вызвать метод finalize() (а может и не вызвать).
Метод предназначен для освобождение не Java ресурсов перед удалением объекта из памяти.
Время и факт вызова этого метода непредсказуемы и зависят от алгоритмов работы сборщика мусора.
В чем отличие final, finally, finalize?
final - модификатор доступа
finally - блок конструкции try/catch/finally
finalize - метод класса Object, который вызывает сборщик мусора.
Что такое try-with-resources?
Специальная конструкция блока try, которая гарантирует вызов метода close() у объектов созданным try(// ТУТ///).
Эти объекты должны реализовывать интерфейс AutoClosable.
Обычно используется при работе с потоками ввода вывода, которые обязательно должны быть закрыты после использования.
Чем отличаются методы wait(1000) и sleep(1000)?
wait(1000) - должен быть вызван на объекте-мьютексе в блоке syncronized и может быть выключен методами notify().
Используется для multi-thread-synchronization
sleep(1000) - вызывается на текущем потоке и может быть остановлен прерыванием выполнения потока
используется для time-syncronization
В чем отличие i++ и ++i ?
i++ - i сначала участвует в выражении и только потом инкрементируется
++i - i сначала инкрементируется, затем используется в выражении.
Как правильно сравнить две строки в Java?
str1.equals(str2)
потому, что строка - это всегда новый объект класса String и “==” ,будет возвращать false.
В сравнении могут быть как литералы строк, так и новые объекты класса String.
equals() - всегда сравнивает правильно.
Как правильно сравнить две строки в Java игнорируя регистр букв?
str1.equalsIgnoreCase(str2)
Как отсортировать список строк в алфавитном порядке?
Collections.sort(list)
В какой кодировке хранятся строки в Java?
Строки в java хранятся в Unicode
Как преобразовать строку в кодировку Windows-1251?
byte[] byte1251 = "Hello world".getBytes("windows-1251"); String outString = new String(byte1251,"windows-1251");
Как разбить строку на отдельные слова?
s. split(“\s+”) - разделитель пробел(ы)
s. split(“\W+”) - разделитель любой не буквенный сивмол
Как развернуть строку задом наперед?
StringBuilder sb = new StringBuilder("Строка для разворота"); String reversed = sb.reverse();
Что происходит, когда мы пишем “A”+”b”+”C”?
Конкатенация.
получится строка “AbC”
Что такое mutable и immutable типы?
mutable - типы, чьи объекты могут изменять свое состояние в течение жизни.
immutable - типы, чьи объекты не могут изменять своего состояния в течение жизни
Что дает типу String то, что его сделали immutable?
- Безопасность.
String широко используется, как параметр для многих классов Java, в частности для открытия сетевых соединений, подключений к БД, открытию файлов и пр. И если бы строка изменялась, то мы могли получить доступ к объекту (файлу например), на который мы имеем право, затем изменить строку с именем (случайно или намеренно) и получить доступ уже к другому файлу.
Так же String используется в механизме загрузки файлов, и это – фундаментальный аспект. И если бы строка изменялась, то запрос на загрузку “java.io.Writer” мог бы быть изменён на “DiskErasingWriter”.
- Hashcode
Из-за того, что строка не изменяется, она кэширует свой хэшкод и не вычисляет его каждый раз, когда мы его вызываем, что делает строку очень быстрой как ключ для hashmap. - Многопоточность
immutable делает экземпляры строк потокобезопасными.
Какие бывают внутренние классы?
- Внутренние
- локальные
- анонимные
- Статические вложенные
Во что компилируется анонимный внутренний класс?
Во внутренний не статический класс
Зачем использовать ключевое слово final при создании анонимных классов?
Если определяется анонимный внутренний класс и ему нужно при этом использовать объекты, определенные вне этого внутреннего класса, компилятор требует, чтобы переданные на них ссылки объявлялись неизменными (final). Без такого объявления вы получите сообщение об ошибке при компиляции программы.
Как правильно создать объект внутреннего класса?
Внутренние (не статические) классы, как переменные и методы связаны с объектом внешнего класса.
Внутренние классы так же имеют прямой доступ к полям внешнего класса. Такие классы не могут содержать в себе статические методы и поля. Внутренние классы не могут существовать без экземпляра внешнего.
Для создания объекта: Outer outer = new Outer(); Innter inner = outer.new Inner();
Как правильно создать объект вложенного класса?
Синтаксис создания объекта вложенного класса:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Можно ли создавать статические методы/переменные во внутреннем классе?
Статические методы/переменные объявлять во внутреннем классе (не вложенном) нельзя.
Внутренние (не статические) классы, как переменные и методы связаны с объектом внешнего класса. Такие классы не могут содержать в себе статические методы и поля.
Назовите три любых внутренних класса?
- private static class Holder —вложенный класс HashMap из java.util.
- В интерфейсе Map есть interface Entry, который опять же в HashMap и реализуется в другом вложенном классе static class Entry implements Map.Entry.
- private static class IntegerCache в классе Integer.
Как внутренние классы решают проблему множественного наследования в Java?
Т.к. множественное наследование классов в Java запрещено, эту проблему решают с помощью внутренних классов: в нужном нам классе мы объявляем внутренний класс и наследуем его от требуемого класса. Пример:
class Tiger extends Cat {
public void tigerRun() { }
public void startTiger(){ TigerThread thread = new TigerThread(); thread.start(); }
class TigerThread extends Thread{ public void run() { tigerRun(); } } }
Чем отличаются анонимные классы, созданные на основе интерфейса и на основе класса?
Анонимный класс согласно JLS 15.9.5 представляют собой выражение, в котором объявление нового класса и его инициализация совмещены:
При объявлении класса будет создан новый класс, производный от указанного класса при использовании в качестве базы другого класса, или реализующий интерфейс при использовании в качестве базы интерфейса.
При инициализации будет создан новый объект и на него будет возвращения ссылка: анонимный класс является конкретным.
Таким образом, единственное отличие в анонимных классах, созданных на основе интерфейса и класса, заключается в количестве абстракных методов, которые необходимо реализовать.
Инициализации анонимного класса на основе интерфейса потребует реализации каждого метода, в то время как при инициализации анонимного класса на основе абстрактного/конкретного класса позволит изменить поведение за счет перегрузки методов и потребует реализации абстракных методов.
Можно ли создать анонимный статический вложенный класс?
Нет, статической становится только переменная, но не класс.
Во что компилируются анонимные внутренние классы?
Анонимные внутренние классы компилируются в файлы внешнийКласс$n.class. На месте внешнего класса, соответственно, название обрамляющего класса, внутри которого описывается анонимный внутренний класс. На месте n число от 1 до количества анонимных классов.
Можно ли наследовать внутренние классы?
Наследовать внутренние классы от других — можно.
Наследование от внутреннего класса получается чуть сложнее, чем обычное, так как конструктор внутреннего класса связывается со ссылкой на окружающий внешний объект. Проблема состоит в том, что «скрытая» ссылка на объект объемлющего внешнего класса должна быть инициализирована, а в производном классе больше не существует объемлющего объекта по умолчанию. Для явного указания объемлющего внешнего объекта применяется специальный синтаксис:
//: innerclasses/InheritInner.java // Наследование от внутреннего класса.
class WithInner { class Inner {} }
public class InheritInner extends WithInner.Inner { //! InheritInner() {} // He компилируется InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } } Здесь класс InheritInner расширяет только внутренний класс, а не внешний. Но когда дело доходит до создания конструктора, предлагаемый по умолчанию конструктор не подходит, и вы не можете просто передать ссылку на внешний объект. Необходимо включить в тело конструктора выражение ссылкаНаОбъемлющийКласс.super(); в теле конструктора. Оно обеспечит недостающую ссылку, и программа откомпилируется.
Можно ли наследовать анонимные внутренние классы?
Описывая анонимный класс мы уже наследуемся от какого-то класса или реализуем какой-либо интерфейс. К анонимным классам напрямую нельзя применить слова extends или implements, но ведь никто не мешает заранее подготовиться и расширить нужный интерфейс, который будем реализовывать с помощью анонимного класса. Пример в коде ниже.
import java.awt.event.WindowListener;
public class TestExtendAnonym { private interface MyInterface extends Runnable, WindowListener { }
Runnable r = new MyInterface() { ... //Пример того как реализовать 2 и более интерфейса в анонимном классе }; } Наследоваться от анонимного класса нельзя.
Можно ли переопределять внутренние классы?
Переопределение внутреннего класса, как если бы он был еще одним методом внешнего класса, фактически не имеет никакого эффекта:
//: innerclasses/BigEgg.java // Внутренний класс нельзя переопределить // подобно обычному методу, import static net.mindview.util.Print.*;
class Egg { private Yolk y; protected class Yolk { public Yolk() { print("Egg.Yolk()"); } } public Egg() { print("New Egg()"); y = new Yolk(); } }
public class BigEgg extends Egg { public class Yolk { public Yolk() { print("BigEgg.Yolk()"); } } public static void main(String[] args) { new BigEgg(); } } Вывод:
New Egg()
Egg.Yolk()
Конструктор по умолчанию автоматически синтезируется компилятором, а в нем вызывается конструктор по умолчанию из базового класса. Можно подумать, что при создании объекта BigEgg должен использоваться «переопределенный» класс Yolk, но это отнюдь не так, как видно из результата работы программы.
Этот пример просто показывает, что при наследовании от внешнего класса ничего особенного с внутренними классами не происходит. Два внутренних класса — совершенно отдельные составляющие, с независимыми пространствами имен.
Иными словами нельзя.
Какие ограничения есть у локальных классов?
Вначале вспомним что такое локальный класс. Это класс, описанный в блоке кода, то есть, по-простому — между кавычек {}. Наиболее часто эти кавычки являются телом метода. Но могут они быть и просто блоком, статическим блоком, телом if-ов, циклов и т.д.
Локальный класс наделён особенностями внутренних классов, но имеет отличительные черты, а именно:
он имеет доступ только к финальным полям и аргументам обрамляющего метода, а также ко всем полям обрамляющего класса, в том числе приватным и статическим;
локальный класс виден и может создаваться только в блоке, в котором описан;
у локального класса не ставится модификатор доступа;
не может иметь статических полей, методов, классов (за исключением финальных);
локальный класс, объявленный в статическом блоке может обращаться только к статическим полям внешнего класса.
Но! Начиная с Java8 мы можем обращаться в локальных классах к не финальным локальным переменным, если они не были изменены до момента инициализации класса. Также теперь стало возможным обращение к не финальным параметрам метода.
Может ли анонимный внутренний класс содержать статические методы?
Нет. У Анонимных внутренних классов, как и у внутренних классов не может быть статических полей, методов. (вспомним, что анонимные классы компилируются в обычные внутренние, а те, в свою очередь, связаны с объектом обрамляющего класса)
Можно ли создать объект внутреннего класса, если у внешнего класса только private конструктор?
Имея подобный код:
public class PrivateConst { private PrivateConst() {} public class InnerClass{ public void f(){ System.out.println("hello"); } } } Напрямую, в другом классе (вне обрамляющего), конечно, создать объект InnerClass следующим способом не получится:
PrivateConst.InnerClass priv = new PrivateConst().new InnerClass(); Но! Что если у нас есть метод, возвращающий экземпляр
PrivateConst:public class PrivateConst { private static PrivateConst instance; private PrivateConst() {}
public static PrivateConst getInstance(){ instance = new PrivateConst(); return instance; }
public class InnerClass{} } В этом случае приватный конструктор нам не помеха для создания объекта InnerClass. Так же мы без проблем сможем создавать его в методах и в других внутренних классах, принадлежащих PrivateConst.
Ответ — можно, если каким-либо способом нам удастся получить объект обрамляющего класса.
Можно ли объявлять внутренние классы private?
Да, можно.
Можно ли объявлять анонимные внутренние классы private?
Можно объявить private переменную от типа которой наследуется наш анонимный класс.
Сколько у класса максимально может быть внутренних классов?
Сколь угодно много. Ограничение особенности ОС и длинны имени файлов.
Назовите все состояния объекта Thread?
NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED
В какие состояния может перейти нить, при входе в блок synchronized?
В RUNNABLE, если блок кода, помеченный synchronized, не занят другой нитью. Иначе наша нить получит состояние
BLOCKED и будет ждать освобождения объекта-мютекса
В какое состояние перейдет нить, при вызове метода wait()?
Вызов этого метода переводит нить в состояние WAITING. Метод wait() можно вызвать только внутри блока synchronized у объекта-мютекса, который был «залочен (заблокирован)» текущей нитью, в противном случае метод выкинет исключение IllegalMonitorStateException. Object monitor = getMonitor(); synchronized(monitor){ … monitor.wait(); … }
При вызове метода wait(), текущая нить снимает блокировку с объекта monitor, и переходит в состояние WAITING, ожидая вызова метода monitor.notify() или monitor.notifyAll() другой нитью. Как только это произойдет, нить проснется и если монитор не был занят, то захватит его и продолжит работу.
Если монитор окажется занят другой нитью, текущая нить перейдет в состояние BLOCKED.
В какое состояние перейдет нить, при вызове метода wait(500)?
Вызов этого метода переводит нить в состояние TIMED_WAITING. По аналогии с методом wait(), wait(timeout) можно вызвать только внутри блока synchronized у объекта-мютекса, который был «залочен (заблокирован)» текущей нитью. Object monitor = getMonitor(); synchronized(monitor){ … monitor.wait(500); … }
При вызове метода wait(), текущая нить снимает блокировку с объекта monitor, и засыпает на 500 миллисекунд. Объект monitor может быть захвачен другой нитью.
Через 500 миллисекунд нить проснется и если monitor не был занят, то захватит его и продолжит работу.
Если монитор окажется занят другой нитью, текущая нить перейдет в состояние BLOCKED.
В какое состояние перейдет нить, при вызове метода notify()?
Object monitor = getMonitor(); synchronized(monitor){ … monitor.wait(); … }
После monitor.wait(), нить перейдет в состояние WAITING. Метод notify(), вызванный другой нитью у объекта monitor переведет нить из состояния WAITING в состояние RUNNABLE, если объект monitor не будет захвачен другой нитью, иначе в состояние BLOCKED.
В какое состояние перейдет нить, при вызове метода notifyAll()?
notifyAll() “пробудит” все нити. Одна из всех “спящих” (WAITING) нитей перейдет в состояние RUNNABLE, захватит монитор используемого объекта и продолжит свою работу. Остальные окажутся в состоянии BLOCKED. Как только первая “проснувшаяся” нить отпустит монитор, который все остальные ожидают, её участь повторит следующая нить (произвольная нить из состояния BLOCKED перейдет в состояние RUNNABLE). Это будет продолжаться до тех пор, пока все “пробужденные” нити не покинут состояния BLOCKED.
Три нити в блоке synchronized вызвали wait() у объекта-мютекса.
В какое состояние перейдут эти нити, если четвертая нить вызовет notifyAll()?
Две из них перейдут в состояние BLOCKED, одна в состояние RUNNABLE
Чем отличается join(500) от wait(500)?
Несмотря на то, что и join(500) и wait(500) переведут текущую нить в состояние TIMED_WAITING, между ними существенные различия:
join(500) вызывается у нити, wait(500) вызывается внутри синхронизированного блока у объекта, по которому данный блок синхронизирован.
При вызове join(500) текущая нить будет ожидать 500 миллисекунд завершения нити, чей метод join() был вызван.
При вызове wait(500) текущая нить снимет блокировку с синхронизированного объекта, и засыпает на 500 миллисекунд.
Через 500 миллисекунд в обоих случаях нити продолжат работу.
Чем отличается wait(500) от sleep(500)?
sleep(500) вызывается у нити, wait(500) вызывается внутри синхронизированного блока у объекта, по которому данный блок синхронизирован.
При вызове sleep(500) текущая нить будет ожидать 500 милисекунд, затем продолжит свою работу.
При вызове wait(500) текущая нить снимет блокировку с синхронизированного объекта, и засыпает на 500 миллисекунд.
В какое состояние перейдет нить при вызове метода yield()?
При вызове метода yield() – текущая нить «пропускает свой ход» и java сразу переключается на выполнение следующей нити. Нить из состояния running переходит в состояние ready. Состояния running & ready – это подсостояния состояния RUNNABLE
Как пользоваться интерфейсом Comparable?
В интерфейсе Comparable объявлен всего один метод compareTo(Object obj), предназначенный для реализации упорядочивания объектов класса. Его удобно использовать при сортировке упорядоченных списков или массивов объектов.
Данный метод сравнивает вызываемый объект с obj. В отличие от метода equals, который возвращает true или false, compareTo возвращает:
- 0, если значения равны;
- Отрицательное значение, если вызываемый объект меньше параметра;
- Положительное значение , если вызываемый объект больше параметра.
Прежде всего он удобен для сортировки упорядоченных списков (java.util.List) и массивов объектов. Если список/массив содержит элементы, реализующие этот интерфейс, то они могут быть отсортированы автоматически методами java.util.Collections.sort(List)/Arrays.sort(Object[]).
С интерфейсом Comparable связано понятие натурального упорядочивания, потому как он устанавливает натуральный порядок следования экземпляров любого класса, реализующего этот интерфейс. Иначе говоря, порядок (x, y) соответствует выполнению условия x.compareTo(y) <= 0.
Правила реализации Comparable, а вернее, его метода compareTo(Object) следующие (x и y – экземпляры класса, реализующего Comparable):
- x.compareTo(y) возвращает -1 или 1, если x должен находиться, соответственно, раньше или позже y. Если метод возвращает 0, то порядки (x, y) и (y, x) эквивалентны.
- Если sign(a) – функция, возвращающая -1,0,1 для а, соответственно, меньше 0, равного 0 и больше 0, то должно выполняться равенство sign(x.compareTo(y))==-sign(y.compareTo(x)). Что логично: если x идет раньше y, то y должен идти позже x, и наоборот.
- Если x.compareTo(y) > 0 и y.compareTo(z) > 0, то x.compareTo(z) > 0 – соотношение транзитивности неравенств.
- Если x.compareTo(y) == 0, то sign(x.compare(z)) == sign(y.compareTo(z)), для любых z.
- Вызов x.compareTo(null) должен бросать исключение NullPointerException. В этом есть расхождение с логикой реализации equals (напомню, x.equals(null) возвращает false).
- Если y по своему типу не может быть сравнен с x, то вызов x.compareTo(y) должен бросать исключение ClassCastException.
- (x.compareTo(y) == 0) == x.equals(y), т.е. вызов x.compareTo(y) должен возвращать 0 тогда и только тогда, когда x.equals(y) возвращает true. Это правило непротиворечивости, и его очень важно учитывать.
Как пользоваться интерфейсом Comparator?
В интерфейсе Comparator объявлено два метода compare(Object obj1, Object obj2) и equals(Object obj).
При использовании интерфейса Comparator, логика сравнения пары объектов не прячется внутрь класса/объекта, а реализуется в отдельном классе.
Метод compare(x,y) в точности соответствует по своей сути вызову x.compareTo(y). Точно так же должны выполняться все правила, что и правила для реализации метода compareTo(Object) интерфейса Comparable.
Comparator может использоваться в любом месте, где нужна сортировка. При этом, во-первых, появляется необходимая гибкость – возможность реализации нескольких правил сортировки. А во-вторых, сортируемые объекты могут не реализовывать интерфейс Comparable. В случае, если они его все-таки реализуют, Comparator имеет приоритет.
Интерфейс Comparator определяет еще и метод equals(Object), как это ни парадоксально. Этот метод служит для сравнения самих экземпляров интерфейса Comparator и должен возвращать true только в том случае, если сравниваемые объекты обеспечивают одинаковый порядок сортировки. Однако всегда безопасно оставлять исходную реализацию Object.equals(Object) нетронутой
Какие методы есть у класса Collections?
- public static boolean addAll(Collection super T> c, T… elements)
Метод добавляет элементы массива elements в коллекцию Collection super T> c. Элементы могут быть указаны по одиночке, либо как массив. Когда элементы указанны по отдельности данный метод предоставляет возможность удобно добавить все элементы в имеющуюся коллекцию:
Collections.addAll(flavors, “Peaches ‘n Plutonium”, “Rocky Racoon”);
- public static int binarySearch(List extends Comparable super T» list, T key)
public static int binarySearch(List extends T> list, T key, Comparator super T> c)
Оба метода ищут в списке переданном в параметре объект переданный в параметре используя алгоритм двоичного поиска. Возвращают индекс элемента, если такой элемент в списке есть, иначе индекс первого элемента списка большего key, если все элементы меньше key, возвращает list.size().
Перед использованием данных методов списки должны быть отсортированы. В первом случае отсортированы по возрастанию в “естественном” порядке следования элементов списка (такой же, как при использовании Collections.sort(list)). Во втором случае список должен быть отсортирован по возрастанию в порядке следования, который обеспечивает переданный компаратор (такой же порядок, как при использовании Collections.sort(list, c)[здесь “с” - компаратор из параметров описываемого метода])
- public static Collection checkedCollection(Collection c, Class type)
Преамбула: механизм дженериков в языке обеспечивает проверку типов во время компиляции. Обычно этого и достаточно, однако бывают случаи, когда все-таки нет. К примеру мы нашу коллекцию передаем в код библиотеки, куда-нибудь на сторону, нам неизвестную, и нам очень сильно хочется, чтоб код этой “third-party library” не вставил в нашу коллекцию элемент неправильного типа. Это вот возможная проблема номер 1.
Возможная проблема номер 2 следующая. Предположим наша программа выдает нам ClassCastException, который оповещает нас о том, что в коллекцию был вставлен элемент неправильного типа. К сожалению данное исключение может вылететь в любой момент, после того, как неправильный элемент был вставлен, и обычно предоставляет нам совсем немного или вообще ноль информации об источнике проблемы.
Используя метод метод checkedCollection мы можем избавить себя от проблемы один и два, т.к. этот метод создает коллекцию проверяемую на этапе выполнения.
Решение проблемы номер два, с помощью данного метода: К примеру мы имеем вот это, и у нас вываливается ClassCastException.
Collection c = new HashSet();
Код выше можно временно заменить на:
Collection c = Collections.checkedCollection( new HashSet(), String.class);
При запуске программы снова мы локализуем строку кода, которая вставляет элемент неправильного типа в нашу коллекцию.
Родственные на мой взгляд методы:
public static List checkedList(List list,Class type)
public static Map checkedMap(Map m, Class keyType,Class valueType)
public static Set checkedSet(Set s,Class type)
public static SortedMap checkedSortedMap(SortedMap m,Class keyType,Class valueType)
public static SortedSet checkedSortedSet(SortedSet s,Class type)
- public static void copy(List super T> dest,List extends T> src)
Метод копирует элементы src в dest. индексы у копированных элементов будут совпадать.
- public static > T min(Collection extends T> coll)
public static > T max(Collection extends T> coll)
public static T min(Collection extends T> coll,Comparator super T> comp)
public static T max(Collection extends T> coll,Comparator super T> comp)
методы возвращают минимальный\максимальный элемент в коллекции с точки зрения “естественного порядка”(интерфейс Comparable) либо порядка переданного компаратора.
- public static boolean disjoint(Collection> c1,Collection> c2)
Возвращает true если у коллекций нет одинаковых элементов.
7. List emptyList(), Map emptyMap(), Set emptySet() – возвращают пустой список, карту отображения и множество соответственно;
- void fill(List super T> list, T obj) – заполняет список заданным элементом ;
- int frequency(Collection> c, Object o) – возвращает количество вхождений в коллекцию заданного элемента;
- List nCopies(int n, T o) – возвращает список из n заданных элементов;
- boolean replaceAll(List list, T oldVal, T newVal) – заменяет все заданные элементы новыми;
- void reverse(List> list) – “переворачивает” список;
- void rotate(List> list, int distance) – сдвигает список циклически на заданное число элементов;
- void shuffle(List> list) – перетасовывает элементы списка;
- Set singleton(T o),
singletonList(T o),
singletonMap(K key, V value) – создают множество, список и карту отображения, состоящие из одного элемента;
16. > void sort(List list), void sort(List list, Comparator super T> c) – сортировка списка, естественным порядком и используя Comparator соответственно;
- void swap(List> list, int i, int j) – меняет местами элементы списка стоящие на заданных позициях
Какие методы есть у класса Arrays?
- public static List asList(T… a)
формирует список на основе массива. Массив при этом используется для внутреннего представления списка. Таким образом сохраняется связь между списком и исходным массивом:
изменения в массиве отразятся на списке:
String[] a = { “foo”, “bar”, “baz”};
List list = Arrays.asList(a);
System.out.println(list); // [foo, bar, baz]
a[0] = “aaa”;
System.out.println(list); // [aaa, bar, baz]
изменения в списке отразятся на массиве:
String[] a = { “foo”, “bar”, “baz”};
List list = Arrays.asList(a);
System.out.println(list); // [foo, bar, baz]
list.set(0, “bbb”);
System.out.println(Arrays.toString(a)); // [bbb, bar, baz]
Если массив содержит объекты, очевидно, и массив и список будут ссылаться на одни и те же экземпляры:
Object[] a = { new Object(), new Object(), new Object()}; List list = Arrays.asList(a); System.out.println(a[0] == list.get(0)); // true
- int binarySearch(параметры) – перегруженный метод организации бинарного поиска значения в массивах примитивных и объектных типов. Возвращает позицию первого совпадения;
- void fill(параметры) – перегруженный метод для заполнения массивов значениями различных типов и примитивами;
- void sort(параметры) – перегруженный метод сортировки массива или его части с использованием интерфейса Comparator и без него;
- static T[] copyOf(T[] original, int newLength) –заполняет массив определенной длины, отбрасывая элементы или заполняя null при необходимости;
- static T[] copyOfRange(T[] original, int from, int to) – копирует заданную область массива в новый массив;
- List asList(T… a) – метод, копирующий элементы массива в объект типа List.
Как называется сортировка, которая используется при вызове Collections.sort()?
Из документации:
Реализация является адаптированным вариантом сортировки списка для Python Тима Петерса (TimSort). Данная реализация сбрасывает список в массив, сортирует массив, затем проходит по списку и перезагружает каждый элемент списка из соответствующего элемента массива. Это позволяет избежать сложности n*n log(n), которая возникла бы при попытки отсортировать связный список напрямую
Из вики:
Timsort — гибридный алгоритм сортировки, сочетающий сортировку вставками и сортировку слиянием, опубликованный в 2002 году Тимом Петерсом. В настоящее время Timsort является стандартным алгоритмом сортировки в Python, OpenJDK 7 и реализован в Android JDK 1.5. Основная идея алгоритма в том, что в реальном мире сортируемые массивы данных часто содержат в себе упорядоченные подмассивы. На таких данных Timsort существенно быстрее многих алгоритмов сортировки.
Что такое канкаренси?
Concurrency – это библиотека классов в Java, в которой собрали специальные классы, оптимизированные для работы из нескольких нитей. Эти классы собраны в пакете java.util.concurrent. Их можно схематично поделить по функциональному признаку следующим образом:
1 Concurrent Collections— набор коллекций, более эффективно работающие в многопоточной среде нежели стандартные универсальные коллекции из java.util пакета. Вместо базового враппера Collections.synchronizedList с блокированием доступа ко всей коллекции используются блокировки по сегментам данных или же оптимизируется работа для параллельного чтения данных по wait-free алгоритмам.
- Queues — неблокирующие и блокирующие очереди с поддержкой многопоточности.
- Неблокирующие очереди заточены на скорость и работу без блокирования потоков.
- Блокирующие очереди используются, когда нужно «притормозить» потоки «Producer» или «Consumer», если не выполнены какие-либо условия, например, очередь пуста или переполнена, или же нет свободного «Consumer»’a. - Synchronizers — вспомогательные утилиты для синхронизации потоков. Представляют собой мощное оружие в «параллельных» вычислениях.
- Executors — содержит в себе отличные фреймворки для создания пулов потоков, планирования работы асинхронных задач с получением результатов.
- Locks — представляет собой альтернативные и более гибкие механизмы синхронизации потоков по сравнению с базовыми synchronized, wait, notify, notifyAll.
- Atomics — классы с поддержкой атомарных операций над примитивами и ссылками.
Какие классы из «канкаренси» ты знаешь?
ConcurrentHashMap — В отличие от Hashtable и блоков synhronized на HashMap, данные представлены в виде сегментов, разбитых по hash’ам ключей. В результате, для доступа к данным лочится по сегментам, а не по одному объекту. В дополнение, итераторы представляют данные на определенный срез времени и не кидают ConcurrentModificationException.
AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray — Что если в классе нужно синхронизировать доступ к одной простой переменной типа int? Можно использовать конструкции с synchronized, а при использовании атомарных операций set/get, подойдет также и volatile. Но можно поступить еще лучше, использовав новые классы Atomic*. За счет использования CAS, операции с этими классами работают быстрее, чем если синхронизироваться через synchronized/volatile. Плюс существуют методы для атомарного добавления на заданную величину, а также инкремент/декремент.
Как устроен класс ConcurrentHashMap?
основные преимущества и особенности реализации ConcurrentHashMap:
- Карта имеет схожий с hashmap интерфейс взаимодействия
- Операции чтения не требуют блокировок и выполняются параллельно
- Операции записи зачастую также могут выполняться параллельно без блокировок
- При создании указывается требуемый concurrencyLevel, определяемый по статистике чтения и записи
- Элементы карты имеют значение value, объявленное как volatile
Что такое класс Lock?
Для управления доступом к общему ресурсу в качестве альтернативы оператору synchronized мы можем использовать блокировки. Функциональность блокировок заключена в пакете java.util.concurrent.locks.
Вначале поток пытается получить доступ к общему ресурсу. Если он свободен, то на поток на него накладывает блокировку. После завершения работы блокировка с общего ресурса снимается. Если же ресурс не свободен и на него уже наложена блокировка, то поток ожидает, пока эта блокировка не будет снята.
Классы блокировок реализуют интерфейс Lock, который определяет следующие методы:
void lock(): ожидает, пока не будет получена блокировка
boolean tryLock(): пытается получить блокировку, если блокировка получена, то возвращает true. Если блокировка не получена, то возвращает false. В отличие от метода lock() не ожидает получения блокировки, если она недоступна
void unlock(): снимает блокировку
Condition newCondition(): возвращает объект Condition, который связан с текущей блокировкой
Организация блокировки в общем случае довольно проста: для получения блокировки вызывается метод lock(), а после окончания работы с общими ресурсами вызывается метод unlock(), который снимает блокировку.
Объект Condition позволяет управлять блокировкой.
Как правило, для работы с блокировками используется класс ReentrantLock из пакета java.util.concurrent.locks. Данный класс реализует интерфейс Lock.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// класс для работы с Lock API. Переписан с приведенной выше программы, // но уже без использования ключевого слова synchronized public class ConcurrencyLockExample implements Runnable{
private Resource resource; private Lock lock;
public ConcurrencyLockExample(Resource r){ this.resource = r; this.lock = new ReentrantLock(); }
@Override public void run() { try { // лочим на 10 секунд if(lock.tryLock(10, TimeUnit.SECONDS)){ resource.doSomething(); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ //убираем лок lock.unlock(); } // Для логгирования не требуется потокобезопасность resource.doLogging(); }
}
Как видно из программы, мы используем метод tryLock(), чтобы убедиться в том, что поток ждет только определенное время. Если он не получает блокировку на объект, то просто логгирует и выходит.
Еще один важный момент. Необходимо использовать блок try-finally, чтобы убедиться в том, что блокировка будет снята, даже если метод doSomething() бросит исключение.
Что такое итератор?
Представленный в релизе JDK 1.2 языка Java интерфейс java.util.Iterator обеспечивает итерацию контейнерных классов. Каждый Iterator реализует методы next() и hasNext() и дополнительно может поддерживать метод remove(). Итераторы создаются соответствующими контейнерными классами, как правило методом iterator().
Метод next() переводит итератор на следующее значение и возвращает указываемое значение итератору. При первоначальном создании итератор указывает на специальное значение, находящееся перед первым элементом, поэтому первый элемент можно получить только после первого вызова next(). Для определения момента, когда все элементы в контейнере были перебраны, используется тестовый метод hasNext(). Следующий пример демонстрирует простое использование итераторов:
Iterator iter = list.iterator(); //Iterator iter = list.iterator(); в J2SE 5.0 while (iter.hasNext()) System.out.println(iter.next());
Для коллекции типов, поддерживающей подобное, метод итератора remove() удаляет последний ‘посещенный’ элемент из контейнера. Почти все остальные типы модификации контейнера во время итерации являются небезопасными.
Кроме того, для java.util.List существует java.util.ListIterator со схожим API, но позволяющем прямую и обратную итерации, обеспечивая определение текущего индекса в списке и переход к элементу по его позиции
Что такое mutex?
Мютекс – это специальный объект для синхронизации нитей/процессов. Он может принимать два состояния – занят и свободен. Если упростить, то мютекс – это boolean-переменная, которая принимает два значения: занят(true) и свободен(false).
Когда нить хочет монопольно владеть некоторым объектом, она помечает его мютекс занятым, а когда закончила работу с ним – помечает его мютекс свободным.
Мютекс прикреплен к каждому объекту в Java. Прямой доступ к мютексу есть только у Java-машины. От программиста он скрыт.
Что такое монитор?
Монитор – это специальный механизм (кусок кода) – надстройка над мютексом, который обеспечивает правильную работу с ним. Ведь мало пометить, что объект – занят, надо еще обеспечить, чтобы другие нити не пробовали воспользоваться занятым объектом.
В Java монитор реализован с помощью ключевого слова synchronized.
Когда мы пишем блок synchronized, то компилятор Java заменяет его тремя кусками кода:
В начале блока synchronized добавляется код, который отмечает мютекс как занятый.
В конце блока synchronized добавляется код, который отмечает мютекс как свободный.
Перед блоком synchronized добавляется код, который смотрит, если мютекс занят – то нить должна ждать его освобождения.
Что такое дедлок?
Дедлок – это ситуация, когда два и более нитей заблокированы, ждущие друг друга. Дедлоком также называется взаимная блокировка.
Взаимная блокировка – это ситуация в которой, два или более процесса занимая некоторые ресурсы, пытаются заполучить некоторые другие ресурсы, занятые другими процессами и ни один из процессов не может занять необходимый им ресурс, и соответственно освободить занимаемый.
Бывают взаимные блокировки порядка синхронизации (решаются назначением порядка);
Взаимная блокировка между объектами (различные объекты пытаются получить доступ к одним и тем же синхронизированным блокам);
Ресурсная взаимоблокировка (при попытке получить доступ к некоторым ресурсам, которые может использовать одновременно только один поток).
Какие вы знаете стратегии, предотвращающие появление дедлоков?
Интерфейс Lock и его реализации доступные в пакете java.util.concurrent.locks позволяют попытаться занять монитор, связанный с экземпляром данного класса методом tryLock (возвращает true, если удалось занять монитор).
Также есть стратегия применения открытых вызовов, то есть вызывать методы других объектов вне синхронизированного блока.
Могут ли возникнуть дедлоки при использовании методов wait-notify?
Дедлоков можно избежать за счет разумного использования synchronized, volatile, монитора (wait(), notify(), notifyAll()),а если копать глубже, то используя классы java.utils.concurrent: вместо обычных коллекций - многопоточные варианты (ConcurrentHashMap, например); если нужен более сложный способ синхронизации потоков — различные CyclicBarrier, CountDownLatch.
Если грамотно использовать wait – notify, то дедлоки возникнуть не должны.)))
Что чаще используется: notify или notifyAll?
зависит от ситуации
Метод wait рекомендуется использовать с конструкциями if или while?
По поводу вызова метода wait. Это уже из разряда чистой техники. Рекомендуется вызывать wait изнутри цикла while. Т.е., писать не
if (some condition){
obj.wait()
}
…, а
while (some condition){
obj.wait()
}
Зачем это надо. Дело в том, что notify может вызвать кто угодно. Просто по ошибке, от которой никто не застрахован. В том случае из опыта, о котором я рассказывал выше, мы взялись за переделку именно для того, чтобы избежать такой возможности. Просто спрятали объект, на котором происходит синхронизация. И доступ к нему имел только наш код. Это хорошая практика, но не всегда возможно, к сожалению. Так вот, если поток ждет выполнения некоторого условия – вариант с while надежнее. Если поток пустили по ошибке – он опять проверит условие и, если надо, будет ждать дальше.
Кроме того, не исключена возможность и простого выхода из ожидания без вызова notify. Некоторые «гуру» утверждают, что VM может выйти из состояния ожидания самопроизвольно. И более того, периодически это наблюдается.
Что происходит после вызова метода notifyAll?
Пробуждает все нити, которые ждали на этом мониторе.
Какие выгоды получает объект, если он immutable?
Immutable объект — это объект, состояние которого после создания невозможно изменить. В случае Java это значит что все поля экземпляра у класс отмечены как final и являются примитивами или тоже immutable типами.
Простейший пример immutable класса из JDK это String. Любые методы, которые вы вызовите на строке (например description.toLowerCase()) вернут новую строку, а не модифицируют исходную
Ключевое слово final для объектных типов гарантирует неизменяемость лишь ссылки, но не самого объекта. Например, если у вас есть final-ссылка на ArrayList, вы тем не менее можете добавлять в него новые элементы или изменять существующие.
В случае же immutable-объекта объект после окончания конструктора не изменяется вообще. Одного лишь модификатора final для этого недостаточно, необходимо, чтобы все подобъекты были тоже неизменяемыми. Вы в принципе можете держать внутри ссылку на изменяемый объект, но обращаться с ним так, чтобы он не менялся.
Использование неизменяемых объектов даёт много выгод. Например, о таком объекте намного легче судить в ситуации, когда во многих частях программы есть ссылка на него (для изменяемого объекта, любая часть программы может вызвать мутирующую функцию в практически любой момент времени и из любого потока).
Но то, что для нас важно в контексте вопроса — неизменяемые объекты не требуют синхронизации при многопоточном доступе. Вот собственно и вся рекомендация: используйте неизменяемые объекты, и вам не придётся думать о том, что нужно, а что не нужно синхронизировать. Единственная возможная проблема — если вы внутри ещё не отработавшего конструктора публикуете ссылку на объект, через которую к нему может получить доступ кто-нибудь ещё, и увидеть объект в изменяющемся состоянии! (Это бывает не так уж и редко. Например, иногда программист хочет добавить объект в конструкторе в коллекцию всех объектов данного типа.)
Обратите внимание, что для полей неизменяемого объекта вы практически обязаны использовать final! Дело в так называемой безопасной публикации. Смотрите. Инструкции в Java-программе могут быть переставлены как оптимизатором, так и процессором (у Java достаточно слабая модель памяти). Поэтому, если не предпринимать специальных действий, окончание работы конструктора и присвоение значений полям может быть переставлено (но невидимо в рамках текущего потока)! Использование final гарантирует, что такого не произойдёт.
В случае многопоточного программирования преимущества immutable классов очевидны: после создания объекты можно передавать другим потокам и они всегда будут в актуальном состоянии. Т.е. вам не надо проверять не устарело ли состояние вашего экземпляра и не модифицировал ли его другой поток пока вы с ним работаете. Например, у вас есть метод bill(Date endDate), в нём вы наивно проверяете соответствие endDate каким-то предварительным условиям и начинаете с ней работать. В этот момент другой поток может изменить endDate, например установит её глубоко в прошлое. Последствия могут быть самыми удивительными.
Что такое «thread-safe»?
Состояние потоко-безопасности подразумевает, что метод или класс может быть использован множеством нитей без проблем столкновения, то есть дедлоков
Что такое «happens-before»?
«Выполняется прежде» (англ. happens before) — отношение строгого частичного порядка (арефлексивное, антисимметричное, транзитивное), введённое между атомарными командами (++ и – не атомарны!), придуманное Лесли Лэмпортом и не означающее «физически прежде». Оно значит, что вторая команда будет «в курсе» изменений, проведённых первой
Что такое JMM?
Java Memory Model
это набор правил:
Правило № 1: однопоточные программы исполняются псевдопоследовательно. Это значит: в реальности процессор может выполнять несколько операций за такт, заодно изменив их порядок, однако все зависимости по данным остаются, так что поведение не отличается от последовательного.
Правило № 2: нет невесть откуда взявшихся значений. Чтение любой переменной (кроме не-volatile long и double, для которых это правило может не выполняться) выдаст либо значение по умолчанию (ноль), либо что-то, записанное туда другой командой
Правило № 3: остальные события выполняются по порядку, если связаны отношением строгого частичного порядка «выполняется прежде» (англ. happens before).
Какое исключение вылетит, если вызвать wait не в блоке synchronized?
Основная причина вызова wait и notify из статического блока или метода в том, что Java API обязательно требует этого. Если вы вызовете их не из синхронизированного блока, ваш код выбросит IllegalMonitorStateException.
Более хитрая причина в том, чтобы избежать состояния гонки между вызовами wait и notify.
Какие приоритеты нитей бывают?
Для оптимизации параллельной работы нитей в Java имеется возможность устанавливать приоритеты нитей. Нити с большим приоритетом имеют преимущество в получении времени процессора перед нитями с более низким приоритетом.
Работа с приоритетами обеспечивается следующими методами класса Thread:
public final void setPriority(int newPriority)
Устанавливает приоритет нити.
public final int getPriority()
Позволяет узнать приоритет нити.
Значение параметра в методе setPriority не может произвольным. Оно должно находиться в пределах от MIN_PRIORITY до MAX_PRIORITY . При своем создании нить имеет приоритет NORM_PRIORITY .
MIN_PRIORITY = 1.
NORM_PRIORITY =5.
MAX_PRIORITY = 10.
Можно ли остановить нить, снизив ее приоритет до 0?
Нет
Программисты в основном полагаются на факт того, что нить останавливается сама, как только заканчивает выполнять методы run() или call(). Для остановки вручную, программисты пользуются преимуществом volatile boolean переменной и проверяют её значение в каждой итерации, если в методе run() есть циклы, или прерывают нити методом interrupt() для внезапной отмены заданий.
Зачем нужен класс ThreadGroup?
ThreadGroup представляет собой набор нитей, которые так же могут содержать в себе другие группы потоков. Группа нитей образует дерево, в котором каждая другая группа нитей имеет родителя (кроме исходной). Поток имеет право доступа к данным из своей группы нитей, но не имеет такого доступа к другим группам или к родительской группе потоков
В какой группе нитей состоит main-thread?
нить main состоит в группе main, которая в свою очередь состоит в группе system, являющейся корневой группой (её parent == null).
Что такое паттерн ThreadPool?
В компьютерном программировании есть модель пула потоков, где определенное число потоков создается для выполнения целого ряда задач, которые обычно организуются в очереди. Результаты от выполненных задач также могут быть помещены в очередь, либо задачи могут не возвращать никакого результата (например, если задача для анимации).
Как правило, существует гораздо больше задач, чем потоков. Как только поток завершит свою задачу, он будет запрашивать следующую задачу из очереди, пока все задачи не будут завершены. Поток может затем прерваться или заснуть. Количество используемых потоков, это параметр, который может быть настроен, для обеспечения наилучшей производительности. Кроме того, число потоков может быть динамическим на основе количества возникающих задач. Например, веб-сервер может добавлять потоки, если запросы многочисленных веб-страниц приходят и может удалить потоки, когда этих запросов становится меньше. С увеличением размера пула потоков увеличивается использование ресурсов компьютера. Алгоритм, используемый для определения того, когда создавать или уничтожать потоки, будет иметь влияние на общую производительность: - Создать слишком много потоков значит тратить ресурсы и время впустую.
Уничтожить слишком много потоков и больше времени будет потрачено позже снова для их создания - Создание потоков слишком медленно, может привести к снижению производительности клиента.
Зачем нужен класс ThreadPoolExecutor?
public class ThreadPoolExecutor extends AbstractExecutorService
ExecutorService это выполняет каждую представленную задачу, используя один возможно из нескольких объединенных в пул потоков, обычно сконфигурированное использование Executors методы фабрики.
Пулы потоков рассматривают две различных проблемы: они обычно обеспечивают улучшенную производительность, выполняя большие количества асинхронных задач, из-за уменьшенных издержек вызова на задачу, и они обеспечивают средство ограничения и управления ресурсами, включая потоки, использованные, выполняя набор задач. Каждый ThreadPoolExecutor также поддерживает немного основной статистики, такой как число завершенных задач.
Чтобы быть полезным через широкий диапазон контекстов, этот класс обеспечивает много корректируемых параметров и рычагов расширяемости. Однако, программистов убеждают использовать более удобное Executors методы фабрики
- Executors.newCachedThreadPool() (неограниченный пул потоков, с автоматическим восстановлением потока),
- Executors.newFixedThreadPool(int) (пул потоков фиксированного размера) и
- Executors.newSingleThreadExecutor() (единственный фоновый поток), которые предварительно конфигурируют настройки для наиболее распространенных сценариев использования.
Сколько способов создать нить вы знаете? (Thread, Runnable, Callable)
На уровне языка есть два способа создания нити. Объект класса java.lang.Thread представляет собой нить, но ей требуется задача для исполнения, которая является объектом, реализующим интерфейс java.lang.Runnable. Так как класс Thread реализует интерфейс Runnable, вы можете переопределить метод run() унаследовав ваш класс от Thread или реализовав в нём интерфейс Runnable.
Для чего используется класс Future?
Future хранит результат асинхронного вычисления. Вы можете запустить вычисление, предоставив кому-либо объект Future, и забыть о нем. Владелец объекта Future может получить результат, когда он будет готов.
В чем преимущества Callable над Runnable?
Callable параметризуемый тип, т.е можно указать тип возвращаемого методом call() значения
Callable может выбрасывать исключение не оказывая влияния на другие выполняющиеся задачи
Метод run интерфейса Runnable вообще не допускал выбрасывания контролируемых исключительных ситуаций, а выброс неконтролируемой (runtime) исключительной ситуации приводил к остановке потока и всего приложения
Можно ли отменить выполнение задачи, если использовать класс Future?
У Future есть метод Future.cancel(boolean), который должен отменить выполнение задачи. Но если задача уже начала выполняться, вызов Future.cancel(true) на самом деле не остановит ее.
Потоку, в котором выполняется задача, всего лишь рекомендуется прекратить выполнение. К тому же, мы не имеем даже возможности узнать выполняется ли задача в данный момент или нет. Есть, метод Future.isDone(), но опять мимо, он возвращает true не только когда задача завершила выполнение, а сразу после вызова Future.cancel(), даже если задача все еще выполняется (ведь Future.cancel(true) не останавливает задачу которая уже начала выполняться).
Что такое autoboxing?
Автоупаковка — это механизм неявной инициализации объектов классов-оберток (Byte, Short, Character, Integer, Long, Float, Double) значениями соответствующих им исходных примитивных типов (соотв. byte, short, char, int, long, float, double), без явного использования конструктора класса.