JavaRush /Blogue Java /Random-PT /Análise de perguntas e respostas de entrevistas para dese...

Análise de perguntas e respostas de entrevistas para desenvolvedor Java. Parte 16

Publicado no grupo Random-PT
Olá amigo! Quanto tempo leva para se tornar um desenvolvedor? Perguntei a muitas pessoas diferentes e ouvi muitas respostas diferentes. Para algumas pessoas até um mês pode ser suficiente, mas para outras nem um ano será suficiente. Mas tenho certeza de que se tornar um desenvolvedor Java é um caminho longo e espinhoso, independentemente de suas habilidades iniciais. Afinal, não é tanto a habilidade que importa, mas a teimosia e o trabalho árduo. Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 1Portanto, hoje continuamos a analisar propositalmente as perguntas de entrevista mais populares para desenvolvedores Java. Estudá-los gradualmente o aproximará de seu objetivo desejado. Vamos começar!

17. Dê exemplos de uso bem e malsucedido de Opcional

Suponha que tenhamos uma certa série de valores pelos quais passamos no fluxo e, no final, obtemos algum opcional como resultado:
Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
   .filter(str -> str.length() >= 3)
   .findAny();
Como esperado, precisamos obter o valor deste Opcional . Apenas usar get() é uma maneira ruim:
String result = stringOptional.get();
Mas esse método deveria obter o valor de Opcional e devolvê-lo para nós? Isto é, claro, verdade, mas se tiver significado. Bom, se os valores no stream fossem diferentes, e no final recebemos um opcional vazio , quando tentamos tirar um valor dele usando o método get() , será lançado o seguinte: Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 2O que não é bom. Neste caso, é melhor utilizar as seguintes construções:
  1. String result = null;
    if (stringOptional.isPresent()) {
     stringOptional.get();
    }

    Neste caso, estamos verificando se o elemento está em Opcional . Caso contrário, a string resultante terá seu valor antigo.

  2. String result = stringOptional.orElse("default value");

    Nesse caso, especificamos algum valor padrão, que será atribuído à string resultante no caso de um opcional vazio .

  3. String result = stringOptional.orElseThrow(() -> new CustomException());

    Nesse caso, nós mesmos lançamos uma exceção quando Opcional está vazio .

Isso pode ser conveniente em uma aplicação quando, por exemplo, o método Spring JPA é usado - findById() , que retorna valores Opcionais . Neste caso, com este método tentamos pegar o valor, e se não estiver lá, lançamos alguma exceção Runtime , que é processada no nível do controlador usando ExceptionHandler e convertida em uma resposta HTTP com o status 404 - NOT FOUND . Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 3

18. É possível declarar um método principal como final?

Sim, claro, nada nos impede de declarar o método main() como final . O compilador não produzirá erros. Mas vale lembrar que qualquer método após declará-lo como final se tornará o último método – não substituído. Porém, quem irá redefinir main ??? Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 4

19. É possível importar duas vezes o mesmo pacote/classe? Quais poderiam ser as consequências?

Sim você pode. Consequências? Teremos algumas importações desnecessárias que o Intelijj IDEA exibirá em cinza, ou seja, não utilizado. Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 5Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 6

20. O que é elenco? Quando podemos obter uma ClassCastException?

Casting, ou conversão de tipo , é o processo de conversão de um tipo de dados em outro tipo de dados: manualmente (conversão implícita) ou automaticamente (conversão de tipo explícito). Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 7A conversão automática é realizada pelo compilador e a conversão manual é realizada pelo desenvolvedor. A conversão de tipo para primitivas e classes é um pouco diferente, portanto, iremos considerá-las separadamente. Tipos primitivos Um exemplo de conversão automática de tipos primitivos:
int value = 17;
double convertedValue = value;
Como você pode ver, nenhuma manipulação adicional além do sinal = é necessária aqui. Exemplo de conversão manual de tipos primitivos:
double value = 17.89;
int convertedValue = (int)value;
Neste caso, podemos observar uma conversão manual, que é implementada usando (int) , onde a parte após a vírgula será descartada e o convertValue terá o valor - 17. Leia mais sobre conversão de tipos primitivos neste artigo . Bem, agora vamos passar para os objetos. Tipos de referência Para tipos de referência, a conversão automática é possível de classes descendentes para classes pai. Isso também é chamado de polimorfismo . Digamos que temos uma classe Lion que herda de uma classe Cat . Neste caso, a conversão automática ficará assim:
Cat cat = new Lion();
Mas com um elenco explícito tudo fica um pouco mais complicado, pois não há funcionalidade para cortar excessos, como acontece com os primitivos. E apenas fazendo uma conversão explícita do formulário:
Lion lion= (Lion)new Cat();
Você receberá um erro: Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 8Na verdade, você pode adicionar métodos à classe descendente de Lion que não estavam originalmente na classe Cat e, em seguida, tentar chamá-los, porque seu tipo de objeto se tornará Lion . Bem, não há lógica nisso. Portanto, o estreitamento de tipo só é possível quando o objeto original era do tipo Lion , mas foi posteriormente convertido em uma classe pai:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
Além disso, para maior confiabilidade, recomenda-se uma conversão restritiva para objetos usando a construção instanceOf :
if (cat instanceof Lion) {
 newLion = (Lion)new Cat();
}
Leia mais sobre conversões de tipo de referência neste artigo .

21. Por que as estruturas modernas usam principalmente exceções não verificadas?

Acho que tudo isso ocorre porque o tratamento de exceções verificadas ainda é um código espaguete que se repete em todos os lugares, mas não é realmente necessário em todos os casos. Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 9Nesses casos, é mais fácil fazer o processamento dentro do framework, para não transferir isso mais uma vez para os ombros dos desenvolvedores. Sim, claro, pode surgir uma situação de emergência, mas essas mesmas exceções não verificadas podem ser tratadas de maneira mais conveniente, sem se preocupar com o processamento no try-catch e sem passá-las adiante pelos métodos. Basta converter a exceção em alguma resposta HTTP em exceçãoHandler .

22. O que é importação estática?

Ao utilizar dados estáticos (métodos, variáveis), não é possível criar o objeto em si, mas sim fazê-lo pelo nome da classe, mas mesmo neste caso precisamos de uma referência à classe. Tudo é simples com ele: é adicionado por meio de importação regular. Mas e se usarmos um método estático sem escrever o nome da classe, como se fosse um método estático da classe atual? Isso é possível com importação estática! Neste caso, devemos escrever uma importação estática e um link para esse método. Assim, por exemplo, um método estático da classe Math para calcular o valor do cosseno:
import static java.lang.Math.cos;
Como resultado, podemos usar o método sem especificar o nome da classe:
double result = cos(60);
Também podemos simplesmente carregar todos os métodos estáticos de uma classe de uma só vez usando importação estática:
import static java.lang.Math.*;
Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 10

23. Qual é a relação entre os métodos hashCode() e equals()?

De acordo com a Oracle , a regra é: se dois objetos forem iguais (ou seja, o método equals() retorna true ), eles devem ter o mesmo código hash. Ao mesmo tempo, não esqueça que dois objetos diferentes podem ter o mesmo código hash. Para entender por que equals() e hashCode() são sempre substituídos em pares, considere os seguintes casos:
  1. Ambos os métodos são substituídos.

    Nesse caso, dois objetos diferentes com os mesmos estados internos retornarão equals() - true , enquanto hashCode() retornarão o mesmo número.

    Acontece que está tudo bem, pois a regra está sendo seguida.

  2. Ambos os métodos não são substituídos.

    Neste caso, dois objetos diferentes com os mesmos estados internos retornarão falso quando equals() , já que a comparação é por referência através do operador == .

    O método hashCode() também retornará valores diferentes (provavelmente), pois produz o valor convertido do endereço de localização da memória. Mas para o mesmo objeto esse valor será o mesmo, assim como equals() neste caso retornará verdadeiro apenas quando as referências apontarem para o mesmo objeto.

    Acontece que neste caso está tudo bem e a regra é cumprida.

  3. equals() substituído , não hashCode() substituído .

    Neste caso, para dois objetos diferentes com os mesmos estados internos, equals() retornará true e hashCode() retornará (provavelmente) valores diferentes.

    Isso é uma violação da regra, portanto não é recomendado fazer isso.

  4. equals() não é substituído , hashCode() é substituído .

    Neste caso, para dois objetos diferentes com os mesmos estados internos, equals() retornará false e hashCode() retornará os mesmos valores.

    Há uma violação da regra, portanto a abordagem está incorreta.

Como você pode ver, a regra só pode ser executada quando equals() e hashCode() são substituídos ou ambos não são substituídos. Leia Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 11mais sobre equals() e hashCode() neste artigo .

24. Quando as classes BufferedInputStream e BufferedOutputStream são usadas?

InputStream é usado para ler dados byte por byte de algum recurso, e OutputStream é usado para gravar dados byte-byte. Mas as operações byte a byte podem ser muito inconvenientes e exigir processamento adicional (para ler/escrever textos normalmente). Na verdade, para simplificar esses registros de bytes, BufferedOutputStream foi introduzido e BufferedInputStream foi introduzido para leitura . Essas classes nada mais são do que buffers que acumulam dados, permitindo trabalhar com dados não byte por byte, mas por pacotes inteiros de dados (arrays). Quando criado, BufferedInputStream leva em seu construtor uma instância do tipo InputStream , da qual os dados são lidos:
BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in é um objeto InputStream que lê dados do console. Ou seja, usando este objeto BufferedInputStream , podemos ler os dados do InputStream gravando-os no array passado. Isso acaba sendo uma espécie de wrapper da classe InputStream . A matriz arr deste exemplo é a matriz que recebe dados de BufferedInputStream . Este, por sua vez, lê os dados do InputStream com outro array, que por padrão tem tamanho de 2.048 bytes. O mesmo vale para BufferedOutputStream : uma instância do tipo OutputStream deve ser passada para o construtor , no qual gravaremos os dados em arrays inteiros:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out é um objeto OutputStream que grava dados no console. O método flush() envia dados de BufferedOutputStream para OutputStream , liberando BufferedOutputStream no processo . Sem este método, nada será gravado. E semelhante ao exemplo anterior: arr é o array a partir do qual os dados são gravados em BufferedOutputStream . A partir daí eles são gravados em OutputStream em um array diferente, que por padrão tem um tamanho de 512 bytes. Leia mais sobre essas duas classes no artigo .

25. Qual é a diferença entre as classes java.util.Collection e java.util.Collections?

Coleção é uma interface que é o chefe da hierarquia de coleção. Ele apresenta classes que permitem criar, conter e modificar grupos inteiros de objetos. Existem muitos métodos fornecidos para isso, como add() , remove() , contains() e outros. Principais interfaces da classe Collection :
  • Conjunto é uma interface que descreve um conjunto que contém elementos únicos não ordenados (não repetidos).

  • Lista é uma interface que descreve uma estrutura de dados que armazena uma sequência ordenada de objetos. Esses objetos recebem seu próprio índice (número), com o qual você pode interagir com eles: pegar, deletar, alterar, sobrescrever.

  • Fila é uma interface que descreve uma estrutura de dados com armazenamento de elementos na forma de uma fila que segue a regra - FIFO - Primeiro a entrar, primeiro a sair .

Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 12Leia mais sobre Coleção . Collections é uma classe utilitária que fornece muitos métodos utilitários diferentes. Por exemplo:
  • addAll(Collection<? super T> collection, T...element) - adiciona os elementos passados ​​do tipo T à coleção .

  • copy(List<? super T> dest, List<? extends T> src) - copia todos os elementos da lista src para a lista em dest .

  • emptyList() - retorna uma lista vazia.

  • max(Collection<? extends T> collection, Comparator<? super T> comp) - Retorna o elemento máximo de uma determinada coleção de acordo com a ordem especificada pelo comparador especificado.

  • unmodifiableList(List<? extends T> list) - retorna uma representação não modificável da lista passada.

E existem muitos métodos convenientes em Collections . Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 13Uma lista completa desses métodos pode ser encontrada no site da Oracle . Não é à toa que eu disse que eles são confortáveis. Afinal, eles são todos estáticos. Ou seja, você não precisa criar um objeto desta classe todas as vezes para chamar o método necessário nele. Você só precisa inserir o nome da classe, chamar nela o método desejado e passar todos os argumentos necessários. Para resumir, Collection é a interface raiz da estrutura de coleções. Collections é uma classe auxiliar para processamento mais conveniente de objetos pertencentes a um tipo da estrutura de coleções. Bem, isso é tudo por hoje. Tudo de bom!Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 16 - 14
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION