目录

Go开发面试

什么叫形参,什么是实参

前者是定义函数的时候,指定需要传入的数据,后者是调用函数的时候,实际传递进去的数据。

函数传递参数,用数组还是切片,为什么

很少直接用数组,如果这个数组很大,因为函数传递值是用值传递,也就是会继续复制一个新的数组,如果数组很大,那么在内存里会有好几份,不够分的,slice 的话,传的只有指针的地址,8个字节*3,代价小,但是如果指针指向的值改变了,函数传递的时候就要很小心了。

操作不同状态的chan会有几种情况

panic:

  1. 向已关闭的chan写入数据会panic
  2. 重复关闭chan会panic

阻塞:

  1. 向未初始化的chan读写数据会导致当前goroutine永久阻塞
  2. 向缓冲区满的chan写入数据会阻塞
  3. chan没数据,读取chan会阻塞

非阻塞:

  1. 读取已关闭的chan不会阻塞,会立即返回通道元素的零值
  2. 向有缓冲但没满的通道读写不会阻塞

描述一下接口初始化的过程

接口的实例化过程中,编译器通过特定的数据接口描述这个过程。非空接口的底层数据类型iface,其包含两个指针类型的字段,itabdata,前者用来存放接口自身类型和绑定的实例类型以及实例相关的函数指针,后者指向绑定的实例的副本,接口的初始化也是一种值拷贝

接口方法的调用和普通的函数调用有什么区别

具体的地址是在运行时决定的,有一定的开销。

一句话概括一下Go的接口设计

非侵入式的设计,一个具体类型实现接口不需要在语法上显示声明,只要具体类型的方法集是接口方法集的超集就代表类型实现了接口。

字面量和命名类型

我理解字面量是赋值的过程,命名类型是定义的过程。

什么是匿名字段

如果定义 struct 过程中,字段只给出字段类型,没有给出字段名,则成为匿名类型,如下

1
2
3
type File struct {
	*file
}

什么叫运行时错误

运行期的错误检测包括空指针、数组越界等,此类错误一般通过自动调用 panic,并且在写代码调试过程中可以通过主动调用来快速报错,定位问题。

聊一下你认为的Go的错误处理

Go 有两种处理:

  1. 运行时错误,runtime errors,这种错误在运行时能够捕获,并采取措施,隐式或显式抛出panic
  2. 程序逻辑错误,程序执行结果不符合预期,但不会引发运行时错误,error

Go 对于错误有两种处理机制:

  1. 通过函数返回错误类型的值来处理错误
  2. 通过panic打印程序调用栈,终止程序

recover是怎么用的

recover() 是用来捕获 panic 的,组织 panic 向上传递,一般和 defer 一起使用,但是 recover() 只有在 defer 后面的函数体内被直接调用才能捕获 panic 终止异常,否则返回 nil,继续向外传递。

讲一下panic

panic 一般有两种情况:

  1. 主动调用
  2. 运行中被检测出错误

讲讲Go的闭包

闭包是函数以及相关引用环境组合而成的实体,一般通过匿名函数引用外部函数的局部变量或者全局变量构成。

考虑一个场景,如果函数返回的闭包引用了该函数的局部变量(参数函数内部的变量),会有以下两种可能:

  1. 多次调用函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量在堆上分配内存
  2. 用一个闭包函数多次,如果闭包修改了其引用的外部变量,则每次调用该闭包对外部变量都会有影响,因为闭包函数是共享外部引用的

闭包有点麻烦,但也不是完全没有价值,最初的目的是为了减少全局变量,在函数调用过程中隐式地传递共享变量,但是坏处就是不够直接,除非非常有价值,否则不建议用闭包。

讲讲defer关键字

用于注册延迟调用,调用以先进后出 FILO 的顺序在函数返回前执行,在有很多 return 语句的场景很有用,副作用也有,尽量不放在循环。

函数是传到形参的传递是什么方式

值传递,函数调用后实参指向的值变了,是因为参数传递的是值指针的拷贝,实参是一个指针变量,传递给形参的是这个指针变量的副本,两者指向同一个地址,本质上参数传递依然是值拷贝。

什么是Go的第一公民,为什么

函数。

  1. 函数是一种类型
  2. 支持多值返回
  3. 支持闭包
  4. 函数支持可变参数

但函数不支持重载,不支持默认参数,也不支持嵌套定义(匿名函数除外)。

标签是什么语法

Lable 用于 goto break continue 语句的跳转。

if结构中的fallthrough有什么用

强制执行下一个 case 子句,不再判断下一个 case 子句的条件是否满足。

map是并发安全的吗

并不是,如果要用并发安全的 map,需要用 sync 包的 map。

切片怎么创建

  1. 通过数组创建,array[1:]
  2. 通过内置函数make

数组和切片的区别,简单说说

数组是定长的,而且是值拷贝,相对来说限制了使用场景,slice 作为变长数组,数据结构里有指向数组的指针,是一种引用类型。

Go的指针支持运算吗,为什么

不支持,因为涉及到指针运算会对垃圾回收算法提供诸多难题,所以设计简单的 Go 是不允许指针运算的。

知道rune类型是啥吗

Go 内置了两种字符类型,一种是 byte 的字节类类型,另一种是表示 Unicode 编码的字符 rune,前者 byte 是 uint 的别名,后者是 int32 类型的别名,占用4个字节一共32位 bit。

下面的操作会有什么问题

1
2
a := "一个很大的字符串"
b := []byte(a)

经常这样转换需要考虑成本,因为每次转换都需要进行复制。

能否手写一个简单的go程序,并保证不会出错

别看这个简单,考验的都是基本细节…

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
	fmt.Println("hello world!")
}

Go是静态语言还是动态语言,怎么区分

  1. 静态语言
  2. 强类型
警告
本文最后更新于 2017年2月1日,文中内容可能已过时,请谨慎参考。