Golang Basic

GO

  • you can use a local mod to replace a remote mod where declared on github.
    go mod edit -replace github.com/GoAdminGroup/go-admin=/home/zhongwei/work/go-admin

environment preparation
setting PATH of GOROOT and GOPATH
to enable the go in command line.

  • firstapp
    go run src/github.com/zw3413/firstapp/Main.go
    go build github.com/zw3413/firstapp
    go install github.com/zw3413/firstapp

不支持任何隐式类型转换
不支持指针运算
字符串是值类型,初始化值为“”,而不是nil
同维度同长度数组可以比较是否相等
除了一般的逻辑运算符和位运算符,GO引入了按位清零运算符&^

循环:Go仅支持for循环,条件不需要加括号
形式一:
for i : =1; i++; i<=10 {

}
形式二:
n:=0
for n<5{

}
形式三:
for{

}

条件:
if var declare; condition{

}
Switch os := runtiome.GOOS; os{
case “darwin” , ”OS X” :
fmt.Println(“OS X.”)
//break GO里面不需要自己加break
default:
fmt.Print(“default os”)
}
switch {
case 0 <= Num && Num <= 3:
fmt.Println(“0-3”)
}

數據類型:
布爾型:true/false
數字類型:int float32 float64 支持複數,位的運算採用補碼
uint8 無符號8位整形0255
uint16 無符號16位整形0
65535
uint32無符號32位整形04294967295
uint64無符號64位整形0
18446744073709551615
int8有符號8位整形-128127
int16有符號16位整形-32768
32767
int32有符號32位整形-21474836482147483647
int64有符號64位整形-9223372036854775808
9223372036854775807
float32 IEEE-754 32位浮點整形
float64 IEEE-754 64位浮點型數
complex64 32位實數和虛數
complex128 64位實數和虛數
byte 類似uint8
rune 類似int32
uint 32或64位
int 與uint一樣大小
uintptr 無符號整形,用於存放一個指針
字符串類型:由一串固定長度的字符鏈接起來的字符序列。由單個字節鏈接起來,每個字節採用URF-8編碼標識的Unicode文本
派生類型:
指針類型(Pointer)
數組類型
結構化類型(struct)
Channel類型

函數類型
切片類型
接口類型(interface)
Map類型

Variable变量
Variable declaration 声明变量的三种方式:
var foo int
var foo int =42
foo:=42

变量声明,与作用域
局部變量:函數內定義的變量
全局變量: 函數外定義的變量
形式參數: 函數定義中的變量

全局變量和局部變量名稱可以相同,但是函數內的局部變量會被優先考慮

变量命名规则

  • lower case first letter for package scope
  • upper case first letter to export
  • no private scope

Naming conventions 变量命名约定:

  • Pascal or camelCase
    Capitalize acronyms(HTTP,URL)
  • As short as reasonable
    Longer names for longer lives

Type convensions 类型约定:

  • destinationType(variable)
  • use strconv package for strings

“{” 不能獨立成行
一般不用 “;”結尾,如果一行有多個語句,必須用“;”間隔

並行賦值,其中可以使用捨棄符_
例如,當調用一個返回多個值的函數,其中有幾個返回值是不需要的,由於Go強制變量必須被使用,所以可以使用_來進行捨棄
a, b := 1, 2
_, b := 1, 2
//這種因式分解關鍵字的寫法一般用於聲明全局變量
var (
a v_type1
b v_type2
)
值類型與引用類型:
值類型:所有像int,float,bool,string這些基本類型都屬於值類型,使用這些類型的變量直接指向存在內存中的值, 使用&i可以獲取到變量i的內存地址
引用類型:一個引用類型的變量r1存儲的是r1的值所在的內存地址(數字),或內存地址中第一個字節所在的位置,這個內存地址叫做指針。

常量
const identifier[type]=value
const b string = “abc”
const b = “abc”
const c_name1,c_name2=value1,value2
常量用作枚舉:
const(
Unknown = 0
Female = 1
Male = 2
)
常量可以使用len(),cap(),unsafe.Sizeof()函數來計算表達式的值。
########
package main
import “unsafe”
const(
a = “abc”
b = len(a)
c = unsafe.Sizeof(a) //常量表達式中,函數必須是內置函數,否則編譯不過
)
func main(){
println(a, b, c)
}
########

iota
是一個可以被編譯器修改的特殊常量,在const關鍵字出現時被重置為0
const中每新增一行常量聲明,iota便會計數一次,可理解為const語句塊中的行索引。
const(
a = iota //0
b = iota //1
c = iota //2
)
iota的用法:
########
package main
import “fmt”
func main(){
const(
a=iota //0
b //1
c //2
d = “ha” //獨立值,iota=+1
e //“ha” iota+=1
f = 100 //iota+=1
g //100 iota+=1
h = iota //7,恢復計數
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
########
result: 0 1 2 ha 100 100 7 8

指針
一個指針指向一值得內存地址
var var_name *var-type
var ip *int /指向整形/
var fp float32 /指向浮點型/
指針的使用流程:
定義指針變量
為指針變量賦值
訪問指針變量中指向地址的值
在變量名前加上&獲取其指針
在指針名前加上
獲取其值
########
var a int=20
var ip *int
ip = &a
fmt.Printf(“a變量的地址是:%x\n”,&a)
fmt.Printf(“ip變量儲存的指針地址:%x\n”,ip)
fmt.Printf(“*ip變量的值:%d\n”,*ip)
########
空指針:
當一個指針被定義後沒有分配到任何變量時,它的值為nil
如何判斷空指針:
if ( ptr != nil )
if ( ptr == nil )

運算符
算數運算符:

      • / % ++ –
        關係運算符:
        == != > < >= <=
        邏輯運算符:
        && || !
        位運算符:
        位運算符對整數在內存中的二進制位進行操作
        & 按位與運算符是雙目運算符,使參與運算的兩數各對應的二進位相與
        | 按位或是雙目運算符,使參與運算的兩位數各對應的二進位相或
        ^ 按位異或運算符是雙目運算符,使參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1,相同時結果為0
        <<左移運算符是雙目運算符,左移n位就是乘以2的n次方。左側數字的各二進位全部左移右側數字位,高位丟棄,低位補0

        右移運算符是雙目運算符,右移n位就是除以2的n次方。左側數字的各二進位全部右移右側數字位
        賦值運算符
        = 右側表達式的值賦給左側變量
        += 相加後再賦值
        -= 相減後再賦值
        *= 相乘後再賦值
        /= 相除後再賦值
        %= 求余后再賦值
        <<=左移后賦值
        = 右移后賦值
        &= 按位與后賦值
        ^= 按位異或后賦值
        |= 按位或后賦值
        其他運算符
        & 返回變量存儲地址,取址運算符 &a給出變量a的實際地址
        *指針變量 *a;是一個指針變量
        運算符優先級
        5 * / % << >> & &^
        4 + - | ^
        3 == != < <= > >=
        2 &&
        1 ||

函數
func swap(x , y string)(string ,string){
return y, x
}
Go語言中的參數傳遞都是值傳遞,而不是引用傳遞
函數支持函數作為參數傳入到另一個函數中
閉包是匿名函數,可在動態編程中使用
方法就是一個包含了接受者的函數

數組、結構體、切片(Slice)、集合(Map)
數組用來存儲一組相同數據類型的數據,
長度不可變
聲明:var balance [10]float32
賦值1:var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
賦值2:var balance = […]float32{1000.0, 2.0, 3.4, 7.0 ,50.0}
輸出:balance[4]=50.0
結構體用來存儲一組不同或者相同數據類型的數據
結構體的定義:
type struct_variable_type struct{
member definition
member definition
member definition

member definition
}
使用結構體來聲明變量:
variable_name := structure_variable_type{value1, value2, … valuen}
variable_name := structure_variable_type{key1: value, key2: value2…, keyn:valuen}
可以使用結構體變量帶上.操作符訪問結構體中的內容,
也可以使用結構體指針帶上.操作符訪問結構體中的內容
Go語言的切片是對數組的抽象
Slice類型是一種“動態數組”,長度不固定,可以追加元素,並且在追加元素時可能使切片的容量增大
切片的聲明:(不需要說明長度,如果給定長度即成為一個數組)
var identifier []type
或者使用make()函數來創建切片
var slice1 []type = make( []type , len )
也可簡寫為:
slice1 := make([]type, len)
也可以指定容量,其中capacity為可選參數
make([]T,len,capacity)
其中的len是數組的長度並且也是切片的初始長度
切片初始化
直接初始化,[]標識切片類型,後面是具體的值,cap=len=3:
s := []int {1,2,3}
將切片s初始化為一個數組的引用,可以指定下標:
s := arr[ : ]
s := arr[ startIndex : endIndex ]
s := arr[ startIndex : ]
s := arr[ : endIndex]
通過一個切片來初始化
s1 := s[ startIndex : endIndex]
通過內置函數make來初始化
s := make([]int, len, cap) 其中[]int標識元素類型為int
內置函數
len()
cap()
append()
copy()
實例代碼:
var numbers []int
len(numbers)
cap(numbers)
append(numbers,1,2)
numbers1 := make([]int, len(numbers),(cap(numbers))*2)
copy(numbers1, numbers)
中文字符串截取:
rune_string := []rune(original_string)
new_string := rune_string[:4]

Map 一種無序的鍵值對的集合

Map最重要的一點是通過key來快速檢索數據,key類似於索引,指向數據的值。
Map可以無序迭代
聲明
var map_variable map[key_data_type]value_data_type
map_variable :=make(map[key_data_type]value_data_type)
訪問和賦值
var countryCapitalMap map[string]string
countryCapitalMap=make(map[string]string)
countryCapitalMap[“France”]=”巴黎”
countryCapitalMap[“Italy”]=”羅馬”
遍歷
for country := range countryCapitalMap{
fmt.Println(country, “首都是”,countryCapitalMap[country])
}
查看元素在集合中是否存在
capital, ok := countryCapitalMap[“American”]
if(ok){
fmt.Println(“American 的首都是”,capital)
}else{
fmt.Println(“American的首都不存在”)
}
刪除元素
delete(countryCapitalMap, “France”)
範圍(Range)
range關鍵字用於for循環中迭代數組(array)、切片(slice)、通道(channel)或集合(map),以及字符串的元素。在數組和切片中它返回元素的索引和索引對應的值,在集合中返回key-value對。

遞歸函數-階乘

########
package main
import “fmt”
func Factorial(n uint64)(result uint64){
    if(n>0){
        result = n * Factorial(n-1)
        return result
    }
    return 1
}
func main(){
    var i int = 15
    fmt.Printf(“%d 的階乘是 %d”, i, Factorial(uint64(i) ))
}
########

斐波那契數列

########
package main
import “fmt”
func fibonacci(n int)int {
    if n<2{
        return n
    }
    return fibonacci(n-2)+fibonacci(n-1)
}
func main(){
for i:=0;i<10;i++{
fmt.Printf(“%d\t”, fibonacci(i))

}
########

接口

/* 定義接口 /
type interface_name interface {
    method_name1 [return_type]
method_name2[return_type]
method_name3[return_type]

method_namen[return_type]
}
/
定義結構體 /
type struct_name struct{
/
variables /
}
/
實現接口方法 /
func (struct_name_variable struct_name) method_name1()[return_type]{
/
方法實現 */
}

func (struct_name_variable struct_name) method_name()[return_type]{
/方法實現/
}

錯誤處理

並發和通道:

go say(“world”)//啟動異步進程
ch := make(struct{}) //創建一個不帶緩存的同步通道,使用一個空結構體參數類型
ch := make(chan int, 100) //創建一個帶緩存的通道
ch <- x //往通道發送數據
x = <-ch //接收通道數據
<-ch //接收通道數據並捨棄
close(ch) //關閉通道
for i:=range ch{…} //遍歷通過通道傳過來的值

用sync.WaitGroup來管理和監事所有goroutine是否結束:

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
func makeThumbnails6(filenames <-chan string) int64{   //參數為一個接受類型的channel
sizes := make(chan int64) //用來存放計算結果的channel
var wg sync.WaitGroup //聲明監視goroutine
for f:= range filenames{
wg.Add(1) //監視器+1
go func(f string){
defer wg.Done() //監視器-1
thumb, err := thumbnail.ImageFile(f)
if err!=nil{
log.Println(err)
return
}
info, _ := os.Stat(thumb)
sizes <- info.Size()
}(f)
}
go func(){ //開啟監視器goroutine,等待所有的goroutine返回
wg.Wait()
close(sizes)
}()

var total int64
for size := range sizes{
total += size
}

return total
}

每隔一秒,發送回一個信息:

time.Tick(1 * time.Second)

多個goroutine之間共享數據的方式:

  1. 通過通信來解決數據共享
  2. 串行綁定
  3. 互斥訪問

slice擴容:

在对slice进行append等操作时,可能会造成slice的自动扩容。其扩容时的大小增长规则是:
如果新的大小是当前大小2倍以上,则大小增长为新大小
否则循环以下操作:如果当前大小小于1024,按每次2倍增长,否则每次按当前大小1/4增长。直到增长的大小超过或等于新大小。

空串與nil:

string的空值是””,它是不能跟nil比较的。
即使是空的string,它的大小也是两个机器字长的。
slice也类似,它的空值并不是一个空指针,而是结构体中的指针域为空,空的slice的大小也是三个机器字长的。

defer:

其实使用defer时,用一个简单的转换规则改写一下,就不会迷糊了。改写规则是将return语句拆成两句写,return xxx会被改写成:
返回值 = xxx
调用defer函数
空的return

Go实现闭包的基础:

将闭包环境变量在堆上分配是Go实现闭包的基础。Go语言能通过escape analyze识别出变量的作用域,自动将变量在堆上分配。

goroutine的各种状态变化:

  1. 在newproc1中新建的goroutine被设置为Grunnable状态
  2. 投入运行时设置成Grunning
  3. 在entersyscall的时候goroutine的状态被设置为Gsyscall
  4. 到出系统调用时根据它是从阻塞系统调用中出来还是非阻塞系统调用中出来,又会被设置成Grunning或者Grunnable的状态。
  5. 在goroutine最终退出的runtime.exit函数中,goroutine被设置为Gdead状态。