Preguntas de la entrevista de desarrollo de fondo de Java que comparten dos

¿Por qué la clase String es definitiva?

  1. Por eficiencia. Si se permite que se herede, su alta tasa de uso puede reducir el rendimiento del programa.

  2. por seguridad. Muchas clases centrales, como String proporcionada en el JDK, la implementación de muchos métodos internos de la clase no están escritas en el lenguaje de programación java en sí, y muchos métodos se denominan API nativa del sistema operativo. Esta es la famosa "llamada al método local", y solo Solo de esta manera se pueden hacer las cosas. Esta clase se comunica frecuentemente con el sistema operativo. Si esta clase se puede heredar y sus métodos se reescriben, entonces se puede escribir un fragmento de código malicioso en el sistema operativo.

  3. No quiero ser modificado. Esta clase es como una herramienta, el proveedor de la clase no quiere ser cambiado. Si puede cambiarlo casualmente, entonces el programa escrito en Java será inestable, y una de las ventajas de Java y C ++ es que es más estable.


¿Cuál es la diferencia entre String, StringBuffer y StringBuilder?

El tipo de cadena es inmutable, StringBuffer y StringBuilder son mutables.

速度 : StringBuilder> StringBuffer> String。

La capa inferior de StringBuffer y StringBuilder se realiza mediante la matriz char [].

StringBuffer es seguro para subprocesos, mientras que StringBuilder no es seguro para subprocesos.

Si desea manipular una pequeña cantidad de datos, use String, use StringBuilder para la manipulación de un solo subproceso de grandes cantidades de datos y use StringBuffer para la manipulación de múltiples subprocesos de grandes cantidades de datos.


El principio y uso de TreeSet (Comparable y Comparator)

  1. Los elementos del TreeSet no pueden repetirse, pero están en orden.

  2. TreeSet utiliza una estructura de árbol para almacenar datos, cuando se almacenan elementos, es necesario compararlos con los elementos del árbol y se debe especificar una estrategia de comparación. La estrategia de comparación se puede especificar mediante Comparable y Comparator.

  3. Las clases del sistema que implementan Comparable se pueden almacenar en TreeSet sin problemas; las clases personalizadas pueden implementar la interfaz Comparable para especificar estrategias de comparación.

  4. Puede crear una clase de implementación de la interfaz Comparator para especificar la estrategia de comparación y pasarla a través de los parámetros del constructor TreeSet; este método es especialmente adecuado para clases de sistema.


Describe brevemente el mecanismo de recolección de basura de Java

La recolección de basura es ejecutada automáticamente por la máquina virtual java, y no se permite la intervención humana. El sistema ejecutará automáticamente el mecanismo de recolección de basura cuando está inactivo. El método System.gc () se puede usar para recomendar la recolección de basura, pero no es seguro cuándo ejecutar la recolección.

Antes de que el recolector de basura JVM recopile un objeto, generalmente se requiere que el programa llame al método apropiado para liberar el recurso, pero en el caso de no liberar explícitamente el recurso, Java proporciona un mecanismo predeterminado para terminar el objeto y liberar el recurso. Este método es finalize ().

La recolección de basura se refiere a la recuperación de la memoria, y la memoria aquí se refiere principalmente a la memoria en el área de almacenamiento dinámico y en el área de métodos de la JVM.


¿Cuál es la diferencia entre dormir () y esperar ()

El método sleep () es un método estático de la clase de subproceso Thread. Permite que el subproceso que realiza la llamada entre en el estado de suspensión y brinda oportunidades de ejecución a otros subprocesos. Una vez finalizado el tiempo de suspensión, el subproceso entra en el estado listo y compite con otros subprocesos por el tiempo de ejecución de la CPU. sleep () es un método estático. No puede cambiar el bloqueo del objeto. Cuando se llama al método sleep () en un bloque sincronizado, aunque el subproceso entra en suspensión, el bloqueo de la máquina del objeto no se libera y otros subprocesos aún no pueden acceder al objeto.

wait () es un método de la clase Object. Cuando un subproceso se ejecuta al método de espera, ingresa a un grupo de espera relacionado con el objeto y, al mismo tiempo, libera el bloqueo del objeto para que otros subprocesos puedan acceder a él. Se puede acceder a través del método notificar o notificar a todos. Despierta el hilo de espera.


Pasos de salto de rana: una rana puede saltar 1 paso / 2 pasos a la vez. ¿Cuántas formas hay de saltar al enésimo paso?

/**
 * @author Renda Zhang
 * @create 2020-06-24 16:40
 */
public class FrogJumpStairs {

    // 总台阶数
    private static final int TOTAL_STAIRS = 10;

    /**
     * 数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
     * 使用 Fibonacci sequence(斐波那契数列)来解答
     * 时间复杂度:O(n) 单循环到 n
     * 空间复杂度:O(1)
     *
     * @param total 总的台阶数量
     * @return 所有跳法的总数量
     */
    private static int jumpStairsFibonacci(int total) {
        if (total == 1) {
            return 1;
        }
        int firstNum = 1;
        int secondNum = 2;
        for (int i = 3; i <= total; i++) {
            int third = firstNum + secondNum;
            firstNum = secondNum;
            secondNum = third;
        }
        return secondNum;
    }

    /**
     * Dynamic Programming (动态规划)
     * 时间复杂度:O(n) 单循环到 n
     * 空间复杂度:O(n) dp 数组用了 n 空间
     *
     * @param total 总的台阶数量
     * @return 所有跳法的总数量
     */
    private static int jumpStairsDp(int total) {
        if (total == 1) {
            return 1;
        }
        int[] dp = new int[total + 1];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= total; i++) {
            dp[i] = dp[i - 1] + dp[i -2];
        }
        return dp[total];
    }

    // 记忆每一层递归对应的数值
    private static int[] memo;

    /**
     * 递归解法的优化
     * 时间复杂度:O(n)
     * 空间复杂度:O(n)
     *
     * @param current 青蛙已经跳过的台阶数量
     * @param total 总的台阶数量
     * @return 所有跳法的总数量
     */
    private static int jumpStairsMemo(int current, int total) {
        // 如果目前已经跳过的台阶数大于总台阶数,说明传入的参数不合理,返回 0 代表跳法为 0
        if (current > total) {
            return 0;
        }
        // 如果相等,说明青蛙已经跳完一次。
        if (current == total) {
            return 1;
        }
        // 说明已有记录,直接返回
        if (memo[current] > 0) {
            return memo[current];
        }
        // 通过递归把所有次数相加即得到总次数。
        memo[current] = jumpStairsMemo(current + 1, total) + jumpStairsMemo(current + 2, total);
        return memo[current];
    }

    /**
     * 递归暴力破解法
     * 时间复杂度:O(2ⁿ)  - 递归树的所有节点数
     * 空间复杂度:O(n) - 递归树可达深度
     *
     * @param current 青蛙已经跳过的台阶数量
     * @param total 总的台阶数量
     * @return 所有跳法的总数量
     */
    private static int jumpStairs(int current, int total) {
        // 如果目前已经跳过的台阶数大于总台阶数,说明传入的参数不合理,返回 0 代表跳法为 0
        if (current > total) {
            return 0;
        }
        // 如果相等,说明青蛙已经跳完一次。
        if (current == total) {
            return 1;
        }
        // 通过递归把所有次数相加即得到总次数。
        return jumpStairs(current + 1, total) + jumpStairs(current + 2, total);
    }

    public static void main(String[] args) {
        System.out.println(jumpStairs(0, TOTAL_STAIRS));

        memo = new int[TOTAL_STAIRS + 1];
        System.out.println(jumpStairsMemo(0, TOTAL_STAIRS));

        System.out.println(jumpStairsDp(TOTAL_STAIRS));

        System.out.println(jumpStairsFibonacci(TOTAL_STAIRS));
    }
}

La diferencia y la conexión entre HashMap y Hashtable

El principio de implementación es el mismo, la función es la misma, la capa inferior es una estructura de tabla hash, la velocidad de consulta es rápida y se puede utilizar en muchos casos.

Las principales diferencias entre los dos son las siguientes

  1. Hashtable es la interfaz proporcionada por los primeros JDK, y HashMap es la interfaz proporcionada por la nueva versión del JDK.

  2. Hashtable hereda la clase Dictionary y HashMap implementa la interfaz Map.

  3. Hashtable es seguro para subprocesos, HashMap no es seguro para subprocesos.

  4. Hashtable no permite valores nulos, HashMap permite valores nulos

  5. Tanto Hashtable como HashMap usan Iterator. Por razones históricas, Hashtable también usa Enumeration.

  6. La diferencia entre el tamaño inicial y el tamaño de cada expansión: el tamaño inicial predeterminado de Hashtable es 11, y cada vez que se expande, la capacidad se convierte en el 2n + 1 original; el tamaño inicial predeterminado de HashMap es 16, y cada vez que se expande, la capacidad se convierte en 2 veces el original.

  7. El uso del valor hash es diferente, HashTable usa directamente el hashCode del objeto. Y HashMap recalcula el valor hash. HashCode es un valor de tipo int calculado por jdk según la dirección, cadena o número del objeto. Hashtable calcula el valor hash usando directamente el hashCode () de la clave, mientras que HashMap recalcula el valor hash de la clave. Hashtable cuando se evalúa el valor de hash del índice de posición correspondiente a una operación de módulo; y HashMap cuando se evalúa el índice de ubicación, el uso y la operación, y donde generalmente primero con la hash &amp; 0x7FFFFFFFpublicación, y luego la longitud del módulo con &amp; 0x7FFFFFFFel propósito de aplicar un hash negativo los valores se convirtieron en un valor positivo, porque hay un valor hash que puede ser negativo, y &amp; 0x7FFFFFFFdespués de eso solo cambia el bit de signo.


La conexión y la diferencia entre el flujo de caracteres y el flujo de bytes. ¿Cuándo usar el flujo de bytes y el flujo de caracteres?

El flujo de caracteres y el flujo de bytes son las divisiones de los flujos de E / S. Se dividen según la unidad de datos que procesa el flujo. Ambos tipos se dividen en operaciones de entrada y salida.

Los datos de salida en el flujo de bytes se realizan principalmente usando la clase OutputStream, y la entrada se realiza usando la clase InputStream; la salida en el flujo de caracteres se realiza principalmente usando la clase Writer, y el flujo de entrada se realiza principalmente usando la clase Reader. Estas cuatro son clases abstractas.

La unidad de procesamiento de flujo de caracteres es un carácter Unicode de 2 bytes, que opera "carácter, matriz de caracteres o cadena", mientras que la unidad de procesamiento de flujo de bytes es 1 byte, operando "matriz de bytes y bytes".

El flujo de bytes es el más básico. Todas las subclases InputStrem y OutputStream se utilizan principalmente para procesar datos binarios, que se procesan por bytes; pero en realidad, muchos datos son texto, por lo que también se propone el concepto de flujo de caracteres. , Se procesa de acuerdo con la codificación de caracteres de la máquina virtual, es decir, se requiere la conversión del juego de caracteres.

El flujo de bytes y el flujo de caracteres están relacionados a través de InputStreamReader y OutputStreamWriter, y la capa inferior está relacionada a través de byte [] y String.


Nombre el rendimiento de almacenamiento y las características de ArrayList y LinkedList

1. ArrayList admite la indexación para obtener los elementos correspondientes (acceso aleatorio), pero LinkedList necesita recorrer toda la lista vinculada para obtener los elementos correspondientes. Por lo tanto, la velocidad de acceso de ArrayList es generalmente más rápida que LinkedList.
2. Debido a que ArrayList es una estructura de matriz, el consumo es relativamente grande para eliminación y modificación (requiere copiar y mover la matriz); mientras que LinkedList es una lista doblemente vinculada, la eliminación y modificación solo necesitan modificar el puntero correspondiente, y su consumo es muy pequeño . Por lo tanto, en general, LinkedList agrega y elimina más rápido que ArrayList.

3. Todos son subprocesos inseguros, pero la colección de clases de vector de matriz dinámica se basa en el acceso sincrónico para lograr la seguridad de subprocesos.

4. La longitud inicial de ArrayList es 0 después de llamar a la construcción sin parámetros. Cuando se llama a add por primera vez, la longitud se convierte en 10; y LinkedList es una lista doblemente vinculada sin tamaño inicial y sin mecanismo de expansión, es decir, siempre está al principio o al final. Adiciones y eliminaciones.


Valor nulo como problema de sobrecarga de parámetros

Examine la precisión de las llamadas a funciones sobrecargadas.

Considere la salida del siguiente programa

public class TestNull { 
    public void show(String a){ 
        System.out.println("String"); 
    } 
    public void show(Object o){ 
        System.out.println("Object"); 
    } 
    public static void main(String args[]){ 
        TestNull t = new TestNull(); 
        t.show(null); 
    } 
} 

El proceso de resolución de sobrecargas de Java se ejecuta en dos etapas: la primera etapa selecciona todos los métodos o constructores disponibles y aplicables; la segunda etapa selecciona el método o constructor más preciso seleccionado en la primera etapa Uno. Si un método o constructor puede aceptar cualquier parámetro pasado a otro método o constructor, entonces el primer método se considera menos preciso que el segundo.

Este programa show(Object o)se puede pasar para aceptar cualquier show(String a)argumento, por lo que la show(Object o)relativa falta de precisión.

Por tanto, el resultado de la operación es: "Cadena".

Quiere saber más, bienvenido a seguir mi cuenta pública de WeChat: Renda_Zhang

Supongo que te gusta

Origin blog.csdn.net/qq_40286307/article/details/108877519
Recomendado
Clasificación