天天看點

Go從入門到精通(一)go語言初識

 一、第一個go程式

package main

import (
       "fmt"
)

func main(){
       fmt.Println("hello world")
}      

對于代碼的解釋

如果是為了将代碼編譯成一個可執行程式,那麼package必須是main

如果是為了将代碼編譯成庫,那麼package則沒有限制

go中所有的代碼都應該隸屬一個包

fmt 是go的一個系統庫

fmt.println()則可以列印輸出

如果想要運作程式:go run 程式名

在一個可執行程式隻有一個main函數

關于注釋

單行注釋://

多行注釋:/* */

二、go語言初識

關于定義一個變量

var 變量名 變量類型

變量名 = 值

這裡需要注意:go語言中定義的變量必須被用到,否則會報錯

同時定義變量和指派可以一步完成通過: 變量名 := 值

定義一個函數

如果我們要定義一個函數,我們先看下面求和的例子:

func add(a int,b int) int {
    var sum int
    sum = a + b
    return sum
}      

這裡我們需要知道,下面這種格式是被嚴格要求的沒包括第一行的大括号不能放到下一行

func 函數名(參數1 參數1類型,參數2 參數2類型) 傳回值類型 { 

}

三、golang語言特性

垃圾回收

記憶體自動回收,不需要開發人員管理記憶體

開發人員專注業務實作

隻需要new配置設定記憶體,不需要釋放

天然高并發

  • 從語言曾元支援并發,非常簡單
  • goroute,輕量級線程,建立成千上萬goroute成為可能
  • 基于CSP模型實作

關于高并發的一個簡單示範:

package main

import (
    "fmt"
    "time"
)


func test_print(a int){
    fmt.Println(a)
}


func main(){
    for i:= 0;i < 100; i ++ {
        go test_print(i)
    }
    time.Sleep(time.Second)
}      

在實作高并發的時候隻需要在調用的函數前面加上go,就表示開啟了并發

如果在for循環的外面不加上time.Sleep(time.Second),就會發現會少列印了,這是因為當主程式運作完之後,并不會等待線程,是以程式直接終止

channel管道

類似linux中的pipe

多個goroute之間通過channel進行通信

支援任何類型

package main

import (
    "fmt"
)

func test_pipe(){
    pipe := make(chan int,3)
    pipe <- 1
    pipe <- 2
    pipe <- 3

    fmt.Println(len(pipe))
}

func main(){
    test_pipe()
}      

上述代碼的解釋:

pipe := make(chan int,3) 這裡是定義一個管道pipe,go是強類型語言,是以這裡聲明一個管道變量pipe需要通過有三個參數,chan表示是管道,int表示管道類型,3表示管道容量

通過len(pipe)可以檢視管道的的長度

如果想要從管道裡擷取資料

t1 :=<- pipe 這個寫法等同于

var t1 int

t1 = <- pipe

fmt.Println(t1)

管道遵循的原則是先進先出,是以第一個擷取的值是1

小結:如果想要給管道放入值:定義的管道pipe < - 要存入的内容

如果想要從管道中擷取值:變量名 =<- 定義的管道pipe

這裡強調一下go中package包的概念,一個包裡的變量,在這個包裡是都可以通路,但是在包之外也是有權限限制是否可以通路到,如果一個變量在一個包裡是大寫的,在其他包裡就可以通路到,如果是小寫的其他包裡則通路不到。類似其他語言中的public和private

多傳回值

一個函數可以傳回多個值

package main

import "fmt"

func calc(a int,b int) (int,int ){
    sum := a + b
    avg := sum / 2
    return sum,avg
}

func main(){
    sum,avg := calc(100,200)
    fmt.Println(sum,avg)
}      

關于需要傳入多個參數的時候是用括号括起來單個的情況下一般不用括号括起來,直接int,而這裡是傳回兩個則需要 (int,int)表示傳回兩個整數類型值

如果有多個傳回值,但是我隻想傳回一個值,是通過下劃線方式實作,則上述代碼改為:

func main(){
    sum,_ := calc(100,200)
    fmt.Println(sum)
}      

四、包的概念

  • 和python一樣,把相同功能的代碼放到一個目錄,稱之為包
  • 包可以被其他包引用
  • main包是用來生成可執行檔案,每個程式隻有一個main包
  • 包的主要用途是提高代碼的課複用性

關于main包中的main函數,go程式經過編譯之後,運作該程式,會将編譯好的二進制檔案加載到記憶體中,會首先調用main函數,是以main函數是程式的入口函數,即必須有package main

關于包,是我們可以把一些常用的功能封裝到包中,這個時候包中的每個go檔案的開頭則不需要package main,而是package 自定義名字 這個自定義名字是根據這個包的功能進行命名

go源碼按package進行組織,并且package要放到非注釋的第一行

一個程式隻有一個main包,一個包中隻能有一個main函數,不能重複定義

main函數是程式的執行入口

沒有被引用的變量,編譯的時候會報錯

go的目錄規範

這裡舉一個簡單的例子:如果我們在建立一個go_project目錄,通常在這個目錄下我們會建立如下目錄

src 存放不同的項目代碼

bin 存放編譯後的可執行程式

vender 存放引用的第三方庫

pgk 存放靜态庫

Go從入門到精通(一)go語言初識

我們的go環境變量中的GOPATH一般會設定為:

(我這裡是路徑是/users/zhaofan/go_project)

export GOPATH=/users/zhaofan/go_project

go的編譯

如果我們寫好了go的代碼檔案,我們如果測試運作可以通過:

go run 快速執行go檔案

go build 編譯程式,生成二進制檔案

go install 安裝可執行檔案到bin目錄下

基本指令:

go test執行單元測試或壓力測試

go env 顯示go相關的環境變量

go fmt 格式化源代碼

我們通過下面例子了解這個編譯指令的使用:

例子一

Go從入門到精通(一)go語言初識

我在/users/zhaofan/go_project/src/go_dev/day01/hello目錄下寫了一個hello程式

現在把這個hello程式進行編譯

Go從入門到精通(一)go語言初識

我們在go_project目錄下執行的編譯指令,如果不指定編譯生成的檔案會直接将編譯檔案生成在目前目錄即go_project目錄下

這裡需要解釋的是go build 後面的路徑go build go_dev/day01/hello 

我們從目錄結構可以看出,go_dev的上一級目錄src目錄并沒有寫,這是因為go編譯的時候,會自動去GOPATH下的src目錄裡去找,是以這裡是不需要寫,同時編譯的路徑的最後我們隻寫到hello目錄而不是hello.go檔案

例子二

我們在/users/zhaofan/go_project/src/go_dev/day01/ 目錄下建立一個goroute目錄

并在goroute目錄下建立兩個go檔案,main.go和goroute.go檔案

main.go檔案的代碼為:

package main
import (
       "time"
)

func main(){
        for i := 0; i < 100; i++ {
            go test_goroute(i)
        }
        time.Sleep(time.Second)
}      

goroute.go檔案的代碼為:

package main

import "fmt"

func test_goroute(a int){
    fmt.Println(a)
}      

這樣我們編譯的時候隻需要在go_project目錄下執行:

go build go_dev/day01/goroute

這樣就會再go_project目錄下生成一個可執行檔案goroute

例子3

還是在/users/zhaofan/go_project/src/go_dev/day01/下建立一個goroute_test目錄

在goroute_test目錄下建立calc目錄和main目錄

同時在calc下建立一個sum.go檔案,在main目錄下建立一個main.go檔案

sum.go檔案代碼如下:

package calc


func Add(a int,b int,c chan int){
    sum := a + b
    c <- sum
}      

這裡有個地方需要注意這裡我們的sum.go是作為包寫的,是以我們開頭是:package calc,即package+sum.go的所在上級目錄,并且是sum中定義的函數名首字母要大些(這個是必須的)這裡其實是因為

我們定義的包都是要被外部的其他包調用,即我們這裡定義的sum.go是要被其他包調用,這個時候隻有首字母大寫才能被其他包調用到

main.go檔案代碼如下:

package main

import (
    "fmt"
    "go_dev/day01/goroute_test/calc"
)

func main(){
    pipe := make(chan int,1)
    calc.Add(100,200,pipe)
    res :=<- pipe
    fmt.Println(res)
}      

這次我們編譯的時候指定編譯檔案生成的目錄路徑,指令如下:

go build -o bin/goroute_test go_dev/day01/goroute_test/main

關于單元測試例子:

單元測試的代碼檔案的名字格式必須是:*_test.go

例如我要寫關于calc.go檔案的單元測試

建立一個檔案命名為:calc_test.go

這裡需要知道的是開頭的calc的名字并不是強制的,但是為了友善測試哪個代碼檔案,開頭就以那個檔案開頭,下面是一個例子代碼:

1 package calc
 2 
 3 import (
 4     "testing"
 5 )
 6 func TestAdd(t *testing.T){
 7     var sum int
 8     sum = Add(5,6)
 9     if sum != 11{
10         t.Fatalf("add is not right,sum:%v expected:11",sum)
11     }
12     t.Logf("add is Ok")
13 }      

在代碼中我們定義函數時候函數的名字也需要以Test開頭

上述測試檔案執行結果:

1 bogon:calc zhaofan$ go test
 2 PASS
 3 ok      go_dev/01/calc  0.007s
 4 bogon:calc zhaofan$ go test -v
 5 === RUN   TestAdd
 6 --- PASS: TestAdd (0.00s)
 7     calc_test.go:12: add is Ok
 8 PASS
 9 ok      go_dev/01/calc  0.007s
10 bogon:calc zhaofan$      

Go的結構開發規範 

好的代碼規範是非常重要的,這樣當你看别人代碼或者别人看你的代碼的時候就能很清楚的明白,下面是結構規範:

// 目前程式的包名
package main

//導入其他的包
import "fmt"

//常量的定義
const PI=3.14

//全局變量的聲明和指派
var name = "gopher"

//一般類型聲明
type newType int

//結構的聲明
type gopher struct{}

//接口的聲明
type golang interface{}

//由main函數作為程式入口點啟動
func main(){
    fmt.Println("Hello world! 你好世界")
}      

所有的努力都值得期許,每一份夢想都應該灌溉!