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)

具體參考Go語言指針詳解

3.2.5結構體(struct)

具體參考Go面向對象編程之結構體

3.2.6接口(interface)

具體參考Go面向對象編程之接口

3.2.7通道(chan)

具體參考Go併發編程之channel

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
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章