go 面试题小结

使用Go编程有什么好处?

1 golang虽然不像jvm那样,一次构建,到处运行。但是golang支持跨平台编译,完全能够适应多平台。

2 内嵌C支持。Go里面也可以直接包含c代码,利用现有的丰富的C库。

3 gofmt,它根据 Go 的建议风格对代码进行格式化。

4 goroutine的轻量级线程,并通过channel进行通信。

5 golang 的调度机制

Go支持类型继承吗?

Go语言中没有类继承,要用多态怎么办?

type shape interface{

    hasArea(name string)bool

}

type square struct {

    length int

}

func (sq square)hasArea(type string) bool {

    if type == "line"

        return false

    else

        return true

}

使用的时候可以这样:

sq := square

fmt.Pringln("Has area?", sq.hasArea())

或者这样

var sq shape

sq = square

fmt.Pringln("Has area?", sq.hasArea())

从上面的例子可以看出,任何能够匹配以上接口中函数定义的结构对象, 
都可以使用这个接口类型。

这样做,就在go语言实现多态了。

Go支持运营商超载吗?

Go支持方法重载吗?

不支持

Go支持指针算术吗?

golang虽说指针变量不能直接参与指针运算, 但是却提供了unsafe包, 能够实现同样的功能。

Golang是否真的没有指针运算

Go支持通用编程吗?

Go编程语言不支持通用/泛型编程

Go是一个区分大小写的语言吗?

区分:例如在方法名上,首字母大写表示方法为public,小写为私有方法

Go中变量的静态类型声明是什么?Go中变量的动态类型声明是什么?

静态类型(static type)是变量声明的时候的声明类型,
在变量声明、new方法创建对象时或者结构体(struct)的元素的类型定义,参数类型等。

接口(interface)类型的变量还有一个动态类型,它是运行时赋值给这个变量的具体的值的类型(当然值为nil的时候没有动态类型)。一个变量的动态类型在运行时可能改变,
这主要依赖它的赋值。

var x interface{}  // x 为零值 nil,静态类型为 interface{}
var v *T           // v 为零值 nil, 静态类型为 *T
x = 42             // x 的值为 42,动态类型为int, 静态类型为interface{}
x = v              // x 的值为 (*T)(nil), 动态类型为 *T, 静态类型为 *T

你能在Go中的单个声明中声明多种类型的变量吗?

var m, n int
a, b := "a", "b";

var(
   no  int
   name string
)

如何在Go中打印变量的类型?

第一种方式,反射:
fmt.Println(reflect.TypeOf(var)) 

第二种方式, %T
var a, b, c = 3, 4, "foo"    
fmt.Printf("a is of type %T\n", a)

什么是指针?

break语句的目的是什么?

break:跳出for循环;break在switch(开关语句)中在执行一条case后跳出语句的作用
continue:跳过当前循环执行下一次循环语句

golang跳转语句goto,break,continue的使用及区别

继续声明的目的是什么?

goto语句的目的是什么?

goto可以跳出多层嵌套循环,
for,while,dowhile本质是用goto语句实现的,goto语句比较底层

除非跳出多个循环嵌套和远程注入技术,否则尽量少用goto

goto会降低程序的可读性,让代码难以调试

利用递归也可以实现循环结构和do while类似

goto语句的本质

解释’for’循环的语法。

https://blog.csdn.net/guofeng93/article/details/90804430

解释在Go中创建函数的语法。

func function_name( [parameter list] ) [return_types] {
   函数体
}

你能从函数中返回多个值吗?

func (file *File) Read(b []byte) (n int, err Error) 

您可以将参数传递给方法的方式有多少?

将参数传递给函数的默认方式是什么?

Go中的函数作为值是什么意思?

在Go中函数也是一种变量,我们可以通过type来定义它,
它的类型就是所有拥有相同的参数,相同的返回值的一种类型

type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])

什么是功能关闭?

定义的或内置的可调用函数

Go中的方法是什么?

Go中局部变量的默认值是多少?

Go中全局变量的默认值是多少?

Go中指针变量的默认值是多少?

在go语言中,任何类型在声明后没有赋值的情况下,都对应一个零值。

整形如int8、byte、int16、uint、uintprt等,默认值为0。
浮点类型如float32、float64,默认值为0。
布尔类型bool的默认值为false。
复数类型如complex64、complex128,默认值为0+0i。
字符串string的默认值为”“。
错误类型error的默认值为nil。
对于一些复合类型,如指针、切片、字典、通道、接口,默认值为nil。
而数组的默认值要根据其数据类型来确定。
例如:var a [4]int,其默认值为[0 0 0 0]。

解释Printf()函数的用途。

格式化输出。

什么是左值和左值?

实际和形式参数之间有什么区别?

变量声明和变量定义有什么区别?

解释模块化编程。

什么是令牌?

哪个关键字用于执行无条件分支?

什么是阵列?

Go中的零指针是什么?

指针上的指针是什么?

Go的结构是什么?

如何在Go中定义一个结构?

Go中的切片是什么?

如何在Go中定义切片?

如何获取切片中存在的元素数?

Go中slice的len()和cap()函数有什么区别?

长度 、容量

如何获得切片的子切片?

Go的范围是什么?

Go中的map是什么?

如何在Go中创建map?

如何从Go中删除map中的条目?

什么是Go中的类型转换?

Go中的接口是什么?

垃圾回收

逃逸分析

除了 mutex 以外还有那些方式安全读写共享变量?

channel

1 无缓冲 chan 的发送和接收是否同步?

ch := make(chan int) 无缓冲的channel由于没有缓冲发送和接收需要同步.

ch := make(chan int, 2) 有缓冲channel不要求发送和接收操作同步

仅当信道的缓冲区填满后,向其发送数据时才会阻塞(阻塞写)。
当缓冲区为空时,接受方会阻塞(阻塞读)

2 channel 是通过注册 goroutine id 实现?

3 如何用channel实现一个令牌桶

go 令牌桶简易实现

令牌桶算法的基本过程如下:

  • 假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中;
  • 假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;
  • 当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main

import (
"fmt"
"sync"
"time"
)

type Bucket struct {
cap int //容量
ch chan bool
timer *time.Ticker //定时填充token
mu sync.Mutex
}

func NewBucket(cap int, interval time.Duration) *Bucket {
bucket := &Bucket{
cap: cap,
ch: make(chan bool, cap),
timer: time.NewTicker(interval),
}
go bucket.startTicker()
return bucket
}

func (bucket *Bucket) startTicker() {
for i := 0; i < bucket.cap; i++ {
bucket.ch <- true
}
for {
select {
case <-bucket.timer.C:
for i := len(bucket.ch); i < bucket.cap; i++ {
bucket.Add()
}
}
}
}

func (bucket *Bucket) Add() {
bucket.mu.Lock()
defer bucket.mu.Unlock()
if len(bucket.ch) < bucket.cap {
bucket.ch <- true
}
}

func (bucket *Bucket) Get() bool {
select {
case <-bucket.ch:
return true
default:
return false
}
}

func main() {

bucket := NewBucket(5, time.Second)
for i := 0; i < 1000; i++ {
time.Sleep(time.Millisecond * 100)
go DoFunc(bucket, i)
}
for {
}
}

func DoFunc(bucket *Bucket, index int) {
if bucket.Get() {
fmt.Printf("######### success : %d \n", index)
bucket.Add() // 每次请求成功后执行Add是否合理???每一时刻最多可有cap个token可用
} else {
fmt.Printf("######### failed : %d \n", index)
}
}

golang 采用什么并发模型?体现在哪里?

Golang并发模型

说说 golang 中常用的并发模式?

JSON 标准库对 nil slice 和 空 slice 的处理是一致的吗?

Go程序能链接C/C++程序吗

为什么Go没有泛型

为什么Go没有异常

因为go开发者认为,所有异常皆可打印。

为什么用CSP思想构建并发如何知道变量分配到堆还是栈

为什么没有goroutine ID

restful熟悉吗?都有哪些请求方法,分别代表什么意思?

手写循环队列进程虚拟空间分布,全局变量放哪里?

c++ 和 go对比

怎么理解云计算

go使用踩过什么坑

go内存模型,什么是小对象,go get,go tool,go test,go vetgo什么情况下会发生内存泄漏?

go为什么高并发好?

Golang 从语言级别支持并发,通过轻量级协程 Goroutine 来实现程序并发运行。

** Goroutine 非常轻量 **,主要体现在以下两个方面:

  • 上下文切换代价小: Goroutine 上下文切换只涉及到三个寄存器(PC / SP / DX)的值修改;
    而对比线程的上下文切换则需要涉及模式切换(从用户态切换到内核态)、
    以及 16 个寄存器、PC、SP…等寄存器的刷新;

  • 内存占用少 :线程栈空间通常是 2M,Goroutine 栈空间最小 2K;

Golang 程序中可以轻松支持10w 级别的 Goroutine 运行,
而线程数量达到 1k 时,内存占用就已经达到 2G。

Go语言 CPU 性能、内存分析调试方法大汇总

pprof

go的分布式

谈谈go的未来

gogc

go的调度

go调度器

go struct能不能比较 空struct{}用处

因为是强类型语言,所以不同类型的结构不能作比较,
但是同一类型的实例值是可以比较的,实例不可以比较,因为是指针类型

map[inter]struct{}

go defer(for defer)

select可以用于什么

常用于gorotine的完美退出
golang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作
每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作    

context包的用途

client如何实现长连接

server是设置超时时间,for循环遍历的

主协程如何等其余协程完再操作

slice,len,cap,共享,扩容

map如何顺序读取

map不能顺序读取,是因为他是无序的,想要有序读取,
首先的解决的问题就是,把key变为有序,
所以可以把key放入切片,对切片进行排序,遍历切片,通过key取值。

实现set

type inter interface{}
type Set struct {
    m map[inter]bool
    sync.RWMutex
}

func New() *Set {
    return &Set{
    m: map[inter]bool{},
    }
}
func (s *Set) Add(item inter) {
    s.Lock()
    defer s.Unlock()
    s.m[item] = true
}

实现消息队列(多生产者,多消费者)

使用切片加锁可以实现

大文件排序

归并排序,分而治之,拆分为小文件,在排序

基本排序,哪些是稳定的

读写锁

http

1 http get跟head

HEAD和GET本质是一样的,区别在于HEAD不含有呈现数据,
而仅仅是HTTP头信息。有的人可能觉得这个方法没什么用,
其实不是这样的。想象一个业务情景:欲判断某个资源是否存在,
我们通常使用GET,但这里用HEAD则意义更加明确。

2 http 401,403

400 bad request,请求报文存在语法错误
401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
403 forbidden,表示对请求资源的访问被服务器拒绝
404 not found,表示在服务器上没有找到请求的资源  

3 http keep-alive

client发出的HTTP请求头需要增加Connection:keep-alive字段
Web-Server端要能识别Connection:keep-alive字段,
并且在http的response里指定Connection:keep-alive字段,
告诉client,我能提供keep-alive服务,
并且"应允"client我暂时不会关闭socket连接

4 http能不能一次连接多次请求,不等后端返回

http本质上市使用socket连接,因此发送请求,接写入tcp缓冲,
是可以多次进行的,这也是http是无状态的原因   

5 tcp与udp区别,udp优点,适用场景

tcp传输的是数据流,而udp是数据包,tcp会进过三次握手,udp不需要

6 time-wait的作用 服务产生大量的time_wait如何解决

https://www.jianshu.com/p/41f7e468f312

如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,
这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,
如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,
此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错

如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,
就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,
必须保证这个ACK完全死掉之后,才能建立新的连接。


在应用中通过设置合理的连接池参数可以避免TIME_WAIT状态过多的问题
比如 http  redis  grpc  mysql...

数据库

1 索引

2 锁

3 事务

幻读,一致性,CAP

4 持久化

redis

1 持久化

2 分布式锁

3 sorted set

mq

1 重复消费

2 保证不丢失消息

孤儿进程,僵尸进程

死锁条件,如何避免

linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程

git文件版本,使用顺序,merge跟rebase