轉自:https://blog.csdn.net/huwh_/article/details/78429965
一、初識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)
具體參考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
*/