CodeGym /Blogue Java /Random-PT /Explorando perguntas e respostas de uma entrevista de emp...
John Squirrels
Nível 41
San Francisco

Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java. Parte 8

Publicado no grupo Random-PT
Prática ou teoria? O que é mais importante? Muitas pessoas dirão naturalmente que a prática é mais importante. Tipo, pratique até o sol se pôr e você ficará feliz. Atrevo-me a discordar disso. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 1Durante as entrevistas, ninguém saberá o quão legal você é quando está praticando. Em vez disso, você será solicitado a demonstrar suas habilidades teóricas. Só mais tarde, quando você tiver passado por todas as etapas das entrevistas e for designado para um projeto, é que você aplicará suas habilidades práticas. Você pode objetar, dizendo que às vezes eles lhe dão uma tarefa de teste, então ainda é necessária prática. Não discordo, mas o que quero dizer é que às vezes isso acontece, mas você SEMPRE precisa demonstrar conhecimento da teoria em uma entrevista. Você sente a diferença? Tudo isso significa que você deve ter uma base teórica sólida sob os pés, e é isso que vamos continuar construindo hoje. Mais especificamente, continuaremos a analisar perguntas frequentemente feitas em entrevistas.

71. O que acontece se não substituirmos o método toString() do Enum?

Suponha que tenhamos o seguinte enum :
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;
}
Vamos exibir o campo STUDENT no console chamando seu método toString() :
System.out.println(Role.STUDENT.toString());
Como resultado, obtemos a seguinte saída do console:
ESTUDANTE
Assim, vemos que para um enum , a implementação padrão de toString() retorna o nome da própria constante.

72. Você pode declarar um construtor dentro de um Enum?

Sim claro. O construtor é o que define os valores dos campos internos do enum . Como exemplo, vamos adicionar dois campos à enumeração anterior ( ageFrom e ageTo ) para indicar a faixa etária de cada função:
public enum Role {
   STUDENT(5,18),
   TEACHER(20,60),
   DIRECTOR(40,70),
   SECURITY_GUARD(18,50);

   int ageFrom;
   int ageTo;

   Role(int ageFrom, int ageTo) {
       this.ageFrom = ageFrom;
       this.ageTo = ageTo;
   }
}

73. Qual é a diferença entre == e igual ()?

Esta é uma das perguntas de entrevista mais comuns feitas a aspirantes a desenvolvedores Java. Para começar, ao comparar valores simples ( int , char , double ...), usamos == , já que essas variáveis ​​contêm valores concretos que podem ser comparados diretamente. Além do mais, variáveis ​​primitivas não são objetos completos — elas não herdam a classe Object e não possuem um método equals() . Se estamos falando em comparar variáveis ​​que se referem a objetos, então precisamos saber que == apenas compara o valor das referências, ou seja, se elas se referem ao mesmo objeto ou não. Mesmo que todos os dados de um objeto sejam idênticos a todos os dados de outro, usar == para uma comparação produzirá um resultado negativo ( false ), porque são objetos separados. Como você deve ter adivinhado, usamos o método equals() para comparar variáveis ​​de referência. Este é um dos métodos padrão da classe Object e é necessário para uma comparação completa de objetos. Mas preciso dizer desde já que para que esse método funcione corretamente, ele deve ser sobrescrito para indicar exatamente como os objetos devem ser comparados. Se você não substituir o método, obterá a implementação padrão, que compara os objetos usando == . No IntelliJ IDEA, você pode substituí-lo automaticamente usando um atalho IDEA: Alt+Insert . Na janela que aparece, selecione equals() e hashCode() . Em seguida, selecione os campos que devem estar envolvidos. Voilá! Os métodos são implementados automaticamente. Aqui está um exemplo de como um método equals gerado automaticamente procura a classe Cat mais simples possível com dois campos — int age e String name :
@Override
public boolean equals(final Object o) {
   if (this == o) return true;
   if (o == null || this.getClass() != o.getClass()) return false;
   final Cat cat = (Cat) o;
   return this.age == cat.age &&
           Objects.equals(this.name, cat.name);
}
Quando se trata de enum , não há uma diferença prática entre == e equals() . Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 2Afinal, um enum armazena constantes, e mesmo quando comparamos valores idênticos usando == , obteremos true , já que as referências comparadas sempre apontarão para os mesmos objetos. E usar equals() também nos dá o resultado correto. Se você entrar no corpo do método equals para Enum , verá que a classe Enum tem a seguinte implementação: Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 3Dentro podemos ver uma boa e velha comparação de referências! Para resumir, para enum s, podemos comparar corretamente usando == e equals() . Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 4

74. O que o método ordinal() de Enum faz?

Quando chamamos o método int ordinal() em um campo enum , obtemos o índice baseado em zero do campo na lista de valores enum. Vamos chamar esse método em um campo na enumeração Role , que consideramos anteriormente:
System.out.println(Role.DIRECTOR.ordinal());
Assim, o console exibe:
2

75. O Enum pode ser usado com TreeSet ou TreeMap em Java?

Podemos usar tipos enum em TreeSet e TreeMap . E podemos escrever isto:
TreeSet<Role> treeSet = new TreeSet<>();
treeSet.add(Role.SECURITY_GUARD);
treeSet.add(Role.DIRECTOR);
treeSet.add(Role.TEACHER);
treeSet.add(Role.STUDENT);
treeSet.forEach(System.out::println);
E o console exibirá:
DIRETOR PROFESSOR ESTUDANTE SECURITY_GUARD
Obtivemos o resultado, mas não em ordem alfabética. A questão é que, se usarmos campos enum como valores TreeSet ou como chaves TreeMap , os campos serão classificados em sua ordem natural (na ordem em que são especificados em enum ) . Entender que é assim que funciona nos ajuda a escrever um código melhor.

76. Como os métodos ordinal() e compareTo() de Enum estão relacionados?

Conforme mencionado anteriormente, ordinal() retorna o índice de um campo na lista de campos enum. Além disso, em nossa consideração da questão anterior, você viu que quando os campos enum são colocados em um TreeSet (que é um conjunto classificado), eles assumem a ordem em que são declarados no enum . E como sabemos, TreeSet e TreeMap classificam itens chamando o método compareTo() de sua interface Comparable . Isso nos diz que a classe Enum implementa a interface Comparable , o que significa que implementa o método compareTo() , que usa internamente o método ordinal() para determinar a ordem de classificação. Indo para a classe Enum , podemos confirmar nossa suposição: Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 5E aqui está o corpo do método em si: Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 6O método ordinal() não é chamado aqui. Em vez disso, é usada a variável ordinal , que é o número de índice do elemento na enumeração. O método ordinal()Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 8 - 7 em si nada mais é do que um getter para a variável ordinal .

77. Escreva um exemplo de Enum

Nas questões discutidas acima, já dei exemplos de enum s. Não vejo razão para duplicar o código aqui. Por exemplo, consulte a pergunta 72 sobre um construtor em um enum.

78. Um Enum pode ser usado em um switch case?

Pode e deve ser! Olhando para minha experiência, observarei que um dos usos mais comuns de enum é em construções lógicas, como instruções switch . Nesse caso, você pode fornecer todos os case s possíveis — depois de escrever a lógica para cada campo enum , você nem precisa de uma cláusula padrão ! Afinal, se você usar String ou um valor numérico, como um int , poderá receber um valor inesperado, mas isso é impossível com um enum . Aqui está a aparência de uma instrução switch para o exemplo acima:
public void doSomething(Role role) {
   switch (role) {
       case STUDENT:
           // some logic for STUDENT
           break;
       case TEACHER:
           // some logic for TEACHER
           break;
       case DIRECTOR:
           // some logic for DIRECTOR
           break;
       case SECURITY_GUARD:
           // some logic for SECURITY_GUARD
           break;
   }
}

79. Como obtenho todos os valores possíveis de um Enum?

Se você precisar obter todos os valores possíveis do enum, existe um método values() , que retorna uma matriz de todos os valores possíveis para o enum em sua ordem natural (ou seja, na ordem em que são especificados no enum ). Exemplo:
Role[] roles = Role.values();
for (Role role : roles) {
   System.out.println(role);
}
Teremos o seguinte no console:
DIRETOR PROFESSOR ESTUDANTE SECURITY_GUARD

API de fluxo

80. O que é um Stream em Java?

A API Java Stream é uma maneira relativamente nova de interagir com um fluxo de dados, permitindo-nos processar big data de maneira mais conveniente e compacta, bem como processar dados em paralelo entre um certo número de fluxos, aumentando potencialmente o desempenho.

81. Cite as principais propriedades das transações

O tópico aqui é a API Stream, mas a questão é sobre transações. Hmm... Primeiro, vamos entender o que é uma transação. Uma transação é um grupo de operações sequenciais em um banco de dados. Representa uma unidade lógica de trabalho. Uma transação pode ser executada independentemente de outras transações simultâneas de forma completa e bem-sucedida, mantendo assim a integridade dos dados, ou não ser executada, caso em que não terá efeito. As transações têm quatro propriedades principais, que podemos lembrar facilmente graças à sigla ACID . Vamos ver o que significa cada letra desta sigla: A significa Atomicidade . Esta propriedade garante que nenhuma transação seja parcialmente confirmada no sistema. Todas as suas suboperações serão executadas ou nenhuma delas será executada ( tudo ou nada ). С significa Consistência . Esta propriedade garante que cada transação bem-sucedida comprometerá apenas resultados válidos. Em outras palavras, é uma garantia de que se a transação for bem-sucedida, todas as regras do sistema para dados específicos serão obedecidas. Se a transação não for bem-sucedida, ela não será executada e os dados do sistema retornarão ao estado anterior. I significa Isolamento . Esta propriedade significa que quando uma transação é executada, as transações simultâneas não devem afetar o seu resultado. Esta propriedade consome muitos recursos, pelo que, via de regra, é parcialmente implementada, permitindo determinados níveis de isolamento que resolvem problemas específicos de isolamento. Discutiremos isso com mais detalhes na próxima pergunta. D significa Durabilidade . Esta propriedade garante que caso o usuário receba a confirmação de que a transação foi concluída, ele poderá ter certeza de que as alterações não serão canceladas por alguma falha. Ou seja, você pode ter certeza de que alguma falha no sistema operacional não afetará seus dados se você já tiver recebido a confirmação de que sua transação foi concluída com sucesso.

82. Quais são os níveis de isolamento das transações?

Como eu disse anteriormente, quando se trata de propriedades ACID, garantir o isolamento é um processo que consome muitos recursos. Assim, esta propriedade está parcialmente implementada. Existem diferentes níveis de isolamento: quanto maior o nível, mais severo é o impacto no desempenho. Antes de passarmos para os níveis de isolamento de transações, precisamos considerar vários problemas que ocorrem devido ao isolamento insuficiente de transações :
  • leituras fantasmas : quando a mesma requisição, chamada mais de uma vez dentro de uma mesma transação, produz resultados diferentes devido a inserções por outra transação;

  • leituras não repetíveis : quando a mesma solicitação, chamada mais de uma vez dentro de uma única transação, produz dados diferentes devido a alterações (atualizações) e exclusões por outra transação;

  • leituras sujas : leitura de dados ainda não confirmados que foram adicionados ou modificados por uma transação e posteriormente revertidos;

  • atualizações perdidas : quando um bloco de dados é alterado simultaneamente por diferentes transações e todas as alterações, exceto a última, são perdidas (semelhante a uma condição de corrida em multithreading).

Na verdade, os níveis de isolamento das transações são caracterizados pelos problemas de isolamento contra os quais eles protegem. Considere a seguinte tabela de níveis de isolamento e os problemas contra os quais eles protegem:
Nível de isolamento Leituras fantasmas Leituras não repetíveis Leituras sujas Atualização perdida
SERIALIZÁVEL + + + +
LEITURA REPETÍVEL - + + +
LEIA COMPROMETIDO - - + +
LEIA NÃO COMPROMETIDO - - - +
NENHUM - - - -
E não se esqueça do outro lado: quanto maior o nível de isolamento, mais tempo as transações levam para serem processadas (dada a execução paralela de múltiplas transações).

83. Qual é a diferença entre uma Declaração e uma PreparedStatement?

Aqui mudamos abruptamente a transição para recursos do JDBC . De qualquer forma, vamos primeiro descobrir do que se trata uma Declaração . É um objeto usado para formar consultas SQL. JDBC usa três tipos: Statement , PreparedStatement e CallableStatement . Não consideraremos CallableStatement hoje. Em vez disso, estamos falando sobre a diferença entre Statement e PreparedStatement .
  1. A instrução é usada para executar consultas SQL simples sem parâmetros de entrada de tempo de execução. PrepareStatement pode aceitar parâmetros de entrada em tempo de execução.

  2. Para definir parâmetros para PreparedStatement , os parâmetros de entrada são escritos como pontos de interrogação na solicitação, para que possam ser substituídos por algum valor usando vários setters, como setDouble() , setFloat() , setInt() , setTime() ... Isso significa que você não inserirá o tipo errado de dados na solicitação.

  3. PreparedStatement é pré-compilado e usa cache, portanto pode ser executado um pouco mais rápido do que uma solicitação feita de objetos Statement . Como resultado, as instruções SQL executadas com frequência são criadas como objetos PreparedStatement para melhorar o desempenho.

  4. A instrução é vulnerável à injeção de SQL, mas PreparedStatement os impede.

E com isso, vamos encerrar o dia!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION