Каков выигрыш от объявления метода как статического

Недавно я просматривал свои предупреждения в Eclipse и натыкался на это:

static warning

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

[edit] Точная цитата внутри справки Eclipse, с акцентом на частном и конечном:

При включении компилятор выдаст ошибку или предупреждение для методы private или final и которые относятся только к статическим члены.

Да, я знаю, что могу отключить его, но я хочу, чтобы знал причину его включения?

Почему было бы полезно объявить каждый метод возможным как статический?

Будет ли это приносить какие-либо выгоды от производительности? (в мобильном домене)

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

В конце дня я должен просто отключить это "игнорировать" или исправить 100+ предупреждений, которые он дал мне?

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

Ответ 1

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

Когда метод статичен, вы не можете получить доступ к нестационарным членам; следовательно, ваш объем уже. Итак, если вам не нужны и вам не нужны (даже в подклассах) нестатические члены для выполнения вашего контракта, зачем предоставлять доступ к этим полям вашему методу? Объявление метода static в этом случае позволит компилятору проверить, что вы не используете члены, которые вы не собираетесь использовать.

Кроме того, это поможет людям, читающим ваш код, понять природу контракта.

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

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

Примеры того, где вы не использовали бы ключевое слово static:

  • Крючок расширения, который ничего не делает (но может делать что-то с данными экземпляра в подклассе)
  • Очень простое поведение по умолчанию, которое должно настраиваться в подклассе.
  • Реализация обработчика событий: реализация будет отличаться от класса обработчика события, но не будет использовать какое-либо свойство экземпляра обработчика события.

Ответ 2

Здесь нет концепции оптимизации.

A static метод static, потому что вы явно объявляете, что этот метод не полагается на какой-либо экземпляр класса-окружения только потому, что ему это не нужно. Так что предупреждение Eclipse, как указано в документации:

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

Если вам не нужна переменная экземпляра, и ваш метод является закрытым (нельзя вызывать извне) или final (нельзя переопределить), тогда нет оснований позволять ему быть обычным методом, вместо того чтобы статический. Статический метод по своей сути безопасен даже потому, что вам разрешено делать с ним меньше вещей (ему не нужен какой-либо экземпляр, у вас нет никакого неявного объекта this).

Ответ 3

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

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

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

Ответ 4

1. Метод объявления static дает небольшое преимущество в производительности, но что более полезно, он позволяет использовать его, не имея экземпляра объекта под рукой (например, о методе factory или получение синглтона). Он также служит документальной цели, чтобы рассказать о характере метода. Эта документальная цель не следует игнорировать, поскольку она дает немедленный намек на характер метода читателям кода и пользователей API, а также служит инструментом мышления для оригинального программиста - явным образом о предполагаемом значении помогает вы также считаете, что прямо и производят код лучшего качества (я думаю, основываясь на моем личном опыте, но люди разные). Например, логично и, следовательно, желательно различать методы, действующие на тип и методы, действующие на экземпляр типа (как указано Jon Skeet в своем комментарии к вопросу С#).

Еще одним вариантом использования методов static является имитация процедурного интерфейса программирования. Подумайте о java.lang.System.println() классе и методах и атрибутах в нем. Класс java.lang.System используется как пространство имен группировки, а не объект-объект.

2.. Как Eclipse (или любой другой запрограммированный или другой вид - биокомпозитный или не-биокомпозитный объект) точно знает, какой метод может быть объявлен как статический? Даже если базовый класс не обращается к переменным экземпляра или вызывает нестатические методы, механизм наследования может измениться. Только если метод не может быть переопределен путем наследования подкласса, можем ли мы заявить со 100% уверенностью, что метод действительно может быть объявлен static. Переопределение метода невозможно точно в двух случаях:

  • private (ни один подкласс не может использовать его напрямую и даже не знает об этом), или
  • final (даже если он доступен подклассом, нет способа изменить метод для обращения к данным экземпляра или функциям).

Следовательно, логика опции Eclipse.

3. Оригинальный плакат также спрашивает: "Указав метод как статический, я полагаю, что вы не используете какие-либо переменные экземпляра, поэтому его можно перенести в класс стиля utils?" Это очень хороший момент. Иногда такое изменение дизайна указывается предупреждением.

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

Ответ 5

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

Вы также задали вопрос о производительности:

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

Однако это влияние производительности очень мало. Поэтому все это касается области.

Ответ 6

Из рекомендаций по производительности Android:

Предпочитает статический виртуальный Если вам не нужен доступ к объекту полей, сделайте свой метод статическим. Вызовы будут составлять около 15-20% Быстрее. Это также хорошая практика, потому что вы можете сказать по методу что вызов метода не может изменить состояние объекта.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic

Ответ 7

Ну, в документации Eclipse говорится об этом предупреждении:

Метод может быть статическим

При включении компилятор выдаст ошибку или предупреждение для методы, которые являются частными или окончательными и которые относятся только к статическим Члены

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

Честно говоря, я не думаю, что есть какая-то таинственная причина.

Ответ 8

Я потерял некоторые цифры для разницы в скорости. Поэтому я попытался сравнить их, что оказалось не так просто: Запуск Java медленнее после некоторых прогонов/ошибок JIT?

Я, наконец, использовал Caliper, и результаты совпадают с результатами моих тестов вручную:

Не существует измеримой разницы для статических/динамических вызовов. По крайней мере, не для Linux/AMD64/Java7.

Результаты калибровки приведены здесь: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenario.vmSpec.options.CMSLargeSplitSurplusPercent,scenario.vmSpec.options.CMSSmallCoalSurplusPercent,scenario.vmSpec.options.CMSSmallSplitSurplusPercent,scenario.vmSpec.options.FLSLargestBlockCoalesceProximity,scenario.vmSpec.options.G1ConcMarkStepDurationMillis

и мои собственные результаты:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

Класс испытания суппорта был:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

И мой собственный тестовый класс:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

Ответ 9

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

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

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

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

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

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