Руководство по управлению памятью Java (и спасению вашего кода)

Примечание переводчика: желание перевести заметку появилось ранним июньским утром после прочтения ее в полусонном состоянии в вагоне метро. Целевая аудитория: люди, делающие первые шаги в мире Java и по роду своего основного технического бэкграунда или желания сильно жаждут забраться под капот Java и изучить все «электродинамические» процессы. Уверен, что для прочитавших это будет отправной точкой путешествия в мир настройки JVM и GC. Попутного ветра!
Оригинал статьи лежит здесь


Будучи разработчиком, вы проводите бесчисленные часы за вычищением багов из Java-приложения и достижением производительности в тех местах где нужно. Во время тестирования замечаете, что приложение постепенно работает медленней, и в конце полностью заваливается или просто демонстрирует плохую производительность. В конечном счете признаете, что происходят утечки памяти. Garbage collector Java делает всё что может, чтобы справиться с этими утечками. Но есть только много вещей, которые можно выполнить, когда сталкиваетесь с ситуациями подобными этим. Вам нужны способы идентификации вызовов утечек памяти, выявления причин и понимания роли Java garbage collector'а влияющего на общую производительность приложения.

Задачи окружающего мира решаемые Java

Прим. автора поста: Перевод местами сделан не совсем гладко. В попытке сгладить шероховатости, были сделаны вольные дополнения, которые нисколько не портят смысл статьи. Оригинал статьи вот здесь
Целевая аудитория статьи — люди, которые колеблются пуститься в плавание в мире Java.


Редакция dzone.com побеседовала с 11 профессионалами в бизнесе, плотно работающих большую часть, если не больше, свой карьеры в экосистеме Java. Спросили их о задачах в реальном мире, которые решаются Java.
  • ,

Double brace инициализация

1. Double brace инициализация


Перевод статьи What is Double Brace Initialization in Java? Anti Pattern Example (опубликованной в октябре 2015 года).

Инициализация с использованием двойных фигурных скобок (далее по тексту — Double brace инициализация) – Java средство для создания коллекций таких как list, set и map одновременно с их объявлением.
Когда вам необходимы списки с фиксированными элементами, такие как перечень поддерживаемых продуктов или денежных единиц, то объявление списка одновременно с его инициализацией улучшает читабельность кода. Именно поэтому Double brace инициализация набирает популярность, так как иных стандартных способов создания с одновременной инициализацией в коде для коллекций нет.
К сожалению, в отличие от других языков программирования Java не поддерживает коллекции литералов. В соответствие с данным ограничением, создание неизменяемого списка (unmodifiableList) даже с небольшим количеством элементов вынуждает нас писать много строк кода в виде повторяющихся вызовов add() для добавления нужных элементов с завершающим обертыванием (wrapping):
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(5);
list.add(7);        
List<Integer> unmodifiableList = Collections.unmodifiableList(list);


Это излишне избыточное описание, которое можо упростить. Заполним статические списки удобным для нас способом, а именно:
непосредственно в статических блоках при инициализации, в чем нам и поможет Double brace инициализация, позволяющая нам записать все в одну строку:
List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>() {{
        add(2);
        add(3);
        add(5);
}});


Аналогично Double brace инициализация поможет нам заполнить значениями и HashMap:
Map<Integer, String> intToString = new HashMap<Integer, String>(){{
         put(1, "one");
         put(2, "two");
         put(3, "three");         
 }};


Все это так элегантно выглядит, однако имеет свои недостатки, которые и делают Double brace инициализацию анти-шаблоном. Рассмотрим их далее в следующей главе.

Плюсы и минусы Double brace инициализации.


Double brace инициализация использует создание внутреннего анонимного класса (anonymous inner class). Что поначалу внешне скрыто, однако Double brace инициализация создает класс с дальнейшей инициализацией его экземпляра, каждый раз как вы применяете ее. Помимо этого используется скрытая ссылка на этот закрытый класс, что может привести нас к возможным утечкам памяти. Вы также не сможете использовать оператор привидения для generic-ов (diamond operator < >), так как мы не можем обратиться внутрь анонимного класса.
(От переводчика: Еще раз более подробно:
после первой { создается внутренний анонимный класс,
после второй { происходит инициализация при создании экземпляра класса при которой мы имеем доступ к полям и методам внешнего (по отношению к анонимному) класса.)


Плюсы:
  1. Уменьшение строк в коде
  2. Создание и инициализация в одном выражении.
Минусы:
  1. Скрытое от вас создание анонимного класса.
  2. Что стоит нам дополнительных расходов для его экземпляра каждый раз при использовании.
  3. Каждый раз создается скрытая ссылка на него, которая возможно приведет к утечкам памяти.

Вердикт:
Из-за вышеуказанных минусов и существованию альтернатив Double brace инициализация рассматривается как анти-шаблон в мире Java.
Спасите котенка

Альтернативы Double brace инициализации в Java.


Хорошая новость состоит в том, что имеются иные способы достижения тех же самых целей в Java. Мы может реализовать в одной строке кода создание и инициализацию значениями ArrayList используя Copy constructor из Collection класса как показано ниже:
List<Integer> list = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(2, 3, 5)));


Arrays.asList() возвратит нам список фиксированной длины переданный в ArrayList copy constructor. Помните про различие между списками фиксированной длины возвращаемыми из Arrays.asList() и Collections.unmodifiableList():
Вы не можете добавлять или удалять элементы ArrayList-а, однако вы можете изменить элемент по индексу с использованием set(), чего вам не удастся сделать со списком, возвращенным Collections.unmodifiableList().
Если вы хотите получить небольшой список – это лучший способ, хотя он будет менее прозрачным для Set и других коллекций, так придется создать List перед созданием Set-а. Но это все же лучше, нежели Double brace инициализация, так как в данном случае не создается лишний внутренний анонимный класс при каждом подобном использовании.

Если вы работаете под Java 8 у вас есть еще один альтернативный способ. JDK 8 Stream API поможет вам создавать небольшие коллекции комбинируя вывод Stream Factory методы в коллекцию List:
List<String> list = Collections.unmodifiableList(Stream.of("abc", "bcd", "cde").collect(toList()));


Для Set вы можете использовать Collectors.toSet() метод вместо Collectors.toList(), как показано ниже:
Set<String> set = Collections.unmodifiableSet(Stream.of("abc", "bcd", "cde").collect(toSet()));


Кстати, Stream collect методы не гарантируют, что сгенерированные ими коллекции защищены от изменений. В Java 8 коллекции, которые они вернули (такие как — ArrayList, HashSet, and HashMap ), вполне себе обычные ( мы можем менять их), но это данный факт возможно будет исправлен в будущих релизах JDK.

Это все на данный момент о Double brace инициализации в Java. Этот шаблон приемлим для тестов и демонстраций, но не достаточно хорош для использования в промышленной эксплуатации (production). Из-за присущих ему минусов, Double brace инициализация стал анти-шаблоном в наши дни, особенно учитывая доступные альтернативы. Сам я все еще использую данную конструкцию для инициализации static map-ов и все. Для List я предпочитаю создавать Collections комбинируя с созданием Array.asList в его конструкторе. А если использую Java 8 – конструкцию с использованием Stream API и collect().

Статьи по теме:
Если вам понравился данный учебный материал и вы хотите узнать побольше о паттернах, принципах и лучших приемах программирования на Java, возможно вы также ознакомитесь и с другими статьями на нашем сайте.

Рекомендуемая литература:
Если вы хотите побольше узнать о шаблонах и лучших практиках вы должны прочитать книгу «Эффективное программирование» Джошуа Блоха, ни одна книга не может занять ее место.
А если вы уже поднаторели в Java и ищете книгу по шаблонам проектирования, юморной стиль изложения которой интересно и легко читать – обратите внимание на «Нead First. Шаблоны проектирования».
От переводчика: Я умышленно привел ссылку на оригинальную книгу Блоха, так как перевод ее на русский неудачен (например, Builder=конструктор).
  • ,

Объектно-ориентированное программирование (перевод статьи)

Автор: Mohit Kanwar
9 ноября, 2015. Java Zone.
Оригинал статьи

От переводчика:
К сожалению, у меня нет сколько-нибудь значительного опыта перевода с английского, хотя я и довольно много читаю на английском. Но выяснилось, что читать и переводить – разные вещи. Также, к сожалению, у меня нет значительного опыта в программировании (недавно только сделал простейшее веб-приложение на Spring MVC и Hibernate). Поэтому перевод получился значительно хуже, чем мог бы быть. Я взял на себя смелость несколько подкорректировать примеры кода, которые даются в статье, так как они не соответствуют соглашениям по наименованию (code conventions) в Java. Возможно, не стоило переводить название некоторых паттернов (такой перевод мало что дает для понимания), но я посчитал, что это меньшее зло. Отдельно стоит сказать о «высокой сцепленности» как переводе «high cohesion». Согласен, не самый лучший перевод. Но «сильная связность» — это «high coupling» (другое важное понятие), а «когерентность» здесь вряд ли подойдет. Я открыт для критики и с признательностью приму замечания по статье в любой форме.


Объектно-ориентированное программирование – это стиль программирования, в котором программа состоит из компонентов, соответствующих объектам реального мира

Любой реальный объект имеет какие-то свойства (которые могут изменяться или нет с течением времени) и поведение (которое может меняться или нет в зависимости от других условий). Например, карандаш – это объект реального мира, который имеет следующие свойства:
  • Он красный (это не меняется с течением времени).
  • Он 10 сантиметров в длину сейчас (это может меняться, если карандаш заточить).
И он имеет следующее поведение:
  • Он оставляет след, если его использовать правильно.
  • След может отличаться в зависимости от давления (зависит от внешних факторов).
  • Его длина сокращается, если его затачивать (постоянное поведение).
Как в этом примере, объекты реального мира могут иметь много свойств, но при написании программ мы принимаем во внимание только необходимые свойства.
Объектно-ориентированное программирование имеет свои преимущества. Например, оно облегчает установление связи между объектом реального мира и программой так, как это ожидается. Это реально помогает по мере того, как приложение растет и множество объектов взаимодействуют друг с другом. Это помогает в распределении ответственности внутри объективного мира, позволяя сфокусироваться на продумывании приложения.
Другая важная особенность, ассоциируемая с ООП (Объектно-ориентированным программированием), — это классификация объектов. Так как мир (реальный/виртуальный) полон объектов, сложно управлять по отдельности. Нам нужен способ классификации этих объектов, который поможет нам связать различные объекты и их свойства, например, черный карандаш. Он был бы неотличим (тот же самый?), если бы использовался в предыдущем примере, но это другой объект. Но так как это оба карандаши, они принадлежат одному и тому же классу «Карандаш». Тогда как ручка, которая очень похожа на карандаш, принадлежит к другому классу. Тем не менее, ручка и карандаш оба являются «Пишущими инструментами».

Объектно-ориентированное программирование имеет следующие принципы:

Абстракция
Абстракция определяется как характерная черта (quality) взаимодействия с идеями, а не событиями или, другими словами, свобода от репрезентирующих качеств. Это позволяет программистам сфокусироваться на том, что программировать, а не как. Абстракцию можно рассматривать в качестве соглашения, посредством которого мы предоставляем функциональность. Детали реализации могут быть скрыты, если использовать этот концепт.
Например, если нам нужен класс, который пишет, то мы должны быть уверены, что обладает методов «писать»
abstract class writer {
	write ();
}

Что мы сделали? Мы разработали класс высокого уровня, являющийся абстрактными, иными словами, он знает, что за функциональность нам нужна, но как ее реализовать – это за пределами видимости (out of scope) данного класса.
Это дает много преимуществ:
  • Мы раскрываем минимум информации необходимой внешним сущностям, это позволяет сосредоточиться на продумывании программы (this enable focused thinking), избежать путаницы и не давать непреднамеренных обещаний.
  • Мы оставляем место для улучшений в будущем, которые были бы невозможны, если детали реализации были раскрыты.

Наследование
«Наследование» в общеупотребительном английском означает «приобретать и передавать дальше». Это слова существует в нашей культуре очень давно. Предки приобретали землю с помощью тяжелой работы и передавали ее своим детям, даже природа благоволит наследованию. Все свойства тела, например, рост, цвет кожи/глаз/волос и т.д. зависят от генов, наследуем нами от наших родителей. Наследование предотвращает изобретение колеса заново и ускоряет прогресс. То же самое и в ООП. Мы создаем родительский класс с несколькими базовыми свойствами/поведением. Все классы, наследуемые от этого родителя, будут содержать такие же свойства/поведение, что и их родитель. Тем не менее, наследуемые классы могут получить больше свойств/поведения или изменить реализацию поведения.
class WritingInstrument {
	colour;
	write() {
}
}
class Pen (child of parent) {
	inkcolour;
}

В примере сверху класс-родитель (WritingInstrument) имеет свойство «цвет» и поведение «писать». Когда класс-наследник (ручка) объявляется, повторное объявление свойства «цвет» и поведения «писать» не требуется. Они присутствуют в классе «ручка» в силу наследования. Однако класс-наследник может объявить собственные дополнительные свойства/поведение.
Как мы можем использовать это на практике? Мы, разработчики, очень ленивы. Мы не хотим печатать что-то снова и снова. Существование множества копий одного и того же кода не приветствуется в силу следующих соображений:
  • Чем меньше копий кода, тем легче его сопровождать.
  • Если нет множества копий кода, то изменение в одном месте становится видимым везде.
  • Чем меньше кода – тем меньше ошибок.
  • Если один код используется во многих местах, то так достигается обобщение.
  • Мы фокусируемся на написании кода.
  • Мы фокусируемся на тестах.
Наследование в Java достигается с помощью ключевых слов «extends» и «implements».
class WritingInstrument {
}
class Pen extends WritingInstrument {
}


Полиморфизм
Слова «полиморфизм» произошло от двух слов:
«Поли», т.е. «множество» / «больше, чем один»
«морф», т.е. «форма»
Буквально, слово «полиморфизм» отсылает к способности объектов вести себя различным образом в зависимости от условий.
В программировании полиморфизм может быть воплощен в нескольких местах:
  • Классы
  • Методы
  • Операторы
Все, перечисленное выше, может вести себя различным образом в зависимости от условий, возможно, от контекста, в которых они используются. Это полезно, так как клиенту (программисту, использующему ваши библиотеки) не нужно знать множество тонкостей, и желаемая функциональность реализуется посредством отбора необходимой информации из контекста.
Class WritingObject {
	wrire() {
	// пишем, используя стандартные (по дефолту) цвета
	}
}

class Pencil extends WritingObject {
	write() {
	// пишем, используя серый цвет, написанный текст можно стереть
	}
}

class Pen extends WritingObject {
	write() {
	// пишем, используя голубой цвет, написанный текст нельзя стереть
	}
}

class Main {
	main() {
		WritingObject wr = new WritingObject();
		wr.write(); // первый вызов
		WritingObject wr = new Pen();
		wr.write(); // второй вызов
		WritingObject wr2 = new Pencil();
		wr2.write(); // третий вызов 
	}
}

В примере выше имеет реализация по умолчанию в WritingObject, которая расширена/переопределена классами-наследниками перо и ручка. Метод write() вызван три раза в классе Main. Каждый раз вызывается различная реализация в зависимости от того, у какого объекта вызывается этот метод. В данном случае метод write() имеет множество типов поведения, так как он полиморфичен.

Инкапсуляция
Инкапсуляция определяется как сбор связанных данный/функциональностей в одном модуле (unit). Это помогает в облегчении доступа/модификации данных. Например, если нам нужно напечатать все свойства, которыми данный пользователь обладает, мы имеем следующие опции:
printUserProperties(userName, userId, firstname, lastname, email, phone, … … ….)

Мы создали метод, который принимает все свойства и печатает их друг за другом. С увеличением количества элементов в списке пропадет возможность идентифицировать корректные поля, а добавление/удаление одного поля изменит сигнатуру метода. Поэтому нам нужно заменить всех пользователей этого метода, если даже недавно добавленные поля им не нужны. Чтобы сделать код более читаемым и упростить будущие модификации проще, мы инкапсулируем свойства в классе и превращаем его в коллективный объект (collective object)
class User {
	userName
	userId
	firstname
	lastname
email
	phone
	..
	..
	..
}
printUserProperties(user) {}

Объект – это система (software bundle) переменных и связанных методов. Вы можете представить объекты реального мира, используя объекты программы. Вы можете представить реальных собак в анимационной программе или реальный велосипед как программный объект внутри велотренажера.
В ООП класс – расширяемый шаблон (program-code-template) для создания объектов, обеспечения их начальным состоянием (переменные) и реализацией поведения (функции, методы).
Аббревиатура SOLID была введена Michael Feather’ом для «первых пяти принципов», названных так Robert C. Martin’ом в начале 2000-х. Целью принципов, реализуемых совместно, является увеличение вероятности того, что программист создаст систему, которую легко будет поддерживать и расширять. Принципы SOLID – ориентиры в разработке программ, которые необходимы для удаления «протухшего» кода посредством рефакторинга, в результате которого код должен стать легко читаемым и расширяемым. Это часть стратегии agile and adaptive programming (гибкого и адаптирующегося программирования).

Принцип единой ответственности (Single Responsibility Principle)
В ООП принцип единой ответственности гласит, что каждый класс должен быть ответственен за одну часть функциональности, обеспечиваемой программой, и что ответственность должна быть полностью инкапсулирована этим классом. Вся его функциональность должна быть тесно связана с этой ответственностью.

Принцип открытости/закрытости (Open/Closed Principle)
В ООП принцип открытости/закрытости гласит «сущности программного обеспечения (классы, модули, методы и т.д.) должны быть открыты для расширения, но закрыты для изменения». Иными словами, сущность должна позволять расширять ее поведение без изменения исходного кода.

Принцип подстановки Лисковой (Liskov Substitution Principle)
Возможность подстановки (Substituability) – это принцип в ООП. Он гласит, что если S в компьютерной программе является подтипом T, то объекты типа T должны быть такими, чтобы их можно было заменить объектами типа S (т.е. объекта типа S могут заменить объекты типа T) без изменения каких-либо требуемых свойств программы (точность, выполнение задачи и т.д.).

Принцип разделения интерфейса (Interface Segregation Principle)
Принцип разделения интерфейса гласит, что программист-клиент не должен быть принужден зависеть от методов, которые он не использует. Согласно этому принципу нужно разделять большие интерфейсы на маленькие и более специфичные, чтобы программист-клиент знал только о методах, которые ему интересны. Целью принципа разделения интерфейса является сохранение системы в несвязанном состоянии (system decoupled), что облегчит рефакторинг, внесение изменений и повторное развертывание (redeploy).

Принцип инверсии зависимостей (Dependency Inversion Principle)
В ООП принцип инверсии зависимости означает специфическую форму несвязности программных модулей. При следовании этому принципу стандартные отношения зависимости, установленные от модулей высокого уровня, формирующих архитектуру приложения (policy-setting) к зависимым модулям низкого уровня инвертированы (обращены), поэтому измененные модули высокого уровня становятся независимы от деталей реализации модулей низкого уровня. Этот принцип утверждает:
  • Модули высокого уровня не должны зависеть от модулей низкого уровня. Модули обоих типов должны зависеть от абстракций.
  • Абстракции не должны зависеть от деталей реализации. Детали должны зависеть от абстракций.
Принцип обращает (inverts) путь, согласно которому люди могут думать об объектно-ориентированном дизайне, утверждая, что объекты высокого и низкого уровней должны зависеть от одних и тех же абстракций.

Принципы GRASP

Паттерны (принципы), используемые для решения общих задач по назначению обязанностей классам и объектам (General Responsibility Assignment Software Patterns (GRASP)) содержат руководства (guidelines) по назначению ответственности классам и объектам в объектно-ориентированном дизайне.

Контроллер (Controller)
Паттерн Контроллер назначает ответственность за взаимодействие с системными событиями классов без графического интерфейса, которые представляют всю систему или use case scenario (сценарии вариантов использования).
Контроллер:
  • Это не взаимодействующий напрямую с пользователем объект, ответственный за получение и реакцию на системные события.
  • Должен использоваться так, чтобы иметь дело со всем системными событиями одного (или множества взаимосвязанных) use cases.
  • Это первый объект за графическим интерфейсом, который контролирует системные операции.
  • Он не должен делать работу сам, его задача – контроль над потоком событий.

Создатель (Creator)
Задача класса-создателя – создание и инициация объектов для последующего использования. Он знает, параметры инициализации, а также какой объект будет создан. Иногда класс-создатель создает объекты активно и помещает их в кэш, и обеспечивает один экземпляр, когда он нужен.

Высокая сцепленность (High Cohesion)
Высокая сцепленность – оценочный паттерн, целью применения которого является сохранение объектов в таком состоянии, чтобы они были нацелены на выполнение одной четкой задачи, легко управляемы и понимаемы. Высокая сцепленность обычно используется для поддержки слабой связности (Low Coupling). Высокая связность означает, что ответственность данного элемента четко обозначена (strongly related and highly focused). Разбиение программы на классы и подсистемы – пример действий, что повышает сцепленность свойств системы. Слабая сцепленность, наоборот, — ситуация, в которой элемент имеет слишком много несвязанных задач. Элементы со слабой сцепленностью обычно отличаются тем, что их сложно понять, сложно повторно использовать, поддерживать и изменять.

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

Информационный эксперт (Information Expert)
Информационный эксперт (также Эксперт или принцип Эксперта) – принцип, используемый для определения того, кому делегировать ответственность. Ответственность включает методы, вычисляемые поля и т.д. При использовании данного принципа при назначении ответственности главным подходом является следующая последовательность действий: анализ ответственности, определение информации, которая нужна для ее исполнения, наконец, установление того, где эта информация находится. Использование принципа Информационный эксперт приводит к назначению ответственности классу, который имеет наибольшее количество информации для ее исполнения.

Слабая связность (Low Coupling)
Слабая связность – оценочный паттерн, который указывает, как назначать ответственность: слабая зависимость между классами, изменение одного должно иметь минимальные последствия для другого, максимальная возможность повторного использования.

Полиморфизм (Polymorphism)
В соответствии с полиморфизмом изменения поведения основывается на типе объекта, на который ссылается переменная (responsibility of defining the variation of behaviors based on type is assigned to the types for which this variation happens). Это достигается использованием полиморфных операций.

Защищенные изменения (Protected Variations)
Паттерн Защищенные изменения защищает элементы от изменений других элементов (объектов, систем, подсистем) путем обертывания центра нестабильности (the focus of instability) интерфейсом и использования полиморфизма для создания различных реализаций данного интерфейса.

Чистое конструирование (Pure Fabrication)
Чистое конструирование предполагает класс, не представляющий концепт в проблемной области (the problem domain) и созданный специально для достижения слабой связности, высокой сцепленности и, следовательно, максимального потенциала повторного использования (решение, предлагаемое паттерном Информационный эксперт этого не обеспечивает). Такая класс обычно называется “Service” в Предметно-ориентированном дизайне (Domain-driven design).

Критика

Исследования Potok’a и др. показали отсутствие существенных различий между ООП и процедурными подходами.
Критическое сравнение ООП с другими технологиями, в особенности реляционными, затруднено в силу отсутствия определения ООП, которое было бы строгим и широко принятым (Christopher J. Date)
В сравнении с другими языками (LISP диалекты, функциональные языка и т.д.) ООП языки не имеют уникального преимущества и навязывают ненужную сложность. (Lawrence Krubner)
Я нахожу объектно-ориентированное программирование технически неосновательным. Оно пытается разложить мир на части в терминах интерфейсов, которые изменяются в пределах одного типа. Чтобы иметь дело с реальными проблемами, вам нужны многосортные алгебры — семейства интерфейсов, которые простираются на многие типы. Я нахожу объектно-ориентированное программирование философски нездоровым. Оно утверждает, что всё является объектом. Даже если это так, это не очень интересно: сказать, что всё является объектом — значит, не сказать вообще ничего. (Александр Степанов)
Популярность ООП среди больших компаний связана с «большими (и часто меняющимися) группами посредственных программистов». Дисциплина, навязываемая ООП, предотвращает нанесение программистом «слишком большого вреда». (Paul Graham)
Объектно-ориентированное программирование ставит существительные первыми и самыми главными. Зачем идти на такие крайние меры и ставить одну часть речи на пьедестал? Почему один концепт получает преимущество над другим? Это невозможно, чтобы ООП внезапно сделало глаголы менее важными для нашего мышления. Это странным образом перекошенная перспектива. (Steve Yegge)
Rick Hickey, создатель Clojure, описывал объектные системы как крайне упрощенные модели реального мира. Он подчеркивал неспособность ООП моделирования времени правильно, что создает огромные проблемы, когда в программах большое распространение получает многопоточность.
Eric S. Raymond, Unix-программист и сторонник программного обеспечения с открытым кодом, был критически настроен в отношении заявления, что ООП является «Единственно верным решением», и писал, что ООП поощряет многослойные программы, что препятствует понятности (transparency). В качестве противоположного подхода Raymond приводил пример Unix и С.

Ссылки

By Margaret Rouse @ WhatIs.com
Wikipedia! (Русский вариант)
inheritance is polymorphism
SOLID (Object Oriented Design) (Русский вариант)
Single Responsibility PrincipleArguments against OOPS (Русский вариант)
What is OOPS (without the hype)

Перевод: Варыгин Д.В.
  • ,

Проектирование Классов и Интерфейсов (Перевод статьи)

Исходная статья: www.javacodegeeks.com/2015/09/how-to-design-classes-and-interfaces.html

Эта статья является частью нашего Курса Академии под названием «Продвинутая Java».
Этот курс призван помочь вам наиболее эффективно использовать Java. В нем обсуждаются передовые темы, такие как создание объекта, взаимосовместимость(параллелизм), преобразование в последовательную форму, отражение и многое другое. Это будет вашим путеводителем в вашем путешествии к мастерству в Java. Убедись сам!


Содержание

  1. Введение
  2. Интерфейсы
  3. Интерфейс-маркеры
  4. Функциональные интерфейсы, статические методы и методы по умолчанию
  5. Абстрактные классы
  6. Неизменяемые (постоянные) классы
  7. Анонимные классы
  8. Видимость
  9. Наследование
  10. Множественное наследование
  11. Наследование и композиция
  12. Инкапсуляция
  13. Final классы и методы
  14. Что дальше
  15. Скачать исходный код

  • ,

Как пользоваться CopyOnWriteArraySet в Java с примером (перевод)

CopyOnWriteArraySet это младший брат класса CopyOnWriteArrayList. Это специализированный набор классов, добавленных в JDK 1.5 вместе с их более популярным двоюродным братом ConcurrentHashMap. Они являются частью concurrent collection framework и расположены в пакете java.util.concurrent.

CopyOnWriteArraySet лучше всего подходит для read-only коллекций, размер которых достаточно мал, чтобы их скопировать, если произойдут некоторые изменяющие операции. Например, вы можете использовать CopyOnWriteArraySet для хранения объекта при запуске приложения, и дать множеству других потоков доступ к этому объекту на протяжении жизненного цикла приложения. Если новое состояние или объект приходят в течение этого времени, он также может быть добавлен в этот Set, со стоимостью создания нового массива.

Одна из самых важных вещей, которую стоит знать о CopyOnWriteArraySet это то, что он реализован при помощи CopyOnWriteArrayList. Это означает, что CopyOnWriteArraySet также разделяет все основные свойства CopyOnWriteArrayList. Еще одна важная вещь, которую стоит запомнить: итераторы этого класса коллекции не поддерживают операцию remove(). Попытка удалить элемент во время итерирования приведет к выбросу UnsupportedOperationException. Это сделано чтобы гарантировать скорость во время обхода. Обход этой реализации Set, используя итератор, осуществляется достаточно быстро, и во время него исключены вмешательства других потоков. В своей работе итераторы опираются на моментальный снимок массива, который был сделан во время создания итератора.

Короче говоря, используйте CopyOnWriteArraySet если set достаточно мал, чтобы копировать его при добавлении, задании значения или удалении объектов, и основной целью является чтение обновляемых от случая к случаю данных. Кроме того, если вы хотите удалить элементы во время итерации, не используйте эту реализацию, потому что ее итератор не поддерживает remove(), и бросает java.lang.UnsupportedOperationException, как показано ниже:


[RAJ] Event received : FOUR 
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(Unknown Source)
    at Publisher.notifySubs(HelloHP.java:43)
    at HelloHP.main(HelloHP.java:23)


CopyOnWriteArraySet пример на Java

Вот готовая программа на Java показывающая, как использовать CopyOnWriteArraySet. В нашем примере мы использовали шаблон проектирования «Издатель-подписчик» (англ. publisher-subscriber pattern), чтобы продемонстрировать его использование. Большинство подписчиков подписаны во время пуска приложения и главной задачей издателя является их перебор и уведомление о каких-либо обновлениях. Время от времени может случаться добавление и удаление подписчика. Так как нам нужен быстрый обход, CopyOnWriteArraySet является хорошим выбором, особенно в многопоточной среде, где один поток может добавить абонента, а другой поток обрабатывает обновления.


import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Java program to demonstrate how to use CopyOnWriteArraySet in Java. Remember,
 * CopyOnWriteArraySet doesn't support remove() operation.
 *
 * @author Javin Paul
 */
public class CopyOnWriteArraySetDemo{

    public static void main(String args[]) {
        Publisher cricNext = new Publisher();

        SubScriber raj = new SubScriber("RAJ");
        SubScriber adom = new SubScriber("ADOM");

        cricNext.addSubscriber(raj);
        cricNext.addSubscriber(adom);

        cricNext.notifySubs("FOUR");
        cricNext.notifySubs("SIX");

    }

}

class Publisher {

    private CopyOnWriteArraySet setOfSubs = new CopyOnWriteArraySet();

    public void addSubscriber(SubScriber sub) {
        setOfSubs.add(sub);
    }

    public void notifySubs(String score) {
        Iterator itr = setOfSubs.iterator();
        while (itr.hasNext()) {
            SubScriber sub = itr.next();
            sub.receive(score);

            //itr.remove(); // not allowed, throws UnsupportedOperationException
        }
    }
}

class SubScriber {

    private String _name;

    public SubScriber(String name) {
        this._name = name;
    }

    public void receive(String score) {
        System.out.printf("[%s] Event received : %s %n", _name, score);
    }
}


Output:
[RAJ] Event received : FOUR 
[ADOM] Event received : FOUR 
[RAJ] Event received : SIX
[ADOM]Event received : SIX


Что нужно запомнить

CopyOnWriteArraySet реализует интерфейсы Collection и Set, а также, добавленный в JDK 1.5, вместе с другой специальной реализацией Set'а, EnumSet. Это также Set, который использует внутренний CopyOnWriteArrayList для всех своих операций. Таким образом, он разделяет те же основные свойства этого класса. Так как это не SortedSet, порядок элементов не гарантируется в течение итерации.

1. CopyOnWriteArraySet лучше всего подходит для приложений, в которых:
  • Размеры Set'ов, как правило остаются небольшими.
  • Операции read-only значительно превосходят операции, изменяющие объекты.
  • Вы должны предотвратить помехи между потоками во время обхода Set'а.

2. Еще одним преимуществом CopyOnWriteArraySet является потокобезопасность. Эта коллекция поддерживает параллелизм.
3. Мутативные операции (добавление, изменение, удаление и т.д.) являются дорогостоящими, так как они, как правило, требуют копирования всего базового массива.
4. Итераторы не поддерживают мутативную операцию удаления.
5. Обход используя итератор достаточно быстр и во время него исключены вмешательства других потоков. В своей работе итераторы опираются на моментальный снимок массива, который был сделан во время создания итератора.

На этом все об использовании CopyOnWriteArraySet в Java. Как я сказал, он является младшим братом CopyOnWriteArrayList. Так что если вы понимаете хотя бы один из них, то сможете использовать и другой. Единственное отличие в том, что один является List'ом а другой Set'ом, таким образом это влечет за собой унаследование всех отличий между этими структурами данных в Java. Например, в List важен порядок расположения элементов, и он может содержать дубликаты. В то время как Set является неупорядоченным, но не позволяет дублирование объектов.

Всегда помните, что CopyOnWriteArraySet это специализированный Collection класс. Его стоит использовать только когда условия являются благоприятными. В любом другом случае можно пользоваться реализациями общего назначения. Например, HashSet, LinkedHashSet или синхронизированными классами коллекций.

Оригинал статьи
  • ,

Основы Параллелизма: взаимоблокировки и мониторы объектов (раздел 3) (перевод статьи)

Исходная статья: www.javacodegeeks.com/2015/09/concurrency-fundamentals-deadlocks-and-object-monitors.html
Автор: Martin Mois

Первые две части перевода здесь.

Содержание

1. Живучесть
 1.1 Взаимоблокировка
 1.2 Голодание
2. Мониторы объектов совместно с wait() и notify()
 2.1 Вложенные синхронизированные блоки совместно с wait() и notify()
 2.2 Условия в синхронизированных блоках
3. Проектирование для многонитевости
 3.1 Неизменяемый объект
 3.2 Проектирование API
 3.3 Локальное хранилище нити

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

3.1 Неизменный объект
Одним из правил проектирования, являющихся очень важным в данном контексте, является Неизменяемость. Если вы распределяете экземпляры объектов, например, между различными нитями, то должны уделить внимание тому, чтобы две нити не изменяли один и тот же объект одновременно. В подобных ситуациях легко управляться с немодифицируемыми объектами, поскольку вы не можете их изменить. Вы всегда должны создавать новый экземпляр, когда хотите изменить данные. Базовый класс java.lang.String — пример неизменяемого класса. Вы получаете новый экземпляр всякий раз, когда хотите изменить строку:

String str = "abc";

String substr = str.substring(1);


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

Далее приведён список правил, которые необходимо применять, чтобы сделать класс неизменяемым:
  • ,

Основы Параллелизма: взаимоблокировки и мониторы объектов (разделы 1, 2) (перевод статьи)

Исходная статья: www.javacodegeeks.com/2015/09/concurrency-fundamentals-deadlocks-and-object-monitors.html
Автор: Martin Mois

Эта статься — часть нашего курса Основы Параллелизма в Java.

В этом курсе вы погрузитесь в магию параллелизма. Вы познаете основы параллелизма и параллельного кода, познакомитесь с такими концепциями как атомарность, синхронизация нитебезопасность. Взгляните на него здесь!


Содержание

1. Живучесть
 1.1 Взаимоблокировка
 1.2 Голодание
2. Мониторы объектов совместно с wait() и notify()
 2.1 Вложенные синхронизированные блоки совместно с wait() и notify()
 2.2 Условия в синхронизированных блоках
3. Проектирование для многонитевости
 3.1 Неизменяемый объект
 3.2 Проектирование API
 3.3 Локальное хранилище нити

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

1.1 Взаимная блокировка
Термин взаимоблокировка хорошо известен разработчикам ПО и даже большинство обычных пользователей используют его время от времени, хотя и не всегда в правильном смысле. Строго говоря, этот термин означает, что каждая из двух (или больше) нитей ждут от другой нити, чтобы она освободила заблокированный ею ресурс, в то время как первая сам заблокировала ресурс, доступа к которому ждёт вторая:


Thread 1: locks resource A, waits for resource B

Thread 2: locks resource B, waits for resource A


Для лучшего понимания проблемы взглянем на следующий код:
  • ,

Java. Справочное руководство для интервью - Часть 1. (Перевод статьи)

Исходная статья находится по адресу:
http://www.javacodegeeks.com/2014/02/java-interview-reference-guide-part-1.html

Опубликовал: Nitin Kumar, в Core Java, 3 февраля 2014 г

Java. Объектно-Ориентированная концепция.


Java основывается на объектно-ориентированной концепции, которая дает более высокий уровень абстракции для решения любой проблемы реальным способом.

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

В Java, класс — это документация, шаблон или прототип, который определяет общее поведение объектов того же вида. Экземпляр является реализацией определенного класса, и все экземпляры класса имеют аналогичные свойства, как представлено в описании класса. Например, вы можете определить класс, который называется Дом, с числом комнат, как атрибутом, и создать объекты: дом с 2 комнатами, дом с 3 комнатами и т.д.

Преимущества:

Ниже перечислено несколько преимуществ разработки объектно-ориентированного программного обеспечения:

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

Существуют четыре основных понятия ООП:

  • Инкапсуляция
  • Наследование
  • Полиморфизм
  • Абстракция

Инкапсуляция:

Инкапсуляция представляет собой правила для других объектов, которые указывают, какие элементы скрыты, а какие открыты для доступа другим объектам. В Java мы используем модификатор доступа private для того, чтобы скрыть метод и ограничить доступ к переменной из внешней среды. Java также предусматривает различные модификаторы доступа, такие как public, который устанавливается по-умолчанию, protected и private, которые используются для ограничения видимости на различных уровнях, но конечная цель инкапсуляции — это скрыть те элементы, которые не должны изменяться. На практике лучше всего, когда класс имеет только одну причину для изменения, и инкапсуляция реализует принципы проектирования этой «одной причины».

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

Преимущества:
  • ,

15 вопросов для собеседования разработчиков, относительно Enum в Джаве (с ответами!)

Перечисление (Enum) было введено в Java 5 и с тех пор оно стало весьма популярно среди разработчиков Java. Оно широко используется в различных Java-приложениях. Поскольку Enum в Java намного универсальнее, чем в C или C ++, оно также представляет множество интересных вариантов использования, пару из которых, вы видели в статье 10 способов использовать Enum в Java.

Но, несмотря на такую популярность, многие Java-программисты по-прежнему не знают о функциональности, предоставляемой Enum и тонкостей использования Enum в Java коде.

Как отправить письмо из Java-приложения (с примером)

Перевод статьи: How to Send Email from Java Program with Example

Отправка электронного сообщения из Java-приложения это распространенное требование. Не имеет значения работаешь ли ты над core Java-приложением, веб-приложением или enterprise Java — приложением, тебе может понадобится отправить письмо для оповещения персонала из тех.поддержки об ошибках, или просто отправить письмо пользователям при регистрации, восстановить пароль или попросить их подтвердить адрес электронной почты после регистрации. Есть очень много сценариев(случаев), когда тебе нужна возможность отправить электронные письма из Java-приложения. В готовых приложениях у тебя уже есть модуль или библиотека, работающая со всеми функциями для отправки электронных писем, например, возможность отправлять вложения, изображения, включая подписи и форматированный текст в электронных письмах, НО если тебе нужно написать что-то с нуля, тогда Java Mail API это прекрасный вариант. В этой статье мы научимся как отправлять электронные письма из Java-приложения, используя почтовый API(javax.mail). Перед написанием кода ты должен знать некоторые основы работы электронной почты, например, тебе нужен SMTP(простой протокол передачи почты) сервер. Если ты запускаешь свое Java-приложение под Linux, тогда ты должен знать, что SMTP демон(прим. «компьютерная программа в системах класса UNIX, запускаемая самой системой и работающая в фоновом режиме без прямого взаимодействия с пользователем») использует порт 25. Ты можешь использовать любой почтовый сервер для отправки сообщений применяя Java, включая общедоступные почтовые сервера, такие как GMail, Yahoo или любой другой поставщик услуг, все что тебе нужно это детали их SMTP сервера, например, имя хоста, порт, параметры соединения и т.д. Так же ты можешь использовать SSL(англ. secure sockets layer — уровень защищённых сокетов), TLS(англ. Transport Layer Security — безопасность транспортного уровня) для безопасного соединения и отправки писем, но этот пример мы сделали простым и cфокусировались на минимальной логике для отправки письма из Java-приложения. В дальнейших статьях мы научимся как отправлять письмо используя вложения, как отправлять форматированное с помощью HTML письмо, как прикрепить изображения в письмо, как использовать SSL аутентификацию для соединения с сервером GMail и отправки писем и т.д. А сейчас, давай поймем(разберем) этот простой пример Java Mail API.

Пример Java-кода для отправки электронного сообщения



Для отправки письма из Java-программы тебе потребуются Java Mail API и Java Activation Framework(JAF); если более точно, то тебе понадобятся mail-1.4.5.jar, smtp-1.4.4.jar, и activation-1.1.jar. Тебе нужно скачать эти JAR файлы и включить их в твой Classpath для запуска этой программы. В качестве альтернативы ты можешь использовать Maven для управления зависимостями и включить все зависимости там. Как только у тебя будут все эти JAR файлы, просто следуй шагам ниже для создания и отправки электронного письма c помощью Java.

  1. Создай объект Session вызывая Session.getDefaultInstance(properties), где properties — содержит все важные свойства, например, имя хоста SMTP сервера.
  2. Создай объект MimeMessage передавая ему объект Session, полученный на предыдущем шаге. У нас есть набор различных свойств в этом объекте, таких как получатель письма, тема, тело сообщения, вложения и т.д.
  3. Используй javax.mail.Transport для отправки письма вызывая статический метод send(email), где email может быть объект MimeMessage.
  • ,

Немного вглубь о числах BigDecimal

Наконец, после 3-х месяцев перерыва, возвращаюсь к прохождению курса. На перерыв ушел в начале января. Решил тогда переключиться на изучение других дисциплин, как то HTML/CSS/Javascript. Попутно и технологии начал учить (сервлеты и JSP). На тот момент прошел 24 уровня и немного завяз на внутренних классах — не то, чтобы что-либо было не понятно, просто требовалась систематизация полученных знаний, т. к. слишком их много было получено за короткий период в режиме «галопом по Европам».
Так вот, сейчас я решил тщательно пройтись по первому тому Хорстмана для закрепления знаний Java Core и одновременно дорешать оставшиеся задачи с предыдущих уровней, после чего пойти дальше по уровням. Начал с бонусной задачи с 15-го уровня про факториал. Разделавшись с ней, решил глубже разобраться с классом BigDecimal. Популярных статей в инете особо не нашел, тогда залез в документацию и первый абзац в ней сразу же заставил пошевелить мозгами, как-то не все в нем было ясно. Решил докопаться до истины, а чтобы труды не пропали даром, оформил все в виде статьи и вот выкладываю на суд сообщества для обмена мнениями, да и может кому-то будет подспорьем.
Итак, я взял первый абзац и разбил его на предложения. В нем всего 5 предложений. Предложения номер 3 и 4 подчеркнул и переместил в конец абзаца (зачем так сделал — читайте дальше). И перевод соответственно сделал в таком измененном порядке.

Оригинал первого абзаца
  1. Immutable, arbitrary-precision signed decimal numbers.
  2. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.
  3. If zero or positive, the scale is the number of digits to the right of the decimal point.
  4. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale.
  5. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).

Оригинал с измененным порядком предложений
  1. Immutable, arbitrary-precision signed decimal numbers.
  2. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.
  3. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).
  4. If zero or positive, the scale is the number of digits to the right of the decimal point.
  5. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale.

Развернутый перевод (каждый пункт в переводе соответствует пункту в оригинале)
  1. Это неизменяемые дробные1 десятичные числа со знаком2, произвольной длины34.
  2. Число BigDecimal задается двумя параметрами. Первый из них, т.н. unscaled value, является целым числом неограниченной длины. Зная только этот параметр, нельзя ничего сказать о фактической величине числа BigDecimal. Для этого нужно обязательно знать второй параметр — 32-битное целое число, которое называется масштабом (scale).
  3. Тогда величину числа BigDecimal можно вычислить по следующей формуле:
    unscaledValue × 10-scale
  4. Если число BigDecimal равно нулю или положительное, масштаб — это количество цифр после десятичной точки.
  5. Если число BigDecimal отрицательное, его unscaled value умножается на 10, возведенное в степень, равную scale со знаком минус.
Примечания к переводу
1. Дробные, потому что в "arbitrary-precision" заложено помимо прочего,
   что это числа точные, а точные числа должны быть с десятичной запятой,
   другими словами — дробными.
2. Т.е. могут быть как положительными, так и отрицательными.
3. Произвольной длины, потому что "arbitrary-precision" означает также
   "произвольное количество цифр".
4. Фактически, числа BigDecimal — это вещественные числа. Но устанавливая
   разные настройки, можно использовать их не только в научных расчетах,
   а и в финансовых.

Так вот, подчеркнутые предложения были основным запутывающим для меня фактором — я бы их на самом деле просто убрал из текста, т. к. фактически они об одном и том же, а кроме того о том же, что говорится и в предложении номер 5. Т.е. в трех предложениях подряд говорится только о том, что unscaled value умножается на 10, возведенное в степень, равную scale со знаком минус. Это сбивает с толку при первом прочтении, т. к. читая предложение номер 3, понимаешь, что раз стоит if, значит тут будет что-то одно, а в следующем предложении — что-то другое. Ан нет — в обоих предложениях то же самое, только другими словами. Ну а третье (последнее в абзаце) предложение подряд о том же самом, наверное, как контрольный выстрел. Неясно, зачем текст составили таким запутанным образом, ведь формула unscaledValue × 10-scale из последнего предложения максимально ясно, однозначно и исчерпывающе дает определение большому числу.