Есть ли в Java какой-либо недостаток статических методов в классе? - PullRequest
35 голосов
/ 18 марта 2010

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

(отредактировано для уточнения)

Я знаю, что вопрос был несколько открытым и расплывчатым, поэтому я прошу прощения за это. Мое намерение спрашивать было в контексте в основном "вспомогательных" методов. Служебные классы (с частными CTOR, чтобы их нельзя было создавать) в качестве держателей для статических методов, которые мы уже делаем. Мой вопрос здесь был скорее в соответствии с этими маленькими методами, которые ПОМОГАЮТ ВЫБРАТЬ API основного класса.

У меня может быть 4 или 5 основных методов API / экземпляра в классе, которые выполняют реальную работу, но в ходе этого они разделяют некоторые общие функции, которые могут работать только с входными параметрами для метода API, и не внутреннее состояние. Это разделы кода, которые я обычно извлекаю в свои собственные вспомогательные методы, и если им не требуется доступ к состоянию класса, сделайте их статическими.

Мой вопрос был таким: является ли это плохой идеей, и если да, то почему? (Или почему нет?)

Ответы [ 14 ]

27 голосов
/ 18 марта 2010

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

22 голосов
/ 04 июня 2010

По моему мнению, есть четыре причины избегать статических методов в Java. Это не означает, что статические методы никогда не применимы, а лишь говорит о том, что их вообще следует избегать.

  1. Как уже отмечали другие, статические методы не могут быть смоделированы в модульном тесте. Если класс зависит, скажем, от DatabaseUtils.createConnection(), то этот зависимый класс и любые классы, которые от него зависят, будет практически невозможно протестировать без фактической наличия базы данных или какого-либо флага «тестирования» в DatabaseUtils. В последнем случае кажется, что у вас есть две реализации интерфейса DatabaseConnectionProvider - см. Следующий пункт.

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

    boolean oldFlag = MyUtils.getFlag();
    MyUtils.someMethod();
    MyUtils.setFlag( oldFlag );
    

    Одним из примеров общей библиотеки, столкнувшейся с этой проблемой, является Apache Commons Lang: см. StringUtilsBean и т. Д.

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

  4. Если у вас есть статические методы, которые ссылаются на статические переменные, они остаются на всю жизнь загрузчика классов и никогда не собирают мусор. Если они накапливают информацию (например, кеши) и вы неосторожны, вы можете столкнуться с «утечками памяти» в вашем приложении. Если вместо этого вы используете методы экземпляра, объекты имеют тенденцию быть более короткими и поэтому через некоторое время будут собираться мусором. Конечно, вы все еще можете попасть в утечки памяти с помощью методов экземпляра! Но это меньше проблем.

Надеюсь, это поможет!

17 голосов
/ 18 марта 2010

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

6 голосов
/ 17 января 2014

Disadvantage -> Static

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

6 голосов
/ 18 марта 2010

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

Полезные классы со статическими методами - хорошая идея.

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

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

Кроме того, наличие класса экземпляра синглтона помогает нам сделать класс, почти как статический класс, все еще использующим концепции ООП.

3 голосов
/ 04 июня 2010

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

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

 class StringUtil {
     private static StringUtil impl = new DefaultStringUtil();

     public static String nullOrValue( String s ) {
          return impl.doNullOrValue();
     }
     ... rest omitted 
  }

Если по «некоторым» причинам вам необходимо изменить класс реализации, который вы можете предложить:

  class StringUtil {
     private static StringUtil impl = new ExoticStringUtil();

     public static String nullOrValue( String s ) {
          return impl.doNullOrValue(s);
     }
     ... rest omitted 
  }

Но может быть чрезмерным в некоторых обстоятельствах.

3 голосов
/ 18 марта 2010

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

public class Sorting {
  public static void quiksort(int [] array) {}
  public static void heapsort(int[] array) { }
}

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

public class Stats {
  public static void printStats(float[] data) { }
}

Первый элемент плохого дизайна здесь заключается в том, что программист намеревается просто распечатать результаты, а не использовать их в общем. Внедрение ввода / вывода в вычисления ужасно для повторного использования. Однако следующая проблема заключается в том, что эта универсальная процедура должна вычислять max, min, среднее, дисперсию и т. Д. И хранить ее где-нибудь. Куда? В состоянии объекта. Если бы это было действительно одноразово, вы могли бы сделать его статичным, но, конечно, вы обнаружите, что хотите вычислить среднее значение двух разных вещей, и тогда будет очень приятно, если вы сможете просто создать экземпляр объекта несколько раз .

public class Stats {
  private double min,max,mean,var;
  public void compute(float data[]) { ... }
  public double getMin() { return min; }
  public double
}

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

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

Что касается производительности, то наибольший прирост производительности при переключении с обычного метода на самом деле заключается в том, чтобы избежать динамической полиморфной проверки, которая по умолчанию используется в java и которая в C ++ указывается вручную с помощью virtual.

Когда я пытался использовать last, было преимущество 3: 1 в вызове метода final перед обычным методом, но при вызове статических функций перед final не было заметно.

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

2 голосов
/ 18 марта 2010

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

Предполагается, что класс уже загружен. В противном случае есть немного ожидания. : -)

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

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

1 голос
/ 18 марта 2010

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

1 голос
/ 18 марта 2010

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

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

...