Olá! Na palestra de hoje conheceremos o conceito de “ modificadores de acesso ” e veremos exemplos de como trabalhar com eles. Embora a palavra “vamos nos conhecer” não seja totalmente correta: você já conhece a maioria deles em palestras anteriores. Por precaução, vamos refrescar a memória sobre o principal. Os modificadores de acesso são geralmente palavras-chave que regulam o nível de acesso a diferentes partes do seu código. Por que "na maioria das vezes"? Porque um deles é definido por padrão e não é indicado por uma palavra-chave :) Existem um total de quatro modificadores de acesso em Java. Nós os listamos em ordem, do mais rigoroso ao mais “suave”:
- privado;
- protegido;
- default(pacote visível);
- público.
Modificador privado
Private
— o modificador de acesso mais restritivo. Limita a visibilidade de dados e métodos dentro de uma única classe. Você conhece esse modificador da palestra sobre getters e setters. Lembra deste exemplo?
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Vimos isso em um dos artigos anteriores. Aqui cometemos um erro grave: abrimos nossos dados, e como resultado os colegas programadores tiveram acesso direto aos campos da classe e alteraram seus valores. Além disso, estes valores foram atribuídos sem verificações, pelo que no nosso programa é possível criar um gato com idade de -1000 anos, nome "" e peso 0. Para resolver este problema, nós usava getters e setters e também limitava o acesso aos dados usando um modificador private
.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
// checking the input parameter
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// checking the input parameter
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
// checking the input parameter
this.weight = weight;
}
}
Na verdade, restringir o acesso aos campos e implementar getters-setters é o exemplo mais comum de uso private
no trabalho real. Ou seja, implementar o encapsulamento em um programa é o objetivo principal deste modificador. A propósito, isso não se aplica apenas aos campos. Imagine que no seu programa existe um método que implementa algumas funcionalidades MUITO complexas. Para usar isso como exemplo... Digamos que seu método readDataFromCollider()
pegue um endereço com dados como entrada, leia os dados do Large Hadron Collider em formato de byte, converta esses dados em texto, grave-os em um arquivo e imprima-os. Até a descrição do método parece assustadora, quanto mais o código :) Para aumentar a legibilidade do código, seria bom não escrever a lógica complexa do método em um só lugar, mas, pelo contrário, quebrar a funcionalidade em métodos separados. Por exemplo, o método readByteData()
é responsável por ler os dados, convertBytesToSymbols()
converter os dados lidos do colisor em texto, saveToFile()
salvar o texto resultante em um arquivo e printColliderData()
imprimir nosso arquivo de dados. O método readDataFromCollider()
acabaria sendo muito mais simples:
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
public byte[] readByteData(Path pathToData) {
// reads data in bytes
}
public String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// convert bytes to characters
}
public File saveToFile(String[] colliderData) {
// save the read data to a file
}
public void printColliderData(File fileWithColliderData) {
// print data from file
}
}
Porém, como você lembra da palestra sobre interfaces, o usuário só tem acesso à interface final. E nossos 4 métodos não fazem parte disso. Eles são auxiliares : nós os criamos para melhorar a legibilidade do código e evitar amontoar quatro tarefas diferentes em um único método. Não há necessidade de conceder ao usuário acesso a esses métodos. Se um usuário tiver acesso ao método ao trabalhar com um colisor convertBytesToSymbols()
, ele provavelmente simplesmente não entenderá o que é esse método e por que ele é necessário. Quais bytes são convertidos? De onde eles vieram? Por que convertê-los em texto? A lógica executada neste método não faz parte da interface do usuário. Somente o método readDataFromCollider()
faz parte da interface. O que fazer com esses quatro métodos “internos”? Certo! Restrinja o acesso a eles com um modificador private
. Dessa forma eles podem fazer seu trabalho facilmente dentro da classe e não confundir o usuário, que não precisa da lógica de cada um deles separadamente.
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
private byte[] readByteData(Path pathToData) {
// reads data in bytes
}
private String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// convert bytes to characters
}
private File saveToFile(String[] colliderData) {
// save the read data to a file
}
private void printColliderData(File fileWithColliderData) {
// print data from file
}
}
Modificador protegido
O próximo modificador de acesso mais restritivo éprotected
. Os campos e métodos designados com o modificador de acesso protected
ficarão visíveis:
- dentro de todas as classes que estão no mesmo pacote que o nosso;
- dentro de todas as classes sucessoras de nossa classe.
protected
há muito menos casos de aplicação do que private
e são específicos. Imagine que temos uma classe abstrata AbstractSecretAgent
que denota um agente secreto de alguma agência de inteligência, bem como um pacote top_secret
que contém essa classe e seus descendentes. Classes concretas - FBISecretAgent
, MI6SecretAgent
, MossadSecretAgent
etc. - são herdadas dele. Dentro da classe abstrata queremos implementar um contador de agentes. Quando um novo objeto agente é criado em algum lugar do programa, ele aumentará.
package top_secret;
public abstract class AbstractSecretAgent {
public static int agentCount = 0;
}
Mas nossos agentes são secretos! Isso significa que somente eles e mais ninguém devem saber sobre seu número. Podemos facilmente adicionar um modificador protected
ao campo agentCount
e, então, objetos de outras classes de agentes secretos ou aquelas classes localizadas em nosso pacote “secreto” podem obter seu valor top_secret
.
public abstract class AbstractSecretAgent {
protected static int agentCount = 0;
}
É para tarefas tão específicas que é necessário um modificador protected
:)
modificador visível do pacote
O próximo da nossa lista é o modificadordefault
ou, como também é chamado, package visible
. Não é indicado por uma palavra-chave porque é definido por padrão em Java para todos os campos e métodos. Se você escrever em seu código -
int x = 10;
... a variável x
terá esse mesmo package visible
acesso. Se um método (ou variável) não estiver marcado com nenhum modificador, ele será considerado marcado com o "modificador padrão". Variáveis ou métodos com tal modificador (ou seja, sem nenhum) são visíveis para todas as classes do pacote em que são declarados. E apenas para eles. Seus usos são limitados, assim como o modificador protected
. Na maioria das vezes, default
-access é usado em um pacote onde existem algumas classes de utilitários que não implementam a funcionalidade de todas as outras classes deste pacote. Vamos dar um exemplo. Imagine que temos um pacote de “ serviços ”. Dentro dele estão diversas classes que trabalham com o banco de dados. Por exemplo, existe uma classe UserService
que lê dados do usuário de um banco de dados, uma classe CarService
que lê dados sobre carros do mesmo banco de dados e outras classes, cada uma das quais trabalha com seu próprio tipo de objetos e lê dados sobre eles do banco de dados.
package services;
public class UserService {
}
package services;
public class CarService {
}
No entanto, pode facilmente acontecer uma situação em que os dados do banco de dados estão em um formato, mas precisamos deles em outro. Imagine que a data de nascimento do usuário no banco de dados esteja armazenada no formato TIMESTAMP WITH TIME ZONE...
2014-04-04 20:32:59.390583+02
... em vez disso, precisamos do objeto mais simples - java.util.Date
. Para isso, podemos criar services
uma classe especial dentro do pacote Mapper
. Ele será responsável por converter os dados do banco de dados nos objetos Java com os quais estamos familiarizados. Uma classe auxiliar simples. Geralmente criamos todas as classes como public class ClassName
, mas isso não é necessário. Podemos declarar nossa classe auxiliar simplesmente como class Mapper
. Nesse caso, ele ainda faz seu trabalho, mas não fica visível para ninguém fora do pacote services
!
package services;
class Mapper {
}
package services;
public class CarService {
Mapper mapper;
}
E esta, de fato, é a lógica correta: por que alguém de fora do pacote veria uma classe auxiliar que funciona apenas com classes do mesmo pacote?
modificador público
E por último da lista, mas não menos importante – o modificadorpublic
! Você o conheceu no primeiro dia de estudos no JavaRush, lançando o public static void main(String[] args)
. Agora que você estudou as palestras sobre interfaces, seu propósito é óbvio para você :) Afinal, public
ele foi criado para dar algo aos usuários. Por exemplo, a interface do seu programa. Digamos que você escreveu um programa tradutor que pode traduzir texto russo para inglês. Você criou um método translate(String textInRussian)
no qual a lógica necessária é implementada. Você marcou este método com a palavra public
e agora ele se tornará parte da interface:
public class Translator {
public String translate(String textInRussian) {
// translates text from Russian to English
}
}
Você pode associar uma chamada a este método ao botão “traduzir” na tela do programa – e pronto! Qualquer um pode usá-lo. Partes do código marcadas com o modificador public
destinam-se ao usuário final. Para dar um exemplo da vida, private
estes são todos os processos que ocorrem dentro da TV durante o seu funcionamento, e public
estes são os botões do controle remoto da TV com os quais o usuário pode controlá-la. Ao mesmo tempo, ele não precisa saber como funciona a TV e como funciona. O controle remoto é um conjunto public
de métodos: on()
, off()
, nextChannel()
, previousChannel()
, increaseVolume()
, decreaseVolume()
etc.
GO TO FULL VERSION