每日 Java 面试题分享【第 14 天】

欢迎来到每日 Java 面试题分享栏目!
订阅专栏,不错过每一天的练习

今日分享 3 道面试题目!

评论区复述一遍印象更深刻噢~

目录

  • 问题一:Java 的 Optional 类是什么?它有什么用?
  • 问题二:Java 的 IO 流是什么?
  • 问题三:什么是 Java 的网络编程?

问题一:Java 的 Optional 类是什么?它有什么用?

Optional 是 Java 8 引入的一种容器类,位于 java.util 包中,用于表示可能包含或不包含非空值的对象。它旨在通过优雅的方式解决**NullPointerException** 问题,使代码更安全和简洁。


Optional 的作用

  1. 避免显式的 null 检查

    • 通过提供工具方法处理值是否存在,减少显式的 if-else 语句。
  2. 提升代码可读性

    • 代码逻辑更清晰,意图更明确。
  3. 防止出现 NullPointerException

    • 避免直接操作可能为 null 的对象。
  4. 帮助函数式编程

    • 提供方法链式调用,适合在流式处理和函数式编程中使用。

Optional 的核心方法

以下是 Optional 类的常用方法及其功能:

方法 描述
of(T value) 创建一个包含非空值的 Optional。若传入 null 会抛出 NullPointerException
ofNullable(T value) 创建一个可能为空的 Optional(允许传入 null)。
empty() 创建一个空的 Optional 实例。
isPresent() 判断是否包含值(是否为非空)。
isEmpty()(Java 11 引入) 判断是否为空(是否为 null)。
get() 获取值,若值为空则抛出 NoSuchElementException
orElse(T other) 值存在则返回值,否则返回默认值。
orElseGet(Supplier<? extends T>) 值存在则返回值,否则通过 Supplier 提供默认值。
orElseThrow(Supplier<Throwable>) 值存在则返回值,否则抛出由 Supplier 提供的异常。
map(Function<? super T,? extends U>) 如果值存在,应用函数并返回新的 Optional,否则返回空的 Optional
flatMap(Function<? super T,Optional<U>>) 类似 map(),但要求返回值必须是 Optional
filter(Predicate<? super T>) 如果值满足谓词条件,则返回原值,否则返回空的 Optional

Optional 的使用示例

  1. 避免显式 null 检查

    java 复制代码
    import java.util.Optional;
    
    public class OptionalExample {
        public static void main(String[] args) {
            String value = "Hello, Optional!";
            Optional<String> optional = Optional.ofNullable(value);
    
            // 不用显式 null 检查
            String result = optional.orElse("Default Value");
            System.out.println(result); // 输出 "Hello, Optional!"
        }
    }
  2. 结合 map()filter()

    java 复制代码
    public class OptionalExample {
        public static void main(String[] args) {
            Optional<String> optional = Optional.ofNullable("Hello");
    
            optional.filter(val -> val.startsWith("H"))
                    .map(String::toUpperCase)
                    .ifPresent(System.out::println); // 输出 "HELLO"
        }
    }
  3. 处理可能为 null 的返回值

    java 复制代码
    public class OptionalExample {
        public static void main(String[] args) {
            Optional<String> optional = getNullableValue();
    
            // 如果值存在则输出,否则输出默认值
            System.out.println(optional.orElse("Default Value"));
        }
    
        private static Optional<String> getNullableValue() {
            return Optional.ofNullable(null); // 模拟可能为 null 的返回值
        }
    }
  4. 避免 NoSuchElementException

    java 复制代码
    public class OptionalExample {
        public static void main(String[] args) {
            Optional<String> optional = Optional.empty();
    
            // 避免直接调用 get() 导致异常
            System.out.println(optional.orElse("No Value")); // 输出 "No Value"
        }
    }

Optional 的注意事项

  1. 不要滥用 Optional

    • Optional 是用来处理返回值的工具,不应将它用于类字段序列化对象
    • 如果值必定非空,直接使用普通对象即可;若值可能为空,首选 Optional
  2. 避免调用 get()

    • get() 方法在值为空时会抛出异常,应尽量使用 orElse()orElseThrow()ifPresent() 等安全方法。
  3. 性能注意

    • Optional 本质上是一个封装对象的容器,可能有少量性能开销,不适合在性能要求较高的场景中广泛使用。

Optional 的实际应用场景

  1. 避免 null 返回值

    • 对于 DAO(数据访问对象)方法、服务层方法的返回值,避免直接返回 null
  2. 处理方法链

    • 在链式调用中,避免 null 导致链条断裂。
  3. 替代传统的 null 检查逻辑

    • 简化代码,提高可读性。

扩展:Optional 的意义与缺陷

意义
  • Optional 是 Java 语言对 空值安全处理 的一次尝试。
  • 它对函数式编程的支持非常友好,特别是在流式操作和数据处理时。
缺陷
  • Optional 并未完全取代 null,尤其是在复杂业务场景中,null 的问题依然存在。
  • 滥用 Optional 可能导致代码冗长,并增加不必要的开销。

总结

  • Optional 是一个容器类 ,用于优雅处理值的缺失,避免 NullPointerException
  • 它提供了许多工具方法,如 map()filter()orElse(),方便处理可能为空的场景。
  • 使用原则
    • 方法返回值中建议使用 Optional 代替直接返回 null
    • 避免将 Optional 用于类字段或方法参数。

问题二:Java 的 IO 流是什么?

Java 的 IO 流 是指 Java 提供的用于处理数据输入和输出的工具类和接口。它们位于 java.io 包中,支持文件、网络、内存缓冲区等数据源之间的数据读写操作。

IO 流分为 输入流 (Input Stream)和 输出流(Output Stream),从方向上看:

  • 输入流:从数据源读取数据到程序中。
  • 输出流:从程序输出数据到目标位置。

IO 流的分类

1. 按数据处理方式分类
  • 字节流 :用于处理 二进制数据 ,操作单位是 字节8-bit)。
    • 输入流:InputStream 及其子类。
    • 输出流:OutputStream 及其子类。
  • 字符流 :用于处理 文本数据 ,操作单位是 字符16-bit Unicode)。
    • 输入流:Reader 及其子类。
    • 输出流:Writer 及其子类。
2. 按流的方向分类
  • 输入流:用于从数据源读取数据。
  • 输出流:用于向目标输出数据。
3. 按功能分类
  • 节点流 :直接操作数据源(如文件、内存、网络等),例如 FileInputStreamFileReader
  • 处理流 :对节点流进行功能增强,主要用于数据的转换、缓存等操作,例如 BufferedInputStreamBufferedReader

IO 流的体系结构

Java 的 IO 流可以归纳为以下继承体系:

1. 字节流
  • 抽象基类:
    • 输入流:InputStream
    • 输出流:OutputStream
  • 常见实现类:
    • 文件流:FileInputStreamFileOutputStream
    • 缓冲流:BufferedInputStreamBufferedOutputStream
    • 对象流:ObjectInputStreamObjectOutputStream
    • 管道流:PipedInputStreamPipedOutputStream
2. 字符流
  • 抽象基类:
    • 输入流:Reader
    • 输出流:Writer
  • 常见实现类:
    • 文件流:FileReaderFileWriter
    • 缓冲流:BufferedReaderBufferedWriter
    • 字符数组流:CharArrayReaderCharArrayWriter
    • 输入转换流:InputStreamReader
    • 输出转换流:OutputStreamWriter

IO 流的常用类与示例

1. 字节流操作文件
java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        String inputPath = "input.txt";
        String outputPath = "output.txt";

        try (FileInputStream fis = new FileInputStream(inputPath);
             FileOutputStream fos = new FileOutputStream(outputPath)) {

            int data;
            while ((data = fis.read()) != -1) {
                fos.write(data);
            }
            System.out.println("文件复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2. 字符流读取文件
java 复制代码
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamExample {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
3. 序列化与反序列化(对象流)
java 复制代码
import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class ObjectStreamExample {
    public static void main(String[] args) {
        String filePath = "person.ser";

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {
            Person person = new Person("Alice", 25);
            oos.writeObject(person);
            System.out.println("对象已序列化!");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {
            Person person = (Person) ois.readObject();
            System.out.println("反序列化对象:" + person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

IO 流的常见问题

  1. 为什么需要缓冲流?

    • 缓冲流(如 BufferedReaderBufferedInputStream)通过内置缓存提高读写效率,减少对底层数据源的访问次数。
  2. close()try-with-resources 的区别

    • close() 手动关闭流,易遗忘或处理不当。
    • try-with-resources 是自动关闭资源的语法糖,更加安全和推荐。
  3. 字符流与字节流的区别

    • 字符流适合处理文本文件,支持字符编码转换。
    • 字节流更通用,可处理所有类型的文件,包括图片、视频、音频等。

扩展:NIO 和 IO 的区别

Java 还提供了 NIO(New IO),是 IO 的改进版本。两者的核心区别如下:

特性 IO(传统 IO) NIO(新 IO)
阻塞模型 默认是阻塞 IO 非阻塞 IO 支持
缓冲方式 面向流(Stream-Oriented) 面向缓冲区(Buffer-Oriented)
多路复用 不支持 支持(Selector 实现多路复用)
性能 较低,适合简单 IO 操作 高性能,适合高并发和大文件传输场景

总结

  • Java IO 流是用于处理数据输入和输出的重要工具,分为 字节流字符流
  • 常见类如 FileInputStreamBufferedReaderObjectOutputStream 等,适合不同场景。
  • 使用 IO 流时应注意资源管理和性能优化,例如使用缓冲流和 try-with-resources
  • 在性能和并发需求较高的场景中,可以选择 NIO 来替代传统 IO。

问题三:什么是 Java 的网络编程?

Java 的网络编程是指使用 Java 提供的网络库和 API,通过网络协议(如 TCP/IP、UDP 等)实现计算机之间的通信。Java 的网络编程基于 Socket(套接字)模型,提供了面向流的高层封装,开发者可以通过 Java 程序发送和接收数据,构建客户端与服务器之间的通信。


Java 网络编程的核心概念

1. Socket(套接字)
  • 套接字是网络编程的核心,用于在通信双方之间建立连接。
  • 它可以理解为通信双方的一个端点。
  • Socket 分为:
    • TCP 套接字 :提供可靠的点对点连接,使用流传输(SocketServerSocket)。
    • UDP 套接字 :提供无连接的数据报传输(DatagramSocketDatagramPacket)。
2. IP 地址
  • 标识网络中的设备位置(如 192.168.1.1)。
  • Java 提供了 InetAddress 类,用于获取 IP 地址和主机名。
3. 端口
  • 标识设备中的通信程序(范围 0~65535)。
  • 常见端口:HTTP(80)、HTTPS(443)、FTP(21)等。
4. 协议
  • TCP(Transmission Control Protocol)
    • 面向连接,数据传输可靠,顺序无误。
    • 适合需要保证数据完整性的场景,如文件传输、Web 服务。
  • UDP(User Datagram Protocol)
    • 无连接,数据包可能丢失,但速度快。
    • 适合实时性要求高的场景,如视频流、在线游戏。

Java 网络编程的常用类

  1. TCP 通信

    • 客户端:Socket
    • 服务器端:ServerSocket
  2. UDP 通信

    • 套接字:DatagramSocket
    • 数据包:DatagramPacket
  3. IP 地址操作

    • InetAddress

Java 网络编程示例

1. TCP 通信示例

服务端:

java 复制代码
import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) {
        int port = 8080;
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("服务器已启动,等待客户端连接...");

            // 等待客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("客户端已连接:" + socket.getInetAddress());

            // 接收客户端发送的数据
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String message = reader.readLine();
            System.out.println("收到客户端消息:" + message);

            // 向客户端发送响应
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            writer.println("消息已收到:" + message);

            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端:

java 复制代码
import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) {
        String host = "localhost";
        int port = 8080;

        try (Socket socket = new Socket(host, port)) {
            System.out.println("已连接到服务器:" + socket.getInetAddress());

            // 向服务器发送数据
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            writer.println("你好,服务器!");

            // 接收服务器的响应
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response = reader.readLine();
            System.out.println("收到服务器消息:" + response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2. UDP 通信示例

服务端:

java 复制代码
import java.net.*;

public class UDPServer {
    public static void main(String[] args) {
        int port = 8080;
        try (DatagramSocket serverSocket = new DatagramSocket(port)) {
            System.out.println("UDP 服务器已启动,等待数据...");

            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            // 接收数据
            serverSocket.receive(packet);
            String message = new String(packet.getData(), 0, packet.getLength());
            System.out.println("收到客户端消息:" + message);

            // 发送响应
            String response = "消息已收到:" + message;
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, packet.getAddress(), packet.getPort());
            serverSocket.send(responsePacket);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端:

java 复制代码
import java.net.*;

public class UDPClient {
    public static void main(String[] args) {
        String host = "localhost";
        int port = 8080;

        try (DatagramSocket clientSocket = new DatagramSocket()) {
            String message = "你好,服务器!";
            byte[] buffer = message.getBytes();
            InetAddress address = InetAddress.getByName(host);

            // 发送数据
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, port);
            clientSocket.send(packet);

            // 接收响应
            byte[] responseBuffer = new byte[1024];
            DatagramPacket responsePacket = new DatagramPacket(responseBuffer, responseBuffer.length);
            clientSocket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.println("收到服务器消息:" + response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

扩展:Java 网络编程的特点

  1. 平台无关性

    • Java 提供的网络 API 封装了底层操作,无需关心操作系统差异。
  2. 高层封装

    • Java 提供了简单易用的类(如 SocketServerSocket),隐藏了复杂的网络通信细节。
  3. 多线程支持

    • Java 网络编程可以结合多线程实现高效的并发通信(如处理多个客户端的请求)。

常见问题与优化建议

  1. 问题:客户端与服务器连接失败

    • 检查主机地址和端口是否正确。
    • 防火墙可能阻止了连接。
  2. 问题:TCP 的连接未正常关闭

    • 解决方案:使用 try-with-resources 自动关闭资源。
  3. 优化:高并发场景

    • 传统 IO 的阻塞模型在高并发场景下性能有限,可以考虑使用 NIONetty 等框架。

总结

  • Java 的网络编程通过 Socket 提供了高层封装,支持 TCP 和 UDP 通信。
  • 常用类包括 SocketServerSocketDatagramSocketInetAddress
  • 在开发时应注意资源管理、多线程支持以及高性能需求,选择合适的通信模型(如阻塞或非阻塞)。

总结

今天的 3 道 Java 面试题,您是否掌握了呢?持续关注我们的每日分享,深入学习 Java 面试的各个细节,快速提升技术能力!如果有任何疑问,欢迎在评论区留言,我们会第一时间解答!

明天见!🎉

相关推荐
小园子的小菜1 分钟前
深入探究 RocketMQ 中的 Broker2Client 组件
java·rocketmq·java-rocketmq
我命由我123452 分钟前
C++ - 头文件基础(常用标准库头文件、自定义头文件、头文件引入方式、防止头文件重复包含机制)
服务器·c语言·开发语言·c++·后端·visualstudio·visual studio code
bing_15811 分钟前
Mybatis 如何自定义缓存?
java·缓存·mybatis
RainbowSea14 分钟前
8. RabbitMQ 消息队列 + 结合配合 Spring Boot 框架实现 “发布确认” 的功能
java·消息队列·rabbitmq
能来帮帮蒟蒻吗18 分钟前
GO语言学习(17)Gorm的数据库操作
开发语言·学习·golang
RainbowSea25 分钟前
7. RabbitMQ 消息队列——延时队列(Spring Boot + 安装message_exchange"延迟插件" 的详细配置说明)的详细讲解
java·消息队列·rabbitmq
Pacify_The_North44 分钟前
【C++进阶五】list深度剖析
开发语言·c++·算法·list
咖啡の猫1 小时前
JavaScript 简单类型与复杂类型
开发语言·javascript
XuanXu1 小时前
Java volatile关键字以及线程安全
java
都叫我大帅哥1 小时前
代码世界的「万能转接头」:适配器模式的跨界艺术
java·后端·设计模式