Java Lendo Erros do javac

Todo programador, do iniciante ao veterano, vê erros de compilação todos os dias. A diferença é que o veterano lê a mensagem e sabe o que fazer em segundos. As mensagens do javac são em inglês, mas seguem sempre o mesmo formato e usam um vocabulário pequeno. Nesta aula você aprende a ler esse formato e conhece, um por um, os erros mais comuns de quem começa, com a mensagem real e a correção.

Você vai aprender
  • As partes de uma mensagem do javac: arquivo, linha, descrição, a linha copiada e o ^.
  • Um método para consertar erros: o primeiro, depois compilar de novo.
  • O pequeno vocabulário em inglês das mensagens.
  • Os erros mais comuns de iniciante, com código, mensagem real e correção.
  • A diferença entre erro de compilação e erro em tempo de execução.

Todos os códigos desta aula têm erros de propósito e foram compilados com o javac 25 na máquina da Ana; as mensagens são copiadas da tela, sem mudar nada.


A ideia: o corretor de redação

Pense numa professora corrigindo uma redação. Ela não reescreve o texto: marca a linha com caneta vermelha, sublinha a palavra e escreve na margem o que está errado ("falta vírgula", "concordância"). O javac faz o mesmo. Ele não conserta nada sozinho, mas diz onde está o problema e o que ele achou de errado. O seu trabalho é ler a anotação com calma.

E um detalhe importante: um erro de compilação não quebra nada. O programa simplesmente não é gerado até o erro ser corrigido. Pode errar à vontade.


Anatomia de uma mensagem

A Ana esqueceu o ponto e vírgula no fim da linha 3:

public class Main {
    public static void main(String[] args) {
        System.out.println("Olá, Ana")
    }
}
$ javac Main.java
Main.java:3: error: ';' expected
        System.out.println("Olá, Ana")
                                      ^
1 error
Main.java
O arquivo onde está o erro. Em projetos com vários arquivos, é a primeira coisa a olhar.
:3
O número da linha. Os editores mostram os números na margem; a caixa Experimente também.
error:
O tipo da mensagem: erro (impede a compilação). Existe também warning: ("aviso"), que não impede.
';' expected
A descrição: "esperava um ;".
System.out.println(…)
A linha do código copiada, para você não precisar procurar.
^
O acento circunflexo aponta a coluna exata onde o javac percebeu o problema. Aqui, logo depois do ), onde o ; deveria estar.
1 error
A contagem: quantos erros no total.

Algumas mensagens têm linhas extras embaixo do ^, com detalhes:

$ javac Main.java
Main.java:4: error: cannot find symbol
        System.out.println(ntoa);
                           ^
  symbol:   variable ntoa              « o que ele não achou: uma variável chamada ntoa
  location: class Main                 « onde procurou: na classe Main
1 error
O ^ mostra onde o javac percebeu, não necessariamente onde você errou

No primeiro exemplo, o erro é da linha 3, e a mensagem diz linha 3. Mas às vezes o compilador só percebe o problema mais adiante: uma chave esquecida no meio do arquivo pode ser acusada só na última linha. Se a linha apontada parece certa, olhe a linha de cima.


O método: o primeiro erro, depois compilar de novo

  1. Leia só o primeiro erro. Os seguintes muitas vezes são consequência dele.
  2. Vá à linha indicada e olhe onde o ^ aponta (e a linha de cima, se não achar nada).
  3. Traduza a descrição com a tabela de vocabulário abaixo.
  4. Conserte e compile de novo. Podem aparecer erros novos: é normal, e é progresso.

Veja o passo 4 acontecer. Este código tem dois problemas, na linha 4 e na linha 5:

public class Main {
    public static void main(String[] args) {
        System.out.println("Oi");
        System.out.println("Tchau")
        System.out.println(total);
    }
}
$ javac Main.java
Main.java:4: error: ';' expected
        System.out.println("Tchau")
                                   ^
1 error                                    « só um erro?

Só um erro foi mostrado. O javac trabalha em etapas: primeiro confere a "gramática" (pontuação, chaves); só se ela estiver toda certa ele passa a conferir os nomes. Depois de pôr o ;:

$ javac Main.java
Main.java:5: error: cannot find symbol
        System.out.println(total);
                           ^
  symbol:   variable total
  location: class Main
1 error                                    « o erro de nome só aparece agora

O contrário também acontece: um único engano gera vários erros. O nome inválido int class = 10; produziu três mensagens na aula Sintaxe e Estrutura. Consertado o engano, as três somem juntas.

Missão: conserte o programa acima usando o método: rode, conserte o primeiro erro, rode de novo. A variável total não existe; apague essa linha ou crie a variável antes dela (int total = 10;).

public class Main {
    public static void main(String[] args) {
        System.out.println("Oi");
        System.out.println("Tchau")
        System.out.println(total);
    }
}
public class Main {
    public static void main(String[] args) {
        int total = 10;
        System.out.println("Oi");
        System.out.println("Tchau");
        System.out.println(total);
    }
}

O vocabulário das mensagens

Com umas vinte palavras, você entende quase todas as mensagens:

InglêsQuer dizer
expectedesperado, "esperava encontrar"
cannot find symbolnão consigo encontrar o símbolo (um nome: variável, método, classe)
symbol / locationo nome procurado / onde ele foi procurado
incompatible typestipos incompatíveis
cannot be converted tonão pode ser convertido em
missingfaltando
unclosednão fechado
illegalinválido, proibido ali
reached end of filecheguei ao fim do arquivo
while parsingenquanto analisava (lia a estrutura do código)
not a statementnão é uma instrução
might not have been initializedpode não ter recebido valor
withoutsem
should be declared indeveria estar declarada em
does not existnão existe
already definedjá definido (nome repetido)

Os erros mais comuns de iniciante

Cada erro abaixo tem o código com o problema, a mensagem real, o que ela quer dizer e como consertar. Nos programas de verdade eles aparecem dentro de public class Main e do main; aqui, para economizar espaço, alguns mostram só o trecho importante, mas a mensagem é a do programa completo.

1. ';' expected

$ javac Main.java
Main.java:3: error: ';' expected
        System.out.println("Olá, Ana")
                                      ^
1 error

Quer dizer: "esperava um ponto e vírgula". A instrução acabou e o ; não veio. Conserto: ponha o ; onde o ^ aponta. Variação: com um espaço no meio de um nome (Sys tem.out) ou um nome inválido (int nota final), o javac também pede um ;, porque acha que a instrução acabou antes do espaço (veja Sintaxe e Estrutura).

2. cannot find symbol: erro de digitação

int nota = 8;
System.out.println(ntoa);
$ javac Main.java
Main.java:4: error: cannot find symbol
        System.out.println(ntoa);
                           ^
  symbol:   variable ntoa
  location: class Main
1 error

Quer dizer: "não encontrei nada chamado ntoa". O symbol: diz o que faltou e de que tipo (variable, method, class). Conserto: compare a grafia com a da declaração (nota). Também aparece quando a variável ainda não foi criada, foi criada mais abaixo, ou quando você esqueceu as aspas de um texto (println(Olá)).

3. cannot find symbol: maiúscula e minúscula

$ javac Main.java
Main.java:3: error: cannot find symbol
        string nome = "Ana";
        ^
  symbol:   class string
  location: class Main
1 error
$ javac Main.java
Main.java:3: error: cannot find symbol
        System.out.Println("Oi");
                  ^
  symbol:   method Println(String)
  location: variable out of type PrintStream
1 error

Quer dizer: no primeiro, "não existe a classe string" (o certo é String); no segundo, "não existe o método Println que recebe um String, no out" (o certo é println). Conserto: o Java diferencia maiúsculas: String, System, println. Com system minúsculo, a mensagem é outra: error: package system does not exist.

4. class Main is public, should be declared in a file named 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

Quer dizer: "a classe Main é pública e deveria estar num arquivo chamado Main.java". Conserto: renomeie o arquivo ou a classe para ficarem iguais, com as mesmas maiúsculas (veja Olá, Mundo).

5. incompatible types: String cannot be converted to int

int idade = "29";
$ javac Main.java
Main.java:3: error: incompatible types: String cannot be converted to int
        int idade = "29";
                    ^
1 error

Quer dizer: "tipos incompatíveis: um texto (String) não pode ser convertido em inteiro (int)". Com aspas, "29" é texto, e a caixa idade só aceita números inteiros. Conserto: tire as aspas (int idade = 29;). Se o número vem mesmo de um texto (do teclado, por exemplo), ele precisa ser convertido (veja Conversão de Tipos). Outras combinações aparecem com o mesmo começo: ao guardar 2.5 num int, a mensagem é incompatible types: possible lossy conversion from double to int ("possível perda na conversão de double para int").

6. reached end of file while parsing

public class Main {
    public static void main(String[] args) {
        System.out.println("Oi");
    }
$ javac Main.java
Main.java:4: error: reached end of file while parsing
    }
     ^
1 error

Quer dizer: "cheguei ao fim do arquivo enquanto analisava": faltou fechar uma chave, e o arquivo acabou com um bloco ainda aberto. O ^ aponta para depois da última chave. Conserto: conte as chaves; com o recuo certo, é fácil ver qual { ficou sem par (aqui, a da classe). Uma aspa ou um comentário /* sem fechar podem causar o mesmo erro.

7. missing return statement

public class Main {
    static int dobro(int n) {
        int r = n * 2;
    }

    public static void main(String[] args) {
        System.out.println(dobro(4));
    }
}
$ javac Main.java
Main.java:4: error: missing return statement
    }
    ^
1 error

Quer dizer: "falta a instrução return". O método dobro promete devolver um int (está escrito int dobro), mas chega à chave final sem devolver nada. Você vai criar métodos assim em Métodos e Valores de Retorno. Conserto: devolva o valor (return r;) antes da }. Se o método não deve devolver nada, ele deve ser void.

8. unclosed string literal

$ javac Main.java
Main.java:3: error: unclosed string literal
        System.out.println("Olá, Ana);
                           ^
1 error

Quer dizer: "texto não fechado": uma aspa abriu um texto e a linha acabou antes da aspa de fechamento. O ^ aponta a aspa que abriu. Repare que tudo depois dela, inclusive o );, virou parte do texto. Conserto: feche as aspas: "Olá, Ana". Com aspas simples num texto ('Olá'), o erro parecido é unclosed character literal: em Java, aspas simples são só para um caractere (veja char e boolean).

9. not a statement

int nota = 8;
nota;
$ javac Main.java
Main.java:4: error: not a statement
        nota;
        ^
1 error

Quer dizer: "isto não é uma instrução": a linha tem um valor solto, que não faz nada (não escreve, não guarda, não chama ninguém). Conserto: diga o que fazer com o valor: System.out.println(nota); ou nota = nota + 1;. Também aparece em nomes inválidos, como int 2notas = 10;. No JShell, ao contrário, um valor solto é permitido: ele mostra o valor.

10. 'else' without 'if'

int nota = 8;
if (nota >= 7);
    System.out.println("Aprovada");
else
    System.out.println("Reprovada");
$ javac Main.java
Main.java:6: error: 'else' without 'if'
        else
        ^
1 error

Quer dizer: "um else sem if". O if está lá, mas o ; logo depois do if (nota >= 7) encerrou o if ali mesmo, com uma instrução vazia. O println("Aprovada") virou uma linha comum, e o else ficou órfão. Você vai estudar o if em if, else if, else. Conserto: tire o ; depois do parêntese e use chaves:

if (nota >= 7) {
    System.out.println("Aprovada");
} else {
    System.out.println("Reprovada");
}

11. variable nota might not have been initialized

int nota;
System.out.println(nota);
$ javac Main.java
Main.java:4: error: variable nota might not have been initialized
        System.out.println(nota);
                           ^
1 error

Quer dizer: "a variável nota pode não ter recebido um valor". Ela foi criada (int nota;), mas a caixa está vazia, e o Java não deixa usar uma caixa vazia. Conserto: dê um valor antes de usar: int nota = 8; (veja Variáveis).

Missão: este programa junta três erros da lista (um ;, uma maiúscula e uma variável sem valor). Rode, identifique cada um pela mensagem e conserte até aparecer Nota: 8.

public class Main {
    public static void main(String[] args) {
        int nota;
        String texto = "Nota: "
        system.out.println(texto + nota);
    }
}
public class Main {
    public static void main(String[] args) {
        int nota = 8;
        String texto = "Nota: ";
        System.out.println(texto + nota);
    }
}

Resumo dos erros

MensagemCausa mais comumConserto
';' expectedFaltou ;Pôr o ; no fim da instrução
cannot find symbolNome digitado errado, maiúscula, variável não criada, texto sem aspasConferir a grafia com a declaração
package system does not existsystem minúsculoSystem
class X is public, should be declared in a file named X.javaNome do arquivo ≠ nome da classeRenomear um dos dois
incompatible typesValor do tipo errado (texto numa caixa de número)Usar o tipo certo; tirar as aspas
reached end of file while parsingFaltou fechar }Contar as chaves
missing return statementMétodo sem returnreturn valor; ou void
unclosed string literalAspa sem parFechar as aspas
not a statementValor solto; nome inválidoUsar o valor (println, atribuição)
'else' without 'if'; depois do if (…)Tirar o ;, usar chaves
might not have been initializedVariável usada sem valorDar um valor ao criar

A lista completa, com dezenas de outras mensagens, fica na referência Erros do Compilador.


Erro de compilação × erro em tempo de execução

Tudo nesta aula é erro de compilação: o javac recusa o código e nenhum programa é gerado. Existe outro tipo: código que compila sem reclamação, mas quebra enquanto roda, quando tenta fazer algo impossível. Esse é um erro em tempo de execução, e em Java ele aparece como uma exceção:

$ javac Div.java                   « compilou sem erro nenhum
$ java Div
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Div.main(Div.java:3)
Erro de compilaçãoErro em tempo de execução (exceção)
Quando apareceAo compilar (javac)Ao rodar (java)
Quem avisaO compiladorA JVM
FormatoMain.java:3: error: … + linha + ^Exception in thread "main" java.lang.NomeDaExcecao: … + at …(Main.java:3)
O programa rodou?Não; nem existe .class novoSim, até a linha do problema
Causa típicaEscrita: pontuação, nomes, tiposSituação: divisão por zero, posição que não existe numa lista, valor vazio (null)
Código de saídajavac devolve 1java devolve 1
Onde estudarEsta aula e Erros do CompiladorO que são Exceções e Exceções Comuns

Os erros de compilação são os "bons": aparecem cedo, sempre, com a linha marcada. Um dos motivos de o Java conferir tantas coisas na compilação (como os tipos das variáveis) é transformar erros que só apareceriam rodando, talvez na frente de um cliente, em erros que aparecem na sua tela antes.

Missão: aqui o objetivo é o contrário: provoque o erro unclosed string literal. Apague a aspa de fechamento do texto e rode. Ver o erro de propósito ajuda a reconhecê-lo depois.

public class Main {
    public static void main(String[] args) {
        System.out.println("Olá, Ana");
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("Olá, Ana);
    }
}

Exercícios

1. A linha do erro

A mensagem é Loja.java:12: error: ';' expected. Em que linha está o problema?

2. O que faltou

A mensagem é error: reached end of file while parsing. Que símbolo provavelmente faltou?

3. O javac mostrou 5 erros. Qual é a melhor estratégia?

4. int idade = "29"; dá qual erro?

5. Em cannot find symbol … symbol: variable totl, o que provavelmente aconteceu?

6. Exception in thread "main" java.lang.ArithmeticException é…


Resumo

  • Formato: Arquivo.java:LINHA: error: descrição, a linha copiada, um ^ na coluna, e a contagem.
  • Leia o primeiro erro, conserte, compile de novo. Erros novos depois de um conserto são normais.
  • O ^ marca onde o javac percebeu; se a linha parece certa, olhe a de cima.
  • Os mais comuns: ';' expected, cannot find symbol, class … is public, incompatible types, reached end of file, missing return statement, unclosed string literal, not a statement, 'else' without 'if', might not have been initialized.
  • Erro de compilação: nada roda. Exceção: rodou e quebrou. A referência completa é Erros do Compilador.