java面试常见的问题以及解决方法

java面试常见的问题以及解决方法 一. 网络抓包问题常用的网络抓包工具1. tcpdump最常用的命令行抓包工具功能强大可过滤各种协议。Linux、macOS2. Wireshark: 最强大、最常用的 GUI 抓包工具支持协议解析、图形化分析等。 Windows、Linux。3. Fiddler: 专注于 HTTP/HTTPS 抓包可解密 SSL非常适合 Web 接口调试。Windows。系统架构前端页面部署在浏览器或 nginx 上后端服务地址为192.168.1.100:8080前端请求一个接口http://192.168.1.100:8080/api/user/info问题描述前端控制台显示请求已经发送200 或 pending后端接口没有打日志、调试器没有命中、服务无响应tcptump命令进行抓包sudo tcpdump -i any port 8080 -n -X-i any监听所有网络接口port 8080只看目标端口 8080-n不进行 DNS 反查-X显示数据内容可看 URLtcpdump -i eth0 port 8080 # 只抓 8080 端口tcpdump -i eth0 tcp # 只抓 TCPtcpdump -i eth0 # 抓所有常用tcpdump命令选项-i [interface]指定监听的网络接口。host [IP]过滤与指定 IP 地址相关的流量。port [number]过滤指定端口的流量。-w [file]将捕获的数据包写入文件。-c [number]捕获指定数量的数据包后停止。-nn不将地址和端口转换为名称显示原始数字sudo tcpdump -i eth0 host 192.168.1.100 and port 80 -w /tmp/capture.pcap此命令的含义-i eth0指定监听的网络接口为 eth0。host 192.168.1.100过滤与该 IP 地址相关的流量。port 80过滤 HTTP 通信。-w /tmp/capture.pcap将捕获的数据包写入文件。tcpdump -s 0 -i eth0 port 22 -nn -tttt output.txt-s 0抓取数据包的完整内容头 body适合调试 HTTP/SSH 等有 payload 的协议-i eth0监听 eth0 接口port 22过滤 SSH 通信-nn不解析主机名和端口名-tttt显示精确时间戳 output.txt将抓包结果保存到文本文件中。二. 线上死锁问题排查数据库死锁代码死锁什么是死锁死锁表现程序不往下走了。并发场景下多个线程在竞争相同的资源。线程1要获取到资源需要持有A,B两把锁线程2要获取到资源需要持有B,A两把锁。线程1在持有A锁尝试去获取B锁线程2在持有B锁尝试去获取A锁。两个线程都持有对方需要的资源等待对方释放资源的过程称为死锁。解决方法1. 一个线程避免持有多把锁 2. 确保所有的线程按照相同的顺序获取锁避免等待。3. 使用超时机制线程在获取锁的过程中如果超时则放弃获取锁避免长期等待出现死锁。死锁定位的方法1. 通过jps命令找到所有的进程2. 通过top -H p pid n 1 : 生成对应进行下面所有线程cpu和内存使用情况的快照信息3. jstack -l pid ./file.txt生成对应的线程快照文件4. 在文件中搜索deadlock字段信息。找到全路径类名代码定位分析。三. 线上OOM问题排查现象用户在下载报表的过程中每隔几个星期出现下载报表失败现象。排查取下gc日志和后端日志查看日志发现在下载报表的过程中jvm在不断的进行full gc老年代内存不见较少反而不断地增加最终导致内存不足出现OOM现象。根据以往的经验可能有两点原因导致。1. JVM 初始化堆内存配置过小 2. 存在大对象未及时释放造成内存泄漏。第一先排查第一种情况java -XX:PrintCommandLineFlags -version命令发现初始化堆内存1g最大堆内存是4g。内存配置合理暂时排除配置问题。第二: 对象泄漏分析线上已预配置-XX:HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath在 OOM 发生时自动生成了.hprof堆转储文件。使用 VisualVM 工具打开该文件发现Workbook对象占用堆内存超过 50%存在明显内存泄漏。分析过程初步判断可能是这个大的对象用完以后没有被及时的释放导致的通过全路径类型定位到代码进行分析。发现workbook对象生成报表以后没有及时调用close()方法关闭资源导致。解决方法修改代码逻辑workbook对象生成报表以后及时调用close()方法关闭资源。然后从新打包替换生产的jar包从新启动程序。验证1. 通过jstat -gc pid 500(ms): 查看垃圾回收趋于正常。2. 报表下载一周没有出现失败的情况堆转储文件生成的方式两种1. 通过事先参数配置-XX:HeapDumpOnOutOfMemoryErrorXX:HeapDumpPath/opt/dumps非核心业务模块2. 通过命令生成jmap -dump pid生成 jps命令查看保证进程还在如果进程不在不能使用核心业务模块非高负载情况下面试官追问线上环境不能使用直接生成线上环境并不是绝对不能使用但要谨慎使用。优点OOM时候可以自动生成堆转储文件方便内存溢出问题的排查。缺点生成堆转储文件可能占用大量的内存导致程序卡顿或者雪崩。因此非核心模块开启自动生成。并且设置磁盘的告警和定期的清理核心业务模块不开启通过监控工具进行监控如 Prometheus Grafana提前预警。使用jmap -dump命令手动生成。四. JVM的调优的经验常见的调优参数-xms初始化堆内存大小 -xmx: 最大堆内存大小 -xss每个线程的栈堆大小。JVM参数可以在哪里设定JVM调优参数有哪些1. 堆空间的大小为了防止垃圾收集器在初始化大小和最大大小之间收缩堆空间造成时间的浪费一般把初始化和最大堆大小设置成一样的。如何设置堆空间1. 初始化堆大小物理内存的1/64 最大堆大小物理内存的1/42. 堆内存设置过小会导致年轻代和老年代不断地进行垃圾回收导致stw3. 堆内存设置过大在进行FULL GC的时候会扫描整个堆空间造成时间浪 费。stop the world: JVM在进行某些垃圾回收的时候会暂时停止应用线程的工作只让垃圾回收线程进行工作保证数据的安全性。详细步骤1默认情况下初始化堆内存为物理内存的1/64最大堆内存为物理内存的1/4。在进行调优的时候如果我们的服务对内存要求不需要特别精确可以遵循堆内存尽可能大的原则将初始化堆内存和最大堆内存设置成一样可以避免堆伸缩。2如果我们服务对内存的要求特别的精确我们可以给堆内存设置一个初始值然后启动服务通过Visual VM工具进行连接观察的。主要观察一下参数1. 堆内存的使用情况: 服务在运行期间程序使用的堆内存峰值不能超过预先设置的堆内存的最大值。如果超过需要增加堆内存参数值。2. 垃圾回收情况: 如果在服务的运行期间频繁的进行垃圾回收特别是FULL GC操作说明设置的堆内存空间过小适当的提高堆内存空间20%-30%3. 线程和CPU的使用定位死锁和CPU过高问题将调节好的新的参数应用到服务上面从新启动服务然后进行观察。多次调整保证最合适的值。保证1. 服务在运行期间使用最大内存不能高于设定的最大堆内存 2. 垃圾回收不频繁不出现OOM现象。2. 栈空间的大小-xss栈空间不能设置过小也不能过大。过小会导致JVM频繁的进行GC操作会导致STW甚至导致stackoverflowerror出现。过大JVM在进行FULL GC的时候会整堆进行扫描浪费时间。可以通过以下方法考虑1. 高并发场景下线程会比较多这个时候将栈空间设置的小一些防止系统内存不足线程无法创建。2. 业务代码比较负载业务代码比较复杂方法存在大量的递归调用这个时候栈空间设置的大一些。防止出现栈内存溢出的风险。3. 年轻代和老年代如何调优年轻代进行调优第一种情况Minor GC频繁对象过早的进入老年代。Minor GC大致频率默认情况老年代:年轻代2:1常见应用中可能是几秒 ~ 几十秒一次高频操作中甚至可能每秒一次或更多。Minor GC频繁一秒多次说明enden区的空间不足增加-xmn年轻代参数设置大小或者增大年轻代的占 比。对象过早进入老年代说明幸存者区空间不足将 Eden:S0:S1 6:1:1默认8:1:1老年代调优: FULLGC频繁说明老年代内存不足。解决方法增加老年代的占比3:1增加堆内存调整-xms,-xmx大小。4. 垃圾收集器的选择Serial: 如果应用服务是在单核cpu的情况且对并发性要求不是很高的情况下采用该垃圾收集 器。parllal: 如果应用服务是在多核cpu上的且服务对并发性要求比极高采用该收集器。CMS, Shenandoah(java 12): 当应用程序对响应时间比较敏感的时候采用该垃圾收集器。G1,ZGCjava 11: 如果涉及到大堆回收应用程序对响应时间要求也比较高的情况下可以采用者两种垃圾收集器。G1和ZGC的区别G1适应与低延迟可配置的中大型的堆内存回收几GB或者几百GB。支持并发标记回收。会出现STWZGC支持超低级别的延迟10ms,超大类型的堆回收TB级别支持并发标记回收通过找色指针和读屏障实现对象的转移。不会出现STW。STW 是 Stop The World 的缩写意思是 “暂停世界”。在 JVM 中它表示在某些操作比如垃圾回收、类加载、栈扫描等时JVM 会暂停所有正在运行的应用线程只让 GC 线程或其他系统线程运行以保证数据一致性。总结: 四个方面进行调节每个方面细讲。五. CPU和内存占用率过高1.cpu过高分析什么情况下过高1. 无限循环或者死循环 2. 高并发场景 3. 锁竞争激烈 4. 不断地发生GC操作特别是FULL GC。cpu过高如何进行定位1)通过top命令找到cpu占用率比较高的进程 2)top -H p pid n1 找到该进程下线cpu占用率比较高的线程并将线程id号转化成16进制 3通过jstack -l pid ./file.txt生成线程快照文件 生成该进程下面所有线程的调用栈信息4在file.txt 中搜索转换后的16 进制线程 id即可找到哪个线程导致cpu占有率过高 5通过全路径类名定位到代码里面去排查2.内存过高分析什么情况下内存过高1. 对内存泄漏 2. 堆外内存泄漏 3. 线程数量过多导致占内存使用过大 4. 大对象和数组的频繁创建 5. 缓存使用不当排查1top命令找到占用内存高的进程 2jmap -dump命令生成堆转储文件 3通过visual vm工具打开进行分析找到内存占用比较大的对象4定位到代码进行分析六. 如何进行分库和分表一. 为啥需要分表1. 单表的数据量太多做了很多优化以后仍然无法提升效率。2. 索引一般是B树的结构树的层级变高查询的速率会变慢。二. 哪些分库分表的工具1. sharding-jdbc: 优点: 不需要部署运维成本低。 缺点耦合度高各个模块都依赖于 sharding-jdbc不利于扩展。2. mycat: 优点耦合度低系统升级容易。三. 分表的策略1. 范围分片表10-1000000 表2: 1000001 - 2000000优点操作简单 缺点热点数据集中易倾斜。2. hash分表:取模运算user_id % 4结果落在4个表/库中优点数据分布均匀不会出现数据倾斜。 缺点不能进行范围查询扩容成本高。3. 按日期时间分片按照月年进行划分夸表查询复杂四. 分表以后出现的问题1. ID唯一性问题?1. 采用雪花算法 64位唯一id 2. 使用数据库的分段式每张表的自增id从不同区间开始2. 事务的一致性问题最终一致性解决方案TCC, 本地消息表3. 查询问题如何实现count(*)、聚合查询、分页、排序1. 如果查询不频繁直接在业务代码里面查询每张表然后在内存中聚合分页。2. 如何需求复杂查询频繁采用ShardingSphere这样的分页工具进行分片路由分页和聚合。如何实现join查询1. 使用相同分片规则的如user_id实现同分片 JOIN。2. 从每个分片表查询出数据在应用层做关联。3. 设计冗余字段业务上避免 JOINorder表中冗余如用户名、手机号等直接通过order表查询。4. 数据迁移和扩容难初期分表hash数量不足相对容易后期需要增加库表代价比较大需要从新进行hash分表hash%16-------hash%64五. 为啥要分库单库超过100GB需要进行分库1. 数据量比较大磁盘的容量被撑爆。2. 数据库的连接数有限高并发的场景下会出现 too many connections报错。六. 分库的策略1. 范围分表user_id1亿存A库 user_id1亿存B库在对1亿数据进行划分采用hash算法hash%162. hash分表3. 时间分表七. 分库出现的问题和分表类似最主要存在的跨数据库事务的问题。八. 分库实现查询问题回答分库无法直接跨库执行sql可以采用应用层路由聚合查询。先查询每个分库再在业务层合并结果。或者使用中间件shardingpheres自动实现路由执行聚合排序shardingpheres分片的规则1.奇偶性 2. hash算法参考链接: mysql面试之分库分表总结-EW帮帮网七. 微服务的优缺点和划分原则和传统的单体架构相比传统的单体架构1启动时间长不利于协同开发一个人开发完成以后不能单独上线要等所有人都开发完成以后才能上线。2传统的单体架构所有的模块都集中在一起高并发场景下不利于压测扩容整体扩容会导致资源的浪费3传统的单体架构业务代码会越来越复杂不利于代码的维护。老员工离职以后新员工很难维护代码。微服务架构1项目启动时间段有利于协同开发每个人只需要负责开发自己的模块即可。2高并发场景下面利用压测对并发场景高的模块进行扩容并发场景的低的不进行扩容有利于很好的利用资源。3利用代码的维护每个人只需要维护自己负责的模块。代码的耦合性比较低。缺点1微服务可能要部署十几到几十个模块给运维人员带来了很大的压力。2) 微服务涉及到多个模块之间的调用涉及到数据一致性问题。3微服务涉及到跨服务跨服务器之间的通信对编程人员的能力有一定的要求。八. 前端面试知识九. 分布式事务1. 两阶段提交概念: 用来保证分布式事务一致性的协议用来保证在多个参与者参与的节点中事务要么全部提交要么全部回滚。两阶段准备阶段协调者向所有参与者询问是否可以执行事务操作参与者本地执行事务但是不提交事务。并将事务执行结果yes/no返回给协调者。提交阶段当所有参与者均返回yes给协调者协调者通知所有参与者提交事务。有一个参与者返回no给协调者则协调者通知所有参与者进行回滚。优点1 ) 保证事务的原子性和强一致性。缺点1) 同步阻塞问题所有事务的参与者在等待其它参与者执行的时候都处于等待状态。 2单点故障问题当协调者出现故障的时候所有的参与者都处于等待状态无法正常工作。适应场景适用于对一致性要求极高、参与方较少的系统。2. 补偿事务TCCtry阶段尝试执行业务检查并预留资源空间并不提交。confirm阶段所有的try阶段都成功以后正式执行对应的业务并提交事务。confirm阶段做了幂等性的处理执行多次的结果保持一致cancel阶段任何一个try没有成功就进行回滚。cancel是为try阶段提供用来回滚的假设一个电商下单操作涉及扣库存和扣余额两个服务✅ 1. Try 阶段预处理商品服务冻结库存账户服务冻结余额仅做预留不做实际扣减✅ 2. Confirm 阶段正式提交商品服务真正扣减库存账户服务真正扣减余额幂等性处理防止重复提交✅ 3. Cancel 阶段补偿回滚商品服务解冻库存账户服务解冻余额恢复 Try 阶段的预处理案例:// Try 阶段冻结余额public boolean tryFreeze(String userId, BigDecimal amount) {// 检查余额是否充足// 冻结余额 amount写入冻结表// 返回成功或失败}// Confirm 阶段正式扣款public boolean confirm(String userId, BigDecimal amount) {// 从冻结表扣减余额// 删除冻结记录// 幂等处理}// Cancel 阶段解冻余额public boolean cancel(String userId, BigDecimal amount) {// 将冻结的余额退回// 删除冻结记录// 幂等处理}本地消息表总的来说有三个阶段组成第一事务发起阶段第二消息投递第三消息消费阶段。第一阶段当应用执行本地的业务操作时候如减少库存在本地的同一个数据库事务中要把分布式消息生成订单插入到本地消息表。本地业务操作和消息插入到本地消息表是原子操作CREATE TABLE local_message (id BIGINT PRIMARY KEY, -- 消息唯一ID如雪花算法biz_id VARCHAR(64), -- 业务唯一标识如订单IDstatus TINYINT, -- 状态0待发送、1已发送、2已完成payload TEXT, -- 消息内容JSON格式的业务数据retry_count INT DEFAULT 0, -- 重试次数next_retry_time DATETIME, -- 下次重试时间created_time DATETIME -- 创建时间);第二阶段消息的投递阶段通过独立的消息发送服务或者定时任务定时轮询消息表里面状态是未发送的任务将消息投递到kafka,MQ等消息队列里面。如果消息投递成功修改本地消息表里面的消息的状态为已发送。如果投递失败则进行重试。第三阶段下游的消费者监听MQ当收到消息以后。1. 先进性幂等性的效验通过订单id查询订单表里面有没有该条记录如果没有。2. 执行本地事务生成订单。3. 响应MQ结果成功向MQ发送ACKMQ删除该记录或者标记为已完成。失败日志记录并触发告警依赖ACK的重试机制。4. 对账补偿定时对账任务检查长时间处于“已发送”状态的消息如超过1小时未完成主动查询下游业务状态若下游业务已成功更新消息状态为“已完成”。若下游业务失败触发告警并人工干预或自动回滚如调用冲正接口。设计的关键要点1. 消息可靠性保障本地事务与消息的原子性使用数据库事务确保业务操作和消息写入同时提交或回滚。java复制下载Transactional public void createOrder(Order order, Message message) { orderDao.insert(order); // 业务操作 messageDao.insert(message); // 写入消息表 }消息投递重试机制采用指数退避策略如1s、3s、10s逐步拉大重试间隔避免雪崩。2. 消费端幂等性唯一业务标识使用biz_id如订单ID作为去重依据确保同一业务仅处理一次。SELECT COUNT(*) FROM order WHERE order_id #{biz_id};数据库唯一约束在业务表设计时对biz_id添加唯一索引防止重复插入。3. 最终一致性延迟异步消息的延迟消息从产生到处理完成可能存在秒级甚至分钟级延迟需业务容忍短暂不一致如“支付成功”后短暂显示“待发货”。适应的场景跨服务事务如电商的“扣库存→生成订单→通知物流”。非强一致性需求允许短暂不一致但要求最终一致如账户余额变动。高可靠系统对事务丢失容忍度低如金融系统的转账记录示例代码// 1. 业务服务库存服务public class InventoryService {Transactionalpublic void deductStock(String productId, int count) {// 扣减库存inventoryDao.deduct(productId, count);// 写入本地消息表Message message new Message();message.setBizId(UUID.randomUUID().toString());message.setPayload(buildOrderMessage(productId, count));messageDao.insert(message);}}// 2. 消息发送服务Scheduled(fixedDelay 5000)public void sendMessages() {ListMessage messages messageDao.selectPendingMessages();for (Message msg : messages) {try {mqProducer.send(msg.getPayload());messageDao.markAsSent(msg.getId());} catch (Exception e) {messageDao.updateRetryInfo(msg.getId());}}}// 3. 订单服务消费者RabbitListener(queues order.queue)public void handleOrderMessage(String payload) {OrderMessage orderMsg parsePayload(payload);if (orderDao.exists(orderMsg.getBizId())) {return; // 幂等性检查}orderDao.createOrder(orderMsg);}十. 分布式锁1. 通过redis实现两步加锁阶段set key value nx exnx如果该key不存在创建该key。ex: 防止锁长时间不释放value用来防止锁误删的锁只能通过锁的创建者释放。可以使用uuid雪花算法生成。释放锁阶段: 通过lua脚本进行释放缺点1.单点故障Redis主从切换可能导致锁失效。 2. 业务没有执行完锁被释放了2. 通过redission进行实现看门狗机制3. 通过乐观锁实现实现原理使用版本号或时间戳字段更新时校验版本号是否变化。例如UPDATE resource SET version new_version WHERE id 1 AND version old_version;优点轻量级避免长期锁竞争。缺点需业务逻辑配合高并发时重试成本高。适用场景读多写少、冲突较少的场景。4. 通过redlock实现解决单点故障问题5. 通过zk实现十一. 数据一致性判断一般从以下几个方面?1. 数据库事务 通过 Spring 的 Transactional 保证一组操作的原子性。2. 并发控制 通过数据库的悲观锁select for update 或 乐观锁version版本号 防止并发修改数据。3. 缓存场景: 一般采用先更新数据库再删除缓存或延迟双删策略。4. 分布式环境下分布式环境下使用分布式锁Redis、Redisson 来避免多服务并发修改同一资源。5. 微服务架构中: 通过消息队列保证最终一致性例如本地消息表、可靠消息机制。4. 分布式事务框架比如 Seata 或 TCC 模式 来保证一致性十二 . 大文件10g以上文件解析的问题10g文件进行解析读取如果解析过程中出现异常了是全部解析流程进行回滚下次重新解析。还是记录当前的解析的操作点下次从该操作点接着解析?两种方式第一种全部回滚下次重新进行解析。不适合大文件。第二种记录解析位置并做幂等性处理。解析文件↓记录当前解析位置↓异常↓从断点继续解析记录当前文件偏移量已处理行号已写入数据三. 实际工程方案1 分段解析 2 记录解析进度 3 幂等设计非常加分 (数据库的唯一索引)十三. 大的文件解析过程第一种直接本地磁盘读取文件: BufferedReader br new BufferedReader(new FileReader(bigfile.txt))1. 边读取边消费。(可以采用NIO模型提高效率) 也可以一个线程读多个线程消费来提高效率。2. 事务补偿机制(失败的任务会重新的执行)3. 记录偏移量和这次读取的行数任务失败后下次读取从失败的地方读取提高解析效率。4. 数据库层面设置幂等性机制防止数据重复。(设置唯一主键)第二种通过接口从网络中读取: InputStream is conn.getInputStream();BufferedReader br new BufferedReader(new InputStreamReader(is))1️. 核心问题文件在远程 → 通过网络获取 → 流式读取。网络可能慢、不稳定 → 容易超时。文件很大几十 GB → 不可能一次性加载到内存。同时解析和落地需要保证效率和稳定性。(1) 网络流式读取不要先下载整个文件再解析。使用 HTTP/FTP/S3 SDK 等的 InputStream直接边读边解析URL url new URL(http://remote-server/largefile.txt);HttpURLConnection conn (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);conn.setReadTimeout(5000);try (InputStream is conn.getInputStream();BufferedReader br new BufferedReader(new InputStreamReader(is))) {String line;while ((line br.readLine()) ! null) {processLine(line); // 解析并落地}}(2) 边读边异步处理读取线程 → 阻塞队列 → 异步线程池处理。异步线程池可以调用远程接口解析数据或者做本地落地。BlockingQueueString queue new LinkedBlockingQueue(1000);Thread reader new Thread(() - {try (BufferedReader br new BufferedReader(new InputStreamReader(is))) {String line;while ((line br.readLine()) ! null) {queue.put(line);}queue.put(EOF);} catch (Exception e) {e.printStackTrace();}});ExecutorService processorPool Executors.newFixedThreadPool(10);Thread processor new Thread(() - {try {String line;while (!(line queue.take()).equals(EOF)) {processorPool.submit(() - {try {String result callRemoteApi(line, 5000); // 超时5秒writeToLocal(result);} catch (Exception e) {writeToFailFile(line);}});}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});3) 批量读取 批量调用如果接口支持不要每行都调用一次按 N 行组成一个批次ListString batch new ArrayList();queue.drainTo(batch, 100); // 每100条组成一个批次callRemoteApi(batch);优点减少网络请求次数提高吞吐量。4) 超时和重试策略网络读取和接口调用都可能超时HTTP/FTP 流读取设置 connectTimeout readTimeout调用远程接口时也设置超时失败的数据可以落地到本地文件或者消息队列后续再重试不影响整个文件流处理。十四. 什么是字节流和字符流?字节流概念以字节8位为单位进行读写适合处理所有类型的数据文本、图片、音频、视频等。字节流 inputstreamoutputstream字符流Character Stream概念以字符16位 Unicode 为单位进行读写专门处理文本数据自动处理字符编码。顶层抽象类Reader输入字符流Writer输出字符流BufferedReader / BufferedWriter: 带缓冲区可按行读写文本读取在网络上数据传输是不是都是字节流1️.网络传输的本质TCP/HTTP/Socket 等协议都是基于 字节传输 的。无论你发送文本、图片、音频、视频网络最终看到的都是 一连串的字节。网络只负责把这些字节从一台机器传到另一台机器并保证顺序、完整性TCP或不保证UDP。2️.为什么我们还要用字符流虽然网络传输的是字节但如果传的是 文本数据如 HTTP 请求体、JSON、XML、HTML 等就需要 把字节按编码转换成字符 才能方便处理。Java 提供了 InputStreamReader / OutputStreamWriter 来做 “字节 ↔ 字符” 的转换InputStream is socket.getInputStream(); // 字节流BufferedReader br new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); // 转字符流按行读本质上网络底层都是字节流字符流只是对字节流的包装为了方便文本处理和编码转换、按行处理大文件十五. 雪花算法和uuid的区别?雪花算法64bit结构1bit 符号位41bit 时间戳10bit 机器ID12bit 序列号4096 × 1000 409.6万 ID / 秒全局唯一时间戳 机器ID(区分不同的节点) 序列号只要机器ID不同时间不回拨ID 就不会重复。雪花算法生成 64 bit Long 类型ID。趋势递增 按时间递增支持分布式不同机器生成不同ID生成速度快高性能本地生成无需网络UUID:UUID 是一种 全局唯一标识符标准长度 128 bit。特点唯一性无序36字符缺点字符串很长 不适合作为数据库主键 索引效率低 不具备时间有序性UUID vs 雪花算法对比| 对比项 | UUID | 雪花算法 || ----- | ------ | ----- || 长度 | 128bit | 64bit || 类型 | 字符串 | Long || 是否有序 | 无序 | 趋势递增 || 数据库索引 | 差 | 好 || 生成方式 | 随机 | 基于时间 || 适合做主键 | 不推荐 | 推荐 || 可读性 | 差 | 较好 |十六. 对象的生命周期?1 创建对象2 分配堆内存3 调用构造方法4 程序使用对象5 对象没有引用6 GC判断对象不可达7 垃圾回收8 内存释放年轻代 GC通常叫 Minor GC 或 Young GC并不是“每隔几秒固定执行一次”而是 在特定条件触发时执行。最常见的触发条件是 年轻代尤其是 Eden 区空间不够。内存驱动而不是时间驱动。