Почему статические переменные считаются злыми?

Я программист Java, новичок в корпоративном мире. Недавно я разработал приложение с использованием Groovy и Java. Весь код, который я написал, использовал довольно много статики. Меня попросила старшая техническая партия сократить количество используемой статики. Я искал ту же тему, что и многие программисты против использования статических переменных.

Я считаю, что статические переменные более удобны в использовании. И я полагаю, что они тоже эффективны (пожалуйста, поправьте меня, если я ошибаюсь), потому что, если мне пришлось сделать 10 000 вызовов функции внутри класса, я был бы рад сделать статический статический метод и использовать простой Class.methodCall() на нем вместо загромождения памяти с 10 000 экземпляров класса, не так ли?

Кроме того, статика уменьшает взаимозависимости на других частях кода. Они могут выступать в качестве прекрасных государственных держателей. Добавляя к этому, я обнаружил, что статика широко применяется на некоторых языках, таких как Smalltalk и Scala. Так почему же это угнетение для статики распространено среди программистов (особенно в мире Java)?

PS: Пожалуйста, поправьте меня, если мои предположения о статике неверны.

Ответы

Ответ 1

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

Я мог бы продолжать довольно долгое время, но более широкая концепция, о которой нужно подумать, состоит в том, что чем меньше объем чего-то, тем легче рассуждать. Мы хорошо думаем о мелочах, но трудно рассуждать о состоянии миллионной линейной системы, если нет модульности. Это относится ко всем видам вещей, кстати, не только статическим переменным.

Ответ 2

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

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

Срок службы объекта: Кроме того, статика имеет срок службы, соответствующий всей продолжительности работы программы. Это означает, что даже после того, как вы закончите использовать свой класс, память из всех этих статических переменных не может быть собрана в мусор. Если, например, вы сделали свои переменные нестатичными, а в своей основной() функции вы сделали один экземпляр вашего класса, а затем попросили ваш класс выполнить определенную функцию 10 000 раз, после того, как эти 10 000 вызовов были выполнены, и вы удаляете свои ссылки на один экземпляр, все ваши статические переменные могут быть собраны и повторно использованы для сбора мусора.

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

Другие параметры: Если эффективность является вашей основной задачей, могут быть и другие более эффективные способы решения проблемы скорости, чем рассматривать только преимущество вызова, которое обычно происходит быстрее, чем создание. Подумайте, нужны ли переходные или изменчивые модификаторы в любом месте. Чтобы сохранить возможность быть встроенным, метод может быть помечен как final, а не static. Параметры метода и другие переменные могут быть отмечены как final, чтобы разрешить определенные оптимизаторы компилятора на основе предположений о том, что может изменить эти переменные. Объект экземпляра может повторно использоваться несколько раз, а не каждый раз создавать новый экземпляр. Могут быть переключатели оптимизации компилятора, которые должны быть включены для приложения в целом. Возможно, дизайн должен быть настроен так, чтобы 10 000 прогонов могли быть многопоточными и использовать многопроцессорные ядра. Если portablity не вызывает беспокойства, возможно, собственный метод даст вам лучшую скорость, чем ваша статика.

Если по какой-то причине вам не нужны несколько копий объекта, шаблон singleton design имеет преимущества перед статическими объектами, такими как поток - безопасность (предполагая, что ваш синглтон закодирован хорошо), разрешая ленивую инициализацию, гарантируя, что объект был правильно инициализирован, когда он используется, подклассификация, преимущества в тестировании и рефакторинг вашего кода, не говоря уже о том, если в какой-то момент вы измените ваш разум только для того, чтобы хотеть один экземпляр объекта, намного проще удалить код, чтобы предотвратить дублирование экземпляров, чем рефакторинг всего вашего статического кода переменной для использования переменных экземпляра. Я должен был сделать это раньше, это не забавно, и вам в итоге нужно редактировать намного больше классов, что увеличивает риск введения новых ошибок... так лучше, чтобы все было правильно "правильно" в первый раз, даже если кажется, что у него есть свои недостатки. Для меня требуется повторная работа, если вы решите, что вам нужно несколько копий что-то, вероятно, одна из самых убедительных причин использовать статику как можно реже. И, таким образом, я бы тоже не согласился с вашим утверждением, что статика уменьшает межзависимости, я думаю, что вы получите код, который будет более скомбинирован, если у вас будет много статистики, к которой можно напрямую обращаться, а не к объекту, который "знает, как это сделать" что-то "на себе.

Ответ 3

Зло - субъективный член.

Вы не контролируете статику с точки зрения создания и уничтожения. Они живут по воле программы погрузки и разгрузки.

Поскольку статика живет в одном пространстве, все потоки, желающие использовать их, должны пройти контроль доступа, которым вы должны управлять. Это означает, что программы более связаны, и это изменение сложнее предусмотреть и управлять (например, J Skeet говорит). Это приводит к проблемам изоляции воздействия изменения и, таким образом, влияет на управление тестированием.

Это две основные проблемы, которые у меня есть.

Ответ 4

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

Глобальные состояния являются абсолютной необходимостью. Мы не можем избежать глобальных государств. Мы не можем избежать рассуждений о глобальных государствах. - Если мы хотим понять нашу семантику применения.

Люди, которые стремятся избавиться от глобальных государств ради него, неизбежно оказываются в гораздо более сложной системе - и глобальные государства все еще существуют, умно/идиотично замаскированные под многими слоями косвенностей; и нам все равно придется рассуждать о глобальных состояниях, после разворачивания всех косвенных действий.

Подобно Spring людям, которые щедро объявляют глобальные состояния в xml и как-то думают, что они превосходят.

@Jon Skeet if I create a new instance of an object теперь у вас есть две причины рассуждать - состояние внутри объекта и состояние среды, в которой размещен объект.

Ответ 5

Существуют две основные проблемы со статическими переменными:

  • Безопасность потоков - статические ресурсы по определению не являются потокобезопасными
  • Кодекс Implicity - вы не знаете, когда статические переменные создаются и будет ли он создан для другой статической переменной

Ответ 6

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

Я бы оценил где-то около 85% времени, когда я вижу "статический без" финала, это НЕПРАВИЛЬНО. Часто я нахожу странные обходные пути для маскировки или скрытия этих проблем.

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

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

Если вы хотите получить более подробную информацию, пожалуйста, прочитайте...

Почему не использовать статику?

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

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

Если вы используете статику, невозможно выполнить замену реализации класса, чтобы проверить компоненты более высокого уровня. Например, представьте себе статический CustomerDAO, который возвращает объекты Customer, которые он загружает из базы данных. Теперь у меня есть класс CustomerFilter, которому нужно получить доступ к некоторым объектам Customer. Если CustomerDAO является статическим, я не могу написать тест для CustomerFilter без предварительной инициализации моей базы данных и заполнения полезной информации.

И заселенность базы данных и инициализация занимает много времени. И, по моему опыту, ваша база инициализации базы данных будет меняться со временем, то есть данные будут морфироваться, а тесты могут нарушиться. IE, представьте, что Клиент 1 был VIP, но каркас инициализации DB изменился, и теперь Клиент 1 больше не VIP, но ваш тест был жестко запрограммирован для загрузки Customer 1...

Лучший подход - создать экземпляр CustomerDAO и передать его в CustomerFilter, когда он будет создан. (Еще лучше было бы использовать Spring или другую структуру инверсии управления.

Как только вы это сделаете, вы можете быстро высмеять или заглушить альтернативный DAO в своем CustomerFilterTest, что позволит вам получить больше контроля над тестом,

Без статического DAO тест будет быстрее (без инициализации db) и более надежным (потому что он не сработает при изменении кода инициализации db). Например, в этом случае обеспечение клиента 1 будет и всегда будет VIP, насколько это касается теста.

Выполнение тестов

Статика вызывает настоящую проблему при совместном выполнении наборов единичных тестов (например, с сервером Continuous Integration). Представьте себе статическую карту сетевых объектов Socket, которые остаются открытыми с одного теста на другой. Первый тест может открыть Socket на порту 8080, но вы забыли очистить карту, когда тест разорвется. Теперь, когда запускается второй тест, он может сбой, когда пытается создать новый Socket для порта 8080, поскольку порт все еще занят. Представьте также, что ссылки Socket в вашей статической коллекции не удаляются, и (за исключением WeakHashMap) никогда не могут быть собраны мусором, что вызывает утечку памяти.

Это обобщенный пример, но в больших системах эта проблема происходит ВСЕ ВРЕМЯ. Люди не думают о модульных тестах, которые запускают и останавливают свое программное обеспечение повторно в одной JVM, но это хороший тест вашего программного обеспечения, и если у вас есть стремления к высокой доступности, то это то, о чем вам нужно знать.

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

Если системная конфигурация, применяемая к этим компонентам инфраструктуры, изменяется между модульными тестами, а структура unit test не срывает и не восстанавливает компоненты, эти изменения не могут вступить в силу, и когда тест основывается на этих изменениях, они потерпят неудачу.

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

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

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

Тонкие ошибки

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

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

Когда поток умирает, статический объект не получает reset или сбор мусора. Представьте, что у вас есть поток, называемый "EmailCustomers", и когда он запускается, он заполняет статическую коллекцию String списком адресов электронной почты и затем отправляет по электронной почте каждый из адресов. Допустим, что поток прерывается или отменяется каким-то образом, поэтому ваша платформа высокой доступности перезапускает поток. Затем, когда поток запускается, он перезагружает список клиентов. Но поскольку коллекция является статической, она может сохранить список адресов электронной почты из предыдущей коллекции. Теперь некоторые клиенты могут получать дубликаты писем.

Рядом: Статический Финал

Использование "статического финала" является фактически эквивалентом Java С#define, хотя существуют технические различия в реализации. C/С++ #define перед компиляцией выгружается из кода предварительным процессором. "Статический финал" Java будет содержать резидентную память в стеке. Таким образом, он больше похож на переменную "static const" в С++, чем на #define.

Резюме

Надеюсь, это поможет объяснить несколько основных причин, почему статика проблематична. Если вы используете современную Java-инфраструктуру, такую ​​как Java EE или Spring и т.д., Вы не можете столкнуться со многими из этих ситуаций, но если вы работаете с большим количеством устаревшего кода, они могут стать намного более частыми.

Ответ 7

Поскольку никто не упомянул об этом: concurrency. Статические переменные могут удивить вас, если у вас есть несколько потоков, которые читают и записывают статическую переменную. Это распространено в веб-приложениях (например, ASP.NET), и это может вызвать некоторые довольно сумасшедшие ошибки. Например, если у вас есть статическая переменная, которая обновляется на странице, а страница запрашивается двумя людьми в "почти одинаковом времени", один пользователь может получить ожидаемый результат другим пользователем или хуже.

статика уменьшает взаимозависимости на других частях кода. Они могут выступать в качестве прекрасных государственных держателей.

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

* На самом деле, Preet Sangha упомянул об этом.

Ответ 8

если мне пришлось сделать 10 000 вызовов функции внутри класса, я бы рад сделать метод статическим и использовать простой class.methodCall() на нем вместо загромождения памяти с 10 000 экземпляры класса, правильно?

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

Кроме того, статика уменьшает взаимозависимости на других частях кода.

Так делает инкапсуляцию. В больших приложениях статика имеет тенденцию создавать код спагетти и нелегко разрешить рефакторинг или тестирование.

Другие ответы также дают веские основания для чрезмерного использования статики.

Ответ 9

Статические переменные обычно считаются плохими, поскольку они представляют собой глобальное состояние и поэтому гораздо труднее рассуждать. В частности, они нарушают предположения объектно-ориентированного программирования. В объектно-ориентированном программировании каждый объект имеет свое собственное состояние, представленное переменными экземпляра (нестатические). Статические переменные представляют состояние через экземпляры, которые могут быть намного сложнее unit test. Это происходит главным образом потому, что сложнее выделить изменения статических переменных для одного теста.

При этом важно провести различие между регулярными статическими переменными (обычно считающимися плохими) и конечными статическими переменными (константы AKA, не так уж плохо).

Ответ 10

Обобщение нескольких основных преимуществ и недостатков использования статических методов в Java:

<сильные > Преимущества:

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

Недостатки:

  • Статические элементы всегда являются частью погоды памяти, в которой они используются.
  • Вы не можете контролировать создание и уничтожение статической переменной. По своей сути они были созданы при загрузке программы и уничтожены при разгрузке программы (или когда JVM отключается).
  • Вы можете сделать поток статики безопасным, используя синхронизацию, но вам нужны дополнительные усилия.
  • Если один поток изменит значение статической переменной, которая может нарушить функциональность других потоков.
  • Прежде чем использовать его, вы должны знать "статический".
  • Вы не можете переопределять статические методы.
  • Сериализация не работает с ними.
  • Они не участвуют в полиморфизме времени выполнения.
  • Проблема с памятью (в некоторой степени, но не так уж и много), если используется большое количество статических переменных/методов. Потому что они не будут GC до окончания программы.
  • Статические методы также трудно проверить.

Ответ 11

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

Это просто о том, как изолировать логику и дать ей хорошее место. Иногда это оправдывает использование статических методов, из которых java.lang.Math является хорошим примером. Я думаю, когда вы назовете большинство своих классов XxxUtil или Xxxhelper, вам лучше пересмотреть свой дизайн.

Ответ 12

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

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

Ответ 13

Еще одна причина: хрупкость.

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

Вы можете документировать его не в этом случае или защищать от него (singleton/factory pattern) - но это дополнительная работа и, следовательно, дополнительная стоимость. Даже тогда, в большой компании, есть вероятность, что кто-то попытается в какой-то момент использовать ваш класс, не обращая внимания на все приятные комментарии или factory.

Если вы часто используете статические переменные, это сломается. Ошибки дороги.

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

Ответ 14

<сильные > Преимущества:

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

Недостатки:

Статические элементы являются частью класса и, таким образом, остаются в памяти до тех пор, пока приложение не завершится и не сможет собраться с мусором. Использование избытка статических элементов когда-либо предсказывает, что вы не можете спроектировать свой продукт и пытаетесь использовать статическое/процедурное программирование. Это означает, что объектно-ориентированный дизайн скомпрометирован. Это может привести к переполнению памяти. Также есть некоторые недостатки, если вы ставите какой-либо метод статическим в Java, например, вы не можете переопределить какой-либо статический метод в Java, поэтому он усложняет тестирование, вы не можете заменить этот метод макетом. Поскольку статический метод поддерживает глобальное состояние, он может создавать тонкую ошибку в параллельной среде, которую трудно обнаружить и исправить.

Что нужно помнить:

Статическая переменная будет частью определения класса, а не кучи. Однако статические переменные полезны, когда вы знаете, что будут доступны объекты из нескольких мест. Доступ к статическим ресурсам не является потокобезопасным. Вы можете получить странные/непредсказуемые результаты в поточной среде. Но если ваше единственное чтение статического значения, то использование потоков для него в порядке.

Как инкапсуляция Static Breaks:

Техническая реализация состоит в том, чтобы позволить поддерживать состояние во всех экземплярах класса. Проблема в том, что это по сути не ООП, поскольку она не учитывает инкапсуляцию. Если переменная может быть изменена любым экземпляром класса, то основной принцип скрытия инкапсуляции/информации полностью теряется: объект больше не полностью контролирует его состояние. Его состояние теперь зависит от переменных, которые по сути являются глобальными. Мы знаем, что это плохо. Даже частные статические переменные сохраняют состояние на глобальном уровне, но просто ограничивают его доступ. Любой экземпляр объекта может изменять статическую переменную, которая вызывает неоднозначность, поскольку отдельные экземпляры объекта больше не имеют контроля над своим собственным состоянием. Изменения состояния могут произвольно происходить без знания объекта, который полагается на это состояние, которое является проблематичным, потому что объект может работать неправильно, когда это происходит. Как часто говорят, что "Наследование прерывает инкапсуляцию", статика делает это гораздо более серьезным образом: не только подвергая внутреннюю реализацию, но и подвергая внутреннее состояние.

Ответ 15

Я только что кратко изложил некоторые моменты, высказанные в ответах. Если вы найдете что-то неправильно, не стесняйтесь исправить его.

Масштабирование: У нас есть только один экземпляр статической переменной для JVM. Предположим, что мы разрабатываем систему управления библиотекой, и мы решили ставить имя книги как статическую переменную, поскольку в каждой книге есть только одна книга. Но если система растет, и мы используем несколько JVM, тогда у нас нет способа выяснить, с какой книгой мы имеем дело?

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

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

Рассуждение о состоянии: Если я создаю новый экземпляр класса, тогда мы можем рассуждать о состоянии этого экземпляра, но если он имеет статические переменные, то он может быть в любом состоянии. Зачем? Поскольку возможно, что статическая переменная была изменена некоторым другим экземпляром, поскольку статическая переменная делится между экземплярами.

Сериализация: Сериализация также не работает с ними.

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

Но что, если они действительно нужны?

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

Также, если статическая переменная отмечена как final, она становится константой, а присваиваемое ей значение некогда не может быть изменено. Это означает, что он спасет нас от всех проблем, с которыми мы сталкиваемся из-за его изменчивости.

Ответ 16

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

Для получения дополнительной информации прочитайте this Спасибо.

Ответ 17

Я считаю, что статические переменные более удобны в использовании. И я полагаю, что они тоже эффективны (пожалуйста, исправьте меня, если я ошибаюсь), потому что, если мне пришлось сделать 10 000 вызовов функции внутри класса, я был бы рад сделать статический статический метод и использовать простой класс .methodCall() на нем вместо загромождения памяти с 10 000 экземпляров класса, правильно?

Я вижу, что вы думаете, но простой шаблон Singleton будет делать то же самое без необходимости создавать 10 000 объектов.

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

Пример:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

Ответ 18

Проблема "Статика зла" больше связана с глобальным состоянием. Соответствующее время, когда переменная статична, - если она не имеет больше одного состояния; Инструменты IE, которые должны быть доступны всей структуре и всегда возвращать одни и те же результаты для тех же вызовов метода, никогда не являются "злыми" как статика. Что касается вашего комментария:

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

Статика - идеальный и эффективный выбор для переменных/классов, которые никогда не меняются.

Проблема с глобальным состоянием - это неотъемлемая несогласованность, которую она может создать. Документация об модульных тестах часто затрагивает эту проблему, так как в любое время существует глобальное состояние, к которому можно получить доступ более чем к нескольким несвязанным объектам, ваши юнит-тесты будут неполными, а не "единичными". Как упоминалось в этой статье о глобальном состоянии и синглетонах, если объекты A и B не связаны (как в одном, прямо не указано ссылка на другое) то A не должно влиять на состояние B.

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

Ответ 19

My $.02 - это то, что некоторые из этих ответов путают проблему, а не говорят, что "статика плохая". Я думаю, что лучше поговорить о области видимости и экземплярах.

Я бы сказал, что static - это переменная класса, она представляет значение, которое используется для всех экземпляров этого класса. Как правило, он также должен быть ограничен (защищенный или закрытый для класса и его экземпляры).

Если вы планируете размещать на нем поведение на уровне класса и выставлять его на другой код, то синглтон может быть лучшим решением для поддержки изменений в будущем (как предложил @Jessica). Это связано с тем, что вы можете использовать интерфейсы на уровне экземпляра /singleton способами, которые нельзя использовать на уровне класса - в частности, наследование.

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

Статики не являются "глобальными". В Java область охвата контролируется отдельно от static/instance.

Concurrency не менее опасен для статики, чем методы экземпляра. Он по-прежнему утверждает, что его нужно защищать. Уверен, что у вас может быть 1000 экземпляров с переменной экземпляра каждая и только одна статическая переменная, но если код, к которому обращается либо, не написан поточно-безопасным способом, вы все еще напортачиваетесь - вам может потребоваться немного больше времени, чтобы вы это осознали.

Управление жизненным циклом - интересный аргумент, но я считаю его менее важным. Я не понимаю, почему его сложнее управлять несколькими методами класса, такими как init()/clear(), чем создание и уничтожение экземпляра singleton. На самом деле, некоторые могут сказать, что одноэлемент немного сложнее из-за GC.

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

Ответ 20

Мне кажется, что вы спрашиваете о статических переменных, но также указываете на статические методы в своих примерах.

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

Статические методы aka utility method. Обычно использовать их недобросовестно, но главная проблема заключается в том, что они могут препятствовать тестированию.

В качестве примера большого проекта java, использующего много статистики и сделайте это правильно, посмотрите Play! рамки. Существует также обсуждение об этом в SO.

Статические переменные/методы в сочетании со статическим импортом также широко используются в библиотеках, которые облегчают декларативное программирование в java, например: упростить или Hamcrest. Это было бы невозможно без множества статических переменных и методов.

Таким образом, статические переменные (и методы) хороши, но используйте их с умом!

Ответ 21

Нет ничего плохого в статических переменных как таковых. Это просто синтаксис Java, который сломан. Каждый Java-класс фактически определяет две структуры - одноэлементный объект, который инкапсулирует статические переменные и экземпляр. Определение обоих в одном исходном блоке - это чистое зло, и в результате получается код, который трудно читать. Scala сделал это правильно.

Ответ 22

a) Причина программ.

Если у вас есть небольшая программа среднего размера, к которой обращается статическая переменная Global.foo, вызов к ней обычно происходит из ниоткуда - нет пути и, следовательно, нет временной шкалы, как переменная попадает в место, где он используется. Теперь, как я узнаю, кто установил ее в фактическую ценность? Как я узнаю, что произойдет, если я изменю его прямо сейчас? У меня есть grep по всему источнику, чтобы собрать все обращения, чтобы знать, что происходит.

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

b) Вам действительно нужен только один?

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

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

Ответ 23

все (может:) иметь свою цель, если у вас есть куча потоков, для которых требуется общий/кеш, а также все доступная память (так что вы не разделите контексты в рамках одной JVM) статичность - лучший выбор

→ Конечно, вы можете заставить только один экземпляр, но почему? я нахожу некоторые комментарии в этой теме зла, а не статики;)

Ответ 24

В вашем сообщении есть два основных вопроса.

Во-первых, о статических переменных. Статические переменные являются совершенно ненужными, и их можно легко избежать. В OOP languajes в целом и в Java в частности, параметры функции поэтапны по ссылке, то есть если вы передаете объект funciont, вы передаете указатель на объект, поэтому вам не нужно определять статические переменные, поскольку вы можете передать указатель на объект в любую область, которая нуждается в этой информации. Даже если это означает, что yo заполнит вашу память указателями, это не обязательно будет представлять собой плохую производительность, потому что фактические системы разбиения памяти оптимизированы для работы с этим, и они будут поддерживать в памяти страницы, на которые ссылаются указатели, которые вы передали новым объем; использование статических переменных может привести к тому, что система загрузит страницу памяти, где они хранятся, когда им нужно получить доступ (это произойдет, если страница не была присоединена в течение длительного времени). Хорошая практика состоит в том, чтобы поместить все эти статические stuf в несколько небольших "конфигурационных кланов", это обеспечит, чтобы система помещала все это на одну и ту же страницу памяти.

Во-вторых, о статических методах. Статические методы не так уж плохи, но они могут быстро снизить производительность. Например, подумайте о методе, который сравнивает два объекта класса и возвращает значение, указывающее, какой из объектов больше (метод типового сравнения), этот метод может быть статическим или нет, но при его вызове нестационарная форма будет более эффективной так как ему придется решать только две ссылки (по одному для каждого объекта) перед тремя ссылками, которые должны будут решить статическую версию того же метода (один для класса плюс два, по одному для каждого объекта). Но, как я уже сказал, это не так уж плохо, если мы посмотрим на класс Math, мы можем найти множество математических функций, определяемых как статические методы. Это действительно более эффективно, чем помещение всех этих методов в класс, определяющий числа, потому что большинство из них редко используются и в том числе все из них в классе чисел вызовут очень сложный класс и потребляют много ресурсов. /p >

В concluson: избегайте использования статических переменных и находите правильное равновесие производительности при работе со статическими или нестационарными методами.

PS: Извините за мой английский.

Ответ 25

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

Ответ 26

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

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

Ответ 27

Здесь есть много хороших ответов, добавляя к нему

Память:  Статические переменные живут до тех пор, пока классный загрузчик живет [в общем случае до смерти VM], но это только в случае массовых объектов/ссылок, хранящихся как статические.

Модульность: рассмотрите такие понятия, как IOC, dependencyInjection, proxy и т.д. Все они полностью против жесткой связи/статических реализаций.

Other Con's: безопасность потока, проверяемость

Ответ 28

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

Ответ 29

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

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

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

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

Подробнее см. Статическое ключевое слово в java

Ответ 30

Я думаю, что чрезмерное использование глобальных переменных со статическим ключевым словом также приведет к утечке памяти в какой-то момент в приложении