O uso fundamental do modificador static em Java inclui:
1. Variáveis declaradas com static pertencem à classe, podendo ser acessadas diretamente através do nome_da_classe.nome_da_variável, sem a necessidade de instanciar a classe.
2. Métodos declarados com static pertencem à classe, podendo ser chamados diretamente através do nome_da_classe.nome_do_método, sem a necessidade de instanciar a classe.
3. Variáveis e métodos static são considerados recursos estáticos da classe, sendo compartilhados entre todas as instâncias da classe.
@ Por que o JDK distribui recursos estáticos em diferentes classes em vez de colocá-los todos em uma única classe?
Existem várias razões para isso:
1. Cada classe pode ter seus próprios recursos estáticos, permitindo uma organização lógica. Por exemplo, recursos matemáticos estão em java.lang.Math, enquanto recursos relacionados a calendários estão em java.util.Calendar, tornando a estrutura mais clara.
2. Evitar conflitos de nomes. É comum que diferentes classes tenham variáveis ou métodos com nomes idênticos. Se todos os recursos estáticos estivessem em uma única classe, inevitavelmente ocorreriam colisões de nomes. A solução é organizar esses recursos em classes distintas.
3. Impedir o crescimento infinito das classes de recursos estáticos, o que tornaria o código difícil de manter.
Métodos estáticos não podem referenciar recursos não estáticos
Por exemplo, no código abaixo:
public class Exemplo
{
private int valor = 10;
public static void metodoPrincipal(String[] args)
{
// Erro: métodos estáticos não podem acessar atributos não estáticos
valor = 20;
}
}
Métodos não estáticos podem referenciar recursos estáticos
Exemplo:
public class Exemplo
{
// Atributo estático
private static int valorEstatico = 15;
// Método não estático
public void metodoNaoEstatico(String[] args)
{
valorEstatico = 25; // Permitido
}
}
Recursos estáticos pertencem à classe, mas existem independentemente das instâncias. Do ponto de vista do mecanismo de carregamento de classes da JVM, os recursos estáticos são carregados durante a inicialização da classe, enquanto os recursos não estáticos são carregados quando uma instância da classe é criada.
Blocos Estáticos
Os blocos estáticos são outra aplicação importante do static. Eles são usados para executar operações durante a inicialização de uma classe, assim como variáveis e métodos estáticos. O código dentro de um bloco estático é executado apenas uma vez, durante a inicialização da classe.
Três pontos importantes a considerar:
1. Qual é a ordem de execução dos métodos estáticos?
public class ClasseA
{
private static int variavelA = metodoB();
static
{
System.out.println("Entrando no bloco estático de A");
}
public static void main(String[] args)
{
new ClasseA();
}
public static int metodoB()
{
System.out.println("Executando ClasseA.metodoB()");
return 1;
}
}
O resultado da execução é:
Executando ClasseA.metodoB()
Entrando no bloco estático de A
Conclusão: Os recursos estáticos são carregados estritamente na ordem em que são definidos.
2. Atribuição e criação de variáveis estáticas
public class ClasseA
{
static
{
c = 30;<br></br> // Erro de compilação
System.out.println(c);
}
private static int c;
}
Erro: "Cannot reference a field before it is defined" (Não é possível referenciar um campo antes de sua definição).
Conclusão: Um bloco de código estático pode atribuir valores a variáveis estáticas definidas posteriormente, mas não pode acessá-las.
3. Ordem de carregamento de blocos estáticos em herança
public class ClassePai
{
static
{
System.out.println("Bloco estático da ClassePai");
}
public ClassePai()
{
System.out.println("Construtor da ClassePai");
}
}
public class ClasseFilha extends ClassePai
{
static
{
System.out.println("Bloco estático da ClasseFilha");
}
public ClasseFilha()
{
System.out.println("Construtor da ClasseFilha");
}
public static void main(String[] args)
{
new ClasseFilha();
new ClasseFilha();
}
}
O resultado da execução:
Bloco estático da ClassePai
Bloco estático da ClasseFilha
Construtor da ClassePai
Construtor da ClasseFilha
Construtor da ClassePai
Construtor da ClasseFilha
Conclusão: Os blocos estáticos são carregados estritamente na ordem bloco estático da classe pai → bloco estático da classe filha, e são executados apenas uma vez.
Observação: Geralmente, static não pode modificar uma classe. No entanto, static pode moidficar uma classe interna, tornando-a uma classe interna estática (anônima).
import static
Esta é uma funcionalidade menos comum, raramente vista em código. Pode ser útil ao usar JUnit para simplificar asserções. O import static é uma nova feature introduzida no JDK 1.5. Esses dois keywords juntos permitem importar recursos estáticos específicos de uma classe sem a necessidade de usar nome_da_classe.nome_do_recurso. É importante notar que a sintaxe deve ser import static, e não static import. Por exemplo:
import static java.lang.Math.*;
public class Exemplo
{
public static void main(String[] args)
{
System.out.println(sin(2.2));
}
}
Isso significa que todos os recursos estáticos de Math foram importados. Dentro do método main, podemos usar sin(2.2) diretamente, sem precisar de Math.sin(2.2). É crucial incluir ".*" no final, pois sem esses caracetres, a importação não funcionaria corretamente. Claro, também podemos importar apenas um recurso específico, como o método sin de Math, em vez de todos os recursos estáticos:
import static java.lang.Math.sin;
public class Exemplo
{
public static void main(String[] args)
{
System.out.println(sin(2.2));
}
}
Sobre o import static, minha recomendação é:
1. Ele simplifica algumas operações, como importar todos os recursos estáticos de Math, reduzindo a necessidade de escrever "Math." repetidamente em código que usa frequentemente esses recursos.
2. Ele pode reduzir a legibilidade do código, pois fica menos claro de qual classe um método ou variável estática pertence.
Recomenda-se importar recursos estáticos específicos em cenários apropriados, evitando o uso do ".*" para importação.
Conhecimentos Importantes:
1. O modificador static em Java não afeta o escopo de variáveis ou métodos.
2. Embora métodos estáticos não tenham acesso a this, em métodos não estáticos podemos usar this para acessar membros estáticos.
Exemplo:
public class Teste {
// Variável estática
static int valor = 11;
// Método estático
public static void main(String[] args) {
new Teste().imprimirValor();
}
// Método não estático
private void imprimirValor() {
int valorLocal = 22;
// Usando this para acessar a variável estática
System.out.println(this.valor);
}
}
O resultado é: 11
Aqui, this representa o objeto atual. Ao chamar imprimirValor através de new Teste(), o objeto atual é o criado por essa expressão. Como variáveis estáticas são compartilhadas por todos os objetos, this.valor dentro de imprimirValor obviamente terá o valor 11.