Bom galera, dia 28 de março de 2010 foi a prova do concurso da
Petrobrás realizada pela Cesgranrio. Realizei a prova de Analista de
Sistema Júnior - Engenharia de Software e não passei (hehehehe, fica pra
próxima). Resolvi comentar as questões de Java desta prova e
publicá-las aqui. Em geral as questões foram médias (leia-se "Em geral"
como "comparando com as questões de Java de outras provas"). Vamos lá
então. Cada tópico abaixo será dedicado a uma das questões. Cada questão
será replicada aqui, neste documento. A versão original da prova foi
publicada no site da Cesgranrio: http://www.cesgranrio.org.br/eventos/concursos/petrobras0109/petrobras0109.html
Resolvendo a Questão 17
17
public class SomaMisteriosa {
private static void somaTres(int x[]) {
x[0] += 3;
}
private static void somaDois(int x) {
x += 2;
}
public static void main(String args[]) {
int x = 0;
int y[] = { 0 };
somaDois(x);
somaTres(y);
somaDois(y[0]);
System.out.print(x + " " + y[0]);
}
}
Após a execução do trecho acima, será impresso
(A) 2 5
(B) 1 5
(C) 0 5
(D) 0 3
(E) 0 0
Essa é uma questão fácil de conhecimento sobre a linguagem Java. Temos um três métodos estáticos, sendo um deles o main.
O que deve-se saber é que em Java, toda passagem de parâmetro é
realizada por valor. Porém, quando se passa um objeto por parâmetro, que
vai é o identificador do objeto que esta na memória, logo, qualquer
alteração feita sobre esse ponteiro afetará o mesmo objeto do método de
origem da chamada.
No programa acima, o método somaDois() por exemplo, executa operações sobre o valor do inteiro x, porém essas alterações não afetam o int x de método main.
O grande detalhe é que um array no Java é um objeto que contém casos de
um mesmo tipo. Dessa forma, o valor que passa pelo parâmetro de um
método é o ponteiro para o vetor na memória, ou seja, qualquer alteração
dentro do método somaTres() afetará o objeto no método main.
Para maiores detalhes, veja http://java.sun.com/docs/books/tutorial/java/nutsandbolts/arrays.html
Dessa forma, as alterações realizadas em int y[] = { 0 } dentro do método somaTres() foram percebidas no método main, o que resultou na saída "0 3". A resposta certa é a letra D.
Questão fácil, pra acertar de primeira! Poisé, mas eu errei. Esqueci
que um array no Java é tratado como um objeto, logo, achei que o método somaTres() não teria impacto em main.
Resolvendo a Questão 18
18 Considere o seguinte trecho de código em Java:
// Arquivo C1.java
package br.com.pk1;
public class C1 {
int x;
public int y;
protected int z;
private int w;
}
// Arquivo C2.java
package br.com.pk2;
public class C2 extends C1 {
}
A Classe C2 pode manipular os atributos
(A) x, y, z
(B) y, z
(C) x, y
(D) y
(E) x, y, z, w
Esta questão foi simples. Um caso típico onde o programador precisa conhecer os 4 níveis de encapsulamento em Java. O atributo int x, como mostrado no código, não possui sintaxe de controle de acesso. Quando um atributo (ou método) não possui modificador ele é configurado como nível de acesso package.
O que é um nível de acesso package? Um atributo package poderá ser manipulado pela classe ou pelas classes do mesmo pacote (ou seja, o atributo int x não poderá ser acessado pela class C2 pois está, mesmo sendo uma subclasse da class C1 está disposta em outro pacote.
O modificador public indicado no atributo int y, como todos sabem, indica que este atributo será acessado por todas as classes.
O modificador protected às vezes causa confusão. Nesta questão especificamente, não temos dúvida que a class C2 poderá manipular o atributo int z, pois subclasses acessam os atributos protegidos de suas superclasses. A confusão gira em torno do package da superclasse class C1. O modificador protected,
além de permitir que a própria classe acesse o atributo, que a
subclasse acesse o atributo, permite também que as classes do pacote da class C1 tenham acesso ao mesmo. Então, resumindo, que pode manipular o atributo protected int z? A própria class C1 (obviamente), as classes do pacote br.com.pk1 (pacote da class C1) e as subclasses da class C1 (neste caso, a class C2).
O modificador private permite que somente a classe que possui o atributo tenha acesso a ele diretamente. Ou seja, o atributo private int w só poderá ser acessado pela própria class C1.
Portanto, os atributos que podem ser manipulados pela class C2 são: y e z. A resposta certa é a letra B.
Essa eu acertei na prova - ufaaaa!!!
Para maiores detalhes sobre os níveis de acesso trabalhados na linguagem Java, veja http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html
Resolvendo a Questão 19
19
01 class C1 {
02 public void f() {
03 System.out.print(" 1 ");
04 }
05
06 public void g() {
07 f();
08 }
09 }
10
11 class C2 extends C1 {
12 public void f() {
13 System.out.print(" 2 ");
14 }
15 }
16
17 public class Prog {
18 public static void main(String args[]) {
19 C1 a = new C1();
20 a.f();
21 C2 b = new C2();
22 b.f();
23 a = b;
24 a.f();
25 b.g();
26 }
27 }
Considerando a execução do trecho de código em Java
acima, o programa
(A) sequer compila, pois a atribuição “a = b” (linha 23) está
incorreta por incompatibilidade de tipos.
(B) compila, mas é gerado um erro de execução por incompatibilidade
da atribuição “a = b” (linha 23).
(C) imprime 1 2 1 1.
(D) imprime 1 2 1 2.
(E) imprime 1 2 2 2.
Essa é uma questão do tipo pegadinha.
Para resolver essa questão, deve ficar claro para o leitor como
funciona as interpretações de polimorfismo na JVM. Antes de explicar a
questão, vou descrever alguns itens que devem ser considerados (leia-se "considerados" como "não podemos nunca se esquecer disso", hehe). Para facilitar, identifiquei os tópicos em perguntas.
Uma subclasse pode ser instanciada numa superclasse?
Sim. É o caso que acontece na linha 23, onde a (classe do tipo C1) recebe uma instância de b (que
é uma classe C2). Em palavras mais simples, a subclasse SEMPRE pode ser
guardada na superclasse. O contrário não é verdadeiro (exceto quando há
casting - porém, se isso não for realizado com cuidado, pode gerar um
ClassCastException).
Quando um método for sobrescrito na subclasse, qual método será chamado?
Sempre o da subclasse - exceto quando se faz uma chamada explícita utilizando a sintaxe super. O que isso significa? Significa que, quando a class C2 herda a class C1 e define o método f() com a mesma assinatura (mesmo número e tipo de parâmetros - neste caso, nenhum), a chamada de f() realizada no método g() da superclasse class C1 executará o comportamento de f() da class C2.
É isso mesmo? Sim!! Legal né... bem vindo ao polimorfismo!!
Com esses conceitos somos capazes de responder a questão. Para isso, vamos interpretar os comandos do método main() linha por linha.
Na linha 19, foi criada uma instância da class C1.
Na linha 20, a chamada do método f() vai imprimir o valor "1".
Na linha 21 foi criado o objeto b (da class C2).
Na linha 22, a chamada do método f() deixa claro (mesmo que você não lembre dos conceitos de polimorfismo) que será chamado o comportamento implementado na class C2, portanto o valor "2" será exibido.
Na próxima linha, conforme vimos estamos
guardando uma instância da subclasse C2 em C1. Isso significa que a
instância na memória será de uma class C2, porém, só poderemos usar os recursos disponíveis na definição de C1 - que neste caso, são os mesmos que C2.
Agora, na linha 24, quando chamamos o método f(), já temos uma instância de class C2, portanto o valor exibido será "2".
A grande pegadinha da questão centra-se na linha 25. Conforme vimos acima, a chamada ao método g() fará uma chamada ao método f() da subclasse, pois é assim que funciona o polimorfismo. Portanto, novamente será exibido o número "2".
Portanto, a resposta correta é a letra E, associada ao resultado impresso pelo programa 1 2 2 2
Eu acertei essa também. Se você errou, tenho
certeza que na próxima não vai errar. Essa é uma regra de difícil
compreensão... mas depois que cai a ficha, não esquece mais.
Resolverndo a Questão 20
20
01 class C1 {
02 public void mostraDados() {
03 System.out.print(" 1 ");
04 }
05 }
06
07 class C2 extends C1 {
08 public void mostraDados() {
09 System.out.print(" 2 ");
10 }
11 }
12
13 public class Prog {
14 public static void f(C1 c) {
15 System.out.print(" A ");
16 c.mostraDados();
17 }
18
19 public static void f(C2 c) {
20 System.out.print(" B ");
21 c.mostraDados();
22 }
23
24 public static void main(String args[]) {
25 C1 c1 = new C2();
26 f(c1);
27 }
28 }
Após a execução do trecho acima, na saída padrão o programa
(A) não compila.
(B) imprime A 1.
(C) imprime A 2.
(D) imprime B 1.
(E) imprime B 2.
Assim como a questão 19, a questão 20 trabalha com polimorfismo e herança. Confesso que fiquei com dúvidas nessa questão. Sabe-se que o polimorfismo permite que métodos de mesmo nome possam ser criados desde que tenham uma assinatura diferente. Esse caso específico onde se tem o método f() com uma superclasse class C1 e o método f() com uma subclasse class C2 também é permitido. A grande pegadinha é em como a JVM vai reconhecer a chamada de um ou outro. Essa foi justamente minha dúvida na prova.
Bom, se tivermos um objeto de class C1 e chamarmos o método f(), o método com a assinatura de C1 será indicado (o da linha 14). Da mesma forma, se tivermos um objeto de class C2 e o método f() for chamado, o método da linha 19 será chamado. E se tivermos uma instância de C2 guardada em C1 na chamada do método f(), qual método será executado?
Poisé, na hora da prova eu achei que o que importava era o objeto que
estava instanciado, porém não é. O tipo da classe onde existe a
instância que será o indicativo predominante entre a chamada entre um f() e
o outro - mesmo que exista a instância da superclasse nele. Pensando
melhor essa questão depois, entendi que isso é lógico, pois nunca
poderíamos cair no método da linha 19 tendo nossa instância guardada em C1, pois não seria possível, por exemplo, realizar ações da subclasse class C2, através de um tipo C1 - se houvessem métodos diferentes (o que não é o caso nessa questão).
Não sei se consegui ser claro. Mas caso não tenha entendido, lembre-se
sempre que o item que será avaliado pela JVM para escolher entre dois
métodos como os f() (das linhas 14 e 19) será o tipo da classe, independente da instância que existe nesse tipo.
Bom, teste caso, na linha 26, quando é chamado o método f(), o procedimento de 14 será executado. Nesse momento a letra " A " foi exibida.
Agora, a chamada de mostrarDados() na linha 21 fará com que o método da class C2 seja executado (pois essa é a instância que existe na memória). Portanto " 2 " será exibido.
Então, o valor final do programa é " A 2 ". Isso indica que a resposta certa é a letra C.
Errei essa desgraçada na prova!!!!! Na hora achei que a instância que seria utilizada pela JVM para indicar qual método seria chamado! Conforme vimos, o que é usado é a classe.
Errei essa desgraçada na prova!!!!! Na hora achei que a instância que seria utilizada pela JVM para indicar qual método seria chamado! Conforme vimos, o que é usado é a classe.
Resolvendo a Questão 26
26
Um programador deve criar um novo projeto que envolva
vários tipos de produtos com as mesmas funções, mas com
peculiaridades diferentes. Por exemplo, o produto do tipo
gold realiza as mesmas funções que o produto standard,
mas, a cada uma delas, armazena a última configuração
para fornecer uma memória para o usuário. O padrão
apropriado para representar as classes deste projeto é o
(A) Facade, pois ele pode juntar vários comportamentos
em um só, criando uma visão simplificada do sistema.
(B) Strategy, pois ele permite criar uma única interface com
várias implementações que diferem apenas em seu
comportamento.
(C) Proxy, pois este permite criar uma representação
menos custosa de cada um dos objetos do sistema.
(D) Iterator, pois este permite visitar todos os objetos do
sistema sem se preocupar com a classe real de cada
um deles.
(E) Singleton, pois este garante a existência de uma única
instância de produto e evita a confusão entre as classes.
Essa é uma questão de design pattern
aplicada a qualquer linguagem orientada a objetos. Mas de qualquer forma
faz parte do mundo Java, portanto, vamos resolvê-la.
Pela descrição do problema, me pareceu que o programador precisa de comportamentos similares entre os tipos gold e standard, sendo que o gold teria que ter coisas a mais do que o standard, independente de que "coisas a mais" sejam essas. Corrijam-me se eu estiver errado (de verdade, podem mandar um e-mail para lgapontes@gmail.com
se vocês discordarem) mas eu acho que neste caso, o padrão Decorator
seria interessante. Porém esse padrão não consta na lista de respostas.
Então precisamos analisar uma por uma, para ver qual se adéqua mais ao
caso.
A resposta A indica que seja utilizado padrão
Facade. Sua definição é exatamente a indica na resposta, ele serve para
facilitar o acesso a sistemas com interfaces complexas, tornando-as
simples através de uma fachada. Isso não serve para resolver o problema.
Resposta errada.
Vou pular propositalmente a letra B.
A letra C indica o uso do Proxy. Entendimento
rápido: Um Proxy é um objeto que encapsula outro objeto, sendo que ambos
possuem a mesma interface. Isso serve para que o Proxy controle o
acesso ao objeto real, oferecendo isso aos clientes (ao outros objetos
do sistema) por uma mesma interface. Podem ser oferecidos recursos como
lasy-loading (que só faz a carga do objeto por demanda) e acesso a
recursos remotos. Esse é tipo de padrão que não resolveria esse problema
destacado. Caso queira saber mais sobre ele, leia http://www.devmedia.com.br/articles/viewcomp.asp?comp=4066 (muito bom). Resposta errada.
A letra D indica um padrão Iterator, que é usado
para encapsular o comportamento de busca dos elementos existentes numa
listagem, por exemplo. Não se aplica ao caso. Essa também está errada.
Por fim, a letra E indica o padrão Singleton.
Como a própria explicação da resposta deixa claro, esse padrão só
permite a inicialização de um objeto da classe. Não é o que precisamos.
Descartando as respostas acima, temos enfim a
letra B, o padrão Strategy. O padrão Strategy é utilizado quando temos
ações comuns a serem realizadas, porém com características diferentes.
Vou usar (e adaptar) o exemplo destacado por Joseph Bergin no site da Universidade Pace.
Imagine um problema onde se tenha que ler um
arquivo de texto e exibir apenas as palavras iniciadas com "t". Imagine
ainda que apenas as palavras com tamanho "10" seriam exibidas. Observem
que o comportamento de leitura do arquivo é o mesmo, porém o resultado
final diferente - devido às peculiaridades de cada caso. Para resolver isso, criamos uma interface com o método check(), que retorna true,
caso o valor obtido do arquivo atenda a determinada característica.
Criamos então dois objetos, ambos implementando a interface citada. O
primeiro objeto codifica check() verificando se a palavra inicia com "t". O segundo verificando se a palavra possui length de 10. Por fim, implementamos a classe que fará a leitura do arquivo e mostrará apenas as palavras aceitas pelo método check() da
interface. Quando essa interface assumir o primeiro objeto, somente as
palavras iniciadas com "t" serão exibidas. De forma semelhante, caso a
interface assuma uma instância do segundo objeto, somente as palavras de
length 10 serão exibidas. Esse é um típico caso do padrão Strategy.
Recomendo a leitura dos sites abaixo (o primeiro
é o material disponibilizado por Joseph, citado acima) para melhor
compreensão desse padrão.
http://csis.pace.edu/~bergin/patterns/strategydecorator.html
http://sourcemaking.com/design_patterns/strategy
http://sourcemaking.com/design_patterns/strategy
Esse padrão poderia sim ser utilizado para resolver o problema acima, portanto, a resposta certa para essa questão é a letra B.
Essa foi minha linha de raciocínio na prova, portanto, eu acertei.
Mas, como eu disse, quando eu vi o problema, imaginei o padrão
Decorator, como não tinha, escolhi o Strategy.
Resolvendo a Questão 59
Essa questão não deixa explicitamente que se trata da tecnologia Java.
Porém como foi comentada a API SAX para leitura de estruturas XML, e
esta API é muito associada ao Java, resolvi comentar a questão.
59
A tecnologia XML possui diversos padrões e especificações para linguagens de marcação, transformação e apresentação
de uso comum no ambiente Internet. Nesse contexto, analise as afirmativas abaixo.
I - A especificação DOM (Document Object Model) possibilita o processamento de dados em formato XML através de
uma API orientada a eventos.
II - O padrão XSLT permite a transformação de arquivos em formato XML para outros formatos.
III - A especificação SAX (Simple API for XML) possibilita o processamento de dados em formato XML, contanto que os
mesmos estejam armazenados em memória principal.
É correto APENAS o que se afirma em
(A) II. (B) III. (C) I e II. (D) I e III. (E) II e III.
Basicamente, podemos identificar que a
primeira afirmativa está errada, pois DOM não é orientada a eventos. DOM
é um padrão para leitura de documentos, independente de linguagem, e é
orientada a objetos. Sobre o padrão XSLT, eu não conhecia, mas pelo que
li em http://www.tableless.com.br/pdf-gratis-sobre-xslt (que inclusive contém a mesma definição descrita no item II), esse item está correto.
O item III indica que a API SAX manipula os
arquivos XML, contanto que estes estejam na RAM. Isso é um caso muito
específico do SAX. A vantagem dessa API sobre a DOM é que ela é capaz de
ler os arquivos XML de forma serial, não precisando assim carregar todo
o documento na memória (como o DOM sugere). Então, os arquivos não
precisam estar na memória para o SAX trabalhar - alias ele foi criado
para não trabalhar assim (veja em http://pt.wikipedia.org/wiki/Simple_API_for_XML). Desta forma, este item também estava errado.
Portanto, a resposta certa é a letra A. Infelizmente
eu errei essa. Marquei a letra C, pois achei que o DOM era orientado a
eventos - na verdade, nem me lembro de ter reparado a palavra eventos
quando respondi. Poderia ter deduzido facilmente que o DOM era
orientado a objetos apenas lendo seu nome: Document Object Model. De
qualquer forma, considerei o item III errado por chute. Apesar de já ter
trabalho com SAX no Java, não sabia que seu diferencial era a leitura
em serial.
Resolvendo a Questão 60
60
O Controlador Frontal (Front Controller) é um dos padrões do catálogo J2EE. Esse padrão propicia ao desenvolvedor que
o utiliza na construção de uma aplicação Web, em camadas,
(A) organizar a camada de integração.
(B) implementar o tratamento de todas as requisições que chegam ao lado servidor da aplicação, provenientes do cliente.
(C) implementar o componente View da tríade MVC (Model-View-Controller).
(D) implementar o controle de acesso dentro de cada caso de uso da aplicação Web.
(E) expor à camada de negócio as estruturas de dados da camada de apresentação.
Essa é uma boa questão de Java EE. Eu não conhecia esse padrão, e pra falar a verdade, nem me lembro da minha resposta, mas...
Vamos a uma simples definição: o padrão Front
Controller serve para atuar como receptor de todas as requisições
realizadas pelo usuário para assim, facilitar o controle e integridade
do fluxo que normalmente as aplicações precisam. Isso auxilia muito
aplicações EE quando publicadas na Web. Ações como um clique no botão
"Voltar" do navegador fariam com que o fluxo previamente definido pelo
sistema saísse do comportamento natural. Esse tipo de coisa precisa ser
controlada. Quem centraliza esse controle é justamente o Front
Controller. para maiores detalhes, leia http://www.devmedia.com.br/articles/viewcomp.asp?comp=1812
Com base nessa definição, resta-nos escolher a opção B (resposta correta).
Como disse, não sei qual foi minha resposta. Mas
não importa, foi um chute mesmo. Preciso estudar os padrões J2EE - só
conheço pouco dos padrões GoF.
Resolvendo a Questão 61
Mais uma vez, essa questão pode estar associada a outras linguagens, e
consequentemente, também dizem respeito ao Java. Portanto vamos
abordá-la.
61
O problema do descasamento de impedância objeto-relacional
(object relational impedance mismatch) diz respeito
a um conjunto de dificuldades no contexto do
mapeamento entre alguns objetos da aplicação e as relações
de um Sistema de Gerência de Bancos de Dados
Relacionais (SGBDR). Sobre esse problema, considere as
afirmativas a seguir.
I - O padrão Objeto de Acesso a Dados (Data Access
Object – DAO) permite a uma aplicação flexibilizar e
isolar o acesso a diferentes fontes de dados, incluindo
um SGBDR.
II - O padrão Registro Ativo (Active Record) é usado
quando são encontradas, em uma mesma classe, a
lógica do negócio e a lógica de acesso a dados
persistentes.
III - Uma Unidade de Trabalho (Unit of Work) é um padrão
que permite registrar todas as alterações feitas
em uma transação e que precisam ser refletidas no
banco de dados.
É correto o que se afirma em
(A) I, apenas.
(B) I e II, apenas.
(C) I e III, apenas.
(D) II e III, apenas.
(E) I, II e III.
Um pouco mais de Patterns. Essa tquestão está relacionada ao que
propõe o JPA (Java Persistence API). Vamos considerar os itens da
questão.
O item I apresenta o padrão DAO. Ele é exatamente utilizado para isso -
permitir o acesso aos dados, de forma flexível e melhorando a
manutenção. Item correto.
O segundo item trata do Active Record. Não conhecia esse padrão... vamos vê-lo juntos:
Concebido por Martin Fowler em "Patterns of Enterprise Application
Architecture" (porra bixo, esse livro é o "livro", caraca... tenho que
lê-lo - já estou planejando fazer isso desde o ano passado... é foda!!!)
o Active Record define que teremos um classe Java associada diretamente
a uma tabela do banco de dados. Porém, neste contexto, os objetos
conteriam também as regras de acesso/escrita aos dados (no mesmo
objeto). Só com essa definição já é possível dizer que o item II está
correto. Para maiores detalhes sobre esse padrão, veja:
Agora sobre o item III, pelas palavras do guru Martin Fowler (porra,
esse ai realmente é o Van Halen do Java) esse padrão "mantém uma lista
de objetos afetados por uma transação comercial e coordena a gravação de
alterações e a resolução de problemas de concorrência." Veja em seu
próprio site http://martinfowler.com/eaaCatalog/unitOfWork.html
Analisando melhor esse item, o trecho "... permite registrar todas as
..." ficou estranho (na minha opinião). Acho que o correto seria "...
permite validar todas as ...", porém o edital indicou que a resposta é a
letra E - o que consequentemente faz esse item correto também.
Portanto, os itens I, II e III são corretos. A resposta certa é a letra E.
Eu não me lembro qual resposta eu marquei, mas confesso que não
conhecia os padrões Active Record e Unit of Work. De qualquer forma, foi
um chute.
Resolvendo a Questão 62
Mais uma questão EE.
62
Em uma aplicação distribuída que possua uma camada de
objetos de negócio localizada no servidor da aplicação, há
a necessidade de comunicação entre processos que estão
sendo executados nas máquinas cliente e servidora.
Para implementar essa comunicação, é possível o uso do
padrão Fachada Remota (Remote Facade), do catálogo P
of EAA. Nesse contexto, considere as afirmações a seguir.
O uso do padrão Fachada Remota para intermediar
chamadas a processos em execução por um servidor
de aplicações, a partir de um cliente fisicamente remoto,
aumenta o desempenho da aplicação.
PORQUE
Uma característica do objeto Fachada Remota é que
ele implementa parte da lógica de negócio no lado cliente,
o que diminui a quantidade de chamadas necessárias
ao servidor.
Analisando as afirmações, conclui-se que
(A) as duas afirmações são verdadeiras e a segunda justifica
a primeira.
(B) as duas afirmações são verdadeiras e a segunda não
justifica a primeira.
(C) a primeira afirmação é verdadeira e a segunda é falsa.
(D) a primeira afirmação é falsa e a segunda é verdadeira.
(E) as duas afirmações são falsas.
Essa questão é interessante.
Particularmente eu não conhecia a aplicação do Facade no contexto
remoto, mas como conheço o padrão Facade puro, posso afirmar que a
segundo afirmativa é falsa. O padrão Facade oferece uma fachada para
facilitar o acesso às interfaces complexas dos sistemas. Com base nisso,
pensei que um Remote Facade faria o mesmo, porém com recurso de acesso
remoto embutido. Em nenhum dos casos, a definição das regras de negócio
na Facade seria utilizada. Isso traria uma confusão de "quem faz o quê" e
dificultaria a manutenção. Lembre-se, o modelo SEMPRE precisa funcionar
independentemente de quem faz acesso a ele. Colocar lógica de negócio
fora do modelo quebraria essa regra. Logo, a resposta certa é a letra C.
De qualquer forma, pesquisei esse padrão para
tentar explicá-lo aqui. Um Remote Facade, além de oferecer acesso às
interfaces remotas do sistema, faz todo o controle de transações e
acesso (se necessários) e envia (ou busca), em uma única solicitação,
vários dados no mesmo instante. Por exemplo, imagine a classe Cliente
com os métodos setNome(), setIdade() e setCPF(). Ao invés do sistema fazer uma chamada remota para cada método, o padrão Remote Facade cria um DTO (Data Transfer Object) que
armazena esses três parâmetros num mesmo objeto e envia o DTO para o
servidor remoto. Dessa forma, ao invés de ocorrer o processamento de
três chamadas remotas (para cada um dos métodos), ocorrerá uma, com
todos os dados sendo enviados em conjunto. Isso indica que o final da
primeira afirmação ("...aumenta o desempenho da aplicação.")
é verdadeiro. Hoje em dia, com a adoção dos Beans oferecidos pelo EJB
3, o uso de DTOs se tornou desnecessário - pois um Bean do modelo é um
Pojo Serializable que pode ser enviado facilmente pela rede - ou seja,
faz o mesmo papel que o DTO fazia. Para maiores detalhes, veja:
Não me lembro exatamente qual foi a alternativa que marquei, mas se não estou enganado, minha linha de raciocínio foi a mesma - portanto, acho que marquei essa.
Resolvendo a Questão 64
Esta é uma questão de orientação a objetos. Achei interessante tratá-la aqui.

Essa questão trata de conceitos que auxiliam a construção da modelagem
orientada a objetos. Certamente algum dia eu aprendi isso, mas na hora
da prova não lembrei (exceto a herança). Vamos relembrar esses conceitos
aqui. Delegação, segundo http://msgorgonho.wordpress.com/tag/projeto-orientado-a-objetos/,
é utilizada quando um objeto delega determinada necessidade para outro
objeto. Neste caso, o item III se relaciona com o X. Coesão (veja também
no link acima) trata-se da regra que "uma classe deve fazer apenas uma
coisa bem feita". Isso significa que a coesão é forma pela qual podemos
medir o nível de associação entre as classes de um módulo. Com base
nisso, podemos entender que o item I se relaciona com o Y. Por fim, a
herança, tão conhecida na prática da programação Java (ou de qualquer
outra linguagem OO) faz com que uma classe (dita subclasse) herde as
características e comportamentos da superclasse. Neste caso, não há item
que define plenamente esse comportamento, mas uma herança não deixa de
ser uma associação entre classes. Portanto, neste caso, o item II fica
associado ao Z. Detalhe: particularmente prefiro usar a palavra
"relacionamento" para o mundo relaciona (de bando de dados e tabelas).
Na orientação a objetos, as classes se associam. A palavra politicamente
correta neste caso seria "É uma associação entre classes". Mas isso não
descarta a questão.
Portanto, temos que I - Y, II - Z e o III - X. Consequentemente a resposta correta é a letra D.
Não me recordo qual foi minha resposta na prova, porém, como eu não
sabia o significado de Coesão e Delegação, achei que a Herança estaria
associada ao item X. Errei mais uma!
Resolvendo a Questão 65
65
Analise as afirmativas a seguir relativas ao paradigma da
orientação a objetos.
I - O princípio do encapsulamento preconiza que um objeto deve esconder a sua complexidade interna.
II - Uma mensagem de um objeto A para um objeto B indica que A realizou uma tarefa requisitada por B.
III - A existência da mesma operação polimórfica definida em duas classes, ClasseA e ClasseB, implica necessariamente
que ou ClasseA seja subclasse de ClasseB, ou que ClasseB seja subclasse de ClasseA.
É correto APENAS o que se afirma em
(A) I.
(B) II.
(C) I e II.
(D) I e III.
(E) II e III.
Essa questão também trata de assuntos de OO. O item I indica um dos pontos associados ao encapsulamento. Está certo.
O item II é um pouco chato. As vezes eu confundo a questão da mensagem
entre objetos, mas para ficar claro, vejamos o gráfico abaixo:

Observem o diagrama de classes da esquerda. Temos duas classes (Classe1 e Classe2), sendo que a Classe2 possui o método calcular(). Agora observe o diagrama de sequência da direita, onde o obj1 (instância da Classe1) envia uma mensagem para o obj2 (instância da Classe2). Isso nos indica que, quando o obj1 envia uma mensagem para o obj2, significa que o obj1 está executando o método calcular() contido no obj2.
Então, a afirmativa II indica que "A realizou uma tarefa requisitada
por B". Na verdade, neste caso, B que realizou uma tarefa requisitada
por A. Ou seja, o item II está errado.
O item III afirma que para que um mesmo método seja polifórmico entre
duas classes, uma precisa ser subclasse da outra, ou vice-versa. Na
prova eu considerei esse item como correto, porém o gabarito indica que a
resposta certa é a letra A. Ou seja, esse item está errado. Mesmo
agora, ainda fico com um pouco de dúvida em relação a essa resposta. Mas
realmente, se imaginarmos um caso em que exista, por exemplo, uma ClasseC que seja a superclasse da ClasseA e da ClasseB,
e que todas elas implementem o mesmo método, teríamos um método
polimórfico nas classes A e B sem que estas estejam na condição exposta
no item III. Essa foi a única alternativa que eu encontrei que poderia indicar que o item está errado.
Bom, enfim, temos então apenas o item I correto. A resposta certa é a letra A.
Na prova eu marquei a letra D. Errei novamente. Na hora não tive
criatividade suficiente para imaginar uma situação que se torna falso o
item III.
Resolvendo a Questão 66
66
O mecanismo de composição de objetos é essencial em
um sistema de software orientado a objetos, pois permite
que esses mesmos objetos colaborem entre si para, por
meio de troca de mensagens, produzir resultados externamente
visíveis aos usuários. Nesse contexto, considere as
afirmativas abaixo.
I - Um objeto de uma classe C qualquer pode ser composto
de outros objetos, sendo que esses últimos
podem também ser da classe C.
II - Uma mensagem pode ser enviada de um objeto da
classe ClasseA para um objeto da classe ClasseB,
sem que a definição de ClasseA faça referência à
ClasseB.
III - Uma operação, definida em uma classe abstrata C qualquer,
pode ser invocada por um objeto de alguma
superclasse de C.
É(São) correta(s) a(s) afirmativa(s)
(A) I, apenas.
(B) I e II, apenas.
(C) I e III, apenas.
(D) II e III, apenas.
(E) I, II e III.
Ainda na orientação a objetos, essa questão trata da composição dos
objetos. A afirmação I indica que um objeto pode ser composto por outros
objetos, inclusive da mesma classe. Isso é verdadeiro. Um caso típico
seria um objeto FuncionarioA composto por um objeto GerenteA, sendo que ambos são instâncias da classe Empregado.
O item II trata de uma mensagem entre dois objetos. Segundo essa afirmação, o objeto da ClasseA pode enviar uma mensagem para um objeto da ClasseB sem que a definição da ClasseA faça referência a ClasseB. Não sei exatamente o que o texto quer dizer com "... sem que a definição da ...". A palavra definição neste caso gerou confusão, mas eu considerei como sendo "sem que a ClasseA seja formada pela ClasseB". Com essa interpretação e pelo ponto de vista prático, se a ClasseA possui um método que tenha como parâmetro uma ClasseB e, dentro desse método existir uma chamada a um comportamento da ClasseB, a ClasseA enviaria uma mensagem para a ClasseB, mesmo que a ClasseA não seja formada pela ClasseB.
Isso ficou confuso? Veja o código abaixo:
class ClasseA {
public void metodoA(ClassB b) {
b.metodoB();
}
}
class ClasseB {
public void metodoB() {
System.out.println( "Eu recebi uma mensagem da Classe A!" );
}
}
public class Main {
public static void main(String args[]) {
ClasseA a = new ClasseA();
ClasseB b = new ClasseB();
a.metodoA( b );
}
}
Neste caso, veja que a ClasseA enviou uma mensagem para a ClasseB, sendo que a ClasseB não está contida (digamos assim) na formação da ClasseA. Por tanto, o item II é verdadeiro.
O item III é falso, pois indica que um método da ClasseC pode ser chamado pela superclasse da ClasseC. Isso não é possível. A classe-pai ainda não sabe da existência dos métodos de sua classe-filha.
Portanto os itens I e II são verdadeiros. A resposta certa é a letra B.
Essa eu acertei. Tive exatamente a mesma linha de pensamento.
Resolvendo a Questão 67
Como contém imagens, precisei do print screen nessa aqui...



Muito boa essa questão! É um típico caso de associação entre a
programação Java e a linguagem UML. Confesso que fiquei com dúvida
nessa, mas conseguir acertar. Antes de entender o diagrama, vamos às
classes.
Observem que o código da Classe0 não foi apresentado. Isso dificultou a resposta. Vejam que a Classe1 herda da Classe0, tem como atributo uma coleção de classes Classe0 chamada a e uma Classe2 chamada b. A classe Classe2 também herda da Classe0 e possui um conjunto de classes Classe1 chamada d. Resumindo isso, a associação entre as classes Classe1 e Classe2 é bidirecional (ou seja, ambas as classes se enxergam), sendo que uma Classe1 está associada a uma Classe2 e uma Classe2 está associada a muitas Classe1. Sabemos que a Classe1 possui uma associação um-para-muitos com a Classe0, mas não temos informações suficientes (o código de Classe0 não foi mostrado) para verificar se esta é uma associação bidirecional ou não.
Vamos agora analisar as respostas. Lembre-se que Classe1 e Classe2 se
conhecem, portanto, na UML, devem existir setas (< e >) para os
dois lados da associação. Isso já nos dá uma limitação nas respostas
(pois as letras A, D e E indicam que a associação dessas classes não é
bidirecional).
Agora vem a parte mais difícil. Observe que não temos o código de Classe0, portanto não temos com saber se é uma associação de muitos-para-um (como sugere a letra C) ou muitos-para-muitos (indicado
na letra B). A única diferença nessas respostas que nos dá um
indicativo é a associação por composição mostrada na letra B. Vendo o
código da Classe1, não existe nenhuma diferença considerável que indique que a associação com a Classe0 seria por composição (ainda mais se compararmos com a associação que existe com a Classe2).
Então, pode-se concluir (digamos que com 80% de chance) que esta não é
uma associação por composição, e sim uma associação simples, o que nos indica que a resposta certa é a letra C.
Confesso que essa é uma questão que pode ter respostas diferentes
se existir linhas de pensamento conflitantes. Ou seja, o que indica com
100% de certeza no código Java que uma associação entre duas classes "É"
ou "NÃO É" por composição? No Jude (que agora é Astah), por exemplo, o
código gerado por uma associação por composição é o mesmo que o gerado
por uma associação simples. Ou seja, eu particularmente considero isso
como um quesito de modelagem e não de codificação. Mas enfim, concurso é foda!!!
Alteração (em 13/8/2012): Algum tempo depois, revendo essa questão, tenho uma observação importante sobre a resposta certa: A letra B está obrigatoriamente errada porque semanticamente, segundo a notação UML, numa associação de agregação ou composição (que é o caso nessa questão), o lado onde está a agregação/composição (aqui, a Classe1), só pode conter o valor 1. Como na letra B, temos um *, está errada.
Resolvendo a Questão 68

Essa foi uma questão de UML. No processo de software que eu estou
acostumado, eu utilizaria o Diagrama de Sequência como intermediário
entre esses diagramas. Foi isso que respondi na prova e essa é realmente
a resposta correta. Já participei de um projeto em que o diagrama de
sequência foi usado exatamente na forma como mostrada na figura, embora
eu nunca tenha visto essa definição. A resposta certa é a letra D.
Mais um ponto pro Guilherme!!!
Resolvendo a Questão 69
Como não podia deixar de ser, a prova possui questões que tratam da lógica estruturada e da API do Java.
69
Considere o trecho de código fonte a seguir, escrito em linguagem Java.

Qual o resultado (saída) do programa acima?
(A) 1
(B) 3
(C) 15
(D) 153
(E) 1530
As grandes dificuldades nessa questão são: tempo para resolver os dois loops; conhecer (ou lembrar) da API Path.pow(); atentar-se ao detalhe que os valores de temp/10 estão sendo guardados em um número inteiro (int temp), portanto, serão arredondados para baixo. Observe por exemplo, o código abaixo:
int t1, t2, t3;
t1 = 153 / 10;
t2 = 155 / 10;
t3 = 159 / 10;
System.out.println( t1 + " " + t2 + " " + t3 );
t1 = 153 / 10;
t2 = 155 / 10;
t3 = 159 / 10;
System.out.println( t1 + " " + t2 + " " + t3 );
Aqui, a saída será "15 15 15", pois mesmo que o resultado de uma divisão seja mais próxima do número acima (por exemplo, 159/10 resultará em 15.9,
que normalmente aproximaríamos para 16), o Java, nesse caso, aproxima
para baixo. Portanto, caso um valor menor que 1 (por exemplo 0.7) seja armazenado num int, o valor será 0 - o que terminará os andamento dos dois loops nesse programa.
A API do Math.pow() também foi relevante para essa questão. Veja a API da versão 6 do Java em: http://java.sun.com/javase/6/docs/api/
O básico deste método é que o primeiro elemento será elevado ao segundo, ou seja, Math.pow(2,3) resultará em 2x2x2. Pode
haver dificuldade (e nessa questão, se você fizer o loop passo-a-passo,
vai verificar) se um dos elementos for 0. Para fazer um teste completo
dessa questão, acrescentei um método estático para impressão dos valores
em alguns momentos da execução. Veja o código modificado abaixo:
package petro;
public class Main {
public static void p(double total, int temp, int n, int qtdDigitos, int digito) {
System.out.println( "\nDADOS NO MOMENTO: " );
System.out.println( String.format("%-15s", "total") + total );
System.out.println( String.format("%-15s", "temp") + temp );
System.out.println( String.format("%-15s", "n") + n );
System.out.println( String.format("%-15s", "qtdDigitos") + qtdDigitos );
System.out.println( String.format("%-15s", "digito") + digito );
}
System.out.println( "\nDADOS NO MOMENTO: " );
System.out.println( String.format("%-15s", "total") + total );
System.out.println( String.format("%-15s", "temp") + temp );
System.out.println( String.format("%-15s", "n") + n );
System.out.println( String.format("%-15s", "qtdDigitos") + qtdDigitos );
System.out.println( String.format("%-15s", "digito") + digito );
}
public static void main(String[] args) {
double total = 0.0;
int temp, n;
temp = n = 153;
int qtdDigitos = 0;
double total = 0.0;
int temp, n;
temp = n = 153;
int qtdDigitos = 0;
p(total, temp, n, qtdDigitos, 0);
while (temp > 0) {
qtdDigitos++;
temp = temp / 10;
qtdDigitos++;
temp = temp / 10;
p(total, temp, n, qtdDigitos, 0);
}
temp = n;
}
temp = n;
p(total, temp, n, qtdDigitos, 0);
while (temp > 0) {
int digito = temp % 10;
total += Math.pow(digito, qtdDigitos);
temp = temp / 10;
int digito = temp % 10;
total += Math.pow(digito, qtdDigitos);
temp = temp / 10;
p(total, temp, n, qtdDigitos, digito);
}
}
p(total, temp, n, qtdDigitos, 0);
System.out.println();
System.out.println((int) total);
}
}
}
}
O método p() vai imprimir os valores em determinados momentos.
Com isso poderemos destacar os valores de cada elemento e as
dificuldades na conversão e uso da API Math.pow().
Ao executar o código acima, obtemos uma série de saídas. Através desses dados podemos montar a tabela abaixo:

Não vou entrar em detalhes sobre cada instante da execução (pois isso é
simples). Porém, grande atenção deve ser dada nos cálculos de temp/10 e Math.pow(). Em todos os momentos em que temp é
dividido por 10, seu valor sempre será arredondado para baixo. Isso
implica diretamente no número de iterações realizadas nos loops. E Math.pow() traz
como dificuldade o tempo para realizar os cálculos de cabeça na hora da
prova. Ou seja, é uma questão que podemos errar por falta de atenção no
temp/10 ou falta de tempo para realizar os loops e calcular as potências.
Se você executar o programa, terá o resultado 153. Ou seja, a resposta certa é a letra D. Fiz
essa questão correndo, fique com receio de ter errado em alguma conta,
mas acertei essa. É claro que na hora da prova eu não tive o recurso do
método p() criado aqui, então foi tudo no papel mesmo.
Resolvendo a Questão 70
Essa questão também foi de lógica e um pouco de conhecimento sobre API.
70

Analisando o programa acima, escrito em linguagem Java, conclui-se que o programa
(A) não compila, pois a classe Random deveria ter sido importada (com a diretiva import).
(B) não compila, visto que um atributo qualificado como final não pode ser usado em um comando de repetição.
(C) imprime o valor numérico 4,0.
(D) imprime o valor numérico zero.
(E) imprime um valor numérico aproximado da constante matemática PI (cujo valor, até a 4a casa decimal, é 3,1415).
Essa foi muito difícil na minha opinião. Eu não tinha conhecimento matemático suficiente para provar que o valore impresso no final seria próximo ao valor de PI. Antes de explicar o resultado, vou comentar um pouco sobre as respostas.
A letra A diz que o programa não compila por falta do import de Random. Na verdade, como foi colocado o nome completo da classe java.util.Random, não existe necessidade do import. Essa resposta está errada.
A reposta B indica que um atributo final não poderia estar sendo usado no comando for. Na verdade, na posição onde o QUANTIDADE_NUMEROS foi usado (limite para a variação do valor de int i - que não é final) é possível. O que não poderia ocorrer é a utilização de um valor final como índice do for (como é o caso de int i). Resposta errada.
Então restaram as respostas C, D e E. Ao certo, qualquer um saberia
que essa não é uma questão para resolver programaticamente (pois são
1000 iterações). É uma questão de conceito.
Certamente precisamos saber que nextDouble() é um método que retorna valores aleatórios entre 0.0 e 1.0.
Tenha em mente que o código n = n + 1 só será executado quando a soma dos produtos de x e y forem menores ou iguais a 1. Para que o produto x*x tenha participação significativa (0.5, por exemplo) na soma x*x + y*y, é necessário que o valor retornado em double x = r.nextDouble(); seja superior 0.7 (aproximadamente). Ou seja, temos 70% de chance para que o valor de x tenha representação inferior a 0.5 na soma x*x + y*y. Isso também é válido para double y = r.nextDouble();. Com base nessas afirmações, em cada iteração do for, a chance de entrar na condição e incrementar o valor de n é maior. Até ai, dá pra entender... mas isso não tem nada a ver... ou quase...
Bom, a grande sacada dessa questão é que para resolvê-la, a pessoa precisaria conhecer o método de Monte Carlo (veja vem http://pt.wikipedia.org/wiki/M%C3%A9todo_de_Monte_Carlo).
Eu nunca ouvi falar dessa cara! Putz... Mais enfim, a ideia é definir
aleatoriamente pontos (que nesse caso são indicados pelos valores x e y)
em um quadrado. Esses pontos devem estar entre as coordenadas (0,0) e
(1,1). No código, os pontos estão entre essas coordenadas. Para calcular
o número aproximado de PI, precisamos obter a divisão do número de
pontos estimados dentro da circunferência de raio 1 em relação ao total
de pontos no quadrado com lado 1. Um valor mais próximo pode ser obtido
aumentando o número de pontos sorteados. Uma melhor explicação pode ser
obtida em http://pt.wikipedia.org/wiki/Pi
Enfim, a reposta certa é a letra E. Essa ai eu errei. Achei que (sei lá como eu achei isso no dia, mas) em nenhum momento n seria
acrescido, e consequentemente o valor exibido seria zero. Talvez se
tivesse pensado mais chegaria à conclusão que isso não poderia
acontecer, mas de qualquer forma seria um chute, pois eu não sabia esse
lance do Monte Carlo.
Conclusão
Bom galera, é isso. Dessa vez a Petrobrás venceu, mas um dia serei eu, você que leu esse documento até aqui, e outros Javeiros de plantão. Espero ter ajudado.
"No sentido estritamente cientifico todos nós nos alimentamos da morte, até mesmo vegetarianos." (Spock - Star Trek)
Abraços.
Nenhum comentário:
Postar um comentário