本文目录如下:
- Go语言面试题(一)
- 葵花宝典
- 一、基础知识
- 为什么选择 Go语言?
- 浅谈一下Go和Java有什么区别?
- 浅谈一下 Go 和 C++ 有什么区别?
- go build 和 go run 有什么区别?
- Go语言中变量、init函数、main函数的执行顺序?
- Go语言 变量常量 命名规范?
- string 是基础的数据类型吗?基础类型有哪些?
- int 和 int32 是同一个概念吗?
- int32 能表示的数值范围是多少?
- uint32 能表示的数值范围是多少?
- char 型变量中能不能存贮一个中文汉字,为什么?
- byte (unit8) 表示的字符 怎么转换为 int 值?
- 简单介绍一下 rune类型?
- iota 的含义和作用 (枚举?)?
- 如何定义类型别名?
- 如何实现字符串反转?
- 常用的字符串处理函数?
- 哪种字符串拼接方式最高效?
- string 和 []byte 相互转换的方法 (处理字节)?
- string 和 []rune 相互转换的方法 (处理字符)?
- 谈谈 Go语言 中的 nil?
- 浅谈一下 strconv 包?
- 浅谈一下 big 包?
- 浅谈一下 flag 包?
- 浅谈一下 json 包?
- import下划线(_)、点操作(.)的作用?
- new 和 make 的区别?
- 浅谈一下 switch语句?
- 函数支持 默认参数 或 可选参数 吗?
- 函数允许多个返回值吗?
- Go语言里有异常类型吗?
- 如何优雅的把 Go项目 部署到 Linux服务器?
- 二、指针
- 三、容器
Go语言面试题(一)
葵花宝典
自我介绍
模板:我是谁+从哪里来+我做过什么+有什么成绩+为什么能胜任。
- 面试官你好,我是
---
,XX大学21级软件工程专业硕士, 本科毕业于--
大学。学习过Java语言、Spring Boot框架、(Go语言、Gin框架)、MySQL、Redis数据库,有全栈项目开发经验,有多次参加 项目竞赛 并获奖经历、对 后端服务程序开发流程 也比较熟悉。因此决定面试 咱们 公司的开发岗位,希望能获得此次机会,谢谢。
项目难点
- 身份验证: 使用 Token (或 Session + Cookie)。
- (Tips: 我的项目没有啥难点, 但也不能说没有难点啊!)
你还有什么想问我的吗 (反向面试)?
- 公司招的实习生的主要工作内容?
- 公司常用的技术栈是什么?
- (面试中没有答出来的有意思的问题) 刚才的问题怎么回答 (反向狙击)?
一、基础知识
为什么选择 Go语言?
Go语言
代码风格 简洁高效。- Go语言 做了 高并发设计,所以 Go语言 执行效率更高。 Go语言比Java快
- Go语言 被编译为 机器代码 并直接执行,所以 Go语言 更高效。
浅谈一下Go和Java有什么区别?
- 1.
Go语言
代码风格 简洁高效。- 2.Go语言 做了高并发设计,所以 Go语言 执行效率更高。
- 3.
Go语言
有指针,Java
没有指针。- 4.Go语言 允许 多继承, Java 只能 单继承。
- 5.Go语言 不允许函数重载,java 允许 函数重载。
- 6.Go语言 被编译为 机器代码 并直接执行,而 Java 需要使用 JVM 来运行其代码,所以 Go语言 更快。
浅谈一下 Go 和 C++ 有什么区别?
- 1.
Go语言
代码风格 简洁高效。- 2.Go语言 做了高并发设计,所以 Go语言 执行效率更高。
- 3.Go语言 的指针相比 C++ 的指针更安全。
- 4.
Go语言
支持 垃圾回收 功能,C++
需要手动 释放内存。- 5.Go语言 不允许 函数重载,C++ 允许 函数重载。
go build 和 go run 有什么区别?
go build
指令用来 编译 文件,可以将 Go语言程序 与 相关依赖 编译成一个可执行文件 (exe)。
go run
指令将 编译 和 执行 合二为一,会在 编译 后立即 执行程序。
Go语言中变量、init函数、main函数的执行顺序?
- 首先初始化 导入包 的 变量 和 常量
- 然后执行 导入包 的 init函数
- 然后初始化 本包 的 变量 和 常量
- 然后执行 本包 的 init函数
- 最后执行 本包 的 main函数
- 【注: 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)】
Go语言 变量常量 命名规范?
公用
:UpperCamelCase
风格。私有
:lowerCamelCase
风格。常量命名
使用UpperCamelCase
风格,并使用 const 声明。
string 是基础的数据类型吗?基础类型有哪些?
String
不是 基本数据类型,而是一个字节类型切片([]byte)
。
基础类型有 8 种:
- 整数类型:
int
(操作系统位数),int8
(8位),int16
(16位),int32
(32位),int64
(64位)- 无符号整数类型:
int
(操作系统位数),uint8
(8位),uint16
(16位),uint32
(32位),uint64
(64位)- 浮点类型:
float32
(32位),float64
(64位)- 逻辑类型:
bool
(8位)- 文本类型:
byte
(等于uint8,专指字节符),rune
(等于int32,专指文字符)- 地址类型:
uintptr
(无符号的储存指针位置的类型)
int 和 int32 是同一个概念吗?
不是一个概念!
- 如果是 32位 操作系统,int类型的大小就是4个字节。
- 如果是 64位 操作系统,int类型的大小就是8个字节。
int32 能表示的数值范围是多少?
- Go语言 中
int32
是 4个字节(32位)。- 所以 int32 的取值范围是 -231~~231-1 (即:
-2147483648 ~ 2147483647
)- 最小值: math.MinInt32
- 最大值: math.MaxInt32
uint32 能表示的数值范围是多少?
- Go语言 中
uint32
是 4个字节(32位、无符号)。- 所以 int32 的取值范围是 0~~232-1 (即:
0 ~ 4294967295
)
char 型变量中能不能存贮一个中文汉字,为什么?
Go语言 中没有 char 类型,使用 byte 代替。
- 通过 byte 处理 英文字符。(byte == int8 ≈≈ char)
- 通过 rune 处理 中文字符。(rune == int32)
- 英文 和 数字占 一个字节;
- 中文 占一个字符,即 两个字节;
byte (unit8) 表示的字符 怎么转换为 int 值?
myInt32 := '8'
fmt.Printf("myInt32-T: %T, myInt32: %v\n", myInt32, myInt32)
// 输出: myInt32-T: int32, myInt32: 56
fmt.Printf("myInt32-T: %T, myInt32: %c\n", myInt32, myInt32)
// 输出: myInt32-T: int32, myInt32: 8
myByte := byte(myInt32)
fmt.Printf("myByte-T: %T, myByte: %v\n", myByte, myByte)
// 输出: myByte-T: uint8, myByte: 56
myByte2 := myByte - '0'
fmt.Printf("myByte2-T: %T, myByte2: %v\n", myByte2, myByte2)
// 输出: myByte2-T: uint8, myByte2: 8
简单介绍一下 rune类型?
- rune类型 是类型 int32 的别名 (等价于int32),用来区分 字符值 和 整数值。
- Go 语言通过 rune 处理 中文字符。
iota 的含义和作用 (枚举?)?
iota
是 Go语言 的 常量计数器,只能在 常量的表达式 中使用。- iota 在 const 关键字出现时将被重置为 0。
- const 中每新增一行常量声明将使 iota 计数一次(iota可理解为 const 语句块中的 行索引)。
如何定义类型别名?
type myType = Type
(1.9 版本之后的方式。之前版本不带=
符号)
import go_0110_XQZ "helloworld/go-0110-XQZ"
如何实现字符串反转?
- 在Go语言中,需要自己实现 字符串反转 函数, 并且要区分 ASCII字符、UTF-8等字符串。
- 主要是通过 byte切片、rune切片 来实现。
// 反转 ASCII 字符的方法
func reverseBytes(s string) string {
r := []byte(s)
for i := 0; i < len(s); i++ {
r[i] = s[len(s)-1-i]
}
return string(r)
}
常用的字符串处理函数?
strings.Index()
: 字符串查找strings.Contains()
: 字符串查找strings.Replace()
: 字符串替换strings.Compare()
: 字符串比较
哪种字符串拼接方式最高效?
- 在已有字符串数组的场合,使用
strings.Join()
能有比较好的性能。- 如果要拼接 字符串 和 数字 的话,可以考虑
fmt.Sprintf()
。
string 和 []byte 相互转换的方法 (处理字节)?
Go语言中
string
和[]byte
可以直接进行 相互转换。
var str string = "xqz"
var myBytes []byte = []byte(str)
var myStr = string(myBytes)
string 和 []rune 相互转换的方法 (处理字符)?
Go语言中
string
和[]rune
可以直接进行 相互转换。
var str string = "hello 谢清照"
var myRunes []rune= []rune(str)
var myStr = string(myRunes)
谈谈 Go语言 中的 nil?
在go语言中:
- 布尔类型的零值(初始值)为
false
- 数值类型的零值为
0
- 字符串类型的零值为空字符串
""
除此之外其它类型的默认值为
nil
,nil
可以代表下面这些类型的零值:
指针类型
(包括unsafe
中的)map
类型slice
类型function
类型channel
类型interface
类型
浅谈一下 strconv 包?
strconv包
中的函数主要用于 类型转换 和 类型格式化。
浅谈一下 big 包?
math/big
包 实现了 大数字的高精度计算。
浅谈一下 flag 包?
Go语言中的
flag
包中,提供了命令行参数
解析 的功能
浅谈一下 json 包?
json
包 可以用于实现 序列化 和 反序列化。
import下划线(_)、点操作(.)的作用?
下划线(_)
: 引用该包,只调用该包 init()函数,无法调用包中的其他函数。点操作(.)
: 导入该包,在调用这个包的函数时,可以 省略前缀。
new 和 make 的区别?
new
只用于分配内存,返回的是 指向地址的指针。(可用于 基本类型、引用类型、结构体 等)make
只可用于 slice, map, channel 的 初始化 , 返回的是 引用类型。
浅谈一下 switch语句?
switch
默认每次只执行一个case
分支,且是 从上向下 执行。- 只有在 case 中明确添加
fallthrough
关键字,才会继续执行紧跟的下一个 case。- 单个 case 中可以有多个结果选项。(case 1,3,5,7:)
函数支持 默认参数 或 可选参数 吗?
不支持。但是可以利用 结构体参数,或者
…
传入 参数切片 (slice)。
函数允许多个返回值吗?
可以。通常函数除了 返回值 还会返回一个 error。
Go语言里有异常类型吗?
- Go语言 里用
error类型
代替try...catch
语句,这样可以 节省资源。- 如果需要 处理异常, 则需要使用
panic
和recover
。
如何优雅的把 Go项目 部署到 Linux服务器?
set CGO_ENABLED=0 // 禁用CGO
set GOOS=linux // 目标平台为 linux
set GOARCH=amd64 // 目标处理器架构是amd64
go build -o name // 编译可执行文件到当前目录 (-o:自定义文件名)
注意事项:
- go项目 运行之后,修改
.env
文件 不会立即生效,需要重启 go服务才会生效。- go项目 运行之后,修改
config.ini
配置文件 会立即生效,无需重启 go服务。
二、指针
值类型、指针类型、引用类型有什么区别?
值类型
: 变量直接指向存在内存中的 值。指针类型
: 变量指向内存中值所在的 内存地址。引用类型
: 本身是 值类型, 但底层 引用 了 指针类型 的数据结构。
Go语言中 指针运算 有哪些?
- 可以通过
&
取指针的 地址。- 可以通过
*
取指针指向的 数据。
Go语言里的 指针 有哪些 限制?
- Go的 指针 不能进行 数学运算。
- 不同类型的 指针 不能 相互转换、相互赋值。
Go语言中的 引用类型 包含哪些?
数组切片(slice)、字典(map)、通道(channel)、接口(interface)
引用类型
: 例如 切片slice, 本身是 值类型,通过参数传递的 slice 被修改后 不会改变 原 slice 的 属性,但其 内部数组 会被改变,因为 内部数组 是 指针类型。引用类型作参数
简述一下Go语言里的 interface?
Interface
类型 是一种 类型,并且是 引用类型。- Interface类型 可以定义一组 方法,不需要具体实现。(interface 不能包含 变量)
- interface 的 重要作用 在于实现 多态。
简述一下 空接口 interface{})?空接口的应用?
空接口 (interface{})
: 没有定义 任何方法的接口 就是 空接口。- 任何类型 都实现了 空接口, 所以 空接口变量 可以存储 任意类型的值!
空接口 的应用(
interface{}
):
- 1.空接口 类型可以作为 函数的参数 (可以接收 任意类型 的参数)。
- 2.空接口可以作为 map 的 value (可以存储任意值的 map) 。
调用函数传入结构体时,应该传 值 还是 指针?为什么?
- 在不发生 内存逃逸 的情况下,传递指针 不会发生 内存拷贝,效率比 传递值 更高。
- 发生 内存逃逸 ,传递指针 的效率要比 传递值 更慢。
值接收者 和 指针接收者 (语法糖)?
- 值类型 调用者既可以调用 值接收者 的方法,也可以调用 指针接收者 的方法。
- 指针类型 调用者既可以调用 指针接收者 的方法,也可以调用 值接收者 的方法。
简单说一下 unsafe包?
unsafe包
提供了 非类型安全 的指针:unsafe.Pointer
。- 任何类型的 指针 和 unsafe.Pointer 可以 相互转换。
什么是链表?
链表是一种 线性表, 链表中每个 节点 存放的是指向下个 节点地址 的 指针。
三、容器
谈谈你对 Go语言 数组 的理解?
- 在 Go语言 中,数组 是 值类型,赋值 和 函数传参 操作都会 复制整个数组。
谈谈你对 切片(slice) 的理解?
- 在 Go语言 中,
切片(slice)
是 引用类型,底层引用了一个 数组,切片 本身是一个结构体。- 通过参数传递的 slice 被修改后 不会改变 原 slice 的 属性,但其 内部数组 会被改变,因为 内部数组 是 指针类型。
slice 和 数组 有什么区别?
数组
是 值类型。切片(slice)
是 引用类型,底层引用了一个 数组。- 数组的长度不可改变。切片的长度可以改变。
浅谈一下Go slice扩容机制?
- 阶段一:如果新申请的容量大于2倍的旧容量 (
oldcap
), 则最终容量 (nwecap
) 是新申请的容量。- 阶段二:若不满足阶段一
如果旧容量<1024, 则 最终容量 是旧容量的 2倍,即newcap = 2 * oldcap
。
如果旧容量>=1024, 则 最终容量 是旧容量的 1.25倍,直到 newcap > cap 为止。
如何比较两个字符串切片 (slice) 是否相等?
- 使用官方的 reflect包 中的
DeepEqual()
方法,可以用来判断任意 x 和 y 是否相等。
谈一谈 map[string]interface{} 数据格式
map[string]interface{}
: 可以存储任意类型 value 的 map。( interface{} 代表任意类型 )- 数据库查询操作可以使用 map[string]interface{} 或者 []map[string]interface{} 获取 查询结果。
如何判断 map 中 key 是否存在?
获取 key 对应值的时候返回两个参数:
value
和ok
,使用 ok 进行判断 key 是否存在。
if value, ok := theMap["key1"]; ok {
fmt.Printf(value)
} else {
fmt.Println("key1 不存在")
}
map 如何顺序读取?
把 map 中的 key 放入切片,对切片进行排序,遍历切片,通过 key 取值。
map 是线程安全的吗?
- Go语言中 map 不是一个线程安全的。
- 同时读写一个 map 是未定义的行为,如果被检测到,会直接
panic
。