Preguntas de la entrevista de Complete Works of Java (8)

Preguntas de la entrevista de Complete Works of Java (8)

Baiyu es jaja

71. ¿Cómo listar todos los archivos en un directorio con código Java?

Respuesta:
Si solo necesita enumerar los archivos en la carpeta actual, el código es el siguiente:


import java.io.File;
class Test12 {
    public static void main(String[] args) {
        File f = new File("/Users/Hao/Downloads");
        for(File temp : f.listFiles()) {
            if(temp.isFile()) {
                System.out.println(temp.getName());
            }
        }
    }
}

Si necesita continuar expandiendo la carpeta, el código es el siguiente:


import java.io.File;
class Test12 {
    public static void main(String[] args) {
        showDirectory(new File("/Users/Hao/Downloads"));
    }
    public static void showDirectory(File f) {
        _walkDirectory(f, 0);
    }
    private static void _walkDirectory(File f, int level) {
        if(f.isDirectory()) {
            for(File temp : f.listFiles()) {
                _walkDirectory(temp, level + 1);
            }
        }
        else {
            for(int i = 0; i < level - 1; i++) {
                System.out.print("\t");
            }
            System.out.println(f.getName());
        }
    }
}

Puede usar la API NIO.2 para hacer lo mismo en Java 7. El código es el siguiente:


class ShowFileTest {
    public static void main(String[] args) throws IOException {
        Path initPath = Paths.get("/Users/Hao/Downloads");
        Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
                    throws IOException {
                System.out.println(file.getFileName().toString());
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

72. Utilice la programación de sockets de Java para implementar un servidor de eco de subprocesos múltiples.

responder:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
    private static final int ECHO_SERVER_PORT = 6789;
    public static void main(String[] args) {        
        try(ServerSocket server = new ServerSocket(ECHO_SERVER_PORT)) {
            System.out.println("服务器已经启动...");
            while(true) {
                Socket client = server.accept();
                new Thread(new ClientHandler(client)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static class ClientHandler implements Runnable {
        private Socket client;
        public ClientHandler(Socket client) {
            this.client = client;
        }
        @Override
        public void run() {
            try(BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
                    PrintWriter pw = new PrintWriter(client.getOutputStream())) {
                String msg = br.readLine();
                System.out.println("收到" + client.getInetAddress() + "发送的: " + msg);
                pw.println(msg);
                pw.flush();
            } catch(Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Nota: El código anterior usa la sintaxis TWR de Java 7. Dado que muchas clases de recursos externos implementan indirectamente la interfaz AutoCloseable (interfaz de devolución de llamada de método único), puede usar la sintaxis TWR para llamar automáticamente a recursos externos mediante devoluciones de llamada al final del intento. El método close () de la clase evita escribir largos bloques de código. Además, el código anterior usa una clase interna estática para implementar la función de subproceso. El uso de subprocesos múltiples puede evitar que la interrupción causada por la operación de E / S de un usuario afecte el acceso de otros usuarios al servidor. En pocas palabras, la operación de entrada de un usuario no causará Bloqueo de otros usuarios. Por supuesto, el código anterior utiliza un grupo de subprocesos para obtener un mejor rendimiento, porque la sobrecarga causada por la creación y destrucción frecuentes de subprocesos no se puede ignorar.

El siguiente es un fragmento de código de prueba del cliente echo:


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class EchoClient {
    public static void main(String[] args) throws Exception {
        Socket client = new Socket("localhost", 6789);
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入内容: ");
        String msg = sc.nextLine();
        sc.close();
        PrintWriter pw = new PrintWriter(client.getOutputStream());
        pw.println(msg);
        pw.flush();
        BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
        System.out.println(br.readLine());
        client.close();
    }
}

Si desea utilizar el conector multiplexado de NIO para implementar un servidor, el código es el siguiente. Aunque el funcionamiento de NIO ofrece un mejor rendimiento, algunas operaciones son de nivel relativamente bajo, lo que sigue siendo un poco difícil de entender para los principiantes.


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class EchoServerNIO {
    private static final int ECHO_SERVER_PORT = 6789;
    private static final int ECHO_SERVER_TIMEOUT = 5000;
    private static final int BUFFER_SIZE = 1024;
    private static ServerSocketChannel serverChannel = null;
    private static Selector selector = null;    // 多路复用选择器
    private static ByteBuffer buffer = null;    // 缓冲区
    public static void main(String[] args) {
        init();
        listen();
    }
    private static void init() {
        try {
            serverChannel = ServerSocketChannel.open();
            buffer = ByteBuffer.allocate(BUFFER_SIZE);
            serverChannel.socket().bind(new InetSocketAddress(ECHO_SERVER_PORT));
            serverChannel.configureBlocking(false);
            selector = Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static void listen() {
        while (true) {
            try {
                if (selector.select(ECHO_SERVER_TIMEOUT) != 0) {
                    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                    while (it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        handleKey(key);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private static void handleKey(SelectionKey key) throws IOException {
        SocketChannel channel = null;
        try {
            if (key.isAcceptable()) {
                ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                channel = serverChannel.accept();
                channel.configureBlocking(false);
                channel.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {
                channel = (SocketChannel) key.channel();
                buffer.clear();
                if (channel.read(buffer) > 0) {
                    buffer.flip();
                    CharBuffer charBuffer = CharsetHelper.decode(buffer);
                    String msg = charBuffer.toString();
                    System.out.println("收到" + channel.getRemoteAddress() + "的消息:" + msg);
                    channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));
                } else {
                    channel.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (channel != null) {
                channel.close();
            }
        }
    }
}

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
public final class CharsetHelper {
    private static final String UTF_8 = "UTF-8";
    private static CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder();
    private static CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();    private CharsetHelper() {
    }
    public static ByteBuffer encode(CharBuffer in) throws CharacterCodingException{
        return encoder.encode(in);
    }
    public static CharBuffer decode(ByteBuffer in) throws CharacterCodingException{
        return decoder.decode(in);
    }
}

73. ¿Cuántas formas de definición de documentos XML? ¿Cuál es la diferencia esencial entre ellos? ¿Cuáles son las formas de analizar documentos XML?

Respuesta: Hay dos formas de definición de documentos XML: DTD y Schema, las cuales son restricciones en la gramática XML. La diferencia esencial es que Schema en sí mismo también es un archivo XML, que puede ser analizado por un analizador XML y puede definir datos transportados por XML. La capacidad de escribir y de contención es más poderosa que la DTD. El análisis de XML incluye principalmente DOM (Document Object Model), SAX (API simple para XML) y StAX (una nueva forma de analizar XML introducida en Java 6, Streaming API para XML), donde DOM procesa archivos grandes. Cuando su rendimiento cae muy severamente, este problema es causado por la memoria ocupada por la estructura del árbol DOM, y el método de análisis DOM debe cargar todo el documento en la memoria antes de analizar el archivo, que es adecuado para el acceso aleatorio a XML (espacio típico Estrategia a cambio de tiempo); SAX es un método de análisis de XML controlado por eventos, que lee archivos XML de forma secuencial sin cargar el archivo completo de una vez. Cuando encuentra cosas como el comienzo del archivo, el final del documento o el comienzo de la etiqueta y el final de la etiqueta, activará un evento. El usuario maneja el archivo XML a través del código de devolución de llamada del evento, que es adecuado para el acceso secuencial a XML; como su nombre lo indica, StAX se enfoca en la transmisión. De hecho, la diferencia esencial entre StAX y otros métodos de análisis es que la aplicación puede procesar XML como un flujo de eventos. La idea de tratar XML como un conjunto de eventos no es nueva (SAX hace esto), pero la diferencia es que StAX permite que el código de la aplicación extraiga estos eventos uno por uno, en lugar de proporcionarlos desde el analizador cuando sea conveniente. Recibir controlador de eventos.

74. ¿Dónde usó XML en su proyecto?

Respuesta: Hay dos funciones principales de XML: intercambio de datos y configuración de información. Al realizar el intercambio de datos, XML ensambla los datos con etiquetas, luego los comprime, empaqueta y encripta y los transmite al destinatario a través de la red. Después de recibir, desencriptar y descomprimir, la información relevante se restaura del archivo XML para su procesamiento. XML solía ser un sistema heterogéneo El estándar de facto para el intercambio de datos, pero esta característica casi ha sido reemplazada por JSON (JavaScript Object Notation). Por supuesto, muchos software todavía usan XML para almacenar información de configuración. En muchos proyectos, generalmente escribimos código duro como información de configuración en archivos XML. Muchos frameworks de Java hacen esto, y estos frameworks han elegido dom4j. Como herramienta para procesar XML, porque la API oficial de Sun no es realmente fácil de usar.

Suplemento: Hoy en día, muchos software de moda (como Sublime) han comenzado a escribir archivos de configuración en formato JSON, y creemos firmemente que la industria abandonará gradualmente otra función de XML.

75. Explique los pasos de la base de datos operativa JDBC.

Respuesta: El siguiente código usa la base de datos local de Oracle como ejemplo para demostrar los pasos de JDBC operando la base de datos.


  • 加载 驱动Class.forName ("oracle.jdbc.driver.OracleDriver");
  • 创建 连接。
    Conexión con = DriverManager.getConnection ("jdbc: oracle: thin: @localhost: 1521: orcl", "scott", "tiger");
  • 创建 语句。
    PreparedStatement ps = con.prepareStatement ("seleccionar * de emp donde sal entre? Y?");
    ps.setInt (1, 1000);
    ps.setInt (2, 3000);
  • Ejecute la declaración.
    ResultSet rs = ps.executeQuery ();
  • 处理 结果。
    while (rs.next ()) {
    System.out.println (rs.getInt ("empno") + "-" + rs.getString ("ename"));
    }
  • Cierre el recurso.

    finally {
            if(con != null) {
                        try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

Sugerencia: El orden de cierre de los recursos externos debe ser el opuesto al orden de apertura, es decir, primero cierre el ResultSet, luego cierre el Statement y cierre el Connection. El código anterior solo cierra la Conexión. Aunque en general, cuando se cierra la conexión, las sentencias creadas en la conexión y el cursor abierto también se cerrarán, pero no hay garantía de que siempre sea así, por lo que deben cerrarse en el orden que acabamos de mencionar. Además, el primer paso para cargar el controlador se puede omitir en JDBC 4.0 (carga automáticamente el controlador desde la ruta de clase), pero recomendamos mantenerlo.

76. ¿Cuál es la diferencia entre Statement y PreparedStatement? ¿Cual es mejor?

Respuesta: En comparación con Statement, la interfaz ①PreparedStatement representa sentencias preparadas. Su principal ventaja es que puede reducir los errores de compilación de SQL y aumentar la seguridad de SQL (reduciendo la posibilidad de inyección de SQL ***); ②La sentencia de SQL en PreparedStatement es Puede tomar parámetros, evitando los problemas y la inseguridad de empalmar declaraciones SQL con conexiones de cadena; ③Cuando procesa SQL por lotes o ejecuta con frecuencia la misma consulta, PreparedStatement tiene ventajas obvias de rendimiento, porque la base de datos puede optimizar la compilación La instrucción SQL se almacena en caché, y la próxima vez que se ejecute la instrucción de la misma estructura, será rápida (sin compilar y generar el plan de ejecución nuevamente).

Suplemento: para proporcionar llamadas a procedimientos almacenados, la API de JDBC también proporciona la interfaz CallableStatement. El procedimiento almacenado (procedimiento almacenado) es un conjunto de sentencias SQL en la base de datos para completar una función específica, compilada y almacenada en la base de datos, el usuario especifica el nombre del procedimiento almacenado y da parámetros (si el procedimiento almacenado tiene parámetros). Ejecutalo. Aunque llamar a procedimientos almacenados obtendrá muchos beneficios en la sobrecarga de la red, la seguridad y el rendimiento, habrá muchos problemas si se migra la base de datos subyacente, porque existen muchas diferencias en la escritura de los procedimientos almacenados para cada base de datos.

77. ¿Cómo mejorar el rendimiento de la lectura de datos cuando se usa JDBC para operar la base de datos? ¿Cómo mejorar el rendimiento de la actualización de datos?

Respuesta: Para mejorar el rendimiento de la lectura de datos, puede especificar el número de registros recuperados cada vez mediante el método setFetchSize () del objeto ResultSet (una estrategia típica de espacio por tiempo); para mejorar el rendimiento de la actualización de datos, puede utilizar PreparedStatement La declaración crea un lote y varias instrucciones SQL se ejecutan en un lote.

78. ¿Cuál es la función del grupo de conexiones durante la programación de la base de datos?

Respuesta: Debido a que existe una gran sobrecarga en la creación y liberación de conexiones (especialmente cuando el servidor de base de datos no es local, se requiere un protocolo de enlace de tres vías de TCP cada vez que se establece una conexión, y un protocolo de enlace de cuatro vías de TCP para liberar la conexión no puede ignorarse). ), para mejorar el rendimiento del sistema para acceder a la base de datos, puede crear una cantidad de conexiones en el grupo de conexiones con anticipación, directamente desde el grupo de conexiones cuando sea necesario, devolver el grupo de conexiones al final del uso sin cerrar la conexión, evitando así la creación y liberación frecuente de conexiones causadas Esta es una estrategia típica de intercambiar espacio por tiempo (una pérdida de espacio para almacenar conexiones, pero ahorra tiempo para crear y liberar conexiones). La tecnología de agrupación es muy común en el desarrollo de Java, y el principio de crear una agrupación de subprocesos cuando se utilizan subprocesos es el mismo. Los grupos de conexión de bases de datos de código abierto basados ​​en Java incluyen principalmente: C3P0, Proxool, DBCP, BoneCP, Druid, etc.

79. ¿Qué es el modo DAO?

Respuesta: DAO (Data Access Object), como su nombre lo indica, es un objeto que proporciona una interfaz abstracta para una base de datos u otro mecanismo de persistencia. Proporciona varias operaciones de acceso a datos sin exponer los detalles de implementación de la solución de persistencia subyacente. En el desarrollo real, todas las operaciones de acceso a la fuente de datos deben abstraerse y encapsularse en una API pública. En lenguaje de programación, consiste en establecer una interfaz, que define todos los métodos de transacción que se utilizarán en esta aplicación. En esta aplicación, use esta interfaz cuando necesite interactuar con la fuente de datos y escriba una clase separada para implementar esta interfaz, que lógicamente corresponde a un almacén de datos específico. El modo DAO en realidad contiene dos modos, uno es Data Accessor (acceso a datos), el otro es Data Object (objeto de datos), el primero debe resolver el problema de cómo acceder a los datos, mientras que el segundo debe resolver el problema de cómo encapsular datos con objetos.

Supongo que te gusta

Origin blog.51cto.com/15061944/2593702
Recomendado
Clasificación