外观
Go语言基础与数据类型
约 2142 字大约 7 分钟
Go数据类型面试题
2025-06-08
1. 什么是Go语言?Go语言有哪些特点?
答案: Go语言是Google开发的一种静态强类型、编译型语言。主要特点包括:
- 简洁的语法
- 高效的编译速度
- 内置并发支持
- 垃圾回收机制
- 跨平台支持
- 丰富的标准库
2. Go语言和Java有什么区别?
答案:
- Go不允许函数重载,必须具有方法和函数的唯一名称,而Java允许函数重载
- 在速度方面,Go的速度要比Java快
- Java默认允许多态,而Go没有传统意义上的多态
- Go语言使用HTTP协议进行路由配置,而Java使用不同的路由机制
- Go代码可以自动扩展到多个核心,而Java并不总是具有这些的可扩展性
- Go语言的继承通过匿名组合完成,支持多继承;而Java的继承通过extends关键字完成,不支持多继承
3. Go是面向对象的语言吗?
答案: 是的,也不是。 原因是:
- Go有类型和方法,并且允许面向对象的编程风格,但没有类型层次
- Go中的"接口"概念提供了一种不同的方法
- Go中的方法比C++或Java中的方法更通用:它们可以为任何类型的数据定义
- Go由于缺乏类型层次,Go中的"对象"比C++或Java等语言更轻巧
4. 什么是面向对象?
答案: 面向对象编程(OOP)是一种基于"对象"概念的编程范式,它可以包含数据和代码:
- 数据以字段的形式存在(通常称为属性性或成员变量)
- 代码以方法的形式存在(通常称为方法)
- 对象自己的程序可以访问并经常修改自己的数据字段
- 对象经常被定义为类的一个实例
- 对象利用属性和方法的私有/受保护/公共可见性,对象的内部状态受到保护
5. nil切片和空切片有什么区别?
答案:
// nil切片
var s1 []int
fmt.Println(s1 == nil) // true
fmt.Println(len(s1)) // 0
fmt.Println(cap(s1)) // 0
// 空切片
s2 := make([]int, 0)
fmt.Println(s2 == nil) // false
fmt.Println(len(s2)) // 0
fmt.Println(cap(s2)) // 0
s3 := []int{}
fmt.Println(s3 == nil) // false
6. 字符串转成byte数组,会发生内存拷贝吗?
答案: 会发生内存拷贝。字符串在Go中是不可变的,当转换为[]byte时,需要分配新的内存空间并复制数据。
7. 翻转含有中文、数字、英文字母的字符串
答案:
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
8. 拷贝大切片一定比小切片代价大吗?
答案: 不一定。切片的拷贝代价取决于切片中元素的类型和数量,而不是切片本身的大小。切片本身只是一个包含指针、长度和容量的结构体。
9. map不初始化使用会怎么样?
答案:
var m map[string]int
m["key"] = 1 // panic: assignment to entry in nil map
对nil map进行写操作会panic,但读操作是安全的,会返回零值。
10. map不初始化长度和初始化长度的区别
答案:
// 不初始化长度
m1 := make(map[string]int)
// 初始化长度
m2 := make(map[string]int, 100)
初始化长度可以减少map扩容的次数,提高性能,但不会限制map的最大容量。
11. map承载多大,大了怎么办?
答案: map的容量理论上只受内存限制。当map变得很大时,可以考虑:
- 分片存储
- 使用更高效的数据结构
- 数据压缩
- 分布式存储
12. map的iterator是否安全?能不能一边delete一边遍历?
答案: map的遍历顺序是随机的,不是线程安全的。可以一边delete一边遍历,但删除的元素可能在后续遍历中出现或不出现。
13. 字符串不能改,那转成数组能改吗,怎么改?
答案:
s := "hello"
bytes := []byte(s)
bytes[0] = 'H'
result := string(bytes)
fmt.Println(result) // "Hello"
14. 怎么判断一个数组是否已经排序?
答案:
func isSorted(arr []int) bool {
for i := 1; i < len(arr); i++ {
if arr[i] < arr[i-1] {
return false
}
}
return true
}
15. 普通map如何不用锁解决协程安全问题?
答案:
- 使用sync.Map
- 使用channel进行串行化访问
- 每个goroutine使用独立的map,最后合并结果
16. array和slice的区别
答案:
- array是值类型,slice是引用类型
- array的长度是固定的,slice的长度是可变的
- array作为函数参数时会发生值拷贝,slice传递的是引用
17. json包变量不加tag会怎么样?
答案: 不加tag时,json包会使用字段名作为json的key,且只有首字母大写的字段才会被序列化。
18. reflect(反射包)如何获取字段tag?为什么json包不能导出私有变量的tag?
答案:
type User struct {
Name string `json:"name"`
age int `json:"age"`
}
func getTag() {
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json")) // "name"
}
json包不能导出私有变量是因为反射无法访问其他包的私有字段。
19. 零切片、空切片、nil切片是什么?
答案:
// nil切片
var s1 []int
// 空切片
s2 := make([]int, 0)
s3 := []int{}
// 零切片(长度为0但容量不为0)
s4 := make([]int, 0, 10)
20. slice深拷贝和浅拷贝
答案:
// 浅拷贝
s1 := []int{1, 2, 3}
s2 := s1
// 深拷贝
s3 := make([]int, len(s1))
copy(s3, s1)
21. map触发扩容的时机,满足什么条件时扩容?
答案: 当负载因子超过6.5时,map会触发扩容。负载因子 = 元素个数 / 桶个数。
22. map扩容策略是什么?
答案:
- 当负载因子过大时,进行增量扩容,桶数量翻倍
- 当overflow bucket过多时,进行等量扩容,重新排列数据
23. 自定义类型切片转字节切片和字节切片转回自定义类型切片
答案:
type MyInt int
func convertSlice() {
// 自定义类型切片转字节切片
ints := []MyInt{1, 2, 3}
bytes := *(*[]byte)(unsafe.Pointer(&ints))
// 字节切片转回自定义类型切片
result := *(*[]MyInt)(unsafe.Pointer(&bytes))
}
24. make和new什么区别?
答案:
- new返回指针,make返回值
- new只分配内存并零值初始化,make会进行初始化
- new可用于任何类型,make只能用于slice、map、channel
25. slice,map,channel创建的时候的几个参数什么含义?
答案:
// slice
make([]int, len, cap) // 类型,长度,容量
// map
make(map[string]int, size) // 类型,初始容量
// channel
make(chan int, buffer) // 类型,缓冲区大小
26. slice,len,cap,共享,扩容
答案:
- len:当前切片的长度
- cap:当前切片的容量
- 多个切片可以共享底层数组
- 当len超过cap时会触发扩容,容量翻倍(小于1024时)或增长25%(大于1024时)
27. 线程安全的map怎么实现?
答案:
- 使用sync.Map
- 使用读写锁保护普通map
- 使用channel串行化访问
28. go slice 和 array 区别
答案:
- slice是动态数组,array是静态数组
- slice是引用类型,array是值类型
- slice有len和cap概念,array只有len
29. go struct能不能比较?
答案: struct可以比较,但有条件:
- 所有字段都是可比较的类型
- 不包含slice、map、function类型的字段
30. map如何顺序读取?
答案:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
31. go中怎么实现set?
答案:
type Set map[string]struct{}
func (s Set) Add(item string) {
s[item] = struct{}{}
}
func (s Set) Contains(item string) bool {
_, exists := s[item]
return exists
}
32. 使用值为nil的slice、map会发生什么?
答案:
- nil slice:读取安全,追加安全,但索引访问会panic
- nil map:读取安全返回零值,写入会panic
33. Golang有没有this指针?
答案: Go没有this指针,但方法接收者类似于this的作用。
34. Golang语言中局部变量和全局变量的缺省值是什么?
答案: 所有变量都会被初始化为其类型的零值:
- 数值类型:0
- 字符串:""
- 布尔:false
- 指针、slice、map、channel、interface:nil
35. Golang中的引用类型包含哪些?
答案: slice、map、channel、interface、function、pointer