面试题
= 和 := 的区别
前者赋值,后者声明+赋值。
指针的作用
指针用来保存变量的地址。
Go 有异常类型吗?
Go 没有异常类型,只有错误类型(Error),通常使用返回值来表示异常状态。
什么是 Goroutine
Goroutine 是与其他函数或方法同时运行的函数或方法。 Goroutines 可以被认为是轻量级的线程。 与线程相比,创建 Goroutine 的开销很小。 Go应用程序同时运行数千个 Goroutine 是非常常见的做法。
defer 的执行顺序
多个 defer 语句,遵从后进先出(Last In First Out,LIFO)的原则,最后声明的 defer 语句,最先得到执行。 defer 在 return 语句之后执行,但在函数退出之前,defer 可以修改返回值。
Go 语言 tag 的用处?
tag 可以理解为 struct 字段的注解,可以用来定义字段的一个或多个属性。框架/工具可以通过反射获取到某个字段定义的属性,采取相应的处理方式。tag 丰富了代码的语义,增强了灵活性。
new和make的区别
new的作用是初始化一个指向类型的指针(*T)
new函数是内建函数,函数定义:func new(Type) *Type
使用new函数来分配空间。传递给new
函数的是一个类型,不是一个值。返回值是指向这个新分配的零值的指针。
make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。
make函数是内建函数,函数定义:func make(Type, size IntegerType) Type
- 第一个参数是一个类型,第二个参数是长度
- 返回值是一个类型
make(T, args)
函数的目的与new(T)
不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。
init函数的特性
- init 函数在main函数前执行,不能被其他函数调用
- init 函数没有输入参数、返回值
- 每个包可以有一个或多个init函数
- 同一个包的init执行顺序,golang没有明确定义,编程时注意不要依赖这个执行顺序
- 不同包的init函数的执行顺序依照包导入的依赖关系决定执行顺序
- import –> const –> var –> init() –> main()
通道特性
- 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的(并发安全)
- 发送操作和接收操作中对元素值的处理都是不可分割的。
- 发送操作在完全完成之前会被阻塞,接收操作也是一样。
- 对于缓冲通道:如果通道已满,那么对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走;如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。
- 对于非缓冲通道:无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。
注意点:
关闭通道要在发送方关闭,关闭后如果channel内还有元素,并不会对接下来的接收产生影响 单向通道最主要的用途就是约束其他代码的行为 通过函数的参数类型或者返回值类型来限制(Go的语法糖)。
数组与切片的区别?
数组
数组是具有固定长度且拥有零个或者多个相同数据类型元素的序列。 数组的长度是数组类型的一部分,所以[3]int 和 [4]int 是两种不同的数组类型。
数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变 ;
数组是值传递;
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。
数组定义:
var array [10]int
var array = [5]int{1,2,3,4,5}
切片
切片表示一个拥有相同类型元素的可变长度的序列。 切片是一种轻量级的数据结构,它有三个属性:指针、长度和容量。
切片不需要指定大小;切片是地址传递;切片可以通过数组来初始化,也可以通过内置函数make()初始化 .初始化时len=cap,在追加元素时如果容量cap不足时将按len的2倍扩容;
切片定义:
var slice []type = make([]type, len)
解释以下命令的作用?
go env: #用于查看go的环境变量
go run: #用于编译并运行go源码文件
go build: #用于编译源码文件、代码包、依赖包
go get: #用于动态获取远程代码包
go install: #用于编译go文件,并将编译结构安装到bin、pkg目录
go clean: #用于清理工作目录,删除编译和安装遗留的目标文件
go version: #用于查看go的版本信息
Go 语言的局部变量分配在栈上还是堆上?
由编译器决定。Go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有超出函数范围,就可以在栈上,反之则必须分配在堆上。
go语言中的引用类型包含哪些?
切片(slice)、字典(map)、通道(channel)、接口(interface)
go语言中指针运算有哪些?
可以通过“&”取指针的地址,可以通过“*”取指针指向的数据
什么是协程泄露(Goroutine Leak)?
协程泄露是指协程创建后,长时间得不到释放,并且还在不断地创建新的协程,最终导致内存耗尽,程序崩溃。常见的导致协程泄露的场景有以下几种:
- 缺少接收器,导致发送阻塞
- 缺少发送器,导致接收阻塞
- 死锁(dead lock)
- 无限循环(infinite loops)
说说进程、线程、协程之间的区别
进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元;同一个进程中可以包括多个线程;进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束。
线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程;进程的创建调用fork或者vfork,而线程的创建调用pthread_create;线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源。
进程是资源分配的单位,而线程是操作系统调度的单位。
进程切换需要的资源很最大,效率很低;线程切换需要的资源一般,效率一般;协程切换任务资源很小,效率高。
多进程、多线程根据cpu核数不一样可能是并行的 也可能是并发的。协程的本质就是使用当前进程在不同的函数代码中切换执行,可以理解为并行。 协程是一个用户层面的概念,不同协程的模型实现可能是单线程,也可能是多线程。
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。(全局变量保存在堆中,局部变量及函数保存在栈中)
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是这样的)。
协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。
协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
参考
关注和赞赏都是对小欧莫大的支持! 🤝 🤝 🤝