Java javac e java
Um programa Java chega à tela em duas etapas: o javac compila o arquivo .java e gera um .class; o java executa esse .class. Nesta aula você faz o caminho completo no terminal, conhece o atalho java Main.java e aprende a reconhecer os erros mais comuns de quem compila e roda pela primeira vez.
- Compilar com
javac Main.javae ver oMain.classaparecer. - Executar com
java Main(e por que é sem.class). - Por que mudar o
.javasem compilar de novo não muda nada. - O modo arquivo-fonte,
java Main.javaJava 11+, que compila e roda de uma vez. - Os erros reais:
java Main.class,java main, arquivo com nome errado, classe não encontrada eUnsupportedClassVersionError. - O que os códigos de saída dizem.
Você precisa do JDK instalado (Linux, Windows e macOS). As sessões são do terminal da Ana, com o JDK 25; no Windows, os comandos javac e java são os mesmos. Se ainda não tem o JDK, acompanhe pelas caixas Experimente.
A ideia: traduzir uma vez, ler muitas
Pense num livro escrito em português que vai ser lido por uma máquina que só entende uma língua especial. Você contrata um tradutor (o javac), que traduz o livro inteiro de uma vez e entrega a versão traduzida (o .class). Depois, um leitor (o java, que liga a JVM) lê a versão traduzida quantas vezes quiser, sem precisar do tradutor.
Main.java ──── javac Main.java ────► Main.class ──── java Main ────► Olá, mundo! (código-fonte, (compila: confere (bytecode) (executa: a JVM (a saída) você escreve) e traduz) lê o bytecode)
Se o livro original mudar, a tradução antiga continua igual até alguém traduzir de novo. Guarde essa ideia: ela explica uma das surpresas desta aula.
Passo 1: o arquivo
A Ana cria uma pasta para o programa e escreve o arquivo Main.java com o editor dela (veja Editores e IDEs). Para conferir o conteúdo pelo terminal, o cat mostra o arquivo:
$ mkdir -p ~/java/ola
$ cd ~/java/ola
$ cat Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Olá, mundo!");
}
}
O programa é o da aula Olá, Mundo. Lembre: a classe se chama Main, então o arquivo se chama Main.java.
Passo 2: compilar com javac
- javac
- O compilador do Java (Java compiler). Lê o código, confere se segue as regras da linguagem e gera o bytecode.
- Main.java
- O arquivo a compilar, com a extensão
.java. Maiúsculas e minúsculas contam.
$ javac Main.java $ echo $? 0 « código de saída 0: compilou sem erro
O javac não escreveu nada. No terminal, silêncio quer dizer sucesso: se houvesse um erro, ele mostraria a mensagem. O echo $? mostra o código de saída do último comando; 0 confirma que deu certo. O que mudou foi a pasta:
$ ls -l total 8 -rw-rw-r-- 1 ana ana 414 out 16 18:44 Main.class « novo: o bytecode, 414 bytes -rw-rw-r-- 1 ana ana 117 out 16 18:44 Main.java « o seu código, 117 bytes
Apareceu o Main.class, com o nome da classe (não do arquivo) e a extensão .class. É ele que a JVM vai executar. Se tiver curiosidade sobre o que tem dentro, a aula JDK, JRE e JVM abre um .class com o javap.
Passo 3: executar com java
- java
- O lançador: liga a JVM e pede a ela para executar uma classe.
- Main
- O nome da classe, sem
.classe sem.java. A JVM procura o arquivoMain.classsozinha, na pasta atual, e começa pelo métodomain.
$ java Main Olá, mundo! $ echo $? 0 « o programa terminou normalmente
Pronto: compilou uma vez, rodou. Você pode rodar java Main de novo quantas vezes quiser, sem compilar outra vez.
Missão: o botão Rodar faz o javac e o java de uma vez. Faça o programa mostrar duas linhas: Compilei e, embaixo, e rodei!.
public class Main {
public static void main(String[] args) {
System.out.println("Olá, mundo!");
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Compilei");
System.out.println("e rodei!");
}
}
A surpresa: mudei o código e nada mudou
A Ana troca mundo por Ana no arquivo (aqui, com o comando sed, que edita texto pelo terminal; no dia a dia, ela usaria o editor) e roda de novo:
$ sed -i 's/Olá, mundo!/Olá, Ana!/' Main.java
$ cat Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Olá, Ana!"); « o código-fonte mudou
}
}
$ java Main
Olá, mundo! « mas a saída é a antiga!
O java executa o Main.class, e o Main.class ainda é a "tradução" antiga. O .java mudou; o .class, não. É preciso compilar de novo:
$ javac Main.java $ java Main Olá, Ana!
.java, rode o javac
Um jeito de nunca esquecer é juntar os dois comandos com && ("e, se deu certo, então"): javac Main.java && java Main. Se a compilação falhar, o java nem roda, e você não vê uma saída velha por engano. Ou use o modo arquivo-fonte, logo abaixo.
O atalho: java Main.java Java 11+
Desde o Java 11, o java aceita direto um arquivo .java. Ele compila o código na memória e já executa, sem criar .class nenhum. É o modo arquivo-fonte (source-file mode):
$ rm Main.class « apaga o .class, para mostrar que não precisa dele $ java Main.java Olá, Ana! $ ls Main.java « nenhum .class foi criado
Como não sobra .class, o java Main (sem .java) agora não tem o que executar:
$ java Main Erro: Não foi possível localizar nem carregar a classe principal Main Causada por: java.lang.ClassNotFoundException: Main
Se o código tiver erro, o modo arquivo-fonte mostra a mensagem do compilador, mais uma linha error: compilation failed ("a compilação falhou"):
$ java Erro.java
Erro.java:2: error: ';' expected
IO.println("Oi")
^
1 error
error: compilation failed
Qual usar?
javac Main.java + java Main | java Main.java | |
|---|---|---|
| Comandos | Dois | Um |
Cria .class | Sim | Não (compila na memória) |
| Compila de novo a cada execução | Não: roda o .class pronto, mais rápido | Sim: sempre usa o código atual |
| Versões do Java | Todas | 11 ou mais nova |
| Bom para | Entender o processo; distribuir o programa compilado; projetos com muitas classes | Programas de um arquivo, testes rápidos, exemplos deste guia |
Nas aulas, você vai ver os dois. Quando a saída é o que importa, o guia usa java Main.java; quando o processo importa, usa javac e java separados. Desde o Java 22, o modo arquivo-fonte também encontra as outras classes do programa em arquivos .java da mesma pasta.
Outra forma: separar o código do bytecode
Em projetos maiores, misturar .java e .class na mesma pasta vira bagunça. A opção -d (de directory, pasta) do javac manda os .class para outro lugar, e a opção -cp (de classpath, "caminho das classes") do java diz onde procurá-los:
$ javac -d classes Main.java « os .class vão para a pasta classes (criada sozinha) $ ls -R .: classes Main.java ./classes: Main.class $ java -cp classes Main « procure as classes na pasta classes Olá, Ana!
O classpath é a lista de lugares onde a JVM procura classes. Quando você não diz nada, ele é a pasta atual (.). Por isso o java Main só funciona na pasta onde está o Main.class. Ferramentas como o Maven e o Gradle fazem essa organização por você.
Quando dá errado
Os erros desta seção são os do caminho entre o arquivo e a execução. Os erros no código (ponto e vírgula, nomes) estão em Lendo Erros do javac.
java Main.class: "Não foi possível localizar nem carregar a classe principal Main.class"
$ java Main.class Erro: Não foi possível localizar nem carregar a classe principal Main.class Causada por: java.lang.ClassNotFoundException: Main.class $ echo $? 1
O que aconteceu: o java recebe o nome de uma classe, não de um arquivo. Ele procurou uma classe chamada Main.class (que seria a classe class num pacote Main!) e não achou. Em inglês, a mensagem é Error: Could not find or load main class Main.class, seguida de Caused by: java.lang.ClassNotFoundException: Main.class.
Como resolver: java Main, sem extensão.
java main (minúscula): a mesma mensagem
$ java main Erro: Não foi possível localizar nem carregar a classe principal main Causada por: java.lang.ClassNotFoundException: main
O que aconteceu: o Java diferencia maiúsculas de minúsculas: main e Main são nomes diferentes, e o Linux também diferencia main.class de Main.class. No Windows e no macOS, que não diferenciam nomes de arquivo, o arquivo é encontrado, mas a JVM percebe que a classe dentro dele tem outro nome e dá outro erro (ilustrativo): Caused by: java.lang.NoClassDefFoundError: Main (wrong name: main), "nome errado".
Como resolver: escreva o nome exatamente como na linha public class Main.
$ cd ~ $ java Main Erro: Não foi possível localizar nem carregar a classe principal Main Causada por: java.lang.ClassNotFoundException: Main
O que aconteceu: o nome está certo, mas o Main.class não está na pasta atual. Ou você está em outra pasta (aqui, a Ana voltou para ~), ou esqueceu de compilar, ou compilou com -d para outra pasta. Em inglês: Error: Could not find or load main class Main.
Como resolver: ls para ver se o Main.class está ali; cd para a pasta certa ou diga onde ele está com -cp (java -cp ~/java/ola/classes Main).
error: file not found: main.java
$ javac main.java error: file not found: main.java Usage: javac <options> <source files> use --help for a list of possible options
O que aconteceu: não existe arquivo com esse nome exato na pasta (aqui, a maiúscula; pode ser também o .txt escondido, veja Editores e IDEs). A segunda e a terceira linhas são só a "ajuda" do javac: "uso: javac <opções> <arquivos-fonte>". Como resolver: ls e copie o nome exato (ou use Tab para completar).
javac Main (sem .java)
$ javac Main error: Class names, 'Main', are only accepted if annotation processing is explicitly requested 1 error
O que aconteceu: o contrário do erro do java Main.class: o javac recebe arquivos e quer a extensão. A mensagem fala de um recurso avançado ("processamento de anotações") que não tem nada a ver com o seu caso. Como resolver: javac Main.java.
$ javac Ola.java
Ola.java:1: error: class Main is public, should be declared in a file named Main.java
public class Main {
^
1 error
O que aconteceu: o arquivo Ola.java tem a classe pública Main. Como resolver: renomeie o arquivo para Main.java ou a classe para Ola (veja Olá, Mundo). Curiosidade: o modo arquivo-fonte não liga para essa regra, e java Ola.java roda normalmente.
UnsupportedClassVersionError: compilado numa versão mais nova
A Ana compilou com o JDK 25 e levou o Main.class para um computador que tem o Java 21. Na máquina dela há um Java 21 instalado pelo sistema, então dá para ver o que acontece:
$ javac Main.java $ /usr/lib/jvm/java-21-openjdk-amd64/bin/java Main Erro: ocorreu LinkageError ao carregar a classe principal Main java.lang.UnsupportedClassVersionError: Main has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 65.0 $ echo $? 1
O que aconteceu: todo .class guarda a versão do formato do bytecode: 69 é Java 25, 65 é Java 21 (a regra é a versão do Java mais 44). A mensagem diz: "Main foi compilada por uma versão mais recente do Java (formato 69.0); esta versão só reconhece até o 65.0". Uma JVM antiga não sabe ler bytecode novo, assim como um programa antigo pode não abrir um documento salvo numa versão nova. (O contrário funciona: um .class antigo roda numa JVM nova.) Num Java 8, a mesma situação mostra Exception in thread "main" java.lang.UnsupportedClassVersionError: … up to 52.0, seguido de várias linhas at java.lang.ClassLoader….
Como resolver: rode com um Java igual ou mais novo que o do javac (confira java -version e javac -version; veja Versões e LTS), ou compile para a versão mais antiga com --release:
$ javac --release 21 Main.java && /usr/lib/jvm/java-21-openjdk-amd64/bin/java Main Olá, mundo!
Com --release 21, o javac gera bytecode versão 65 e só aceita recursos que já existiam no Java 21.
Os códigos de saída
Todo programa devolve um número ao terminar, o código de saída, que o terminal guarda em $? (no Windows: %ERRORLEVEL% no Prompt de Comando, $LASTEXITCODE no PowerShell). Scripts e ferramentas usam esse número para saber se algo deu certo.
| Situação | Código |
|---|---|
javac compilou sem erros | 0 |
javac achou erros no código | 1 |
java: o programa chegou ao fim do main | 0 |
java: o programa terminou com uma exceção | 1 |
java: classe não encontrada, main não encontrado, versão incompatível | 1 |
java: o programa chamou System.exit(N) | N |
O terminal não achou o comando (javac: comando não encontrado) | 127 |
Um programa pode escolher o próprio código com System.exit, que encerra o programa na hora:
public class Sai {
public static void main(String[] args) {
System.out.println("antes");
System.exit(3);
}
}
$ java Sai.java; echo $? antes 3 « o número passado ao System.exit
E um programa que termina com exceção devolve 1:
$ java Div.java; echo $? Exception in thread "main" java.lang.ArithmeticException: / by zero at Div.main(Div.java:3) 1
Missão: faça o programa mostrar Estoque vazio e terminar com o código de saída 3. O editor mostra o código no fim da saída.
public class Main {
public static void main(String[] args) {
System.out.println("Verificando o estoque...");
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Verificando o estoque...");
System.out.println("Estoque vazio");
System.exit(3);
}
}
Referência
| Comando | O que faz |
|---|---|
javac Main.java | Compila; gera Main.class. |
javac *.java | Compila todos os .java da pasta. |
javac -d classes Main.java | Compila e põe os .class na pasta classes. |
javac --release 21 Main.java | Compila para rodar no Java 21. |
java Main | Executa a classe Main (procura Main.class na pasta atual). |
java -cp classes Main | Executa procurando as classes na pasta classes. |
java Main.java | Compila na memória e executa Java 11+. |
java Main.java Ana 29 | Executa passando argumentos (veja Argumentos). |
javac Main.java && java Main | Compila e, só se deu certo, executa. |
echo $? | Mostra o código de saída do último comando. |
Exercícios
1. Compilar
Complete o comando que compila o arquivo Loja.java:
2. Executar
Depois de compilar, complete o comando que executa a classe Loja:
3. Você mudou o texto do println no Main.java e rodou java Main, mas a saída é a antiga. Por quê?
4. A mensagem diz class file version 69.0 e only recognizes class file versions up to 61.0. O que está acontecendo?
5. Qual comando roda um programa de um arquivo só sem criar .class?
6. Um programa terminou com Exception in thread "main". Qual é o código de saída?
Resumo
javac Main.javacompila (com.java) e geraMain.class; silêncio = sucesso.java Mainexecuta a classe (sem extensão); procura o.classna pasta atual ou no-cp.- Mudou o
.java? Compile de novo. Ou usejavac Main.java && java Main. java Main.javaJava 11+ compila na memória e roda, sem.class.- "Não foi possível localizar nem carregar a classe principal" = nome errado (
.class, maiúscula) ou pasta errada. UnsupportedClassVersionError=.classde um Java mais novo que ojavaque roda; use um Java mais novo ou--release.- Códigos de saída:
0sucesso;1erro de compilação ou exceção;System.exit(N)devolveN.