一文帶你入門 Golang

go 語言特色

腳本化的語法,容易上手。html

靜態類型+編譯性,開發、運行效率都有保證java

函數式 & 面向對象 兩種編程範式,mysql

原生支持併發編程支持,下降開發成本,維護成本,以及更好的兼容性,效率。linux

劣勢:語法糖沒有 Python 和 Ruby 多。運行效率不及C,但已趕超C++,Java。第三方庫很少,就是輪子少(喜歡造輪子的能夠加入golang輪子大軍)。nginx

安裝

官方: https://golang.org/git

國內官方站點: https://go-zh.org/github

Linux

http://golang.org/dl/ 下載最新Go語言二進制包golang

wget https://dl.google.com/go/go1.13.15.linux-amd64.tar.gz

tar -C /usr/local -xzf go1.13.15.linux-amd64.tar.gz

export PATH=$PATH:/usr/local/go/bin

go version

Mac

環境變量配置

GOROOT, GOPATH, GOBIN, PATH, 如今安裝的最新golang runtiem都不用配置了環境變量了。sql

目錄結構

go命令

go run

go 代碼關鍵字

break         //退出循環
default     //選擇結構默認項(switch、select)
func         //定義函數
interface    //定義接口
select        //channel
case         //選擇結構標籤
chan         //定義channel
const         //常量
continue     //跳過本次循環
defer         //延遲執行內容(收尾工做)
go         //併發執行
map         //map類型
struct        //定義結構體
else         //選擇結構
goto         //跳轉語句
package     //包
switch        //選擇結構
fallthrough     //??
if         //選擇結構
range         //從slice、map等結構中取元素
type        //定義類型
for         //循環
import         //導入包
return         //返回
var        //定義變量

標示符

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

語言特點

不要求縮進,不要求末尾加分號——;,同一行代碼中有多個表達式,須要用 分號 分割。沒有使用的變量,包,會致使報錯。shell

每一個go源文件開頭必須是package開頭,定義本身的包

一個目錄下,只能有一個包名

一個可執行的文件必需要有 main() 函數

import 引入包

兩種引入風格

import "package1"
import "package2"
import (
    "package1"
    pa2 "package2"      // 包別名,別名爲 pa2
    . "fmt"
    _ "mysql"
)

. "fmt" 方式引入包的化,使用fmt裏面的函數就可直接使用,不用帶 fmt 前綴了

若是引入的包不使用,會報錯, 或者加個前綴 _ 便可,這樣的下劃線會把引入的包的init函數執行一下。定義的變量不用,也會報錯。

包內初始化函數

定義 包內 初始化函數

func init() {
    
}

只導入這個包部分,並運行init函數,因爲導入不全,因此在代碼中就不能使用這個包了。

import _ "MyPackage"

數據類型

序號 類型和描述
1 布爾型 布爾型的值只能夠是常量 true 或者 false。一個簡單的例子:var b bool = true。
2 數字類型 整型 int 和浮點型 float3二、float64,Go 語言支持整型和浮點型數字,而且支持複數,其中位的運算採用補碼。
3 字符串類型: 字符串就是一串固定長度的字符鏈接起來的字符序列。Go 的字符串是由單個字節鏈接起來的。Go 語言的字符串的字節使用 UTF-8 編碼標識 Unicode 文本。
4 派生類型: 包括: (a) 指針類型(Pointer) (b) 數組類型 (c) 結構化類型(struct) (d) Channel 類型 (e) 函數類型 (f) 切片類型 (g) 接口類型(interface) (h) Map 類型

使用 int 時,根據當前操做系統來的,64位系統對應 int64, 32位操做系統,對應int32.

變量聲明

  • 變量聲明: var <變量名> [變量類型]
  • 變量賦值: <變量名> = <值,表達式,函數返回>
  • 變量聲明賦值:var <變量名> [變量類型] = <值,表達式,函數返回>
  • 變量聲明,類型推斷,並賦值 <變量名> := <值,表達式,函數返回>
分組聲明

var (
    i int
    foo float32
    name string
)

分組批量聲明、賦值
var a,b,c,d int = 1,2,3,4
a,b := 1,2

特殊變量 _

變量做用域

  • 函數內定義的變量稱爲局部變量
  • 函數外定義的變量稱爲全局變量
  • 全局變量必須使用 var 聲明,局部變量可省略

做用域能夠分爲如下四個類型:

  • 內置做用域:不須要本身聲明,全部的關鍵字和內置類型、函數都擁有全局做用域
  • 包級做用域:必須函數外聲明,在該包內的全部文件均可以訪問
  • 文件級做用域:不須要聲明,導入便可。一個文件中經過import導入的包名,只在該文件內可用
  • 局部做用域:在本身的語句塊內聲明,包括函數,for、if 等語句塊,或自定義的 {} 語句塊造成的做用域,只在本身的局部做用域內可用

語句塊

語句塊是由花括弧({})所包含的一系列語句。

在 Go 中還有不少的隱式語句塊:

  • 主語句塊:包括全部源碼,對應內置做用域
  • 包語句塊:包括該包中全部的源碼(一個包可能會包括一個目錄下的多個文件),對應包級做用域
  • 文件語句塊:包括該文件中的全部源碼,對應文件級做用域
  • for 、if、switch等語句自己也在它自身的隱式語句塊中,對應局部做用域

類型轉換

  • 不存在隱式轉換,必須是顯示
  • 類型轉換必須是在兩種兼容的類型之間
  • <變量名稱> [:]= <目標類型>( <須要轉換的變量名> )

類型轉換精度丟失

類型斷言

斷言,顧名思義就是果斷的去猜想一個未知的事物。在 go 語言中,interface{} 就是這個神祕的未知類型,其斷言操做就是用來判斷 interface{} 的類型。

var foo interface{} = 22

    f, ok := foo.(int)
    if !ok {
        t.Log("Guess wrong ...")
    }
    t.Logf("The type is : %T", f)

常量

  • 顯示 const idenfity [type] = value
  • 隱式 const identify = value () (無類型常量)

變量類型支持: bool, int, float, string

特殊常量 iota

運算

算術運算

運算符 描述 實例
+ 相加 A + B 輸出結果 30
- 相減 A - B 輸出結果 -10
* 相乘 A * B 輸出結果 200
/ 相除 B / A 輸出結果 2
% 求餘 B % A 輸出結果 0
++ 自增 A++ 輸出結果 11
-- 自減 A-- 輸出結果 9

關係運算

運算符 描述 實例
== 檢查兩個值是否相等,若是相等返回 True 不然返回 False。 (A == B) 爲 False
!= 檢查兩個值是否不相等,若是不相等返回 True 不然返回 False。 (A != B) 爲 True
> 檢查左邊值是否大於右邊值,若是是返回 True 不然返回 False。 (A > B) 爲 False
< 檢查左邊值是否小於右邊值,若是是返回 True 不然返回 False。 (A < B) 爲 True
>= 檢查左邊值是否大於等於右邊值,若是是返回 True 不然返回 False。 (A >= B) 爲 False
<= 檢查左邊值是否小於等於右邊值,若是是返回 True 不然返回 False。 (A <= B) 爲 True

邏輯運算

運算符 描述 實例
&& 邏輯 AND 運算符。 若是兩邊的操做數都是 True,則條件 True,不然爲 False。 (A && B) 爲 False
\ \ 邏輯 OR 運算符。 若是兩邊的操做數有一個 True,則條件 True,不然爲 False。 (A \ \ B) 爲 True
! 邏輯 NOT 運算符。 若是條件爲 True,則邏輯 NOT 條件 False,不然爲 True。 !(A && B) 爲 True

位運算

運算符 描述 實例
& 按位與運算符"&"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相與。 (A & B) 結果爲 12, 二進制爲 0000 1100
\ 按位或運算符"\ "是雙目運算符。 其功能是參與運算的兩數各對應的二進位相或 (A \ B) 結果爲 61, 二進制爲 0011 1101
^ 按位異或運算符"^"是雙目運算符。 其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果爲1。 (A ^ B) 結果爲 49, 二進制爲 0011 0001
<< 左移運算符"<<"是雙目運算符。左移n位就是乘以2的n次方。 其功能把"<<"左邊的運算數的各二進位所有左移若干位,由"<<"右邊的數指定移動的位數,高位丟棄,低位補0。 A << 2 結果爲 240 ,二進制爲 1111 0000
>> 右移運算符">>"是雙目運算符。右移n位就是除以2的n次方。 其功能是把">>"左邊的運算數的各二進位所有右移若干位,">>"右邊的數指定移動的位數。 A >> 2 結果爲 15 ,二進制爲 0000 1111

賦值運算

運算符 描述 實例
= 簡單的賦值運算符,將一個表達式的值賦給一個左值 C = A + B 將 A + B 表達式結果賦值給 C
+= 相加後再賦值 C += A 等於 C = C + A
-= 相減後再賦值 C -= A 等於 C = C - A
*= 相乘後再賦值 C = A 等於 C = C A
/= 相除後再賦值 C /= A 等於 C = C / A
%= 求餘後再賦值 C %= A 等於 C = C % A
<<= 左移後賦值 C <<= 2 等於 C = C << 2
>>= 右移後賦值 C >>= 2 等於 C = C >> 2
&= 按位與後賦值 C &= 2 等於 C = C & 2
^= 按位異或後賦值 C ^= 2 等於 C = C ^ 2
\ = 按位或後賦值 C \ = 2 等於 C = C \ 2

優先級

優先級 運算符 功能
9 () [] -> . 後綴運算
8 ! *(指針) & ++ -- +(正號) -(負號) 單目運算
7 * / % + - 算術運算,加減乘除
6 << >> 位運算
5 == != < <= > >= 邏輯運算、不等、等
4 & \ ^ 按位 邏輯與、或
3 \ \ && 邏輯或、與
2 = += -= *= 等等 賦值運算
1 , 逗號

一元賦值 這兩大運算符是 從右到左 關聯,其餘都是 從左到右 關聯。

注意:優先級 值越大則優先級越高。爲了方便理解、記憶,我對沒有嚴格按照優先級製表,只是作了個大概!!
更詳細的

代碼控制語句

if, else, else if

var number int = 37
    if number += 4; 10 > number {
        fmt.Print("less than 10:", number)
    } else if 10 < number {
        number -= 2
        fmt.Print("greater 10:", number)
    } else {
        
    }

switch, select

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    ia := []interface{}{byte(6), 'a', uint(10), int32(-4), "CC"}
    v := ia[rand.Intn(4)]
    
    // 值 switch
    switch v {
        case 'a' :
            fmt.Println("char: ", v)
        case 10 :
            fmt.Println("uint: ", v)
        case -4 :
            fmt.Println("int: ", v)
        case 0.1 :
            fallthrough
        caes "0.1"
            fmt.Println("float: ", v)
        default :
            fmt.Println("byte: ", v)
    }
    
    // 變量類型 switch
    switch interface{}(v).(type) {
    case string :
        fmt.Printf("Case A.")
    case byte :
        fmt.Printf("Case B.")
    case int :
        fmt.Printf("Case B.")
    default:
        fmt.Println("Unknown!")
    }
}

注意,go語言和其餘語言不一樣的時,每一個case代碼末尾會自動加上break 操做, 若是你須要使用 fallthrough 來抵消默認的 break

select 用於管道

for

是的 golangforforforeachfor inwhile於一體。

do while 表示:golang你這麼繞,不優雅
package main

import (
    "fmt"
    "time"
)

func main() {
    map1 := map[int]string{1: "Golang", 2: "Java", 3: "Python", 4: "C"}
    
    n := 1
    for {   // 省略則默認是true
        if n > 3 {
            break;
        }
        fmt.Println("for true map item: ", map1[n])
        time.Sleep(1)
        n++
    }
    
    for i := 1; i < 4; i++ {
        fmt.Println("for i map item: ", map1[i])
    }
    
    for k,v := range map1 {
        fmt.Print(k, ":", v)
    }
}

goto, break, continue

goto 是跳過代碼塊

package main

import (
    "fmt"
    "time"
)

func main() {
    code:
        fmt.Println("do some thing~")
        time.Sleep(1)
        
    goto code
}

break 跳出並結束循環

continue 跳過當前循環

雖然不能和 PHP那樣 break 2 跳出多層, 單隻要有goto就能幹不少事了。

golang給 循環 就分配了一個 for,語句跳轉語句卻整了那麼多花樣

複合數據

內建方法 make & new

內建方法就是不須要引入包就能用的

make 能夠建立 slice、map、chan,返回指針類型

  • slice 是可變長的數組
  • map 是key-map 數據數組
  • chan 是go獨有的 管道
一股c編程風格撲面而來, char ptr = (char )malloc(sizeof(char) * 5);

內建方法 new

內存置0,返回傳入類型的指針地址

package main
import fmt
import reflect

func main() {
    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)
    
    mMap := make(map[int]string)
    mMap[10] = "dog"
    mMap['2'] = "cat"
    fmt.Println(reflect.TypeOf(mMap))
    fmt.Println("animals :: ", mMap)
    
        
    nMap := new(map[int]string)
    fmt.Println(reflect.TypeOf(nMap))
}

append copy delete

slice可使用copy,append 函數

delete 是專門用來刪除 map

  • append(src, ele) 追加元素
  • copy(dst, src) 把src元素賦值到dst上,
  • delete() 刪除元素

例子:

package main
import "fmt"

func main() {
    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)

    // append(mSlice, "id-3")   // 這樣寫會致使報錯: append(mSlice, "id-3") evaluated but not used
    mSlice = append(mSlice, "id-3")
    fmt.Println("animals update:", mSlice)
    fmt.Println("animals len :", len(mSlice))
    fmt.Println("animals cap:", cap(mSlice))
    
    // newSlice := make([]string)      // 這樣寫致使報錯:missing len argument to make([]string)
    // newSlice := make([]string, 2)       // 這樣寫會致使數據丟失2個,不會自動擴容
    newSlice := make([]string, 3)       // 不要屢次定義初始化:no new variables on left side of :=
    copy(mSlice, newSlice)          // 這樣反向copy,會致使前面的幾個數組元素被置爲空
    // copy(newSlice, mSlice)
    fmt.Println("animals dst:", mSlice)
    fmt.Println("animals copy:", newSlice)
    
    delete(mMap, 50)
    fmt.Println(mMap)
}

panic & recover

異常處理

panic() 拋出異常

recover() 獲取異常

報錯會致使程序代碼中斷,不會再執行後續操做

例子:

package main

import "fmt"
import "errors"

func panicFunc() {
    defer func() {
        // recover()
        message := recover()    // 聲明瞭message 變量就須要使用哦,否則報錯
        fmt.Println("panice msg: ", message)
        
        switch message.(type) {
            case string:
            case error:
                fmt.Println("panice error msg: ", message)
            default:
        }
    }()
    // panic("報錯啦")
    panic(errors.New("I am error."))
}

func main() {

    panicFunc()
}

len & cap & close

len能夠計算 string, array, slice, map, chan
cap 能夠計算 slice, map, chan

  • len() 獲取數組長度
  • cap() 獲取佔用空間分配
  • close() 用於關閉管道——chan

當聲明一個數組時,go會預先分配一部分空間給當前數組,獲取實際空間佔用大小,使用cap()

不用像PHP那樣,strlen(), count(), length 傻傻分不清楚了。

例子:

package main

import "fmt"

func main() {

    mSlice := make([]string, 3)
    mSlice[0] = "dog"
    mSlice[1] = "cat"
    mSlice[2] = "pig"
    fmt.Println("animals: ", mSlice)

    fmt.Println("animals update:", mSlice)
    fmt.Println("animals len :", len(mSlice))
    fmt.Println("animals cap:", cap(mSlice))

    mChan := make(chan int, 1)
    close(mChan)
    mChan <- 1      // 會致使報錯: panic: send on closed channel
}

defer

定一個當前方法關閉時,運行的代碼, 壓棧設計,先聲明的後執行。

結構體

package main

import "fmt"

type Dog struct {
    ID int
    Name string
    Age int32
}

func main() {
    
    var dog Dog
    dog.ID = 1
    dog.Name = "haha"
    dog.Age = 3
    fmt.Println("print Dog Struct", dog)

    dog2 := Dog{ID:2, Name:"san", Age:4}
    fmt.Println("print Dog 2 Struct", dog2)
    
    dog3 := new(Dog)
    dog3.ID = 3
    dog3.Name = "Tom"
    dog3.Age = 5
    fmt.Println("print Dog 3 Struct", dog)
}

輸出

print Dog Struct {1 haha 3}
print Dog 2 Struct {2 san 4}
print Dog 3 Struct &{3 Tom 5}

屬性 & 函數

接口

/* define an interface */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   ...
   method_namen [return_type]
}

/* define a struct */
type struct_name struct {
   /* variables */
}

/* implement interface methods*/
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* method implementation */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* method implementation */
}

併發

指針

json

須要引入包 encoding/json, 兩個函數分別是 json.Marshal(), json.Unmarshal().

注意,最後一個是英文字母小寫的 L,不是 1

json 序列化

package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string
    SerIp   string
    SerPort uint16
}

func main() {
    server := new(ServerInfo)
    server.SerName = "http-nginx"
    server.SerIp = "127.0.0.1"
    server.SerPort = 8080
    re,err := json.Marshal(server)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("struct json bytes: ", re)
        fmt.Println("struct json string: ", string(re))
    }
    
    mServer := make(map[string]interface{})
    mServer["serverName"] = "apache2-http"
    mServer["serIp"] = "192.168.30.133"
    mServer["serPort"] = "3033"
    mRe,err := json.Marshal(mServer)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("map json string: ", string(mRe))
    }
}

輸出

struct json bytes:  [123 34 83 101 114 78 97 109 101 34 58 34 104 116 116 112 45 110 103 105 110 120 34 44 34 83 101 114 73 112 34 58 34 49 48 46 49 48 48 46 49 55 46 50 55 58 51 48 48 48 49 34 44 34 83 101 114 80 111 114 116 34 58 56 48 56 48 125]
struct json string:  {"SerName":"http-nginx","SerIp":"10.100.17.27:30001","SerPort":8080}
map json string:  {"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}
ps: 我也不知道 10.100.17.27:30001 是怎麼回事

json 反序列化

可使用 tag 來作 mapping,
package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string  `json:"name"`
    SerIp   string  `json:"ip"`
    SerPort uint16  `json:"port"`
}

func main() {
    // jsonStr := "{\"SerName\":\"http-nginx\",\"SerIp\":\"10.100.17.27:30001\",\"SerPort\":8080}"   \\ 雙引號注意轉義
    jsonStr := "{\"name\":\"http-nginx\",\"ip\":\"10.100.17.27:30001\",\"port\":8080}"
    
    sServer := new(ServerInfo)
    jsonBytes := []byte(jsonStr)
    uerr := json.Unmarshal(jsonBytes, &sServer)
    if nil != uerr {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("uns struct: ", sServer)
    }
    
    jsonStr3 := `{"serIp":"192.168.30.133","serPort":"3033","serverName":"apache2-http"}`   \\ 使用鍵盤1旁邊的 ` 符號包裹雙引號就不用轉義了
    
    uSer := make(map[string]interface{})
    uErr := json.Unmarshal([]byte(jsonStr3), &uSer)
    if nil != uErr {
        fmt.Println("error: ", uErr.Error())
    } else {
        fmt.Println("unmar map: ", uSer)
    }
}

輸出

uns struct:  &{http-nginx 10.100.17.27:30001 8080}
unmar map:  map[serIp:192.168.30.133 serPort:3033 serverName:apache2-http]

tag

tag 這個東東把,就是json的別名,感受這個功能是go的特點,與encoding/json包緊密結合。

爲何會有這個東西,我估計是這個和 go命名規則 有關,go命名規則,要求public的變量開頭要大寫,小寫開頭的變量是private的,因此,json中的變量就會影響一個接口體變量的訪問權限,爲了避免像java那樣複雜,提供了方便的tag功能。

package main

import "fmt"
import "encoding/json"

type ServerInfo struct {
    SerName string  `json:"name"`
    SerIp   string  `json:"ip"`
    SerPort uint16  `json:"port"`
}

func main() {
    
    server := new(ServerInfo)
    server.SerName = "http-nginx"
    server.SerIp = "127.0.0.1"
    server.SerPort = 8080
    re,err := json.Marshal(server)
    if nil != err {
        fmt.Println("error: ", err.Error())
    } else {
        fmt.Println("struct json string: ", string(re))
    }
}

輸出

struct json string:  {"name":"http-nginx","ip":"10.100.17.27:30001","port":8080}
map json strin

go 特點語法

_

  • _ 變量

這就比如是Linux 裏的 /dev/null, 因爲go語言要求聲明的變量必須被使用,返回的變量必須被接收,那麼真有個變量沒用但必需要接受怎麼辦呢,就把返回的參數給他。例如:

package main
import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
    for _, v := range pow {
        fmt.Printf("value is %d\n", v)
    }
}

這裏咱們只要值,不要key的信息,返回的key不能不收不是,但我也不像把它輸出出來,就讓 _ 來接收好了。

  • _ 包

引入包, 並不直接使用這個包,運行時執行一次它的 init() 函數,

import (
    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
)

參考

相關文章
相關標籤/搜索