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 7

Publicado no grupo Random-PT
Olá a todos! A programação está cheia de armadilhas. E praticamente não há assunto em que você não tropece e tenha solavancos. Isto é especialmente verdadeiro para iniciantes. A única maneira de reduzir isso é estudando. Em particular, isto se aplica a análises detalhadas dos tópicos mais básicos. Hoje continuo analisando mais de 250 perguntas de entrevistas com desenvolvedores Java, que cobrem bem tópicos básicos. Gostaria de observar que a lista também contém algumas perguntas fora do padrão que permitem examinar tópicos comuns de um ângulo diferente.Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 1

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

Na memória em Java (Heap, sobre o qual falaremos mais tarde) existe uma área - String pool ou string pool. Ele foi projetado para armazenar valores de string. Em outras palavras, quando você cria uma determinada string, por exemplo entre aspas duplas:
String str = "Hello world";
é feita uma verificação para ver se o conjunto de strings tem o valor fornecido. Nesse caso, a variável str recebe uma referência a 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 . Vejamos um exemplo:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true será exibido na tela . Lembramos que == compara referências – o que significa que essas duas referências referem-se ao mesmo valor do conjunto de strings. Isso é feito para não produzir muitos objetos idênticos do tipo String na memória, pois como lembramos, String é uma classe imutável, e se tivermos muitas referências ao mesmo valor, não há nada de errado nisso. Não é mais possível uma situação em que a alteração de um valor em um local leve a alterações em vários outros links ao mesmo tempo. Mesmo assim, se criarmos uma string usando new :
String str = new String("Hello world");
um objeto separado será criado na memória que armazenará esse valor de string (e não importa se já temos esse valor no conjunto de strings). Como confirmação:
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 dois valores falsos , o que significa que temos três valores diferentes aqui que estão sendo referenciados. Na verdade, é por isso que é recomendado criar strings simplesmente usando aspas duplas. No entanto, você pode adicionar (ou obter uma referência a) valores em um conjunto de strings ao criar um objeto usando new . Para fazer isso, usamos o método da classe string - intern() . Este método cria à força um valor no conjunto de strings ou obtém um link para ele se já estiver armazenado 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);
Como resultado, receberemos três valores verdadeiros no console , o que significa que todas as três variáveis ​​se referem à mesma string.Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 2

63. Quais padrões GOF são usados ​​no conjunto de strings?

O padrão GOF é claramente visível no pool de strings - flyweight , também chamado de colono. Se você vir outro modelo aqui, compartilhe-o nos comentários. Bem, vamos falar sobre o modelo leve. Lightweight é um padrão de projeto estrutural em que um objeto que se apresenta como uma instância única em diferentes locais do programa, na verdade, não o é. Lightweight economiza memória compartilhando o estado compartilhado dos objetos entre si, em vez de armazenar os mesmos dados em cada objeto. Para entender a essência, vejamos o exemplo mais simples. Digamos que temos uma interface de funcionário:
public interface Employee {
   void work();
}
E existem algumas implementações, por exemplo, advogado:
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("Юрист взят в штат.");
   }

   @Override
   public void work() {
       System.out.println("Решение юридических вопросов...");
   }
}
E o contador:
public class Accountant implements Employee{

   public Accountant() {
       System.out.println("Бухгалтер взят в штат.");
   }

   @Override
   public void work() {
       System.out.println("Ведение бухгалтерского отчёта....");
   }
}
Os métodos são muito condicionais: só precisamos ver se são executados. A mesma situação se aplica ao construtor. Graças à saída do console, veremos quando novos objetos serão criados. Temos também um departamento de funcionários, cuja função é emitir o funcionário solicitado, e caso ele não esteja, contratá-lo e emitir em resposta à solicitação:
public class StaffDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee receiveEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Бухгалтер":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Юрист":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("Данный сотрудник в штате не предусмотрен!");
           }
       }
       return result;
   }
}
Ou seja, a lógica é simples: se existe uma determinada unidade, devolva-a; caso contrário, crie-a, coloque-a num armazenamento (algo como uma cache) e devolva-a. Agora vamos ver como tudo funciona:
public static void main(String[] args) throws Exception {
   StaffDepartment staffDepartment = new StaffDepartment();
   Employee empl1  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl2  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl3  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl4  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl5  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl6  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl7  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl8  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
   Employee empl9  = staffDepartment.receiveEmployee("Юрист");
   empl1.work();
   Employee empl10  = staffDepartment.receiveEmployee("Бухгалтер");
   empl2.work();
}
E no console, respectivamente, haverá uma saída:
O advogado foi contratado. Resolvendo questões legais... Um contador foi contratado. Manter um relatório contábil.... Resolver questões jurídicas... Manter um relatório contábil.... Resolver questões jurídicas... Manter um relatório contábil.... Resolver questões jurídicas... Manter um relatório contábil.... Resolver questões legais. .. Manter relatórios contábeis...
Como você pode ver, foram criados apenas dois objetos, que foram reutilizados diversas vezes. O exemplo é muito simples, mas demonstra claramente como o uso deste modelo pode economizar nossos recursos. Bem, como você percebeu, a lógica desse padrão é muito semelhante à lógica do pool de seguros. Você pode ler mais sobre os tipos de padrões GOF neste artigo .Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 3

64. Como dividir uma corda em partes? Forneça um exemplo do código correspondente

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);
regex é um separador de linhas - alguma expressão regular que divide uma 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 seguinte será enviado para o console:
Olá, mundo, é Amigo!
Ou seja, nosso valor de string foi dividido em um array de strings e o separador era um espaço (para separação, poderíamos usar uma expressão regular sem espaço "\\s" e apenas uma expressão de string " " ). O segundo método sobrecarregado possui um argumento adicional - limit . limit — o valor máximo permitido da matriz resultante. Ou seja, quando a string já tiver sido dividida no número máximo permitido de substrings, não haverá mais divisão e o último elemento terá um “resto” da string possivelmente subdividida. 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, é Amigo!
Como podemos ver, se não fosse pela restrição limit=2 , o último elemento do array poderia ser dividido em três substrings.Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 4

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. Conjunto de strings e imutabilidade de strings. Ao usar um array ( char[] ), podemos apagar explicitamente os dados depois de terminarmos de usá-los. Além disso, podemos reescrever o array tanto quanto quisermos, e a senha válida não estará em nenhum lugar do sistema, mesmo antes da coleta de lixo (basta alterar algumas células para valores inválidos). Ao mesmo tempo, String é uma classe imutável. Ou seja, se quisermos alterar seu valor, obteremos um novo, enquanto o antigo permanecerá no pool de strings. Se quisermos remover o valor String de uma senha, isso pode ser uma tarefa muito difícil, pois precisamos que o coletor de lixo remova o valor do pool de String , e há uma grande probabilidade de que esse valor de String permaneça lá por um tempo. muito tempo. Ou seja, nesta situação, String é inferior ao array char em termos de segurança de armazenamento de dados. 2. Se o valor String for enviado acidentalmente para o console (ou logs), o próprio valor será exibido:
String password = "password";
System.out.println("Пароль - " + password);
Saída do console:
Senha
Ao mesmo tempo, se você acidentalmente enviar um array para o console:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Пароль - " + arr);
Haverá gobbledygook incompreensível no console:
Senha - [C@7f31245a
Na verdade, não é gobbledygook, mas: [C é o nome da classe é uma matriz de caracteres , @ é um separador, seguido por 7f31245a é um código hash hexadecimal. 3. O documento oficial, o Java Cryptography Architecture Guide, menciona explicitamente o armazenamento de senhas em char[] em vez de String : “Pareceria lógico coletar e armazenar uma senha em um objeto do tipo java.lang.String . No entanto, há uma ressalva aqui: os objetos String são imutáveis, ou seja, não existem métodos definidos para permitir que o conteúdo de um objeto String seja modificado (sobrescrito) ou anulado após o uso. Esse recurso torna os objetos String inadequados para armazenar informações confidenciais, como senhas de usuários. Em vez disso, você deve sempre coletar e armazenar informações confidenciais de segurança em uma matriz de caracteres.”Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 5

Enum

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

Enum é uma enumeração, um conjunto de constantes de string unidas por um tipo comum. Declarado através da palavra-chave -enum . Aqui está um exemplo com enum - funções válidas em uma determinada escola:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
As palavras escritas em letras maiúsculas são as mesmas constantes de enumeração que são declaradas de forma simplificada, sem utilizar o operador new . O uso de enumerações simplifica muito a vida, pois ajudam a evitar erros e confusões na nomenclatura (já que só pode haver uma determinada lista de valores). Pessoalmente, acho-os muito convenientes quando usados ​​no design lógico do Switch .

67. O Enum pode implementar interfaces?

Sim. Afinal, as enumerações devem representar mais do que apenas coleções passivas (como papéis). Em Java, eles podem representar objetos mais complexos com algumas funcionalidades, portanto, pode ser necessário adicionar funcionalidades adicionais a eles. Isso também permitirá que você use os recursos do polimorfismo, substituindo o valor enum em locais onde o tipo de interface implementada é necessário.

68. O Enum pode estender uma aula?

Não, não pode, porque um enum é uma subclasse padrão da classe genérica Enum <T> , onde T representa o tipo genérico de enum. Não passa de uma classe base comum para todos os tipos de enum da linguagem Java. A conversão de enum em classe é feita pelo compilador Java em tempo de compilação. Esta extensão não é explicitamente indicada no código, mas está sempre presente de forma invisível.

69. É possível criar um Enum sem instâncias de objetos?

Quanto a mim, a pergunta é um pouco estranha, ou não a entendi bem. Tenho duas interpretações: 1. Pode haver um enum sem valores - sim, claro que seria algo como uma classe vazia - sem sentido:
public enum Role {
}
E ligando:
var s = Role.values();
System.out.println(s);
Receberemos no console:
[Lflyweight.Role;@9f70c54
(matriz vazia de valores de função ) 2. É possível criar um enum sem o operador new - sim, claro. Como falei acima, você não precisa usar o operador new para valores enum (enumerações) , pois são valores estáticos.

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

Sim, é claro que você pode substituir o método toString() para definir uma maneira específica de exibir seu enum ao chamar o método toString (ao traduzir o enum em uma string regular, por exemplo, para saída para o console ou logs).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Выбрана роль - " + super.toString();
   }
}
Por hoje é tudo, até a próxima parte!Análise de perguntas e respostas de entrevistas para desenvolvedor Java.  Parte 7 - 6
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION