Apostila de Java, Capítulo 6 - Modificadores de acesso e atributos de classe

Neste artigo de nosso curso, iremos resolver e comentar todas as questões propostas na apostila de Java da Caelum, do capítulo 6.
Este capítulo fala sobre modificadores de acesso (private e public), atributos de classe (set, get, construtores, variáveis e métodos estáticos), encapsulamento e singleton

Deseja trabalhar com Java? Clique aqui e obtenha seu certificado para entrar no Mercado de Trabalho!

Apostila de Java da Caelum

Nesse tutorial de nosso curso iremos comentar e resolver os exercícios propostos no capítulo 6, sobre Modificadores de acesso e atributos de classe da apostila FJ-11: Java e Orientação a Objetos, da Caelum.que é disponibilizada gratuitamente por eles e nos autorizaram utilizar seu material.

É interessante que você faça o download da apostila (ou acesse ela online) e estude o conteúdo deste capítulo e tente resolver por si só os exercícios.
Fazendo isso, certamente irá reforçar seus conhecimentos adquiridos até aqui.

Clique aqui para saber mais sobre a empresa Caelum e suas apostilas gratuitas.

O material da apostila é uma excelente fonte de estudos sobre diversos assuntos que já abordamos aqui no Java Progressivo, dentre eles:



Enunciados do capítulo 6 da apostila

6.8 - Exercícios: Encapsulamento, construtores e static

1) Adicione o modificador de visibilidade (private, se necessário) para cada atributo e método da classe Funcionario.
Tente criar um Funcionario no main e modificar ou ler um de seus atributos privados. O que acontece?

2) Crie os getters e setters necessários da sua classe Funcionario. Por exemplo:
   class Funcionario {
        private double salario;
        // ...
        public double getSalario() {
             return this.salario;
        }
        public void setSalario(double salario) {
             this.salario = salario;
        }
   }

Não copie e cole! Aproveite para praticar sintaxe. Logo passaremos a usar o Eclipse e aí sim teremos procedimentos mais simples para este tipo de tarefa.
Repare que o método calculaGanhoAnual parece também um getter. Aliás, seria comum alguém nomeá-lo de getGanhoAnual. Getters não precisam apenas retornar atributos. Eles podem trabalhar com esses dados.

3) Modifique suas classes que acessam e modificam atributos de um Funcionario para utilizar os getters e setters recém criados.
Por exemplo, onde você encontra:
   f.salario = 100;
   System.out.println(f.salario);
   passa para:
   f.setSalario(100);
   System.out.println(f.getSalario());

4) Faça com que sua classe Funcionario possa receber, opcionalmente, o nome do Funcionario durante a criação do objeto. Utilize construtores para obter esse resultado.
Dica: utilize um construtor sem argumentos também, para o caso de a pessoa não querer passar o nome do Funcionario.
Seria algo como:
   class Funcionario {
        public Funcionario() {
             // construtor sem argumentos
        }
        public Funcionario(String nome) {
             // construtor que recebe o nome
        }
   }

Por que você precisa do construtor sem argumentos para que a passagem do nome seja opcional?

5) (opcional) Adicione um atributo na classe Funcionario de tipo int que se chama identificador. Esse identificador deve ter um valor único para cada instância do tipo Funcionario. O primeiro Funcionario instanciado tem identificador 1, o segundo 2, e assim por diante. Você deve utilizar os recursos aprendidos aqui para resolver esse problema.
Crie um getter para o identificador. Devemos ter um setter?

6) (opcional) Crie os getters e setters da sua classe Empresa e coloque seus atributos como private. Lembre-se de que não necessariamente todos os atributos devem ter getters e setters.
Por exemplo, na classe Empresa, seria interessante ter um setter e getter para a sua array de funcionários?
Não seria mais interessante ter um método como este?

   class Empresa {
        // ...
        public Funcionario getFuncionario (int posicao) {
             return this.empregados[posicao];
        }
   }

7) (opcional) Na classe Empresa, em vez de criar um array de tamanho fixo, receba como parâmetro no construtor o tamanho do array de Funcionario.
Com esse construtor, o que acontece se tentarmos dar new Empresa() sem passar argumento algum? Por quê?

8) (opcional) Como garantir que datas como 31/2/2012 não sejam aceitas pela sua classe Data?

9) (opcional) Crie a classe PessoaFisica. Queremos ter a garantia de que pessoa física alguma tenha CPF inválido, nem seja criada PessoaFisica sem cpf inicial. (você não precisa escrever o algoritmo de validação de cpf, basta passar o cpf por um método valida(String x)...)


6.9 - Enunciado dos desafios

1) Porque esse código não compila?
   class Teste {
        int x = 37;
        public static void main(String [] args) {
              System.out.println(x);
        }
   }

2) Imagine que tenha uma classe FabricaDeCarro e quero garantir que só existe um objeto desse tipo em toda a memória. Não existe uma palavra chave especial para isto em Java, então teremos de fazer nossa classe de tal maneira que ela respeite essa nossa necessidade. Como fazer isso? (pesquise: singleton design pattern)


Solução das questões do capítulo 6 da apostila

Como é de praxe na apostila de Java da Caelum, as questões são na verdade um programa em Java maior, onde se faz um pouco dessa aplicação em cada questão. Vamos resolver mostrar o código de uma aplicação que resolve as 7 primeiras questões.

Questão 01
Inicialmente, vamos criar 3 atributos: o nome e o setor do funcionário (que são Strings) e o salário, que é do tipo Double. Como são informações pessoais, vamos declarar todas como private.
Criamos a variável 'programador', que é um objeto da classe 'Funcionario', e tentamos definir o nome do programador como "Alonso", porém o atributo 'nome' foi declarado com visibilidade do tipo 'private', ou seja, esse método é privado e somente é visto pela classe/objeto e métodos pertencentes a ela.

Como o método main é de outra classe, não é possível, nem por reza brava, acessar os elementos declarados como private.
Daí obtemos erros do tipo: The field Funcionario.nome is not visible
Obviamente, vamos comentar esse trecho do código, pois não seria possível continuar nosso programa com este erro :)

Questões 02 e 03
Nessa questão da apostila, apenas implementamos os métodos setters e getters.
A única coisa de diferente que fazemos é criar o método getGanhoAnual(), que retorna o valor que o funcionário ganha em um anos (12 salários e o 13o terceiro). Para testar, usamos o método setSalario() para colocar um valor no atributo 'salario'. Ao fazer isso, automaticamente o métogo getGanhoAnual() retornará 13 vezes o valor deste salário.

Depois preenchemos os campos de nome e setor que trabalham, através dos setters.
Depois exibimos tudo isso em um único print, usando os getters.

Questão 04
Para essa questão da apostila, vamos criar outro funcionário, o 'analista'.
Vamos inicializar ele através da criação do construtor que recebe uma String com seu nome, e dentro deste construtor apenas chamamos o método setNome() e passamos a String que esse construtor recebe.

Para inicializar esse tipo de objeto, mas passar uma String como argumento no: new Funcionario(string_aqui);
Pois dentro da implementação da classe Funcionario já existe um construtor que 'espera' receber uma String como argumento, então quando você manda essa String, o Java automaticamente detecta o construtor pra qual ela deve ir.

Porém, nas questões passadas não passamos nenhuma String. E como criamos um construtor, o construtor padrão (que não recebe argumentos) não existe, por isso precisamos criar um construtor padrão, para que também possamos criar um objeto da classe Funcionario sem ter que passar argumento algum.

Questão 05
Essa questão da apostila é realmente interessante, pode atiçar bastante nossa imaginação e perturbar o juízo de alguns, em busca da solução.

Nossa classe deve ter 'ciência' de todos os objetos que foram criados.
Ela deve ter um atributo que é comum à todas as classes, que é o atributo que iremos chamar de 'numFuncionarios', um inteiro que irá armazenar o número de funcionários (que é o mesmo número de objetos criados dessa Classe).

Para ele ser visto e poder ser acessado de qualquer objeto, ele deve ser do tipo 'static', ou seja, esse inteiro é comum à todos os objetos, pois ele é, na verdade, o mesmo local da memória. Assim, quando alteramos seu valor num objeto, estamos alterando para todos os outros.

Cada vez que criamos um objeto, estamos criando um funcionário. Então devemos incrementar esse atributo.
E o que acontece quando criamos um objeto? Sim, o construtor é sempre chamado.
Então, iremos fazer com que cada vez que algum construtor seja chamado, ele irá incrementar a varável 'numFuncionarios', e somente nos construtores. Assim teremos total controle do número de funcionários.

Após ser criado um funcionário, o valor de 'numFuncionarios' muda (para numFuncionarios + 1), e esse será o valor do identificador do novo funcionário.
Colocamos a variável 'identificador' como 'final'. Ou seja, agora ela é uma constante.
Então, uma vez que um funcionário tem seu identificador, esse atributo não poderá mais mudado, por isso não deve ter um setter.
Seguro e totalmente automatizado nosso aplicativo, não?

Questões 06 e 07
Vamos criar a classe Empresa com quatro atributos: uma string com o nome da empresa, um array com os funcionários (não inicializados, pois não sabemos o tamanho), e dois atributos de controle, um que irá conter a 'capacidade' de funcionários (que o usuário irá dizer) e se o número 'total' de funcionários, que armazena o número atual de funcionários.

O único construtor recebe um número inteiro, que é será a capacidade total da empresa. E esse número é usado para declarar o array de funcionários.

Agora que temos o número exato de funcionários no array, precisamos inicializar esse objetos da classe 'Funcionario'.
E para isso, vamos usar o método 'adiciona()', que recebe um objeto dessa classe.
Porém, antes de adicionar o novo funcionário, precisamos saber se tem lugar pra ele, para isso apenas checamos o número atual de funcionários , na variável 'total', e se estiver dentro da capacidade, fazemos com que um objeto do array de funcionários receba a referência deste novo objeto.

Para ilustrar, na main(), criamos uma empresa com capacidade de 3 funcionários.
Adicionamos os objetos 'programador' e 'analista', que já trabalhamos nas questões anterior deste capítulo da apostila.

Agora vamos criar mais um funcionário, o 'estagiario'.
Como já adicionamos 2 objetos, este vai ser o terceiro, então vai ser o elemento de índice 2 do array.
Para pegar esse objeto do array, usamos o método: JP.getFuncionario(2), que vai devolver a referência do objeto de nossa empresa 'JP'.
Então, para definir o setor desse funcionário, vamos usar o método setSetor() dele. Fazemos isso assim:
JP.getFuncionario(2).setSetor("Faz tudo");
Para definir o salário, é análogo, mas usamos o método setSalario(), desse mesmo funcionário, dessa mesma empresa:
JP.getFuncionario(2).setSalario(0);

Note como fica a ordem: empresa.funcionario.metodoDesseFuncionario()
Sacou a lógica?
Depois é só exibir tudo.

Assim, o código solução desses 7 primeiras questões é:
Código Java:
Classe: ApostilaJavaCap6.java

public class ApostilaJavaCap6 {

    public static void main(String[] args){
      Funcionario programador = new Funcionario();
      //Questão 1
      //programador.nome = "Alonso";
      
      //Questão 2 e 3
      System.out.println("Questões 2 e 3 ->");
      programador.setSalario(2000);
      programador.setNome("Alonso");
      programador.setSetor("TI - Programador Java");
      
      System.out.println("Nome do funcionário: " + programador.getNome() +
    		  			 "\nSetor de trabalho: " + programador.getSetor() + 
    		  			 "\nSalário: " + programador.getSalario() + 
      					 "\nGanho anual: " + programador.getGanhoAtual() +
      					 "\nID: " + programador.getIdentificador());
      System.out.println("--------------------------------");
      
      //Questão 4
      Funcionario analista = new Funcionario("Bruce Dickinson");
      analista.setSetor("Gestão de projetos");
      analista.setSalario(3000);
      System.out.println("Nome do analista: " + analista.getNome() + 
    		  			 "\nID: " + analista.getIdentificador());
      
      //Questão 6 e 7
      System.out.println("\n\nNa nova empresa");
      Empresa JP = new Empresa(3);
      JP.setNome("Empresa Java Progressivo");
      JP.adiciona(programador);
      JP.adiciona(analista);
      JP.adiciona(new Funcionario("Estagiário"));
      JP.getFuncionario(2).setSetor("Faz tudo");
      JP.getFuncionario(2).setSalario(0);
      
      System.out.println(JP.getNome());
      for(int i=0 ; i < 3 ; i++){
    	  System.out.println("Nome do funcionário: " + JP.getFuncionario(i).getNome() +
    			  			 "\nSetor de trabalho: " + JP.getFuncionario(i).getSetor() + 
    			  			 "\nSalário: " + JP.getFuncionario(i).getSalario() + 
    			  			 "\nGanho anual: " + JP.getFuncionario(i).getGanhoAtual() +
    			  			 "\nID: " + JP.getFuncionario(i).getIdentificador() + 
					 		 "\n-----------------------");
      }
      
      
    }
    
}


Classe: Funcionario.java
public class Funcionario {
	private String nome,
				   setor;
	private double salario;
	private final int identificador;
	private static int numFuncionarios;
	
	Funcionario(){
		identificador = ++numFuncionarios;
	}
	
	Funcionario(String nome){
		setNome(nome);
		identificador = ++numFuncionarios;
	}
	
	public int getIdentificador() {
		return identificador;
	}

	public Double getGanhoAtual(){
		return 13 * this.salario;
	}
	
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	public String getSetor() {
		return setor;
	}
	public void setSetor(String setor) {
		this.setor = setor;
	}
	public double getSalario() {
		return salario;
	}
	public void setSalario(double salario) {
		this.salario = salario;
	}
}


Classe: Empresa.java

public class Empresa {
	private String nome;
	private Funcionario[] empregados;
	private int total=0,
				capacidade;
	
	Empresa(int numFuncionarios){
		empregados = new Funcionario[numFuncionarios];
		this.capacidade = numFuncionarios;
	}
	
	public Funcionario getFuncionario (int posicao) {
	    return this.empregados[posicao];
	}
	
	public void adiciona(Funcionario f){
		if(this.total < this.capacidade){
			empregados[total] = f;
			this.total++;
		}
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

}



Questão 08
Essa merecia um artigo só para resolver este tipo de aplicativo, mas não é nada de outro mundo.
Para criar uma aplicação realmente robusta e funcional, precisamos fazer uma série de testes.

Primeiros temos que checar o ano, para saber se é bissexto ou não, pois dependendo disso, o ano vai ter a data 29 de fevereiro ou não.
Depois temos que checar o mês, pois dependendo deste mês, ele vai até dia 28 ou 29 (caso seja fevereiro e dependendo se é ano bissexto), dia 30 ou 31.
Após isso, poderemos saber se o dia é válido ou não.

Tente resolver, é uma questão interessante.
A única dificuldade é criar uma lógica para saber se o ano é bissexto ou não. Em breve vamos resolver esta interessante questão da apostila.

Questão 09
Essa é parecida com a anterior, e vai exibir uma pesquisa no Google sobre 'validação de CPF', pois o Ministério da Fazenda tem regras sobre o número de CPF. Onde, a partir de um algoritmo sobre os 9 primeiros dígitos do CPF, descobrimos os dois últimos dígitos, que são os validadores.

Veja a regra aqui e tente fazer em Java: http://www.geradorcpf.com/algoritmo_do_cpf.htm
É uma interessante questão, que certamente iremos fazer em breve.

6.9 - Solução dos desafios

Questão 01
Embora a variável 'x' esteja na mesma classe, não é possível acessá-la pois 'x' não é static, já que o método main() é estático e ele só acessa membro static também.

Questão 02
Essa questão da apostila é bem difícil de ser resolvida somente pensando, e provavelmente você vai precisar pesquisar.
Mas se quiser tentar, deverá fazer 3 procedimentos básicos e nada óbvios.

O primeiro é, dentro da classe, declarar um objeto do tipo static da PRÓPRIA CLASSE.
Isso mesmo, se sua classe se chama 'Exemplo', vamos criar um objeto chamado 'objExemplo' dela mesma:
private static Exemplo objExemplo;

Depois criamos um construtor, mas do tipo private. Criamos ele para que o padrão, que é público, não exista mais.
Assim, só existe um construtor, o private. E como é privado, não pode ser acessado fora da classe.
Ora, sempre que criamos um objeto ele chama o construtor automaticamente.
Nesse caso, não vai ser possível chamar o construtor já que é private, então NÃO SERÁ POSSÍVEL CRIAR OUTRO OBJETO desta classe!

Se não é possível criar um objeto dela, como vamos criar o objeto único dela?
Ué, já foi criado, é aquele que explicamos antes, o 'objExemplo'.

Mas esse objeto precisa ter um 'contato' com o mundo exterior, então vamos criar um método getter, para podermos passar esse objeto para quem pedir.

Vamos chamar esse método de getInstancia().
E aqui vamos fazer outra coisa: note que declaramos nosso objeto, mas não inicializamos ele.
E é nesse método que vamos inicializá-lo.

Ao invocarem o método getInstancia() ele checa se o objeto já foi inicializado (se não tiver sido, ainda aponta para null). Se ainda não tiver sido, ele inicializa dando o 'new'.
Se já tiver sido inicializado, retorna o objeto.

Pronto. Na main, não precisamos declarar nem inicializar o objeto, pois ele já existe (é estático, e está dentro da classe, foi criado automaticamente no ato da compilação).
Como prova disso, apenas acessamos o método 'hello()' desse objeto, que retorna uma string.

Sagaz, não?
Então, nosso código dessa aplicação é:
Código Java:
Classe: Singleton.java
public class Singleton {

	public static void main(String[] args) {

			TestSingleton.getInstancia().hello();	

	}

}


Classe: TestSingleton.java

public class TestSingleton {
    private static TestSingleton instanciaUnica=null;

    private TestSingleton() {
         
    }

    public static TestSingleton getInstancia(){

          if (instanciaUnica == null) {
        	  instanciaUnica = new TestSingleton();
          }
          
          return instanciaUnica;
    }
    
    public void hello(){
    	System.out.println("Sou única");
    }

}

Nenhum comentário:

Dicas e Novidades de Java por e-mail

Sabe quanto custa um bom livro de java?
Entre R$ 100,00 e R$300,00

Sabe quanto custa um bom curso presencial de Java?
Entre R$ 1.500,00 até R$ 4.000,00

Sabe quanto custa estudar pelo Java Progressivo?
Absolutamente nada.

Porém, também precisamos de sua ajuda e apoio.
Para isso, basta curtir nossa Fan Page e clicar no botão G+ do Google.