Go語言基礎(一)

一 簡介

1 定義

Go 是一種開源的程序設計語言,它意在使得人們可以方便地構建簡單,可靠,高效的軟件。html

2 產生緣由

1 計算機硬件技術更新頻繁,性能提升很快,目前主流編程語言發展落後,不能合理利用多核CPU優點來提升系統性能
2 軟件設計複雜度高,維護成本大
3 C/C++編譯速度過慢,須要解決提升速度java

3 歷史

Go是從2007年底由 Robert griesemer,rob pike,ken thompson 主持開發,後來加入了Ian Lance Taylor,Russ Cox,並最終於2009年11月開源,在2012年早些時候發佈了Go 1穩定版本,如今Go徹底開放。2015 年發佈了Go 1.5 版本,徹底移除了C代碼python

4 go 語言特色

1 繼承C不少理念,包括表達式語法,控制結構,基礎數據類型,調用參數傳值,指針等mysql

2 引入包的概念,用於組織程序結構,go語言中一個文件都要屬於一個包,而不能單獨存在 程序員

3 垃圾回收機制,內存自動回收,不須要開發人員管理,C須要 golang

4 自然併發
緣由:
A 從語言層面支持病啊,實現簡單
B goroutine輕量級線程,可實現大併發處理,高效利用多核特性
C 基於CPS併發模型實現 算法

5 吸取了管道通訊機制,造成Go語言特有的管道channel,經過管道,實現了不一樣goroute之間的互相通訊sql

6 函數返回多個值編程

7 新的創新,切片,延時執行等特性 緩存

5 學習方向

1 區塊鏈開放,其核心是去中心化,公開透明


2 go服務器端/遊戲開發軟件工程師,其核心是支撐主站後臺流量(排序,推薦,搜索等),提供負載均衡,cache緩存,容錯,按條件分流等特色,運行統計指標


3 golang分佈式/雲計算工程師 ,其核心是CDN調度系統,分發系統

二 環境安裝

1 Go語言環境安裝

安裝環境可見菜鳥教程,此處介紹也較爲詳細
https://www.runoob.com/go/go-environment.html

2 Go開發環境安裝

1 簡介

安裝Go語言開發包後,可選擇安裝集成開發環境(Intergrated Development Environment,IDE)或者編譯器來提升開發效率

集成開發環境推薦使用Jetbrains 公司的Goland,其公司共有多種開發環境和工具,其次可以使用微軟公司的visual studio code,其是一個高定製化的輕量編譯器,可以根據本身須要制定Go語言的開發流程

2 集成環境 jebrains Goland

Goland 是Jetbrains公司在IntelliJ平臺上開發的Go語言整合工具開發繼承環境,提供Go語言的編輯,編譯,調試,工程管理,重構等各類功能,其地址爲:https://www.jetbrains.com/go/

安裝過程本篇不在贅述。主要針對安裝完成設置進行相關說明

1 設置GOROOT

GOROOT 是Go語言安裝路徑,其GoLand會自動識別,可手動設置GOROOT

Go語言基礎(一)

此處可進行編輯和修改其目錄

Go語言基礎(一)

2 GOPATH

GOPATH 是Go語言編譯時參考的工做路徑,其默認是空,可選擇對應的目錄進行設置,默認會讀取系統的GOPATH ,可增長多個

Go語言基礎(一)

Go語言基礎(一)

3 方便定義功能的編譯器Visual Studio Code

Virsual Studio Code(簡稱VS Code)是一款由微軟公司開發的,能運行再Mac OS X、Windows和Linux 上的跨平臺開源代碼編譯器。

VS Code 使用JSON 格式的配置文件進行全部功能和特性的配置,VS Code 能夠經過擴展程序爲編譯器實現語言高亮,參數提示,編譯,調試,文檔生成等各類功能。

1 切換語言

可見我以前的博客
http://www.javashuo.com/article/p-ztkldhmx-e.html

2 安裝Go語言擴展

Go語言基礎(一)

搜索Go,找到Rich Go language support for Visual Studio Code 的字樣擴展,單擊安裝便可

3 設置GOPATH

選擇"文件----首選項----設置",在用戶-----擴展----Go中進行修改和編輯相關所需的參數

Go語言基礎(一)

三 Go語言基本語法與使用

1 基本代碼

package main //定義該文件所屬的包,每一個文件都必須屬於一個包,通常這個包時該文件所在的目錄名稱

import "fmt" //引入庫fmt,可用於打印和格式化

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println("hello world!") // 打印結果,使用fmt.Println進行打印 
}

2執行流程

1 簡介

1 若是先編譯成可執行文件,則可將可執行文件拷貝到沒有go開發環境的機器上,仍可運行,但其必須和源文件使用同一種系統


2 若是直接使用go run,則若是須要在另外一臺設備上運行,則須要該設備安裝go環境


3 在編譯時,編譯器會將程序運行以來的庫文件包含在可執行文件中,因此,可執行文件變得很大

2 執行

1 開發工具執行

Go語言基礎(一)

2 命令行執行

Go語言基礎(一)

3 編譯

1 有了xxx.go 源文件,經過編譯器將其生成可識別的二進制碼文件
2 在源文件目錄下,經過 go build 進行編譯,默認其名稱是xxx.exe 或 其餘(Linux中無後綴,Windows 中是 *.exe)
3 若程序沒提示錯誤,則會生成可執行文件
4 程序錯誤,則會報告對應的行數

1 開發工具編譯

Go語言基礎(一)

Go語言基礎(一)

在此執行run,便可看到生成的go_build_test_go.exe可執行文件

Go語言基礎(一)

Go語言基礎(一)

2 命令行編譯

Go語言基礎(一)

其名稱和源碼文件相同,只是後綴發生了變化

3 go 程序開發注意事項

1 go 源文件必須是以".go"爲擴展名
2 go 應用程序的執行入口時main()函數
3 go 語言嚴格區分大小寫
4 go 方法是由一條條語句構成,每一個語句不須要分號
5 go 編譯器時一行行編譯,一行只能寫一個語句,若要寫多個,則經過; 分號隔開
6 go語言定義變量或者import的包若沒用到,則編譯代碼不能經過
7 go語言中大括號是成對出現的,缺一不可

4 註釋

1 做用

提升代碼閱讀可讀性

2 類型分類

1 單行註釋
2 多行註釋

3 代碼示例

package main //定義該文件所屬的包,每一個文件都必須屬於一個包,通常這個包時該文件所在的目錄名稱

import "fmt" //引入庫fmt,可用於打印和格式化

// 這是一個程序執行入口

/*
這是一個多行註釋,可用於說明較多的程序問題
*/
func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println("hello world!") // 打印結果,使用fmt.Println進行打印
}

4 注意事項

1 註釋的不會被執行
2 註釋塊中不能被嵌套
3 官方推薦使用單行註釋

5 Go 開發常見問題和解決問題

1 使用一次tab 操做,實現縮進,默認總體向右移動,使用shift+tab 總體向左移動
2 或者使用gofmt來進行格式化
3 運算符兩邊習慣性各加上一個空格
4 通常的,大括號必須位於上一行的結尾而不是下一行的開始


以下

Go語言基礎(一)

四 Go 語言變量

1 簡介

變量至關於內存中的一個數據存儲空間的表示,能夠把變量值看作是一個房間的門牌號,經過這個門牌號可找到這個房間,及經過變量名能找到內存中訪問的變量值


變量的功能是存儲用戶數據的,不一樣的邏輯有不一樣的對象類型,也就是不一樣變量類型,通過半個多世紀的發展,編程語言已經造成一整套固定的類型,這些類型在編程語言中基本是相通的,常見變量的數據類型有:整型,浮點型,布爾型,結構體等。


Go 語言做爲C語言家族表明,在C語言的定義方法和類型上進行了優化和調整,更加靈活易學。

Go語言的每個變量都擁有本身的類型,必須通過聲明才能開始使用

2 變量的基本使用步驟

1 聲明變量

標準格式
var 變量名 變量類型

// 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
var a int  // 定義整形數據a
var b string  // 定義字符型數據b
var c float32  //定義浮點類型數據c
)

批量定義格式

// 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
var (
    a int         // 定義整形數據a
    b string      // 定義字符型數據b
    c float32     //定義浮點類型數據c
)

2 賦值變量和使用變量

Go 語言在聲明變量時,自動對變量對應的內存區域進行初始化操做,每一個變量會初始化成其類型的默認值,如


整形和浮點型變量的默認值爲0
字符串變量的默認值爲空字符串
布爾便來給你默認爲bool
切片,函數,指針變量的默認類型爲nil


在C 語言中,變量在聲明時,並不對變量對應的內存區域進行清理操做,此時,變量值多是徹底不可預期的結果。

package main

import "fmt"

// 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
var (
    a int     // 定義整形數據a
    b string  // 定義字符型數據b
    c float32 //定義浮點類型數據c
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println(a, b, c) // 只定義,其會自動賦予初始的默認值
    // 變量的賦值
    a = 10
    b = "a"
    c = 10.10
    //變量使用
    fmt.Println(a, b, c) // 打印結果,使用fmt.Println進行打印
}

結果以下

Go語言基礎(一)

3 初始化變量

標準格式

var 變量名 類型= 表達式

package main

import "fmt"

// 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
var (
    a int     = 10    // 定義整形數據a並進行初始化
    b string  = "a"   // 定義字符型數據b並進行初始化 
    c float32 = 10.10 //定義浮點類型數據c並進行初始化
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println(a, b, c) // 只定義,其會自動賦予初始的默認值
    // 變量的賦值
}

編譯器推導類型的格式

package main

import "fmt"

// 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
var (
    a = 10    // 定義整形數據a並進行初始化
    b = "a"   // 定義字符型數據b
    c = 10.10 //定義浮點類型數據c
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println(a, b, c) // 只定義,其會自動賦予初始的默認值
    // 變量的賦值
}

短變量聲明並初始化

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main

    // 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
    a := 10
    b := "a"
    c := 10.10
    fmt.Println(a, b, c) // 只定義,其會自動賦予初始的默認值
    // 變量的賦值
}

4 多變量同時賦值

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main

    // 變量聲明,此處聲明的變量若不使用,則編譯和運行是不能經過的
    a, b, c := 10, "a", 10.10
    fmt.Println(a, b, c) // 只定義,其會自動賦予初始的默認值
    // 變量的賦值
}

5 交換賦值 (起初的內存資源很是緊缺,計算機大牛經過一些算法來避免使用中間變量)

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var a int = 100
    var b int = 200

    // 方法1

    a = a ^ b
    b = b ^ a
    a = a ^ b
    fmt.Println(a, b)

    //方法2

    a = a + b
    b = a - b
    a = a - b
    fmt.Println(a, b)
    //方法3
    a, b = b, a
    fmt.Println(a, b)
}

結果以下

Go語言基礎(一)

6 匿名變量

在使用多重賦值時,若不須要左值中接受變量,可以使用匿名變量,匿名變量的表現是一個"_"下劃線,使用匿名變量時,只須要在變量聲明的地方使用下劃線替換便可

package main

import "fmt"

func Test() (int, int) {  //其返回兩個整形數據
    return 100, 200
}

func main() { //定義main函數,通常的文件執行的入口函數都是main
    a, _ := Test()
    _, b := Test()
    fmt.Println(a, b)
}

結果以下

Go語言基礎(一)

7 變量使用基本總結

1 變量表示內存中的一個存儲區域,該區域內的數值能夠在同一種類型範圍內不斷變化
2 該區域有本身的名稱(變量名)和類型(數據類型)
3 golang變量的三種方式
A 指定變量類型,聲明後,不賦值,使用默認值
B 根據值自行判斷變量類型
C 使用:= 進行聲明,左側的變量不該該是已經聲明的變量,不然會致使編譯錯誤
4 變量在其做用域內不能重名
5 變量= 變量名+數值類型
6 go 變量若沒給初始值,則編譯器會使用默認值

3 變量數據類型

1 簡介

Go 語言中有豐富的數據類型,除了基本的整形,浮點型,布爾型,字符串外,還有切片,結構體,函數,map,通道(channel)等,Go語言的基本類型和其餘語言大致相同,切片類型有着指針的便利性,但比指針安全,不少高級語言都配有切片進行安全和高效率的內存操做。


複雜的數據類型,如結構體,函數,map和切片將會在後面介紹,此處再也不介紹。

2 整形

基本兩種分類,一種是按長度分類,另外一種是按有無符號分類
1 按長度分爲爲int8 uint8 佔用一個字節,int16 uint16 佔用兩個字節 int32 uint32佔用四個字節 int64 和 uint64 佔用八個字節
2 按有無符號分爲有符號和無符號,有符號長度分爲 int8 int16 int32 int64,無符號分爲 uint8 uint16 uint32 uint64


使用細節
1 golang整形類型分爲有符號和無符號,int 和uint 的大小和系統有關,32位系統int默認是 -2^31-1 到 2^31-1,64位系統-2^63-1到2^63-1 Uint默認32 位系統是0-2^32-1,64位系統是0-2^64-1
2 golang的整形默認聲明爲int 類型
3 golang程序中整形變量在使用時,遵循保小不保大原則,及:在保證程序正確運行時,應儘可能使用佔用空間小的數據類型
4 bit:計算機中的最小存儲單元,byte 計算機中最基本的存儲單元
5 uint8 也就是byte型,int16 對應C語言中的short型,int64對應C語言中的long型。

3 浮點型

GO 語言支持兩種浮點型數:float32和float64,這兩種浮點型數據遵循IEEE 754 標準

單精度 4 字節 float32 -3.403E38 到 3.403E38
雙精度 8 字節 float64 -1.798E308 到 1.798E308


注意:
1 關於浮點數字在機器中存放形式是浮點數=符號位+指數位+尾數位
2 尾部部分可能丟失,形成精度丟失


浮點數使用細節
1 golang浮點類型有固定的範圍和字段長度,不受具體的操做系統影響
2 golang的浮點數類型默認是float64
3 浮點型常量有兩種表現形式
A 十進制表示,如 5.12 .512
B 科學計數法 5.12E2=5.12*10^2,5.12E-2=5.12/10^2
4 一般狀況下,應該是float64,由於其比float32更精確


實例以下

package main

import (
    "fmt"
    "math"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Printf("%f\n", math.Pi)   //格式化輸出爲浮點型
    fmt.Printf("%.2f\n", math.Pi) // 指定小數點後面的位數
    fmt.Printf("%10f\n", math.Pi) // 指定其輸出長度
}

4 字符類型

基本介紹
golang中沒有專門的字符類型,若須要存儲單個字符(字母),通常使用byte來保存


字符串是一串固定長度的字符連接起來的字符序列,golang的字符是由單個字節連接起來的,也就是傳統的字符串是由字符組成,而golang是由字節組成

package main

import (
    "fmt"
    "unsafe"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var c1 byte = 'a' //    其必須是單引號,如果雙引號,則是字符串,
    var c2 byte = 'a'
    c3 := '愛' //此處默認類型是Int
    c4 := 98
    c5 := "a"
    c6 := "abcd"
    c7 := '\t'                                                                        //特殊字符
    c8 := c1 + c2                                                                     //字符運算
    fmt.Println(c1, c2, c3, c4, c5, c6, c7, c8)                                       //打印值
    fmt.Printf("%T,%T\n", c1, c5)                                                     //打印類型,經過"%T"來獲取
    fmt.Printf("%v,%v,%v\n", unsafe.Sizeof(c1), unsafe.Sizeof(c5), unsafe.Sizeof(c6)) //打印佔用字節大小
    fmt.Printf("%c,%c,%c,%c,%c\n", c1, c2, c3, c4, c8)                                //格式化輸出原有的值
}

結果以下

Go語言基礎(一)

使用細節
1 字符類型常量是用單引號''括起來的單個字符,""雙引號括起來的叫字符串
2 golang中容許使用轉義字符"\"來將後面的字符變爲特殊字符
3 go語言使用utf-8編碼,基本不存在亂碼問題,字符是四字節,字符串是16字節大小
4 在go語言中,字符本質是一個整數,直接輸出時,是該字符對應的utf-8編碼對應的碼值
5 能夠直接給某個變量賦值一個數字,而後格式化輸出使用%c,便可輸出對應的Unicode字符
6 字符類型是能夠進行運算的,至關於一個整數


相關說明
1 若是咱們保存的字符ASCII表,如[0-9,a-z,A-Z],則可直接保存在byte中
2 若保存的字符對應碼值大於255,則可以使用int保存
3 若使用字符串輸出,則使用%c


實例以下

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var c1 byte = 'a'
    var c2 int = '中'
    var c3 byte = 97
    fmt.Printf("%c,%c,%c", c1, c2, c3)
}

字符類型的本質

1 字符型存儲到計算機中,須要將字符對應的碼值找出來其順序以下

存儲:字符-----對應碼值----二進制---存儲
讀取:二進制----碼值---字符---讀取

2 字符和碼值的對應關係是經過字符編碼表來決定的

3 go語言的編碼都爲utf8,不會產生亂碼狀況

5 字符串

字符串就是一串固定長度的字符連接起來的字符序列,go語言的字符串就是由單個字符連接

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c1 := "1234"
    c1 = "6789"
    c2 := "124234234\n13423542345" // 默認會轉義\n
    c3 := `124234234\n13423542345` //此處不會轉義\n,原生輸出
    fmt.Println(c1, c2, c3)
}

字符串轉義符

Go 語言的字符串常見轉義符包含回車、換行、單雙引號、指標符等

轉義符 含義
\r 回車符
\n 換行符(直接跳到下一行的同列位置)
\t 製表符
\' 單引號
\" 雙引號
\ 反斜槓
package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    fmt.Println("str:=\"c:\\Go\\bin\\go.exe\"")
}

Go語言基礎(一)

**使用和注意事項

1 golang語言的字符串的字節使用utf-8編碼表示Unicode文本,這樣golang統一使用utf-8編碼,不會產生亂碼
2 字符串,不能修改,字符串是不可變數據類型,及不能經過修改某個字符來修改,總體是能夠進行賦值的
3 字符串的兩種表示形式
A 雙引號,會識別轉義字符
B 反引號,以字符串原生形式輸出,包括換行符和特殊字符,能夠防止輸出源碼等效果 **

6 布爾型

布爾型數據在Go語言中以bool 類型進行聲明,布爾型數據只有true和false兩個值,布爾類型佔用一個字節,適用於邏輯運算,通常適用於流程控制。

package main

import (
    "fmt"
    "unsafe"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main

    c1 := true
    var c2 bool //默認爲false
    fmt.Printf("%v,%v,%v,%v", c1, c2, unsafe.Sizeof(c1), unsafe.Sizeof(c2))
}

結果以下

Go語言基礎(一)

細節說明

不能夠是0或者非0的整數替代false或true,這點和C語言不一樣

4 基本數據類型轉換

1 其餘數據類型轉換爲string

介紹
golang和java/C 不一樣,go在不一樣類型的變量之間賦值須要顯式轉換,也就是golang中的數據類型不能自動轉換


基本語法
表達式T(v),將v轉換爲類型T
T:就是數據類型,如int32,int64,float32等
v:就是須要轉換的變量

package main

import (
    "fmt"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var i int = 32
    var j float32 = float32(i) //類型轉換以下
    var x int32 = 1024
    var z int8 = int8(x) //其值會變爲0
    fmt.Printf("類型分別爲:%T   %T  %T  %T   %v\n", i, j, x, z, z)
}

Go語言基礎(一)

細節說明

Go中,數據類型的轉換是能夠從範圍小的--->表示範圍大的,也能夠從範圍大的-->範圍小的,被轉換的是變量存儲的數值,變量自己的數據類型並無發生變化

在轉換中,若將int64轉換成int8,編譯器自己不會報錯,只是轉換結果按溢出處理

2基本數據類型和string類型的轉換

1 介紹

在程序開發中,咱們常常須要將基本數據類型轉換成string類型,或者將string類型轉換成基本數據類型

2 基本數據類型轉換成string數據類型

方式一
fmt.Sprintf("%參數",表達式) [建議使用]

方式二
使用strconv 包函數

package main

import (
    "fmt"
    "strconv"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    st1 := fmt.Sprintf("%d", 11234) //%d爲原有類型

    st2 := fmt.Sprintf("%t", false)
    fmt.Printf("轉換值爲%v,轉換後的類型爲%T\n", st1, st1)
    fmt.Printf("轉換值爲%v,轉換後的類型爲%T\n", st2, st2)
    st3 := strconv.FormatBool(true)
    fmt.Printf("轉換值爲%v,轉換後的類型爲%T\n", st3, st3)
    st4 := strconv.FormatFloat(10.000, 'f', -1, 64)
    /*
        func FormatFloat(f float64, fmt byte, prec, bitSize int) string
        f:表示要轉換的浮點型的數,bitSize表示f的來源類型(32:float3二、64:float64),會據此進行舍入。
        fmt表示格式:'f'(-ddd.dddd)、'b'(-ddddp±ddd,指數爲二進制)、'e'(-d.dddde±dd,十進制指數)、'E'(-d.ddddE±dd,十進制指數)、'g'(指數很大時用'e'格式,不然'f'格式)、'G'(指數很大時用'E'格式,不然'f'格式)。
        prec控制精度(排除指數部分):對'f'、'e'、'E',它表示小數點後的數字個數;對'g'、'G',它控制總的數字個數。若是prec 爲-1,則表明使用最少數量的、但又必需的數字來表示f。
    */
    fmt.Printf("轉換值爲%v,轉換後的類型爲%T\n", st4, st4)
}

結果以下

Go語言基礎(一)

注意事項

將string轉換爲整數時,須要確保string類型可以轉換成有效數據,如"hello",則不能轉換爲整形

package main

import (
    "fmt"
    "strconv"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    t1, _ := strconv.ParseBool("false")
    f1, _ := strconv.ParseFloat("10.100", 64)
    l1, _ := strconv.ParseInt("11", 8, 64) //base 指定前面的數的進制數,後面返回默認是十進制,bitSize 表示指定進制的位數
    /*
        func ParseInt(s string, base int, bitSize int) (i int64, err error)
        返回字符串表示的整數值,接受正負號。
        base指定進制(2到36),若是base爲0,則會從字符串前置判斷,"0x"是16進制,"0"是8進制,不然是10進制;
        bitSize指定結果必須能無溢出賦值的整數類型,0、八、1六、3二、64 分別表明 int、int八、int1六、int3二、int64;返回的err是*NumErr類型的,若是語法有誤,err.Error = ErrSyntax;若是結果超出類型範圍err.Error = ErrRange。
    */

    fmt.Printf("%v,類型爲%T \t  %v 類型爲%T\n", t1, t1, f1, f1)
    fmt.Printf("%v,類型爲%T\n", l1, l1)

}

結果以下

Go語言基礎(一)

5 字符串應用

1 計算字符串長度len() 函數

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c1 := "ABCD"
    c2 := "中國"
    c3 := "hello中國"
    fmt.Printf("c1 長度爲:%v,c2 長度爲:%v,c3的長度爲:%v\n", len(c1), len(c2), len(c3))
    fmt.Printf("c1 長度爲:%v,c2 長度爲:%v,c3的長度爲:%v\n", utf8.RuneCountInString(c1), utf8.RuneCountInString(c2), utf8.RuneCountInString(c3))

}

結果以下

Go語言基礎(一)

總結
ASCII 字符串長度可以使用len() 函數
Unicode 字符串長度使用utf8.RuneCountInString()函數

2 遍歷字符串

package main

import (
    "fmt"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c3 := "hello中國"
    for i := 0; i < len(c3); i++ {
        fmt.Printf("%c\n", c3[i])
    }

    fmt.Println("----------------------------")

    for _, value := range c3 {
        fmt.Printf("%c\n", value)
    }
}

結果以下

Go語言基礎(一)

總結
ASCII 字符串遍歷直接使用下表
Unicode字符串遍歷須要使用for range進行

3 獲取字符串某一段字符

package main

import (
    "fmt"
    "strings"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c3 := "hello 中國"
    c1 := strings.Index(c3, " ")      //經過指定的值獲取對應的索引,其爲空格,索引是從0開始,所以其爲5
    c2 := strings.Index(c3[c1:], "中") //其從5開始,左閉右開,其中子爲第二個,及爲1,若後面的不存在,則爲-1 
    fmt.Println(c1, c2, c3[c1+c2:])   //此處的c3[6:] 則爲從中開始
}

結果以下

Go語言基礎(一)

總結

字符串索引比較經常使用的有如下幾種方式

1 strings.Index: 正向搜索字符串
2 strings.LastIndex: 反向搜索字符串
3 搜索起始位置可經過切片偏移製做

4 修改字符串

Go語言的字符串沒法直接修改每個字符元素,只能從新構造新的字符串並賦值給原來的字符串變量實現

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c1 := "hello 中國"
    c2 := []byte(c1)
    fmt.Println(len(c1))
    for i := 5; i <= 11; i++ {
        c2[i] = ' ' //此處修改成空串
    }
    fmt.Println(string(c2), len(string(c2)))
}

結果以下

Go語言基礎(一)

總結
GO語言中的字符串是不可修改的
修改字符串時,可將字符串轉換成[]byte進行修改
[]byte個string能夠經過強制類型轉換互轉

5 連接字符串

package main

import (
    "fmt"
    "strings"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    c1 := "hello"
    c2 := "中國"
    c3 := c1 + c2  //簡單的字符串相加進行鏈接 
    fmt.Println(c3)
    var c4 strings.Builder  //定義字節緩衝,
    c4.WriteString(c1) // 將字符串寫入字節緩衝
    c4.WriteString(c2)
    fmt.Println(c4.String())  //緩衝字符的方式輸出
}

結果以下

Go語言基礎(一)

6 格式化

相關樣式和功能

動詞 功能
%v 按值的原本值輸出
%+v 在%v的基礎上,對結構體字段名和值進行了展開
%#v 輸出符合Go語言格式的值
%T 輸出Go語言語法格式的類型的值
%% 輸出%本體
%b 整形以二進制方式顯示
%o 整形以八進制方式顯示
%d 整形以十進制方式顯示
%x 整形以十六進制方式顯示
%X 整形以十六進制,字母大寫方式顯示
%U Unicode字符
%f 浮點數
%p 指針
%t bool類型

7 字符串經常使用函數講解

package main

import (
    "fmt"
    "strconv"
    "strings"
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    str := "這個Go語言的測試字串"
    fmt.Printf("字符串長度爲%d\n", len(str))
    str1 := [] rune(str) //其默認是29個字符串,此處可進行處理
    for i := 0; i < len(str1); i++ {
        fmt.Printf("%c", str1[i])
    }
    fmt.Println()
    str2 := strconv.Itoa(1234) //數字轉字符串
    fmt.Printf("類型爲%T,值爲%s\n", str2, str2)
    var byt = []byte("hello world") //字符串轉字節序列
    fmt.Printf("類型爲%T,值爲%c\n", byt, byt)
    num := strconv.FormatInt(1234, 2) // 將10進制的1234轉換爲二進制值
    fmt.Println(num)
    boo := strings.Contains("hello", "llo") //若存在,則返回爲true,不存在,則返回爲false
    fmt.Println(boo)
    num1 := strings.Count("mmmmysql", "m") //字符串中包含子串的個數
    fmt.Println(num1)
    boo1 := strings.EqualFold("abc", "ABC") //比較是否爲大小寫
    fmt.Println(boo1)
    num2 := strings.Index("hello", "o") // 子串第一次出現的索引
    fmt.Println(num2)
    num3 := strings.LastIndex("go  golang", "go") // 返回子串最後一次出現的索引
    fmt.Println(num3)
    str3 := strings.Replace("hello  Go", "Go", "Go 語言", 1)
    fmt.Println(str3)
    str4 := strings.Split("Golang python mysql", " ") //以空格劃分
    fmt.Printf("類型爲%T,值爲%s\n", str4, str4)
    str5 := strings.ToLower("Go lang") //大寫替換爲小寫
    fmt.Println(str5)
    str6 := strings.TrimSpace("   sfasdfasfas    ") //去掉左右兩邊空格
    fmt.Println(str6)
    str7 := strings.Trim("!!!! Golang !!!", "!") //去掉指定字符
    fmt.Println(str7)
    str8 := strings.TrimLeft("!!!! Golang !!!", "!") //去掉左邊字符
    fmt.Println(str8)
    str9 := strings.TrimRight("!!!! Golang !!!", "!") //去掉右邊的字符
    fmt.Println(str9)
}

結果以下

Go語言基礎(一)

6 指針

1 簡介

指針概念在Go語言中被拆分爲兩個核心概念

1 類型指針:容許對這個指針類型的數據進行修改,傳遞數據使用指針,而無需值拷貝,類型指針不能進行偏移和運算


2 切片:由指向起始元素的原始指針,元素數量和容量組成


Go語言的指針類型變量擁有指針的高效訪問,但又不會發生指針偏移,從而避免非法修改關鍵性數據問題,同時,垃圾回收機制也比較容易對不會發生偏移的指針進行檢索和回收。


切片比原始指針具有更強大的特性,更爲安全,切片發生越界時,運行時會報出宕機,並打印出堆棧,而指針只會崩潰。

2 指針和指針類型

建立指針

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    ptr := new(string) // 建立指針變量,並指定其指針對應的值的類型爲string
    *ptr = "ABCD"
    fmt.Printf("其變量類型爲:%T,其變量值爲:%v", ptr, *ptr)
}

結果以下

Go語言基礎(一)

每一個變量在運行時都會擁有一個地址,這個地址表明變量在內存中的位置,Go語言中使用"&"操做符放在變量前面來對變量進行取值操做

如 ptr:=&v // v的類型爲T
其中v表明被取地址的變量,被取地址的v使用ptr變量進行接收,ptr的類型就是"*T",稱做T的指針類型,"*"表示指針

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var l1 int = 10
    ptr := &l1 //指針類型,此處指針類型和源數據類型類似,爲*int ,其值爲地址
    fmt.Printf("值爲%v,類型爲%T, l1對應的地址爲: %v", ptr, ptr, &l1)
}

結果爲

Go語言基礎(一)

其每次運行的值是不一樣的,在32位平臺上,將是32位地址,在64位平臺上將是64位地址


變量,指針和地址的關係:每一個變量都擁有地址,指針的值就是變量的地址。

3 取值和修改

package main

import "fmt"

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var l int = 10
    ptr := &l                                      //指針類型,此處指針類型和源數據類型類似,爲*int ,其值爲地址
    fmt.Printf("l1修改前的值爲%v, *ptr的值爲%v\n", l, *ptr) // *ptr  根據其地址取值
    *ptr = 200                                     //修改指針指向的值
    fmt.Printf("值爲%v,類型爲%T, l1對應的地址爲: %v  \t l1 修改後的值爲:%v", ptr, ptr, &l, l)
}

結果以下

Go語言基礎(一)

4 總結

取地址操做符"&" 和 取值操做符"*" 是一對互補操做符,"&"取出地址,"*" 根據地址取出地址指向的值。

變量,指針地址,指針變量,取地址,取值的關係以下

對變量進行取地址(&)操做,可獲取這個變量的指針變量
指針變量的值是指針地址
對指針變量進行取值(*)操做,能夠得到這個指針變量指向的原變量值,可進行賦值修改。

7 變量生命週期

1 棧

1 棧基礎

棧(stack)是一種擁有特殊規則的線性表數據結構
概念
棧只容許放線性表的一段放入數據,以後在這一端取出數據,按照先進後出LIFO的順序,如向箱子中放東西,放的越早,越是最後被拿出來的。

2 棧的概念

棧頂(Top)
入棧(Push)
出棧(Pop)
棧底(Bottom)

Go語言基礎(一)

往棧中放入元素的過程稱爲入棧,入棧會增長棧的元素數量,最後放入的元素老是位於棧的頂部,最早放入的元素老是位於棧的底部。


從棧中取出元素時,只能從棧頂取出,取出元素後,棧的數量會變少。最早放入的元素老是最後被取出,最後放入的元素老是最早被取出,不容許從棧底取出數據,也不容許對棧成員進行任何查看和修改操做。

2 變量和棧的關係

棧可用於內存分配,棧的分配和回收速度極快

func Test(a, b int) int {
    var c int   //此處會分配內存
    c = a * b
    var d int  //此處也會分配內存 
    d = c * a   
    return d
}

Go 默認狀況下會將c和d 分配在棧上,這兩個變量在Test()函數退出時就再也不使用,函數結束時,保存在c和d的棧內存再出站釋放內存,整個分配內存的過程經過棧的分配和回收都會很是迅速。

2 堆

堆在內存分配中相似於一個往房間裏擺放各類傢俱,傢俱的尺寸有大有小,分配內存時,須要找一塊足夠大的空間進行存儲,在屢次分配內存後,其會致使在向其中分配空間,雖然有足夠空間,但各空間分配不均,致使沒法造成連續空間來存儲數據,此時便須要對這些空間進行調整優化。


堆分配內存和棧分配內存相比,堆適合不可預知大小的內存分配,但爲此付出的代價是分配速度較慢,且容易造成內存碎片。

3 變量逃逸(Escape Analysis)

1 概念

堆和棧各有優缺點,在C/C++語言中,須要開發者本身學習如何進行內存分配,選用不一樣的內存分配方式來適應不一樣的算法需求,如函數局部變量儘可能使用棧,全局變量,結構體成員使用堆分配等,程序員須要在不一樣項目中學習,記憶並實踐和使用


Go語言將這個過程整合到編譯器中,命名爲"變量逃逸分析",這個技術由編碼分析代碼的特徵和代碼生命週期,決定應該如何對堆仍是棧進行內存分配,即便程序員使用Go語言完成了整個工程後也不會感覺到這個過程


2 變量逃逸分析

package main

import "fmt"

func Test(b int) int {
    var c int
    c = b
    return c
}

func Test1() {

}
func main() { //定義main函數,通常的文件執行的入口函數都是main
    var a int
    Test1()
    fmt.Println(a, Test(0))
}

結果以下

Go語言基礎(一)

參數:

其中-goflags參數是編譯參數,其中-m 表示內存分配分析,-l 表示避免程序內聯,也就是避免對程序進行優化

第二行表示變量a逃逸到堆。
第三行表示Test(0) 調用逃逸到堆,因爲Test()函數會返回一個整形值,這個值被fmt.Println使用後仍是會在其聲明後繼續在main()函數中存在。

上面例子中變量c是整形,其值經過Test()的返回值"逃出"了Test()函數,c變量值被複制並做爲Test()函數的返回,及時c變量在Test()函數中分配的內存被釋放,也不會影響main()中使用Test()返回的值,c變量使用棧分配不會影響結果。

3 取地址發生逃逸

package main

import "fmt"

type Test struct {
}

func Test1() *Test { //此處表示返回是一個指針,且是類型爲Test的指針
    var c Test
    return &c //此處取地址付
}
func main() { //定義main函數,通常的文件執行的入口函數都是main

    fmt.Println(Test1())
}

結果以下

Go語言基礎(一)

第一行出現了新提示,將c移動到堆中,這話表示,Go編譯器已經確認若是將c變量分配在棧上是沒法保證程序最終結果的,若是堅持這樣作,則多是引入一個局部變量的地址,Go最終選擇將c的Test結構分配到堆上,而後由垃圾回收機制進行回收c的內存。

4 原則

在使用Go語言進行編譯時,Go語言的設計者不但願開發者將精力放在內存應該分配在棧仍是堆上的問題,編譯器會自動幫助開發者完成這個選擇。
其編譯其以爲應該分配在堆和棧上的原則是:
1 變量是否被取地址
2 變量是否發生逃逸

8 標識符規範

1 概念

golang對各類變量,方法等命名時使用的字符序列稱爲標識符,凡是能夠起名字的地方都叫標識符

2 命名規範

1 有26個大小寫英文字母、數字0-9和下劃線"_"組成
2 數字不可開頭
3 golang中嚴格區分大小寫
4 標識符不能包含空格
5 下劃線自己在Go中是一種特殊的標識符,稱爲空標識符,其能夠表明任何其餘的標識符,可是其對應的值會被忽略,因此單獨出現時僅表示爲佔位符,不能做爲標識符使用
6 不能以系統關鍵字做爲標識符

3 標識符命名注意事項

1 包名:保持package的名字和上級目錄保持一致,儘可能採用有意義的包名,簡短,有意義,不要和標準庫衝突


2 變量名,函數名,常量名均採用駝峯命名法


3 若是變量名,函數名,常量名首字母大寫,則可被其餘包訪問,若是首字母小寫,則只能在本包中使用(注: 可理解成,首字母大寫是共有,首字母小寫是私有)

4 保留關鍵字

在Go中爲了簡化代碼編譯過程當中對代碼的解析,定義的保留關鍵字有25個,以下

break   default   func    interface   select   
case    defer  go  map   struct    chan   
else     goto        package    switch     
const   fallthrough   if   range    type     
continue   for  import   return    var

5 預約義標識符

除了保留關鍵字外,go還提供了36個預約義標識符,其包含基礎數據類型和內嵌函數

append  bool    byte    cap close
complex complex64   complex128  uint16  copy
false   float32 float64 imag    int
int8    int16   uint32  int32   int64
iota    len make    new nil
panic   uint64  print   println real
recover string  true    uint    uint8
uintptr

6 值類型和引用類型

值類型:變量直接存儲值,內存一般在棧中分配

應用類型:變量存儲一個地址,這個地址對應的空間纔是真正存儲值的地方,內存一般在堆上分配,當沒有任何變量引用這個地址時,該地址對應的數據空間就成爲一個垃圾,由GC來回收

五 常量

1 簡介

相對於變量,常量是恆定不變的值,如圓周率


能夠在編譯時,對常量表達式求值計算,並在運行期使用該計算結果,計算結果沒法被修改

2 常量的定義和使用

常量的聲明和變量很是相似,只是把var換成了const

package main

import "fmt"

//單個常量的定義
const l1 = "2345"
const l2 = 1234

//多個常量的定義
const (
    l3 = "goland"
    l4 = "golang"
    l5 = 5
)

func main() { //定義main函數,通常的文件執行的入口函數都是main
    var arr [l5]int //由於其是常量,所以可被直接使用
    fmt.Println(l1, l2, l3, l4, arr)
}

結果以下

Go語言基礎(一)

3 枚舉--一組常量值

Go 語言中現階段沒有枚舉,可使用常量配合iota模擬枚舉

package main

import "fmt"

type C1 int //將此定義爲int 類型,枚舉的本質和int類似

const (
    A C1 = iota //開始生成枚舉值,默認爲0,將A的類型標識爲C1,這樣標識後,const下方的常量默承認以是默認類型,
    // 默認使用前面的類型做爲常量類型,此行的iota進行常量值自動生成,iota起始爲0,通常建議從0開始
    // 一個const聲明被的每一行常量聲明,將會自動套用前面的iota格式,並自動增長,
    S
    R
    B
)

func main() {
    fmt.Println(A, S, R, B) //輸出全部枚舉類型
    fmt.Printf("%T\n", A)

    var c1 C1 = B //使用枚舉類型賦初始值
    fmt.Println(c1)
}

結果以下

Go語言基礎(一)

package main

import "fmt"

type C1 int //將此定義爲int 類型,枚舉的本質和int類似

const (
    A = 1 << iota //此處定義一個將其左移動1位的操做
    B             //10
    C             //100
    D             //1000
)

func main() {
    fmt.Printf("%d  %d  %d  %d\n", A, B, C, D) //十進制打印
    fmt.Printf("%b  %b  %b  %b\n", A, B, C, D) // 二進制打印
}

Go語言基礎(一)

六 類型別名

1 簡介

類型別名是Go 1.9 版本添加的新功能,主要用於代碼升級,遷移中類型兼容性的問題,在C/C++語言中,代碼重構升級可使用宏快速定義新的一段代碼,Go語言中沒有選擇加宏,而是將解決代碼重構中最麻煩的類型名變動問題。

2 類型別名定義

//Go  1.9 版本以前的內建類型定義的代碼以下

type byte uint8
type rune int32

//Go  1.9 版本以後變爲

type byte = uint8
type rune = int32

3 區分類型別名和類型定義

類型別名的寫法爲

type TypeAlias=Type


類型別名規範:TypeAlias 只是Type的別名,本質上和TypeAlias 與 Type 是同一個類型。


類型別名與類型定義表面上只是一個等號的差別,但實際區別比較大

以下

package main

import "fmt"

//定義新類型
type NewInt int

//定義類型別名
type IntAlias = int

func main() {
    var a NewInt //聲明變量a類型爲NewInt
    fmt.Printf("a 的類型爲:%T\n", a)
    var b IntAlias //將b聲明爲IntAlias 類型
    fmt.Printf("b 的類型爲:%T\n", b)

}

結果以下

Go語言基礎(一)

IntAlias 類型只會存在於代碼中,在編譯完成時,不會有IntAlias 類型。

4 非本地類型不能定義方法

可以隨意爲各類類型起名字,是否意味着能夠在本身包裏爲這些類型任意添加方法,以下

package main

import "time"

type A = time.Duration

func (m A) Test(a string) {  //不能在一個非本地類型time.Duration上定義新方法,,非本地方法是指使用time.Duration 
//的代碼所在的包,也就是main包,由於time.Duration 是在time中定義的,在main中使用,time.Duration 包和main包不在同一個
//包中,所以不能爲不在同一個包中的類型定義方法

}
func main() {
}

結果以下

Go語言基礎(一)

解決方法

1 將type A = time.Duration修改成 type A time.Duration ,也就是從別名修改成類型

2 將 type A = time.Duration 定義到time中

相關文章
相關標籤/搜索