golang面试题整理

1.struct结构体能不能比较

结构体不可以比较,但是同一类型的结构体的值可以比较是否相等的(不可以比较大小):

  • 结构体所有字段的值都相等,两个结构体才相等
  • 比较的两个结构体必须是相同类型才可以,也就是说他们字段的顺序、名称、类型、标签都相同才可以
2. defer的顺序

先注册,后执行;后注册,先执行。

3.select用法

select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。类似linux中的select,io多路复用机制。可以用作协程的退出。

//select基本用法
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程

注意事项:

  • 如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行。
  • 所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。
  • break关键字结束select。
4.waitgroup和context

waitgroup和context都可以用控制并发,waitgroup类似linux中的waitpid,使用如下:

func main() {
	var wg sync.WaitGroup

	wg.Add(2)
	go func() {
		time.Sleep(2*time.Second)
		fmt.Println("1号完成")
		wg.Done()
	}()
	go func() {
		time.Sleep(2*time.Second)
		fmt.Println("2号完成")
		wg.Done()
	}()
	wg.Wait()
	fmt.Println("好了,大家都干完了,放工")
}

context

  • context可以用来跟踪goroutine,比如有一个网络请求Request,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他的goroutine。这样的话, 我们就可以通过Context,来跟踪这些goroutine,并且通过Context来控制他们的目的,这就是Go语言为我们提供的Context,中文可以称之为“上下文”。
  • 另外一个实际例子是,在Go服务器程序中,每个请求都会有一个goroutine去处理。然而,处理程序往往还需要创建额外的goroutine去访问后端资源,比如数据库、RPC服务等。由于这些goroutine都是在处理同一个请求,所以它们往往需要访问一些共享的资源,比如用户身份信息、认证token、请求截止时间等。而且如果请求超时或者被取消后,所有的goroutine都应该马上退出并且释放相关的资源。这种情况也需要用Context来为我们取消掉所有goroutine
5.生产者消费者模型实现

用channel实现生产者消费者模型

func produce(ch chan<- int) {
	for i := 0; i < 10; i++ {
		ch <- i
		fmt.Println("Send:", i)
	}
}
 
func consumer(ch <-chan int) {
	for i := 0; i < 10; i++ {
		v := <-ch
		fmt.Println("Receive:", v)
	}
}

ch = make(chan int) //无缓冲
ch = make(chan int) //有缓冲

用互斥锁和条件变量实现的生产者和消费者模型:

lock := new(sync.Mutex)
cond := sync.NewCond(lock)
var count int

func producer() {
	for i:=0;i<10;i++ {
		lock.Lock()
		count++
		cond.Signal()
		lock.Unlock()
	}
}

func consumer(){
	for i:=0;i<10;i++{
		lock.Lock()
		while(count == 0){
			cond.Wait()
		}
		count--
		lock.Unlock()
	}
}
6.new和make的区别

new 的作用是初始化一个指向类型的指针(T),使用new函数来分配空间。传递给new 函数的是一个类型,不是一个值。返回值是 指向这个新分配的零值的指针。
make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。
make(T, args)函数的目的与new(T)不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T
)的一个初始化的(不是零值)的实例。

7.go触发异常的场景

空指针解析,数组越界访问,除数为0,调用panic函数 。

8.异常处理机制panic defer recover

panicing:F函数出现panic后,终止当前函数,并调用其defer函数,然后在终止调用F函数的函数并调用其defer函数,直至goroutine退出。
panic的处理:在defer函数中调用recover获得错误值,再处理错误。

9.函数变长参数使用
10.类型转换

type类型不能直接转换,只能强制转换比如:

type Myint int

var a int
var b Myint

a = b //错误
a = int(b) //正确
10.append,delete用法

delete用来删除map中的元素

11.接口方法集调用规则
  • 类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
  • 类型 T 的可调用方法集包含接受者为 T 的所有方法
  • 类型 T 的可调用方法集不包含接受者为 *T 的方法
12.函数内变量在函数结束后并不释放,有垃圾回收器释放
13.协程池实现
import "sync"

type Woker interface {
	Work()
}

type Pool struct {
	work chan Worker
	wg sync.WaitGroup
} 

func NewPool(maxRoutine int) *Pool {
	p := Pool{
		work: make(chan Worker)
	}
	p.wg.Add(maxRoutine)
	for i:=0;i<maxRoutine;++i {
		go func(){
			for v := range work {
				v.Work()
			}
			p.wg.Done()
		}()
	}
	return &p
}

//提交工作到池子
func (p *Pool) Run(w Worker) {
	p.work <- w
}

//关闭池子
func (p *Pool) Close() {
	close(p.work)
	p.wg.Wait()
}
14.go类似多态实现
package main

import "fmt"

type AnimalIF interface {
    Sleep()
    Age() int
    Type() string
}
type Animal struct {
    MaxAge int
}

/*=======================================================*/

type Cat struct {
    Animal Animal
}

func (this *Cat) Sleep() {
    fmt.Println("Cat need sleep")
}
func (this *Cat) Age() int {
    return this.Animal.MaxAge
}
func (this *Cat) Type() string {
    return "Cat"
}

/*=======================================================*/

type Dog struct {
    Animal Animal
}

func (this *Dog) Sleep() {
    fmt.Println("Dog need sleep")
}
func (this *Dog) Age() int {
    return this.Animal.MaxAge
}
func (this *Dog) Type() string {
    return "Dog"
}

/*=======================================================*/

func Factory(name string) AnimalIF {
    switch name {
    case "dog":
        return &Dog{Animal{MaxAge: 20}}
    case "cat":
        return &Cat{Animal{MaxAge: 10}}
    default:
        panic("No such animal")
    }
}

/*======================================================*/

func main() {
    animal := Factory("dog")
    animal.Sleep()
    fmt.Printf("%s max age is: %d", animal.Type(), animal.Age())
}
15.panic和err的区别

处理错误并且在函数发生错误的地方给用户返回错误信息:照这样处理就算真的出了问题,你的程序也能继续运行并且通知给用户。panic and recover 是用来处理真正的异常(无法预测的错误)而不是普通的错误。
当发生像数组下标越界或类型断言失败这样的运行错误时,Go 运行时会触发运行时 panic,伴随着程序的崩溃抛出一个 runtime.Error 接口类型的值。这个错误值有个 RuntimeError() 方法用于区别普通错误。

15.go语言实现单例模式

双重检验加锁

type Singleton struct{
}

var ins *Singleton
var mutex sync.Mutex
func GetIns() *Singleton{
	if ins == nil {
		mutex.Lock()
		if ins == nil {
			ins = &Singleton{}
		}
		mutex.Unlock()
	}
	return ins
}

sync.Once 实现

type Singleton struct{}

var ins *Singleton
var once sync.Once

func GetIns *Singleton {
	once.Do(func() {
		ins = &Singleton{}
	})
	return ins
}

16.go实现简单工厂
package main
import (
    "fmt"
)
type Op interface {
    getName() string
}
type A struct {
}
type B struct {
}
type Factory struct {
}
func (a *A) getName() string {
    return "A"
}
func (b *B) getName() string {
    return "B"
}
func (f *Factory) create(name string) Op {
    switch name {
    case `a`:
        return new(A)
    case `b`:
        return new(B)
    default:
        panic(`name not exists`)
    }
    return nil
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值