O comando 'java' compila programas Java?

145

A maioria dos sites na internet diz:

"use o javaccomando para compilar um .javaarquivo. Em seguida, execute-o usando o javacomando"

Mas hoje eu tentei executar um programa java sem javace obtive um resultado estranho.

Aqui está o conteúdo de um arquivo chamado hello.java:

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

Então eu corri:

$ javac hello.java

O que me dá esse erro:

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

Mas quando eu o executo sem o javaccomando, ele é executado sem erros.

$ java hello.java
hello world

O javacomando também compila o programa? Se sim, por que precisamos do javaccomando?

A versão do meu java é:

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
fonte
11
Qual versão você está usando? Eu acho que eles introduziram o Java Console no Java 9, e pode ser o que você experimentou.
Matthieu
6
Você precisa combinar o nome da classe com o nome do arquivo - esse é o padrão Java. Apenas mude o nome do arquivo para Myclass.javae, em seguida, na linha de comando, compile-o assim javac Myclass.javae execute-o assim java Myclass.
unnsse 24/09/19
6
sim, javacainda usado para compilar se você não deseja implantar o código-fonte ou se possui mais de um único arquivo ( documentação da javaopção para arquivo de origem: usada apenas para iniciar um único programa de arquivo-fonte.)
user85421
@Matthieu a saída de "java -version" é: openjdk versão "12.0.2" 2019-07-16 OpenJDK Runtime Environment (compilação 12.0.2 + 10) OpenJDK 64-Bit VM VM (compilação 12.0.2 + 10, mista )
milad 24/09/19
1
@Milad - O que acontece é isso - javaccompila a fonte Java no bytecode interpretado específico da JVM e o javacomando a carrega dentro do ClassLoader da JVM.
Unnsse

Respostas:

188

Antes do Java 11, para executar seu código, você precisa primeiro compilá-lo e executá-lo. Aqui está um exemplo:

javac test.java
java test

Desde o Java 11, você ainda pode executar o javac+ java, ou pode executar javasozinho para compilar e executar automaticamente o seu código. Observe que nenhum .classarquivo será gerado. Aqui está um exemplo:

java test.java

Se você executar java -help, verá os vários usos permitidos. Aqui está o que parece na minha máquina. O último é o que você encontrou: o java [options] <sourcefile> [args]que "executará um único programa de arquivo de origem".

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

ATUALIZAR:

Conforme apontado por @BillK, o OP também perguntou:

por que precisamos do comando javac?

A razão pela qual precisamos javacé criar .classarquivos para que o código possa ser criado, testado, distribuído, executado, compartilhado etc. como é hoje. A motivação para o JEP 330 era facilitar os "estágios iniciais do aprendizado de Java e ao escrever pequenos programas utilitários" sem alterar outros usos existentes.

Kaan
fonte
obrigado @CarlosHeuberger pelos detalhes adicionais. Eu fiz uma pequena edição em minha resposta para refletir que foi introduzido em Java 11.
kaan
7
@Spikatrix que é Java 8 (Eles caiu a 1.em 1.8em versões mais recentes)
Muru
1
Você não respondeu à pergunta por que ainda precisamos do javac - acho que o java opera apenas no único arquivo que você fornece e nos arquivos compilados anteriormente. Eu acredito que você deve compilar todos os outros arquivos que deseja usar no arquivo que você chama.
Bill K
2
Esta resposta não aborda por que esse novo método não resulta em um erro de nome de arquivo x nome de classe, conforme relatado por javac.
sebrockm 27/09/19
52

Se você estiver executando o Java 11, há um novo recurso que permite a execução de arquivo de origem única. O compilador de origem única é mais promíscuo em termos de nome de classe versus nome de arquivo; é assim que você pode executar, mas não compila com êxito.

Se você estiver em uma versão anterior do Java, o hello.java atual não será compilado devido a erros de compilação, especificamente ao redor do nome da classe. Portanto, não há absolutamente nenhuma maneira de chamar java hello.java compilar seu código, porque ele não é compilado.

Parece muito provável que você estivesse executando algum código compilado anteriormente ao executar o comando java.

Evan
fonte
obrigado, a versão java é: openjdk versão "12.0.2" 2019-07-16 OpenJDK Runtime Environment (versão 12.0.2 + 10) VM do servidor OpenJDK de 64 bits (versão 12.0.2 + 10, modo misto)
milad
5
verifique Usando o modo de arquivo de origem para iniciar programas de código-fonte de arquivo único : "O compilador não impõe a restrição opcional definida no final do JLS ?? 7.6, que um tipo em um pacote nomeado deve existir em um arquivo cujo nome seja composto a partir do nome do tipo seguido pela extensão .java ".
user85421
2
A API de script Java e o Lançamento do programa de código-fonte de arquivo único Java ( JEP 330 ) são duas coisas completamente separadas e totalmente não relacionadas.
David Conrad
@DavidConrad, palavreado atualizado de acordo. Obrigado.
Evan
Aprecie a entrada, @TJCrowder. Mas tenho quase certeza de que pretendia escrevê-lo como está. Além disso, segunda definição em seus links: Promíscuo significa incluir uma ampla variedade de coisas diferentes.
Evan
6

Para responder por que esse erro é fornecido, o nome da classe do arquivo deve corresponder ao do arquivo basename.

Você tem duas opções para que esse código funcione para o tradicional javac; javaseqüência:

  1. Renomeie a classe para public class Helloou

  2. Renomeie hello.javapara myclass.java.

O javaintérprete para Java 11 não impõe esse requisito. A classe que contém mainpode ter qualquer nome, desde que seja a primeira classe no arquivo. O objetivo principal era facilitar o processo de aprendizado para iniciantes e permitir "scripts java" com o shebang ( ref. ).

SS Anne
fonte
5

Sim, mas não da maneira que você provavelmente quer dizer.

Quando você usa o javaccomando para compilar um arquivo .java em um arquivo .class, a saída é algo chamado bytecode. Bytecode é o código da máquina (instruções nativas) para uma CPU teórica com base na especificação da Java Virtual Machine.

Essa especificação de CPU virtual é uma espécie de média de tipos de CPUs que eram comuns no momento em que a especificação foi gravada. Por esse motivo, está próximo a muitos tipos diferentes de CPU, facilitando a execução dos mesmos arquivos Java .class em vários tipos de CPU.

Quando o Java foi iniciado pela primeira vez, o javacomando leu o arquivo .class e interpretou as instruções do bytecode uma de cada vez e, em seguida, mapeou-as para a instrução nativa equivalente para qualquer CPU em que estivesse realmente executando. Isso funcionou, mas não foi particularmente rápido. Para melhorar essa compilação Just in Time (JIT), foi adicionado ao Java Runtime.

Com o JIT, o javacomando pega o bytecode e o compila novamente com as instruções nativas da CPU em que está sendo executado. Os tempos de execução Java modernos tendem a começar a interpretar o bytecode enquanto o JIT compila em segundo plano e alterna para as instruções nativas compiladas quando estiver pronto e também cria um perfil do aplicativo em execução e recompila o bytecode novamente com otimização diferente para obter o melhor desempenho possível.

EDIT (para apaziguar os eleitores rebaixados):

Portanto, no seu caso específico (como você está executando um JRE mais recente que a v11), o código é compilado (pelo menos) duas vezes

  1. Como um único arquivo .java para bytecode
  2. Através do compilador JIT, uma vez que interpreta o bytecode (embora, para o helloWorld, ele possa não ter tempo para executar qualquer código nativo compilado)
hardillb
fonte
7
Isso não responde à pergunta.
David Conrad
2
@DavidConrad Mas faz! A resposta para "O comando 'java' compila programas Java?" é um retumbante "sim" pelas razões que o hardlib fornece aqui: ele compilará o código de bytes com instruções nativas na hora certa (para programas não triviais, com configurações padrão).
Peter - Restabelece Monica
A compilação agora é obrigatória? Historicamente, o código de bytes Java pode ser interpretado; A compilação JIT era opcional.
MSalters
O JIT está ativado por padrão hoje em dia (por muito tempo), como mostra o resultado mixed-modena versão
hardillb