Java static

Até agora, cada objeto que você criou tinha os seus próprios dados: cada Aluno com o seu nome, cada Conta com o seu saldo. Mas algumas informações não são de um objeto: são da classe inteira, como "quantos alunos já foram criados". A palavra static serve para isso. Você já a escreve desde o primeiro programa, em public static void main; nesta aula você vai entender o que ela faz.

Você vai aprender
  • A diferença entre um membro do objeto (de instância) e um membro da classe (static).
  • Como fazer um contador de objetos criados.
  • O que são métodos static, como Math.sqrt, e quando criar os seus.
  • Por que o main é static (e o que mudou no Java 25).
  • O que é um bloco static { } e quando ele roda.
  • O que um método static não pode acessar, e o erro non-static variable … cannot be referenced from a static context.

Esta aula usa classes, atributos e construtores. Se algum desses nomes ainda é estranho, reveja Classes e Objetos, Atributos e Construtores.


A ideia: o caderno de cada aluno e o quadro da sala

Pense numa sala de aula. Cada aluno tem o seu caderno: o que a Ana escreve no dela não aparece no do Bruno. Mas a sala tem um quadro só, na parede, que todos veem. Se a professora escreve "prova na sexta" no quadro, todos os alunos leem a mesma frase; se ela apaga, some para todos.

  • O caderno é um atributo comum, de instância: cada objeto tem uma cópia. (Instância é outro nome para "objeto criado a partir de uma classe".)
  • O quadro é um atributo static: existe uma única cópia, que pertence à classe e é compartilhada por todos os objetos. Ele existe até antes de o primeiro aluno chegar.

A palavra vem do inglês static, "estático", "parado". O nome é histórico (vem das linguagens C e C++, onde indicava algo que fica num lugar fixo da memória). Em Java, a melhor tradução mental é "da classe": um membro static é da classe; um membro sem static é de cada objeto.


O contador de alunos

A Ana quer saber quantos objetos Aluno o programa criou. A primeira ideia é colocar um atributo total na classe e somar 1 no construtor. Veja o que acontece sem static:

public class Main {
    public static void main(String[] args) {
        Aluno a = new Aluno("Ana");
        Aluno b = new Aluno("Bruno");
        Aluno c = new Aluno("Carla");
        System.out.println(a.nome + ", " + b.nome + ", " + c.nome);
        System.out.println("Alunos criados: " + a.total + " " + b.total + " " + c.total);
    }
}

class Aluno {
    String nome;
    int total = 0;          // SEM static: cada aluno tem o seu

    Aluno(String nome) {
        this.nome = nome;
        total++;
    }
}
$ java Main.java
Ana, Bruno, Carla
Alunos criados: 1 1 1          « cada aluno contou só a si mesmo

Cada objeto ganhou o seu total, que começou em 0 e virou 1 no construtor. É como se cada aluno escrevesse "1" no próprio caderno. O que a Ana quer é um número no quadro: basta uma palavra.

public class Main {
    public static void main(String[] args) {
        Aluno a = new Aluno("Ana");
        Aluno b = new Aluno("Bruno");
        Aluno c = new Aluno("Carla");
        System.out.println(a.nome + ", " + b.nome + ", " + c.nome);
        System.out.println("Alunos criados: " + Aluno.total);
    }
}

class Aluno {
    String nome;            // cada aluno tem o seu
    static int total = 0;   // um só, da classe Aluno

    Aluno(String nome) {
        this.nome = nome;
        total++;
    }
}
String nome;
Atributo de instância: cada new Aluno(…) cria uma variável nome nova, dentro daquele objeto.
static
"Este atributo é da classe." Existe uma variável total só, criada quando a classe Aluno é carregada, antes de qualquer objeto.
int total = 0;
O tipo, o nome e o valor inicial, como em qualquer atributo. O = 0 roda uma vez, e não a cada objeto.
total++;
Dentro do construtor, soma 1 ao total compartilhado. Três construtores rodaram: 0 → 1 → 2 → 3.
Aluno.total
Fora da classe, um membro static é acessado pelo nome da classe, sem precisar de objeto.
$ java Main.java
Ana, Bruno, Carla
Alunos criados: 3

Agora o total é um só. Na memória, a situação fica assim:

OndeO que existe aliQuantas cópias
Classe Alunototal = 31 (da classe)
Objeto anome = "Ana"1 por objeto
Objeto bnome = "Bruno"
Objeto cnome = "Carla"

Missão: crie mais dois alunos (Daniel e Eva) no main e rode de novo. O contador deve mostrar Alunos criados: 5, sem você mexer na classe Aluno. Depois, como teste, apague a palavra static e veja o erro que aparece na linha do Aluno.total.

public class Main {
    public static void main(String[] args) {
        Aluno a = new Aluno("Ana");
        Aluno b = new Aluno("Bruno");
        Aluno c = new Aluno("Carla");
        System.out.println("Alunos criados: " + Aluno.total);
    }
}

class Aluno {
    String nome;
    static int total = 0;

    Aluno(String nome) {
        this.nome = nome;
        total++;
    }
}
public class Main {
    public static void main(String[] args) {
        Aluno a = new Aluno("Ana");
        Aluno b = new Aluno("Bruno");
        Aluno c = new Aluno("Carla");
        Aluno d = new Aluno("Daniel");
        Aluno e = new Aluno("Eva");
        System.out.println("Alunos criados: " + Aluno.total);
    }
}

class Aluno {
    String nome;
    static int total = 0;

    Aluno(String nome) {
        this.nome = nome;
        total++;
    }
}

Pelo objeto também funciona (mas confunde)

O Java deixa acessar um membro static através de um objeto, como a.total. Funciona, mas engana quem lê: parece que o total é da Ana. Veja:

public class Main {
    public static void main(String[] args) {
        Aluno a = new Aluno("Ana");
        Aluno b = new Aluno("Bruno");
        System.out.println(Aluno.total);
        System.out.println(a.total);
        a.total = 100;
        System.out.println(b.total);
    }
}

class Aluno {
    String nome;
    static int total = 0;

    Aluno(String nome) {
        this.nome = nome;
        total++;
    }
}
$ java Main.java
2
2                  « a.total é o mesmo Aluno.total
100                « mudou "pela Ana", apareceu "no Bruno": é o quadro, não o caderno

A regra de estilo é: membro static, acesse pelo nome da classe (Aluno.total). Assim fica claro que o valor é compartilhado. Editores como o IntelliJ e o VS Code até sublinham a.total com um aviso.


Métodos static

Métodos também podem ser static. Um método static pertence à classe e é chamado pelo nome dela, sem criar objeto. Você já usa vários desde o Nível 1:

public class Main {
    public static void main(String[] args) {
        System.out.println(Math.sqrt(81));
        System.out.println(Math.max(7, 12));
        System.out.println(Integer.parseInt("42") + 1);
    }
}
$ java Main.java
9.0          « raiz quadrada de 81 (sqrt = square root)
12           « o maior entre 7 e 12
43           « o texto "42" virou o número 42, mais 1

Repare que você nunca escreveu new Math(). Nem pode: a classe Math foi feita só para guardar métodos static (veja Math), e os criadores do Java proibiram criar objetos dela. Se você tentar:

$ javac Main.java
Main.java:3: error: Math() has private access in Math
        Math m = new Math();
                 ^
1 error

A mensagem diz que o construtor Math() é private (veja Modificadores de Acesso): só a própria classe pode usá-lo. Não faria sentido mesmo: um "objeto Math" não teria nada de seu para guardar. A raiz de 81 é 9 para todo mundo.

Criando o seu método static

A Ana quer converter temperaturas de Celsius para Fahrenheit. A conta só depende do número recebido; não há "um objeto temperatura" com dados próprios. É o caso típico de método static:

public class Main {
    public static void main(String[] args) {
        System.out.println(Temperatura.paraFahrenheit(25));
        System.out.println(Temperatura.paraFahrenheit(100));
    }
}

class Temperatura {
    static double paraFahrenheit(double celsius) {
        return celsius * 9 / 5 + 32;
    }
}
static
O método é da classe Temperatura: dá para chamá-lo sem new Temperatura().
double
O tipo do valor que ele devolve (veja Valores de Retorno).
paraFahrenheit(double celsius)
O nome e o parâmetro. Tudo o que o método precisa chega por aqui.
Temperatura.paraFahrenheit(25)
A chamada: nome da classe, ponto, nome do método. Igual a Math.sqrt(81).
$ java Main.java
77.0
212.0          « 100 °C, a água fervendo, é 212 °F

Quando um método deve ser static? Quando ele não usa nenhum atributo de objeto: recebe tudo pelos parâmetros e devolve uma resposta. Se o método precisa do nome ou do saldo de um objeto, ele é de instância (veja Métodos de Objeto).

MétodoPrecisa de dados de um objeto?Então é…
Math.sqrt(81)Não, só do númerostatic
Temperatura.paraFahrenheit(25)Não, só do númerostatic
conta.depositar(50)Sim, do saldo daquela contade instância
"Ana".length()Sim, daquele textode instância
String.valueOf(10)Não, só do númerostatic

Missão: crie, na classe Temperatura, um método static double paraCelsius(double fahrenheit) que faça a conta contrária: (fahrenheit - 32) * 5 / 9. No main, mostre Temperatura.paraCelsius(77). A saída deve ter 25.0.

public class Main {
    public static void main(String[] args) {
        System.out.println(Temperatura.paraFahrenheit(25));
        // mostre aqui Temperatura.paraCelsius(77)
    }
}

class Temperatura {
    static double paraFahrenheit(double celsius) {
        return celsius * 9 / 5 + 32;
    }

    // crie aqui o método paraCelsius
}
public class Main {
    public static void main(String[] args) {
        System.out.println(Temperatura.paraFahrenheit(25));
        System.out.println(Temperatura.paraCelsius(77));
    }
}

class Temperatura {
    static double paraFahrenheit(double celsius) {
        return celsius * 9 / 5 + 32;
    }

    static double paraCelsius(double fahrenheit) {
        return (fahrenheit - 32) * 5 / 9;
    }
}

Por que o main é static

Quando você digita java Main, a JVM (a máquina virtual que roda o seu programa, veja JDK, JRE e JVM) precisa chamar o main. Nesse momento não existe nenhum objeto: o programa nem começou. Se o main fosse de instância, quem criaria o primeiro objeto para chamá-lo?

Com static, a JVM faz o equivalente a Main.main(args): chama o método pelo nome da classe, sem new. É por isso que, desde o Java 1.0, o modelo é:

public class Main {
    public static void main(String[] args) {
        System.out.println("Olá!");
    }
}
public
Visível de fora da classe: a JVM precisa enxergá-lo.
static
Da classe: a JVM chama sem criar objeto.
void
"Vazio": não devolve nada.
main(String[] args)
O nome que a JVM procura e os argumentos da linha de comando.

O main sem static do Java 25

Java 25+ A partir do Java 25, o lançador java ficou mais esperto: se o main não for static, ele mesmo cria um objeto da classe (com new Main()) e chama o método nesse objeto. O public e o String[] args também ficaram opcionais. No JDK 25 da Ana:

public class Main {
    void main() {
        System.out.println("main sem static, Java 25");
    }
}
$ javac Main.java && java Main
main sem static, Java 25

Isso foi feito para facilitar a vida de quem está começando. Mas a JVM continua precisando de um ponto de partida que funcione sem objetos; ela só passou a criar o objeto por você. Em programas reais, em livros e em todo código anterior a 2025, você vai ver public static void main(String[] args), e é essa forma que o guia usa. Num JDK 21 ou mais antigo, o código acima não roda. Mais em Olá, Mundo e em Novidades do Java 17 ao 25.


O que um método static não pode acessar

Esta é a consequência mais importante, e a fonte de um dos erros mais comuns de quem começa. Um método static roda sem objeto. Então, dentro dele, não existe "o nome deste aluno" nem "o saldo desta conta": não há este. Veja o que acontece quando o main tenta usar coisas de instância:

Este código tem um erro de propósito
public class Main {
    String saudacao = "Olá";

    void cumprimentar() {
        System.out.println("Bom dia!");
    }

    public static void main(String[] args) {
        System.out.println(saudacao);
        cumprimentar();
        System.out.println(this.saudacao);
    }
}
$ javac Main.java
Main.java:9: error: non-static variable saudacao cannot be referenced from a static context
        System.out.println(saudacao);
                           ^
Main.java:10: error: non-static method cumprimentar() cannot be referenced from a static context
        cumprimentar();
        ^
Main.java:11: error: non-static variable this cannot be referenced from a static context
        System.out.println(this.saudacao);
                           ^
3 errors

Traduzindo: non-static variable saudacao cannot be referenced from a static context = "a variável não estática saudacao não pode ser usada num contexto estático". O contexto estático é o corpo de um método static (ou de um bloco static). Os três erros dizem a mesma coisa:

  • saudacao é um atributo de instância: de qual objeto Main? Nenhum foi criado.
  • cumprimentar() é um método de instância: precisa de um objeto para ser chamado.
  • this quer dizer "o objeto atual" (veja this). Num método static, não há objeto atual.

Como consertar: crie um objeto e use-o. Agora existe "um Main" com a sua saudacao:

public class Main {
    String saudacao = "Olá";

    void cumprimentar() {
        System.out.println(saudacao + ", bom dia!");
    }

    public static void main(String[] args) {
        Main programa = new Main();
        System.out.println(programa.saudacao);
        programa.cumprimentar();
    }
}
$ java Main.java
Olá
Olá, bom dia!

A outra saída é colocar static no atributo e no método, se eles de fato forem "da classe". Qual escolher depende do significado: a saudação é de cada objeto ou é uma só para todos?

O contrário não tem problema: um método de instância pode usar membros static à vontade (no contador, o construtor usou total++). Quem tem um objeto também enxerga a classe; quem só tem a classe não enxerga nenhum objeto.

De onde → para ondeMembro staticMembro de instância
Método staticSim, diretoSó através de um objeto (obj.nome)
Método de instância / construtorSim, diretoSim, direto (ou com this.)
Fora da classeClasse.membroobjeto.membro

Blocos static

Às vezes, preparar um atributo static exige mais que um = valor: um cálculo, um laço, uma mensagem. Para isso existe o bloco static (também chamado de inicializador estático): um trecho static { … } dentro da classe, fora de qualquer método. Ele roda uma vez só, quando a classe é carregada, isto é, na primeira vez que o programa usa a classe.

public class Main {
    static {
        System.out.println("1. bloco static de Main");
    }

    public static void main(String[] args) {
        System.out.println("2. começou o main");
        System.out.println("3. taxa = " + Config.TAXA);
        System.out.println("4. taxa de novo = " + Config.TAXA);
    }
}

class Config {
    static double TAXA;

    static {
        System.out.println("   (bloco static de Config rodando)");
        TAXA = 0.05;
    }
}
$ java Main.java
1. bloco static de Main                   « antes do main: a JVM carregou Main para chamá-lo
2. começou o main
   (bloco static de Config rodando)       « primeira vez que Config foi usada
3. taxa = 0.05
4. taxa de novo = 0.05                    « segunda vez: o bloco não roda de novo

Leia a ordem com calma. O bloco de Main rodou antes do main, porque a JVM precisa carregar a classe para chamar o método. O bloco de Config só rodou quando o programa usou Config.TAXA pela primeira vez, e não rodou de novo no passo 4.

Na prática, blocos static são raros em código do dia a dia. Você vai encontrá-los preparando tabelas, carregando configurações ou registrando drivers. Para um valor simples, prefira static double TAXA = 0.05;.


static junto com final: as constantes

Uma combinação muito comum é static final: um valor que é da classe (uma cópia só) e que nunca muda. É assim que se escreve uma constante em Java, com o nome em MAIÚSCULAS:

class Escola {
    static final int NOTA_MINIMA = 6;
}

O Math.PI (3.141592653589793) e o Integer.MAX_VALUE (2147483647) são exatamente isso. O final ganha a próxima aula inteira: final. As constantes em si foram vistas em Constantes.

Chamar sem o nome da classe: import static

Se você usa muito Math.sqrt e Math.PI, pode escrever import static java.lang.Math.*; no topo do arquivo e usar só sqrt(81) e PI. Essa forma de import aparece em Pacotes e import.


Quando dá errado

"non-static variable … cannot be referenced from a static context"
$ javac Main.java
Main.java:3: error: non-static variable nome cannot be referenced from a static context
        System.out.println(Aluno.nome);
                                ^
1 error

O que aconteceu: o código usou Aluno.nome, pelo nome da classe, mas nome é de cada aluno. Qual aluno? O mesmo erro aparece quando o main usa direto um atributo ou método da própria classe que não é static (a variação non-static method … cannot be referenced…).

Como resolver: pergunte-se "isto é de cada objeto ou da classe?". Se for de cada objeto, crie o objeto e use objeto.nome. Se for da classe, acrescente static na declaração.

"Math() has private access in Math"
$ javac Main.java
Main.java:3: error: Math() has private access in Math
        Math m = new Math();
                 ^
1 error

O que aconteceu: tentou-se criar um objeto de uma classe que só tem membros static. Classes assim (Math, Arrays, Collections) escondem o construtor de propósito.

Como resolver: não crie o objeto; chame direto: Math.sqrt(81).

O contador mostra sempre 1 (sem erro nenhum)
$ java Main.java
Produtos cadastrados: 1

O que aconteceu: o atributo do contador foi declarado sem static. Cada objeto tem o seu contador, que vai de 0 a 1 no próprio construtor. O programa compila e roda: o erro é de lógica, e o Java não tem como avisar.

Como resolver: static int cadastrados = 0;. É exatamente a missão do fim da aula.

Mudei o valor num objeto e "mudou em todos"

O que aconteceu: o atributo é static, e o código o acessou por um objeto (a.total = 100). Como só existe uma cópia, todos "veem" a mudança.

Como resolver: se cada objeto deveria ter o seu valor, tire o static. Se o valor é mesmo compartilhado, escreva Aluno.total para não enganar quem lê.


Referência

EscritaO que éComo usar
static int total;Atributo da classe (uma cópia só)Aluno.total
int idade;Atributo de instância (uma cópia por objeto)aluno.idade
static double f(double x) { … }Método da classe, sem objetoClasse.f(2)
void g() { … }Método de instânciaobjeto.g()
static { … }Bloco static: roda uma vez, ao carregar a classePreparar atributos static
static final int MAX = 10;Constante da classeClasse.MAX
public static void main(String[] args)Ponto de entrada clássicoChamado pela JVM
void main() Java 25+Ponto de entrada sem staticA JVM cria o objeto e chama

Exercícios

1. Da classe

Complete para o atributo visitantes ser um só, compartilhado por todos os objetos:

int visitantes = 0;

2. Chamar pela classe

A classe Temperatura tem o método static double paraFahrenheit(double c). Complete a chamada para converter 30 graus:

double f = ;

3. O bloco que roda uma vez

Complete a primeira linha de um bloco que roda uma vez, quando a classe é carregada:

System.out.println("carregou"); }

4. Uma classe tem static int contador = 0; e o construtor faz contador++. O programa cria 4 objetos. Quanto vale contador?

5. Dentro de public static void main, você escreve System.out.println(nome);, e nome é um atributo sem static da mesma classe. O que acontece?

6. Qual destes métodos faz mais sentido como static?

Missão final: a loja da Ana cadastrou três produtos, mas o programa mostra Produtos cadastrados: 1. Rode e confira. Depois conserte: o contador deve ser da classe, o método mostrarTotal também, e o main deve chamá-lo como Produto.mostrarTotal(). A saída esperada é Produtos cadastrados: 3.

public class Main {
    public static void main(String[] args) {
        Produto p1 = new Produto("Arroz");
        Produto p2 = new Produto("Feijão");
        Produto p3 = new Produto("Café");
        p3.mostrarTotal();
    }
}

class Produto {
    String nome;
    int cadastrados = 0;

    Produto(String nome) {
        this.nome = nome;
        cadastrados++;
    }

    void mostrarTotal() {
        System.out.println("Produtos cadastrados: " + cadastrados);
    }
}
public class Main {
    public static void main(String[] args) {
        Produto p1 = new Produto("Arroz");
        Produto p2 = new Produto("Feijão");
        Produto p3 = new Produto("Café");
        Produto.mostrarTotal();
    }
}

class Produto {
    String nome;
    static int cadastrados = 0;

    Produto(String nome) {
        this.nome = nome;
        cadastrados++;
    }

    static void mostrarTotal() {
        System.out.println("Produtos cadastrados: " + cadastrados);
    }
}

Resumo

  • static = "da classe". Um atributo static tem uma cópia só, compartilhada por todos os objetos; sem static, cada objeto tem a sua.
  • Acesse membros static pelo nome da classe: Aluno.total, Math.sqrt(81).
  • Um método deve ser static quando não precisa de dados de nenhum objeto, só dos parâmetros.
  • O main é static porque a JVM o chama antes de existir qualquer objeto. No Java 25, ele pode não ser static: o lançador cria o objeto por você.
  • Um bloco static { } roda uma vez, quando a classe é usada pela primeira vez.
  • Dentro de um método static não há this: atributos e métodos de instância só através de um objeto. Senão: non-static … cannot be referenced from a static context.
  • static final é o jeito de fazer constantes. Próxima aula: final.