1. map的原理
  • 字典[map]:

初始化: var amap map[string]string, 如果需要赋值,需要先make 一下
testMap := make(map[string]string)

Go中的字典是引用类型,但Golang中是没有引用传递的,均为值传递。这意味着传递的是数据的拷贝,所以map 也存在线程安全的问题

底层结构是hmap,hmap 的结构体里有一个buckets,他是一个指针,指向了一个bucket的结构体,bucket可以看错一个链表,bucket 里存放着k&&v 的数组,以及指向下一个bucket的指针。

golang 常见面试基础(2)_golang

所以当hmap收到了key,会进行hash运算,然后基于运算结果找到对应的bucket里的数组,然后找到对应的Key

  1. make和new的区别
  • new:用来初始化一个对象,并且返回该对象的首地址.其自身是一个指针.可用于初始化任何类型
  • make:返回一个初始化的实例,返回的是一个实例,而不是指针,其只能用来初始化:slice,map和channel三种类型
  1. go的GC 机制
  • 标记-清除模式: 从根变量开始遍历所有引用的对象,引用的对象标记为"被引用",没有被标记的进行回收。
  1. defer的执行顺序

    多个defer 执行顺序类似堆栈,后进先出,defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

如果函数的返回值是无名的(不带命名返回值),则go语言会在执行return的时候会执行一个类似创建一个临时变量作为保存return值的动作,而有名返回值的函数,由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存操作,而后续的defer函数会改变这个返回值(虽然defer是在return之后执行的,但是由于使用的函数定义的变量,所以执行defer操作后对该变量的修改会影响到return的值

package main

import (
    "fmt"
)

func haveReturnName() (a int) {
    defer func() {
        a++
    }()
    return a
}

func haveNoReturnName() (int) {
    var b int
    defer func() {
        b++
    }()
    return b
}

func main() {
    fmt.Println(haveReturnName())
    fmt.Println(haveNoReturnName())
}
  1. recover && panic

    go没有 try, expect,finnally 之类的异常捕捉方法,通过defer 判断是否捕捉到recover,如果没有则返回nil,如果recover捕获到panic,则处理。
package main

import "fmt"

func prTest() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("panic recover! %v",r)
        }
    }()

    for i := 0; i < 5; i++ {
        if i == 3 {
            panic(fmt.Sprintf("%v",i))
        }
        fmt.Println(i)
    }
}
func main() {
    prTest()
}
  1. golang 如何处理大文件?
  • 切片(数据流),适合二进制文件,没有换行符的,非常大的http返回
package main

import (
    "os"
    "fmt"
)

func readBigFile(fileName string) {
	f, err := os.Open(fileName)
	if err != nil {
		fmt.Println("can't opened this file")
		panic(err.Error())
	} 
    defer f.Close()
	s := make([]byte, 1024)
    for {
        switch nr, _ := f.Read(s[:]); true{
        case nr > 0:
            fmt.Println(nr)
        case nr <= 0: //EOF
            return 
        }
    }
}

func main() {
    readBigFile("loutong.go")
}
  • 逐行读取
package main

import (
	"bufio"
	"fmt"
	"os"
	"io"
)

func readBigFile(fileName string) {
	f, err := os.Open(fileName)
	defer f.Close()
	if err != nil {
		panic(err.Error())
	}
	buf := bufio.NewReader(f)

	for {
		c, _, err := buf.ReadLine()
		fmt.Printf("%s\n",c)
		if err == io.EOF {
			return
		}
		if err != nil && err != io.EOF {
			fmt.Printf("读取错误")
			return
		}
	}
}

func main() {
	readBigFile("loutong.go")
}
  1. go的context作用
  • 批量协程的关闭
  • 协程的超时处理
package main

import (
    "context"
    "time"
    "fmt"
)

func ctxCancelThing() {
    ctx, cancel := context.WithCancel(context.Background())
    go doSomething(ctx)

    time.Sleep(10 * time.Second)
    cancel()
}

func ctxTimeoutThing() {
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5 * time.Second))
    go doSomething(ctx)

    time.Sleep(10 * time.Second)
    cancel()
}

func doSomething(ctx context.Context) {
    for {
        time.Sleep(1 * time.Second)
        select {
        case <-ctx.Done():
            fmt.Printf("done")
            return
        default:
            fmt.Printf("work")
        }
    }
}

func main() {
    ctxCancelThing()
    ctxTimeoutThing()
}
  1. 谈谈go的协程泄露
    一个原则,在不知道如何关闭一个协程的情况下,永远不要启动一个goroutine。

  2. select 和 switch的区别
    select 不带判断条件,主要是用于判断IO操作的,确切的说,应该是一个面向channel的IO操作

  3. go语言中布尔类型的缺省值是什么?
    False