제이온

[Эффективный Java] Элемент 6. Избегайте ненужного создания объектов

  • Язык написания: Корейский
  • Страна: Все страныcountry-flag
  • ИТ

Создано: 2024-04-28

Создано: 2024-04-28 13:40

Случаи создания ненужных объектов

Использование new String()


Строки a, b и c все будут содержать строку «hi». Однако, поскольку адреса, на которые ссылаются эти три строки, различны, происходит ненужное выделение памяти для одних и тех же данных.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa53d14aa-6abf-40f0-8441-435647d172fa%2FUntitled.png?table=block&id=f168859c-367c-4924-a4c6-5e04788fab67&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1146;" width="2000" height="1146"></span>


Поэтому при объявлении строк следует использовать литералы, а не ключевое слово new.



В приведенном выше коде используется только один экземпляр. Более того, при использовании этого метода гарантируется, что все фрагменты кода, использующие строковый литерал «hi» в той же JVM, будут использовать один и тот же объект. Это происходит из-за особенности пула констант Java.


Использование new Boolean()

В приведенном выше коде создается экземпляр Boolean с помощью конструктора, принимающего строку в качестве параметра. Boolean может иметь только значения true или false, поэтому создание экземпляра каждый раз является пустой тратой памяти. Поэтому лучше использовать статический фабричный метод Boolean.valueOf().



Использование String.matches()

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



Однако метод String.matches() имеет проблемы с производительностью. Экземпляр Pattern, который этот метод создает внутри себя для регулярных выражений, используется один раз и сразу становится кандидатом на сборку мусора. По мере увеличения частоты использования этого регулярного выражения увеличивается стоимость создания и удаления одинаковых экземпляров Pattern. Поэтому лучше кэшировать экземпляр Pattern заранее и повторно использовать его при каждом вызове метода isRomanNumeral().



Примечание

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


Адаптер (представление) — это объект, который делегирует фактическую работу объекту бэкэнда, а сам выступает в роли вторичного интерфейса. Адаптеру нужно управлять только объектом бэкэнда, поэтому для каждого объекта бэкэнда достаточно создать только один адаптер.


Например, метод keySet() интерфейса Map возвращает представление Set, содержащее все ключи объекта Map. Пользователь может подумать, что при каждом вызове метода keySet() создается новый экземпляр Set, но на самом деле в реализации JDK возвращается один и тот же изменяемый экземпляр Set.


Это связано с тем, что все функции, выполняемые возвращаемым экземпляром Set, одинаковы, и все экземпляры Set представляют экземпляр Map. Поэтому, даже если keySet() создает несколько объектов представления, это не имеет значения и не приносит никакой пользы.



Поэтому, если изменить экземпляр names1, экземпляр names2 также будет затронут.


Но лично я считаю, что возвращаемое значение метода keySet() должно использовать защитное копирование и возвращать новый объект каждый раз. Если экземпляр Set, полученный с помощью метода keySet(), также используется в другом месте, и в коде есть код, который изменяет состояние этого экземпляра, то мы не сможем быть уверены в значениях используемого в данный момент экземпляра Set и экземпляра Map.


Кроме того, если среда не использует keySet() слишком часто, создание интерфейса Set каждый раз не окажет существенного влияния на производительность. Лучше сделать интерфейс Set неизменяемым и обеспечить стабильность в обслуживании.


Автоматическая распаковка

Автоматическая распаковка — это технология, которая автоматически преобразует примитивные типы и типы-оболочки при смешанном использовании программистом. Однако автоматическая распаковка просто размывает различие между примитивными типами и типами-оболочками, но не устраняет его полностью.



С точки зрения логики проблем нет, но с точки зрения производительности этот код очень неэффективен. Причиной этого является тип sum и тип i внутри цикла for.


Тип sum — Long, а тип i — long. То есть, когда тип long i добавляется к sum во время итерации цикла, создается новый экземпляр Long. В результате следует использовать примитивные типы вместо типов-оболочек и избегать непреднамеренной автоматической распаковки.


Что следует понимать неправильно

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


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


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


Источники

Комментарии0

29.11.2024 Поздние новости: платная подписка на Replit / Хорошие привычки в разработке ПОВ записи блога от 29 ноября 2024 года мы делимся опытом использования платной подписки на Replit и представляем 10 хороших привычек в разработке программного обеспечения. Обсуждаются стратегии эффективного кодирования и рефакторинга, а также важность тес
Charles Lee
Charles Lee
Charles Lee
Charles Lee

November 29, 2024

[Для неспециалистов, выживание разработчика] 14. Краткое изложение часто задаваемых вопросов на техническом собеседовании для начинающих разработчиковМы обобщили и систематизировали часто задаваемые технические вопросы на собеседовании для начинающих разработчиков (области памяти, структуры данных, базы данных и т. д.). Надеемся, что это поможет вам подготовиться к собеседованию по разработке.
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 3, 2024

Получение неизменяемых значений с помощью genericПредставляем способ простой передачи неизменяемых значений в generic с помощью нового ключевого слова TypeScript 5.0. Может использоваться с массивами или литеральными типами, что способствует разработке библиотек и улучшению пользовательского опыта.
Sunrabbit
Sunrabbit
Sunrabbit
Sunrabbit

October 30, 2024

LLM для младших школьниковПонятное объяснение концепции LLM даже для младших школьников! LLM — это ИИ, который отвечает текстом на текстовые вопросы, выполняя различные задачи, такие как кодирование и анализ изображений. Сегодня разработчики используют ИИ как инструмент.
Sunrabbit
Sunrabbit
Sunrabbit
Sunrabbit

March 4, 2025

Записи о жизни, интересные моменты: Новости / TDD / ИИ / Анализ данныхЗапись повседневных наблюдений от 19 ноября 2024 года. Включает в себя информацию о новостях, TDD, ИИ и анализе данных. Кратко изложены различные данные, такие как фондовый рынок, тенденции криптовалют и прогнозы будущего ИИ.
Charles Lee
Charles Lee
Charles Lee
Charles Lee

November 19, 2024