切换导航
{{systemName}}
{{ info.Title }}
{{info.Title}}
{{ menu.Title }}
{{menu.Title}}
登录
|
退出
搜索
golang面试题
作者:ych
### 常见问题 ### 重点知识 #### goroutine and channels 通讯进程解决过程中,go提供了简单的模型解决基本的并发,而go语言最主要的特点就是通道和携程。 ##### 携程(goroutine) ##### 通道(channels) 它就像unix管道,一端发送,另一端接收,按照先后顺序,直到发送端关闭。(需要处理防止多人并发读写) #### 锁 #### 切片、数组和map #### 扩展知识 ##### restful熟悉吗?都有哪些请求方法,分别代表什么意思? 答: >`restful`是符合`rest`架构风格的网络API接口,完全承认`Http`是用于标识资源。`restFul URL`是面向资源的,可以唯一标识和定位资源。 对于该`URL`标识的资源做何种操作是由Http方法决定的。`rest`请求方法有4种,包括`get`,`post`,`put`,`delete`分别对应获取资源,添加资源,更新资源及删除资源. **扩展问题及答案** 为何很多公司不完全使用`restful`编写接口呢? 答: >简单说,`RESTful`是一个设计范式,不是强制规范,既不像被吹捧的那么好,也没被骂的那么糟糕。 最有说服力的正面例子,请去仔细阅读[谷歌云API设计指导](https://cloud.google.com/apis/design "谷歌云API设计指导"),看明白了再回来骂不迟。 如果把`REST`等同于`HTTP`,那么问题就在于表现力受限于`HTTP`。`Google`的解决方案是大多数符合CRUD的需求都可以对应到`HTTP`方法,剩下的自定义方法。 由于业务的复杂度确定了我们的设计范式不能是简单的一个Controller只有一个`get`或`post`来实现。 Rest风格与MVC风格对比 >MVC风格的出现将模型、视图、控制解耦,其亮点是从前到后的一致性,其结构简洁、逻辑清晰,易于扩展和增强。MVC风格偏重于解决服务器端的逻辑分层,客户端是逻辑分层的延伸。MVC的标签库,虽然其形态已经和HTML页面融合, 但本质上还是编写的装饰模式的类实例,对应的是服务器端编写的模型类或者控制器类,因此MVC很难实现跨语言解耦。 而REST风格偏重于统一接口,因此具体实现就可以跨平台和跨语言。MVC和REST式并不是互斥的,像Spring的MVC模块已经支持REST式的开发。 #### 编程语言类灵活答案,理解就行 ##### Go是什么? Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。 Go 语言语法与 C 语言相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。 简答:Go是一种编程语言。 ##### 使用Go编程有什么好处? 简单、快速、GC机制、内存安全、并发(加go 天然并发)、开源、打包方便(交叉编译、copy)、引包方便(go mod)、包小不需要虚拟机(占用内存小)、国人推崇他,由谷歌大神编写(出身名门、血统纯正)、薪资高(最重要)。 ##### Go支持类型继承吗? 答:不支持继承。 go语言可以`嵌入类型`实现复用的目的。 解释: 实际上,类型嵌入不是继承。它只是某种形式上的语法糖而已。在面向对象编程中,子类应该是可以被当做父类来使用的。在里氏替换原则中,子类应该能在任何需要的地方替换掉父类。 深层分析: [出处](https://my.oschina.net/u/1029640/blog/3053063 "出处") 在Go中有一种叫做Embedding的东西。在网上的一些文章中,他们说这就是Go中实现继承的方式。可是看起来,这更像是Java中的组合,至少语法上像,是不? ``` package main import ( "fmt" ) func main() { artisticUser := &ArtisticUser{User: &User{name: "Chris"}} artisticUser.sayName() artisticUser.sayType() } type User struct { name string } func (u *User) sayHi() { u.sayName() u.sayType() } func (u *User) sayName() { fmt.Printf("I am %s.", u.name) } func (u *User) sayType() { fmt.Println("I am a user.") } type ArtisticUser struct { *User } func (u *ArtisticUser) sayType() { fmt.Println("I am an artistic user.") } ``` 结果 ``` I am Chris.I am an artistic user. ``` 干得漂亮!这样我就可以复用User的sayName方法,只要把sayType方法用我自己的逻辑实现就好了。这正是我想要的。 那么他是继承?组合? 但是,少侠请留步!我们试一下sayHi方法看看会发生什么? ``` package main import ( "fmt" ) func main() { artisticUser := &ArtisticUser{User: &User{name: "Chris"}} artisticUser.sayHi() } type User struct { name string } func (u *User) sayHi() { u.sayName() u.sayType() } func (u *User) sayName() { fmt.Printf("I am %s.", u.name) } func (u *User) sayType() { fmt.Println("I am a user.") } type ArtisticUser struct { *User } func (a *ArtisticUser) sayType() { fmt.Println("I am an artistic user.") } ``` 结果 ``` I am Chris.I am a user. ``` 这不科学!在Java里,子类总是会调用自己的方法的(已经override了父类的方法)。除非子类没有覆盖父类的方法,才会使用从父类继承来的方法。 在这个例子中,我override了(其实Go中没有这个概念)sayType方法,但是当我们在sayHi中调用它时,却没有调用这个override方法,而是用了父类的原始方法。 实际上,类型嵌入不是继承。它只是某种形式上的语法糖而已。在面向对象编程中,子类应该是可以被当做父类来使用的。在里氏替换原则中,子类应该能在任何需要的地方替换掉父类。(注意一点,我们这里一开始尝试覆盖父类的非抽象方法已经违背了里氏替换原则)。 但是在上边的例子中,ArtisticUser和User是两种不同的类型。且不能替换使用。 ``` package main import ( "fmt" ) func main() { user := &User{name: "Chris"} artisticUser := &ArtisticUser{User: user} fmt.Printf("user's type is: %T\n", user) fmt.Printf("artisticUser's type is: %T\n", artisticUser) acceptUser(user) //acceptUser(artisticUser) } type User struct { name string } func (u *User) sayHi() { u.sayName() u.sayType() } func (u *User) sayName() { fmt.Printf("I am %s.", u.name) } func (u *User) sayType() { fmt.Println("I am a user.") } type ArtisticUser struct { *User } func (a *ArtisticUser) sayType() { fmt.Println("I am an artistic user.") } func acceptUser(u *User) { } ``` 结果 ``` user's type is: *main.User artisticUser's type is: *main.ArtisticUser ``` >如果你尝试去掉注释掉的那一行,你会得到一个build错误: ``` cannot use artisticUser (type *ArtisticUser) as type *User in argument to acceptUser ``` 要我说,嵌入类型既不是继承,也不是组合,只是跟它们都有点像。 ##### Go支持方法重载吗? 不支持重载。 解释: go官方解释有这样一句话: ``` Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice ``` 文中说:其他语言的经验告诉我们,有各种同名但签名不同的方法有时是有用的,但在实践中也可能令人困惑和脆弱。 另外,Go官方接着说:在Go的类型系统中,仅通过名称匹配并要求类型的一致性是一个主要的简化决策。 官方博客最后还说:关于运算符重载,似乎也是更方便。但是同样的,没有重载事情会更简单。 通过简短的几句话,我们大概可以看出来。Go语言的设计者之所以没有在Go中实现重载,其实并没有多么高深的理由,就是最核心的一个原则:让Go保持足够的简单。别小看这个简单的描述,在我看来,坚持让Go足够简单,需要Go的设计者保持极大的克制。 ##### Go支持指针算术吗? golang指针不能参与计算, 但是unsafe包可以 unsafe包绕过了Go的类型系统,达到直接操作内存的目的,使用它有一定的风险性, 正常事情下, 不建议开发的代码中大量引入这个. 但是我们需要知道有这样的黑科技存在! **内存对齐** 在这提一下比较容易忘记的点:有的时候我们调整结构体字段的顺序是可以节省内存的。 内存对齐要求一: 存储这个结构体的起始地址,是对齐边界的倍数。 假设从0开始存,结构体的每个成员在存储时,都要把这个起始地址当作地址0,然后再用相对地址来决定自己该放在哪里。 内存对齐要求2: 结构体整体占用字节数需要是类型对齐边界的倍数,不够的话要往后扩张一下。 ``` type Part1 struct { a bool b int32 c int8 d int64 e byte } type Part2 struct { e byte c int8 a bool b int32 d int64 } func main() { part1 := Part1{} part2 := Part2{} fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1)) fmt.Printf("part2 size: %d, align: %d\n", unsafe.Sizeof(part2), unsafe.Alignof(part2)) } $ go run main.go part1 size: 32, align: 8 part2 size: 16, align: 8 ``` ##### Go是一个区分大小写的语言吗? Go语言通过首字母的大小写来控制访问权限。无论是方法,变量,常量或是自定义的变量类型,如果首字母大写,则可以被外部包访问,反之则不可以。 ##### Go中变量的静态类型声明是什么? 静态类型是变量声明的时候的声明类型,在变量声明、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中变量的动态类型声明是什么? ##### 你能在Go中的单个声明中声明多种类型的变量吗? ##### 如何在Go中打印变量的类型? ##### 什么是指针? ##### break语句的目的是什么? ##### 继续声明的目的是什么? ##### goto语句的目的是什么? ##### 解释'for'循环的语法。 ##### 解释在Go中创建函数的语法。 ##### 你能从函数中返回多个值吗? ##### 您可以将参数传递给方法的方式有多少? ##### 将参数传递给函数的默认方式是什么? ##### Go中的函数作为值是什么意思? ##### 什么是功能关闭? ##### Go中的方法是什么? ##### Go中局部变量的默认值是多少? ##### Go中全局变量的默认值是多少? ##### Go中指针变量的默认值是多少? ##### 解释Printf()函数的用途。 ##### 什么是左值和左值? ##### 实际和形式参数之间有什么区别? ##### 变量声明和变量定义有什么区别? ##### 解释模块化编程。 ##### 什么是令牌? ##### 哪个关键字用于执行无条件分支? ##### 什么是阵列? ##### Go中的零指针是什么? ##### 指针上的指针是什么? ##### Go的结构是什么? ##### 如何在Go中定义一个结构? ##### Go中的切片是什么? ##### 如何在Go中定义切片? ##### 如何获取切片中存在的元素数? ##### Go中slice的len()和cap()函数有什么区别? ##### 如何获得切片的子切片? ##### Go的范围是什么? ##### Go中的地图是什么? ##### 如何在Go中创建地图? ##### 如何从Go中删除地图中的条目? ##### 什么是Go中的类型转换? ##### Go中的接口是什么? ##### Go程序能链接C/C++程序吗 ##### 为什么Go没有泛型 ##### 为什么Go没有异常 ##### 为什么用CSP思想构建并发 ##### 为什么没有goroutine ID ##### go命令,go get,go tool,go test,go vet ##### go什么情况下会发生内存泄漏? ##### go为什么高并发好? ##### go的分布式 ##### 如何知道变量分配到堆还是栈 ##### 进程虚拟空间分布,全局变量放哪里? ##### c++ 和 go对比 ##### nil切片和空切片指向的地址一样吗?这个代码会输出什么? ``` package main import ( "fmt" "reflect" "unsafe" ) func main() { var s1 []int s2 := make([]int,0) s4 := make([]int,0) fmt.Printf("s1 pointer:%+v, s2 pointer:%+v, s4 pointer:%+v, \n", *(*reflect.SliceHeader)(unsafe.Pointer(&s1)),*(*reflect.SliceHeader)(unsafe.Pointer(&s2)),*(*reflect.SliceHeader)(unsafe.Pointer(&s4))) fmt.Printf("%v\n", (*(*reflect.SliceHeader)(unsafe.Pointer(&s1))).Data==(*(*reflect.SliceHeader)(unsafe.Pointer(&s2))).Data) fmt.Printf("%v\n", (*(*reflect.SliceHeader)(unsafe.Pointer(&s2))).Data==(*(*reflect.SliceHeader)(unsafe.Pointer(&s4))).Data) } ``` 怎么答: nil切片和空切片指向的地址不一样。nil空切片引用数组指针地址为0(无指向任何实际地址) 空切片的引用数组指针地址是有的,且固定为一个值 ``` s1 pointer:{Data:0 Len:0 Cap:0}, s2 pointer:{Data:824634207952 Len:0 Cap:0}, s4 pointer:{Data:824634207952 Len:0 Cap:0}, false //nil切片和空切片指向的数组地址不一样 true //两个空切片指向的数组地址是一样的,都是824634207952 ``` 解释: 切片的数据结构为 ``` type SliceHeader struct { Data uintptr //引用数组指针地址 Len int // 切片的目前使用长度 Cap int // 切片的容量 } ``` >nil切片和空切片最大的区别在于指向的数组引用地址是不一样的。 所有的空切片指向的数组引用地址都是一样的 ### 编程题 #### 计算字母出现次数 最简单写法 ``` type LetterFreq map[rune]int func CountLetters(strs []string) LetterFreq { // m := make(map[rune]int) m := map[rune]int{} for _, str := range strs { for _, r := range str { m[r]++ fmt.Println(r) } } return LetterFreq(m) } func main() { input := []string{"dcdvd", "aaaa", "diugiu"} res := CountLetters(input) fmt.Println(fmt.Sprintf("get data:%+v", res)) } ``` channel写法 ``` type FreqMap map[rune]int // data as a FreqMap. func Frequency(s string) FreqMap { m := FreqMap{} for _, r := range s { m[r]++ } return m } func ConcurrentFrequency(strs []string) FreqMap { resChannel := make(chan FreqMap) for _, str := range strs { go func(s string) { resChannel <- Frequency(s) }(str) } res := make(FreqMap) for range strs { m := <-resChannel for letter, freq := range m { res[letter] += freq } } return res } func main() { input := []string{"dcdvd", "aaaa", "diugiu"} res := ConcurrentFrequency(input) fmt.Println(fmt.Sprintf("get data:%+v", res)) } ``` #### golang在哪成长交流 [go语言中文网](https://studygolang.com/ "go语言中文网") [golang中国](https://golangtc.com/ "golang中国") [官网](https://go.dev/ "官网") ### 题库 #### 初级1 1.make 和 new 的区别? new 和 make 是两个内置函数,主要用来创建并分配类型的内存。 new|make -|- new 可以分配任意类型的数据;|make 只能用来分配及初始化类型为 slice、map、chan 的数据。 new 分配返回的是指针,即类型 *Type。|make 返回引用,即 Type; new 分配的空间被清零。|make 分配空间后,会进行初始化; >new 只分配内存, make 只能用于 slice、map 和 channel 的初始化 ``` func new(Type) *Type ``` new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new 函数会把分配的内存置为零,也就是类型的零值。 >make 也是用于内存分配的,但是和 new 不同,它只用于 chan、map 以及 slice 的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。 总结:make 关键字的主要作用是创建 slice、map 和 Channel 等内置的数据结构,而 new 的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。 2.了解过golang的内存管理吗? 1.使用缓存提高效率。在存储的整个体系中到处可见缓存的思想,Go内存分配和管理也使用了缓存,利用缓存一是减少了系统调用的次数,二是降低了锁的粒度、减少加锁的次数,从这2点提高了内存管理效率。 2.以空间换时间,提高内存管理效率。空间换时间是一种常用的性能优化思想,这种思想其实非常普遍,比如Hash、Map、二叉排序树等数据结构的本质就是空间换时间,在数据库中也很常见,比如数据库索引、索引视图和数据缓存等,再如Redis等缓存数据库也是空间换时间的思想。 一个内存块包含了3类信息:元数据、用户数据和对齐字段,内存对齐是为了提高访问效率。 TCMalloc是Thread Cache Malloc的简称,是Go内存管理的起源,Go的内存管理是借鉴了TCMalloc,随着Go的迭代,Go的内存管理与TCMalloc不一致地方在不断扩大,但其主要思想、原理和概念都是和TCMalloc一致的,如果跳过TCMalloc直接去看Go的内存管理,也许你会似懂非懂。 引入虚拟内存后,让内存的并发访问问题的粒度从多进程级别,降低到多线程级别。然而同一进程下的所有线程共享相同的内存空间,它们申请内存时需要加锁,如果不加锁就存在同一块内存被2个线程同时访问的问题。 TCMalloc的做法是什么呢?为每个线程预分配一块缓存,线程申请小内存时,可以从缓存分配内存,这样有2个好处: >为线程预分配缓存需要进行1次系统调用,后续线程申请小内存时直接从缓存分配,都是在用户态执行的,没有了系统调用,缩短了内存总体的分配和释放时间,这是快速分配内存的第二个层次。 多个线程同时申请小内存时,从各自的缓存分配,访问的是不同的地址空间,从而无需加锁,把内存并发访问的粒度进一步降低了,这是快速分配内存的第三个层次。 TCMalloc的几个重要概念: 字段|介绍 -|- Page|操作系统对内存管理以页为单位,TCMalloc也是这样,只不过TCMalloc里的Page大小与操作系统里的大小并不一定相等,而是倍数关系。《TCMalloc解密》里称x64下Page大小是8KB。 Span|一组连续的Page被称为Span,比如可以有2个页大小的Span,也可以有16页大小的Span,Span比Page高一个层级,是为了方便管理一定大小的内存区域,Span是TCMalloc中内存管理的基本单位。 ThreadCache|ThreadCache是每个线程各自的Cache,一个Cache包含多个空闲内存块链表,每个链表连接的都是内存块,同一个链表上内存块的大小是相同的,也可以说按内存块大小,给内存块分了个类,这样可以根据申请的内存大小,快速从合适的链表选择空闲内存块。由于每个线程有自己的ThreadCache,所以ThreadCache访问是无锁的。 CentralCache|CentralCache是所有线程共享的缓存,也是保存的空闲内存块链表,链表的数量与ThreadCache中链表数量相同,当ThreadCache的内存块不足时,可以从CentralCache获取内存块;当ThreadCache内存块过多时,可以放回CentralCache。由于CentralCache是共享的,所以它的访问是要加锁的。 PageHeap|PageHeap是对堆内存的抽象,PageHeap存的也是若干链表,链表保存的是Span。当CentralCache的内存不足时,会从PageHeap获取空闲的内存Span,然后把1个Span拆成若干内存块,添加到对应大小的链表中并分配内存;当CentralCache的内存过多时,会把空闲的内存块放回PageHeap中。 对象大小介绍: 类型|大小|分配流程 -|-|- 小对象大小|0~256KB|ThreadCache -> CentralCache -> HeapPage,大部分时候,ThreadCache缓存都是足够的,不需要去访问CentralCache和HeapPage,无系统调用配合无锁分配,分配效率是非常高的。 中对象大小|257~1MB|中对象分配流程:直接在PageHeap中选择适当的大小即可,128 Page的Span所保存的最大内存就是1MB。 大对象大小|1MB|大对象分配流程:从large span set选择合适数量的页面组成span,用来存储数据。 Go内存管理源自TCMalloc,但它比TCMalloc还多了2件东西:逃逸分析和垃圾回收,这是2项提高生产力的绝佳武器。 >内存管理不应当只有堆,也应当有栈。每个goroutine都有自己的栈,栈的初始大小是2KB,100万的goroutine会占用2G,但goroutine的栈会在2KB不够用时自动扩容,当扩容为4KB的时候,百万goroutine会占用4GB。 [更多资料](https://zhuanlan.zhihu.com/p/76802887 "更多资料") 3.调用函数传入结构体时,应该传值还是指针﹖说出你的理由? >1、传指针使得多个函数能操作同一个对象。 2、传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。 3、Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。 4.线程有几种模型?Goroutine的原理了解过吗,讲一下实现和优势? 操作系统所实现的线程基本上有三种线程调度模型: >多对一(M:1)用户线程模型 一对一(1:1)内核线程模型 多对多(M:N)混合线程模型 goroutine并非传统意义上的协程,现在主流的线程模型分三种:内核级线程模型、用户级线程模型和两级线程模型(也称混合型线程模型),传统的协程库属于用户级线程模型,而goroutine和它的Go Scheduler在底层实现上其实是属于两级线程模型,因此,有时候为了方便理解可以简单把goroutine类比成协程,但心里一定要有个清晰的认知 — goroutine并不等同于协程。 Goroutine 的实现原理: >S(Sched):结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。 M(Machine):一个M直接关联了一个内核线程。 P(processor):代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。 G(Goroutine):其实本质上也是一种轻量级的线程 [更多参考](https://blog.csdn.net/ljlinjiu/article/details/89182216 "更多参考") 5.Goroutine什么时候会发生阻塞? 在Golang中,goroutine是在线程之上的,如果某个goroutine阻塞,会将该gorotine对应的线程阻塞,此时运行时环境(runtime)会把该线程上其余的goroutine放在其他未阻塞的线程之中。 6.PMG模型中Goroutine有哪几种状态? 在PMG模型中 - M去抢占G的时候有个自旋和非自旋状态. 7.每个线程/协程占用多少内存知道吗? 线程:固定的栈内存(通常为2MB左右)、协程:一个goroutine(协程)占用内存非常小 2 KB左右. 8.如果Goroutine—直占用资源怎么办,PMG模型怎么解决的这个问题? 9.如果若干线程中一个线程OOM,会发生什么?如果是Goroutine 呢? 项目中出现过OOM吗,怎么解决的? 10.项目中错误处理是怎么做的? 11.如果若干个Goroutine,其中有一个panic,会发生什么? 12.defer可以捕获到其Goroutine的子Goroutine 的panic吗? 13.开发用Gin框架吗?Gin怎么做参数校验? 14.中间件使用过吗?怎么使用的。Gin的错误处理使用过吗?Gin中自定义校验规则知道怎么做吗?自定义校验器的返回值呢? 15.golang中解析tag是怎么实现的?反射原理是什么?通过反射调用函数 16.golang的锁机制了解过吗? Mutex的锁有哪几种模式,分别介绍一下? Mutex锁底层如何实现了解过吗? 17.channel、channel使用中需要注意的地方? 18.数据库用的什么?数据库锁有了解吗?mysql锁机制讲一下。mysql分库分表。 19.讲一下redis分布式锁?redis主从模式和集群模式的区别了解过吗?redis的数据类型有哪些?redis持久化怎么做的? 20.编程题:你了解的负载均衡算法有什么?实现一个负载均衡算法。 21.内存泄漏 https://blog.csdn.net/chizong2116/article/details/100732815
相关推荐
golang 中解析 tag 是怎么实现的?反射原理是什么?(中高级肯定会问,比较难,需要自己多去总结)
使用gorm不当出现too Many Connections的问题
golang map 使用注意的点,是否并发安全?
uint 类型溢出问题
Golang中defer和return执行的先后顺序
golang orm框架 gorm
讲讲 Go 的 select 底层数据结构和一些特性?(难点,没有项目经常可能说不清,面试一般会问你项目中怎么使用select)
golang中两个变量值的4种交换方式
golang进行封包和拆包的完整解决方案
对已经关闭的 chan 进行读写,会怎么样?为什么?
调用函数传入结构体时,应该传值还是指针? (Golang 都是传值)
为 sync.WaitGroup 中Wait函数支持 WaitTimeout 功能.
单例模式的应用场景
Golang判断slice是否相等
讲讲 Go 的 defer 底层数据结构和一些特性?
Golang空结构体 struct{} 的使用
go defer,多个 defer 的顺序,defer 在什么时机会修改返回值?
序列化协议
Golang 单引号,双引号,反引号的区别?
Golang表示枚举类型的详细讲解
昨天那个在for循环里append元素的同事,今天还在么?
什么是死锁?死锁产生的原因?如何避免死锁?
golang并发题目测试
交替打印数字和字母
for range 的时候它的地址会发生变化么?
B+树为什么快
数组和切片的区别 (基本必问)
实现阻塞读且并发安全的map
机器人坐标问题
复利计算 递归/非递归
在 golang 协程和channel配合使用
MySQL索引原理
写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出?
讲讲 Go 的 slice 底层数据结构和一些特性?
高并发下的锁与map的读写
Redis的优点
判断两个给定的字符串排序后是否一致
常见语法题目2
字符串替换问题
ElasticSearch使用场景
常见语法题目1
golang 中 make 和 new 的区别?(基本必问)
七道语法找错题目
判断字符串中字符是否全都不同
golang 实现一个负载均衡案例(随机,轮训)
redis缓存穿透、缓存击穿、缓存雪崩原因+解决方案
基本数据结构和算法
消息队列使用的场景介绍
操作系统基本原理
翻转字符串
redis在项目中的使用
TiDB使用场景
评论区
先去登录
版权所有:机遇屋在线 Copyright © 2021-2025 jiyuwu Co., Ltd.
鲁ICP备16042261号-1