Go安裝 三種安裝方式,源碼安裝、壓縮包安裝
GOPATH和工作空間 作用?工作空間的目錄結(jié)構(gòu)
GO命令
go build
如果是普通包,就像我們?cè)?.2節(jié)中編寫的mymath包那樣,當(dāng)你執(zhí)行g(shù)o build之后,它不會(huì)產(chǎn)生任何文件。如果你需要在$GOPATH/pkg下生成相應(yīng)的文件,那就得執(zhí)行g(shù)o install。
如果是main包,當(dāng)你執(zhí)行g(shù)o build之后,它就會(huì)在當(dāng)前目錄下生成一個(gè)可執(zhí)行文件。如果你需要在$GOPATH/bin下生成相應(yīng)的文件,需要執(zhí)行g(shù)o install,或者使用go build -o 路徑/a.exe。
你也可以指定編譯輸出的文件名。例如1.2節(jié)中的mathapp應(yīng)用,我們可以指定go build -o astaxie.exe,默認(rèn)情況是你的package名(非main包),或者是第一個(gè)源文件的文件名(main包)。 (注:實(shí)際上,package名在Go語言規(guī)范中指代碼中“package”后使用的名稱,此名稱可以與文件夾名不同。默認(rèn)生成的可執(zhí)行文件名是文件夾名。)
go build會(huì)忽略目錄下以“_”或“.”開頭的go文件。
如果你的源代碼針對(duì)不同的操作系統(tǒng)需要不同的處理,那么你可以根據(jù)不同的操作系統(tǒng)后綴來命名文件。例如有一個(gè)讀取數(shù)組的程序,它對(duì)于不同的操作系統(tǒng)可能有如下幾個(gè)源文件:array_linux.go array_darwin.go array_windows.go array_freebsd.go
go build的時(shí)候會(huì)選擇性地編譯以系統(tǒng)名結(jié)尾的文件(Linux、Darwin、Windows、Freebsd)。例如Linux系統(tǒng)下面編譯只會(huì)選擇array_linux.go文件,其它系統(tǒng)命名后綴文件全部忽略。 參數(shù)的介紹
-o
指定輸出的文件名,可以帶上路徑,例如 go build -o a/b/c
-i
安裝相應(yīng)的包,編譯+go install
-a
更新全部已經(jīng)是最新的包的,但是對(duì)標(biāo)準(zhǔn)包不適用
-n
把需要執(zhí)行的編譯命令打印出來,但是不執(zhí)行,這樣就可以很容易的知道底層是如何運(yùn)行的
-p n
指定可以并行可運(yùn)行的編譯數(shù)目,默認(rèn)是CPU數(shù)目
-race
開啟編譯的時(shí)候自動(dòng)檢測(cè)數(shù)據(jù)競(jìng)爭(zhēng)的情況,目前只支持64位的機(jī)器
-v
打印出來我們正在編譯的包名
-work
打印出來編譯時(shí)候的臨時(shí)文件夾名稱,并且如果已經(jīng)存在的話就不要?jiǎng)h除
-x
打印出來執(zhí)行的命令,其實(shí)就是和-n
的結(jié)果類似,只是這個(gè)會(huì)執(zhí)行
-ccflags 'arg list'
傳遞參數(shù)給5c, 6c, 8c 調(diào)用
-compiler name
指定相應(yīng)的編譯器,gccgo還是gc
-gccgoflags 'arg list'
傳遞參數(shù)給gccgo編譯連接調(diào)用
-gcflags 'arg list'
傳遞參數(shù)給5g, 6g, 8g 調(diào)用
-installsuffix suffix
為了和默認(rèn)的安裝包區(qū)別開來,采用這個(gè)前綴來重新安裝那些依賴的包,-race
的時(shí)候默認(rèn)已經(jīng)是-installsuffix race
,大家可以通過-n
命令來驗(yàn)證
-ldflags 'flag list'
傳遞參數(shù)給5l, 6l, 8l 調(diào)用
-tags 'tag list'
設(shè)置在編譯的時(shí)候可以適配的那些tag,詳細(xì)的tag限制參考里面的 Build Constraints
go clean 這個(gè)命令是用來移除當(dāng)前源碼包和關(guān)聯(lián)源碼包里面編譯生成的文件。
go fmt 代碼格式化 用go fmt命令,其實(shí)是調(diào)用了gofmt,而且需要參數(shù)-w,否則格式化結(jié)果不會(huì)寫入文件。gofmt -w -l src,可以格式化整個(gè)項(xiàng)目。 所以go fmt是gofmt的上層一個(gè)包裝的命令,我們想要更多的個(gè)性化的格式化可以參考 gofmt gofmt的參數(shù)介紹
-w 把改寫后的內(nèi)容直接寫入到文件中,而不是作為結(jié)果打印到標(biāo)準(zhǔn)輸出。
-r 添加形如“a[b:len(a)] -> a[b:]”的重寫規(guī)則,方便我們做批量替換
-s 簡(jiǎn)化文件中的代碼
-d 顯示格式化前后的diff而不是寫入文件,默認(rèn)是false
-e 打印所有的語法錯(cuò)誤到標(biāo)準(zhǔn)輸出。如果不使用此標(biāo)記,則只會(huì)打印不同行的前10個(gè)錯(cuò)誤。
-cpuprofile 支持調(diào)試模式,寫入相應(yīng)的cpufile到指定的文件
go get 這個(gè)命令是用來動(dòng)態(tài)獲取遠(yuǎn)程代碼包的 這個(gè)命令在內(nèi)部實(shí)際上分成了兩步操作:第一步是下載源碼包,第二步是執(zhí)行g(shù)o install。下載源碼包的go工具會(huì)自動(dòng)根據(jù)不同的域名調(diào)用不同的源碼工具, 參數(shù) -d 之只下載不安裝
go install 這個(gè)命令在內(nèi)部實(shí)際上分成了兩步操作:第一步是生成結(jié)果文件(可執(zhí)行文件或者.a包),第二步會(huì)把編譯好的結(jié)果移到$GOPATH/pkg或者$GOPATH/bin。
go test 執(zhí)行這個(gè)命令,會(huì)自動(dòng)讀取源碼目錄下面名為*_test.go的文件,生成并運(yùn)行測(cè)試用的可執(zhí)行文件。
go tool 下面下載聚集了很多命令
go generate 這個(gè)命令是從Go1.4開始才設(shè)計(jì)的,用于在編譯前自動(dòng)化生成某類代碼。
godoc 在Go1.2版本之前還支持go doc命令,但是之后全部已到了godoc這個(gè)命令下,需要這樣安裝go get
go version 查看go當(dāng)前的版本 go env 查看當(dāng)前go的環(huán)境變量 go list 列出當(dāng)前全部安裝的package go run 編譯并運(yùn)行Go程序
開發(fā)工具
Sublime Text
PHPStorm
LiteIDE
Go語言基礎(chǔ)
Go使用package(和Python的模塊類似)來組織代碼。main.main()函數(shù)(這個(gè)函數(shù)位于主包)是每一個(gè)獨(dú)立的可運(yùn)行程序的入口點(diǎn)。Go使用UTF-8字符串和標(biāo)識(shí)符(因?yàn)閁TF-8的發(fā)明者也就是Go的發(fā)明者),所以它天生支持多語言。
基礎(chǔ)知識(shí)
定義變量 定義一個(gè)名稱為“variableName”,類型為”type”的變量 var variableName type
定義三個(gè)類型都是“type”的變量var vname1, vname2, vname3 type
初始化“variableName”的變量為“value”值,類型是“type” var variableName type = value
定義三個(gè)類型都是”type”的變量,并且分別初始化為相應(yīng)的值 vname1為v1,vname2為v2,vname3為v3 var vname1, vname2, vname3 type= v1, v2, v3
定義三個(gè)變量,它們分別初始化為相應(yīng)的值 vname1為v1,vname2為v2,vname3為v3 編譯器會(huì)根據(jù)初始化的值自動(dòng)推導(dǎo)出相應(yīng)的類型 vname1, vname2, vname3 := v1, v2, v3
流程和函數(shù) 流程控制
if
Go里面if條件判斷語句中不需要括號(hào),如下代碼所示
1
2
3
4
5
if x > 10 {
fmt.Println("x is greater than 10" )
} else {
fmt.Println("x is less than 10" )
}
goto
用goto跳轉(zhuǎn)到必須在當(dāng)前函數(shù)內(nèi)定義的標(biāo)簽。
1
2
3
4
5
6
7
func myFunc () {
i := 0
Here: //這行的第一個(gè)詞,以冒號(hào)結(jié)束作為標(biāo)簽
println (i)
i++
goto Here //跳轉(zhuǎn)到Here去
}
for
Go里面最強(qiáng)大的一個(gè)控制邏輯就是for,它即可以用來循環(huán)讀取數(shù)據(jù),又可以當(dāng)作while來控制邏輯,還能迭代操作。它的語法如下:
1
2
3
for expression1; expression2; expression3 {
//...
}
expression1、expression2和expression3都是表達(dá)式,其中expression1和expression3是變量聲明或者函數(shù)調(diào)用返回值之類的,expression2是用來?xiàng)l件判斷,expression1在循環(huán)開始之前調(diào)用,expression3在每輪循環(huán)結(jié)束之時(shí)調(diào)用。 通常用法
1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"
func main () {
sum := 0 ;
for index:=0 ; index < 10 ; index++ {
sum += index
}
fmt.Println("sum is equal to " , sum)
}
// 輸出:sum is equal to 45
循環(huán)
1
2
3
4
sum := 1
for ; sum < 1000 ; {
sum += sum
}
簡(jiǎn)寫
1
2
3
4
sum := 1
for sum < 1000 {
sum += sum
}
for 中有break 、continue
1
2
3
4
5
6
7
8
for index := 10 ; index>0 ; index-- {
if index == 5 {
break // 或者continue
}
fmt.Println(index)
}
// break打印出來10、9、8、7、6
// continue打印出來10、9、8、7、6、4、3、2、1
for配合range可以用于讀取slice和map的數(shù)據(jù):
1
2
3
4
for k,v:=range map {
fmt.Println("map's key:" ,k)
fmt.Println("map's val:" ,v)
}
由于 Go 支持 “多值返回”, 而對(duì)于“聲明而未被調(diào)用”的變量, 編譯器會(huì)報(bào)錯(cuò), 在這種情況下, 可以使用_來丟棄不需要的返回值 例如
1
2
3
for _, v := range map {
fmt.Println("map's val:" , v)
}
switch
if 不夠用使用switch 1
2
3
4
5
6
7
8
9
10
switch sExpr {
case expr1:
some instructions
case expr2:
some other instructions
case expr3:
some other instructions
default :
other code
}
another
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
integer := 6
switch integer {
case 4 :
fmt.Println("The integer was <= 4" )
fallthrough
case 5 :
fmt.Println("The integer was <= 5" )
fallthrough
case 6 :
fmt.Println("The integer was <= 6" )
fallthrough
case 7 :
fmt.Println("The integer was <= 7" )
fallthrough
case 8 :
fmt.Println("The integer was <= 8" )
fallthrough
default :
fmt.Println("default case" )
}
函數(shù)函數(shù)是Go里面的核心設(shè)計(jì),它通過關(guān)鍵字func來聲明,它的格式如下:
1
2
3
4
5
func funcName (input1 type1, input2 type2) (output1 type1, output2 type2) {
//這里是處理邏輯代碼
//返回多個(gè)值
return value1, value2
}
關(guān)鍵字func用來聲明一個(gè)函數(shù)funcName 函數(shù)可以有一個(gè)或者多個(gè)參數(shù),每個(gè)參數(shù)后面帶有類型,通過,分隔 函數(shù)可以返回多個(gè)值 上面返回值聲明了兩個(gè)變量output1和output2,如果你不想聲明也可以,直接就兩個(gè)類型 如果只有一個(gè)返回值且不聲明返回值變量,那么你可以省略 包括返回值 的括號(hào) 如果沒有返回值,那么就直接省略最后的返回信息 如果有返回值, 那么必須在函數(shù)的外層添加return語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
// 返回a、b中最大值.
func max (a, b int ) int {
if a > b {
return a
}
return b
}
func main () {
x := 3
y := 4
z := 5
max_xy := max(x, y) //調(diào)用函數(shù)max(x, y)
max_xz := max(x, z) //調(diào)用函數(shù)max(x, z)
fmt.Printf("max(%d, %d) = %d\n" , x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n" , x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n" , y, z, max(y,z)) // 也可在這直接調(diào)用它
}
多個(gè)返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
//返回 A+B 和 A*B
func SumAndProduct (A, B int ) (int , int ) {
return A+B, A*B
}
func main () {
x := 3
y := 4
xPLUSy, xTIMESy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n" , x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n" , x, y, xTIMESy)
}
變參 func myfunc(arg …int) {} 傳值與傳指針
當(dāng)我們傳一個(gè)參數(shù)值到被調(diào)用函數(shù)里面時(shí),實(shí)際上是傳了這個(gè)值的一份copy,當(dāng)在被調(diào)用函數(shù)中修改參數(shù)值的時(shí)候,調(diào)用函數(shù)中相應(yīng)實(shí)參不會(huì)發(fā)生任何變化,因?yàn)閿?shù)值變化只作用在copy上。 傳指針使得多個(gè)函數(shù)能操作同一個(gè)對(duì)象。 傳指針比較輕量級(jí) (8bytes),只是傳內(nèi)存地址,我們可以用指針傳遞體積大的結(jié)構(gòu)體。如果用參數(shù)值傳遞的話, 在每次copy上面就會(huì)花費(fèi)相對(duì)較多的系統(tǒng)開銷(內(nèi)存和時(shí)間)。所以當(dāng)你要傳遞大的結(jié)構(gòu)體的時(shí)候,用指針是一個(gè)明智的選擇。 Go語言中channel,slice,map這三種類型的實(shí)現(xiàn)機(jī)制類似指針,所以可以直接傳遞,而不用取地址后傳遞指針。(注:若函數(shù)需改變slice的長(zhǎng)度,則仍需要取地址傳遞指針)
defer Panic和Recover
struct類型
Go語言中,也和C或者其他語言一樣,我們可以聲明新的類型,作為其它類型的屬性或字段的容器。例如,我們可以創(chuàng)建一個(gè)自定義類型person代表一個(gè)人的實(shí)體。這個(gè)實(shí)體擁有屬性:姓名和年齡。這樣的類型我們稱之struct。如下代碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type person struct {
name string
age int
}
type person struct {
name string
age int
}
var P person // P現(xiàn)在就是person類型的變量了
P.name = "Astaxie" // 賦值"Astaxie"給P的name屬性.
P.age = 25 // 賦值"25"給變量P的age屬性
fmt.Printf("The person's name is %s" , P.name) // 訪問P的name屬性.
除了上面這種P的聲明使用之外,還有另外幾種聲明使用方式:
1.按照順序提供初始化值 P := person{“Tom”, 25}
2.通過field:value
的方式初始化,這樣可以任意順序 P := person{age:24, name:”Tom”}
3.當(dāng)然也可以通過new
函數(shù)分配一個(gè)指針,此處P的類型為*person P := new(person)
面向?qū)ο?/h3>帶有接收者的函數(shù),我們稱為method
計(jì)算面積的例子
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//原始的函數(shù)
package main
import "fmt"
type Rectangle struct {
width, height float64
}
func area (r Rectangle) float64 {
return r.width*r.height
}
func main () {
r1 := Rectangle{12 , 2 }
r2 := Rectangle{9 , 4 }
fmt.Println("Area of r1 is: " , area(r1))
fmt.Println("Area of r2 is: " , area(r2))
}
//接受者
type Rectangle struct {
width, height float64
}
type Circle struct {
radius float64
}
func (r Rectangle) area () float64 {
return r.width*r.height
}
func (c Circle) area () float64 {
return c.radius * c.radius * math.Pi
}
func main () {
r1 := Rectangle{12 , 2 }
r2 := Rectangle{9 , 4 }
c1 := Circle{10 }
c2 := Circle{25 }
fmt.Println("Area of r1 is: " , r1.area())
fmt.Println("Area of r2 is: " , r2.area())
fmt.Println("Area of c1 is: " , c1.area())
fmt.Println("Area of c2 is: " , c2.area())
}
method繼承
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
29
30
31
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
type Employee struct {
Human //匿名字段
company string
}
//在human上面定義了一個(gè)method
func (h *Human) SayHi () {
fmt.Printf("Hi, I am %s you can call me on %s\n" , h.name, h.phone)
}
func main () {
mark := Student{Human{"Mark" , 25 , "222-222-YYYY" }, "MIT" }
sam := Employee{Human{"Sam" , 45 , "111-888-XXXX" }, "Golang Inc" }
mark.SayHi()
sam.SayHi()
}
interface
Go語言里面設(shè)計(jì)最精妙的應(yīng)該算interface,它讓面向?qū)ο?,?nèi)容組織實(shí)現(xiàn)非常的方便 簡(jiǎn)單的說,interface是一組method的組合,我們通過interface來定義對(duì)象的一組行為。
并發(fā)
goroutine goroutine是通過Go的runtime管理的一個(gè)線程管理器。goroutine通過go關(guān)鍵字實(shí)現(xiàn)了,其實(shí)就是一個(gè)普通的函數(shù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import (
"fmt"
"runtime"
)
func say (s string ) {
for i := 0 ; i < 5 ; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main () {
go say("world" ) //開一個(gè)新的Goroutines執(zhí)行
say("hello" ) //當(dāng)前Goroutines執(zhí)行
}
runtime.Gosched()表示讓CPU把時(shí)間片讓給別人,下次某個(gè)時(shí)候繼續(xù)恢復(fù)執(zhí)行該goroutine
channels
goroutine運(yùn)行在相同的地址空間,因此訪問共享內(nèi)存必須做好同步。那么goroutine之間如何進(jìn)行數(shù)據(jù)的通信呢,Go提供了一個(gè)很好的通信機(jī)制channel。channel可以與Unix shell 中的雙向管道做類比:可以通過它發(fā)送或者接收值。這些值只能是特定的類型:channel類型。定義一個(gè)channel時(shí),也需要定義發(fā)送到channel的值的類型。注意,必須使用make 創(chuàng)建channel:
1
2
3
4
5
6
ci := make (chan int )
cs := make (chan string )
cf := make (chan interface {})
//channel通過操作符<-來接收和發(fā)送數(shù)據(jù)
ch <- v // 發(fā)送v到channel ch.
v := <-ch // 從ch中接收數(shù)據(jù),并賦值給v
channel例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
func sum (a []int , c chan int ) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to c
}
func main () {
a := []int {7 , 2 , 8 , -9 , 4 , 0 }
c := make (chan int )
go sum(a[:len (a)/2 ], c)
go sum(a[len (a)/2 :], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x + y)
}
Buffered Channels
1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"
func main () {
c := make (chan int , 2 )//修改2為1就報(bào)錯(cuò),修改2為3可以正常運(yùn)行
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
//修改為1報(bào)如下的錯(cuò)誤:
//fatal error: all goroutines are asleep - deadlock!
web基礎(chǔ)
web工作原理
http包建立Web服務(wù)器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
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName (w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析參數(shù),默認(rèn)是不會(huì)解析的
fmt.Println(r.Form) //這些信息是輸出到服務(wù)器端的打印信息
fmt.Println("path" , r.URL.Path)
fmt.Println("scheme" , r.URL.Scheme)
fmt.Println(r.Form["url_long" ])
for k, v := range r.Form {
fmt.Println("key:" , k)
fmt.Println("val:" , strings.Join(v, "" ))
}
fmt.Fprintf(w, "Hello astaxie!" ) //這個(gè)寫入到w的是輸出到客戶端的
}
func main () {
http.HandleFunc("/" , sayhelloName) //設(shè)置訪問的路由
err := http.ListenAndServe(":9090" , nil ) //設(shè)置監(jiān)聽的端口
if err != nil {
log.Fatal("ListenAndServe: " , err)
}
}