1、介绍一下Golang的特点和优点?
答:Golang是一种由Google开发的开源编程语言,具有并发编程能力强、内存管理优秀、语法简洁、性能高效、代码可读性好、静态类型安全、跨平台支持等特点。同时还有如优点:
内存使用效率高:Golang的垃圾回收机制可以在运行时自动回收不再使用的内存,同时Golang的指针机制也可以减少不必要的内存拷贝,使得Golang的内存使用效率更高。
平台移植性好:Golang可以运行在不同的操作系统和硬件平台上,通过交叉编译可以很容易地将程序部署到不同的环境中。
标准库丰富:Golang的标准库提供了丰富的功能,包括网络编程、加密解密、文本处理、压缩解压、正则表达式等,可以满足大部分应用的需求。
代码风格一致:Golang强制采用一致的代码风格和命名规范,使得不同开发者之间可以更加容易地协作和交流。
开发效率高:Golang的语法简洁,代码可读性好,同时也提供了很多方便的工具和框架,可以提高开发效率和代码质量。
开源社区活跃:Golang是一个开源项目,拥有庞大的开源社区,社区成员可以共享代码、工具和经验,使得Golang的生态系统更加繁荣和稳定。
2、 什么是Go程序的基本语法?
一个Go程序通常以包声明开始,接着是导入语句,然后是主函数(main
)。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
3、Go的基本数据类型有哪些?
Go语言的数据类型可以分为以下四种类型:
(1)基本类型:数字类型、布尔类型、字符串类型。
- 数字类型:
- 整数类型(
int
,uint
) - 浮点数类型(
float32
,float64
) - 复数类型(
complex64
,complex128
)
- 整数类型(
- 布尔类型(
bool
) - 字符串类型(
string、byte、rune
)
(2)聚合类型:数组和结构属于此类型
- 数组
- 结构体(
struct
)
(3)引用类型:指针,切片,map集合,函数和Channel属于此类别。
- 指针
- 切片
- 映射(
map
) - 函数
- channel
(4) 接口类型
4、go 语言类型分类:(参照 数据类型 | Golang 中文学习文档)
(1)布尔类型(bool
)
布尔类型只有真值和假值。
类型 | 描述 |
---|---|
bool | true 为真值,false 为假值 |
提示:在 Go 中,整数 0 并不代表假值,非零整数也不能代表真值,即数字无法代替布尔值进行逻辑判断,两者是完全不同的类型。
(2)整型
Go 中为不同位数的整数分配了不同的类型,主要分为无符号整型与有符号整型。
序号 | 类型和描述 |
---|---|
uint8 | 无符号 8 位整型 |
uint16 | 无符号 16 位整型 |
uint32 | 无符号 32 位整型 |
uint64 | 无符号 64 位整型 |
int8 | 有符号 8 位整型 |
int16 | 有符号 16 位整型 |
int32 | 有符号 32 位整型 |
int64 | 有符号 64 位整型 |
uint | 无符号整型 至少 32 位 |
int | 整型 至少 32 位 |
uintptr | 等价于无符号 64 位整型,但是专用于存放指针运算,用于存放死的指针地址。 |
(3)浮点型
IEEE-754
浮点数,主要分为单精度浮点数与双精度浮点数。
类型 | 类型和描述 |
---|---|
float32 | IEEE-754 32 位浮点数 |
float64 | IEEE-754 64 位浮点数 |
(4)复数类型
类型 | 描述 |
---|---|
complex128 | 64 位实数和虚数 |
complex64 | 32 位实数和虚数 |
(5)字符串类型
go 语言字符串完全兼容 UTF-8
类型 | 描述 |
---|---|
byte | 等价 uint8 可以表达 ANSCII 字符 |
rune | 等价 int32 可以表达 Unicode 字符 |
string | 字符串即字节序列,可以转换为[]byte 类型即字节切片 |
(6)派生类型
类型 | 例子 |
---|---|
数组 | [5]int ,长度为 5 的整型数组 |
切片 | []float64 ,64 位浮点数切片 |
映射表 | map[string]int ,键为字符串类型,值为整型的映射表 |
结构体 | type Gopher struct{} ,Gopher 结构体 |
指针 | *int ,一个整型指针。 |
函数 | type f func() ,一个没有参数,没有返回值的函数类型 |
接口 | type Gopher interface{} ,Gopher 接口 |
通道 | chan int ,整型通道 |
(7)零值
官方文档中零值称为zero value
,零值并不仅仅只是字面上的数字零,而是一个类型的空值或者说默认值更为准确。
类型 | 零值 |
---|---|
数字类型 | 0 |
布尔类型 | false |
字符串类型 | "" |
数组 | 固定长度的对应类型的零值集合 |
结构体 | 内部字段都是零值的结构体 |
切片,映射表,函数,接口,通道,指针 | nil |
(8)nil
nil
类似于其它语言中的none
或者null
,但并不等同。nil
仅仅只是一些引用类型的零值,并且不属于任何类型,从源代码中的nil
可以看出它仅仅只是一个变量。
var nil Type
并且nil == nil
这样的语句是无法通过编译的。
5、 如何在Go中声明变量?
可以使用var
关键字声明变量:
var name string = "John Doe"
6、var
和:= 和 =
有什么区别?
var
用于声明具有明确类型的变量。:=
用于声明并赋值类型由编译器推断的变量。- = 用于变量赋值
var name string = "John Doe"
name := "Jane Doe"
name = "Tom"
7、 如何将字符串转换为整数?
可以使用strconv.Atoi
函数将字符串转换为整数:
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123"
i, err := strconv.Atoi(s)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(i)
}
}
8、.nil
关键字的作用是什么?
nil
表示值的缺失,是一些引用类型的零值。
var name string
fmt.Println(name == "") // true
fmt.Println(name == nil) // false
9、什么是map?如何创建和操作map?如何在map中删除键值对?
答:map是一种哈希表,用于存储键值对。map中的键是唯一的,而值可以重复。
创建map的方式是使用make函数。
可以使用索引和delete函数来操作map。
//使用make函数创建map
m := make(map[string]int)
//使用索引和delete函数来操作map
m["one"] = 1
delete(m, "one")
10、如何检查一个map
中是否包含某个键?
可以使用以下语法检查map
中是否包含某个键:
m := map[string]int{"one": 1}
if _, ok := m["one"]; ok {
fmt.Println("Map contains key")
}
11、len
和cap
有什么区别?
len
返回数组或切片的长度。cap
返回数组或切片的容量。
a := make([]int, 5, 10)
fmt.Println(len(a)) // 5
fmt.Println(cap(a)) // 10
12、append
函数的作用是什么?
append
函数用于向切片中添加一个或多个元素:
s := make([]int, 5)
s = append(s, 1, 2, 3)
13.什么是切片(slice)?它和数组有什么区别?如何创建和操作切片?
答:切片(slice)是一个动态数组,可以在运行时自动扩容。切片和数组的主要区别在于,切片的长度是可变的,而数组的长度是固定的。
创建切片的方式是使用make函数或者通过切片表达式进行创建。切片可以使用索引和切片表达式进行操作,如:s[1:3]表示从切片s中取出下标为1到下标为2的元素。
//使用make 函数创建切片
s := make([]int, 5)
14、Go 中空 struct{} 有哪些用途?
Go语言中的空struct类型struct{}不占用任何内存空间,被称为"空struct"。这种特殊的类型在Go语言中有很多用途,下面是其中一些:
- 作为信号量(signal):一个channel可以用来传递数据,也可以用来传递信号。如果一个channel只是用来传递信号而不传递数据,可以使用空struct作为信号量。
func main() {
ch := make(chan struct{}, 1)
go func() {
<-ch
// do something
}()
ch <- struct{}{}
// ...
}
- 占位符:在一些数据结构中,需要占用一个位置,但并不需要存储实际的数据。这种情况下可以使用空struct作为占位符。
- 集合的key:在Go语言中,可以使用map来实现集合(set)的功能。当key值不重要,只关心是否存在时,可以使用空struct作为map的key。这样可以避免分配额外的空间和降低内存占用。
type Set map[string]struct{}
func main() {
set := make(Set)
for _, item := range []string{"A", "A", "B", "C"} {
set[item] = struct{}{}
}
fmt.Println(len(set)) // 3
if _, ok := set["A"]; ok {
fmt.Println("A exists") // A exists
}
}
- 函数参数:当不需要传递参数时,可以使用空struct作为函数参数,以避免分配不必要的内存。
- 结构体占位符:在定义结构体时,有时候需要为未来添加的字段留出空间。这时可以使用空struct作为占位符,以避免修改已有的代码。
type Lamp struct{}
总之,空struct的主要作用是在不需要实际存储数据的情况下占用空间,从而实现一些特殊的功能。使用空struct可以避免额外的内存分配,提高程序的性能和效率。
15、 go里面的int和int32是同一个概念吗?
不是一个概念。go语言中的int的大小是和操作系统位数相关的,如果是32位操作系统,int类型的大小就是4字节。如果是64位操作系统,int类型的大小就是8个字节。除此之外uint也与操作系统有关,占字节与int类型情况一致。
而int后有数字的话,占用空间大小是固定的:int8占1个字节,int16占2个字节,int32占4个字节,int64占8个字节。
16、如何交换 2 个变量的值?
对于变量而言a,b = b,a; 对于指针而言*a,*b = *b, *a
17、2 个 nil 可能不相等吗?
可能不等。interface在运行时绑定值,只有值为nil接口值才为nil,但是与指针的nil不相等。举个例子:
var p *int = nil
var i interface{} = nil
if(p == i){ fmt.Println("Equal") }
两者并不相同。总结:两个nil只有在类型相同时才相等。
18、如何比较两个切片是否相等?
(1)使用 ==
运算符
直接比较两个切片是否相等,要求长度和元素完全一致:
s1 := []string{"a", "b", "c"}
s2 := []string{"a", "b", "c"}
fmt.Println(s1 == s2) // true(仅当切片指向同一底层数组时有效)
注意:
==
运算符在 Go 中比较的是切片的指针、长度和容量,只有当两个切片指向同一底层数组且内容完全一致时才返回true
。如果底层数组不同但内容相同,==
会返回false
。
(2)使用 reflect.DeepEqual
通过反射包深度比较切片内容:
import "reflect"
s1 := []string{"a", "b", "c"}
s2 := []string{"a", "b", "c"}
fmt.Println(reflect.DeepEqual(s1, s2)) // true
适用场景:需要比较切片内容是否完全一致(包括元素顺序和值)。
(3)自定义比较函数
手动遍历切片比较每个元素:
func equalSlices(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}
s1 := []string{"a", "b", "c"}
s2 := []string{"a", "B", "c"}
fmt.Println(equalSlices(s1, s2)) // false(区分大小写)
扩展性:可以在此基础上添加逻辑,例如忽略大小写、去空格等。
(4)使用第三方库
例如 go-cmp
库的 cmp.Equal
函数:
import "github.com/google/go-cmp/cmp"
s1 := []string{"a", "b", "c"}
s2 := []string{"a", "b", "c"}
fmt.Println(cmp.Equal(s1, s2)) // true
优势:支持更复杂的比较逻辑(如忽略字段、自定义比较器)。
19、go slice是怎么扩容的?
- 1.7版本:
- 如果当前容量小于1024,则判断所需容量是否大于原来容量2倍,如果大于,当前容量加上所需容量;否则当前容量乘2。
- 如果当前容量大于1024,则每次按照1.25倍速度递增容量,也就是每次加上cap/4。
- 1.8版本:Go1.18不再以1024为临界点,而是设定了一个值为256的
threshold
,以256为临界点;超过256,不再是每次扩容1/4,而是每次增加(旧容量+3*256)/4;- 当新切片需要的容量cap大于两倍扩容的容量,则直接按照新切片需要的容量扩容;
- 当原 slice 容量 < threshold 的时候,新 slice 容量变成原来的 2 倍;
- 当原 slice 容量 > threshold,进入一个循环,每次容量增加(旧容量+3*threshold)/4。
20、new和make的区别?
- new只用于分配内存,返回一个指向地址的指针。它为每个新类型分配一片内存,初始化为0且返回类型*T的内存地址,它相当于&T{}
- make只可用于slice,map,channel的初始化,返回的是引用。