Apostila de Java, Capítulo 4 - Orientação a objetos básica

Nesse artigo iremos comentar e resolver os exercícios propostos no capítulo 4, sobre Orientação a objetos básica da apostila FJ-11: Java e Orientação a Objetos, da Caelum.

Clique aqui para saber sobre a Caelum e sua apostila.
Clique aqui para baixar a apostila.
Recomendamos que tentem ao máximo resolver os exercícios, e só quando conseguir (ou depois de MUITO tentar) veja o código. Caso tenha dificuldades, veja a Solução, que conterá uma explicação a respeito do código, e tente criar seu código.

Programação é criatividade, logo existem mais de uma solução para o mesmo exercício.

Página 51, 4.12 Exercícios: Orientação a Objetos básica

Enunciados

QUESTÃO 01:
Modele um funcionário. Ele deve ter o nome do funcionário, o departamento onde trabalha, seu salário
(double), a data de entrada no banco (String) e seu RG (String).
Você deve criar alguns métodos de acordo com sua necessidade. Além deles, crie um método bonifica
que aumenta o salario do funcionário de acordo com o parâmetro passado como argumento. Crie também um método calculaGanhoAnual, que não recebe parâmetro algum, devolvendo o valor do salário multiplicado por 12..
A ideia aqui é apenas modelar, isto é, só identifique que informações são importantes e o que um funcionário faz. Desenhe no papel tudo o que um Funcionario tem e tudo que ele faz.


QUESTÃO 02:
Transforme o modelo acima em uma classe Java. Teste-a, usando uma outra classe que tenha o main.
Você deve criar a classe do funcionário chamada Funcionario, e a classe de teste você pode nomear como
quiser. A de teste deve possuir o método main.
Um esboço da classe:

class Funcionario {
       double salario;
       // seus outros atributos e métodos

       void bonifica(double aumento) {
       // o que fazer aqui dentro?
       }


       double calculaGanhoAnual() {
       // o que fazer aqui dentro?
       }
}


Você pode (e deve) compilar seu arquivo java sem que você ainda tenha terminado sua classe
Funcionario. Isso evitará que você receba dezenas de erros de compilação de uma vez só. Crie a
classe Funcionario, coloque seus atributos e, antes de colocar qualquer método, compile o arquivo java.
Funcionario.class será gerado, não podemos “executá-la” pois não há um main, mas assim verificamos
que nossa classe Funcionario já está tomando forma.
Esse é um processo incremental. Procure desenvolver assim seus exercícios, para não descobrir só no fim
do caminho que algo estava muito errado.
Um esboço da classe que possui o main:

class TestaFuncionario {

      public static void main(String[] args) {
      Funcionario f1 = new Funcionario();

      f1.nome = "Fiodor";
      f1.salario = 100;
      f1.bonifica(50);

      System.out.println("salario atual:" + f1.salario);
      System.out.println("ganho anual:" + f1.calculaGanhoAnual());

}


Incremente essa classe. Faça outros testes, imprima outros atributos e invoque os métodos que você criou a mais.
Lembre-se de seguir a convenção java, isso é importantíssimo. Isto é, nomeDeAtributo, nomeDeMetodo, nomeDeVariavel, NomeDeClasse, etc...


QUESTÃO 03:
Crie um método mostra(), que não recebe nem devolve parâmetro algum e simplesmente imprime todos
os atributos do nosso funcionário. Dessa maneira, você não precisa ficar copiando e colando um monte
de System.out.println() para cada mudança e teste que fizer com cada um de seus funcionários, você
simplesmente vai fazer:

Funcionario f1 = new Funcionario();
// brincadeiras com f1....
f1.mostra();

Veremos mais a frente o método toString, que é uma solução muito mais elegante para mostrar a representação de um objeto como String, além de não jogar tudo pro System.out (só se você desejar).
O esqueleto do método ficaria assim:


class Funcionario {
      // seus outros atributos e métodos

      void mostra() {
      System.out.println("Nome: " + this.nome);
      // imprimir aqui os outros atributos...
      // tambem pode imprimir this.calculaGanhoAnual()
      }
}



QUESTÃO 04:
Construa dois funcionários com o new e compare-os com o ==. E se eles tiverem os mesmos atributos?
Para isso você vai precisar criar outra referência:
Funcionario f1 = new Funcionario();
      f1.nome = "Fiodor";
      f1.salario = 100;
      Funcionario f2 = new Funcionario();
      f2.nome = "Fiodor";
      f2.salario = 100;

      if (f1 == f2) {
            System.out.println("iguais");
      } else {
            System.out.println("diferentes");
      }


QUESTÃO 05:
Crie duas referências para o mesmo funcionário, compare-os com o ==. Tire suas conclusões. Para criar
duas referências pro mesmo funcionário:
Funcionario f1 = new Funcionario():
f1.nome = "Fiodor";
f1.salario = 100;
Funcionario f2 = f1;
O que acontece com o if do exercício anterior?



QUESTÃO 06:
Em vez de utilizar uma String para representar a data, crie uma outra classe, chamada Data.
Ela possui 3 campos int, para dia, mês e ano. Faça com que seu funcionário passe a usá-la. (é parecido
com o último exemplo, em que a Conta passou a ter referência para um Cliente).

class Funcionario {
      Data dataDeEntrada; // qual é o valor default aqui?
      // seus outros atributos e métodos
      }

class Data {
      int dia;
      int mes;
      int ano;
}


Modifique sua classe TestaFuncionario para que você crie uma Data e atribua ela ao Funcionario:
Funcionario f1 = new Funcionario();
//...
Data data = new Data(); // ligação!
f1.dataDeEntrada = data;
Faça o desenho do estado da memória quando criarmos um Funcionario.


QUESTÃO 07:
Modifique seu método mostra para que ele imprima o valor da dataDeEntrada daquele Funcionario:

class Funcionario {
      // seus outros atributos e métodos
      Data dataDeEntrada;

      void mostra() {
            System.out.println("Nome: " + this.nome);
            // imprimir aqui os outros atributos...
            System.out.println("Dia: " + this.dataDeEntrada.dia);
            System.out.println("Mês: " + this.dataDeEntrada.mes);
            System.out.println("Ano: " + this.dataDeEntrada.ano);
      }
}



Teste-o. O que acontece se chamarmos o método mostra antes de atribuirmos uma data para este
Funcionario?


QUESTÃO 08:
O que acontece se você tentar acessar um atributo diretamente na classe? Como, por exemplo:
Funcionario.salario = 1234;
Esse código faz sentido? E este:
Funcionario.calculaGanhoAtual();

Faz sentido perguntar para o esquema do Funcionario seu valor anual?

SOLUÇÕES

QUESTÕES 1, 2, 3, 4, e 5 
As questões de número 1, 2, 3, 4 e 5 se referem a somente um aplicativo em Java, feito passo-a-passo através das questões.

Basicamente criamos a classe Funcionario com os atributos: nome, departamento, dataEntrada, RG e salario. Além disso, essa classe também possui o método mostra(), que ao ser solicitado, exibe todas as informações de um objeto.

Já a classe CaelumCap4 é a que contém a main, e que cria dois objetos do tipo Funcionario: f1 e f2.
Mostramos os dados ao iniciar, depois fazemos uma bonificação em f1 e mostramos seus dados novamente. Note que o valor do salário e o ganho anual realmente mudaram.

Depois criamos um clone de f1: f2.
Porém, embora possua os mesmos atributos e valores, esses objetos referenciam regiões na memória, portanto não são iguais, como mostra o teste condicional if.

Depois criamos outro objeto: f3
Esse objeto é referenciado para apontar o mesmo local de memória que f1 referencia.
Portanto, ao comparar, vemos que f1 e f2 são iguais.

Nossa solução fica:
Código:
--> CaelumCap4.java
public class CaelumCap4 {

    public static void main(String[] args) {
        Funcionario f1 = new Funcionario();
        Funcionario f2 = new Funcionario();
        
        f1.nome = "Programador Java";
        f1.departamento = "TI";
        f1.dataEntrada = "21/12/2012";
        f1.RG = "123456789-0";
        f1.salario = 2000;
        f1.mostra();
        
        System.out.println("\nApós fazer o curso Java Progressivo, o funcionário obteve bonificação de mil reais.");
        System.out.println("Os novos dados, após o aumento, são:\n");
        f1.bonifica(1000);
        f1.mostra();
        
        //Vamos agorar preencher os dados de f2
        f2.nome = "Programador Java";
        f2.departamento = "TI";
        f2.dataEntrada = "21/12/2012";
        f2.RG = "123456789-0";
        f2.salario = 2000;
        f2.bonifica(1000);
        
        if(f1 == f2){
            System.out.println("\nFuncionários 1 e 2 são iguais");
        } else {
            System.out.println("\nFuncionários 1 e 2 são diferentes");
        }
        
        Funcionario f3 = f1;
        if(f1 == f3){
            System.out.println("Funcionários 1 e 3 são iguais");
        } else {
            System.out.println("Funcionários 1 e 3 são diferentes");
        }
        
    }

}

--> Funcionario.java


public class Funcionario {
    String nome,
           departamento,
           dataEntrada,
           RG;
    
    double salario;
    
    void bonifica(double aumento){
        this.salario += aumento;
    }
    
    double calculaGanhoAtual(){
        return (salario * 12);
    }
    
    void mostra(){
        System.out.println("Nome do funcionário: " + this.nome);
        System.out.println("Departamento: " + this.departamento);
        System.out.println("Entrou em: " + this.dataEntrada);
        System.out.println("RG: " + this.RG);
        System.out.println("Salário: " + this.salario);
        System.out.println("Ganha anualmente: " + calculaGanhoAtual());
    }

}


QUESTÕES 6, 7 e 8
A criação da classe Data é bem simples e sem mais complicações.
Na classe Funcionario criamos um objeto da classe Data, mas ele só é instanciado na classe principal, a CaelumCap4.

Ao final da main mostramos como é possível acessar e alterar diretamente seus valores.
Nossa classes ficam assim:
Código:
--> CaelumCap4.java
public class CaelumCap4 {

    public static void main(String[] args) {
        Funcionario f1 = new Funcionario();
        Funcionario f2 = new Funcionario();
        Data data = new Data();
        
        f1.nome = "Programador Java";
        f1.departamento = "TI";
        f1.RG = "123456789-0";
        f1.salario = 2000;
        f1.dataEntrada = data;
        f1.mostra();
        
        System.out.println("\nApós fazer o curso Java Progressivo, o funcionário obteve bonificação de mil reais.");
        System.out.println("Os novos dados, após o aumento, são:\n");
        f1.bonifica(1000);
        f1.mostra();
        
        //Vamos agorar preencher os dados de f2
        f2.nome = "Programador Java";
        f2.departamento = "TI";
        f2.RG = "123456789-0";
        f2.salario = 2000;
        f2.bonifica(1000);
        
        if(f1 == f2){
            System.out.println("\nFuncionários 1 e 2 são iguais");
        } else {
            System.out.println("\nFuncionários 1 e 2 são diferentes");
        }
        
        Funcionario f3 = f1;
        if(f1 == f3){
            System.out.println("Funcionários 1 e 3 são iguais");
        } else {
            System.out.println("Funcionários 1 e 3 são diferentes");
        }
        
        System.out.println("Acessando e alterando os valores diretamente:");
        f1.salario = 1234;
        System.out.println(f1.calculaGanhoAtual());
    }

}


--> Funcionario.java
public class Funcionario {
    String nome,
           departamento,
           RG;
    Data dataEntrada;
    double salario;
    
    void bonifica(double aumento){
        this.salario += aumento;
    }
    
    double calculaGanhoAtual(){
        return (salario * 12);
    }
    
    void mostra(){
        System.out.println("Nome do funcionário: " + this.nome);
        System.out.println("Departamento: " + this.departamento);
        System.out.println("Data de entrada: " + dataEntrada.dia + 
                           "/" + dataEntrada.mes +"/"
                           + dataEntrada.ano);
        System.out.println("RG: " + this.RG);
        System.out.println("Salário: " + this.salario);
        System.out.println("Ganha anualmente: " + calculaGanhoAtual());
    }

}


--> Data.java
public class Data {
    int dia,
        mes,
        ano;
}


Página 55, Desafios 

Enunciados

QUESTÃO 01:
Um método pode chamar ele mesmo. Chamamos isso de recursão. Você pode resolver a série de fibonacci
usando um método que chama ele mesmo. O objetivo é você criar uma classe, que possa ser usada da
seguinte maneira:
Fibonacci fibo = new Fibonacci();
int i = fibo.calculaFibonacci(6);
System.out.println(i);

Aqui imprimirá 8, já que este é o sexto número da série.
Este método calculaFibonacci não pode ter nenhum laço, só pode chamar ele mesmo como método.
Pense nele como uma função, que usa a própria função para calcular o resultado.


QUESTÃO 02:
Por que o modo acima é extremamente mais lento para calcular a série do que o modo iterativo (que se
usa um laço)?


QUESTÃO 03:
Escreva o método recursivo novamente, usando apenas uma linha. Para isso, pesquise sobre o operador
condicional ternário. (ternary operator)

Soluções

QUESTÃO 01:
Vamos criar um método chamado calculaFibonacci que recebe um inteiro como argumento. Chamemos esse número de n.

Da definição dos números de FIbonacci, sabemos que se esse número n for 0, o valor do número Fibonacci é 0.
Caso n seja 1, o valor do número de Fibonacci é 1.

E para TODOS os demais, o número de Fibonacci é a soma dos dois anteriores.
Por exemplo, para n=3:
calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1)

Porém: calculaFibonacci(2) = calculaFibonacci(1) + calculaFibonacci(0) = 1 + 0 = 1
Agora colocamos esse valor na outra equação:
calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1) = 1 + 1 = 2

Agora, para calcular n=4:
calculaFibonacci(4) = calculaFibonacci(3) + calculaFibonacci(2)

Mas: calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1)
E: calculaFibonacci(2) = calculaFibonacci(1) + calculaFibonacci(0)

Então: 
calculaFibonacci(4) = calculaFibonacci(2) + calculaFibonacci(1)  + calculaFibonacci(1) + calculaFibonacci(0)

Substituindo calculaFibonacci(2) novamente:
calculaFibonacci(4) = calculaFibonacci(1) + calculaFibonacci(0) + calculaFibonacci(1)  + calculaFibonacci(1) + calculaFibonacci(0) = 1 + 1 +1 +1 +1 = 5

Ou seja, não importa o número que coloquemos, ele será sempre substituído pela soma dos dois elementos anteriores. Cada um desses elementos anteriores serão substituído pela soma de seus respectivos elementos anteriores. E assim vai indo...até sobrar somente uma soma de calculaFibonacci(1) e calculaFibonacci(0).
Interessante, não?

Podemos representar em Java essa idéia matemática da seguinte maneira:
Código:
public class Fibonacci {
    
    int calculaFibonacci(int n){
        if(n==0){
            return 0;
        }
        
        if(n==1){
            return 1;
        }
        
        return ( calculaFibonacci(n-1) + calculaFibonacci(n-2) );
    }

}


QUESTÃO 02:
Experimente colocar n=30, na questão passada. É calculado bem rápido.
Agora tente n=50 e verá uma baita demora.

Se você testar obter esses números com a técnica utilizada nos exercícios do capítulo 3 da apostila da Caelum, que usa somente laços, verá que é muito mais rápido.
Isso porque os métodos são bem mais 'pesados', em termos computacionais.

Se notar bem, o método calculaFibonacci é chamado muitas vezes.
Para n=6, o método é invocado 25 vezes.
Para n=20, o método é invocado 21891 vezes !!!
(para saber quantas vezes o método foi chamado, crie uma variável inteira na classe Fibonacci, e faça 'chamadas++' assim que se inicia o método, e ela guardará quantas vezes esse método foi acessado)


QUESTÃO 03:
O operador condicional ternário tem a seguinte sintaxe:

(teste de condição) ? (retorno isso caso seja true) : (retorna isso caso seja false) ;

Ele faz um teste condicional (n < 1, a == b, x >= 0 etc). Caso ele seja verdadeiro, retorna o que vem logo após a interrogação '?', e caso seja falso, retorna o que vem após os dois pontos ':'
Sim, é como se fosse um teste condicional IF ELSE, mas usamos apenas '?' e ':'

Voltemos ao Fibonacci.
Você noto que, se n=0, o retorno é 0. E se n=1, o retorno é 1?
Ou seja: se n < 2, o número de Fibonacci é o próprio n.

E se não for? Aí o retorno é a soma dos dois elementos anteriores.

Agora, de posse dessas informações, podemos calcular os números de Fibonacci em apenas uma linha:
Código:
public class Fibonacci {
    
    int calculaFibonacci(int n){
        return ( n < 2 ? n : ( calculaFibonacci(n-1) + calculaFibonacci(n-2) ) );
    }

}

2 comentários:

Gabriel disse...

Meu cálculo do Fibonacci ficou "pior" do que o apresentado por vocês!
Eu já havia feito este mesmo cálculo em C, porém de maneira iterativa (usando laço for). Eu não consegui pensar em algo melhor, então apenas fiz uma "gambiarra" do jeito iterativo para o recursivo.

Vou lhes mostrar o método que eu montei:

public void calculo(int n) {
a=b+c;
b=c;
c=a;

if (n>1) {
calculo(n-1);
} else {
JOptionPane.showMessageDialog(null, a);
this.a=0;
this.b=1;
this.c=0;
}
}

Como podem ver, uma bela gambiarra! Mas pelo menos funcionou... rs.

Muito boas as aulas de vocês. Dia 20 de fevereiro de 2014 inicia o próximo semestre da minha faculdade, farei Programação II que é orientação a objeto, e eu sei que o professor usará Java durante as aulas. Estou fazendo as aulas do JavaProgressivo pra já chegar lá bem afiado!

Abraço.

Bráian disse...

Hehe Gabriel concordo tbm fiz uma gambiarra:

public class Fibonacci {
private int num,
count = 2,
soma = 1,
total = 1,
aux;

public int calculaFibonacci(int num){
this.num = num;
if(this.num == 1 || this.num == 2)
return total;
this.aux = this.total;
this.total += this.soma;
this.soma = this.aux;
this.count++;
if(this.num == this.count)
return this.total;
else
return this.calculaFibonacci(this.num);
}
}

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.