¿El comando 'java' compila programas Java?

146

La mayoría de los sitios web en internet dicen:

"use el javaccomando para compilar un .javaarchivo. Luego ejecútelo usando el javacomando"

Pero hoy intenté ejecutar un programa Java sin javacy obtuve un resultado extraño.

Aquí están los contenidos de un archivo llamado hello.java:

public class Myclass {
 public static void main(String[] args){
    System.out.println("hello world");
  }
}

Entonces corrí:

$ javac hello.java

Lo que me da este error:

hello.java:1: error: class Myclass is public, should be declared in a file named Myclass.java
public class Myclass {
       ^
1 error

Pero cuando lo ejecuto sin el javaccomando, se ejecutó sin ningún error.

$ java hello.java
hello world

¿El javacomando también compila el programa? En caso afirmativo, ¿por qué necesitamos el javaccomando?

La versión de mi java es:

openjdk version "12.0.2" 2019-07-16
OpenJDK Runtime Environment (build 12.0.2+10)
OpenJDK 64-Bit Server VM (build 12.0.2+10, mixed mode)
milad
fuente
11
¿Qué versión está utilizando? Creo que introdujeron la Consola Java en Java 9, y eso podría ser lo que usted experimentó.
Matthieu
66
Debe hacer coincidir el nombre de la clase con su nombre de archivo, ese es el estándar de Java. Simplemente cambie el nombre del archivo Myclass.javay luego desde la línea de comandos compílelo así javac Myclass.javay luego ejecútelo así java Myclass.
Unnsse
66
sí, javactodavía se usa para compilar si no desea implementar el código fuente, o si tiene más de un archivo ( documentación de la javaopción para el archivo fuente: solo se usa para iniciar un solo programa de archivo fuente)
user85421
@Matthieu el resultado de "java -version" es: versión openjdk "12.0.2" 2019-07-16 OpenJDK Runtime Environment (compilación 12.0.2 + 10) OpenJDK 64-Bit Server VM (compilación 12.0.2 + 10, mixto modo)
milad
1
@Milad: lo que sucede es esto: javaccompila la fuente de Java en el código de bytes interpretado específico de JVM y el javacomando lo carga dentro del ClassLoader de JVM.
Unnsse

Respuestas:

189

Antes de Java 11, para ejecutar su código, primero debe compilarlo, luego puede ejecutarlo. Aquí hay un ejemplo:

javac test.java
java test

Desde Java 11, aún puede hacer javac+ java, o puede ejecutarse javasolo para compilar y ejecutar automáticamente su código. Tenga en cuenta que no .classse generará ningún archivo. Aquí hay un ejemplo:

java test.java

Si corres java -help, verás los diversos usos permitidos. Así es como se ve en mi máquina. El último es con el que se topó: java [options] <sourcefile> [args]que "ejecutará un solo programa de archivo fuente".

$ java -help
Usage: java [options] <mainclass> [args...]
           (to execute a class)
   or  java [options] -jar <jarfile> [args...]
           (to execute a jar file)
   or  java [options] -m <module>[/<mainclass>] [args...]
       java [options] --module <module>[/<mainclass>] [args...]
           (to execute the main class in a module)
   or  java [options] <sourcefile> [args]
           (to execute a single source-file program)

ACTUALIZAR:

Como señaló @BillK, OP también preguntó:

¿Por qué necesitamos el comando javac?

La razón por la que necesitamos javaces crear .classarchivos para que el código se pueda crear, probar, distribuir, ejecutar, compartir, etc., como es hoy. La motivación para JEP 330 fue facilitar las "primeras etapas de aprendizaje de Java y al escribir pequeños programas de utilidad" sin cambiar ningún otro uso existente.

kaan
fuente
gracias @CarlosHeuberger por los detalles adicionales. Hice una pequeña edición en mi respuesta para reflejar que se introdujo en Java 11.
kaan
77
@Spikatrix que es Java 8 (Se dejó caer el 1.en 1.8, según la versión)
Muru
1
No respondió la pregunta de por qué todavía necesitamos javac: creo que java solo opera en el archivo único que usted proporciona y los archivos compilados previamente. Creo que debe compilar todos los otros archivos que desea utilizar del archivo que llama.
Bill K
2
Esta respuesta no aborda por qué este nuevo método no produce un error de nombre de archivo vs. nombre de clase según lo informado por javac.
sebrockm
52

Si está ejecutando Java 11, hay una nueva característica que permite la ejecución de un solo archivo fuente. El compilador de fuente única es más promiscuo en términos de nombre de clase versus nombre de archivo, por lo que así es como puede ejecutar pero no compilar correctamente.

Si está en una versión anterior de Java, entonces su hello.java actual no se compila, debido a errores de compilación, específicamente alrededor del nombre de la clase. Entonces, no hay absolutamente ninguna manera de que llamar a java hello.java compile su código, porque no se compila.

Parece muy probable que estuviera ejecutando algún código compilado previamente al ejecutar el comando java.

Evan
fuente
gracias, la versión de Java es: openjdk versión "12.0.2" 2019-07-16 OpenJDK Runtime Environment (compilación 12.0.2 + 10) OpenJDK 64-Bit Server VM (compilación 12.0.2 + 10, modo mixto)
milad
55
verifique el uso del modo de archivo de origen para iniciar programas de código de origen de un solo archivo : "El compilador no aplica la restricción opcional definida al final de JLS? 7.6, que un tipo en un paquete con nombre debe existir en un archivo cuyo nombre es compuesto del nombre del tipo seguido de la extensión .java ".
user85421
2
La API de secuencias de comandos de Java y el Lanzamiento del programa de código fuente de un solo archivo de Java ( JEP 330 ) son dos cosas completamente separadas y no relacionadas.
David Conrad
@DavidConrad, verborrea actualizada en consecuencia. Gracias.
Evan
Agradezco la entrada, @TJCrowder. Pero, estoy bastante seguro de que tenía la intención de escribirlo tal como está. Además, la segunda definición en sus enlaces: Promiscuo significa que incluye una amplia gama de cosas diferentes.
Evan
6

Para responder por qué se da este error, el nombre de la clase para el archivo debe coincidir con el del archivo basename.

Tiene dos opciones para que este código funcione para el tradicional javac; javasecuencia:

  1. Cambiar el nombre de la clase a public class Helloo

  2. Cambiar el nombre hello.javaa myclass.java.

El javaintérprete para Java 11 no impone este requisito. La clase que contiene mainpuede tener cualquier nombre, siempre que sea la primera clase en el archivo. Esto estaba destinado principalmente a facilitar el proceso de aprendizaje para principiantes, y para permitir "scripts java" con el shebang ( ref. ).

SS Anne
fuente
5

Sí, pero no de la manera que probablemente quieras decir.

Cuando utiliza el javaccomando para compilar un archivo .java en un archivo .class, el resultado es algo llamado bytecode. Bytecode es un código de máquina (instrucciones nativas) para una CPU teórica basada en la especificación de Java Virtual Machine.

Esta especificación de CPU virtual es una especie de promedio de tipos de CPU que eran comunes en el momento en que se escribió la especificación. Debido a esto, está cerca de muchos tipos diferentes de CPU, lo que facilita la ejecución de los mismos archivos Java .class en múltiples tipos de CPU.

Cuando Java se lanzó por primera vez, el javacomando leería el archivo .class e interpretaría las instrucciones de bytecode de una en una y luego las asignaría a la instrucción nativa equivalente para la CPU en la que se estaba ejecutando. Esto funcionó pero no fue particularmente rápido. Para mejorar esto, se agregó la compilación Just in Time (JIT) a Java Runtime.

Con JIT, el javacomando toma el código de bytes y lo vuelve a compilar con las instrucciones nativas para la CPU en la que se está ejecutando. Los tiempos de ejecución modernos de Java tienden a comenzar a interpretar el código de bytes mientras compila JIT en segundo plano y cambiar a las instrucciones nativas compiladas cuando está listo y también perfilará la aplicación en ejecución y luego volverá a compilar el código de bytes con una optimización diferente para obtener el mejor rendimiento posible.

EDITAR (para apaciguar a los votantes caídos):

Entonces, en su caso específico (ya que está ejecutando un JRE más nuevo que v11), el código se compila (al menos) dos veces

  1. Como un único archivo .java para bytecode
  2. A través del compilador JIT, ya que interpreta el código de bytes (aunque para helloWorld podría no tener tiempo para ejecutar ninguno de los códigos nativos compilados)
hardillb
fuente
77
Esto no responde la pregunta.
David Conrad
2
@DavidConrad ¡Pero lo hace! La respuesta a "¿El comando 'java' compila programas Java?" es un rotundo "sí" por las razones que hardlib da aquí: compilará el código de bytes a las instrucciones nativas justo a tiempo (para programas no triviales, con configuraciones estándar).
Peter - Restablece a Monica el
¿La compilación ahora es obligatoria? Históricamente, el código de bytes de Java podría interpretarse; La compilación JIT era opcional.
MSalters
El JIT está activado de forma predeterminada en estos días (durante mucho tiempo), como se muestra mixed-modeen la salida de la versión
hardillb