Pregunta

Me encontré con una clase que se configuró de esta manera:

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
  }
}

Mi comprensión con los métodos estáticos es que no debes usar variables de clase en ellos a menos que sean constantes y no cambien. En su lugar, debe utilizar los parámetros. Mi pregunta es por qué no se interrumpe esto cuando se llama varias veces haciendo MyClass.doSomething (). Me parece que no debería funcionar pero lo hace. Solo pasará la sentencia if una vez.

Entonces, ¿podría alguien explicarme por qué esto no se rompe?

¿Fue útil?

Solución

El método doSomething () y la variable started son estáticos, por lo que solo hay una copia de la variable y es accesible desde doSomething () . La primera vez que se llama a doSomething () , inició es falso, por lo que establece iniciado en verdadero y luego hace ... bueno, algo. La segunda vez que se llama, started es verdadera, por lo que vuelve sin hacer nada.

Otros consejos

No hay ninguna razón por la que el uso de una variable estática no funcione. No estoy diciendo que sea una práctica particularmente buena, pero funcionará.

Lo que debería suceder es:

  1. Se hace la primera llamada. La clase se inicializa, el inicio es falso.
  2. doSomething se llama. El si falla y el código lo pasa por alto. iniciado se establece en verdadero y el otro código se ejecuta.
  3. doSomething se llama de nuevo. El si pasa y la ejecución se detiene.

Lo único que se debe tener en cuenta es que aquí no hay sincronización, por lo que si se invocó doSomething () en subprocesos separados increíblemente próximos, cada subproceso podría iniciarse como falso, omitir la instrucción if y hacer el trabajo, es decir, hay un condición de carrera.

El código dado no es seguro para subprocesos. La manera fácil de hacer que este hilo de código sea seguro sería hacer algo como

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
  }
}

Esto debería ser seguro para subprocesos ya que AtomicBoolean getAndSet está sincronizado.

Es cierto que esto no es un problema si no usas subprocesos (ten en cuenta que una aplicación web puede usar muchos subprocesos que manejan varias solicitudes sin que te des cuenta).

No es un código particularmente bueno; en general, los diseños deberían usar instancias de objetos donde el estado cambia, pero no hay nada ilegal en ello.

  

Mi comprensión con los métodos estáticos es que no debes usar variables de clase en ellos a menos que sean constantes y no cambien.

Parece que ha extrapolado de una guía de diseño a una función de idioma. Lea uno de los muchos tutoriales de Java disponibles en línea sobre lo que realmente está permitido en el idioma. Usted puede usar campos estáticos no finales libremente en métodos estáticos, pero conduce a un código de procedimiento en lugar de orientado a objetos.

  

En su lugar, debe utilizar parámetros.

Es difícil ver cómo se usaría un parámetro comenzado . Si la persona que llama supiera que el proceso se inició, ¿por qué llamaron al método?

Dentro del método estático, se le permite invocar o acceder a miembros estáticos dentro de la misma clase.

Ignorando múltiples escenarios de hilos, La primera llamada a doSomething hará que la variable estática booleana sea verdadera, por lo tanto, la segunda llamada ejecutará el código de bloque if que simplemente sale del método.

Tu método estático está hablando con una variable de clase estática, por lo que debería estar bien. Podría pensar en esto como un código global y una variable global, aunque esté en el espacio de nombres de la clase.

Si ha intentado acceder a una variable miembro no estática:

private int foo = 0;

desde el método estático, el compilador se quejará.

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!

Ahora, hay problemas en el código anterior con seguridad de subprocesos, pero aparte de eso, parece que funciona según lo diseñado.

Solo recuerda la regla del pulgar de que " Las variables estáticas son nivel de clase y todas las variables no estáticas son instancia variables " ;. ¡Entonces no tendrás ninguna confusión!

es decir, Para la variable estática, todas las referencias hechas en código a la variable apuntan a la misma ubicación de memoria. Y para la variable no estática, la nueva asignación de memoria se realiza cada vez que se crea una nueva instancia de esa clase (por lo tanto, cada referencia hecha en el código a la variable apunta a una ubicación de memoria diferente asignada para la instancia de la clase que llama).

El código anterior funciona completamente bien (a menos que se ejecute en un entorno multiproceso). ¿Por qué crees que debería romperse?

  

Mi comprensión con los métodos estáticos es que no debes usar variables de clase en ellos a menos que sean constantes y no cambien

Supongo que solo se puede acceder a los miembros estáticos. ¡No tiene que ser constante!

  

Mi pregunta es por qué no se interrumpe esto cuando se llama varias veces haciendo MyClass.doSomething (). Me parece que no debería funcionar pero lo hace. Solo pasará la sentencia if una vez

Por la lógica existente. Solo la primera llamada ejecuta el // código que debe ejecutarse parte

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top