• 互联网Java工程师面试题·Java 总结篇·第八弹


    目录

    72、用 Java 的套接字编程实现一个多线程的回显(echo)服务器。

    73、XML 文档定义有几种形式?它们之间有何本质区别?解析XML 文档有哪几种方式?

    74、你在项目中哪些地方用到了 XML?


    72、用 Java 的套接字编程实现一个多线程的回显(echo)服务器。

    1. import java.io.BufferedReader;
    2. import java.io.IOException;
    3. import java.io.InputStreamReader;
    4. import java.io.PrintWriter;
    5. import java.net.ServerSocket;
    6. import java.net.Socket;
    7. public class EchoServer {
    8. private static final int ECHO_SERVER_PORT = 6789;
    9. public static void main(String[] args) {
    10. try(ServerSocket server = new
    11. ServerSocket(ECHO_SERVER_PORT)) {
    12. System.out.println("服务器已经启动...");
    13. while(true) {
    14. Socket client = server.accept();
    15. new Thread(new ClientHandler(client)).start();
    16. }
    17. } catch (IOException e) {
    18. e.printStackTrace();
    19. }
    20. }
    21. private static class ClientHandler implements Runnable {
    22. private Socket client;
    23. public ClientHandler(Socket client) {
    24. this.client = client;
    25. }
    26. @Override
    27. public void run() {
    28. try(BufferedReader br = new BufferedReader(new
    29. InputStreamReader(client.getInputStream()));
    30. PrintWriter pw = new
    31. PrintWriter(client.getOutputStream())) {
    32. String msg = br.readLine();
    33. System.out.println("收到" + client.getInetAddress() + " 发送的: " + msg);
    34. pw.println(msg);
    35. pw.flush();
    36. } catch(Exception ex) {
    37. ex.printStackTrace();
    38. } finally {
    39. try {
    40. client.close();
    41. } catch (IOException e) {
    42. e.printStackTrace();
    43. }
    44. }
    45. }
    46. }
    47. }

    注意:上面的代码使用了 Java 7 的 TWR 语法,由于很多外部资源类都间接的实现了 AutoCloseable 接口(单方法回调接口),因此可以利用 TWR 语法在 try结束的时候通过回调的方式自动调用外部资源类的 close()方法,避免书写冗长的finally 代码块。此外,上面的代码用一个静态内部类实现线程的功能,使用多线程可以避免一个用户 I/O 操作所产生的中断影响其他用户对服务器的访问,简单的说就是一个用户的输入操作不会造成其他用户的阻塞。当然,上面的代码使用线程池可以获得更好的性能,因为频繁的创建和销毁线程所造成的开销也是不可忽视的。

    下面是一段回显客户端测试代码:

    1. import java.io.BufferedReader;
    2. import java.io.InputStreamReader;
    3. import java.io.PrintWriter;
    4. import java.net.Socket;
    5. import java.util.Scanner;
    6. public class EchoClient {
    7. public static void main(String[] args) throws Exception {
    8. Socket client = new Socket("localhost", 6789);
    9. Scanner sc = new Scanner(System.in);
    10. System.out.print("请输入内容: ");
    11. String msg = sc.nextLine();
    12. sc.close();
    13. PrintWriter pw = new PrintWriter(client.getOutputStream());
    14. pw.println(msg);
    15. pw.flush();
    16. BufferedReader br = new BufferedReader(new
    17. InputStreamReader(client.getInputStream()));
    18. System.out.println(br.readLine());
    19. client.close();
    20. }
    21. }

    如果希望用 NIO 的多路复用套接字实现服务器,代码如下所示。NIO 的操作虽然带来了更好的性能,但是有些操作是比较底层的,对于初学者来说还是有些难于理解。

    1. import java.io.IOException;
    2. import java.net.InetSocketAddress;
    3. import java.nio.ByteBuffer;
    4. import java.nio.CharBuffer;
    5. import java.nio.channels.SelectionKey;
    6. import java.nio.channels.Selector;
    7. import java.nio.channels.ServerSocketChannel;
    8. import java.nio.channels.SocketChannel;
    9. import java.util.Iterator;
    10. public class EchoServerNIO {
    11. private static final int ECHO_SERVER_PORT = 6789;
    12. private static final int ECHO_SERVER_TIMEOUT = 5000;
    13. private static final int BUFFER_SIZE = 1024;
    14. private static ServerSocketChannel serverChannel = null;
    15. private static Selector selector = null;// 多路复用选择器
    16. private static ByteBuffer buffer = null;// 缓冲区
    17. public static void main(String[] args) {
    18. init();
    19. listen();
    20. }
    21. private static void init() {
    22. try {
    23. serverChannel = ServerSocketChannel.open();
    24. buffer = ByteBuffer.allocate(BUFFER_SIZE);
    25. serverChannel.socket().bind(new
    26. InetSocketAddress(ECHO_SERVER_PORT));
    27. serverChannel.configureBlocking(false);
    28. selector = Selector.open();
    29. serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    30. } catch (Exception e) {
    31. throw new RuntimeException(e);
    32. }
    33. }
    34. private static void listen() {
    35. while (true) {
    36. try {
    37. if (selector.select(ECHO_SERVER_TIMEOUT) != 0) {
    38. Iterator it =
    39. selector.selectedKeys().iterator();
    40. while (it.hasNext()) {
    41. SelectionKey key = it.next();
    42. it.remove();
    43. handleKey(key);
    44. }
    45. }
    46. } catch (Exception e) {
    47. e.printStackTrace();
    48. }
    49. }
    50. }
    51. private static void handleKey(SelectionKey key) throws IOException {
    52. SocketChannel channel = null;
    53. try {
    54. if (key.isAcceptable()) {
    55. ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
    56. channel = serverChannel.accept();
    57. channel.configureBlocking(false);
    58. channel.register(selector, SelectionKey.OP_READ);
    59. } else if (key.isReadable()) {
    60. channel = (SocketChannel) key.channel();
    61. buffer.clear();
    62. if (channel.read(buffer) > 0) {
    63. buffer.flip();
    64. CharBuffer charBuffer =
    65. CharsetHelper.decode(buffer);
    66. String msg = charBuffer.toString();
    67. System.out.println("收到" + channel.getRemoteAddress() + "的消息:" + msg);
    68. channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));
    69. } else {
    70. channel.close();
    71. }
    72. }
    73. } catch (Exception e) {
    74. e.printStackTrace();
    75. if (channel != null) {
    76. channel.close();
    77. }
    78. }
    79. }
    80. }
    1. import java.nio.ByteBuffer;
    2. import java.nio.CharBuffer;
    3. import java.nio.charset.CharacterCodingException;
    4. import java.nio.charset.Charset;
    5. import java.nio.charset.CharsetDecoder;
    6. import java.nio.charset.CharsetEncoder;
    7. public final class CharsetHelper {
    8. private static final String UTF_8 = "UTF-8";
    9. private static CharsetEncoder encoder =
    10. Charset.forName(UTF_8).newEncoder();
    11. private static CharsetDecoder decoder =
    12. Charset.forName(UTF_8).newDecoder();
    13. private CharsetHelper() {
    14. }
    15. public static ByteBuffer encode(CharBuffer in) throws
    16. CharacterCodingException{
    17. return encoder.encode(in);
    18. }
    19. public static CharBuffer decode(ByteBuffer in) throws
    20. CharacterCodingException{
    21. return decoder.decode(in);
    22. }
    23. }

    73、XML 文档定义有几种形式?它们之间有何本质区别?解析XML 文档有哪几种方式?

            XML 文档定义分为 DTD 和 Schema 两种形式,二者都是对 XML 语法的约束,其本质区别在于 Schema 本身也是一个 XML 文件,可以被 XML 解析器解析,而且可以为 XML 承载的数据定义类型,约束能力较之 DTD 更强大。对 XML 的解析主要有 DOM(文档对象模型,Document Object Model)、SAX(Simple API for XML)和 StAX(Java 6 中引入的新的解析 XML 的方式,Streaming API for XML),其中 DOM 处理大型文件时其性能下降的非常厉害,这个问题是由 DOM 树结构占用的内存较多造成的,而且 DOM 解析方式必须在解析文件之前把整个文档装入内存,适合对 XML 的随机访问(典型的用空间换取时间的策略);SAX 是事件驱动型的 XML 解析方式,它顺序读取 XML 文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过事件回调代码来处理 XML 文件,适合对 XML 的顺序访问;顾名思义,StAX 把重点放在流上,实际上 StAX 与其他解析方式的本质区别就在于应用程序能够把 XML 作为一个事件流来处理。将 XML 作为一组事件来处理的想法并不新颖(SAX 就是这样做的),但不同之处在于 StAX 允许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。


    74、你在项目中哪些地方用到了 XML?

            XML 的主要作用有两个方面:数据交换和信息配置。

            在做数据交换时,XML 将数据用标签组装成起来,然后压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再从 XML 文件中还原相关信息进行处理,XML 曾经是异构系统间交换数据的事实标准,但此项功能几乎已经被 JSON(JavaScript Object Notation)取而代之。当然,目前很多软件仍然使用 XML 来存储配置信息,我们在很多项目中通常也会将作为配置信息的硬代码写在 XML 文件中,Java 的很多框架也是这么做的,而且这些框架都选择了 dom4j 作为处理 XML 的工具,因为 Sun 公司的官方API 实在不怎么好用。

    补充:现在有很多时髦的软件(如 Sublime)已经开始将配置文件书写成 JSON格式,我们已经强烈的感受到 XML 的另一项功能也将逐渐被业界抛弃。


    要想了解更多:

    千题千解·Java面试宝典_时光の尘的博客-CSDN博客

  • 相关阅读:
    Java总结String类
    如何将当前文件夹里的所有python文件打包到镜像里面
    浅谈安科瑞ADL系列导轨式多功能仪表在迪拜楼宇EMS中的应用
    redis-cli报错Could not connect to Redis at 127.0.0.1:6379: Connection refused
    Airtext连接chrome谷歌浏览器报错
    C#上位机系列(1)—项目的建立
    微信小程序如何使用地球半径计算两组经纬度点之间的距离(自身位置与接口返回位置)【上】
    目标检测YOLO实战应用案例100讲-基于YOLOv5的航拍图像旋转目标检测
    238.除自身以外数组的乘积
    Java项目使用自定义的公共单元(Maven管理)
  • 原文地址:https://blog.csdn.net/MANONGDKY/article/details/133901291