当前位置: 首页 > article >正文

网易日常实习一面面经

1. 自我介绍

2. 两道代码题:

  • 第一道题:写一道链表排序题要求空间复杂度O(1) :已ac
    插入排序算法 时间复杂度 O(N^2),空间复杂度O(1)
class ListNode{
    int val;
    ListNode next;
    public ListNode(int x) {
        this.val = x;
    }
}
public class LinkedListInsertionSort {
    // 插入排序
    public ListNode insertionSort(ListNode head) {
        if(head == null || head.next == null)
            return head;

        ListNode dummy = new ListNode(0);
        ListNode cur = head;
        // 4 2 1 3
        while(cur != null){
            // 每次从链表的哨兵结点向后寻找插入位置
            ListNode prev = dummy;
            // 在已排序的链表中找到合适的位置插入节点
            while(prev.next != null && prev.next.val < cur.val){
                prev = prev.next;
            }
            // 保存当前链表当中的下一个节点,因为这个节点要进行插入了
            ListNode nextTemp = cur.next;
            // 将 cur 节点插入到 pre 后面
            cur.next = prev.next;
            prev.next = cur;
            // 继续处理下一个节点
            cur = nextTemp;
        }
        return dummy.next;
    }

    public static void main(String[] args) {
        LinkedListInsertionSort sorter = new LinkedListInsertionSort();

        // 创建测试链表 4 -> 2 -> 1 -> 3
        ListNode head = new ListNode(4);
        head.next = new ListNode(2);
        head.next.next = new ListNode(1);
        head.next.next.next = new ListNode(3);

        // 排序
        ListNode sorted = sorter.insertionSort(head);

        // 输出排序后的链表
        while(sorted != null){
            System.out.print(sorted.val + " ");
            sorted = sorted.next;
        }
    }
}

归并排序 时间复杂度O(nlogn) , 空间复杂度O(nlogn) 。当然这道题不可以使用这个算法。

  • 第二道题:写出可以StackOverFlow的代码
public class StackOverFlow {
    public static void main(String[] args) {
        dfs();
    }

    private static void dfs() {
        dfs();
    }
}

3. 讲一讲自己项目当中那部分是比较有成就感的。

那我就讲一下第一个项目:基于微服务架构的社区社交平台:
我认为在笔记服务当中:首先我先测试的仅使用了 Mysql 数据库存储和读取数据的接口设计,并用Jmeter 压测单机单节点,发现在本地运行笔记发布删除的吞吐量仅有 10000+/ s ,响应耗时也较高,此时我选择引用 Redis分布式缓存来对数据库查询修改接口之前加上一段缓存,此时吞吐量提升了近1倍,后面我又在Redis之前添加Caffeine 的本地缓存,吞吐量又有较高的提升。但是此时产生了二级缓存结构与Mysql数据库数据不一致的问题。因此我采取在读取记录的读策略时发现缓存当中不存在的情况下先查询数据库在添加到缓存当中。在更新、删除记录时,使用 RocketMQ 广播服务,实现集群环境下,对本地缓存中缓存的删除,达到数据一致性。并且在点赞接口支持高并发写:使用 Redis Bloom 布隆过滤器,高性能判断用户是否点赞,通过 Redis ZSET + MQ 异步落库,消费者中使用 RateLimiter 令牌桶实现流量削峰,避免了缓存雪崩,缓存穿透,缓存击穿等问题;
在用户关系服务当中:通过 Redis 缓存 + 发送顺序消息 MQ 异步存库,实现接口的高并发写与操作的顺序性,使用 Lua 脚本,避免频繁操作 Redis 造成的性能瓶颈,且保证多次操作的原子性;消费者使用联合唯一索引保证关系记录的幂等性;

4. 加入说有个用户进行了点赞,怎么统计某个笔记和内容的点赞数量

首先使用 Redis Bloom 布隆过滤器,高性能判断用户是否点赞:然后发送MQ消息,将点赞落数据落库:最后调用计数服务通过聚合操作将数据落入数据库。

5.怎样处理中间件的消息积压问题?

发生了消息积压,这时候就得想办法赶紧把积压的消息消费完,就得考虑提高消费能力,一般有两种办法:
消息积压处理

  • 消费者扩容: 如果当前 Topic 的 Message Queue 的数量大于消费者数量,就可以对消费者进行扩容,增加消费者,来提高消费能力,尽快把积压的消息消费玩。
  • 消息迁移 Queue 扩容: 如果当前 Topic 的 Message Queue 的数量小于或者等于消费者数量,这种情况,再扩容消费者就没什么用,就得考虑扩容 Message Queue。可以新建一个临时的 Topic,临时的 Topic 多设置一些 Message Queue,然后先用一些消费者把消费的数据丢到临时的 Topic,因为不用业务处理,只是转发一下消息,还是很快的。接下来用扩容的消费者去消费新的 Topic 里的数据,消费完了之后,恢复原状。
    消息迁移扩容消费

6. Java基础

6.1

	String a = "abc";
    String b = new String("abc");
    String c = new String("abc");
    String d = "abc";
    System.out.println(a == b);
    System.out.println(b == c);
    System.out.println(c == d);
    System.out.println(d == a);

这几个用==判断是否相同,首先在字符串常量池当中创建字符串 "abc" 所以 a, d都是指向的字符串常量池的地址,b,c都是在堆上new出来的结果,由于a = "abc"先执行,所以都在堆中的对象指向字符串常量池中的地址,但是他们这两个对象地址又不相同,所以a = d, a != b, b != c, a != c。

6.2 假如当前有一个对象,要重写euqals方法,在重写euqals方法的时候要注意什么?

在重写 equals 方法时,一定要重写 hashCode 方法。equals 和 hashCode 之间有一个重要的契约:

  • 如果两个对象通过 equals 比较结果相等,则它们的 hashCode 也必须相等。
  • 如果两个对象通过 equals 比较结果不相等,它们的 hashCode 不一定要不同,但建议尽量不同,以提高性能。

例如String类当中重写了euqals方法,他会首先判断两个对象是否是同一个对象,即==来判断,通过 这个判断的原理就是计算两个对象的hashcode是否相同。如果相同直接返回true,不同再回判断是否类型相同长度相同,里面的元素相同等等。

如何遍历一个Map,我想要拿到他们所有的key所有的value

在 Java 中,遍历 Map 可以通过多种方式来实现。你可以选择遍历 Map 的所有键 (key),值 (value),或者键值对 (key-value pair)。
以下是几种常见的遍历 Map 的方式,适用于任何实现了 Map 接口的类(如 HashMap、TreeMap 等)。

1. 使用 for-each 和 entrySet()(推荐方式)

entrySet() 方法返回 Map 中所有键值对的集合 (Set<Map.Entry<K, V>>),你可以通过这种方式高效地遍历 Map 的所有键值对。

Map<String, String> map = new HashMap<>();
map.put("a", "apple");
map.put("b", "banana");
map.put("c", "cherry");

// 遍历 key-value 对
for (Map.Entry<String, String> entry : map.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

2. 使用 for-each 和 keySet()

keySet() 方法返回 Map 中所有的键(Set),你可以通过这种方式获取所有键,并使用 get() 方法获取对应的值。

Map<String, String> map = new HashMap<>();
map.put("a", "apple");
map.put("b", "banana");
map.put("c", "cherry");

// 遍历所有键,然后获取值
for (String key : map.keySet()) {
    String value = map.get(key);
    System.out.println("Key: " + key + ", Value: " + value);
}

3. 使用 for-each 和 values()(仅遍历值)

如果你只对值感兴趣,而不关心具体的键,可以使用 values() 方法,它返回一个 Collection,包含 Map 中的所有值。

Map<String, String> map = new HashMap<>();
map.put("a", "apple");
map.put("b", "banana");
map.put("c", "cherry");

// 仅遍历值
for (String value : map.values()) {
    System.out.println("Value: " + value);
}

4. 使用 Iterator 遍历 entrySet()

如果你需要在遍历过程中进行删除操作或者有特殊的遍历需求,可以使用 Iterator。

Map<String, String> map = new HashMap<>();
map.put("a", "apple");
map.put("b", "banana");
map.put("c", "cherry");

// 使用 Iterator 遍历 key-value 对
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, String> entry = iterator.next();
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println("Key: " + key + ", Value: " + value);
}

5. Java 8 Stream API(现代方式)

如果你使用的是 Java 8 或更高版本,Stream API 可以为你提供更简洁的代码风格。

Map<String, String> map = new HashMap<>();
map.put("a", "apple");
map.put("b", "banana");
map.put("c", "cherry");

// 使用 Java 8 Stream API 遍历
map.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));

7. JUC

7.1 假设有一个发音引擎:部署在本地最多只支持32路并发,假设有第33个请求进入翻译引擎,请问怎样实现这个业务?

可以通过阻塞队列或者是信号量来实现这个需求:
信号量实现就32个信号,没有了就阻塞

8. Linux

8.1 讲一下tail命令

tail 命令用于查看文件的末尾内容,默认情况下显示文件的最后 10 行。
语法

tail [OPTION]... [FILE]...

常用选项

选项说明
-n N显示文件的最后 N 行
-f监听文件内容变化,实时输出新增内容(常用于日志文件)
-c N显示文件的最后 N 字节
-q静默模式,不输出文件名
-v始终输出文件名
使用示例

1. 查看文件最后 10 行

tail file.txt

2. 查看文件最后 20 行

tail -n 20 file.txt

3. 实时监听日志文件

tail -f /var/log/syslog

4. 监听多个文件

tail -f file1.log file2.log

5. 显示文件的最后 50 个字节

tail -c 50 file.txt

结合其他命令使用
6. 结合 grep 过滤日志

tail -f /var/log/syslog | grep "error"

退出 tail -f

在 tail -f 运行时,可以使用 Ctrl + C 退出监听模式。
总结: tail 是一个非常实用的命令,特别适用于日志文件的查看和实时监控。

8.2 如果当前有个文件他的权限是644,那么这个644代表什么?

在 Linux 中,文件权限是通过三组数字(通常是三位或四位)来表示的,每个数字代表一组用户的权限设置。数字 644 具体表示以下含义:
解释:

  • 数字 6: 表示文件所有者(Owner)的权限。
  • 数字 4: 表示文件所属组(Group)的权限。
  • 数字 4: 表示其他用户(Others)的权限。

权限映射:
每个数字实际上是由三个二进制位组成,分别代表读(r)、写(w)和执行(x)权限:
r (读权限) = 4
w (写权限) = 2
x (执行权限) = 1
- (没有权限) = 0

权限详情:

6: 文件所有者的权限是 rw-(读 + 写,无法执行),对应二进制是 110,即 4 + 2 + 0 = 6。
4: 文件所属组的权限是 r–(只读),对应二进制是 100,即 4 + 0 + 0 = 4。
4: 其他用户的权限是 r–(只读),对应二进制是 100,即 4 + 0 + 0 = 4。

综合起来:

文件所有者: 有读和写权限(rw-)
文件所属组: 只有读权限(r–)
其他用户: 只有读权限(r–)

总结:

数字 644 代表文件权限为:

  • 所有者(Owner):读和写权限(rw-)
  • 所属组(Group):只读权限(r–)
  • 其他用户(Others):只读权限(r–)

9. git

9.1 讲一下git的pull

在 Git 中,git pull 是一个常用的命令,用于从远程仓库获取最新的更改并将它们合并到当前本地分支。简单来说,git pullgit fetchgit merge 两个操作的组合。
语法

git pull [<remote>] [<branch>]
  • <remote>:指定远程仓库的名称,默认为 origin,即默认的远程仓库。
  • <branch>:指定要拉取的远程分支,默认为当前分支。

工作流程

  1. git fetch:从远程仓库拉取最新的代码(但是不会合并到本地分支)。
  2. git merge:将从远程仓库拉取下来的更改与当前本地分支进行合并。

所以,执行 git pull 相当于先执行 git fetch,然后执行 git merge
示例

1. 拉取默认远程仓库 origin 的当前分支更新

git pull

这会从远程仓库 origin 获取当前分支的最新更新,并与本地分支进行合并。
2. 拉取指定远程仓库 origin 的指定分支(例如 main)

git pull origin main

这会从远程仓库 originmain 分支拉取更新,并合并到当前本地分支。
3. 拉取更新后不进行合并(仅获取更改)

如果你想只获取远程更新,但不合并到本地分支,可以使用:

git fetch

这会从远程仓库拉取所有的更改,但不会自动合并。你可以查看更改并决定如何处理它们。
4. 使用 git pull --rebase

默认情况下,git pull 会执行一个 merge 操作,可能会产生合并提交(merge commit)。如果你想避免生成额外的合并提交,而是将本地提交“放在”远程分支的最新提交之后,可以使用 --rebase 选项。

git pull --rebase

这会让你的本地更改基于远程分支的最新更改进行重放(rebase)。
注意事项

  • 合并冲突: 如果本地更改和远程更改冲突,Git 会提示你进行冲突解决。在解决冲突后,你需要手动提交合并结果。
  • 历史记录: 如果你使用 --rebase 选项,历史记录会变得更为线性,没有合并提交。

总结

git pull 是用来从远程仓库获取并合并代码到本地的命令。
它是 git fetchgit merge 的组合,可以自动合并更新。
使用 git pull --rebase 可以避免产生额外的合并提交,使得历史记录更加整洁。


http://www.kler.cn/a/540824.html

相关文章:

  • 【R】Dijkstra算法求最短路径
  • 【批量获取图片信息】批量获取图片尺寸、海拔、分辨率、GPS经纬度、面积、位深度、等图片属性里的详细信息,提取出来后导出表格,基于WPF的详细解决方案
  • C语言基础第04天:数据的输出和输出
  • EasyExcel 导出合并层级单元格
  • kafka消费端之分区分配策略
  • 【Qt 常用控件】输入类控件1(QLineEdit和QTextEdit 输入框)
  • 安卓使用JExcelApi读取Excel文件
  • 06排序 + 查找(D2_查找(D1_基础学习))
  • STM32EXTI外部中断
  • 废品回收小程序:技术创新下的经济效益
  • 【快应用】多语言适配案例
  • Spring模块组成
  • 什么是 React Router?如何使用?
  • 使用云计算,企业的数据监管合规问题如何解决?
  • BUCK电路的双脉冲测试
  • 【通俗易懂说模型】反向传播(附多元分类与Softmax函数)
  • 深度学习|表示学习|Mini-Batch Normalization 具体计算举例|23
  • Intellij IDEA调整栈内存空间大小详细教程,添加参数-Xss....
  • 【推荐】爽,在 IDE 中做 LeetCode 题目的插件
  • 基于 FFmpeg 和 OpenGLES 的 iOS 视频预览和录制技术方案设计
  • Spring容器初始化扩展点:ApplicationContextInitializer
  • MVVM设计模式
  • 大模型基础面试问题汇总
  • 1.2 环境搭建
  • 「vue3-element-admin」告别 vite-plugin-svg-icons!用 @unocss/preset-icons 加载本地 SVG 图标
  • 2.1 Mockito核心API详解