Jquery中文网 www.jquerycn.cn
Jquery中文网 >  后端编程  >  Go语言  >  正文 276-go语言golang面试题知识点

276-go语言golang面试题知识点

发布时间:2021-04-24   编辑:www.jquerycn.cn
jquery中文网为您提供276-go语言golang面试题知识点等资源,欢迎您收藏本站,我们将为您提供最新的276-go语言golang面试题知识点资源







go面试题






1.go语言中的主要关键字,一共25个
1.程序声明: 
	1.import
	2.package
2.实体声明和定义
	1.chan
	2.const
	3.func
	4.interface
	5.map
	6.struct
	7.type
	8.var
3.流程控制
	1.go
	2.select
	3.break
	4.case
	5.continue
	6.default
	7.defer
	8.else
	9.fallthrough
	10.for
	11.goto
	12.if
	13.range
	14.return
	15.switch









2.go语言类型定义
比如字符串
func main() {

	var a string = "a"

	b := "b"

	var c string
	c = "c"

	var d = "d"

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)

}








3.go语言全局变量定义
var a string = "a"

var b string 

var c = ""

func main() {

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)

}








4.结构体
func main() {

	p1 := Person{"Alice", 20}

	var p2 Person
	p2.name = "Alice"
	p2.age = 20

	fmt.Println(p1)
	fmt.Println(p2)
}

type Person struct {
	name string
	age  int
}









5.通过指针变量访问成员变量
func main() {

	person := Person{"Alice", 20}

	p1 := &person

	fmt.Println(person)
	fmt.Println(&person)
	fmt.Println(&p1)
	fmt.Println(*p1)

	fmt.Println(person.name)
	fmt.Println(&person.name)
	fmt.Println(p1.name)
	fmt.Println(&p1.name)
}

type Person struct {
	name string
	age  int
}









6.格式化输出
%T 类型
%t 布尔
%d 10进制整数
%X 16进制整数
%f 浮点数
%s 字符串
	person := Person{"Alice", 20}
	fmt.Printf("%T\n", person)

	flag := true
	fmt.Printf("%t\n", flag)

	number10 := 99
	fmt.Printf("%d\n", number10)

	//十进制100为16进制的64
	number16:= 0x64
	fmt.Printf("%X\n", number16)
	fmt.Printf("%d\n", number16)

	number0:= 0.123
	fmt.Printf("%f\n", number0)

	str:= "hello world"
	fmt.Printf("%s\n", str)









7.接口
1.一个类如果实现了一个接口的所有函数,
	那么这个类就实现了这个接口







8.init函数
func init() {
    fmt.Println("init 1")
}

func init() {
    fmt.Println("init 2")
}

func main() {
    fmt.Println("main")
}

结果为
init 1
init 2
main







9.多参数函数
func add(args ...int) int {}
这个函数的调用方式有
add(1,2,3)
add([]int{1,2,3}...)








10.类型转换
type MyInt int
var a int = 1
var b MyInt = MyInt(a)








11.引用类型
slice
map
channel






12.main函数
1.main函数不能带参数
2.main函数不能定义返回值
3.main函数所在包必须为main
4.main函数中可以使用flag包来获取和解析命令行参数








13.slice切片初始化
func main() {
	s1 := make([]int, 0)
	s2 := make([]int, 6,10)
	s3 := []int{1, 2, 3, 4, 5}

	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println(s3)

	fmt.Println(len(s2))
	fmt.Println(cap(s2))

}








14.函数定义
func main() {
	r1, r2 := getResult(1, 2)
	fmt.Println(r1)
	fmt.Println(r2)
}

func getResult(a int, b int) (c int, d int) {
	return a   b, a - b;
}

func getResult(a int, b int) (int, int) {
	return a   b, a - b;
}










15.接口
1.如果两个接口有相同的方法列表,那么它们就是等价的,可以相互赋值
2.如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A
3.接口查询是否成功,要在运行期才能够确定










16.同步锁
1.当一个goroutine获得了Mutex后,其他goroutine就只能乖乖的等待,除非该goroutine释放这个Mutex
2.RWMutex在 读锁 占用的情况下,会阻止写,但不阻止读
3.RWMutex在 写锁 占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占







17.channel
1.给一个 nil channel 发送数据,造成永远阻塞
2.从一个 nil channel 接收数据,造成永远阻塞
3.给一个已经关闭的 channel 发送数据,引起 panic
4.从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值









18.channel缓冲
无缓冲的channel是同步的,而有缓冲的channel是非同步的







19.cap函数
1.array
2.slice
3.channel







20.go convey
1.goconvey是一个支持golang的单元测试框架
2.goconvey能够自动监控文件修改并启动测试,并可以将测试结果实时输出到web界面
3.goconvey提供了丰富的断言简化测试用例的编写










21.类型断言
func main() {

	m := make(map[int]interface{})
	m[0] = Person{}
	m[1] = "abc"

	r1, r2 := m[0].(Person)
	r3, r4 := m[1].(string)

	fmt.Println(r1)
	fmt.Println(r2)
	fmt.Println(r3)
	fmt.Println(r4)

}
输出结果为
{}
true
abc
true







22.切片删除元素
func main() {

	s := make([]string, 0)
	s = append(s, "abc0")
	s = append(s, "abc1")
	fmt.Println(s)

	s = append(s[:0], s[0 1:]...)
	fmt.Println(s)

	s = append(s[:0], s[0 1:]...)
	fmt.Println(s)
}








23.json重命名
func main() {

	p1 := Person{"Alice", 20}
	fmt.Println(p1)

	bytes, _ := json.Marshal(p1)
	fmt.Println(string(bytes))

}

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}








24.继承
func main() {

	stu := Student{Person{"Alice", 20}}
	fmt.Println(stu)

}

type Person struct {
	Name string
	Age  int
}

type Student struct {
	Person
}

输出结果
{{Alice 20}}








25.使用for range迭代map时每次迭代的顺序可能不一样,因为map的迭代是随机的
func main() {

	m := make(map[string]int)

	m["string"] = 0
	m["int"] = 1
	m["float"] = 2
	m["bool"] = 3
	m["byte"] = 4

	for k, v := range m {
		fmt.Println(k, ",", v)
	}

}
打印的顺序会出现不一样的情况





26.golang基本数据类型
一共是18个
主要有
1.bool
2.string
3.byte
4.int
5.uint
6.float









27.switch语句
func main() {

	i := rand.Intn(2)

	switch i {
	case 0:
		fmt.Println("get 0")
	case 1:
		fmt.Println("get 1")
	}

}

switch后面可以不跟表达式
func main() {

	i := rand.Intn(2)

	switch {
	case i == 0:
		fmt.Println("get 0")
	case i == 1:
		fmt.Println("get 1")
	}

}









28.
结构体在序列化时非导出变量(以小写字母开头的变量名)不会被encode
所以在decode时这些非导出变量的值为其类型的零值








29.new和make的区别
new的作用是初始化一个指向类型的指针
new函数是内建函数,函数定义: func new(Type) *Type
使用new函数来分配空间
传递给new函数的是一个类型,而不是一个值
返回值是指向这个新分配的地址的指针

看下代码
func main() {

	p := new(Person)
	person := Person{"Alice"}

	fmt.Printf("%T\n",p)
	fmt.Printf("%T\n",person)
}

type Person struct {
	name string
}

输出结果为
*main.Person
main.Person

所以new函数返回的是指针
person是实体









30.make
make的作用是为slice,map或chan初始化
然后返回引用
make函数是内建函数,函数定义: func make(Type,size IntegerType) Type
make(T,args)函数的目的和new(T)不同
仅仅用于创建slice,map,channel,
而且返回类型是实例








31.Printf(),Sprintf(),FprintF()
都是格式化输出,但是输出的目标不一样
Printf是标准输出,一般是屏幕,也可以重定向

Sprintf()是把格式化字符串输出到指定字符串中
func main() {
	person := Person{"Alice"}
	s := fmt.Sprintf("类型是%T\n", person)
	fmt.Println(s)
}

Fprintf()是把格式化字符串输出到文件中
主要用于文件操作

	file, e := os.OpenFile("f:/1.txt", os.O_RDWR, 0777)
	defer file.Close()
	if e != nil {
		fmt.Println(e)
	}

	fmt.Fprintln(file,"hello world")







32.数组和切片的区别
1.数组固定长度
2.数组长度是数组类型的一部分,
所以[3]int和[4]int是两种不同的数组类型
3.数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变
4.数组是值传递

切片
1.切片可变长度
2.切片是轻量级数据结构,三个属性,指针,长度,容量
3.不需要指定大小
4.切片是地址传递(引用传递)
5.可以通过数组来初始化,也可以通过内置函数make()初始化,
初始化的时候len=cap,然后会进行扩容





33.值传递和地址传递(引用传递)
func main() {
	p1 := Person{"Alice"}
	p2 := Person{"Bob"}
	change(p1)
	changeAddress(&p2)

	fmt.Println(p1)
	fmt.Println(p2)
}

func change(p Person) {
	p.name = "Hello"
}

func changeAddress(p *Person) {
	p.name = "Hello"
}

type Person struct {
	name string
}






34.数组和切片传递的区别
先看下类型有啥区别
func main() {

	arr1 := [3]int{1, 2, 3}
	arr2 := []int{1, 2, 3}

	fmt.Printf("%T\n", arr1)
	fmt.Printf("%T\n", arr2)

}
结果为
[3]int
[]int

然后看下传递的时候的区别
func main() {

	arr1 := [3]int{1, 2, 3}
	arr2 := []int{1, 2, 3}

	changeArr(arr1)
	changeSlice(arr2)

	fmt.Printf("%T\n", arr1)
	fmt.Printf("%T\n", arr2)

	fmt.Println(arr1)
	fmt.Println(arr2)
}

func changeArr(arr [3]int) {
	arr[0] = 9
}

func changeSlice(arr []int)  {
	arr[0] = 9
}
结果为
[1 2 3]
[9 2 3]
所以
数组是值传递
切片是引用传递







35.写入文件
	file, err := os.OpenFile("f:/1.txt", os.O_RDWR, 0777)
	defer file.Close()
	if err != nil {
		fmt.Println(err)
		return
	}

	file.WriteString("hello world")






36.切片扩容
func main() {
	arr := make([]int, 0)
	for i := 0; i < 2000; i   {
		fmt.Println("len为", len(arr), "cap为", cap(arr))
		arr = append(arr, i)
	}
}
我们可以看下结果
依次是
0,1,2,4,8,16,32,64,128,256,512,1024
但到了1024之后,就变成了
1024,1280,1696,2304
每次都是扩容了四分之一左右










37.foreach
看下代码
func pase_student() {
	m := make(map[string]*student)
	stus := []student{
		{Name: "zhou", Age: 24},
		{Name: "li", Age: 23},
		{Name: "wang", Age: 22},
	}
	for _, stu := range stus {
		m[stu.Name] = &stu
	}

	fmt.Println(m)
}

看下结果
map[zhou:0xc000004440 li:0xc000004440 wang:0xc000004440]

我们再看一段直观一点的代码
	stus := []student{
		{Name: "zhou", Age: 24},
		{Name: "li", Age: 23},
		{Name: "wang", Age: 22},
	}

	for _, stu := range stus {
		fmt.Printf("%p\n",&stu)
	}
结果是
0xc000052400
0xc000052400
0xc000052400

原因是foreach使用副本的方式,所以&stu实际上
指向的是同一个指针
最终该指针的值是最后一个struct的值的拷贝

正确的方法应该是这样
	for i := 0; i < 3; i   {
		stu:=stus[i]
		fmt.Printf("%p\n",&stu)
	}










38.runtime.GOMAXPROCS
看下代码
func main() {
    runtime.GOMAXPROCS(1)
    wg := sync.WaitGroup{}
    wg.Add(20)
    for i := 0; i < 10; i   {
        go func() {
            fmt.Println("A: ", i)
            wg.Done()
        }()
    }
    for i := 0; i < 10; i   {
        go func(i int) {
            fmt.Println("B: ", i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

先看第一个循环
首先,for循环很快就结束了
然后开启10个go协程
所以输出了10个A:10

再看第二个循环
每次循环把i当做参数传入函数
但是go协程启动的顺序是不一定的
所以输出10个数字,顺序是不一定的








39.组合继承
看一段代码
func (p *People) ShowA() {
    fmt.Println("showA")
    p.ShowB()
}
func (p *People) ShowB() {
    fmt.Println("showB")
}

type Teacher struct {
    People
}

func (t *Teacher) ShowB() {
    fmt.Println("teacher showB")
}

func main() {
    t := Teacher{}
    t.ShowA()
}

首先,调用ShowA方法
那么会输出ShowA
然后People指针调用ShowB方法
这时候这个指针不知道自己是Teacher
所以还是走People的ShowB
所以结果是
ShowA
ShowB









40.select随机性
看一段代码
func main() {
    runtime.GOMAXPROCS(1)
    int_chan := make(chan int, 1)
    string_chan := make(chan string, 1)
    int_chan <- 1
    string_chan <- "hello"
    select {
    case value := <-int_chan:
        fmt.Println(value)
    case value := <-string_chan:
        panic(value)
    }
}

我们看到select中的两个case都满足
那么会随机选择一个来执行
所以程序有可能崩溃,也有可能不会崩溃









41.defer执行顺序
func calc(index string, a, b int) int {
    ret := a   b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    a := 1
    b := 2
    defer calc("1", a, calc("10", a, b))
    a = 0
    defer calc("2", a, calc("20", a, b))
    b = 1
}

结果为
"10" 1 2 3
"20" 0 2 2
"2"  0 2 2
"1"  1 3 4










42.切片
看下代码
func main() {
    s := make([]int, 5)
    s = append(s, 1, 2, 3)
    fmt.Println(s)
}

结果为0,0,0,0,0,1,2,3
make初始化是有默认值的,这里默认值是0








43.线程安全
type UserAges struct {
	ages map[string]int
	sync.Mutex
}

func (ua *UserAges) Add(name string, age int) {
	ua.Lock()
	defer ua.Unlock()
	ua.ages[name] = age
}

func (ua *UserAges) Get(name string) int {
	if age, ok := ua.ages[name]; ok {
		return age
	}
	return -1
}

这段代码有可能会出现
fatal error: concurrent map read and map write

我们可以修改一下
type UserAges struct {
	ages map[string]int
	sync.Mutex
}

func (ua *UserAges) Add(name string, age int) {
	ua.Lock()
	defer ua.Unlock()
	ua.ages[name] = age
}

func (ua *UserAges) Get(name string) int {
    ua.Lock()
    defer ua.Unlock()
    if age, ok := ua.ages[name]; ok {
        return age
    }
    return -1
}











44.chan缓存池
看下代码
func (set *threadSafeSet) Iter() <-chan interface{} {
	ch := make(chan interface{})
	go func() {
		set.RLock()

		for elem := range set.s {
			ch <- elem
		}

		close(ch)
		set.RUnlock()

	}()
	return ch
}

看下完整代码
type threadSafeSet struct {
	sync.RWMutex
	s []interface{}
}

func (set *threadSafeSet) Iter() <-chan interface{} {
	// ch := make(chan interface{}) // 解除注释看看!
	ch := make(chan interface{}, len(set.s))
	go func() {
		set.RLock()

		for elem, value := range set.s {
			ch <- elem
			fmt.Println("Iter:", elem, value)
		}

		close(ch)
		set.RUnlock()

	}()

	return ch
}

func main() {

	th := threadSafeSet{
		s: []interface{}{"1", "2"},
	}
	v := <-th.Iter()
	fmt.Sprintf("%s%v", "ch", v)
}









45.interface内部结构
type People interface {
	Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func live() People {
	var stu *Student
	return stu
}

func main() {
	if live() == nil {
		fmt.Println("AAAAAAA")
	} else {
		fmt.Println("BBBBBBB")
	}
}

我们说一下interface的内部结构
interface分为两种
1.空接口
2.带方法的接口
var people interface{}
type People interface{
	sayHello()
}

底层结构如下:
type eface struct {      //空接口
    _type *_type         //类型信息
    data  unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type iface struct {      //带有方法的接口
    tab  *itab           //存储type信息还有结构实现方法的集合
    data unsafe.Pointer  //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type _type struct {
    size       uintptr  //类型大小
    ptrdata    uintptr  //前缀持有所有指针的内存大小
    hash       uint32   //数据hash值
    tflag      tflag
    align      uint8    //对齐
    fieldalign uint8    //嵌入结构体时的对齐
    kind       uint8    //kind 有些枚举值kind等于0是无效的
    alg        *typeAlg //函数指针数组,类型实现的所有方法
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}
type itab struct {
    inter  *interfacetype  //接口类型
    _type  *_type          //结构类型
    link   *itab
    bad    int32
    inhash int32
    fun    [1]uintptr      //可变大小 方法集合
}

可以看出iface比eface中间多了一层itab结构
itab存储_type信息和[]fun方法集
从上面的结构我们可以看出
data指向了nil
但是并不代表interface是nil
所以返回值不为空
这里的fun方法集定义了接口的接收规则
在编译的过程中需要验证是否实现接口









46.结构体
func main() {
	var p1 Person
	var p2 Person
	p2.name= "Alice"
	p3:=Person{}

	fmt.Println(p1)
	fmt.Println(p2)
	fmt.Println(p3)
}

结果为
{}
{Alice}
{}







47.channel
看下代码
	channel := make(chan int)

	for i := 0; i < 10; i   {
		go func() {
			fmt.Println(i)
			channel <- i
		}()
	}

	for i := 0; i < 10; i   {
		num := <-channel
		fmt.Println("num:", num)
	}

结果是非常随机的








48.go语言同步锁
1.当一个go程获得Mutex,其他的只能等,除非这个go程释放这个Mutex
2.RWMutex在读锁占用的情况下,会阻止写,但不会阻止读
3.RwMutex在写锁占用的情况下,会阻止写,也阻止读







49.go语言
go语言是Google开发的一种
1.静态强类型
2.编译型
3.并发型
4.有垃圾回收功能
的编程语言








50.强类型
强类型指的是程序中表达的任何对象所从属的类型
都必须能在编译时刻确定
常见的强类型语言有
C  ,Java,Python,Golang等
适用于大规模信息系统开发

javascript是弱类型的
比如
var a = 1;
var b = 'a';
console.log(a b);






 

到此这篇关于“276-go语言golang面试题知识点”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
想系统学习GO语言(Golang
Go语言发展历史、核心、特性及学习路线
Go 开发关键技术指南 | 为什么你要选择 Go?(内含超全知识大图)
学习golang开始前的准备工作
276-go语言golang面试题知识点
Golang : 推荐一个 B站up主给想深入理解 Golang 内部实现的人
从零开始学习GO语言-搭建Go语言开发环境-快速开发入门第一个小程序
Go 语言十年而立,Go2 蓄势待发
golang byte转string_golang面试题:怎么避免内存逃逸?
go语言核心编程_Go语言核心编程李文塔

[关闭]