CodeGym /Blog Java /Random-ES /Explorando preguntas y respuestas de una entrevista de tr...
John Squirrels
Nivel 41
San Francisco

Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java. parte 7

Publicado en el grupo Random-ES
¡Hola a todos! La programación está llena de trampas. Y casi no hay un solo tema que no le haga tropezar y golpearse el dedo del pie. Esto es especialmente cierto para los principiantes. La única forma de salvar los dedos de los pies es aprender. En particular, es necesario profundizar en los temas más fundamentales. Hoy continuaremos repasando las preguntas más populares durante las entrevistas para desarrolladores de Java. Estas preguntas de la entrevista hacen un excelente trabajo al cubrir temas básicos. Tenga en cuenta que la lista también incluye algunas preguntas no tan estándar que le permiten abordar problemas comunes de manera diferente. Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 7 - 1

62. ¿Qué es el grupo de cadenas y por qué es necesario?

Parte de la memoria disponible para un programa Java se llama montón (del que hablaremos más adelante) y parte del montón se llama grupo de cadenas . Es para almacenar valores de cadena. En otras palabras, cuando creas una cadena, por ejemplo, usando comillas dobles como esta:
String str = "Hello world";
La JVM comprueba si el grupo de cadenas ya tiene el valor especificado. Si es así, a la variable str se le asigna una referencia a ese valor en el grupo. Si no es así, se crea un nuevo valor en el grupo y se asigna una referencia a él a la variable str . Consideremos un ejemplo:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
verdadero se mostrará en la pantalla. Recuerde que == compara referencias y estas dos variables apuntan al mismo valor en el grupo de cadenas. Esto ayuda a evitar producir muchos objetos String idénticos en la memoria. Podemos hacer esto porque, como recordará, String es una clase inmutable, por lo que no hay nada de malo en tener múltiples referencias al mismo valor. Ahora es imposible tener una situación en la que cambiar el valor en un lugar resulte en cambios en varias otras referencias. Aún así, si creamos una cadena usando new :
String str = new String("Hello world");
luego se crea un objeto separado en la memoria y almacena el valor de cadena especificado (no importa si ese valor ya está en el grupo de cadenas). Para confirmar esta afirmación, considere esto:
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);
Nos saldrán dos líneas que indican false , lo que significa que tenemos tres cadenas separadas. Básicamente, esta es la razón por la que deberías crear cadenas simplemente usando comillas dobles. Dicho esto, es posible agregar (u obtener una referencia a) valores en el grupo de cadenas incluso al crear un objeto usando la nueva palabra clave. Para hacer esto, usamos el método intern() de la clase String. Este método garantiza que creamos el valor en el grupo de cadenas o que obtenemos una referencia a él si ya está allí. He aquí un ejemplo:
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 sale verdadero en la consola tres veces, lo que nos dice que las tres variables se refieren a la misma cadena en la memoria.

63. ¿Qué patrones de diseño GoF se utilizan en el grupo de cadenas?

En el grupo de cuerdas, el patrón de diseño GoF es un patrón de peso mosca . Si has notado otro patrón de diseño aquí, compártelo en los comentarios. Aquí hablaremos sobre el patrón de diseño de peso mosca. Es un patrón de diseño estructural en el que un objeto que se representa a sí mismo como una instancia única en diferentes lugares del programa en realidad no es único. Flyweight ahorra memoria al almacenar el estado compartido de los objetos en lugar de almacenar datos idénticos en cada objeto. Para entender la esencia, considere este ejemplo elemental. Digamos que tenemos una interfaz de Empleado :
public interface Employee {
   void work();
}
Y tiene algunas implementaciones, como una clase de Abogado :
public class Lawyer implements Employee {

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

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
Y una clase 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...");
   }
}
Los métodos son completamente arbitrarios; para este ejemplo, solo necesitamos ver que se están ejecutando. Lo mismo ocurre con el constructor. La salida de la consola nos dice cuando se crean nuevos objetos. También contamos con un departamento de recursos humanos cuya tarea es devolver a un empleado solicitado. Si ese empleado aún no forma parte del personal, entonces se lo contrata y el departamento de recursos humanos devuelve al nuevo empleado:
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;
   }
}
Entonces, la lógica es simple: si el objeto deseado existe, devuélvalo; si no, créelo, guárdelo (algo así como un caché) y devuélvalo. Ahora veamos cómo funciona todo:
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();
}
Esto es lo que veremos en la consola:
Se contrató a un abogado. Resolver cuestiones legales... Se contrató a un contador. Llevar registros contables... Resolver cuestiones legales... Llevar registros contables... Resolver cuestiones legales... Llevar registros contables... Resolver cuestiones legales... Mantener registros contables... Resolver cuestiones legales... Llevar contabilidad registros…
Como puede ver, creamos solo dos objetos y los reutilizamos muchas veces. El ejemplo es muy simple, pero demuestra cómo este patrón de diseño puede conservar nuestros recursos. Como habrás notado, la lógica de este patrón es dolorosamente similar a la lógica de un fondo común de seguros. Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 7 - 2

64. ¿Cómo dividimos una cuerda en partes? Dé un ejemplo del código relevante.

Obviamente, esta pregunta trata sobre el método de división . La clase String tiene dos variaciones de este método:
String split(String regex);
y
String split(String regex);
El parámetro regex es el delimitador: alguna expresión regular utilizada para dividir la cadena en una matriz de cadenas, por ejemplo:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
La consola mostrará:
¡Hola mundo, soy Amigo!
Entonces, nuestra cadena se dividió en una matriz de cadenas, usando un espacio como delimitador (en lugar de la expresión regular "\\s" , también podríamos haber usado la expresión de cadena ordinaria " " ). La segunda variante, sobrecargada, tiene un parámetro límite adicional . limit es el tamaño máximo permitido de la matriz resultante. En otras palabras, una vez que la cadena se ha dividido en el número máximo permitido de subcadenas, la división se detiene y el último elemento contendrá los "restos" de la cadena posiblemente no dividida. Ejemplo:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
Salida de consola:
¡Hola mundo, soy Amigo!
Como podemos ver, si no fuera por limit = 2 , el último elemento del array podría dividirse en tres subcadenas.

65. ¿Por qué una matriz de caracteres es mejor que una cadena para almacenar una contraseña?

Hay varias razones para preferir una matriz a una cadena al almacenar una contraseña:

1. El grupo de cadenas y la inmutabilidad de cadenas.

Cuando usamos una matriz ( char[] ), podemos borrar explícitamente los datos una vez que hayamos terminado de trabajar con ellos. También podemos sobrescribir la matriz tanto como queramos, eliminando la contraseña del sistema incluso antes de la recolección de basura (basta con cambiar un par de celdas a valores no válidos). Por el contrario, String es una clase inmutable. Esto significa que si queremos cambiar el valor de un objeto String , obtendremos uno nuevo, pero el anterior permanecerá en el grupo de cadenas. Si queremos eliminar la cadena que contiene la contraseña, nos enfrentamos a una tarea complicada ya que necesitamos que el recolector de basura elimine ese valor del grupo de cadenas, pero es probable que esa cadena permanezca allí durante mucho tiempo. Es decir, cuando se trata de almacenar datos de forma segura, String es inferior a una matriz de caracteres .

2. Si enviamos el valor de Cadena a la consola (o a un registro), obtenemos:

String password = "password";
System.out.println("Password - " + password);
Salida de consola:
Contraseña - contraseña
Y si imprimes la matriz en la consola:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
la consola mostrará un galimatías incomprensible:
Contraseña: [C@7f31245a
De hecho, no es una tontería. A continuación se explica cómo entender lo que ve: [C es el nombre de la clase - matriz de caracteres , @ es un delimitador y luego 7f31245a es un código hash hexadecimal.

3. La Guía de referencia oficial de Java Cryptography Architecture (JCA) menciona explícitamente el almacenamiento de contraseñas en un char[] en lugar de un String :

"Parecería lógico recopilar y almacenar la contraseña en un objeto de tipo java.lang.String . Sin embargo, aquí está la advertencia: los objetos de tipo String son inmutables, es decir, no hay métodos definidos que le permitan cambiar (sobrescribir) o poner a cero el contenido de un String después de su uso. Esta característica hace que los objetos String no sean adecuados para almacenar información sensible a la seguridad, como contraseñas de usuario. En su lugar, siempre debe recopilar y almacenar información sensible a la seguridad en una matriz de caracteres." Explorando preguntas y respuestas de una entrevista de trabajo para un puesto de desarrollador de Java.  Parte 7 - 3

enumeración

66. Dé una breve descripción de Enum en Java.

Enum es la abreviatura de enumeración, que es un conjunto de constantes de cadena unidas por un tipo común. Declaramos uno usando la palabra clave enum . Aquí hay un ejemplo con enum : roles permitidos en algún campus escolar:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
Las palabras escritas en mayúsculas son las constantes de enumeración. Se declaran de forma simplificada, sin el nuevo operador. Usar enumeraciones hace la vida mucho más fácil ya que ayudan a evitar errores y confusiones en los nombres (ya que la lista define los únicos valores válidos). Para mí, son muy convenientes en la construcción del interruptor .

67. ¿Puede un Enum implementar interfaces (use la palabra clave implements)?

Sí. Después de todo, las enumeraciones deberían representar algo más que simples conjuntos pasivos (como roles en el campus de una escuela). En Java, pueden representar objetos más complejos, por lo que es posible que deba agregarles funcionalidad adicional. Esto también le permite aprovechar el polimorfismo sustituyendo el valor de enumeración en lugares donde se necesita el tipo de interfaz implementado.

68. ¿Puede Enum extender una clase (use la palabra clave extends)?

No, no puede, porque una enumeración es una subclase de la clase predeterminada Enum<T> , donde T es el tipo de enumeración. Esto no es más que una clase base común para todos los tipos de enumeración en el lenguaje Java. La conversión de una enumeración a una clase la realiza el compilador de Java en el momento de la compilación. La extensión no se indica explícitamente en el código, pero siempre está implícita.

69. ¿Es posible crear una enumeración sin instancias de objetos?

Esta pregunta es un poco confusa y no estoy seguro de entenderla completamente. Tengo dos interpretaciones: 1. ¿Puedes tener una enumeración sin ningún valor? Sí, por supuesto, pero sería como una clase vacía, sin sentido, por ejemplo.
public enum Role {
}
Y si llamamos:
var s = Role.values();
System.out.println(s);
Obtenemos lo siguiente en la consola:
[Rol de peso mosca;@9f70c54
(una matriz vacía de valores de roles ) 2. ¿Es posible crear una enumeración sin el nuevo operador? Sí, claro. Como dije anteriormente, no usas el operador new para valores de enumeración, ya que son valores estáticos.

70. ¿Podemos anular el método toString() de Enum?

Sí, por supuesto, puede anular el método toString() para definir cómo mostrar su enumeración cuando se llama al método toString (al convertir una enumeración en una cadena normal, por ejemplo, para enviarla a la consola o a los registros).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
Eso es todo para mí hoy. ¡Hasta la próxima parte!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION