Статические переменные и методы - PullRequest
3 голосов
/ 03 марта 2009

Я наткнулся на класс, который был настроен так:

public class MyClass {

  private static boolean started = false;

  private MyClass(){
  }

  public static void doSomething(){
    if(started){
      return;
    }
    started = true;
    //code below that is only supposed to run
    //run if not started
  }
}

Мое понимание статических методов заключается в том, что вы не должны использовать в них переменные класса, если они не являются постоянными и не меняются. Вместо этого вы должны использовать параметры. Мой вопрос заключается в том, почему это не ломается, когда вызывается несколько раз, выполняя MyClass.doSomething (). Мне кажется, что это не должно работать, но работает. Пройдет оператор if только один раз.

Так может ли кто-нибудь объяснить мне, почему это не сломается?

Ответы [ 9 ]

11 голосов
/ 03 марта 2009

Метод doSomething() и переменная started являются статическими, поэтому существует только одна копия переменной, и она доступна из doSomething(). В первый раз, когда вызывается doSomething(), started является ложным, поэтому он устанавливает started в истинное значение и затем ... что-то делает Второй и последующие моменты, когда он называется, started - это истина, поэтому он возвращается, ничего не делая.

6 голосов
/ 03 марта 2009

Данный код не является потокобезопасным. Самый простой способ сделать этот поток кода безопасным - сделать что-то вроде

public class MyClass {

  private static AtomicBoolean started = new AtomicBoolean(false);

  private MyClass(){
  }

  public static void doSomething(){
    boolean oldValue = started.getAndSet(true);
    if (oldValue)
      return;
    }

    //code below that is only supposed to run
    //run if not started
  }
}

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

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

6 голосов
/ 03 марта 2009

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

Что должно произойти:

  1. Первый звонок сделан. Класс инициализирован, запущен - false.
  2. doSomething называется. Если происходит сбой, и код обходит это. начальный установлен в true, а другой код выполняется.
  3. doSomething вызывается снова. If проходит и выполнение останавливается.

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

2 голосов
/ 03 марта 2009

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

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

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

Вместо этого вы должны использовать параметры.

Трудно понять, как будет использоваться параметр started - если вызывающий знает, что процесс запущен, зачем ему вызывать метод?

1 голос
/ 03 марта 2009

Просто запомните правило большого пальца: «Статические переменные переменные уровня класса , а все нестатические переменные экземпляр переменные». Тогда у вас не будет никакой путаницы!

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

1 голос
/ 03 марта 2009

Ваш статический метод обращается к статической переменной класса, так что все должно быть в порядке. Вы можете думать об этом как о глобальном коде и глобальной переменной, так как это ЕСТЬ в пространстве имен класса. Если вы пытались получить доступ к нестатической переменной-члену:

private int foo = 0;

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

started is false - initial state.
MyClass.doSomething() - statered is now true
MyClass.doSomething() - started is STILL true

MyClass foo = new MyClass();
foo.started -> it's STILL true, because it's static
foo.doSomething() - not sure you can do this in Java, but if you can, it's be STILL TRUE!

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

1 голос
/ 03 марта 2009

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

Без учета многопоточных сценариев, Первый вызов doSomething придаст булевой статической переменной значение true, поэтому второй вызов выполнит код блока if, который просто выходит из метода.

0 голосов
/ 03 марта 2009

Мое понимание статических методов заключается в том, что вы не должны использовать в них переменные класса, если они не являются постоянными и не меняются

Полагаю, доступны только статические члены. Это не должно быть постоянным!

У меня вопрос, почему это не ломается, когда вызывается несколько раз, выполняя MyClass.doSomething (). Мне кажется, что это не должно работать, но работает. Пройдет оператор if только один раз

В соответствии с существующей логикой. Только первый вызов запускает //code to be run part

0 голосов
/ 03 марта 2009

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

...