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 7

Publicado no grupo Random-PT
Olá a todos! A programação está cheia de armadilhas. E dificilmente existe um único tópico que não faça você tropeçar e dar uma topada no dedo do pé. Isto é especialmente verdadeiro para iniciantes. A única maneira de salvar os dedos dos pés é aprendendo. Em particular, você precisa se aprofundar nos tópicos mais fundamentais. Hoje continuaremos a revisão das perguntas mais populares durante entrevistas para desenvolvedores Java. Essas perguntas da entrevista fazem um excelente trabalho ao cobrir tópicos básicos. Observe que a lista também inclui algumas perguntas não tão padronizadas que permitem abordar questões comuns de maneira diferente. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 7 - 1

62. O que é o pool de strings e por que ele é necessário?

Parte da memória disponibilizada para um programa Java é chamada de heap (do qual falaremos mais tarde), e parte do heap é chamada de String pool . É para armazenar valores de string. Em outras palavras, quando você cria uma string, por exemplo, usando aspas duplas como esta:
String str = "Hello world";
A JVM verifica se o conjunto de cadeias já possui o valor especificado. Se isso acontecer, então a variável str receberá uma referência para esse valor no pool. Caso contrário, um novo valor será criado no pool e uma referência a ele será atribuída à variável str . Vamos considerar um exemplo:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true será exibido na tela. Lembre-se de que == compara referências e essas duas variáveis ​​apontam para o mesmo valor no conjunto de strings. Isso ajuda a evitar a produção de muitos objetos String idênticos na memória. Podemos fazer isso porque, como você deve se lembrar, String é uma classe imutável, portanto não há nada de errado em ter múltiplas referências ao mesmo valor. Agora é impossível ter uma situação em que a alteração do valor em um local resulte em alterações em diversas outras referências. Ainda assim, se criarmos uma string usando new :
String str = new String("Hello world");
então, um objeto separado é criado na memória e armazena o valor da string especificado (não importa se esse valor já está no conjunto de strings). Para confirmar esta afirmação, considere o seguinte:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
Obteremos duas linhas indicando false , o que significa que temos três strings separadas. Basicamente, é por isso que você deve criar strings simplesmente usando aspas duplas. Dito isto, é possível adicionar (ou obter uma referência a) valores no conjunto de strings mesmo ao criar um objeto usando a palavra-chave new . Para fazer isso, usamos o método intern() da classe String. Este método garante que criaremos o valor no conjunto de strings ou obteremos uma referência a ele se já estiver lá. Aqui está um exemplo:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
Este código retorna true para o console três vezes, o que nos diz que todas as três variáveis ​​se referem à mesma string na memória.

63. Quais padrões de design GoF são usados ​​no pool de strings?

No conjunto de strings, o padrão de design GoF é um padrão flyweight . Se você notou outro padrão de design aqui, compartilhe-o nos comentários. Aqui falaremos sobre o padrão de design flyweight. É um padrão de projeto estrutural no qual um objeto que se representa como uma instância única em diferentes locais do programa não é, na verdade, único. O flyweight economiza memória armazenando o estado compartilhado dos objetos em vez de armazenar dados idênticos em cada objeto. Para entender a essência, considere este exemplo elementar. Digamos que temos uma interface Employee :
public interface Employee {
   void work();
}
E tem algumas implementações, como uma classe Lawyer :
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
E uma aula de contador :
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
Os métodos são totalmente arbitrários — neste exemplo, só precisamos ver se eles estão sendo executados. O mesmo se aplica ao construtor. A saída do console nos informa quando novos objetos são criados. Dispomos também de um departamento de recursos humanos cuja função é devolver um funcionário solicitado. Se esse funcionário ainda não estiver na equipe, ele será contratado e o departamento de RH devolverá o novo funcionário:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
Então a lógica é simples: se o objeto desejado existir, retorne-o; caso contrário, crie-o, coloque-o em armazenamento (algo como um cache) e devolva-o. Agora vamos ver como tudo funciona:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
Aqui está o que veremos no console:
Um advogado foi contratado. Resolvendo questões legais... Um contador foi contratado. Manter registros contábeis... Resolver questões jurídicas... Manter registros contábeis... Resolver questões jurídicas... Manter registros contábeis... Resolver questões jurídicas... Manter registros contábeis... Resolver questões jurídicas... Manter a contabilidade registros…
Como você pode ver, criamos apenas dois objetos e os reutilizamos diversas vezes. O exemplo é muito simples, mas demonstra como esse padrão de design pode conservar nossos recursos. Como você deve ter notado, a lógica desse padrão é dolorosamente semelhante à lógica de um pool de seguros. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 7 - 2

64. Como dividimos uma string em partes? Dê um exemplo do código relevante

Obviamente, esta questão é sobre o método split . A classe String possui duas variações deste método:
String split(String regex);
e
String split(String regex);
O parâmetro regex é o delimitador — alguma expressão regular usada para dividir a string em um array de strings, por exemplo:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
O console exibirá:
Olá, mundo, é o Amigo!
Portanto, nossa string foi dividida em um array de strings, usando um espaço como delimitador (em vez da expressão regular "\\s" , também poderíamos ter usado a expressão de string comum " " ). A segunda variante, sobrecarregada, possui um parâmetro de limite adicional . limit é o tamanho máximo permitido da matriz resultante. Em outras palavras, uma vez que a string tenha sido dividida no número máximo permitido de substrings, a divisão para e o último elemento conterá quaisquer "sobras" da string possivelmente não dividida. Exemplo:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
Saída do console:
Olá, mundo, é o Amigo!
Como podemos ver, se não fosse limit = 2 , o último elemento do array poderia ser dividido em três substrings.

65. Por que uma matriz de caracteres é melhor do que uma string para armazenar uma senha?

Existem vários motivos para preferir um array a uma string ao armazenar uma senha:

1. O pool de strings e a imutabilidade das strings.

Ao usar um array ( char[] ), podemos apagar explicitamente os dados assim que terminarmos de trabalhar com eles. Também podemos sobrescrever o array tanto quanto quisermos, eliminando a senha do sistema antes mesmo da coleta de lixo (é suficiente alterar algumas células para valores inválidos). Por outro lado, String é uma classe imutável. Isso significa que se quisermos alterar o valor de um objeto String , obteremos um novo, mas o antigo permanecerá no conjunto de strings. Se quisermos remover a String que contém a senha, enfrentaremos uma tarefa complicada, pois precisamos que o coletor de lixo remova esse valor do conjunto de strings, mas essa String provavelmente permanecerá lá por muito tempo. Ou seja, quando se trata de armazenar dados com segurança, String é inferior a um array char .

2. Se enviarmos o valor String para o console (ou um log), obteremos:

String password = "password";
System.out.println("Password - " + password);
Saída do console:
Senha - senha
E se acontecer de você imprimir o array no console:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
o console exibirá um jargão incompreensível:
Senha - [C@7f31245a
Na verdade, não é bobagem. Veja como entender o que você vê: [C é o nome da classe - array char , @ é um delimitador e, em seguida, 7f31245a é um código hash hexadecimal.

3. O Guia de referência oficial da Java Cryptography Architecture (JCA) menciona explicitamente o armazenamento de senhas em um char[] em vez de um String :

"Pareceria lógico coletar e armazenar a senha em um objeto do tipo java.lang.String . Porém, aqui vai a ressalva: Objetos do tipo String são imutáveis, ou seja, não existem métodos definidos que permitam alterar (sobrescrever) ou zerar o conteúdo de uma String após o uso. Esse recurso torna os objetos String inadequados para armazenar informações confidenciais de segurança, como senhas de usuário. Em vez disso, você deve sempre coletar e armazenar informações confidenciais de segurança em uma matriz de caracteres. Explorando perguntas e respostas de uma entrevista de emprego para um cargo de desenvolvedor Java.  Parte 7 - 3

Enum

66. Dê uma breve descrição de Enum em Java

Enum é a abreviação de enumeração, que é um conjunto de constantes de string unidas por um tipo comum. Declaramos um usando a palavra-chave enum . Aqui está um exemplo com enum : funções permitidas em algum campus escolar:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
As palavras escritas em letras maiúsculas são as constantes de enumeração. São declarados de forma simplificada, sem o novo operador. O uso de enumerações facilita muito a vida, pois ajudam a evitar erros e confusões nos nomes (já que a lista define os únicos valores válidos). Para mim, eles são muito convenientes na construção do switch .

67. Um Enum pode implementar interfaces (use a palavra-chave implements)?

Sim. Afinal, enums devem representar mais do que apenas conjuntos passivos (como funções em um campus escolar). Em Java, eles podem representar objetos mais complexos, portanto, pode ser necessário adicionar funcionalidades adicionais a eles. Isso também permite aproveitar o polimorfismo, substituindo o valor enum em locais onde o tipo de interface implementado é necessário.

68. O Enum pode estender uma classe (use a palavra-chave extends)?

Não, não pode, porque um enum é uma subclasse da classe Enum<T> padrão , onde T é o tipo de enum. Isso nada mais é do que uma classe base comum para todos os tipos de enum na linguagem Java. A conversão de um enum em uma classe é feita pelo compilador Java em tempo de compilação. A extensão não está explicitamente indicada no código, mas está sempre implícita.

69. É possível criar um Enum sem nenhuma instância de objeto?

Esta questão é um pouco confusa e não tenho certeza se a entendi completamente. Tenho duas interpretações: 1. Você pode ter um enum sem nenhum valor? Sim, claro, mas seria como uma aula vazia - sem sentido, por exemplo
public enum Role {
}
E se ligarmos:
var s = Role.values();
System.out.println(s);
Obtemos o seguinte no console:
[Lflyweight.Role;@9f70c54
(uma matriz vazia de valores Role ) 2. É possível criar um enum sem o operador new ? Sim claro. Como eu disse acima, você não usa o operador new para valores enum, pois são valores estáticos.

70. Podemos substituir o método toString() de Enum?

Sim, claro que você pode substituir o método toString() para definir como exibir seu enum quando o método toString for chamado (ao converter um enum em uma string comum, por exemplo, para enviá-lo para o console ou logs).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
Isso é tudo para mim hoje. Até a próxima parte!
Comentários
  • Populares
  • Novas
  • Antigas
Você precisa acessar para deixar um comentário
Esta página ainda não tem nenhum comentário