Exercícios — O Paradigma Orientado a Objetos e a Máquina Java
Aula 1 — Programação Orientada a Objetos
Marcos M. Raimundo — Instituto de Computação, UNICAMP
Questões discursivas
Em um sistema de e-commerce, a classe
CalculadoraDeFretefoi modificada para suportar envios internacionais, e essa alteração causou falhas no móduloRelatoriosFinanceiros, que acessava diretamente variáveis internas da calculadora para projetar custos. Usando o acrônimo TRUE, identifique quais propriedades foram violadas, e explique como um design Transparent teria evitado o problema.Analise o trecho de código procedural abaixo, que gerencia o saldo de um usuário:
Explique por que esse código viola o princípio Tell, Don’t Ask, e como mover essa lógica para dentro do objeto
Usuariopreserva as invariantes de negócio.Em Java, “absolutamente tudo é passado por valor”. Se declararmos
Produto p = new Produto("TV", 50.0)e passarmosppara um método que executap.setPreco(90.0), a alteração é vista por quem chamou o método. Mas se esse mesmo método executarp = new Produto("Geladeira", 3000.0), a variável de quem chamou não muda. Explique essa aparente contradição usando o conceito de “cópia do endereço de memória”, indicando onde cada variável vive (Stack ou Heap).
Questões de Verdadeiro/Falso
Cada bloco de 4 itens trata do mesmo tema. A questão só é considerada correta se todos os 4 itens forem julgados corretamente (deixar em branco tem penalidade de 20% da nota da questão).
- □ Um código pode ser 100% funcional e passar em todos os testes automatizados e, ainda assim, falhar completamente no critério TRUE proposto por Sandi Metz.
- □ Uma correção rápida (quick fix) que funciona perfeitamente, mas ensina ao próximo programador que ler o código a ignorar os padrões de design do sistema, ainda satisfaz a propriedade Exemplary.
- □ Um código Reasonable impõe que qualquer mudança tenha sempre o mesmo custo fixo, independente do benefício.
- □ Rigidez, fragilidade e imobilidade descrevem, na prática, o mesmo problema estrutural visto de ângulos diferentes — um sistema que elimina a rigidez necessariamente também deixa de sofrer de fragilidade e imobilidade.
- □ Reorganizar um programa procedural em vários arquivos e módulos, sem alterar o fato de que funções externas continuam manipulando diretamente as structs de dados, já elimina o risco de efeito colateral apontado no paradigma procedural.
- □ Uma classe cujos atributos são todos declarados
public, mas cujos métodos que os utilizam ficam definidos dentro da mesma classe, já realiza plenamente a proposta orientada a objetos de unir dados e comportamento. - □ No exemplo do desconto, a visão orientada a objetos calcula o desconto por fora e depois grava o resultado no objeto
Produto. - □ Mesmo que um sistema orientado a objetos declare todos os atributos de suas classes como
public, ele deixa de correr o risco de dados globais manipulados livremente por funções externas, simplesmente por estar estruturado em classes.
- □ Se o Estado de um objeto
Produtomudar (por exemplo, apósaplicarDesconto), sua Identidade também muda, pois o objeto passa a representar uma versão diferente de si mesmo. - □ Dois objetos com o mesmo estado são necessariamente o mesmo objeto na memória.
- □ Um objeto sem nenhum método público, apenas atributos, ainda pode ter Comportamento, desde que seu Estado seja suficientemente rico e detalhado.
- □ Um objeto
ContaBancariacom saldo negativo por falha de sistema e sem nome de titular perde sua Identidade, deixando de ser uma instância única na memória.
- □ Uma classe cujos atributos são todos
private, mas cujos métodos públicos apenas devolvem e recebem esses atributos sem qualquer validação (getters/setters triviais), está tão protegida contra fragilidade quanto uma classe com validação de invariantes, como a que valida o preço emProduto. - □ Em uma equipe pequena e de confiança, em que nenhum código malicioso jamais seria escrito, o Ocultamento de Informação deixa de trazer benefício, pois sua função é impedir invasões externas.
- □ Um objeto que expõe os métodos públicos
getSaldo()esetSaldo(double), permitindo que código externo leia o saldo, decida se deve subtrair o valor de uma compra, e então grave o novo saldo de volta, está aplicando corretamente o princípio Tell, Don’t Ask. - □ Uma classe cujos atributos são todos
public, mas que ainda define métodos comoaplicarDesconto()ao lado desses atributos, preserva a autonomia do objeto, pois dados e comportamento continuam fisicamente dentro da mesma classe.
- □ Se duas equipes de desenvolvimento diferentes escreverem
new Produto(...)no mesmo sistema, ambas estão, de fato, criando cópias físicas independentes da classeProduto, e não apenas instâncias que compartilham o mesmo bytecode. - □ Se um sistema instanciar 10.000 objetos
Clienteao carregar os dados de um banco, a memória consumida para armazenar o bytecode dos métodos deClientecresce proporcionalmente a esses 10.000 objetos. - □ Ao reiniciar a JVM e executar o mesmo programa novamente, o bytecode das classes já utilizadas na execução anterior continua disponível no Metaspace, sem precisar ser recarregado a partir do arquivo
.class. - □ Se a classe
Clientetiver um campostaticque conta quantas instâncias já foram criadas, esse contador vive na Heap, dentro de uma das instâncias deCliente, e não no Metaspace junto com o restante dos membros estáticos da classe.
- □ O
javaccompila o código-fonte Java diretamente para instruções nativas da CPU hospedeira. - □ Se a JVM instalada em uma determinada máquina tiver um bug de implementação e interpretar um opcode do Bytecode de forma diferente do especificado, o mesmo arquivo
.classainda produzirá exatamente o mesmo resultado em qualquer ambiente, pois a portabilidade do Bytecode independe da JVM usada. - □ Um arquivo
.classcompilado em uma máquina Windows pode ser executado em um servidor Linux sem qualquer JVM instalada, desde que o processador de ambas as máquinas seja da mesma arquitetura (por exemplo, x86-64). - □ Mesmo sem nenhuma JVM instalada no ambiente hospedeiro, o Bytecode de um programa Java consegue ser executado diretamente pelo sistema operacional, já que foi compilado uma vez pelo
javac.
- □ O JIT compila o programa inteiro para código nativo assim que a aplicação é iniciada.
- □ Um método chamado apenas uma única vez durante toda a execução do programa é considerado um hotspot pelo JIT e recebe prioridade de compilação para código nativo.
- □ Em uma aplicação de vida muito curta, como uma função serverless que roda por poucos milissegundos e termina, a compilação JIT tende a trazer mais vantagem de desempenho do que a compilação estática (AOT).
- □ Como o JIT compila os hotspots para código nativo, um programa Java em execução deixa de ter qualquer trecho de código sendo interpretado pela JVM a partir desse ponto, comportando-se como um binário totalmente compilado.
- □ No método
processarPedido()do exemplo da aula, seenviarEmailConfirmacao(p1)criasse internamente um novo objetoEmailcomnew, esse objetoEmailseria armazenado na mesma área de memória (Stack) que a variável localp1. - □ Se um objeto for criado com
newdentro de um método, mas a variável que o referencia for declaradastaticem vez de local, o objeto passa a ser alocado no Metaspace junto com a classe, em vez da Heap. - □ No método
processarPedido(), se a variávelp1fosse declaradastaticem vez de local, o objetoPedidoque ela referencia se tornaria elegível para coleta assim que o método terminasse, exatamente como aconteceria com uma variável local comum. - □ Se um objeto A for referenciado apenas por um objeto B, e o objeto B, por sua vez, também não for alcançável a partir de nenhuma GC Root, o objeto A ainda é considerado alcançável, pois existe pelo menos uma referência apontando para ele.
- □ O Java é imune a qualquer forma de acúmulo indevido de objetos na memória, graças ao Garbage Collector.
- □ Se o método
registrar()doMonitorDeVendasremovesse cadaPedidodocacheInfinitoimediatamente após inseri-lo, essa estrutura estática deixaria de causar retenção obsoleta, mesmo continuando a existir como GC Root. - □ Um objeto
Scannerusado para ler um arquivo, se simplesmente saísse de escopo ao fim de um método sem ser explicitamente fechado, teria seu identificador de arquivo do sistema operacional liberado no mesmo instante em que o objeto se tornasse elegível para coleta pelo GC. - □ Se o bloco
tryde umtry-with-resourcesterminar sem lançar nenhuma exceção, o métodoclose()do recurso não é chamado, poisclose()serve apenas para lidar com falhas.
- □ Se o método
depositar(double valor)da classeContaBancariafosse declaradoprivate, o código externo que hoje chamaconta.depositar(100)continuaria compilando normalmente, só deixaria de funcionar em tempo de execução. - □ Uma classe
ContaBancariacom o atributosaldodeclaradopublic, mas com um método privadovalidarSaldo()chamado internamente antes de qualquer operação, ainda impede que código externo executeconta.saldo = -5000diretamente. - □ Um projeto Java com várias classes no mesmo pacote (package), mas sem nenhuma relação de herança entre elas, ainda impede completamente que um atributo
privatede uma classe seja acessado diretamente por outra classe desse mesmo pacote. - □ Se um atributo
privatefor acessado por outra classe através de reflection (pacotejava.lang.reflect), a JVM impede essa leitura da mesma forma rígida com que o compilador impede o acesso direto em código-fonte comum.
this
- □ Se um construtor
Produto(String nome)não tiver nenhum parâmetro com o mesmo nome de um atributo da classe, ainda é possível ocorrer sombreamento entre uma variável local declarada dentro do próprio corpo do construtor e algum atributo da classe. - □ Se um construtor tiver um parâmetro chamado
nome, igual ao atributonomeda classe, e o corpo do construtor fornome = nome;, o valor do atributonomeda instância passa a ser igual ao valor do parâmetro depois que essa linha é executada. - □ Se um método de instância usar apenas o nome simples de um atributo (sem o prefixo
this.) e não houver nenhum parâmetro ou variável local com o mesmo nome naquele escopo, o Java ainda assim falha em compilar, pois toda referência a um atributo exige obrigatoriamente o prefixothis.. - □ Se dois objetos
p1ep2da classeProdutochamarem o mesmo métodoaplicarDesconto(10)ao mesmo tempo, em duas threads diferentes, ambos os objetos serão alterados simultaneamente, poisthisé compartilhado entre as duas execuções do método.
- □ Se um método Java recebesse um array com 10 milhões de elementos como parâmetro, a JVM copiaria fisicamente todos os 10 milhões de valores para a Stack do método antes de executá-lo.
- □ Se um método receber um parâmetro
inte multiplicá-lo por 2 internamente, a variável original que foi passada para o método também dobra de valor depois que a chamada retorna. - □ Ao passar dois objetos
Produtodiferentes (a TV e a Geladeira) para dois parâmetros do mesmo método, cada parâmetro guarda uma cópia de um endereço de memória diferente, mesmo que os dois objetos tivessem, por coincidência, exatamente os mesmos valores denomeepreco. - □ Se, dentro do método
sabotarProduto(Produto prod), a reatribuiçãoprod = new Produto("Geladeira", 3000.0)ocorresse ANTES da chamadaprod.setPreco(999.0), o objeto TV original do chamador ainda assim acabaria com o preço alterado para 999.0.