Métodos e Variáveis Estáticas


O modificador static é usado para criar variáveis e métodos que irão existir independentemente de qualquer instância criada para a classe.
Em outras palavras, membros static existem antes mesmo da criação da instância de uma classe, e haverá apenas uma cópia do membro independentemente do número de instâncias daquela classe, ou seja, todas as instâncias dessa classe irão compartilhar o mesmo valor para qualquer variável static.
Podem ser marcados como static: métodos, variáveis, uma classe interna dentro de outra classe (mas não dentro de um método) e blocos de inicialização; não pode ser marcados como static: construtores, classes (exceto as internas), interfaces, métodos de classes locais, variáveis de instância e variáveis locais.
Métodos static não têm acesso direto a membros (métodos ou variáveis) não-estáticos.


Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide

Declarações de Variável



Existem dois tipos de variáveis em Java:
·         Variáveis primitivas: uma variável primitiva pode ser de oito tipos: char, boolean, byte, short, int, long, double ou float. Uma vez que uma variável primitiva tenha sido declarada, seu tipo primitivo nunca poderá mudar; embora seu valor possa na maioria das vezes;
·         Variáveis de referência: é usada para referenciar ou acessar um objeto. É declarada com um tipo específico que não pode ser mudado, mas pode ser usada para fazer referência a qualquer objeto do tipo declarado, ou de um subtipo do tipo declarado (um tipo compatível).

Declarando primitivos:

Variáveis primitivas podem ser declaradas como variáveis de classe (estáticas), variáveis de instância, parâmetros de métodos ou variáveis locais.
A seqüência dos tipos numéricos do menor para o maior é: byte, short, int e long; double é maior do que float.

Declarando variáveis de referência:

Podem ser declaradas como variáveis estáticas, variáveis de instância, parâmetros de métodos e variáveis locais.

Declarando variáveis de instância:

Variáveis de instância são definidas dentro de uma classe, mas fora de qualquer método, e só podem ser inicializadas quando a classe é instanciada. São campos que pertencem a cada objeto único, ou seja, cada instância da classe tem seus próprios valores para os campos ou variáveis de instância.
Regras para variáveis de instância:
·         Podem usar qualquer um dos quatro níveis de acesso;
·         Podem ser marcadas como final e transient;
·         Não podem ser marcadas como abstract, synchronized, strictfp, native e static (porque nesse caso se tornariam variáveis de classe).
Os modificadores de acesso funcionam em variáveis de instância da mesma forma que em métodos.

Variáveis locais:

Variáveis locais são declaradas dentro de um método, o que significa que são construídas quando o método é executado, e destruídas quando o método termina. Não podem usar a maioria dos modificadores, como: todos os modificadores de acesso, transient, volatile, abstract e static; o único que pode ser utilizando é o final.
Antes de uma variável local ser usada, ela deve ser inicializada, pois variáveis locais não recebem valor default; qualquer código que tentar utilizar uma variável local não inicializada o compilador não irá aceitar.
É possível declarar uma variável local com o mesmo nome de uma variável de instância; isso é chamado de sombreamento. Para resolver problemas de nomes de variáveis locais e de instância com a mesma identificação, basta utilizar a palavra-chave this seguida de ponto (.) antes de variáveis de instância.

public class Sombreamento {

      int cont = 1;
     
      public void setCont(int cont) {
            //não sabe a qual variável está referindo
            cont = cont;
            //por isso utiliza-se a notação abaixo
            this.cont = cont;
           
      }
     
}

Declarações de array:

Arrays são objetos que guardam múltiplas variáveis do mesmo tipo, ou variáveis que são subclasses do mesmo tipo; podem guardam tanto primitivos quanto objetos. São declarados indicando o tipo de elementos que irão usar (primitivo ou objeto), seguido por um par de colchetes que pode vir antes ou depois do nome do array.
Pode-se declarar também arrays multidimensionais, que são arrays de arrays; o conjunto de pares de colchetes pode aparecer antes ou depois do nome da matriz.
Não é permitido incluir o tamanho do array na sua declaração, pois isso deve ser feito na inicialização.

public class Arrays {
     
      /* é recomendado colocar os colchetes antes
       * do nome do array */
      int[] array;
     
      /* é permitido colocar os colchetes depois
       * do nome do array, mas é menos legível */
      Thread array2 [];
     
      String [][] matriz;
     
      /* também é permitida essa disposição dos
       * colchetes depois, mas é menos legível */
      String [] matriz2 [];

}

Variáveis final:

Declarar uma variável primitiva como final torna impossível reinicia-la com outro valor após ter recebido um valor explicitamente (valor não default). Entretanto, uma variável de referência (objeto) marcada como final não poderá jamais se referir a um objeto diferente, somente os dados dentro do objeto podem ser modificados. Isso quer dizer que não existe objetos final e sim referências de objetos final.

import java.util.Date;

public class Var_final {

      final int num = 10;
      final Date data = new Date();
     
      void teste() {
            /* erro, variável final não pode receber
             * um novo valor */
            num = 45;
            /* erro, objeto final não pode receber 
             * uma nova referência */
            data = new Date();
            /* mas pode ter os valores de seus campos
             * alterados */
            data.setYear(2008);  
      }

}

Variáveis transient:

Quando uma variável de instância é marcada por transient, significa que a máquina virtual irá ignorar esta variável quando tentar serializar o objeto que a contém.
A serialização permite salvar o estado de um objeto (em outras palavras, o valor das suas variáveis de instância) em um tipo especial de transmissão I/O; é possível salvar um objeto em um arquivo, ou enviá-lo para outra máquina virtual.

Variáveis volatile:

O modificador volatile diz a máquina virtual que uma thread acessando a variável deve sempre conciliar a sua própria cópia privada da variável com a cópia mestre em memória.
Assim como o modificador transient, volatile só ser aplicado a variáveis de instância.


Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide

Declarações de Construtor


Em Java, objetos são “construídos”. Toda vez que um objeto é criado, um construtor é invocado. Toda classe possui um construtor, mesmo que não esteja declarado explicitamente na classe, isso quer dizer que, caso não esteja, o compilador irá criar um.
Construtores parecem muito com métodos. A diferença é que construtores jamais terão um tipo de retorno.
Declarações de construtores podem, entretanto, ter qualquer tipo de modificador de acesso, e podem ter argumentos (inclusive var-args), assim como métodos.
Além disso, construtores devem ter o mesmo nome da classe na qual estão sendo declarados, não podem ser marcados como static (porque estão completamente associados à instanciação, e o modificador static não está relacionado a isso) e não podem ser marcados como final ou abstract (porque não podem ser sobrescritos).

public class Construtor {

      Construtor() { }
     
      protected Construtor(int value) { }
     
      private Construtor(String... value) { }
     
      // construtores não podem ser static
      static Construtor(String var) { }
     
      // construtores não podem ser final
      final Construtor(char var) { }
     
      /* aparenta ser um construtor, mas trata-se
       * de um método com o mesmo nome da classe */
      public void Construtor() { }
     
}


Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide

Métodos com Listas de Argumento Variável (var-args)


Argumentos são variáveis especificadas entre parênteses na invocação (chamada) de um método; parâmetros são variáveis declaradas na assinatura de métodos, que indica o que o método deve receber quando for invocado.


      // exemplo de parâmetros
      private void test1(String var, int value) { }

      //exemplo de argumentos
      test1(var, value);


Java permite a criação de métodos que podem ter um número variável de argumentos ou var-args.
Regras para declaração de var-args:
-         Tipo de var-arg: quando um parâmetro var-arg é declarado, é necessário especificar o tipo de argumento que esse parâmetro irá receber; podendo este ser um tipo primitivo ou um tipo de objeto;
-         Sintaxe básica: para declarar um método usando um parâmetro var-arg, coloca-se o tipo do var-arg acompanhado de reticências, um espaço e o nome do array que irá guardar os parâmetros recebidos;

      /* esse método espera de 0 a muitos ints
       * como parâmetro*/    
      void testVar_arg(int... x) { }
     
-        Outros parâmetros: é permito ter outros parâmetros comuns na assinatura do método que usa var-args;

      /* esse método espera primeiro um char e depois
       * de 0 a muitos ints como parâmetros */
      void testVar_arg(char c, int... x) { }

-         Limites de var-args: o var-arg deve ser o último parâmetro na assinatura do método, e só é permitido um var-arg por método.

      // só é permitido um var-arg por método
      void testVar_arg(int... x, char... y) { }
     
      // o var-arg deve ser o último parâmetro
void testVar_arg(String... s, byte b) { }


Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide

Modificadores Não Referentes à Acesso para Membros de Classes


Métodos final:

A palavra-chave final impede um método de ser sobrescrito em uma subclasse, sendo frequentemente usado para impor a funcionalidade de um método.

Argumentos final:

Argumentos de métodos são declarações de variáveis que aparecem entre parênteses em uma declaração de método.
São tratados da mesma forma que variáveis locais, ou seja, podem ser usadas com o modificador final, o que significa que seu valor não poderá ser modificado dentro do método.
Nesse caso, “modificado” significa a reatribuição de um novo valor à variável, ou seja, o argumento final deve manter o mesmo valor que o parâmetro tinha quando foi passado para o método até o final.

public class Argumentos {

      public void setArg(String obj1,
                         final String obj2) {
            String obj3 = new String();
            obj1 = obj3;
            /* erro, argumentos final não podem ter
             * um novo valor reatribuído */
            obj2 = new String();
            /* erro, argumentos final não podem ser
             * modificados */
            obj2 = obj3;          
      }
     
}

Métodos abstratos:

Um método abstrato é um método que foi declarado, mas não foi implementado; ou seja, o método não contém código funcional.
Métodos abstratos não são declarados com chaves, apenas com ponto-e-vírgula depois dos parênteses, já que não possuem corpo de método. São utilizados quando é necessário forçar subclasses a implementar a funcionalidade.
Classes que possuem métodos abstratos devem ser necessariamente declaradas como abstract também, entretanto, classes abstratas podem conter métodos não-abstratos.
Qualquer classe que herdar de uma classe abstrata deve implementar todos os métodos abstratos da superclasse, exceto se a subclasse também for abstrata.
A regra é: a primeira subclasse concreta (não-abstrata) de uma árvore de herança de classes abstratas deve implementar todos os métodos abstratos das suas superclasses. No caso de um método abstrato de uma superclasse ser implementado na subclasse abstrata, não será obrigatória a implementação deste na classe concreta que herdar dessas classes, pois foi possível implementa-lo na classe abstrata.

public abstract class Animal {

      private String nome;
     
      // método não abstrato
      public String getNome() {
            return nome;
      }
     
      // método abstrato
      public abstract void falar();
     
}


public abstract class Mamifero extends Animal {
     
      /* um método abstrato pode ser implementado
       * em outra classe abstrata */
      public void falar() { }
     
}


public class Cachorro extends Mamifero {
     
      /* não é uma implementação do método abstrato de
       * Animal, e sim uma sobrecrita do método falar
       * de Mamifero */
      public void falar() { }
     
}

Observação1: métodos abstratos não podem ser marcados como final, pois estes não podem ser sobrescritos por subclasses, e métodos final têm justamente função oposta de métodos abstratos (não serem sobrescritos).
Observação2: métodos abstratos não podem ser também privados, pois estes não são vistos por outras classes.
Observação3: um método abstrato também não poderá ser marcado como static.

Métodos sincronizados:

A palavra-chave synchronized indica que um método só pode ser acessado por uma thread de cada vez. Esse modificador só pode ser aplicado a métodos, e pode ser combinado com qualquer outro modificador de controle de acesso.

Métodos native:

O modificador native indica que um método está implementado em código dependente de plataforma, normalmente C. Esse modificador também só pode ser aplicado a métodos.
Assim como em métodos abstratos, métodos marcados como native não possuem corpo, portanto, deve terminar com ponto-e-vírgula no lugar de chaves, indicando que a implementação é omitida.

Métodos strictfp:

Mesmo a classe não sendo strictfp, pode-se declarar um método como strictfp. Esse modificador força pontos flutuantes (e qualquer operação de ponto flutuante) a aderir ao padrão IEEE 754. Uma variável não pode ser declarada como strictfp, somente classes e métodos.

Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide

Modificadores de Acesso para Membros de Classes


É possível aplicar modificadores a membros de classe, métodos e variáveis de instância (variáveis globais). Esses modificadores podem ser de acesso e não-acesso, da mesma forma que ocorre com as classes. Aqui iremos tratar dos modificadores de acesso.
Métodos e variáveis de instância são membros de classe e são tratados com modificadores de acesso da mesma maneira. Enquanto as classes só podem usar dois controles de acesso (default e public), membros de classe podem usar todos os quatro: default, public, protected, e private.
Existem duas questões de acesso diferentes: se uma classe pode acessar um membro de outra classe, e se uma subclasse pode herdar o membro de uma superclasse.
O primeiro tipo de acesso ocorre quando em uma classe um método instancia outra classe e tenta acessar métodos ou variáveis desta. O segundo tipo de acesso ocorre quando uma subclasse herda de uma superclasse e passa a ter acesso aos seus membros diretamente, ou seja, sem ter que instanciar a superclasse dentro de um método.

Membros públicos:

Quando um método ou variável de instância é declarado como public, significa que todas as outras classes, independente de pacote, podem acessar esse membro.

Membros privados:

Métodos e variáveis marcadas como private só podem ser acessados pela classe na qual foram declarados; outras classes não acessam membros privados nem por herança, nem por instância.

Membros protegidos e default:

Os controles de acesso protected e default são quase idênticos. Os dois só podem ser acessados por classes do mesmo pacote por meio da instanciação, com a diferença de que membros protected também podem ser acessados por classes de outros pacotes através de herança; ou seja, membros default e protected diferem apenas quando se trata de subclasses.
Mesmo que aconteça de uma classe conseguir instanciar uma outra classe que possua membros protegidos e esteja em um pacote diferente, ela não conseguirá acessar esses membros através da instanciação, apenas por herança (com a chamada explícita do método).


package geracao1;

public class Pai {

      public String nome;
     
      protected int cod;
     
      private void ensinar() { }
     
      protected void educar() { }
           
}



package geracao2;

import geracao1.Pai;

/* encontra-se em um pacote diferente da sua
 * superclasse */
public class Filho extends Pai {

      public void aprender() {
            // membro público de Pai
            String var = nome;
           
            /* n possui acesso a esse membro default,
             * pois estão em pacotes diferentes */
            int codPai = cod;
           
            /* não possui acesso a esse membro porque
             * trata-se de um membro private de Pai */
            ensinar();
           
            /* consegue acessar esse membro protected
             * de Pai por herança,apesar de estarem 
             * em pacotes diferentes */
            educar();
           
            Pai pai = new Pai();
           
            /* não consegue acessar esse membro
 * protected de Pai por instanciação 
 * porque estão em pacotes diferentes */
            pai.educar();
      }
     
}


Observação1: modificadores de acesso não são aplicados a variáveis locais; de fato, apenas o modificador de não-acesso final pode ser aplicado a variáveis locais.



Fonte: SCJP Sun Certifi ed Programmer for Java 6 Study Guide