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.

Você vai aprender
  • Compilar com javac Main.java e ver o Main.class aparecer.
  • Executar com java Main (e por que é sem .class).
  • Por que mudar o .java sem compilar de novo não muda nada.
  • O modo arquivo-fonte, java Main.java Java 11+, que compila e roda de uma vez.
  • Os erros reais: java Main.class, java main, arquivo com nome errado, classe não encontrada e UnsupportedClassVersionError.
  • 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 .class e sem .java. A JVM procura o arquivo Main.class sozinha, na pasta atual, e começa pelo método main.
$ 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!
Regra: mudou o .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 Mainjava Main.java
ComandosDoisUm
Cria .classSimNão (compila na memória)
Compila de novo a cada execuçãoNão: roda o .class pronto, mais rápidoSim: sempre usa o código atual
Versões do JavaTodas11 ou mais nova
Bom paraEntender o processo; distribuir o programa compilado; projetos com muitas classesProgramas 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.

"Não foi possível localizar nem carregar a classe principal Main" (sem erro de digitação)
$ 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.

Arquivo com nome diferente da classe
$ 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çãoCódigo
javac compilou sem erros0
javac achou erros no código1
java: o programa chegou ao fim do main0
java: o programa terminou com uma exceção1
java: classe não encontrada, main não encontrado, versão incompatível1
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

ComandoO que faz
javac Main.javaCompila; gera Main.class.
javac *.javaCompila todos os .java da pasta.
javac -d classes Main.javaCompila e põe os .class na pasta classes.
javac --release 21 Main.javaCompila para rodar no Java 21.
java MainExecuta a classe Main (procura Main.class na pasta atual).
java -cp classes MainExecuta procurando as classes na pasta classes.
java Main.javaCompila na memória e executa Java 11+.
java Main.java Ana 29Executa passando argumentos (veja Argumentos).
javac Main.java && java MainCompila 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.java compila (com .java) e gera Main.class; silêncio = sucesso.
  • java Main executa a classe (sem extensão); procura o .class na pasta atual ou no -cp.
  • Mudou o .java? Compile de novo. Ou use javac Main.java && java Main.
  • java Main.java Java 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 = .class de um Java mais novo que o java que roda; use um Java mais novo ou --release.
  • Códigos de saída: 0 sucesso; 1 erro de compilação ou exceção; System.exit(N) devolve N.