quinta-feira, 18 de setembro de 2014

Java - Static


Static

static é a palavra reservada no Java utilizada para definir um membro estático dentro de uma classe. Isso torna o método ou variável pertencente a classe que o mesmo se encontra, com isso transformando o membro acessível diretamente pela chamada pela classe, ao invés de instanciar, como é normalmente feito em um contexto de objeto.

Basicamente é essa a definição que temos em mente na hora de falar sobre static em java. Mas isso implica em muitas duvidas sobre como o mesmo funciona, tanto na hora de executar um sistema quanto como ele é alocado na JVM.

Eu percebi isso principalmente quando comecei a ministrar aulas de Java no meu ambiente de trabalho, pois houveram duvidas sobre o mesmo e eu não tinha uma explicação profunda e esclarecedora como eu esperava poder entregar para os alunos que tinham a duvida. Então decidi vir falar um pouco mais profundamente sobre o static.

O método mais conhecido atualmente com o static é o próprio método main, nele temos uma estrutura que a JVM necessita pra compilar e aceitar que o código seja executado para haver o inicio do programa, o método main precisa necessariamente ter acesso public, ser static, não retornar coisa alguma ( void ) e receber como argumento um array de String (String args[] ).


Com isso em mente temos que saber que dentro de métodos static somente é possível pode acessar outros métodos e variáveis que também sejam staticdentro de membros non-static, é possível acessar tanto propriedades static quanto as non-static. Precisamos criar instancias da classes para chamarmos membros non-static porque métodos estáticos não possuem referência para o ponteiro this. O ponteiro this serve para fazermos referência a classe em que estamos trabalhando, dessa forma:

<pre class="brush: csharp">
...
// Variavel qualquer  
private String name;  
// Qualquer metodo
public void getName() {
this.nome = "Guilherme";  
}
</pre>

Se o método getName() fosse estático não funcionaria mais, pois esse tipo de método não tem o ponteiro this em seu corpo. Pode parecer que a variável ou método que receber o static não poderá mudar seu valor, mas isso não é verdade (só se definirmos o final no método ou variável). O static nos garante que haverá somente uma referência para o método ou variável disponível em memória, fazendo com que quando declaramos algo static todas as instancias da classe irá ter a mesma cópia da variável ou método. Essa forma de ser acessada diretamente sem precisar de uma instancia da classe também foi traduzida em um Pattern, o Singleton.

Vamos ver agora um exemplo mostrando o funcionamento desse conceito. Teremos a Contador.java, ela terá uma variável static e outra non-static, iremos criar objetos e então incrementar o valor de cada variável em cada objeto criado da seguinte forma:

<pre class="brush: csharp">
class Contador { // Variavel static public static int staticContador = 0; // Variavel nao-static public int nonStaticContador = 0; public Contador() {} // Precisa ser static porque "contador" é static public static void incrementaStaticContador() { staticContador++; System.out.println("staticContador agora é "+ staticContador); } public void incrementaNonStaticContador() { nonStaticContador++; System.out.println("nonStaticContador agora é "+ nonStaticContador); } } public class StaticTest { public static void main(String args[]) { Contador c1 = new Contador(); c1.incrementaStaticContador(); c1.incrementaNonStaticContador(); Contador c2 = new Contador(); c2.incrementaStaticContador(); c2.incrementaNonStaticContador(); Contador c3 = new Contador(); c3.incrementaStaticContador(); c3.incrementaNonStaticContador(); Contador c4 = new Contador(); c4.incrementaStaticContador(); c4.incrementaNonStaticContador(); } }
</pre>

O resultado dessa classe será:

<pre class="brush: csharp">
staticContador agora é 1 nonStaticContador agora é 1 staticContador agora é 2 nonStaticContador agora é 1 staticContador agora é 3 nonStaticContador agora é 1 staticContador agora é 4 nonStaticContador agora é 1
</pre>

Percebam que o valor "staticContador" não teve seu valor zerado a cada objeto criado da classe Contador, mas sim incrementado, enquanto a variável nonStaticContador ficou sempre 1, por a cada novo objeto é zerado o valor pelo construtor da classe.

Vamos ver agora um exemplo de acesso direto a um método estático:

<pre class="brush: csharp">
class StaticClassExample { // Escreve alguma frase na tela public static void escreve(String msg) { System.out.println(msg); } // Retorna a multiplicação de dois números int public static int multiplica(int n1, int n2) { return (n1 * n2); } // Construtor, apenas para mostrar que // ele nem chega ser chamado public StaticClassExample() { System.out.println("Construtor de StaticClassExample"); } } public class StaticTest2 { public static void main(String args[]) { StaticClassExample.escreve("Multiplicando 3 vezes 3:"); int resultado = StaticClassExample.multiplica(3, 3); StaticClassExample.escreve("Resultado: "+ resultado); } }

</pre>

Agora se você rodar o programa irá perceber que o construtor da classe não foi chamado. Caso não usássemos o static teríamos que instanciar o objeto pra poder fazer a chamada ao método


<pre class="brush: csharp">
public class StaticTest2
{
public static void main(String args[])
{
StaticClassExample c2 = new StaticClassExample();
c2.escreve("Multiplicando 3 vezes 3:");
int resultado = c2.multiplica(3, 3);
c2.escreve("Resultado: "+ resultado);
}

}
</pre>

O código acima funciona perfeitamente mesmo com os métodos sendo static. Isso funciona porque apesar de podermos chamar diretamente os membros das classes quando forem static, não é obrigatório, podendo perfeitamente ser criado uma instancia da classe e então chamar os métodos.

Ok, agora entendemos o funcionamento do static, vamos falar sobre a sua alocação na JVM.


Os métodos estáticos (na verdade todos os métodos), bem como variáveis ​​estáticas são armazenadas na seção PermGen da memória Heap do Java, uma vez que eles fazem parte dos dados de reflexão (dados relacionados com classe, não instância relacionada). 

Note que apenas as variáveis ​​e seus valores técnicos (primitivos ou referências) são armazenados no espaço PermGen. 

Se a sua variável estática é uma referência a um objeto e esse objeto é armazenado nas seções normais da memória Heap (old/young gen). Esses objetos não são armazenados no espaço PermGen.

<pre class="brush: csharp">
static int i = 1; // o valor 1 eh colocado na secao permgen do Java
static Object o = new SomeObject(); // a referencia(ponteiro/endereco de memoria) eh alocado na secao  permgen, o objeto em si nao.
</pre>

Com isso concluímos de uma forma mais abrangente como funciona a palavra reservada static no Java, com o tempo e experiência podemos ver e identificar onde usar e como usar de maneira que atenda a nossa necessidade