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.
- 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, comoMath.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
staticnão pode acessar, e o erronon-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ávelnomenova, dentro daquele objeto. - static
- "Este atributo é da classe." Existe uma variável
totalsó, criada quando a classeAlunoé carregada, antes de qualquer objeto. - int total = 0;
- O tipo, o nome e o valor inicial, como em qualquer atributo. O
= 0roda uma vez, e não a cada objeto. - total++;
- Dentro do construtor, soma 1 ao
totalcompartilhado. 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:
| Onde | O que existe ali | Quantas cópias |
|---|---|---|
Classe Aluno | total = 3 | 1 (da classe) |
Objeto a | nome = "Ana" | 1 por objeto |
Objeto b | nome = "Bruno" | |
Objeto c | nome = "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 semnew 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étodo | Precisa de dados de um objeto? | Então é… |
|---|---|---|
Math.sqrt(81) | Não, só do número | static |
Temperatura.paraFahrenheit(25) | Não, só do número | static |
conta.depositar(50) | Sim, do saldo daquela conta | de instância |
"Ana".length() | Sim, daquele texto | de instância |
String.valueOf(10) | Não, só do número | static |
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:
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 objetoMain? Nenhum foi criado.cumprimentar()é um método de instância: precisa de um objeto para ser chamado.thisquer dizer "o objeto atual" (veja this). Num métodostatic, 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 onde | Membro static | Membro de instância |
|---|---|---|
Método static | Sim, direto | Só através de um objeto (obj.nome) |
| Método de instância / construtor | Sim, direto | Sim, direto (ou com this.) |
| Fora da classe | Classe.membro | objeto.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.
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
$ 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.
$ 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).
$ 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.
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
| Escrita | O 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 objeto | Classe.f(2) |
void g() { … } | Método de instância | objeto.g() |
static { … } | Bloco static: roda uma vez, ao carregar a classe | Preparar atributos static |
static final int MAX = 10; | Constante da classe | Classe.MAX |
public static void main(String[] args) | Ponto de entrada clássico | Chamado pela JVM |
void main() Java 25+ | Ponto de entrada sem static | A JVM cria o objeto e chama |
Exercícios
1. Da classe
Complete para o atributo visitantes ser um só, compartilhado por todos os objetos:
2. Chamar pela classe
A classe Temperatura tem o método static double paraFahrenheit(double c). Complete a chamada para converter 30 graus:
3. O bloco que roda uma vez
Complete a primeira linha de um bloco que roda uma vez, quando a classe é carregada:
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 atributostatictem uma cópia só, compartilhada por todos os objetos; semstatic, cada objeto tem a sua.- Acesse membros
staticpelo nome da classe:Aluno.total,Math.sqrt(81). - Um método deve ser
staticquando não precisa de dados de nenhum objeto, só dos parâmetros. - O
mainéstaticporque a JVM o chama antes de existir qualquer objeto. No Java 25, ele pode não serstatic: 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
staticnã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.