go語言基礎知識匯總
一、go語言簡介
- 天然支持高并發
- 內存自動回收,不需要開發人員管理內存
- 支持管道,從而支持多個協程之間通信
- 多返回值,一個函數可以允許多個返回值
- 異常和錯誤的區分,異常是不能預料到的事情發生了,錯誤是預料到的事情發生了。
二、理解gopath
- gopath go項目的工作目錄,目錄下面包含三個目錄,src,pkg,bin
- goroot go源碼的安裝路徑
- gobin 存放go編譯后生成的可執行文件
- go get 執行go get會把源碼放在第一個gopath的src目錄下面
三、go基礎知識
3.1 基本數據結構和操作符
-
- 文件名&關鍵字&標識符(略)
-
- Go程序基本結構
-
- 任何一個代碼文件隸屬于一個包
-
- import 關鍵字,引用其他包:
-
- golang可執行程序,package main,并且有且只有一個main入口函數
-
- 包中函數調用:a.同一個包中直接實用名稱調用;b。不同包中的函數通過包名+點+函數名稱進行調用
-
- 包訪問控制規則:大寫意味函數公有;小寫意味函數私有;
-
- 常量和變量
- 常量使用const 修飾,代表永遠是只讀的,不能修改。
-
- const 只能修飾boolean,number(int相關類型、浮點類型、complex)和string。
-
- 數據類型和操作符
- 值類型:變量直接存儲值,內存通常在棧中分配。
- 值類型:基本數據類型int、float、bool、string以及數組和struct。
- 引用類型:變量存儲的是一個地址,這個地址存儲最終的值。內存通常在堆上分配。通過GC回收。
- 引用類型:指針、slice、map、chan等都是引用類型
- 在函數內部聲明的變量叫做局部變量,生命周期僅限于函數內部。
- 在函數外部聲明的變量叫做全局變量,生命周期作用于整個包,如果是大寫的,則作用于整個程序。
- bool類型,只能存true和false
- 相關操作符, !、&&、||
- 數字類型,主要有int、int8、int16、int32、int64、uint8、uint16、uint32、uint64、float32、float64
- 類型轉換,type(variable),比如:var a int=8; var b int32=int32(a)
- 邏輯操作符:== 、!=、<、<=、>和 >=
- 數學操作符:+、-、*、/等等
-
- 字符串類型
- 字符類型:var a byte
- 字符串類型: var str string
- 字符串表示兩種方式: 1)雙引號2)``(不需要轉義)
3.2 字符串處理&時間和日期類型&指針類型&流程控制&函數詳解
- strings和strconv使用
- strings.HasPrefix(s string, prefix string) bool:判斷字符串s是否以prefix開頭。
-
- strings.HasSuffix(s string, suffix string) bool:判斷字符串s是否以suffix結尾。
-
- strings.Index(s string, str string) int:判斷str在s中首次出現的位置,如果沒有出現,則返回-1
-
- strings.LastIndex(s string, str string) int:判斷str在s中最后出現的位置,如果沒有出現,則返回-1
-
- strings.Replace(str string, old string, new string, n int):字符串替換
-
- strings.Count(str string, substr string)int:字符串計數
-
- strings.Repeat(str string, count int)string:重復count次str
-
- strings.ToLower(str string)string:轉為小寫
-
- strings.ToUpper(str string)string:轉為大寫
- strings.TrimSpace(str string):去掉字符串首尾空白字符
- strings.Trim(str string, cut string):去掉字符串首尾cut字符
- strings.TrimLeft(str string, cut string):去掉字符串首cut字符
- strings.TrimRight(str string, cut string):去掉字符串首cut字符
- strings.Field(str string):返回str空格分隔的所有子串的slice
- strings.Split(str string, split string):返回str split分隔的所有子串的slice
- strings.Join(s1 []string, sep string):用sep把s1中的所有元素鏈接起來
- strings.Itoa(i int):把一個整數i轉成字符串
- strings.Atoi(str string)(int, error):把一個字符串轉成整數
- Go中的時間和日期類型
-
- time包
-
- time.Time類型,用來表示時間
-
- 獲取當前時間, now := time.Now()
-
- time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year()
-
- 格式化,fmt.Printf("%02d/%02d%02d %02d:%02d:%02d", now.Year()…)
-
- time.Duration用來表示納秒
- 指針類型
-
- 普通類型,變量存的就是值,也叫值類型
-
- 獲取變量的地址,用&,比如: var a int, 獲取a的地址:&a
-
- 指針類型,變量存的是一個地址,這個地址存的才是值
-
- 獲取指針類型所指向的值,使用:,比如:var p int, 使用*p獲取p指向的值
- 流程控制
-
- If / else分支判斷
-
- switch case語句,可以使用fallthrough強制執行后面的case代碼
-
- for 語句
-
- for range 語句
-
- break continue語句
-
- goto 和 label 語句 (明天補充)
- 函數詳解
-
- 聲明語法:func 函數名 (參數列表) [(返回值列表)] {}
-
- golang函數特點:
- a. 不支持重載,一個包不能有兩個名字一樣的函數
- b. 函數是一等公民,函數也是一種類型,一個函數可以賦值給變量
- c. 匿名函數
- d. 多返回值
-
- 函數參數傳遞方式:
- 1). 值傳遞
- 2). 引用傳遞
注意1:無論是值傳遞,還是引用傳遞,傳遞給函數的都是變量的副本,不過,值傳遞是值的拷貝。引用傳遞是地址的拷貝,一般來說,地址拷貝更為高效。而值拷貝取決于拷貝的對象大小,對象越大,則性能越低。
注意2:map、slice、chan、指針、interface默認以引用的方式傳遞
-
- _標識符,用來忽略返回值:
-
- 可變參數:func add(a int, b int, arg…int) int {} //兩個或多個參數
- 注意:其中arg是一個slice,我們可以通過arg[index]依次訪問所有參數,通過len(arg)來判斷傳遞參數的個數
-
- defer用途:
-
- 當函數返回時,執行defer語句。因此,可以用來做資源清理
-
- 多個defer語句,按先進后出的方式執行
-
- defer語句中的變量,在defer聲明時就決定了。
-
- defer的用途:關閉文件句柄;釋放資源;關閉數據庫連接
3.3 內置函數、遞歸函數、閉包
內置函數
-
- close:主要用來關閉channel
-
- len:用來求長度,比如string、array、slice、map、channel
-
- new:用來分配內存,主要用來分配值類型,比如int、struct。返回的是指針
-
- make:用來分配內存,主要用來分配引用類型,比如chan、map、slice
-
- append:用來追加元素到數組、slice中
-
- panic和recover:用來做不可預料的異常捕獲
-
- new和make的區別
- New與make都是用來分配內存。不同點:new用來分配值類型,make用來分配引用類型;new返回的是一個指針,make返回的是值類型。
遞歸函數
-
- 一個函數調用自己,就叫做遞歸。
-
- 斐波那契數
-
- 遞歸的設計原則
- 1)一個大的問題能夠分解成相似的小問題
- 2)定義好出口條件
閉包
- 一個函數和與其相關的引用環境而組合成的實體。
3.4 數組和切片、map數據結構
數組
- 數組:是同一種數據類型的固定長度的序列。
- 數組定義:var a [len]int,比如:var a[5]int
- 長度是數組類型的一部分,因此,var a[5] int和var a[10]int是不同的類型
- 數組可以通過下標進行訪問,下標是從0開始,最后一個元素下標是:len-1
- 訪問越界,如果下標在數組合法范圍之外,則觸發訪問越界,會panic
- 數組是值類型,因此改變副本的值,不會改變本身的值
- 數組初始化
切片
- 切片:切片是數組的一個引用,因此切片是引用類型
- 切片的長度可以改變,因此,切片是一個可變的數組
- 切片遍歷方式和數組一樣,可以用len()求長度
- cap可以求出slice最大的容量,0 <= len(slice) <= (array),其中array是slice引用的數組
- 切片的定義:var 變量名 []類型,比如 var str []string var arr []int
Slice的數據結構
Type slice struct {
ptr *[5]type
len int
cap int
}
Cap:不指定的話,默認為2^0 2^1 2^2 2^n n為元素的個數
- 通過make來創建切片(另一種通過數組創建切片)
- var slice []type = make([]type, len)
- slice := make([]type, len)
- slice := make([]type, len, cap)
- 用append內置函數操作切片,當切片容量不夠,會自動擴容,創建一個新的地址。
- For range 遍歷切片
- 切片拷貝
- string與slice
- 排序和查找操作
- sort.Ints對整數進行排序, sort.Strings對字符串進行排序, sort.Float64s對浮點數排序
- sort.SearchInts(a []int, b int) 從數組a中查找b,前提是a必須有序
- sort.SearchFloats(a []float64, b float64) 從數組a中查找b,前提是a必須有序
- sort.SearchStrings(a []string, b string) 從數組a中查找b,前提是a必須有序
map數據結構
- key-value的數據結構,又叫字典或關聯數組
- 聲明 var a map[string]string
- 聲明是不會分配內存的,初始化需要make
- map相關操作
- var a = map[string]string{"key":"value"}
- a = make(map[string]string, 10)
- a[“hello”] = “world” //插入或者更新
- Val, ok := a[“hello”] // 查找
- for k, v := range a //遍歷
- delete(a, “hello”) //刪除
- map是引用類型
- slice of map
- map排序
- a. 先獲取所有key,把key進行排序
- b. 按照排序好的key,進行遍歷
- Map反轉
- a. 初始化另外一個map,把key、value互換即可
3.5 package介紹
- golang中的包
- a. golang目前有150個標準的包,覆蓋了幾乎所有的基礎庫
- b. golang.org有所有包的文檔,沒事都翻翻
- 線程同步
- a. import(“sync”)
- b. 互斥鎖, var mu sync.Mutex
- c. 讀寫鎖, var mu sync.RWMutex
- d. 為什么要有鎖:十字路口是公有的資源,紅綠燈就是鎖。為了避免小車爭搶資源就要上鎖。
- 課后作業
實現一個冒泡排序、選擇排序、插入排序、快速排序(參考書籍及搜索引擎)
3.6 結構體&方法&接口
結構體的特點
- 用來自定義復雜數據結構
- struct里面可以包含多個字段(屬性)
- struct類型可以定義方法,注意和函數的區分
- struct類型是值類型
- struct類型可以嵌套
- Go語言沒有class類型,只有struct類型
結構體的定義以及初始化
- struct 聲明:
- struct 中字段訪問:和其他語言一樣,使用點
- struct定義的三種形式:
- var stu Student
- var stu *Student = new (Student)
- var stu *Student = &Student{}
- 其中2和3返回的都是指向結構體的指針,訪問形式:a. stu.Name、stu.Age和stu.Score或者 (stu).Name、(stu).Age等
- struct的內存布局:struct中的所有字段在內存是連續的,布局如下:
- 鏈表定義
- 雙鏈表定義
- 二叉樹定義
- 如果每個節點有兩個指針分別用來指向左子樹和右子樹,我們把這樣的結構叫做二叉樹
type Student struct {
Name string
left* Student
right* Student
}
- 結構體是用戶單獨定義的類型,不能和其他類型進行強制轉換
- golang中的struct沒有構造函數,一般可以使用工廠模式來解決這個問題
type student struct {
Name string
Age int
}
func NewStudent(name string, age int) *student {
return &student{
Name:name,
Age:age,
}
}
func main() {
s := new(student)
a := NewStudent("tony", 23)
s = a
fmt.Println(*s)
fmt.Println(*a)
}
- 再次強調:
- make 用來創建map、slice、channel、interface,new用來創建值類型
- 我們可以為struct中的每個字段,寫上一個tag。這個tag可以通過反射的機制獲取到,最常用的場景就是json序列化和反序列化
type student struct {
Name stirng "this is name field"
Age int "this is age field"
}
- 結構體中字段可以沒有名字,即匿名字段
type Train struct {
Car
Start time.Time
int
}
3.7 方法
- Golang中的方法是作用在特定類型的變量上,因此自定義類型,都可以有方法,而不僅僅是struct
- 定義:func (recevier type) methodName(參數列表)(返回值列表){}
- 方法的調用
type A struct {
a int
}
func (this A) test() {
fmt.Println(this.a)
}
var t A
t.test()
- 方法和函數的區別
- 函數調用: function(variable, 參數列表)
- 方法調用:variable.function(參數列表)
- 指針receiver vs 值receiver
- 方法的訪問控制,通過大小寫控制
- 繼承
- 如果一個struct嵌套了另一個匿名結構體,那么這個結構可以直接訪問匿名結構體的方法,從而實現了繼承。
- 組合和匿名字段
- 如果一個struct嵌套了另一個有名結構體,那么這個模式就叫組合。
- 多重繼承
- 如果一個struct嵌套了多個匿名結構體,那么這個結構可以直接訪問多個匿名結構體的方法,從而實現了多重繼承。
- 實現String()
- 如果一個變量實現了String()這個方法,那么fmt.Println默認會調用這個變量的String()進行輸出。
3.8 接口
接口的定義
- interface類型可以定義一組方法,但是這些方法不需要實現。并且interface不能包含任何變量。
- 定義接口
type example interface {
Method1(parse1) return1
method2(parse1) return1
}
- interface類型默認是一個指針
- 接口實現
- a. Golang中的接口,不需要顯示的實現。只要一個變量,含有接口類型中的所有方法,那么這個變量就實現這個接口。因此,golang中沒有implement類似的關鍵字
- b. 如果一個變量含有了多個interface類型的方法,那么這個變量就實現了多個接口。
- c. 如果一個變量只含有了1個interface的方部分方法,那么這個變量沒有實現這個接口。
- 多態
一種事物的多種形態,都可以按照統一的接口進行操作
- 接口嵌套
一個接口可以嵌套在另外的接口
package main
type ReadWrite interface {
Read()
Write()
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
8.類型斷言,由于接口是一般類型,不知道具體類型,如果要轉成具體類型,可以采用以下方法進行轉換:
var t int
var x interface{}
x = t
y, ok := x.(int) //轉成int
if ok {...}
switch b := x.(type) {
case int:
fmt.Println("int:", b)
case string:
fmt.Println("string", b)
default:
fmt.Println("i don't know")
}
- 空接口 interface{},空接口沒有任何方案,所有類型都實現了空接口,可以使用空接口
var a int
var b interface{}
b = a
-
判斷一個變量是否實現了指定接口
- if sv, ok := v.(Stringer); ok {}
- 問題
-
- 指針類型和值類型的區別
-
- 實現一個通用的鏈表類
-
- 實現一個負載均衡調度算法,支持隨機、輪訓等算法
-
- 變量slice和接口slice之間賦值操作,for range???
3.9 反射
- 反射:可以在運行時動態獲取變量的相關信息Import (“reflect”)
- a. reflect.TypeOf,獲取變量的類型,返回reflect.Type類型
- b. reflect.ValueOf,獲取變量的值,返回reflect.Value類型
- c. reflect.Value.Kind,獲取變量的類別,返回一個常量
- d. reflect.Value.Interface(),轉換成interface{}類型
- 變量 <--> interface <--> Reflect.Value
- 獲取變量的值
- reflect.ValueOf(x).Float()
- reflect.ValueOf(x).Int()
- reflect.ValueOf(x).String()
- reflect.ValueOf(x).Bool()
- 通過反射的來改變變量的值
- reflect.Value.SetXX相關方法,比如:
- reflect.Value.SetFloat(),設置浮點數
- reflect.Value.SetInt(),設置整數
- reflect.Value.SetString(),設置字符串
package main
import (
"fmt"
"reflect"
)
func main() {
var a float64
fv := reflect.ValueOf(&a)
fv.Elem().SetFloat(3.3)
fmt.Printf("%v\n", a)
}
fv.Elem()用來獲取指針指向的變量
- 用反射操作結構體 ???
- a. reflect.Value.NumField()獲取結構體中字段的個數
- b. reflect.Value.Method(n).Call來調用結構體中的方法
3.9 終端以及文件操作
- 終端讀寫
- os.Stdin:標準輸入
- os.Stdout:標準輸出
- os.Stderr:標準錯誤輸出
- 帶緩沖區的讀寫:
- var inputReader *bufio.Reader //聲明一個結構體類型的變量
- inputReader = bufio.NewReader(os.Stdin) //讀取標準輸入
- input, err = inputReader.ReadString('\n') // 讀取換行
- os.File封裝所有文件相關操作,之前講的 os.Stdin,os.Stdout, os.Stderr都是*os.File
- 打開一個文件進行讀操作: os.Open(name string) (*File, error)
- 關閉一個文件:File.Close()
- bufio.NewReader //構造一個reader結構體
- inputReader.ReadString //逐行讀取
- ioutil.ReadFile(inputFile) //讀取整個文件
- ioutil.WriteFile(outputFile,buf,0x644) //寫入整個文件
- gzip.NewReader(fi) //構造一個壓縮文件的reader結構體
- 文件寫入
- os.OpenFile(“output.dat”, os.O_WRONLY|os.O_CREATE, 0666)
- 第二個參數:文件打開模式:
- 第三個參數:權限控制:
- bufio.NewWriter(outputFile) //構建一個寫入文件的writer
- outputWriter.WriteString(outputString) //寫入文件
- outputWriter.Flush() 提交
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
inputFile, err := os.Open("file")
if err != nil {
fmt.Printf("open file err:%v\n", err)
return
}
defer inputFile.Close()
inputReader := bufio.NewReader(inputFile)
for {
inputString, readerError := inputReader.ReadString('\n')
if readerError == io.EOF {
return
}
fmt.Printf("the input was %s", inputString)
}
}
//讀取以及寫入整個文件
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
inputFile := "file"
outputFile := "output_file"
buf, err := ioutil.ReadFile(inputFile)
if err != nil {
fmt.Fprintf(os.Stderr, "File Error:%s\n", err)
return
}
fmt.Printf("%s\n",string(buf))
err = ioutil.WriteFile(outputFile,buf,0x644)
if err != nil {
panic(err.Error())
}
}
//讀取壓縮文件
gzip.NewReader(fi)
//寫入文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
outputFile, outputErr := os.OpenFile("new_file", os.O_WRONLY|os.O_CREATE,0666)
if outputErr != nil {
fmt.Printf("An error occurred with file creation\n")
return
}
defer outputFile.Close()
outputWriter := bufio.NewWriter(outputFile)
outputString := "hello world!\n"
for i:= 0;i < 10; i++ {
outputWriter.WriteString(outputString)
}
outputWriter.Flush()
}
- 拷貝文件 io.Copy(dst, src)
- os.Args是一個string的切片,用來存儲所有的命令行參數
- flag包的使用,用來解析命令行參數:
flag.BoolVar(&test, "b", false, "print on newline")
flag.StringVar(&str, "s", "", "print on newline")
flag.IntVar(&count, "c", 1001, "print on newline")
package main
import (
"flag"
"fmt"
)
func main() {
var test bool
var str string
var count int
flag.BoolVar(&test, "b", false, "print on newline")
flag.StringVar(&str, "s", "hello", "print on newline")
flag.IntVar(&count, "c", 1001, "print on newline")
flag.Parse()
fmt.Println(test,str,count)
}
package main
import (
"bufio"
"fmt"
"os"
)
//帶緩沖的區的終端讀寫
func main() {
fmt.Fprintf(os.Stdout,"%s\n","hello world!-unbuffered")
buf := bufio.NewWriter(os.Stdout)
fmt.Fprintf(buf, "%s\n", "hello world!-buffered" )
buf.Flush()
}
四、中階知識
4.1 json數據協議
- golang --> json字符串 --> 網絡傳輸 --> 程序 --> 其他語言
- 導入包:Import “encoding/json”
- 序列化: json.Marshal(data interface{}) //json化
- 反序列化: json.UnMarshal(data []byte, v interface{}) //解析json
- json序列化結構體
- json序列化map
4.2 錯誤處理
- 定義錯誤 var errNotFound error = errors.New("Not found error")
- 自定義錯誤
- 如何判斷自定義錯誤
- pannic 這一部分參考俯瞰四維
五、并發編程
六、高階編程
6.1 tcp編程
redis是個開源的高性能的key-value的內存數據庫,可以把它當成遠程的數據結構。支持的value類型非常多,比如string、list(鏈表)、set(集合)、hash表等等。redis性能非常高,單機能夠達到15w qps,通常適合做緩存。使用第三方開源的redis庫: github.com/garyburd/redigo/redis
- 鏈接redis c, err := redis.Dial("tcp", "localhost:6379")
- Set 接口 _, err = c.Do("Set", "abc", 100) r, err := redis.Int(c.Do("Get", "abc"))
- Hash表 _, err = c.Do("HSet", "books", "abc", 100) r, err := redis.Int(c.Do("HGet", "books", "abc"))
- . 批量Set _, err = c.Do("MSet", "abc", 100, "efg", 300) r, err := redis.Ints(c.Do("MGet", "abc", "efg"))
- 過期時間 _, err = c.Do("expire", "abc", 10)
- 隊列操作 _, err = c.Do("lpush", "book_list", "abc", "ceg", 300) r, err := redis.String(c.Do("lpop", "book_list"))
6.3 http編程
特點
- a. Go原生支持http,import(“net/http”)
- b. Go的http服務性能和nginx比較接近
- c. 幾行代碼就可以實現一個web服務
- 處理http請求
使用 net/http 包提供的 http.ListenAndServe() 方法,可以在指定的地址進行監聽, 開啟一個HTTP,服務端該方法的原型:func ListenAndServe(addr string, handler Handler) error <br>
第二個參數表示服務端處理程序, 通常為空,這意味著服務端調用 http.DefaultServeMux 進行處理,而服務端編寫的業務邏 輯處理程序 http.Handle() 或 http.HandleFunc() 默認注入 http.DefaultServeMux 中
- 處理https請求 func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error
- 路由 http.HandleFunc()方法接受兩個參數
- 第一個參數是HTTP請求的 目標路徑"/hello",該參數值可以是字符串,也可以是字符串形式的正則表達式
- 第二個參數指定具體的回調方法,比如helloHandler
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(“hello beifeng!”))
})
- post訪問
resp, err:=http.Get(“.....”)
defer resp.Body.Close()
body,err:=ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
- post訪問
resp, err:=http.Post(“.....”, ”application/x-www-form-urlencoded”, strings.NewReader(“..=...”))
defer resp.Body.Close()
body,err:=ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
6.4 正則表達式
Go語言標準庫內建提供了regexp包
. 匹配除換行符以外的任意字符
\w 匹配字母或數字或下劃線或漢字
\s 匹配任意的空白符
\d 匹配數字
\b 匹配單詞的開始或結束
^ 匹配字符串的開始
$ 匹配字符串的結束
* 重復零次或更多次
+ 重復一次或更多次
? 重復零次或一次
{n} 重復n次
{n,} 重復n次或更多次
{n,m} 重復n到m次
捕獲 (exp) 匹配exp,并捕獲文本到自動命名的組里
(?<name>exp) 匹配exp,并捕獲文本到名稱為name的組里,也可以寫成(?'name'exp)
(?:exp) 匹配exp,不捕獲匹配的文本,也不給此分組分配組號
func Match(pattern string, b []byte) (matched bool, err error)
func MatchString(pattern string, s string) (matched bool, err error)
func MustCompile(str string) *Regexp
func (re *Regexp) FindAllString(s string, n int) []string
https://my.oschina.net/kuerant/blog/199146
-
mysql驅動
https://github.com/Go-SQL-Driver/MySQL
- sqlx
- database, err := sqlx.Open("mysql", "root:@tcp(127.0.0.1:3306)/test")
- r, err := Db.Exec("insert into person(username, sex, email)values(?, ?, ?)", "stu001", "man", "stu01@qq.com")
- err := Db.Select(&person, "select user_id, username, sex, email from person where user_id=?", 1)
- _, err := Db.Exec("update person set username=? where user_id=?", "stu0001", 1)
- _, err := Db.Exec("delete from person where user_id=?", 1)
6.6 beego框架
beego.me
beego框架之請求數據處理
- ``符號,表示里面的不用轉義
- 直接解析到struct
- 獲取request body里的內容
-
- 在配置文件里設置 copyrequestbody = true
-
- this.Ctx.Input.RequestBody