fundamentals.md

·interview-questions

语言基础

和 C++ 对比介绍一下 Go 语言的特点和优势?

和 C++ 相比的话 Go 最大的特点和优势自然就是语法的简洁了,比如写并发,go 自带协程和 channel,写法更直观也更简单,而 c++ 要用线程和锁自定义来写;另外像是内存管理和部署方面,go 的垃圾回收和天生的跨平台编译与容器化都很方便,不需要手动管理内存,一条命令就可以产出可执行文件,并且 go 还自带了性能分析等工具;同时 go 的性能与 c++ 也非常接近,工程效率却更高,更适合上层的常见后端应用开发

go 包管理的方式有哪些?

现在的主流是使用 Go Modules,是官方推荐的包管理工具,使用 go.mod 和 go.sum 来管理包和版本,不依赖 GOPATH,可以在任何地方创建项目

早期的话是 GOPATH 和 vendor,前者是没有版本锁定,所有项目都要放在 GOPATH 下面,后者是体积较大,升级也很麻烦,因为是把依赖的源码拷过去

go 支持重载吗?如何在 go 中实现一个方法的重载?

go 语言不支持重载,函数、方法、运算符的重载均不支持,在同一作用域里,不能定义多个名字相同参数不同的函数,但可以通过使用接口+类型断言,或者使用泛型来模拟重载,比较推荐使用泛型,因为它是类型安全的,并且有编译时检查

go 语言中如何实现继承?

go 语言是没有传统的继承方式的,但可以用结构体嵌入和接口来模拟,go 本身也是更提倡优先使用组合而非继承

比如结构体可以将另一个结构体嵌套为自己的字段,被嵌套的结构体中的匿名字段和方法会被提升到外部结构体中,可以直接访问和调用;子结构体可以定义与父结构体相同的方法,从而覆盖嵌套结构体的方法,实现类似方法重写的功能,如果需要调用被覆盖的方法,可以显式调用嵌套结构体的方法

go 的接口配合组合机制,就可以实现类似继承的多态功能,会调用自己类型的函数,但 go 并不支持访问控制,类似于 protected 这种关键字,所有嵌套字段和方法的访问权限取决于其首字母是否大写,同时也没有强制的父子关系和多级继承

go 语言中如何实现多态?

go 的多态主要有两种,接口多态和泛型多态,前者是运行时,后者是编译时;多态是指统一接口可以在不同类型上表现出不同的行为

接口多态是先定义接口,任何类型只要实现了接口要求的方法,就能被当成这个接口来用,用接口类型接收具体实现,调用时按实际类型派发

泛型多态则是给函数或类型加类型参数,再用约束限定可用操作,一个实现适配多种元素类型,在编译器实例化

接口多态本身是可以动态存储实现该接口的任何类型的,另外 go 还提供了类型断言和类型选择来识别不同类型,也可以让断言和选择来配合空接口实现泛型化的多态

map 和 slice 作为参数传递时会遇到什么问题?

map 和 slice 都是引用类型,作为参数传递给函数时,函数内对数据的任何修改都会影响原始的

map 不是并发安全的,如果在函数内部为 slice 进行扩容操作,会分配新的底层数组,但原始切片不会引用新的数组

Go 语言中 struct 和 class 有什么区别?

go 没有 class,是使用 struct 和方法+接口来实现面向对象的功能,结构体中只定义字段,函数写在类型外部,且可以绑定到任何已命名类型;没有访问权限控制,只有包级导出规则:首字母大写对外可见,小写包内可见,也没有继承

讲讲 go 的错误处理机制?

go 是把错误当作返回值来处理的,没有异常机制,一般是调用方来检查,能恢复的则上报,不能恢复的就尽早终止

函数把 error 作为最后一个返回值,中间层尽量不反复打日志,把错误上交到边界层,再统一记录、告警;用自定义错误类型来表示特定情形,包装错误时保留原始原因链路,方便边界层判断真实原因

当不可能恢复的前置条件被破坏时(如数组越界),可以在进程或 goroutine 的最外层用 recover 函数兜底来恢复,避免进程直接崩溃,但业务分支一律用错误返回,不要用 panic 当流程控制

和外部交互时,要区分临时性(如网络抖动、超时)和永久性(资源不存在、输入非法)错误,前者可以用指数退避重试且确保操作幂等,后者就直接返回给调用方让其修正;当多个 goroutine 并行时,第一处失败就尽快取消其他分支;对用户隐藏内部细节,转成明确的业务提示,对运维日志保留完整上下文,方便排查

讲讲 go 里面常见的数据类型?

布尔、整型、浮点型、复数、字符串、数组、切片、哈希表、结构体、接口(interface)、channel

这些类型的零值为 nil:指针、切片、映射、通道、函数、接口

在你日常使用 go 语言开发时有什么注意事项或者遇到过哪些问题吗?

在 go 语言中什么时候会需要性能调优?

指标/SLO 失守:p95/p99 延迟超出目标、超时率/重试率上升、抖动大;批处理超出时间窗;冷启动或首包过慢

吞吐或容量逼顶:QPS 提升后延迟非线性上升、队列/背压积压明显、单实例扩不动(扩容只能“加机器”)

资源/成本异常:CPU 长期 >70% 且延迟劣化;RSS 逼近 cgroup 限额、频繁 OOM;GC 周期/停顿放大;网络/磁盘带宽接近瓶颈;账单异常

依赖/架构变化:上线前压测暴露瓶颈;成为上游核心依赖、SLA 收紧;数据分布改变出现热键;锁竞争、过多序列化/反射、小对象高分配

可靠性信号:背压失效导致雪崩;消息堆积不可消化;限流后仍抖;GC 抖动引发级联超时

环境切换:迁移到容器/边缘节点/低配实例;Go 版本或运行时参数变更(调度、GC 行为变化)后性能回退

活动前置:大促/峰值/营销活动前的容量评估与余量校准

不该调优的时刻:

已满足 SLO 且成本合理,仅出于“看起来更快”;或会显著牺牲可读性/稳定性而收益很小

假设现在有一个函数,它内部写了一个子协程,子 goroutine,这个 goroutine 内部 panic 掉了,那你觉得外层的程序它能捕捉到内层的 panic 吗?

  • go 里面捕捉 panic 的函数是什么

  • 如果现在你编写了一段程序,运行时会造成切片的越界,进而导致程序崩溃,但在开发时你并没有办法显式地捕捉到这个错误,那你会怎么从最顶层去防止这种情况导致程序崩溃

  • 你在项目里有用到过 channel 吗

  • 有缓冲和无缓冲的 channel 的区别是什么

  • 我们在 go 里面经常会传一些参数,那我该如何确定这个参数传的是值还是指针呢

  • 那简单来说我们应该用什么样的载体或者类型去定义指针呢

  • 在 go 语言中如何处理 panic

defer 是用来干嘛的以及 defer的执行顺序是什么

A:defer 主要是用来延迟执行的,常用于资源释放和清理操作,比如关闭文件、解锁和关闭事务等,它能保证在函数所有逻辑结束后,defer 的所有语句都被执行,可以增强代码的可读性和健壮性;defer 是在函数真正返回之前被执行的,多个 defer 会被压入一个栈,最后注册的最先执行,也就是先进后出,并且 defer 的参数在注册时就会被求值,也就是如果参数在中间有改变的话也不会影响 defer 的值

你之前自己学了一些 go,现在又写了一点 php,这两个语言你觉得写起来各自有哪些优劣?各方面都可以讲

刚才你提到了 php 的一些安全机制,你接触过哪些? A:我目前接触过的就是比如说我用那个 webman 框架,在我写,因为 php 它这个框架它用 ORM 是用那个 illuminate 的 eloquent 的 ORM,它是对于比如说我要写库,我想往库里写一个那种类似于数组一样的数据进去的话,它会有一个安全限制,就是它不能直接像 go 里面那样直接用 gORM 那样直接写一个类似于 SQL 的命令,它必须要求你在这个数组里面每一个字段你都要给上一个声明,就是你要去声明它是什么类型的,让这个 ORM 来识别出来它合不合法,来避免一些安全问题,但是它底层可能我没有了解 那 go 这样写的话听上去是不是会有隐患呢?