理解以下知識,初步寫簡單go專案就足夠了

語言參考(基礎篇)

基本語法

基本組成

  • 包宣告
  • 引入包
  • 函式
  • 變數
  • 語句 & 表示式
  • 註釋
  • 其它(可忽略)
    • go沒有像php、js那樣需要在檔案開始申明 <?php ... 或者 <script>
    • 註釋:多行 /* * 單行 //
    • 字串連結符+
    • 標記:可以是關鍵字,識別符號,常量,字串,符號。
      • 識別符號:識別符號用來命名變數、型別等程式實體 ;一個識別符號實際上就是一個或是多個字母(AZ和az)數字(0~9)、下劃線_組成的序列,但是第一個字元必須是字母或下劃線而不能是數字。

資料型別

  • 布林值:truefalse
  • 數字型別
  • 字串型別
  • 派生型別:依據上面是三個基礎型別,衍生出的新型別
    • 指標型別(Pointer)
    • 陣列型別(陣列)
    • 結構化體型別(結構體)
    • Channel型別
    • 函式型別
    • 切片型別
    • 介面型別
    • Map型別
布林型別

布林型別就只有真假,truefalse

資料型別

會根據系統架構(如32位、64位系統)變化的型別

  • int
  • uint
  • uintptr

明確範圍的數字型別

  • 整型

    • 無符號型

      • uint8 範圍(0 ~255) 2百
      • uint16 範圍(0 ~ 65535) 6萬
      • uint32 範圍(0 ~ 4294967295) 42億
      • uint64 範圍(0 ~ 18446744073709551615)184京
    • 有符號型(範圍最大值小減半)
      • int8
      • int16
      • int32
      • int64
  • 浮點型
  • 其它數字型別
    • byte =》 uint8
    • rune =》 int32
    • uint
    • int
    • uintptr 無符號型

變數

定義

var identifier type
var identifier1, identifier2 type

說明

變數生命關鍵字 var

變數名稱:identifier

變數型別:type

常用的幾種宣告方式

第一種:標準定義

指定變數型別,如果沒有初始化,則變數預設為零值。

//var identifier type
var a int
//a值為0
  • 數值型別(包括complex64/128)為 0
  • 布林型別為 false
  • 字串為 ""(空字串)
  • 以下幾種型別為 nil
第二種:自適應

根據值自行判定變數型別。

//var identifier = value
var a = 100
//a值為100 型別 int
第三種: :=

不使用 var 宣告,用 := 宣告變數

//value_name := value
a := 100
//等同於
// var a int
// a = 100
//注意:所以這裡的a如果被宣告過,這裡再次var宣告就會報錯

派生型別宣告定義方式不同,後面會講到

多個變數宣告

//法一
var a,b,c int
//-----環境分割線---------------------
//法二
var a,b,c = 100,200,300
//-----環境分割線---------------------
//法三
a,b,c := 100,200,300
//法四
//下面這種因式分解關鍵字的寫法一般用於宣告全域性變數
var (
vname1 v_type1
vname2 v_type2
)

常量

定義

const identifier [type] = value
//如果不定義型別,會根據值自動確定型別

特殊常量 iota ,每次出現自增,但是每次CONST關鍵字出現時,將會被重置為0

表示式

變數使用

作用域

Go 語言中變數可以在三個地方宣告:

  • 函式內定義的變數稱為區域性變數
  • 函式外定義的變數稱為全域性變數
  • 函式定義中的變數稱為形式引數

定義

字串、數字、布林
//字串
var a = "abc"
//數字
var b = 1
//布林
var c = true

總結:字面量的型別定義使用常規定義就行var identifier = value ,變種不過多闡述。

//注意:  字串使用雙引號;單引號一般使用單個字元,並且還不是字串型別,而是資料型別(type untyped rune,值為ASCII 碼對應值)。
陣列

陣列是具有相同唯一型別的一組已編號且長度固定的資料項序列,這種型別可以是任意的原始型別例如整型、字串或者自定義型別

//宣告: var variable_name [SIZE] variable_type
var calss_member [60] string
//賦值: variable_name[0] = "Tom" //初始化
var class_member_1 =[5]string{"Jim","Tom","Neo","Lucy","Mei"}
class_member_2 :=[5]string{"Jim","Tom","Neo","Lucy","Mei"}
//不確定長度
class_member_3 =[...]string{"Jim","Tom","Neo","Lucy","Mei"}

總結:

  • {}中元素的個數,不能大於[]中指定的長度
  • 不知道長度時,可以使用...代替
  • 訪問資料使用下標索引,從0開始
//多維陣列
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type //初始化 例子
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引為 0 */
{4, 5, 6, 7} , /* 第二行索引為 1 */
{8, 9, 10, 11}, /* 第三行索引為 2 */
}
//那麼[2][2][2][2][2]是什麼樣子呢?
a := [2][2][2][2][2] int {
{
{
{
{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},
},
},
},
}
指標

一個指標變數指向了一個值的記憶體地址。

所以指標變數儲存的是地址,同時操作的也是地址(上的值),因為地址無法修改。當一個指標被定義後沒有分配到任何變數時,它的值為 nil。

//舉例:jack的錢包裡有100元
var jack int
var point_jack *int
jack = 100
point_jack = &jack //拿到錢包
*point_jack = 50 //操作錢包中的錢,放50元
fmt.Println(jack) //jack只有五十了
// 50

使用

//定義
// var var_name *var-type
var point_jack *int
// & 取地址
var num = 100
point_jack = &num
// * 使用
fmt.Println(*point_jack)
// 操作(不能掉了符號*)
*point_jack = 50
結構體

結構體是由一系列具有相同型別或不同型別的資料構成的資料集合。

/* 定義  */
type struct_variable_type struct {
member definition
member definition
...
member definition
}
// 例子,如一個學生有ID、名字、學號、自我介紹
type student struct {
id int
name string
code string
dis string
} /* 變數宣告 */
variable_name := structure_variable_type {value1, value2...valuen}

variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
//例子
Jack := student{1,"Jack","10001","一個叫jack的孩子"}
Neo := student{id:2,naem:"Neo",code:"10002",dis:"一個叫Neo的孩子"} /* 使用 */
variable_name.key
//例子
Jack.dis

結構體在函式中的使用

func main()  {
Jack := student{1,"Jack","10001","一個叫jack的孩子"}
fmt.Println(Jack.dis)
var jk *student
var num *int
code := 1231231;
num = &code
jk = &Jack
show(jk,num)
} func show(stu *student,id *int) {
fmt.Println(stu.dis)
fmt.Println(*id)
} /* 函式執行結果 */
//一個叫jack的孩子
//一個叫jack的孩子
//1231231

總結:

  • 結構體是一個指定的資料結構,訪問時使用符號.訪問
  • 指標結構體訪問和普通結構體訪問沒區別,但是基本型別指標訪問值需要加字首符號*
切片(slice)

Go 語言切片是對陣列的抽象。陣列的長度是固定的,切片的長度是不固定的。一個切片在未初始化之前預設為 nil,長度為 0

/* 定義  */
var identifier []type
// 其它定義方式
var slice1 []type = make([]type, len)
slice1 := make([]type, len)
make([]T, length, capacity) // 初始化(變數宣告)
variable_name := [] variable_type {value1, value2...valuen}
variable_name := 陣列[start_index:end_index]
//例子
name := []string{"Tom","Jim","Neo"}
arr := [5]int{1,2,3,4,5}
slice_arr :=arr[2:4] /* 使用 */
variable_name[index]
//例子
fmt.Println(name);
fmt.Println(slice_arr);
fmt.Println(slice_arr[0]); -------
[Tom Jim Neo]
[3 4]
3

從上面可以看到切片常用的操作

/*
擷取 [start_index:end_index] 明顯索引範圍是半包圍,即 start_index 到 end_index -1 // 常見的擷取如下:
[start_index:end_index]
[start_index:]
[:end_index] // len() 和 cap() 函式
var numbers = make([]int,3,5)
len(numbers) => 3
cap(numbers) => 5
fmt.Println(numbers) => [0,0,0] //空(nil)切片 //nil 空值,未賦值前變數的值
var numbers []int
if(numbers == nil){
fmt.Printf("切片是空的")
}
*/ //append() 和 copy() 函式
slice_int1 := [] int //空切片, []
slice_int1 = append(slice_int1,1) //[1]
slice_int2 = []int{10,11,12}
copy(slice_int1,slice_int2) // slice_int1 => [1,10,11,12]
範圍(rang)

Go 語言中 range 關鍵字用於 for 迴圈中迭代陣列(array)、切片(slice)、通道(channel)或集合(map)的元素。在陣列和切片中它返回元素的索引和索引對應的值,在集合中返回 key-value 對。

//有點類似 PHP語言的 as ,使用方法如下
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
fmt.Println(num)
}
集合(map)

Map 是一種無序的鍵值對的集合。

/* 定義 */
// 宣告變數;預設 map 是 nil 和切片,指標一樣
var variable map[key_type]value_type // make 函式
variable := make(map[key_type]value_type) /* 初始化(變數宣告) */
var name_CN map[string]string
name_CN['Jim'] = "張三"
name_CN['Lilei'] = "李雷"
name_CN['Hanmeimei'] = "韓梅梅" num_EN := map[string]string{"one":1,"two":2} /* 刪除元素: delete() 函式 */
delete(map,index) delete(name_CN,"Jim")

型別轉換

型別轉換用於將一種資料型別的變數轉換為另外一種型別的變數。

type_name(expression)

/* 例如 */

var money = 199.99
var pay int pay = int(money)
fmt.Println(pay)
//199

運算子

  • 算術運算子:(假定有變數 a 和 b ,a=100, b=200)

    • + a+b
    • - a-b
    • * a*b
    • / a/b
    • % a%b
    • ++ a++
    • -- a--
  • 關係運算符

    • ==
    • !=
    • >
    • <
    • >=
    • <=
  • 邏輯運算子

    • &&
    • ||
    • !
  • 位運算子

    • &
    • |
    • ^
    • <<
    • >>
  • 賦值運算子

    • =
    • 字首= a+=b ;a*=b 等
  • 其他運算子

    • & 取地址
    • * 地址指向值

流程控制

if

if 布林表示式 {
/* 在布林表示式為 true 時執行 */
} // 例如,a=100 if a > 50 {
fmt.Printf("> 50")
}

if...else

if 布林表示式 {
/* 在布林表示式為 true 時執行 */
} else {
/* 在布林表示式為 false 時執行 */
} // 例如,a=100 if a > 50 {
fmt.Printf("> 50")
}else{
fmt.Printf("< 50")
}

if 巢狀

if 布林表示式 1 {
/* 在布林表示式 1 為 true 時執行 */
if 布林表示式 2 {
/* 在布林表示式 2 為 true 時執行 */
}
} // 例如,a=100
if a > 50 {
fmt.Printf("> 50")
if a > 80{
fmt.Printf("> 80")
}
}

switch

switch var1 {
case val1:
...
case val2:
...
default:
...
}
// 例如,成績有 S,A,B,C,D zhangsan ='B'
switch zhangsan {
case 'S':
fmt.Printf("優")
case 'A':
fmt.Printf("良")
case 'B':
fmt.Printf("一般")
case 'C','D':
fmt.Printf("不及格")
default:
fmt.Printf("沒有成績")
} //fallthrough 強制執行,繼續執行後面的選項,並且下一個還是強制執行
package main import "fmt" func main() { var a = 'B' switch a {
case 'S':
fmt.Println("S")
fallthrough
case 'A':
fmt.Println("A")
case 'B':
fmt.Println("B")
fallthrough
case 'C':
fmt.Println("C")
case 'D':
fmt.Println("D")
fallthrough
default:
fmt.Println("other")
}
}
//
B
C

select

後期體會

for迴圈

//for
for init; condition; post { } for condition { } for { } //搭配使用 break continue goto //例子:
package main import "fmt" func main() { var i int
for i = 0; i<10;i++ {
fmt.Println(i)
}
for ; i<20;{
i++
fmt.Println(i)
}
for i<30 {
i++
if(i%2 == 0){
continue
}
fmt.Println(i)
} for {
i++
fmt.Println(i)
if i>40 {
break
}
}
goto LOOP
fmt.Println("你可能出不來")
LOOP:fmt.Println("果然出不來")
}

函式

函式是基本的程式碼功能塊,用來實現一個基本功能。

/* 定義 */
func function_name(param type [,param type,param type])[return_type]{ }
/* 例子 */
func double(x int)int {
return x*2
}
func changePosition(x,y int)(int,int) {
return y,x
}

介面

Go 語言提供了另外一種資料型別即介面,它把所有的具有共性的方法定義在一起,任何其他型別只要實現這些方法就是實現了這個介面

/* 定義 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
} type struct_name struct { }
/* 實現 */
func (struct_varibale_name struct_name) method_name()[return_type]{ } /* 示例 */

異常處理

內建的go錯誤處理介面

type error interface {
Error() string
}

舉例

package main

import "fmt"
import "errors" func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: error")
}
return f*f , errors.New("math: ok")
} func main(){
result, err:= Sqrt(22)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
} //math: ok
//484

併發

Go 語言支援併發,我們只需要通過 go 關鍵字來開啟 goroutine 即可。

goroutine

goroutine 是輕量級執行緒,goroutine 的排程是由 Golang 執行時進行管理的。

goroutine 語法格式:

/* 定義 */
go 函式名( 引數列表 ) /* 例子 */
package main
import "fmt"
import "time" func message(message string){
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
fmt.Println(message)
}
}
func main() {
message("000")
go message("111")
message("222")
} // 輸出
000
000
000
000
000
111
222
222
111
111
222
111
222
222
111
//結論:1.主流程不受影響,還是順序執行;2.新啟動的流程和主流程同時在執行
channel

通道(channel)是用來傳遞資料的一個數據結構。

通道可用於兩個 goroutine 之間通過傳遞一個指定型別的值來同步執行和通訊。

操作符 <- 用於指定通道的方向,傳送或接收。如果未指定方向,則為雙向通道。

package main
import "fmt"
import "time" func tmpCache(a int,b int ,cache chan int){
num := 0 fmt.Println("node 2:")
fmt.Println(time.Now()) time.Sleep(3 * time.Second) num = (a+b)*(a-b)/2
cache <- num
}
func main() {
message :=make(chan int)
count := 0 fmt.Println("node 1:")
fmt.Println(time.Now()) go tmpCache(1,100,message) fmt.Println("node 3:")
fmt.Println(time.Now()) // time.Sleep(1 * time.Second) count = <- message
fmt.Println("node 4:")
fmt.Println(time.Now()) fmt.Println(count) }
//結果
node 1:
2021-09-15 16:48:35.746247 +0800 CST m=+0.000101265
node 3:
2021-09-15 16:48:35.746418 +0800 CST m=+0.000271887
node 2:
2021-09-15 16:48:35.746425 +0800 CST m=+0.000278833
node 4:
2021-09-15 16:48:38.748364 +0800 CST m=+3.002355026
-4999
// 注意點:node 2 到 node 4 有3秒;說明阻塞了3秒
package main
import "fmt" func tmpCache(a int,b int ,cache chan int){
num := 0
// time.Sleep(3 * time.Second)
num = (a+b)*(a-b)/2
cache <- num
close(cache)
}
func main() {
message :=make(chan int) go tmpCache(0,5,message)
go tmpCache(5,10,message)
go tmpCache(10,15,message)
go tmpCache(15,20,message)
go tmpCache(20,25,message)
//close(message)
fmt.Println("===================")
for i := range message{
fmt.Println(i)
}
} //結果1
===================
-12
-62
-37
-87
-112
//結果2
===================
-12
panic: send on closed channel goroutine 22 [running]:
main.tmpCache(0x0, 0x0, 0x0)
/Users/liupeng/go/test/src/heelo/func_channel1.go:8 +0x45
created by main.main
/Users/liupeng/go/test/src/heelo/func_channel1.go:19 +0x1cf
exit status 2
//結果3
===================
-12
-37
-62

總結:

  • 通道訊息是按佇列進出的
  • rang使用到通道時,會有異常關閉情況,如結果3明顯是沒有跑完,程序就關閉了;所以開啟的程序數是1時可以(在非同步函式中)關閉程序