1. 程式人生 > >Golang系列(一)之基礎篇

Golang系列(一)之基礎篇

一、初識Go語言

(一)概述

一個在語言層面實現了併發機制的類C通用型程式語言。

(二)Go關鍵字(25個)

類別關鍵字說明
程式宣告package,import包的宣告和匯入
宣告與定義var,const變數和常量的宣告
type用於定義型別
複合資料型別struct定義結構體,類似java中的class
interface定義介面
map定義鍵值對
func定義函式和方法
chan定義管道,併發中channel通訊
併發程式設計go併發程式設計
select用於選擇不同型別通訊
流程控制for;if,else;switch,case迴圈語句;條件語句;選擇語句
break,continue,fallthrough,default,goto跳轉語句等
return函式返回值
defer延遲函式,用於return前釋放資源
range用於讀取slice,map,channel容器類資料

(三)Go語言命令

Usage:go command [arguments]

分類命令說明
buildcompile packages and dependencies
cleanremove object files
docshow documentation for package or symbol
envprint Go environment information
fixrun go tool fix on packages
fmtrun gofmt on package sources
generategenerate Go files by processing source
getdownload and install packages and dependencies
installcompile and install packages and dependencies
listlist packages
runcompile and run Go program
testtest packages
toolrun specified go tool
versionprint Go version
vetrun go tool vet on packages

二、順序程式設計

1.變數

1.1變數宣告

//1、單變數宣告,型別放在變數名之後,可以為任意型別
var 變數名 型別
var v1,v2,v3 string //多變數同類型宣告
//2、多變數宣告
var {
    v1 int
    v2 []int
}

1.2變數初始化

//1、使用關鍵字var,宣告變數型別並賦值
var v1 int=10
//2、使用關鍵字var,直接對變數賦值,go可以自動推匯出變數型別
var v2=10
//3、直接使用“:=”對變數賦值,不使用var,兩者同時使用會語法衝突,推薦使用
v3:=10

1.3變數賦值

//1、聲明後再變數賦值
var v int
v=10
//2、多重賦值,經常使用在函式的多返回值中,err,v=func(arg)
i,j=j,i  //兩者互換,並不需要引入中間變數

1.4匿名變數

//Go中所有聲明後的變數都需要呼叫到,當出現函式多返回值,並且部分返回值不需要使用時,可以使用匿名變數丟棄該返回值
func GetName()(firstName,lastName,nickName string){
  return "May","Chan","Make"
}
_,_,nickName:=GetName()  //使用匿名變數丟棄部分返回值

2.常量

​ Go語言中,常量是編譯時期就已知且不可變的值,常量可以是數值型別(整型、浮點型、複數型別)、布林型別、字串型別。

2.1字面常量

//字面常量(literal)指程式中硬編碼的常量
3.14
“foo”
true

2.2常量定義

//1、可以限定常量型別,但非必需
const Pi float64 = 3.14
//2、無型別常量和字面常量一樣
const zero=0.0
//3、多常量賦值
const(
  size int64=1024
  eof=-1
)
//4、常量的多重賦值,類似變數的多重賦值
const u,v float32=0,3
const a,b,c=3,4,"foo"    //無型別常量的多重賦值
//5、常量賦值是編譯期行為,可以賦值為一個編譯期運算的常量表達式
const mask=1<<3

2.3預定義常量

//預定義常量:true、false、iota
//iota:可修改常量,在每次const出現時被重置為0,在下一個const出現前,每出現一次iota,其代表的值自動增1。
const(          //iota重置為0
  c0=iota       //c0==0
  c1=iota       //c1==1
  c2=iota       //c2==2
)
//兩個const賦值語句一樣可以省略後一個
const(          //iota重置為0
  c0=iota       //c0==0
  c1            //c1==1
  c2            //c2==2
)

2.4列舉

列舉指一系列相關常量。

const(
  Sunday=iota    //Sunday==0,以此類推
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday       //大寫字母開頭表示包外可見
  numberOfDays   //小寫字母開頭表示包內私有
)

3.型別

3.1基礎型別

3.1.1布林型別
//布林型別的關鍵字為bool,值為true或false,不可寫為0或1
var v1 bool
v1=true
//接受表示式判斷賦值,不支援自動或強制型別轉換
v2:=(1==2)
3.1.2整型

這裡寫圖片描述

//1、型別表示
//int和int32為不同型別,不會自動型別轉換需要強制型別轉換
//強制型別轉換需注意精度損失(浮點數→整數),值溢位(大範圍→小範圍)
var v2 int32
v1:=64
v2=int32(v1)

//2、數值運算,支援“+,-,*,/和%”
5%3 //求餘

//3、比較運算,“<,>,==,>=,<=,!=”
//不同型別不能進行比較例如int和int8,但可以與字面常量(literal)進行比較
var i int32
var j int64
i,j=1,2
if i==j  //編譯錯誤,不同型別不能進行比較
if i==1 || j==2  //編譯通過,可以與字面常量(literal)進行比較

//4、位運算
//Go(^x)取反與C語言(~x)不同,其他類似,具體見下表

這裡寫圖片描述

3.1.3浮點型
//1、浮點型分為float32(類似C中的float),float64(類似C中的double)
var f1 float32
f1=12     //不加小數點,被推導為整型
f2:=12.0  //加小數點,被推導為float64
f1=float32(f2)  //需要執行強制轉換
//2、浮點數的比較
//浮點數不是精確的表達方式,不能直接使用“==”來判斷是否相等,可以借用math的包math.Fdim 
3.1.4複數型別
//1、複數的表示
var v1 complex64
v1=3.2+12i
//v1 v2 v3 表示為同一個數
v2:=3.2+12i
v3:=complex(3.2,12)
//2、實部與虛部
//z=complex(x,y),通過內建函式實部x=real(z),虛部y=imag(z)
3.1.5字串
//宣告與賦值
var str string
str="hello world"

這裡寫圖片描述

3.1.6字元型別
//1、byte,即uint8的別名
//2、rune,即Unicode
3.1.7錯誤型別(error)

3.2複合型別

3.2.1陣列(array)

陣列表示同一型別資料,陣列長度定義後就不可更改,長度是陣列內的一個內建常量,可通過len()來獲取。

//1、建立陣列
var array1 [5]int    //宣告:var 變數名 型別
var array2 [5]int=[5]int{1,2,3,4,5}   //初始化
array3:=[5]int{1,2,3,4,5}    //直接用“:=”賦值
[3][5]int  //二維陣列
[3]*float  //指標陣列

//2、元素訪問
for i,v:=range array{
  //第一個返回值為陣列下標,第二個為元素的值
}

//3、值型別
//陣列在Go中作為一個值型別,值型別在賦值和函式引數傳遞時,只複製副本,因此在函式體中並不能改變陣列的內容,需用指標來改變陣列的值。
3.2.2切片(slice)

​ 陣列在定義了長度後無法改變,且作為值型別在傳遞時產生副本,並不能改變陣列元素的值。因此切片的功能彌補了這個不足,切片類似指向陣列的一個指標。可以抽象為三個變數:指向陣列的指標;切片中元素的個數(len函式);已分配的儲存空間(cap函式)。

//1、建立切片
//a)基於陣列建立
var myArray [5]int=[5]{1,2,3,4,5}
var mySlice []int=myArray[first:last]
slice1=myArray[:]   //基於陣列所有元素建立
slice2=myArray[:3]  //基於前三個元素建立
slice3=myArray[3:]  //基於第3個元素開始後的所有元素建立
//b)直接建立
slice1:=make([]int,5)       //元素初始值為0,初始個數為5
slice2:=make([]int,5,10)    //元素初始值為0,初始個數為5,預留個數為10
slice3:=[]int{1,2,3,4,5}    //初始化賦值
//c)基於切片建立
oldSlice:=[]int{1,2,3,4,5}
newSlice:=oldSlice[:3]   //基於切片建立,不能超過原切片的儲存空間(cap函式的值)

//2、元素遍歷
for i,v:=range slice{
  //與陣列的方式一致,使用range來遍歷
  //第一個返回值(i)為索引,第二個為元素的值(v)
}

//3、動態增減元素
//切片分儲存空間(cap)和元素個數(len),當儲存空間小於實際的元素個數,會重新分配一塊原空間2倍的記憶體塊,並將原資料複製到該記憶體塊中,合理的分配儲存空間可以以空間換時間,降低系統開銷。
//新增元素
newSlice:=append(oldSlice,1,2,3)   //直接將元素加進去,若儲存空間不夠會按上述方式擴容。
newSlice1:=append(oldSlice1,oldSlice2...)  //將oldSlice2的元素打散後加到oldSlice1中,三個點不可省略。

//4、內容複製
//copy()函式可以複製切片,如果切片大小不一樣,按較小的切片元素個數進行復制
slice1:=[]int{1,2,3,4,5}
slice2:=[]int{6,7,8}
copy(slice2,slice1)   //只會複製slice1的前三個元素到slice2中
copy(slice1,slice1)   //只會複製slice2的三個元素到slice1中的前三個位置
3.2.3鍵值對(map)

map是一堆鍵值對的未排序集合。

//1、先聲明後建立再賦值
var map1 map[鍵型別] 值型別
//建立
map1=make(map[鍵型別] 值型別)
map1=make(map[鍵型別] 值型別 儲存空間)
//賦值
map1[key]=value

// 直接建立
m2 := make(map[string]string)
// 然後賦值
m2["a"] = "aa"
m2["b"] = "bb"

// 初始化 + 賦值一體化
m3 := map[string]string{
    "a": "aa",
    "b": "bb",
}

//2、元素刪除
//delete()函式刪除對應key的鍵值對,如果key不存在,不會報錯;如果value為nil,則會丟擲異常(panic)。
delete(map1,key)  

//3、元素查詢
value,ok:=myMap[key]
if ok{//如果找到
  //處理找到的value值
}

//遍歷
for key,value:=range myMap{
    //處理key或value
}

map可以用來判斷一個值是否在切片或陣列中。

// 判斷某個型別(假如為myType)的值是否在切片或陣列(假如為myList)中
// 構造一個map,key的型別為myType,value為bool型
myMap := make(map[myType]bool)
myList := []myType{value1, value2}
// 將切片中的值存為map中的key(因為key不能重複),map的value都為true
for _, value := range myList {
    myMap[value] = true
}
// 判斷valueX是否在myList中,即判斷其是否在myMap的key中
if _, ok := myMap[valueX]; ok {
    // 如果valueX 在myList中,執行後續操作
}
3.2.4指標(pointer)
3.2.5結構體(struct)
3.2.6介面(interface)
3.2.7通道(chan)

4.流程語句

4.1條件語句

//在if之後條件語句之前可以新增變數初始化語句,用;號隔離
if <條件語句> {    //條件語句不需要用括號括起來,花括號必須存在
  //語句體
}else{
  //語句體
}

//在有返回值的函式中,不允許將最後的return語句放在if...else...的結構中,否則會編譯失敗
//例如以下為錯誤範例
func example(x int) int{
  if x==0{
    return 5
  }else{
    return x  //最後的return語句放在if-else結構中,所以編譯失敗
  }
}

4.2選擇語句

//1、根據條件不同,對應不同的執行體
switch i{
  case 0:
    fmt.Printf("0")
  case 1:                //滿足條件就會退出,只有新增fallthrough才會繼續執行下一個case語句
    fmt.Prinntf("1")
  case 2,3,4:            //單個case可以出現多個選項
    fmt.Printf("2,3,4")
  default:               //當都不滿足以上條件時,執行default語句
    fmt.Printf("Default")
}

//2、該模式等價於多個if-else的功能
switch {
  case <條件表達式1>:
    語句體1
  case <條件表達式2>:
    語句體2
}

4.3迴圈語句

//1、Go只支援for關鍵字,不支援while,do-while結構
for i,j:=0,1;i<10;i++{    //支援多個賦值
  //語句體
}

//2、無限迴圈
sum:=1
for{  //不接條件表示式表示無限迴圈
  sum++
  if sum > 100{
    break   //滿足條件跳出迴圈
  }
}

//3、支援continue和break,break可以指定中斷哪個迴圈,break JLoop(標籤)
for j:=0;j<5;j++{
  for i:=0;i<10;i++{
    if i>5{
      break JLoop   //終止JLoop標籤處的外層迴圈
  }
  fmt.Println(i)
}
JLoop:    //標籤處
...

4.4跳轉語句

//關鍵字goto支援跳轉
func myfunc(){
  i:=0
  HERE:           //定義標籤處
  fmt.Println(i)
  i++
  if i<10{
    goto HERE     //跳轉到標籤處
  }
}

5.函式

5.1函式定義與呼叫

//1、函式組成:關鍵字func ,函式名,引數列表,返回值,函式體,返回語句
//先名稱後型別
func 函式名(引數列表)(返回值列表){  //引數列表和返回值列表以變數宣告的形式,如果單返回值可以直接加型別
  函式體
  return    //返回語句
}
//例子
func Add(a,b int)(ret int,err error){
  //函式體 
  return   //return語句
}

//2、函式呼叫
//先匯入函式所在的包,直接呼叫函式
import "mymath"
sum,err:=mymath.Add(1,2)   //多返回值和錯誤處理機制
//可見性,包括函式、型別、變數
//本包內可見(private):小寫字母開頭
//包外可見(public):大寫字母開頭

5.2不定引數

//1、不定引數的型別
func myfunc(args ...int){   //...type不定引數的型別,必須是最後一個引數,本質是切片
  for _,arg:=range args{
    fmt.Println(arg)
  }
}
//函式呼叫,傳參可以選擇多個,個數不定
myfunc(1,2,3)
myfunc(1,2,3,4,5)

//2、不定引數的傳遞,假如有個變參函式myfunc2(args ...int)
func myfunc1(args ...int){
  //按原樣傳遞
  myfunc2(args...)
  //傳遞切片
  myfunc2(args[1:]...)
}

//3、任意型別的不定引數,使用interface{}作為指定型別
func Printf(format string,args ...interface{}){   //此為標準庫中fmt.Printf()函式的原型
  //函式體
}

5.3多返回值

//多返回值
func (file *File) Read(b []byte) (n int,err error)
//使用下劃線"_"來丟棄返回值
n,_:=f.Read(buf)

5.4匿名函式與閉包

//1、匿名函式:不帶函式名的函式,可以像變數一樣被傳遞
func(a,b int,z float32) bool{  //沒有函式名
  return a*b<int(z)
}
f:=func(x,y int) int{
  return x+y
}

//2、閉包

6.錯誤處理

1、error介面

//定義error介面
type error interface{
  Error() string
}
//呼叫error介面
func Foo(param int) (n int,err error){
  //...
}
n,err:=Foo(0)
if err!=nil{
  //錯誤處理
}else{
  //使用返回值
}

2、defer[延遲函式]

語法:defer function_name() 
1)defer在宣告時不會執行,而是推遲執行,在return執行前,倒序執行defer[先進後出],一般用於釋放資源,清理資料,記錄日誌,異常處理等。 
2)defer有一個特性:即使函式丟擲異常,defer仍會被執行,這樣不會出現程式錯誤導致資源不被釋放,或者因為第三方包的異常導致程式崩潰。 
3)一般用於開啟檔案後釋放資源的操作,比如開啟一個檔案,最後總是要關閉的。而在開啟和關閉之間,會有諸多的處理,可能會有諸多的if-else、根據不同的情況需要提前返回

f, = os.open(filename)
defer f.close()
do_something()
if (condition_a) {return}
do_something_again() 
if (condition_b) {return}
do_further_things()

4)defer示例

package main
import "fmt"

func deferTest(number int) int {
    defer func() {
        number++
        fmt.Println("three:", number)
    }()

    defer func() {
        number++
        fmt.Println("two:", number)
    }()

    defer func() {
        number++
        fmt.Println("one:", number)
    }()

    return number
}

func main() {
    fmt.Println("函式返回值:", deferTest(0))
}

/*
one: 1
two: 2
three: 3
函式返回值: 0
*/