Golang中级面试题

  1. Goroutine 和线程的区别

Goroutine 是 Go 中并发执行函数或方法的方式。它比线程更轻量级,因为它的创建和销毁的代价更低。与线程相比,Goroutine
在运行时的栈空间要小得多,并且可以根据需要动态增长和缩小。同时,Go 运行时也负责在系统线程上调度所有 Goroutine 的执行,这使得
Goroutine 的使用更加方便且高效。

  1. Go 的垃圾回收

Go 使用的是标记清除(Mark and Sweep)的垃圾回收策略,这种策略分为“标记”和“清除”两个阶段。在“标记”阶段,GC
会遍历所有的活动对象(也就是还在使用的对象),然后标记它们。在“清除”阶段,GC 会遍历所有的对象,回收那些未被标记(即未被使用)的对象。从
Go 1.5 开始,Go 的 GC 还实现了并发执行的能力,也就是说,在 GC 执行的时候,应用的 Goroutine
依然可以继续运行,只在必要的时候停下来与 GC 进行同步

  1. Go 中如何使用接口

在 Go 中,接口是定义对象行为的方式,它是一种类型,定义了一组方法,但是没有实现。任何实现了这些方法的类型都被认为实现了该接口。例如:

type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

// Rectangle implements the Shape interface
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

在这个例子中,Rectangle 实现了 Shape 接口,因为它定义了 Area 方法。

  1. 在 Go 中,何时应该使用指针而不是值

当你需要修改一个函数或方法的接收者,或者当你处理的对象很大并且你想要避免复制该对象时,你应该使用指针。另外,由于在 Go
中,所有的函数参数都是按值传递的,所以如果你想让一个函数修改其参数的值,你也需要使用指针。

  1. Go 中的切片(slice)和数组的区别

数组是具有固定长度的值序列,而切片则是对数组中的连续片段的引用。切片的长度可以在运行时改变,这使得它比数组更加灵活。数组和切片都是有序的元素集,但是它们在使用方式和用途上有所不同。例如,如果你知道你需要存储的元素数量,那么可以使用数组。如果你不知道你需要多少元素,或者你需要一个可以改变大小的容器,那么你应该使用切片。

  1. 零值

在 Go 语言中,当我们声明一个变量但不给它赋值时,该变量将会被自动初始化为该类型的零值。各类型的零值如下:
数值类型(int、float64 等)的零值为 0。 布尔类型的零值为 false。 字符串类型的零值为 “”(空字符串)。
指针类型的零值为 nil。 切片、函数、接口、映射(map)和通道(channel)类型的零值也都是 nil。

  1. 错误处理

Go 语言使用了一个特殊的类型,即 error 类型,来处理错误。你可以通过返回 error 类型的值来表示错误。如果一切正常,就返回
nil,否则返回一个包含错误信息的 error 类型的值。例如:

func divide(x, y float64) (float64, error) {
    if y == 0.0 {
        return 0.0, errors.New("Cannot divide by zero")
    }
    return x / y, nil
}
  1. Channel

Channel 是 Go 语言中用于协程间通信的数据结构,它是一种类型安全的队列。Channel
能够用来发送和接收值,并且这些操作是原子性的。在协程间同步或数据传递时会用到。例如:

func run(EX chan bool,TMax int){
    for {
        select {
        case <-EX:
            // 收到停止信号,停止运行任务
            tip("计划任务", "计划任务已退出", "./crontab/back.ico")
            return
        default:
            runTask()
            ONE1:
            for i:=0;i<(3600*TMax);i++{
                select{
                case <-EX:
                    EX<-true
                    break ONE1
                default:
                    time.Sleep(1*time.Second)
                }
                
            }
        }
    }

}
  1. Map

Map 是 Go
语言的一种内建数据类型,它是键值对的无序集合。键可以是任何可比较的类型,值可以是任何类型。例如,map[string]int
是一个键为字符串、值为整数的 map。map 是引用类型,可以使用 make 函数创建。

  1. defer 语句

在 Go 中,defer 语句用来延迟执行一个函数或方法,直到包含该 defer 语句的函数执行完毕。defer
常常用于处理成对的操作,如打开和关闭文件,连接和断开数据库,加锁和解锁等。

  1. 实现继承

Go 语言并没有提供传统的继承机制,而是通过组合和接口来实现代码的重用和多态性。我们可以在结构体中嵌入其他类型,以实现类似继承的效果。

type Animal struct {
    Name string
}

func (a *Animal) Move() {
    fmt.Printf("%s is moving\n", a.Name)
}

type Bird struct {
    Animal  // 嵌套Animal结构体,Bird拥有了Animal的所有字段和方法
    CanFly bool
}

func (b *Bird) Fly() {
    if b.CanFly {
        fmt.Printf("%s is flying\n", b.Name)
    } else {
        fmt.Printf("%s cannot fly\n", b.Name)
    }
}

func main() {
    b := Bird{}
    b.Name = "Sparrow"
    b.CanFly = true
    b.Move()
    b.Fly()
}
  1. 包(package)

在 Go 中,包是代码的集合,它提供了一种将代码组织为逻辑单元的方式。要在代码中使用其他包,可以通过 import
语句导入,然后通过包名来访问其公开的类型、函数等。

  1. range 关键字

range 关键字用于 for 循环中迭代数组、切片、字符串、map 或通道等。range 返回索引和元素值。例如:

nums := []int{1, 2, 3, 4, 5}
for i, num := range nums {
    fmt.Printf("index: %d, value: %d\n", i, num)
}

对于 map,range 返回键和值;对于通道,range 返回通道中的每个值。

  1. Go中如何使用反射:在Go中,反射由reflect包提供。反射可以在运行时检查类型和变量,例如它的值,是否可以被设置,它的地址等等。我们甚至可以修改这些值。举个简单的例子
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
  1. Go的空接口:在Go中,空接口

interface{}不包含任何方法,因此所有类型都实现了空接口。它常被用于容纳任意类型的值。例如

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

func main() {
    describe(42)
    describe("hello")
}
  1. Go中的同步原语:Go提供了一些同步原语,如sync.Mutex和sync.RWMutex,用于在协程之间同步访问资源。以下是一个使用互斥锁的例子
var counter = 0
var lock sync.Mutex

func increment() {
    lock.Lock()
    defer lock.Unlock()
    counter++
    fmt.Println(counter)
}
  1. 只运行一次的代码段:在Go中,sync.Once的Do方法可以保证函数只被调用一次。这常被用于单例模式
var once sync.Once

func getInstance() *Singleton {
    once.Do(func() {
        instance = new(Singleton)
    })
    return instance
}
  1. 实现异步操作:Go中的goroutine(通过go关键字)和channel是实现异步操作的主要方式。例如
ch := make(chan int)

go func() {
    ch <- doSomething() // doSomething()将在新的goroutine中异步执行
}()

result := <-ch // 从ch接收结果
  1. 使用context控制并发:Go的context包可以让你发送取消信号给goroutine,或者设置超时。例如
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()

select {
case result := <-ch:
    fmt.Println("received result", result)
case <-ctx.Done():
    fmt.Println("timeout")
}
  1. Go的内存管理

Go有一个垃圾收集器,会自动释放不再使用的内存。当一个变量不再被引用时,垃圾收集器就会释放它的内存。Go也允许手动管理内存,例如通过使用指针。

  1. Go协程与其他语言的线程

Go的协程是轻量级的,可以在单个操作系统线程上运行多个协程。协程比线程更轻量级,创建和销毁的成本更低,而且可以更好地利用多核。

  1. 面向接口编程

在Go中,接口是定义和组织代码的主要工具。接口定义了一组方法,但是没有实现。任何实现了这些方法的类型都被认为实现了该接口。

  1. 处理panic和recover

Go中的panic函数会停止当前函数的执行,并开始在同一个goroutine中执行panic。recover函数可以捕获到panic,并返回传入panic的值。这样,我们可以在defer函数中使用recover,来捕获和处理panic。

goCopy codefunc main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from", r)
        }
    }()
    panic("a problem occurred")
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我重来不说话

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值