Часть 1, Часть 2, Часть 3, Часть 4
Данные статьи помогут легко и быстро разобраться в концепциях и программировании на Java. Даже при нулевых знаниях в Java трудностей в освоении этих материалов не возникнет. А опытные Java-разработчики смогут освежить свои знания.
Неизменяемость
Неизменяемым называется любой класс, в котором состояние объекта не меняется после создания экземпляра класса.
- Примеры неизменяемых классов: String, все классы-оболочки и enum.
- Неизменяемые классы всегда потокобезопасны.
Как создать неизменяемый класс:
1. Убедитесь, что класс нельзя переопределить, — объявите класс final. (прим. ред. Класс, объявленный с модификатором final, не может иметь подклассов и исключает наследование.)
2. Все поля должны быть закрытыми.
3. Не добавляйте методов, которые могут изменять состояние объекта. Никаких сеттеров.
4. Используйте клонирование или защитное копирование.
- BigDecimal не относится к неизменяемым классам, т.к. не является конечным.
String, StringBuffer и StringBuilder
- String — это неизменяемый класс. И объекты String изменять нельзя. При присвоении нового значения в стеке каждый раз создается новый объект String, и указатель обращается к новому объекту.
- Пул строк (String intern pool) — это специальная область памяти в Java Heap. Если строка уже создана или присутствует в пуле, то будет возвращаться именно ее ссылка. Новый объект не создается, и ссылка на него не возвращается.
Вопрос: Почему String относится к неизменяемым?
1. Пул строк: При созданной или существующей строке в пуле не создается новый объект. Вместо этого возвращается ссылка на уже существующую строку. Если бы строка не была неизменяемой, то изменение одной ссылки в строке привело бы к ошибкам в значении остальных.
2. Кеширование хеш-кода: Если строка не является неизменяемой, то можно изменить ее хеш-код. А это не годится для кеширования.
3. Безопасность: String активно используется в качестве параметра для многих Java классов (сетевые подключения, открытие файлов и т.д.). В изменяемой строке возникают угрозы безопасности, т.к. ее значения могут перехватить другие участки кода.
Сравнение строк:
String a = “abcd”;
String b = “abcd”;
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
String c = new String(“abcd”);
String d = new String(“abcd”);
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
== проверяет участки памяти; equals() проверяет значения. Использование конструктора приводит к образованию лишнего объекта. Поэтому для создания одной строки применяются двойные кавычки.
- В String имеются следующие методы: concat(), trim(), substring() и replace().
- StringBuffer и StringBuilder — изменяемы.
- StringBuilder добавлен в версии 1.5, не синхронизирован.
- Метод intern() — это ссылочное значение String, т.е. адрес.
Поэтому S1.intern() == S2.intern() только при истинности S1.equals(S2).
Сериализация
- Сериализация — это процесс сохранения состояния объекта. Сам объект запоминается в виде последовательности байтов, а затем заново «воссоздается» из этих байтов.
- Выполняется через реализацию интерфейса Serializable. Это маркерный интерфейс.
- Поля, отмеченные как переходные, не доступны для сериализации.
- serialVersionUID — добавляет номер версии. Так можно проверить, что сериализированный объект не был изменен после десериализации.
- В сериализации задействованы методы writeObject() и readObject(). Их можно переопределять и перенастраивать.
- При экстернализации (т.е. в интерфейсе Externalizable) используются методы readExternal() и writeExternal().
Comparator и Comparable
- Интерфейс Comparable сравнивает текущий объект с другим объектом через метод CompareTo().
- Интерфейс Comparator сравнивает два разных объекта. Использует метод Compare().
Синтаксис метода:
CompareTo(Object obj)
Compare(Object obj1, Object obj2)
- Comparator более управляемый.
- Collections.sort(list) для Comparable и Collections.sort(list, new comparatorObject()) для Comparator.
- Comparable реализуется классом для сравнения собственного объекта с другими.
class HDTV implements Comparable<HDTV> {
private int size;
private String brand;
public HDTV(int size, String brand) {
this.size = size;
this.brand = brand;
}
// .. getters & setters
@Override
public int compareTo(HDTV tv) {
if (this.getSize() > tv.getSize())
return 1;
else if (this.getSize() < tv.getSize())
return -1;
else
return 0;
}}
public class Main {
public static void main(String[] args) {
HDTV tv1 = new HDTV(55, “Samsung”);
HDTV tv2 = new HDTV(60, “Sony”);
if (tv1.compareTo(tv2) > 0) {
System.out.println(tv1.getBrand() + “ is better.”);
} else {
System.out.println(tv2.getBrand() + “ is better.”);
}
}}
Бывают случаи, когда не стоит изменять класс и делать его сопоставимым.
class SizeComparator implements Comparator<HDTV> {
@Override
public int compare(HDTV tv1, HDTV tv2) {
int tv1Size = tv1.getSize();
int tv2Size = tv2.getSize();
if (tv1Size > tv2Size) {
return 1;
} else if (tv1Size < tv2Size) {
return -1;
} else {
return 0;
}
}}
public class Main {
public static void main(String[] args) {
HDTV tv1 = new HDTV(55, “Samsung”);
HDTV tv2 = new HDTV(60, “Sony”);
HDTV tv3 = new HDTV(42, “Panasonic”);
ArrayList<HDTV> al = new ArrayList<HDTV>();
al.add(tv1);
al.add(tv2);
al.add(tv3);
Collections.sort(al, new SizeComparator());
for (HDTV a : al) {
System.out.println(a.getBrand());
}
}}
Коллекции
Коллекцией называется любая структура данных, в которой хранятся и итерируются объекты. Структуры данных — это довольно объемная тема. О ней поговорим в другой раз. Сейчас же ограничимся общим представлением о структурах данных в библиотеках коллекций Java.
- Коллекция представляет собой интерфейс для расширения списков, множеств и очередей.
- Класс Collections содержит статические служебные методы.
- Для работы со структурами данных, объем которых известен заранее, лучше брать Array. Он работает быстрее, чем ArrayList или Vector. Массивы не могут «разрастаться» как списки.
- ArrayList и Vector — это специальные структуры данных, которые используют Array и ряд полезных методов: add(), remove() и т.д. Поэтому такой массив может разрастаться и сокращаться при необходимости.
- ArrayList поддерживает индексированный поиск с методами indexOf() и lastIndexOf().
- Vector синхронизируется и является потокобезопасным. Тем не менее, для синхронизации лучше брать ArrayList и код ниже:
List mylist = Collections.synchronizedList(mylist);
// Single lock for the entire list
- Интерфейс Iterator используется для циклического прохода по всей коллекции в прямом направлении.
- ListIterator расширяет Iterator и поддерживает двунаправленное прохождение.
Классы Collection являются так называемыми «fail first», то есть при изменении значения одного потока в процессе движения по другому, выбрасывается ConcurentModificationException. Это исключение распространяется и на SynchronizedList с SynchronizedMap, т.к. они являются условно потокобезопасными. Это означает, что потокобезопасными считаются лишь отдельные операции, а не весь процесс. Поэтому смело берите блок Synchronize, ConcurentHashMap, или CopyOnWriteArrayList. ConcurentHaspMap, CopyOnWriteArrayList и CopyOnWriteArraySet считаются потокобезопасными и доступными для синхронизации.
- HashMap работает по принципу хеширования (Hashing).
- Простейшая форма хеширования — это некий способ присвоения уникального кода для любой переменной или объекта после применения формул или алгоритмов к их свойствам.
- В HashMap присутствует внутренний класс Entry для хранения ключа и преобразованного значения.
- MapReduce содержит два ключевых компонента — Map и Reduce. Функция Map применяется к набору входных значений для вычисления пары «ключ/значение». Reduce берет эти значения и применяет к ним еще одну функцию.
Рекомендуется использовать Collections.EMPTY_LIST/EMPTY_SET вместо null. Пример:
List testList = Collections. EMPTY_LIST;
- А лучше всего в качестве ключей в Map брать неизменяемые объекты.
Arrays.asList(T… a) возвращает java.util.Arrays.ArrayList, а не java.util.ArrayList. Это представление исходного массива Array.
Сравнение ArrayList
Collection<String> listOne = new ArrayList(Arrays.asList(“a”,”b”, “c”,”g”));
Collection<String> listTwo = new ArrayList(Arrays.asList(“a”,”b”,”d”, “e”));
List<String> sourceList = new ArrayList<String>(listOne);
List<String> destinationList = new ArrayList<String>(listTwo);
sourceList.removeAll( listTwo ); // Result: [c, g]
destinationList.removeAll( listOne ); // Result: [d, e]
Различия интерфейсов ITERATOR и ENUMERATION: В Iterator есть дополнительный метод remove().К тому же, он позволяет вызывающему оператору удалять элементы из основной коллекции при итерации по определенной семантике.
Вопрос: Как сделать reverse объектов TreeMap?
С помощью Collections.reverseOrder()
Map tree = new TreeMap(Collections.reverseOrder());
Вопрос: Можно ли добавлять гетерогенные объекты в TreeMap?
Нет. Упорядоченные коллекции не допускают включения гетерогенных элементов, т.к. их нельзя сравнить. На деле ничего страшного нет — сопоставимость реализована в их классах.
Вопрос: Есть ли разница между int[] x и int x[]?
Между ними нет разницы. Оба способа отлично подходят для объявления массива.
Иерархия затрат памяти:
- Contains() использует линейный поиск.
- HashMap разрешает один нулевой (null) ключ и несколько нулевых значений. В HashTable, наоборот, нулевые значения запрещены.
- LinkedHashMap отлично предотвращает конфликты, например, в кэше LRU.
- ConcurentHashMap — лучше HashTable.
- Сортируйте HashMap по ключам и значениям.
Guava
Guava — это одна из основных Java-библиотек от Google с добавленными классами и интерфейсами коллекций.
- Добавлены MultiSet и UniqueList. В обычном списке (List) элементы упорядочены, повторяющиеся значения разрешены. В UniqueList список упорядочен, однако повторяющиеся значения запрещены. Обычный набор данных (Set) — повторяющиеся значения запрещены, элементы не упорядочены. В MultiSet элементы также не упорядочены, но повторы разрешены.
- Добавлены ImmutableList, ImmutableSet, ImmutableSortedSet и ImmutableMap.
- Еще добавлены ImmutableMultiSet, HashMultiSet, LinkedHashMultiSet, TreeMultiSet, EnumMultiSet и MultiMap.
- В MultiMap поддерживаются отношения «ключ-значение» и «многие-ко-многим». В стандартном Java присутствует только отношение «один-ко-многим».
История версий Java
После базовых представлений о языке имеет смысл бегло пробежаться по истории и эволюции Java.
- 1.4: Добавлены системы входа/выхода (I/O), масштабируемость. Улучшена производительность.
- 1.5: Добавлены обобщения (generic) — коллекции, Iterator и т.д. Также добавлены циклы, автоупаковка/распаковка, аннотации, перечисления (enum) и статический импорт.
- 1.6: Полная поддержка Windows Vista. Более быстродействующая система. Улучшения по части компиляции. Добавлена XML-обработка веб-сервисов.
- 1.7: Добавлен объект String в операторе switch, несколько исключений в одном блоке catch и т.д.
- 1.8:
Лямбда-выражения: Это метод без объявления, который возвращает имя и значение. Позволяет не тратить время на объявление и написание отдельного метода для содержащего класса. Пример: модификатор доступа.
Функции рассматриваются как аргумент метода, а код — как некие данные.
MathOperation addition = (int a, int b) -> a + b;
addition(a,b);
Интерфейсы даты и времени: Текущее время представлено классом Clock. Это абстрактный класс, поэтому экземпляры класса в нем не создаются. Статический метод systemUTC() возвращает текущее время.
import javax.time.Clock;
Clock clock = Clock.systemUTC();
Clock clock = Clock.systemDefaultZone();
ZoneId zone = ZoneId.of(“Europe/Berlin”);
// ZoneId zone = ZoneId.systemDefault(); you can also use this
Clock clock = Clock.system(zone);
import javax.time.LocalDate;
LocalDate date = LocalDate.now();
Стрим (stream)— это «одноразовый» объект. После прохождения объекта повторить это действие невозможно. По мере прохождения стримы можно фильтровать или выполнять операции Map/Reduce. Пример последовательного стрима:
List <Person> people = list.getStream.collect(Collectors.toList());
Using a parallel stream:
List <Person> people = list.getStream.parallel().collect(Collectors.toList());
Перевод статьи Madhu Pathy : A Beginner’s Guide to Java: Part 2 of 4