Golang系列(一)之基礎篇
阿新 • • 發佈:2019-02-19
一、初識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]
分類 | 命令 | 說明 |
---|---|---|
build | compile packages and dependencies | |
clean | remove object files | |
doc | show documentation for package or symbol | |
env | print Go environment information | |
fix | run go tool fix on packages | |
fmt | run gofmt on package sources | |
generate | generate Go files by processing source | |
get | download and install packages and dependencies | |
install | compile and install packages and dependencies | |
list | list packages | |
run | compile and run Go program | |
test | test packages | |
tool | run specified go tool | |
version | print Go version | |
vet | run 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
*/