La commande 'java' compile-t-elle les programmes Java?

146

La plupart des sites Web sur Internet disent:

"utilisez la javaccommande pour compiler un .javafichier. Puis exécutez-le en utilisant la javacommande"

Mais aujourd'hui, j'ai essayé d'exécuter un programme java sans javacet j'ai obtenu un résultat étrange.

Voici le contenu d'un fichier appelé hello.java:

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

Puis j'ai couru:

$ javac hello.java

Ce qui me donne cette erreur:

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

Mais quand je l'exécute sans la javaccommande, il s'exécute sans aucune erreur.

$ java hello.java
hello world

La javacommande compile- t-elle également le programme? Si oui, pourquoi avons-nous besoin de la javaccommande?

La version de mon java est:

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
la source
11
Quelle version utilisez-vous? Je pense qu'ils ont introduit Java Console dans Java 9, et c'est peut-être ce que vous avez vécu.
Matthieu
6
Vous devez faire correspondre le nom de la classe avec son nom de fichier - c'est la norme Java. Changez simplement le nom du fichier en Myclass.javapuis à partir de la ligne de commande, compilez-le comme ceci javac Myclass.java, puis exécutez-le comme ceci java Myclass.
unnsse le
6
oui, javactoujours utilisé pour compiler si vous ne voulez pas déployer le code source, ou si vous avez plus d'un seul fichier ( documentation de l' javaoption for source-file: Uniquement utilisé pour lancer un seul programme de fichier source.)
user85421
@Matthieu la sortie de "java -version" est: openjdk version "12.0.2" 16/07/2019 Environnement d'exécution OpenJDK (build 12.0.2 + 10) VM serveur OpenJDK 64 bits (build 12.0.2 + 10, mixte mode)
milad
1
@Milad - Ce qui se passe est ceci - javaccompile la source Java dans le bytecode interprété spécifique à la JVM et la javacommande le charge dans le ClassLoader de la JVM.
unnsse le

Réponses:

189

Avant Java 11, pour exécuter votre code, vous devez d'abord le compiler, puis vous pouvez l'exécuter. Voici un exemple:

javac test.java
java test

Depuis Java 11, vous pouvez toujours faire javac+ java, ou vous pouvez exécuter javapar lui-même pour compiler et exécuter automatiquement votre code. Notez qu'aucun .classfichier ne sera généré. Voici un exemple:

java test.java

Si vous exécutez java -help, vous verrez les différentes utilisations autorisées. Voici à quoi cela ressemble sur ma machine. Le dernier est ce que vous avez rencontré: java [options] <sourcefile> [args]qui "exécutera un seul programme de fichier source".

$ 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)

METTRE À JOUR:

Comme l'a souligné @BillK, OP a également demandé:

pourquoi avons-nous besoin de la commande javac?

La raison pour laquelle nous avons besoin javacest de créer des .classfichiers afin que le code puisse être créé, testé, distribué, exécuté, partagé, etc., comme c'est le cas aujourd'hui. La motivation de JEP 330 était de faciliter les "premières étapes de l'apprentissage de Java, et lors de l'écriture de petits programmes utilitaires" sans changer les autres utilisations existantes.

Kaan
la source
merci @CarlosHeuberger pour les détails supplémentaires. J'ai fait une petite modification dans ma réponse pour refléter qu'elle a été introduite dans Java 11.
kaan
7
@Spikatrix qui est Java 8 (Ils ont laissé tomber le 1.dans 1.8dans les versions plus récentes)
muru
1
Vous n'avez pas répondu à la question de savoir pourquoi nous avons toujours besoin de javac - je pense que java ne fonctionne que sur le fichier unique que vous le fournissez et sur les fichiers précédemment compilés. Je pense que vous devez compiler tous les autres fichiers que vous souhaitez utiliser à partir du fichier que vous appelez.
Bill K du
2
Cette réponse ne traite pas de la raison pour laquelle cette nouvelle méthode n'entraîne pas une erreur de nom de fichier contre nom de classe, comme indiqué par javac.
sebrockm
52

Si vous exécutez Java 11, il existe une nouvelle fonctionnalité qui permet l'exécution d'un fichier source unique. Le compilateur source unique est plus promiscuité en termes de nom de classe que de nom de fichier, c'est ainsi que vous pouvez exécuter mais pas compiler avec succès.

Si vous utilisez une version précédente de Java, votre hello.java actuel ne se compile pas, en raison d'erreurs de compilation, en particulier autour du nom de la classe. Il n'y a donc absolument aucun moyen d'appeler java hello.java pour compiler votre code, car il ne compile pas.

Il semble très probable que vous exécutiez du code précédemment compilé lors de l'exécution de la commande java.

Evan
la source
merci, la version java est: openjdk version "12.0.2" 2019-07-16 Environnement d'exécution OpenJDK (build 12.0.2 + 10) VM serveur OpenJDK 64 bits (build 12.0.2 + 10, mode mixte)
milad
5
check Utilisation du mode fichier source pour lancer des programmes de code source à fichier unique : "Le compilateur n'applique pas la restriction facultative définie à la fin de JLS ?? 7.6, selon laquelle un type dans un package nommé doit exister dans un fichier dont le nom est composé du nom du type suivi de l'extension .java. "
user85421
2
L'API de script Java et le lancement du programme Java Single-File Source-Code ( JEP 330 ) sont deux éléments complètement séparés et totalement indépendants.
David Conrad
@DavidConrad, mise à jour du verbiage en conséquence. Merci.
Evan
Merci de votre contribution, @TJCrowder. Mais je suis assez sûr que je voulais l'écrire tel quel. Aussi, deuxième définition dans vos liens: Promiscuous signifie inclure un large éventail de choses différentes.
Evan le
6

Pour expliquer pourquoi cette erreur est donnée, le nom de classe du fichier doit correspondre à celui du fichier basename.

Vous avez deux options pour que ce code fonctionne pour le traditionnel javac; javaséquence:

  1. Renommez la classe en public class Helloou

  2. Renommez hello.javaen myclass.java.

L' javainterpréteur pour Java 11 n'impose pas cette exigence. La classe qui contient mainpeut avoir n'importe quel nom, à condition qu'il s'agisse de la première classe du fichier. Cela visait principalement à faciliter le processus d'apprentissage pour les débutants, et à permettre le "script java" avec le shebang ( réf. ).

SS Anne
la source
5

Oui, mais pas de la façon dont vous l'entendez probablement.

Lorsque vous utilisez la javaccommande pour compiler un fichier .java dans un fichier .class, la sortie est quelque chose appelé bytecode. Bytecode est un code machine (instructions natives) pour un processeur théorique basé sur la spécification Java Virtual Machine.

Cette spécification de CPU virtuelle est en quelque sorte une moyenne des types de CPU qui étaient courants au moment où la spécification a été écrite. Pour cette raison, il est proche de nombreux types de processeurs différents, ce qui facilite l'exécution des mêmes fichiers Java .class sur plusieurs types de processeurs.

Lorsque Java était lancé pour la première fois, la javacommande lisait le fichier .class et interprétait les instructions de bytecode une par une, puis les mappait à l'instruction native équivalente pour le processeur sur lequel elle s'exécutait réellement. Cela a fonctionné mais n'était pas particulièrement rapide. Pour améliorer cette compilation Just in Time (JIT) a été ajoutée au Java Runtime.

Avec JIT, la javacommande prend le bytecode et le compile à nouveau dans les instructions natives du processeur sur lequel il s'exécute. Les environnements d'exécution Java modernes ont tendance à commencer à interpréter le bytecode pendant la compilation JIT en arrière-plan et à basculer vers les instructions natives compilées lorsqu'il est prêt.Ils profileront également l'application en cours d'exécution, puis recompileront à nouveau le bytecode avec une optimisation différente pour obtenir les meilleures performances possibles.

EDIT (pour apaiser les votants):

Donc, dans votre cas spécifique (comme vous exécutez un JRE plus récent que la v11), le code est compilé (au moins) deux fois

  1. En tant que fichier .java unique à bytecode
  2. Via le compilateur JIT lorsqu'il interprète le bytecode (bien que pour helloWorld, il pourrait ne pas avoir le temps d'exécuter l'un des codes natifs compilés)
hardillb
la source
7
Cela ne répond pas à la question.
David Conrad
2
@DavidConrad Mais c'est le cas! La réponse à "La commande 'java' compile-t-elle les programmes Java?" est un «oui» retentissant pour les raisons que hardlib donne ici: il compilera du code d'octet en instructions natives juste à temps (pour les programmes non triviaux, avec des paramètres standard).
Peter - Réintègre Monica le
La compilation est-elle désormais obligatoire? Historiquement, le code d'octet Java pouvait être interprété; La compilation JIT était facultative.
MSalters le
Le JIT est activé par défaut ces jours-ci (pendant très longtemps), comme le montre le mixed-modedans la sortie de la version
hardillb