理解以下知識,初步寫簡單go專案就足夠了
語言參考(基礎篇)
基本語法
基本組成
- 包宣告
- 引入包
- 函式
- 變數
- 語句 & 表示式
- 註釋
- 其它(可忽略)
- go沒有像php、js那樣需要在檔案開始申明
<?php ...
或者<script>
- 註釋:多行
/* *
單行//
- 字串連結符
+
- 標記:可以是關鍵字,識別符號,常量,字串,符號。
- 識別符號:識別符號用來命名變數、型別等程式實體 ;一個識別符號實際上就是一個或是多個字母(AZ和az)數字(0~9)、下劃線_組成的序列,但是第一個字元必須是字母或下劃線而不能是數字。
- go沒有像php、js那樣需要在檔案開始申明
資料型別
- 布林值:
true
和false
- 數字型別
- 字串型別
- 派生型別:依據上面是三個基礎型別,衍生出的新型別
- 指標型別(Pointer)
- 陣列型別(陣列)
- 結構化體型別(結構體)
- Channel型別
- 函式型別
- 切片型別
- 介面型別
- Map型別
布林型別
布林型別就只有真假,true
和 false
資料型別
會根據系統架構(如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時可以(在非同步函式中)關閉程序