Modificadores de Acesso
Modificadores de Acesso controlam a forma como os membros de uma classe são visíveis por outras classes e /ou instâncias de outras classes.
Um membro de uma classe pode ter um ou nenhum modificador de acesso na sua declaração, quando não especificamos um dos três modificadores de acesso dizemos que o membro tem acesso friendly.
private
O modificador de acesso private e o mais restritivo de todos, variáveis, métodos e construtores com esse modificador são visíveis somente dentro da definição da própria classe, com acesso directo ou através de uma instância da mesma classe.
O modificador private pode ser usado em variáveis, métodos e construtores. Em classes, seu uso é restrito a classes internas, ou seja, classes declaradas dentro de outras classes.
Considere os códigos das Figuras 01 e 02:
1. public class Pilha {
2.
3. private int topo, capacidade, dados[];
4.
5. public Pilha() {
6. topo = -1;
7. capacidade = 10;
8. dados = new int[capacidade];
9. }
10.
11. public void empilhar(int i) {
12. if ( topo+1==capacidade )
13. realocar(capacidade*2);
14. dados[++topo] = i;
15. }
16.
17. public int desempilhar() {
18. return dados[topo--];
19. }
20.
21. public void unir(Pilha p) {
22. realocar(capacidade + p.capacidade);
23. for (int i=0; i<=p.topo; i++)
24. dados[i+p.topo+1] = p.dados[i];
25. topo += p.topo + 1;
26. }
27.
28. private void realocar(int novaCapacidade) {
29. capacidade = novaCapacidade;
30. int temp[] = new int[capacidade];
31. for (int i=0; i<=topo; i++)
32. temp[i] = dados[i];
33. dados = temp;
34. }
35. }
Figura 1: Código da Classe Pilha.java
1. public class TestarPilha {
2. public static void main(String args[]) {
3. Pilha p1 = new Pilha();
4. Pilha p2 = new Pilha();
5.
6. for (int i=0; i<10; i++) {
7. p1.empilhar(i);
8. p2.empilhar(i+10);
9. }
10. p1.unir(p2);
11. p1.realocar();
//erro de compilacao (acesso privado)
12. System.out.println(p2.topo);
//erro de compilacao (acesso privado)
13. }
14. }
Figura 2: Código da Classe TestarPilha.java
Na linha 22 do código 01 vemos o acesso a uma variável privada através de uma instância da classe Pilha (p.capacidade), esse acesso é permitido já que quando declaramos que uma variável é privada estamos a dizer que essa variável é visível dentro da definição da própria classe, acedida directamente ou por intermédio de instâncias da mesma classe. Declarações semelhantes são usadas nas linhas 23, 24 e 25.
Nas linhas 11 e 12 do código 02, tentamos aceder ao método privado realocar() e o atributo topo também privado, logo é gerado um erro de compilação, pois os elementos privados de uma classe apenas podem ser acedidos dentro da própria classe. Veja, por exemplo, o acesso aos mesmos elementos realocar() e topo no código 01 nas linhas 13 e 14 respectivamente, esses acessos não causam erros de compilação pois estão dentro da definição da classe Pilha na qual esses elementos são declarados.
Imagine agora se incluirmos na classe Pilha o método main da classe TestarPilha, conforme podemos ver na Figura 03:
1. public class Pilha {
//linhas 2 a 35 idem ao código 01
36. public static void main(String args[]) {
37. Pilha p1 = new Pilha();
38. Pilha p2 = new Pilha();
39.
40. for (int i=0; i<10; i++) {
41. p1.empilhar(i);
42. p2.empilhar(i+10);
43. }
44. p1.unir(p2);
45. p1.realocar();
//Nao ha erro de compilacao, estamos dentro da classe Pilha
46. System.out.println(p2.topo);
//Nao ha erro de compilacao, estamos dentro da classe Pilha
47. }
48. }
Figura 3: Outra versão para a Classe Pilha.java
O programa vai compilar correctamente, pois o método realocar() e a variável topo estão a ser acedidas internamente na classe Pilha (lembre-se que o método main é um método da classe Pilha) diferente do exemplo anterior.
Uma relação curiosa ocorre quando herdamos de uma classe que contêm elementos privados, veja da Figura 04, que herda da classe Pilha:
1. public class SubPilha extends Pilha {
2. public Pilha(int t, int c) {
3. topo = t; //erro de compilacao
4. capacidade = c; //erro de compilacao
5. }
6. }
Figura 4: Código da Classe SubPilha.java
Apesar da classe SubPilha conter os atributos topo e capacidade herdados de Pilha, o acesso a essas variáveis nas linhas 3 e 4 não é permitido já que foram declarados privados na classe Pilha.
“friendly”
O acesso friendly em Java decorre da não especificação de nenhum dos três modificadores de acesso (private, protected, public), na verdade não há nenhuma palavra chave que defina que um determinado elemento terá acesso friendly. O acesso friendly pode ser aplicado a classes, métodos, variáveis e construtores. Outros nomes também usados para designar esse tipo de acesso são: “package” e “default”.
O efeito de aplicarmos o acesso friendly a um elemento (ou seja, não aplicar nenhum dos três modificadores) e que elementos com esse tipo de acesso podem somente ser acedidos por classes do mesmo pacote.
1. class Friendly {
2. int variavelFriendly;
3.
4. void metodoFriendly () {
5. System.out.println(variavelFriendly);
6. }
7. }
Figura 5: Código da Classe Friendly.java
No código da Figura 05 podemos ver a definição de uma classe, um método e uma variável com acesso friendly, todos esses elementos são acessíveis de qualquer classe do mesmo pacote, ou seja, qualquer classe que está no mesmo directório.
É importante lembrar que a não especificação do pacote de uma classe resulta na incorporação dessa classe ao pacote default, portanto, todas as classes onde não é especificado o seu pacote e que contêm elementos com o acesso friendly, tais elementos são visíveis por todas as outras classes que também não foram especificados pacotes, e isso em grande parte das vezes não é o que desejamos.
protected
O modificador de acesso protected define que variáveis, métodos e construtores com esse modificador podem somente ser acedidos por classes no mesmo pacote ou subclasses da classe onde o elemento foi definido, é importante notar que para os elementos serem acedidos na subclasse não é preciso estar no mesmo pacote da classe pai. O modificador protected pode ser aplicado somente em variáveis, métodos e construtores, o seu uso em classes não é permitido.
Considere os códigos da Figuras 06, 07 e 08:
1. package banco;
2.
3. public class Conta {
4.
5. public Conta() { }
6.
7. protected float saldo;
8.
9. protected void saque(float s) {
10. saldo -= s;
11. }
12. }
Figura 06: Código da Classe Conta.java
1. public class Poupanca extends banco.Conta {
2. public void reajuste(float juros) {
3. saldo *= juros;
4. }
5. }
Figura 07: Código da Classe Poupanca.java
1. package banco;
2.
3. public class Transferencia {
4. public void transferir(Conta c1, Conta c2, float valor) {
5. c1.saque(valor);
6. c2.saldo += valor;
7. }
8. }
Figura 08: Código da Classe Poupanca.java
Como a variável saldo é declarada como protected (linha 7 do código 06) o acesso a essa variável na linha 3 do código 07 não causa erro, pois a classe Poupanca é uma subclasse de Conta. Nas linhas 5 e 6 do código 08 podemos ver a outra forma de acesso de membros declarados como protected, apesar da classe Transferencia não ser subclasse de Conta, esta pode aceder a elementos declarados protected, pois pertence ao mesmo pacote de Conta.
Caso eliminássemos a linha 1 do código 08 o compilador apresentaria um erro informando que não foi possível determinar a variável saldo e o método saque.
public
O mais abrangente de todos os tipos de acesso, o modificador public declara que elementos com esse modificador são acessíveis de qualquer classe Java. Este modificador é aplicável a variáveis, métodos, construtores e classes.
Os próximos modificadores que vamos ver não podem ser usado em construtores, para esse efeito apenas podem ser usado os modificares apresentados acima.
Outros Modificadores
final
O modificador final pode ser aplicado a variáveis, métodos e classes. O comportamento descrito pelo operador final varia de elemento para elemento, mas tem um conceito em comum, o conceito de imutabilidade, isto é, elementos final não podem ser mudados.
No caso de uma classe, quando dizemos que está é final, dizemos que ela não pode ser herdada, considere a definição da classe Imutavel no código 10.
public final class Imutavel {}
Figura 10: Código da Classe Imutavel.java
Qualquer tentativa de herança a partir da classe Imutavel irá causar um erro de compilação, como exemplo tente compilar o código 11.
public class Impossivel extends Imutavel {} //erro de compilação
Figura 11: Código da Classe Impossível.java
O erro devolvido pelo compilador é o seguinte: “cannot inherit from final Imutavel”.
Há pelo menos dois bons motivos para se declarar classes final, segurança e design. Em relação à segurança você pode querer declarar sua classe final de modo que uma subclasse não possa ser substituída dentro de um sistema, onde essa nova classe poderá ter códigos indesejáveis ao seu sistema. Um exemplo de uma classe final é a classe String, esta classe é vital para a normal operação da Máquina Virtual Java, já que todos os programas Java precisam dela, pois ela é declarada como parâmetro no método main(String args[]).
O outro motivo é simplesmente uma questão de design, por exemplo, você pode desejar declarar classes que implementam seus algoritmos como classes final.
Métodos final não podem ser redefinidos, ou seja, caso você herde de uma classe que contenha métodos com esse modificador, esses métodos não podem ser redefinidos na sua subclasse. Os mesmos objectivos que motivam o uso do modificador final em classes também motivam seu uso em métodos.
1. public class Transacao {
2. public final boolean verificarSenha() {
3. ...
4. }
5. }
Figura 12: Código da Classe Transacao.java
1. public class TransacaoPublica extends Transacao {
2. public final boolean verificarSenha() { //erro de compilacao
3. ...
4. }
5. }
Figura 13: Código da Classe TransacaoPublica.java
Na linha 2 do código 13 tentamos redefinir o método verificarSenha(), o compilador proíbe a redefinição e apresenta o seguinte erro: “verificarSenha() in TransacaoPublica cannot override verificarSenha() in Transacao; overridden method is final”.
O último elemento a que o modificador final pode ser aplicado é a atributos. Atributos final não podem mudar de valor, ou seja, são constantes, a partir do momento que se define o seu valor inicial, esse será o seu valor durante todo o ciclo de vida do objecto.
Quando trabalhamos com objectos devemos notar que apenas o valor do objecto, ou seja, sua referência, não pode ser mudada, atributos não final de uma instância podem ser mudados conforme podemos ver no código 14.
1. public class Carro {
2. public final int numeroDeRodas = 4;
3. public int numeroDePortas = 4;
4.
5. public static void main(String args[]) {
6. Carro c1, c2;
7. final Carro c3;
8.
9. c1 = new Carro();
10. c1.numeroDeRodas = 5; //erro de compilacao
11.
12. c2 = c3 = c1;
13. c2.numeroDePortas = 2;
14. c3.numeroDePortas = 2;
15.
16. c3 = c2; //erro de compilacao
17. }
18. }
Figura 14: Código da Classe Carro.java
Na linha 10 tentamos atribuir o valor 5 a variável numeroDeRodas, essa atribuição causa um erro de compilação já que estamos a tentar mudar o valor de uma variável final. Na linha 12, atribuímos a c3 a referência para c1, essa atribuição é correta já que é a primeira vez que atribuímos um valor a essa variável, já na linha 16 tentamos novamente mudar o seu valor, o que resulta em um erro de compilação. Na linha 14 mudamos o valor de um atributo de um objecto declarado como final, isso é correto já que não estamos a mudar o valor do objecto (referência), mas sim o valor de um atributo que não é final.
abstract
O modificador abstract pode ser aplicado somente a classes e métodos. Classes abstratas provem um modo de adiar a implementação de métodos para subclasses. Uma classe abstrata não pode ser instanciada, ou seja, não podemos chamar os seus construtores.
Veja como exemplo o código 16. A classe Figura pode ser herdada para criar classes tais como, Retangulo, Triangulo, Quadrado, Circulo, entre outros, nesse caso a classe Figura teria atributos comuns entre esses objetos, mas não poderia-mos aplicar o operador new à classe Figura, ou seja, não poderia-mos criar uma instância dessa classe.
1. public abstract class Figura {
2. public abstract void desenhar();
3.
4. public static void main(String args[]) {
5. Figura f;
6. f = new Figura(); //erro de compilacao
7. }
8. }
Figura 16: Código da Classe Carro.java
Na linha 5 declaramos um objecto do tipo Figura, o qual não causa erro já que a restrição em relação a classes abstractas é que estas não podem ser instanciadas com vemos na linha 6, mas podem sim, ser declaradas.
Métodos abstractos são métodos em que num determinado nível de programação ainda não sabemos como implementá-los, mas sabemos que eles serão necessários, veja, por exemplo, o método desenhar() na classe Figura. No momento de design da classe não é possível ainda conhecer como desenhar instâncias desses objectos, mas sabemos no momento que criamos uma subclasse, como por exemplo, para definir uma classe Circulo, nesta classe você já tem todas as informações necessárias para implementação desse método.
Classes devem ser declaradas abstractas quando qualquer uma das três afirmações abaixo forem verdadeiras:
a) a classe contêm métodos abstractos;
b) a classe herda de classes abstractas e não define todos os seus métodos abstractos;
c) a classe implementa um interface, mas não declara todos os seus métodos.
Estas são condições em que a classe obrigatoriamente deve ser declarada como abstracta, mas uma classe pode não cair em nenhum das três afirmações acima e ainda ser declarada como abstracta.
De uma forma geral uma classe deve ser declarada abstracta quando sua definição está incompleta.
static
Este modificador pode ser aplicado a variáveis, métodos e a uma porção de código que não está dentro de nenhum método, geralmente referenciado como bloco estático.
A ideia geral sobre o modificador static é que elementos com esse modificador estão associados com a classe e não com instâncias dessa classe.
Uma variável estática é compartilhada por todas as instâncias de uma classe, ou seja, ao invés de cada instância da classe ter uma cópia dessa variável ela é uma única variável compartilhada por todos as instâncias. Para a existência de uma variável estática não é preciso nem mesmo a criação de uma instância da classe que contenha a variável, é necessário somente que a classe seja carregada na Máquina Virtual Java, já que está é criada e inicializada no momento de carga da classe.
Em relação a métodos as mesmas explicações são verdadeiras, ou seja, métodos podem ser chamados mesmo sem nenhuma instância da classe ter sido construída. Um bom exemplo de métodos estáticos existe em todas as aplicações Java, o método main() é invocado sem a existência de nenhuma instância.
Métodos e variáveis podem ser referenciados além da forma padrão (através do nome do objeto) através do nome da classe. Embora as duas formas sejam corretas e trabalham exactamente da mesma forma a primeira pode tornar-se confusa quando referenciando métodos e variáveis estáticas, levando o programador a pensar que este método ou variável é não estático. Por esse motivo e conveniente sempre referenciar variáveis estáticas através do nome da classe para evitar confusões.
É importante dizer que métodos estáticos podem apenas aceder a variáveis também estáticas, ou seja, é proibido o uso de variáveis não estáticas dentro de blocos estáticos, com excepção de variáveis criadas dentro do próprio bloco é claro. Essa restrição deve-se ao fato de que é possível fazer uma chamada a um método estático mesmo sem haver nenhuma instância dessa classe, ou seja, a provável variável a ser referenciada dentro do método pode nem mesmo ter sido criada ainda. Outro motivo dessa restrição é caso haja várias instâncias criadas, qual é a instância que contêm a variável na qual estamos a referenciar?
Outra importante característica é que não podemos usar a palavra chave this com métodos e variáveis estáticas, já que esta é uma referência para o objecto que contém o método o qual não é verdadeiro para métodos e variáveis estáticas já que estes não pertencem a um objecto em particular, mas sim à classe.
Um outro local onde podemos usar o modificador static são nos desconhecidos blocos de código estáticos de Java. Veja o código da Figura 19.
1. public class BlocoEstatico {
2. static int i = 4;
3.
4. static {
5. System.out.println("Carregando BlocoEstatico\r\n valor de i: " + i);
6. }
7.
8. public static void main(String args[]) {
9. System.out.println("metodo main");
10. }
11. }
Figura 19: Código da Classe Carro.java
native
Algumas vezes é necessário executar código não Java em aplicações. Quando desejamos aceder a chamadas do Sistema Operativo, criar interfaces com dispositivos de hardware, reutilizar código não Java, implementar código onde o tempo é um parâmetro critico, etc. Esse modificador é usado somente em métodos e sua definição está fora da Maquina Virtual Java, em bibliotecas C e C++.
A declaração de um método nativo é bastante semelhante a declaração de métodos em interfaces, não existe a definição do método. A chamada a esses métodos é exactamente da mesma forma que métodos não nativos.
transient
É aplicado somente a variáveis. Quando uma variável é declarada transient ela não é guardada como parte do estado de persistência do objecto.
Muitas vezes é necessário escrever dados para fora da Máquina Virtual Java, quando isso ocorre nenhum dos mecanismos de segurança da linguagem é válido e se existe informações que não devem ser escritas as variáveis que contem esses dados devem ser declaradas como modificador transient para que estes não sejam escritos para fora da Máquina Virtual Java.
synchronized
Esse modificador é usado para sincronizar o acesso a código em ambientes paralelos.
volatile
São aplicados somente a variáveis. Este modificador indica que variáveis podem ser modificadas assincronamente.
Fonte : www.jeebrasil.com.br
Adaptado por mim.