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

ops/2025/2/5 13:21:49/

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

今日分享 3 道面试题目!

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

目录

  • 问题一:Java Object 类中有什么方法,有什么作用?
  • 问题二:Java 中的字节码是什么?
  • 问题三:什么是 BIO、NIO、AIO?

问题一:Java Object 类中有什么方法,有什么作用?

Java Object 类的方法及作用

Object 类是 Java 所有类的祖先,位于 java.lang.Object,它为所有 Java 对象提供了一些基本的方法。


1. Object 类的主要方法

(1)equals(Object obj)—比较对象是否相等

作用:比较当前对象与参数对象是否 " 逻辑相等 "。

默认实现(在 Object 类中):

  • 使用 == 比较,即比较对象的内存地址,而不是内容。
  • 如果需要比较对象内容,需要重写 equals() 方法(如 StringInteger 等类已重写)。

示例:

java">class Person {String name;Person(String name) { this.name = name; }@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;return name.equals(person.name);}
}Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true(重写后,比较内容)

(2)hashCode()—获取对象的哈希值

作用:返回对象的哈希码,用于哈希表(如 HashMapHashSet)存储对象时计算索引。

  • 默认实现:基于对象内存地址计算哈希值。
  • 通常与 equals() 一起重写,保证相等的对象哈希值相同(否则 HashSet 等容器可能无法正确存储)。

示例:

java">@Override
public int hashCode() {return name.hashCode();  // 让相同 name 的对象 hashCode 也相同
}

(3)toString()—返回对象的字符串表示

作用:返回对象的字符串描述,默认格式为 类名@哈希码

  • 建议重写,以便更清晰地输出对象信息。

示例:

java">class Person {String name;Person(String name) { this.name = name; }@Overridepublic String toString() {return "Person{name='" + name + "'}";}
}Person p = new Person("Alice");
System.out.println(p);  // Person{name='Alice'}

(4)getClass()—获取对象的 Class 对象

作用:返回对象的运行时 Class 对象,可以用于反射。

示例:

java">Person p = new Person("Alice");
System.out.println(p.getClass().getName());  // 输出类的全限定名

(5)clone()—复制对象

作用:返回对象的浅拷贝(仅拷贝基本类型,引用类型不会复制对象)。

  • 默认 Object 实现浅拷贝,必须实现 Cloneable 接口并重写 clone() 方法。

示例:

java">class Person implements Cloneable {String name;Person(String name) { this.name = name; }@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}Person p1 = new Person("Alice");
Person p2 = (Person) p1.clone();
System.out.println(p1 == p2);  // false,不是同一个对象

(6)finalize()—对象被回收前的操作

作用:对象被垃圾回收前执行的清理方法(通常不推荐使用)。

  • 被废弃(JDK 9 开始标记为 deprecated,可以用 try-with-resourcesclose() 代替。

示例:

java">@Override
protected void finalize() throws Throwable {System.out.println("对象即将被回收");
}

(7)wait() / notify() / notifyAll()—线程通信

这些方法用于线程同步,必须在同步代码块中调用。

  • wait():让当前线程进入等待状态,直到被 notify() 唤醒。
  • notify():随机唤醒一个等待的线程。
  • notifyAll():唤醒所有等待的线程。

示例:

java">synchronized (this) {this.wait();  // 让当前线程等待this.notify();  // 唤醒一个等待的线程
}

2. 总结

方法作用是否需要重写
equals(Object obj)比较对象是否相等(默认比较地址)通常需要
hashCode()计算对象哈希值(用于 HashMap)通常需要
toString()返回对象字符串信息通常需要
getClass()获取对象的 Class 信息一般不重写
clone()复制对象(默认浅拷贝)按需重写
finalize()对象回收前执行(已废弃)不建议使用
wait() / notify() / notifyAll()线程通信不可重写

重点

  1. equals()hashCode() 一起重写,确保 HashMap、HashSet 正常存储对象。
  2. toString() 适当重写,方便调试和日志输出。
  3. clone() 需要实现 Cloneable 接口,并自己实现深拷贝。
  4. finalize() 已过时,不推荐使用。
  5. wait() / notify() 用于线程同步,必须在 synchronized 代码块中调用。

这就是 Object 类的方法及其作用!


问题二:Java 中的字节码是什么?

Java 中的字节码(Bytecode)

1. 什么是字节码?

Java 字节码(Bytecode) 是一种 中间表示(Intermediate Representation,IR),它不是直接运行在 CPU 上的机器码,而是运行在 Java 虚拟机(JVM) 上的指令集。

当我们编写 Java 代码并编译(javac)后,Java 源代码(.java 文件)会被编译器转换成 字节码文件(.class 文件),然后由 JVM 解释或 JIT 编译后执行。


2. Java 字节码的特点
  • 平台无关性:Java 字节码可以在任何安装了 JVM 的环境中运行,而不需要重新编译。
  • 面向对象:字节码仍然保留了 Java 的对象模型,如类、方法、继承、接口等。
  • 堆栈指令集(Stack-based Instruction Set):JVM 采用基于操作数栈的架构(而非寄存器架构),所有操作通过入栈、出栈完成。

3. 字节码执行流程
  1. 编译:Java 源代码(.java)→ 编译器(javac → 字节码(.class
  2. 加载:JVM 通过类加载器(ClassLoader) 加载 .class 文件到方法区。
  3. 解释或编译
    • 解释执行:字节码由 解释器(Interpreter) 逐条翻译成机器码并执行(启动快但慢)。
    • JIT(即时编译,Just-In-Time Compiler):JVM 可能将热点代码转换为机器码,提高执行效率。

4. Java 字节码示例

例如,编译以下代码:

java">public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, Bytecode!");}
}

使用 javap -c HelloWorld.class 反编译:

public static void main(java.lang.String[]);Code:0: getstatic     #2 // 获取 System.out3: ldc           #3 // 加载字符串 "Hello, Bytecode!"5: invokevirtual #4 // 调用 println 方法8: return
  • getstatic:获取 System.out
  • ldc:加载字符串常量
  • invokevirtual:调用 println() 方法

5. 字节码优化

JVM 使用 JIT(Just-In-Time 编译)垃圾回收(GC) 进行优化:

  • 热点代码 会被 JIT 编译成机器码,提高运行效率。
  • 死代码消除、方法内联 等优化提升性能。

6. 总结
特点说明
平台无关编译后可运行在任意 JVM 上
基于栈架构通过栈操作指令执行计算
JIT 编译优化热点代码会被 JIT 转换为机器码,提高性能
面向对象支持类、方法、继承等

Java 字节码是 Java 跨平台特性的关键,也是 JVM 执行 Java 代码的中间步骤!🚀


问题三:什么是 BIO、NIO、AIO?

BIO、NIO、AIO 的概念与区别

在 Java 中,BIO(Blocking I/O)、NIO(Non-blocking I/O)、AIO(Asynchronous I/O) 是三种不同的 I/O(输入/输出)模型,主要用于网络编程文件操作。它们的核心区别在于 数据读写时的阻塞行为线程管理方式


1. BIO(Blocking I/O)—阻塞式 I/O

概念

  • 同步 & 阻塞:每个连接都需要一个独立的线程处理,数据读取/写入时会阻塞,直到操作完成。
  • 适用于小规模连接(如传统 C/S 架构),如 Web 服务器、数据库连接等。
  • 缺点
    • 线程资源消耗大,每个连接都需要一个线程,连接数过多时,系统负担重。
    • 低并发,大量连接时,线程切换成本高,容易导致 " 线程过载 "

示例

java">ServerSocket server = new ServerSocket(8080);
while (true) {Socket socket = server.accept(); // 阻塞等待连接new Thread(() -> {try {InputStream in = socket.getInputStream();byte[] buffer = new byte[1024];int len = in.read(buffer); // 阻塞等待数据System.out.println(new String(buffer, 0, len));} catch (IOException e) {e.printStackTrace();}}).start();
}
  • server.accept() 阻塞等待客户端连接。
  • in.read(buffer) 阻塞等待数据到来。
  • 每个请求都创建新线程,连接数多时消耗大。

2. NIO(Non-blocking I/O)—非阻塞式 I/O

概念

  • 同步 & 非阻塞:采用 多路复用(Selector) 机制,一个线程可以管理多个连接,提升并发性能。
  • 适用于高并发场景(如聊天系统、游戏服务器)
  • 特点
    • 非阻塞,但仍是同步 I/O(线程需要主动轮询检查数据)。
    • 采用 Selector 监听多个 Channel,避免一个线程对应一个连接的弊端。

示例

java">Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 设置非阻塞模式
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 监听连接事件while (true) {selector.select(); // 轮询监听事件(阻塞)Set<SelectionKey> keys = selector.selectedKeys();for (SelectionKey key : keys) {if (key.isAcceptable()) { // 处理新连接SocketChannel socketChannel = serverChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) { // 处理可读数据SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);System.out.println(new String(buffer.array()));}}keys.clear();
}
  • Selector 监听多个 Channel,避免多个线程处理多个连接。
  • 数据读取非阻塞,如果没有数据,不会卡住线程。
  • 适用于高并发服务器(如 Netty)

3. AIO(Asynchronous I/O)— 异步 I/O

概念

  • 异步 & 非阻塞:基于 操作系统内核 异步通知机制,I/O 操作完成后会回调相应的处理逻辑,不需要手动轮询检查。
  • 适用于超高并发(如高吞吐量服务器、消息推送)
  • 特点
    • 真正的异步,数据准备好后,操作系统会自动回调处理数据,避免轮询。
    • 比 NIO 更高效,但依赖于操作系统的异步 I/O 支持(Windows、Linux 支持良好)。

示例

java">AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel channel, Void attachment) {ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer buffer) {buffer.flip();System.out.println(new String(buffer.array()));}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}
});
  • server.accept() 是非阻塞的,连接建立后自动执行回调 completed()
  • channel.read() 也是异步的,数据到达后触发 completed(),无需轮询检查。
  • 适用于超高并发,如 Netty 也有 AIO 支持。

4. BIO、NIO、AIO 区别总结

特性BIO(同步阻塞)NIO(同步非阻塞)AIO(异步非阻塞)
线程模型一个连接占用一个线程一个线程管理多个连接真正的异步,I/O 完成后回调
阻塞方式阻塞(线程等待)非阻塞(轮询检查)非阻塞(OS 直接回调)
并发能力低(线程数多时资源耗尽)中等(Selector 机制)高(事件驱动,线程数少)
适用场景小规模连接(如传统 Web)高并发(如聊天室、消息推送)超高并发(如 Netty、分布式系统)
JDK 支持JDK 1.0JDK 1.4(java.nioJDK 1.7(java.nio.channels

5. 何时使用 BIO、NIO、AIO?

  1. BIO

    • 适用于连接数较少并发要求不高的应用(如 Web 服务器、小型服务端)。
    • 优点:实现简单,调试方便。
    • 缺点:高并发下资源消耗大,性能低
  2. NIO

    • 适用于高并发场景,如聊天室、游戏服务器、即时通讯
    • 优点:支持单线程管理多个连接,性能比 BIO 高。
    • 缺点:代码复杂度增加,需要手动管理 SelectorChannel
  3. AIO

    • 适用于超高并发,如Netty、分布式架构、消息推送
    • 优点:完全异步,系统资源占用少,适合高吞吐场景。
    • 缺点:依赖操作系统支持,适用范围比 NIO 更局限。

如果是高并发网络编程,推荐使用 Netty(基于 NIO 和 AIO 的封装),性能更强!🚀


总结

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

明天见!🎉


http://www.ppmy.cn/ops/155878.html

相关文章

lstm代码解析1.2

在使用 LSTM&#xff08;长短期记忆网络&#xff09;进行训练时&#xff0c;model.fit 方法的输入数据 X 和目标数据 y 的形状要求是不同的。具体来说&#xff1a; 1. 输入数据 X 的形状 LSTM 层期望输入数据 X 是三维张量&#xff0c;形状为 (samples, timesteps, features)…

leetcode——二叉树展开为链表(java)

给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同。 示例 1&#…

如何学习Java后端开发

文章目录 一、Java 语言基础二、数据库与持久层三、Web 开发基础四、主流框架与生态五、分布式与高并发六、运维与部署七、项目实战八、持续学习与提升总结路线图 学习 Java 后端开发需要系统性地掌握多个技术领域&#xff0c;从基础到进阶逐步深入。以下是一个详细的学习路线和…

【JavaEE】Spring(6):Mybatis(下)

一、Mybatis XML配置文件 Mybatis开发有两种方式&#xff1a; 注解XML 之前讲解了注解的方式&#xff0c;接下来学习XML的方式 1.1 配置数据库连接和Mybatis 直接在配置文件中配置即可&#xff1a; spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?cha…

《苍穹外卖》项目学习记录-Day7缓存菜品

我们优先去读取缓存数据&#xff0c;如果有就直接使用&#xff0c;如果没有再去查询数据库&#xff0c;查出来之后再放到缓存里去。 微信小程序根据分类来展示菜品&#xff0c;所以每一个分类下边的菜品对应的就是一份缓存数据&#xff0c;这样的话当我们使用这个数据的时候&am…

RESTful API的设计原则与这些原则在Java中的应用

RESTful API 是基于 REST&#xff08;Representational State Transfer&#xff09; 架构风格设计的 API&#xff0c;其核心目标是提高系统的可伸缩性、简洁性和可维护性。以下是 RESTful API 的设计原则及在 Java 中的实现方法&#xff1a; 一、RESTful API 的核心设计原则 客…

98,【6】 buuctf web [ISITDTU 2019]EasyPHP

进入靶场 代码 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;通常用于调试或展示代码&#xff0c;方便用户查看代码逻辑 highlight_file(__FILE__);// 从 GET 请求中获取名为 _ 的参数值&#xff0c;并赋值给变量 $_ // 符号用于抑制可能出现的错误信息&#xff…

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式&#xff0c;由于跨域资源共享(cors)是浏览器的一种安全机制&#xff0c;它会阻止前端应用…