[TOC]
### **go中"_"的作用**
>import中的下滑線
**當導入一個包的時候,不需要把所有的包都導進來,只需要導入使用該包下的文件里所有的init()的函數。**
>下劃線在代碼中
下劃線在代碼中是忽略這個變量
>占位符
是特殊標識符,用來忽略結果
### **make 和 new 的區別**
* make 僅用來分配及初始化類型為 slice、map、chan 的數據。
* new 可分配任意類型的數據,根據傳入的類型申請一塊內存,返回指向這塊內存的指針,即類型 \*Type。
* make 返回引用,即 Type,new 分配的空間被清零, make 分配空間后,會進行初始。
### **數組和切片的區別**
**相同點:**
1)只能存儲一組相同類型的數據結構
2)都是通過下標來訪問,并且有容量長度,長度通過 len 獲取,容量通過 cap 獲取
**區別:**
1)數組是定長,訪問和復制不能超過數組定義的長度,否則就會下標越界,切片長度和容量可以自動擴容
2)數組是值類型,切片是引用類型,每個切片都引用了一個底層數組,切片本身不能存儲任何數據,都是這底層數組存儲數據,所以修改切片的時候修改的是底層數組中的數據。切片一旦擴容,指向一個新的底層數組,內存地址也就隨之改變
### **for range**
在 for k,v := range c 遍歷中, k 和 v 在內存中只會存在一份,即之后每次循環時遍歷到的數據都是以值覆蓋的方式賦給 k,v ,它們的內存地址始終不變。由于有這個特性,for 循環里面如果開協程,不要直接把 k 或者 v 的地址傳給協程。解決辦法:在每次循環時,創建一個臨時變量。
### **go defer,多個 defer 的順序,defer 在什么時機會修改返回值**
多個defer ,遵循先進后出,
defer,return,return value(函數返回值) 執行順序:首先return,其次return value,最后defer
defer可以修改函數最終返回值,修改時機:**有名返回值或者函數返回指針**
### **Go 的 defer 底層數據結構和一些特性**
每個 defer 語句都對應一個\_defer 實例,多個實例使用指針連接起來形成一個單連表,保存在 gotoutine 數據結構中,每次插入\_defer 實例,均插入到鏈表的頭部,函數結束再一次從頭部取出,從而形成后進先出的效果。
**defer 的規則總結**:
延遲函數的參數是 defer 語句出現的時候就已經確定了的。
延遲函數執行按照后進先出的順序執行,即先出現的 defer 最后執行。
延遲函數可能操作主函數的返回值。
申請資源后立即使用 defer 關閉資源(文件、鎖、鏈接)。
### **介紹下 rune 類型嗎**
* uint8 類型,或者叫 byte 型,代表了 ASCII 碼的一個字符。
* rune 類型,代表一個 UTF-8 字符,當需要處理中文、日文或者其他復合字符時,則需要用到 rune 類型。rune 類型等價于 int32 類型。
### **golang 中解析 tag 是怎么實現的?反射原理是什么?**
tag 是通過反射實現的
反射是指計算機程序在運行時(Run time)可以訪問、檢測和修改它本身狀態或行為的一種能力或動態知道給定數據對象的類型和結構,并有機會修改它。
>三大原理:
反射將接口變量轉換成反射對象 Type 和 Value;
反射可以通過反射對象 Value 還原成原先的接口變量;
反射可以用來修改一個變量的值,前提是這個值可以被修改;
### 講一下**Go 的 slice 底層數據結構和一些特性**
結構:指向底層數組的指針、切片的長度(len)和切片的容量(cap)。
擴容機制:
* 首先判斷,如果新申請容量大于 2 倍的舊容量,最終容量就是新申請的容量。
* 否則判斷,如果舊切片的長度小于 1024,則最終容量就是舊容量的兩倍。
* 否則判斷,如果舊切片長度大于等于 1024,則最終容量從舊容量開始循環增加原來的 1/4 , 直到最終容量大于等于新申請的容量。
* 如果最終容量計算值溢出,則最終容量就是新申請容量。
### **講講 Go 的 select 底層數據結構和一些特性**
select 結構組成主要是由 case 語句和執行的函數組成 select 實現的多路復用是:每個線程或者進程都先到注冊和接受的 channel(裝置)注冊,然后阻塞,然后只有一個線程在運輸,當注冊的線程和進程準備好數據后,裝置會根據注冊的信息得到相應的數據。
**select 的特性**
1)select 操作至少要有一個 case 語句,出現讀寫 nil 的 channel 該分支會忽略,在 nil 的 channel 上操作則會報錯。
2)select 僅支持管道,而且是單協程操作。
3)每個 case 語句僅能處理一個管道,要么讀要么寫。
4)多個 case 語句的執行順序是隨機的。
5)存在 default 語句,select 將不會阻塞,但是存在 default 會影響性能。
### **單引號,雙引號,反引號的區別?**
單引號,表示byte類型或rune類型,對應 uint8和int32類型,默認是 rune 類型。byte用來強調數據是raw data,而不是數字;而rune用來表示Unicode的code point。
雙引號,才是字符串,實際上是字符數組。可以用索引號訪問某字節,也可以用len()函數來獲取字符串所占的字節長度。
反引號,表示字符串字面量,但不支持任何轉義序列。字面量 raw literal string 的意思是,你定義時寫的啥樣,它就啥樣,你有換行,它就換行。你寫轉義字符,它也就展示轉義字符。
### **Go 當中同步鎖有什么特點?作用是什么**
> 特點
1、當一個 Goroutine(協程)獲得了 Mutex 后,其他 Goroutine(協程)就只能等待,除非該 Goroutine 釋放了該Mutex。
2、RWMutex 在讀鎖占用的情況下, 會阻止寫,但不阻止讀 RWMutex。 在寫鎖占用情況下,會阻止任何其他 Goroutine(無論讀和寫)進來,整個鎖相當于由該 Goroutine 獨占
>作用:
同步鎖的作用是保證資源在使用時的獨有性,不會因為并發而導致數據錯亂, 保證系統的穩定性。
### **Go 語言中 cap 函數可以作用于哪些內容**
Array、Slice、Channel
### **Printf(),Sprintf(),FprintF() 都是格式化輸出,有什么不 同?**
* Printf 是標準輸出,一般是屏幕,也可以重定向。
* Sprintf()是把格式化字符串輸出到指定的字符串中。
* Fprintf()是把格式化字符串輸出到文件中。
### **go 打印時 %v %+v %#v 的區別?**
* %v 只輸出所有的值;
* %+v 先輸出字段名字,再輸出該字段的值;
* %#v 先輸出結構體名字值,再輸出結構體(字段名字+字段的值);
~~~
package main
import "fmt"
type student struct {
id int32
name string
}
func main() {
a := &student{id: 1, name: "微客鳥窩"}
fmt.Printf("a=%v \n", a) // a=&{1 微客鳥窩}
fmt.Printf("a=%+v \n", a) // a=&{id:1 name:微客鳥窩}
fmt.Printf("a=%#v \n", a) // a=&main.student{id:1, name:"微客鳥窩"}
}
~~~
### **什么是進程、線程、協程?**
進程:是應用程序的啟動實例,每個進程都有獨立的內存空間,不同的進程通過進程間的通信方式來通信。
線程:從屬于進程,每個進程至少包含一個線程,線程是 CPU 調度的基本單位,多個線程之間可以共享進程的資源并通過共享內存等線程間的通信方式來通信。
協程:為輕量級線程,與線程相比,協程不受操作系統的調度,協程的調度器由用戶應用程序提供,協程調度器按照調度策略把協程調度到線程中運行
### **協程和線程的區別?**
* 一個線程可以有多個協程
* 線程、進程都是同步機制,而協程是異步
* 協程可以保留上一次調用時的狀態,當過程重入時,相當于進入了上一次的調用狀態
* 協程是需要線程來承載運行的,所以協程并不能取代線程,「線程是被分割的CPU資源,協程是組織好的代碼流程」
### **Go的Struct能不能比較?**
* 相同struct類型的可以比較
* 不同struct類型的不可以比較,編譯都不過,類型不匹配
### **兩個 interface 可以比較嗎?**
* 判斷類型是否一樣
reflect.TypeOf(a).Kind() == reflect.TypeOf(b).Kind()
* 判斷兩個interface{}是否相等
reflect.DeepEqual(a, b interface{})
* 將一個interface{}賦值給另一個interface{}
reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))
### **能說說uintptr和unsafe.Pointer的區別嗎?**
> uintptr:是一種整數類型,其大小足以容納任何指針的位模式
* unsafe.Pointer只是單純的通用指針類型,用于轉換不同類型指針,它不可以參與指針運算;
* 而uintptr是用于指針運算的,GC 不把 uintptr 當指針,也就是說 uintptr 無法持有對象, uintptr 類型的目標會被回收;
* unsafe.Pointer 可以和 普通指針 進行相互轉換;
* unsafe.Pointer 可以和 uintptr 進行相互轉換。
- Go準備工作
- 依賴管理
- Go基礎
- 1、變量和常量
- 2、基本數據類型
- 3、運算符
- 4、流程控制
- 5、數組
- 數組聲明和初始化
- 遍歷
- 數組是值類型
- 6、切片
- 定義
- slice其他內容
- 7、map
- 8、函數
- 函數基礎
- 函數進階
- 9、指針
- 10、結構體
- 類型別名和自定義類型
- 結構體
- 11、接口
- 12、反射
- 13、并發
- 14、網絡編程
- 15、單元測試
- Go常用庫/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go優化
- Go問題排查
- Go框架
- 基礎知識點的思考
- 面試題
- 八股文
- 操作系統
- 整理一份資料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小點
- 樹
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面試題
- 基礎
- Map
- Chan
- GC
- GMP
- 并發
- 內存
- 算法
- docker