好特卖-2025实习-Golang面经

十一点开始面试,先甩了我一个word来做笔试题,写了快一个小时才开始面试。

都是Ai整理的,可能有错误。

  1. 为什么要写高性能网关?
  • 问:你为什么会去写高性能的网关系统?这是你自己的项目吗?
  • 答:想把学到的技术落到一个“完整且有意义”的产品化开源项目上。观察到 Nginx 性能强但链路和配置较复杂,于是用 Go 在高并发场景做一个可复用的高性能网关。属于自研,参考了常见架构模式后自己实现。
  1. JWT / OAuth 相关
  • 问:你用的 JWT 是哪个版本?(面试官口误/写法混乱,实际意图:所用 JWT 库版本)
  • 答:具体库版本记不清。系统里实现的是无状态鉴权(候选人口中混用了“JWT / OAuth 2.0”的说法)。流程:每个租户有唯一密钥;客户端发起请求时在 HTTP Header 写入密钥;请求到网关后,网关调用鉴权模块,从数据库读取用户信息核验;校验通过后生成并返回 Token;后续所有代理请求在 Header 附带该 Token,网关侧都会先鉴权再转发。
  1. 实习动机与现状
  • 问:你现在实习想获得什么?你现在的实习状态是?
  • 答:想多接触“高并发业务开发”,不仅做“轮子”。目前仍在职,但准备离开(对方无转正通道),将专注找工作。

(下面是反问夹杂问题)

  1. 业务与技术栈(对方公司)
  • 问:我们做什么业务?Go 在系统中负责哪部分?
  • 答(面试官说明):公司做 AI 在企业的落地,使用“大模型”,算法侧用 Python,工程与系统主体用 Go,主要通过接口/服务调用完成对接。
  • 反问(候选人):为什么整个系统都采用 Go?
  • 答(面试官):简单、跨平台编译方便;与 Java/Python相比差异没那么大,选型更偏工程效率。
  1. 数据结构与算法追问
  • 问:图的最短路有什么印象?用什么算法?
  • 答:提到 DFS/BFS;最短路说到 Dijkstra。
  • 问:Dijkstra 怎么做?
  • 答:印象是用堆辅助(细节记不清)。
  • 问:堆的性质、如何快速定位子节点?
  • 答:只笼统提到最大堆/最小堆,未能展开性质与下标关系。
  • 问:快速排序时间复杂度与原理?
  • 答:时间复杂度 O(n log n),原理一时想不起来。
  • 问:你写的“栈”这题时间复杂度?
  • 答:O(1),因为只操作“尾元素”,不需要遍历底层数组
  1. 前端经历与“React”
  • 问:你用过 React 吗?(面试官提到“瑞艾特”)
  • 答:没用过 React,之前说的是 Raft 共识算法。前端实操主要是 Vue 写过网关项目的后台管理最小功能;在实习中用 JS 做过表单提交,整体偏简单。
  1. 到岗与实习时长
  • 问:你现在人在南京吗?多久能入职?能实习多久?
  1. 薪资期望
  • 问:实习与转正的薪资期望?
  1. 反问与面评建议
  • 问:作为应届生,我最需要提升哪方面?
  • 答(面试官建议):多写程序、多做项目;你后面代码题完成度还可以,但前面静态题错误较多,主要是代码量不够导致理解不牢。两条路都行:多刷题 or 多做工程,增加“见过的场景”。

---

下面是笔试题(只有答案)

收到~我把这份笔试题按「题目|原答案|纠正/标准答案」理了一遍;原文里明显的转写/语法问题我也顺手修了。没给出原始代码导致无法精确跑出的几题,我会标注“待核实”,并给出通用的正确做法,方便你替换。(每题后附原文出处)

1) 简述 Session

原答案(摘录):Session 是“在客户端和服务端之间保存会话连接”的方式;用于“提高连接复用能力,使连接在一定时间内有效”;SessionID 返回给客户端,通常放在 Cookie 里。纠正/标准答案

  • Session 是服务端保存的用户会话状态(登录态、用户偏好等),用来在多次 HTTP 请求间识别同一用户。
  • 客户端仅保存Session ID(多为 Cookie 存储),请求时带上以便服务端查回会话。
  • “提高连接复用能力”不准确;连接复用/Keep-Alive属于TCP/HTTP 连接层,与 Session 的业务会话状态是两回事。
  • 常见风险:会话固定/劫持,需配合 HttpOnlySecure、CSRF 防护、过期/刷新策略、存储(内存/Redis)等。

2) OOP 三大特性

原答案:封装、继承、多态。纠正/标准答案:答案正确,可在笔试中补一句:多态=“面向接口编程”,运行期按动态类型分派。

3) Go 里“近似类”的实现

原答案(摘录):Go 没有继承,采用组合;示例 type cat + 方法。纠正/标准答案(要点 + 更佳示例)

  • Go 没有类与继承,但有类型 + 方法结构体嵌入(组合/“伪继承”)接口多态
  • 示例:
type Animal interface{ Speak() string }

type Cat struct{ Voice string }
func (c Cat) Speak() string { return c.Voice }

// 结构体嵌入(复用 Cat 的字段/方法)
type RobotCat struct{ Cat }

4) 什么是 Stack Overflow

原答案:栈空间溢出。纠正/标准答案:正确。常见诱因:无限/过深递归、超大栈上数组;解决:改迭代/尾递归、移堆上、调小栈帧。

5) “会用日志吗”

原答案(摘录):单例持有文件描述符,写入运行信息/错误。纠正/标准答案(更规范要点)

  • 选型:标准库 log 或结构化库(zap/zerolog)。
  • 级别:DEBUG/INFO/WARN/ERROR;带字段(traceID、uid)。
  • 轮转:配合按大小/时间切分;并发安全;不要 defer 在热路径频繁打开/关闭文件
  • 生产:异步写、落盘失败降级、敏感信息脱敏。

6)(提示词改写题)

原文(摘录):把 sj.Get(...).Int() 改成 bitableRepository.GetCell(rowSj, "是否已转审","String"),并说明 5 点修改。纠正/标准答案:原说明已清楚;注意方法返回值类型错误处理,避免静默丢错。

7) 代码结果(goroutine 计数)

原答案

gorountines:2
gorountines:1
gorountines:0

状态待核实(原题未给代码)。通用说明runtime.NumGoroutine() 的输出取决于启动/结束时机调度器时序;若题意是“开启两个子协程并等待全部退出”,常见打印顺序为 1→3→1(主→创建两协程→回到仅主),不一定是 2→1→0。

8) 计算代码结果(数组推演)

原答案:最终 {1,2,3,7,14}(含中间推演表)。状态待核实(无源码)。建议写法:若是“按规则累加/翻倍”的动态推演题,请把转移方程循环与下标变化补齐,否则很难客观给分。

9) 代码结果

原答案4+6=10状态待核实(无源码)。若题为字符串拼接或位运算,结果可能不同;需以题面为准。

10) 补全代码(回文数判断)

原答案(摘录):“取出 i 的每一位拼到 j;判断 n==j;调用 IsSymmetry(n)。”纠正/标准答案(可直接替换)

func isPalindromeNumber(n int) bool {
    if n < 0 { return false }
    rev, x := 0, n
    for x > 0 {
        rev = rev*10 + x%10
        x /= 10
    }
    return n == rev
}

11) Go 的“缺省参数”

原答案(摘录):通过 slice 或 “...” 缺省参数;示例函数签名语法错误(func() quankang.)。纠正/标准答案

  • Go 不支持默认/可选参数;常见替代:可变参数 / Functional Options
  • 正确示例(variadic):
func Join(sep string, parts ...string) string {
    if len(parts) == 0 { return "" }
    b := strings.Builder{}
    for i, s := range parts {
        if i > 0 { b.WriteString(sep) }
        b.WriteString(s)
    }
    return b.String()
}

  • 正确示例(Functional Options):
type Server struct{ Addr string; Timeout time.Duration }
type Option func(*Server)

func WithAddr(a string) Option   { return func(s *Server){ s.Addr = a } }
func WithTimeout(d time.Duration) Option { return func(s *Server){ s.Timeout = d } }

func NewServer(opts ...Option) *Server {
    s := &Server{Addr: ":8080", Timeout: 3*time.Second}
    for _, opt := range opts { opt(s) }
    return s
}

12) 链式调用

原答案(摘录)cls.getCell().toString()CusData 未定义;toString 直接 string(ce.data)纠正/标准答案(可直接替换)

type CusData []byte

type cell struct{ data CusData }
func (ce *cell) ToString() string { return string(ce.data) }

type cls struct{ acell *cell }
func (c *cls) GetCell() *cell { return c.acell }

// 链式使用
c := (&cls{acell: &cell{data: []byte("hello")}}).GetCell().ToString() // "hello"

约定:导出方法用大写开头;链式=返回下一步可调用的对象。

13) HTTP 请求(头与 Body)

原答案(摘录)header:=map[string]string 未初始化;http.POST 非标准库;defer resp.Close() 不对;resp.body() 不存在。纠正/标准答案(标准库 net/http 版本)

type Body struct{ Data string }

func httpRequest(b *Body, url string) (string, error) {
    req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(b.Data))
    if err != nil { return "", err }

    // 写入头(鉴权/自定义等)
    req.Header = make(http.Header)
    req.Header.Set("Content-Type", "text/plain") // 依实际场景修改
    // req.Header.Set("Authorization", "Bearer <token>")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { return "", err }
    defer resp.Body.Close()

    data, err := io.ReadAll(resp.Body)
    if err != nil { return "", err }
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        return "", fmt.Errorf("http %d: %s", resp.StatusCode, string(data))
    }
    return string(data), nil
}

14) 「打印题目」(限制每个元素出现次数 ≤ n)

原答案 A:试图用 posMap 维护位置并“搬移切片”,有语法错误 back. 且逻辑多余。原答案 B:仅用 cntMap 计数并追加,思路正确。纠正/标准答案(保序 & 通过常见样例)

func outputNth(slice []int, n int) []int {
    if n <= 0 { return []int{} }
    cnt := make(map[int]int)
    ans := make([]int, 0, len(slice))
    for _, x := range slice {
        if cnt[x] < n {
            ans = append(ans, x)
            cnt[x]++
        }
    }
    return ans
}
// 输入: [20 37 20 20 21], n=2  输出: [20 37 20 21]

复杂度 O(n),空间 O(k)(k 为不同元素个数)。

15) 栈实现

原答案(摘录)popdata[s.size]pushdata[s.size+1] 且未递增 size;越界条件不严谨。纠正/标准答案(两种写法)

这题必须使用固定数组,不允许用slice等

A. 固定数组 + 大小指针

type stack struct {
    data [50]int
    size int // 栈内元素个数,范围 [0, len(data)]
}
func (s *stack) empty() bool { return s.size == 0 }

func (s *stack) push(v int) error {
    if s.size == len(s.data) { return errors.New("full") }
    s.data[s.size] = v
    s.size++
    return nil
}
func (s *stack) pop() (int, error) {
    if s.empty() { return 0, errors.New("empty") }
    s.size--
    return s.data[s.size], nil
}

全部评论
现在用这个语言的比较少了吧
点赞 回复 分享
发布于 09-09 11:21 陕西

相关推荐

评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务