java - para - ¿Cuál es la diferencia entre un bloque de código de inicialización estático y no estático?
swing containers (9)
Mi pregunta es sobre un uso particular de la palabra clave estática. Es posible usar una palabra clave static
para cubrir un bloque de código dentro de una clase que no pertenece a ninguna función. Por ejemplo, el siguiente código compila:
public class Test {
private static final int a;
static {
a = 5;
doSomething(a);
}
private static int doSomething(int x) {
return (x+5);
}
}
Si elimina la palabra clave static
se queja porque la variable a
es final
. Sin embargo, es posible eliminar tanto las palabras clave final
como las static
y hacer que se compile.
Es confuso para mí en ambos sentidos. ¿Cómo se supone que tengo una sección de código que no pertenece a ningún método? ¿Cómo es posible invocarlo? En general, ¿cuál es el propósito de este uso? O mejor, ¿dónde puedo encontrar documentación sobre esto?
"final" garantiza que una variable debe inicializarse antes del final del código de inicialización del objeto. Del mismo modo, la "final estática" garantiza que una variable se inicializará al final del código de inicialización de clase. La omisión de la "estática" de su código de inicialización lo convierte en un código de inicialización de objetos; por lo tanto su variable ya no satisface sus garantías.
El bloque static
es un "inicializador estático".
Se invoca automáticamente cuando se carga la clase, y no hay otra forma de invocarla (ni siquiera a través de Reflexión).
Personalmente, solo lo he usado al escribir el código JNI:
class JNIGlue {
static {
System.loadLibrary("foo");
}
}
El bloque de código con el modificador estático significa un inicializador de clase ; sin el modificador estático, el bloque de código es un inicializador de instancia .
Los inicializadores de clase se ejecutan en el orden en que se definen (de arriba abajo, como los inicializadores de variable simple) cuando se carga la clase (en realidad, cuando se resuelve, pero eso es un tecnicismo).
Los inicializadores de instancia se ejecutan en el orden definido cuando se crea una instancia de la clase, inmediatamente antes de que se ejecute el código del constructor, inmediatamente después de la invocación del superconstructor.
Si elimina la static
de int a
, se convierte en una variable de instancia, a la que no puede acceder desde el bloque de inicializador estático. Esto no podrá compilarse con el error "no se puede hacer referencia a una variable no estática desde un contexto estático".
Si también elimina la static
del bloque de inicialización, entonces se convierte en un inicializador de instancia y, por lo tanto, se inicializa en la construcción.
El bloque de código estático se puede usar para instanciar o inicializar variables de clase (en lugar de variables de objeto). Por lo tanto, declarar "a" significa estático que solo está compartido por todos los objetos de prueba, y el bloque de código estático inicializa "a" solo una vez, cuando se carga la clase de prueba, sin importar cuántos objetos de prueba se creen.
Esto es directamente de http://www.programcreek.com/2011/10/java-class-instance-initializers/
1. Orden de ejecución
Mira la siguiente clase, ¿sabes cuál se ejecuta primero?
public class Foo {
//instance variable initializer
String s = "abc";
//constructor
public Foo() {
System.out.println("constructor called");
}
//static initializer
static {
System.out.println("static initializer called");
}
//instance initializer
{
System.out.println("instance initializer called");
}
public static void main(String[] args) {
new Foo();
new Foo();
}
}
Salida:
inicializador estático llamado
inicializador de instancia llamado
constructor llamado
inicializador de instancia llamado
constructor llamado
2. ¿Cómo funciona el inicializador de instancia de Java?
El inicializador de instancia anterior contiene una instrucción println. Para entender cómo funciona, podemos tratarlo como una instrucción de asignación de variables, por ejemplo, b = 0
. Esto puede hacer que sea más obvio de entender.
En lugar de
int b = 0
, podrías escribir
int b;
b = 0;
Por lo tanto, los inicializadores de instancia y los inicializadores de variable de instancia son más o menos los mismos.
3. ¿Cuándo son útiles los inicializadores de instancia?
El uso de inicializadores de instancia es raro, pero aún puede ser una alternativa útil a los inicializadores de variable de instancia si:
- El código de inicializador debe manejar excepciones
- Realice cálculos que no se pueden expresar con un inicializador de variable de instancia.
Por supuesto, tal código podría estar escrito en constructores. Pero si una clase tuviera múltiples constructores, tendría que repetir el código en cada constructor.
Con un inicializador de instancia, solo puede escribir el código una vez, y se ejecutará sin importar qué constructor se use para crear el objeto. (Supongo que esto es solo un concepto y no se usa con frecuencia).
Otro caso en el que los inicializadores de instancia son útiles son las clases internas anónimas, que no pueden declarar ningún constructor en absoluto. (¿Será este un buen lugar para colocar una función de registro?)
Gracias a Derhein.
También tenga en cuenta que las clases anónimas que implementan interfaces [1] no tienen constructores. Por lo tanto, los inicializadores de instancia son necesarios para ejecutar cualquier tipo de expresiones en el momento de la construcción.
La palabra clave estática se puede utilizar con clase, variable, método y bloque. Los miembros estáticos pertenecen a la clase en lugar de una instancia específica, esto significa que si haces un miembro estático, puedes acceder a él sin objeto. Ejemplo:
Aquí tenemos un método estático myStatic (), podemos llamar a este método sin ningún objeto porque cuando hacemos un miembro estático se convierte en un nivel de clase. Si eliminamos la palabra clave estática y la hacemos no estática, debemos crear un objeto de la clase para poder llamarla.
Los miembros estáticos son comunes para todas las instancias (objetos) de la clase, pero los miembros no estáticos son independientes para cada instancia de la clase.
class SimpleStaticExample {
//static method
static void myStatic()
{
System.out.println("my Static Method");
}
public static void main(String[] args)
{
/* You can see that we are calling this
* method without creating any object.
*/
myStatic();
}
}
Salida: mi método estático
No escribirá código en un bloque estático que deba invocarse en cualquier parte de su programa. Si se desea invocar el propósito del código, debe colocarlo en un método.
Puede escribir bloques de inicialización estáticos para inicializar variables estáticas cuando se carga la clase, pero este código puede ser más complejo.
Un bloque de inicializador estático se parece a un método sin nombre, sin argumentos y sin tipo de retorno. Como nunca lo llamas no necesita un nombre. La única vez que se llama es cuando la máquina virtual carga la clase.
cuando un desarrollador utiliza un bloque de inicialización, el compilador de Java copia el inicializador en cada constructor de la clase actual.
Ejemplo:
el siguiente código:
class MyClass {
private int myField = 3;
{
myField = myField + 2;
//myField is worth 5 for all instance
}
public MyClass() {
myField = myField * 4;
//myField is worth 20 for all instance initialized with this construtor
}
public MyClass(int _myParam) {
if (_myParam > 0) {
myField = myField * 4;
//myField is worth 20 for all instance initialized with this construtor
//if _myParam is greater than 0
} else {
myField = myField + 5;
//myField is worth 10 for all instance initialized with this construtor
//if _myParam is lower than 0 or if _myParam is worth 0
}
}
public void setMyField(int _myField) {
myField = _myField;
}
public int getMyField() {
return myField;
}
}
public class MainClass{
public static void main(String[] args) {
MyClass myFirstInstance_ = new MyClass();
System.out.println(myFirstInstance_.getMyField());//20
MyClass mySecondInstance_ = new MyClass(1);
System.out.println(mySecondInstance_.getMyField());//20
MyClass myThirdInstance_ = new MyClass(-1);
System.out.println(myThirdInstance_.getMyField());//10
}
}
es equivalente a:
class MyClass {
private int myField = 3;
public MyClass() {
myField = myField + 2;
myField = myField * 4;
//myField is worth 20 for all instance initialized with this construtor
}
public MyClass(int _myParam) {
myField = myField + 2;
if (_myParam > 0) {
myField = myField * 4;
//myField is worth 20 for all instance initialized with this construtor
//if _myParam is greater than 0
} else {
myField = myField + 5;
//myField is worth 10 for all instance initialized with this construtor
//if _myParam is lower than 0 or if _myParam is worth 0
}
}
public void setMyField(int _myField) {
myField = _myField;
}
public int getMyField() {
return myField;
}
}
public class MainClass{
public static void main(String[] args) {
MyClass myFirstInstance_ = new MyClass();
System.out.println(myFirstInstance_.getMyField());//20
MyClass mySecondInstance_ = new MyClass(1);
System.out.println(mySecondInstance_.getMyField());//20
MyClass myThirdInstance_ = new MyClass(-1);
System.out.println(myThirdInstance_.getMyField());//10
}
}
Espero que mi ejemplo sea entendido por los desarrolladores.
¡Uff! ¿Qué es el inicializador estático?
El inicializador estático es un bloque de código static {}
dentro de la clase java y se ejecuta solo una vez antes de llamar al constructor o al método principal.
¡DE ACUERDO! Dime más...
- es un bloque de código
static { ... }
dentro de cualquier clase Java. y ejecutado por máquina virtual cuando se llama clase. - No se admiten declaraciones de
return
. - No se admiten argumentos.
- No
this
osuper
son compatibles.
Hmm donde puedo usarlo?
Puede ser utilizado en cualquier lugar que se sienta bien :) así de simple. Pero veo que la mayor parte del tiempo se usa cuando se hace una conexión de base de datos, API init, registro y etc.
¡No solo ladre! donde esta el ejemplo
package com.example.learnjava;
import java.util.ArrayList;
public class Fruit {
static {
System.out.println("Inside Static Initializer.");
// fruits array
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Orange");
fruits.add("Pear");
// print fruits
for (String fruit : fruits) {
System.out.println(fruit);
}
System.out.println("End Static Initializer./n");
}
public static void main(String[] args) {
System.out.println("Inside Main Method.");
}
}
¿¿¿Salida???
Inicializador estático interior.
manzana
naranja
Pera
Inicializador estático final.
Método principal interno.
¡Espero que esto ayude!