2011-03-03 5 views
23

Un compañero de trabajo (que es muy nuevo en Java) se detuvo en el día de hoy y me preguntó qué parecía una pregunta muy simple. Desafortunadamente, hice un trabajo absolutamente horrible al tratar de explicárselo. Tenía un libro que tenía un poco de código que se veía así:¿Por qué puede crear una clase en su definición?

class XCopy { 

    public static void main(String[] args) { 
     XCopy x = new XCopy(); // 1 
     x.doIt(); 
    } 

    public void doIt() { 
     // Some code... 
    } 
} 

Estaba confundido en la línea 1. Lo que quería saber era por qué una nueva instancia de XCopy podría crearse dentro de la definición de la clase XCopy . Pensó que esto habría dado algún tipo de error de referencia directa. Después de todo, aún no habíamos terminado de declarar cuál era la clase XCopy, entonces, ¿cómo podríamos crear una?

Ciertamente sé que este código es válido pero, cuando intenté explicárselo, me encontré tropezando con la respuesta y me temo que lo dejé más confundido que cuando comenzó. Me gustaría escuchar algunas otras explicaciones de por qué esto funciona.

¿Alguna idea? ¿Por qué se puede crear una instancia de una clase dentro de la definición de la clase?

Respuesta

32

Está definiendo la clase, todos sus campos y métodos, etc. en tiempo de compilación. La instancia no se crea hasta el tiempo de ejecución. Entonces, no hay contradicción, la clase está completamente definida para cuando llegue a la línea 1.

Como otros señalan, porque el método main es static, llegará a la línea 1 sin haber creado una instancia de un objeto, pero puede hacerlo sin problemas. Uso este patrón todo el tiempo para experimentos de una clase.

+1

¿Qué son los "experimentos de una sola clase"? – Andru

+2

@Andru Es solo cuando estás experimentando con el idioma, y ​​no quieres crear un programa completo, así que simplemente pones todo en una sola clase, incluido el método principal – Eames

+0

@Mathew, solo quiero volver a comprobar mi conocimiento, es decir, una La clase se carga primero en la memoria y luego se llama a la principal estática, ¿verdad? – light

1

Porque el método principal es estático. Y estático, significa que el método no pertenece a ninguna instancia particular de la clase. Es decir, se puede acceder sin crear una instancia.

Por lo tanto, para invocar el método doIt que no es estático, se debe crear una instancia de la clase que lo contiene.

1

El jvm carga la clase cada vez que se menciona "primero". Entonces no hay nada que impida la creación de instancias: ya está cargado

+0

realmente relevante. Una clase también puede crear una instancia de sí mismo dentro de un método no estático. No hay una razón inherente por la que no pueda hacer que un objeto cree otro objeto del mismo tipo.Al igual que ayer, escribí una función para un objeto que representa una fecha/hora a la que llamé "medianoche", y que devuelve otra instancia de la misma clase con la misma fecha que el objeto llamante, pero el tiempo vuelve a la más reciente medianoche, es decir, MyDate ("2011-01-01 08:30:00"). medianoche() efectivamente devuelve MyDate ("2011-01-01 00:00:00"). – Jay

+0

@jay Sí, debo haber tenido sueño. Retenido solo la parte relevante de mi respuesta ahora – Bozho

5

No es una referencia directa en el sentido C/C++. Su método principal es referirse a la clase como un tipo dentro de su propio contexto. Usted no está "adelantado" a nada.

El hecho de que el principal es estática, no es pertinente, ya que todavía va a trabajar incluso para los métodos no estáticos:

public class Foo 
{ 
    private String x; 

    public Foo(String x) { this.x = x; } 
    public Foo(Foo f) { this.x = f.x; } // copy constructor; still compiles fine, even without static 
} 

Una diferencia es la compilación y vinculación. C/C++ tiene pasos de compilación y enlace separados. Java tiene un cargador de clases. Creo que compilar para bytear código y cargar según sea necesario en tiempo de ejecución usando el cargador de clases es una sutil diferencia entre Java y C/C++ que explica por qué la idea de referencia directa no es necesaria, pero no estoy seguro.

12

Porque el código se compila primero y se ejecuta más adelante. Todo lo que el compilador necesita saber para validar esa línea es que existe una clase llamada XCopy, y que tiene un constructor sin argumentos. No necesita saber todo sobre la clase.

3

¿La misma razón por la que puede llamar a un método en la línea 42 que no está definido hasta la línea 78? Java no es un lenguaje de scripting, por lo que no es necesario declarar las cosas antes de que se utilicen (esto es cierto incluso en algunos lenguajes de scripting). Las definiciones de clase se consideran como un todo en tiempo de compilación.

Incluso puede crear una instancia de un objeto de una clase en su propio constructor:

public class Test { 
    Test a; 

    Test() { 
     a = new Test(); 
    } 

    public static void main(String[] args) { 
     System.out.println(new Test()); 
    } 
} 

Esto produce ... esperar a que ... una java.lang.StackOverflowError.

4

Una clase es solo una impresión azul que describe cómo se verán y se comportarán todas las instancias de la clase. Dependiendo de la visibilidad de la clase y sus constructores, el código en la misma clase, en el mismo paquete o completos extraños pueden crear instancias.

Por ejemplo, es común proporcionar un método de fábrica en las clases donde el constructor no debe ser pública:

public class Foo { 
    // only I get to create new instances 
    private Foo() { 
    } 

    // but you can get instances through this factory method 
    public static Foo createFoo() { 
     return new Foo(); 
    } 
} 
+0

Buen ejemplo de la vida real simple! ¡Era lo que estaba buscando! – Andru

3

Si su compañero de trabajo está viniendo de una C o fondo de programación Pascal esta pregunta es abolutely lógico. En un programa C, los métodos deben declararse por encima de la línea donde se usan por primera vez. Ya que no siempre es práctico para ordenar las funciones en este orden, hay que acaba de dar el nombre de métodos, tipo de retorno y los parámetros, sin definir el cuerpo de la función:

// forward declaration 
void doSomething(void); 

void doSomethingElse(void) { 
    doSomething(); 
} 

// function definition 
void doSomething(void) { 
    ... 
} 

Esto se hizo para simplificar la creación de un programa de análisis y para permitir un análisis más rápido ya que se necesitan menos pases sobre la fuente. Sin embargo, en Java, los identificadores pueden usarse antes de su punto de definición. Por lo tanto, el análisis debe ocurrir en varias fases. Después de compilar un árbol de sintaxis correspondiente al código fuente, este árbol se recorre para determinar todas las definiciones de clases o métodos. Los cuerpos del método se procesan en una etapa posterior, cuando se conoce toda la información sobre los nombres en el alcance.

Así que por el hecho de que se procesa el cuerpo del método de su método principal, el compilador conoce el constructor predeterminado de su clase y su método doIt y puede generar el bytecode correcto para llamar exactamente a este método.

+0

Sin duda había más grupos de organización cuando los diseñadores de Java estaban trabajando en el compilador en comparación con los diseñadores de C. :) [Ley de Conway] (https://en.wikipedia.org/wiki/Conway%27s_law) –

-1

El cargador de clases carga una clase en la memoria y se inicializa si ocurre algo de lo siguiente.

1) Se crea una instancia de clase utilizando la palabra clave new() o utilizando la reflexión usando class.forName(), que puede arrojar ClassNotFoundException en Java.

2) se invoca un método estático de Clase.

3) se asigna un campo estático de clase.

4) se utiliza un campo de clase estático que no es una variable constante.

5) si la clase es una clase de nivel superior y se ejecuta una declaración de afirmación léxicamente anidada dentro de la clase.

así que por la línea 1, la clase está cargada e inicializada y, por lo tanto, no hay ningún problema al crear una instancia de una clase en sí misma.

Pero si su código es así,

class Test { 
    Test test2 = new Test(); 
    public static void main(String[] args) { 
     Test test1 = new Test();  
    } 
} 

the above code will result in stackoverflow exception. 



class Test { 
    public static void main(String[] args) { 
     Test test = new Test();  
    } 
} 

In the above code creating an instance won't call main again. 
Remember, main is a static method, not tied to any particular instance. 



class Test { 
    static Test test2 = new Test(); 
    public static void main(String[] args) { 
     Test test1 = new Test();  
    } 
} 

This code will also run fine. 

Más de https://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html

No
Cuestiones relacionadas