官方網(wǎng)站:https://golang.org/
標(biāo)準(zhǔn)庫文檔:https://golang.org/pkg/
在線編碼學(xué)習(xí):https://play.golang.org/
PS:如若訪問不了婿奔,請自行FQ。
簡介
安裝
Hello World 實(shí)例
編譯
基礎(chǔ)語法
行分隔符
注釋
標(biāo)識(shí)符
關(guān)鍵字
數(shù)據(jù)類型
指針類型(Pointer)
數(shù)組(Array)
結(jié)構(gòu)化類型(struct)
接口類型(interface)
Map 類型
Any類型
類型轉(zhuǎn)換
類型增加方法
變量作用域
值類型和引用類型
常量
iota
運(yùn)算符
運(yùn)算符優(yōu)先級(jí)
條件與循環(huán)語句
函數(shù)
返回多個(gè)值
參數(shù)
匿名函數(shù)與閉包
范圍(Range)
遞歸
查詢
接口查詢
類型查詢
錯(cuò)誤處理
面向?qū)ο? 成員的可訪問性
封裝
繼承
多態(tài)
goroutine和channel
同步
消息傳遞
多個(gè)channel
參考
簡介
Go語言是谷歌推出的一種全新的編程語言,可以在不損失應(yīng)用程序性能的情況下降低代碼的復(fù)雜性如叼。谷歌首席軟件工程師羅布派克(Rob Pike)說:我們之所以開發(fā)Go冰木,是因?yàn)檫^去10多年間軟件開發(fā)的難度令人沮喪。
Go是從2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持開發(fā),后來還加入了Ian Lance Taylor, Russ Cox等人(真是豪門出身)踊沸,并最終于2009年11月開源歇终,在2012年早些時(shí)候發(fā)布了Go 1穩(wěn)定版本。現(xiàn)在Go的開發(fā)已經(jīng)是完全開放的逼龟,并且擁有一個(gè)活躍的社區(qū)评凝。
Go語言專門針對多處理器系統(tǒng)應(yīng)用程序的編程進(jìn)行了優(yōu)化,使用Go編譯的程序可以媲美C或C++代碼的速度腺律,而且更加安全奕短、支持并行進(jìn)程。
Go的目標(biāo)是希望提升現(xiàn)有編程語言對程序庫等依賴性(dependency)的管理匀钧,這些軟件元素會(huì)被應(yīng)用程序反復(fù)調(diào)用翎碑。由于存在并行編程模式,因此這一語言也被設(shè)計(jì)用來解決多處理器的任務(wù)榴捡。
如果第一次接觸Go語言杈女,更像是對c語言的修補(bǔ)和延伸。
** 特性 **
- 編譯型語言,執(zhí)行效率接近c(diǎn)/c++,可以編譯成機(jī)器碼姆打,不依賴其他庫
- 自動(dòng)垃圾回收
- 更豐富的內(nèi)置類型和自動(dòng)類型推導(dǎo)(類似c++11的auto)
- 函數(shù)可以返回多個(gè)值
- 擁有更好的錯(cuò)誤處理
- 匿名函數(shù)和閉包
- 支持類型和接口
- 并發(fā)編程
- 支持反射機(jī)制
- 語言交互性強(qiáng)
- 工程依賴自動(dòng)推導(dǎo)
- 打包和部署容易
安裝
$ sudo apt-get install golang
$ sudo apt-get install gccgo
$ go version
go version go1.2.1 linux/amd64
Hello World 實(shí)例
package main
import"fmt"
func main(){/* 這是我的第一個(gè)簡單的程序 */
fmt.Println("Hello, World!")
}
//執(zhí)行
$ go run hello.go
Hello,World!
編譯
Go是一個(gè)編譯型的語言根吁。目前有兩種編譯器,其中”Gccgo”采用GCC作為編譯后端。另外還有 根據(jù)處理器架構(gòu)命名的編譯器:針對64位x86結(jié)構(gòu)為”6g”,針對32位x86結(jié)構(gòu)的為”8g”等等。 這些go專用的編譯器編譯很快蝇裤,但是產(chǎn)生的目標(biāo)代碼效率比gccgo稍差一點(diǎn)。
基礎(chǔ)語法
行分隔符
在 Go 程序中频鉴,一行代表一個(gè)語句結(jié)束栓辜。每個(gè)語句不需要像 C 家族中的其它語言一樣以分號(hào) ; 結(jié)尾,因?yàn)檫@些工作都將由 Go 編譯器自動(dòng)完成垛孔。 如果你打算將多個(gè)語句寫在同一行藕甩,它們則必須使用 ;人為區(qū)分,但在實(shí)際開發(fā)中我們并不鼓勵(lì)這種做法周荐。
注釋
注釋不會(huì)被編譯狭莱,每一個(gè)包應(yīng)該有相關(guān)注釋。 單行注釋是最常見的注釋形式概作,你可以在任何地方使用以 // 開頭的單行注釋腋妙。多行注釋也叫塊注釋,均已以 /* 開頭讯榕,并以 */ 結(jié)尾骤素。如:
// 單行注釋
/*
Author by Tonny
我是多行注釋
*/
標(biāo)識(shí)符
標(biāo)識(shí)符用來命名變量匙睹、類型等程序?qū)嶓w。一個(gè)標(biāo)識(shí)符實(shí)際上就是一個(gè)或是多個(gè)字母(AZ和az)數(shù)字(0~9)谆甜、下劃線_組成的序列垃僚,但是第一個(gè)字符必須是字母或下劃線而不能是數(shù)字。
關(guān)鍵字
Go 代碼中會(huì)使用到的 25 個(gè)關(guān)鍵字或保留字规辱,Go 語言還有 36 個(gè)預(yù)定義標(biāo)識(shí)符。
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
---
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
程序一般由關(guān)鍵字栽燕、常量罕袋、變量、運(yùn)算符碍岔、類型和函數(shù)組成浴讯。 程序中可能會(huì)使用到這些分隔符:括號(hào) (),中括號(hào) [] 和大括號(hào) {}蔼啦。 程序中可能會(huì)使用到這些標(biāo)點(diǎn)符號(hào):.榆纽、,、;捏肢、: 和 …奈籽。
數(shù)據(jù)類型
在 Go 編程語言中,數(shù)據(jù)類型用于聲明函數(shù)和變量鸵赫。 數(shù)據(jù)類型的出現(xiàn)是為了把數(shù)據(jù)分成所需內(nèi)存大小不同的數(shù)據(jù)衣屏,編程的時(shí)候需要用大數(shù)據(jù)的時(shí)候才需要申請大內(nèi)存,就可以充分利用內(nèi)存辩棒。
***布爾型 ***
布爾型的值只可以是常量 true 或者 false狼忱。一個(gè)簡單的例子:var b bool = true。
***數(shù)字類型 ***
整型 int 和浮點(diǎn)型 float一睁,Go 語言支持整型和浮點(diǎn)型數(shù)字钻弄,并且原生支持復(fù)數(shù),其中位的運(yùn)算采用補(bǔ)碼者吁。
uint8
無符號(hào) 8 位整型 (0 到 255)
uint16
無符號(hào) 16 位整型 (0 到 65535)
uint32
無符號(hào) 32 位整型 (0 到 4294967295)
uint64
無符號(hào) 64 位整型 (0 到 18446744073709551615)
int8
有符號(hào) 8 位整型 (-128 到 127)
int16
有符號(hào) 16 位整型 (-32768 到 32767)
int32
有符號(hào) 32 位整型 (-2147483648 到 2147483647)
int64
有符號(hào) 64 位整型 (-9223372036854775808 到 9223372036854775807)
float32
IEEE-754 32位浮點(diǎn)型數(shù)
float64
IEEE-754 64位浮點(diǎn)型數(shù)
complex64
32 位實(shí)數(shù)和虛數(shù)
complex128
64 位實(shí)數(shù)和虛數(shù)
byte
類似 uint8
rune
類似 int32
uint
32 或 64 位
int
與 uint 一樣大小
uintptr
無符號(hào)整型窘俺,用于存放一個(gè)指針
字符串類型
字符串就是一串固定長度的字符連接起來的字符序列。Go的字符串是由單個(gè)字節(jié)連接起來的砚偶。Go語言的字符串的字節(jié)使用UTF-8編碼標(biāo)識(shí)Unicode文本批销。
指針類型(Pointer)
Go 語言中指針是很容易學(xué)習(xí)的,Go 語言中使用指針可以更簡單的執(zhí)行一些任務(wù)染坯。我們都知道均芽,變量是一種使用方便的占位符,用于引用計(jì)算機(jī)內(nèi)存地址单鹿。 Go 語言的取地址符是 &掀宋,放到一個(gè)變量前使用就會(huì)返回相應(yīng)變量的內(nèi)存地址。
一個(gè)指針變量可以指向任何一個(gè)值的內(nèi)存地址它指向那個(gè)值的內(nèi)存地址。 類似于變量和常量劲妙,在使用指針前你需要聲明指針湃鹊。 在指針類型前面加上 * 號(hào)(前綴)來獲取指針?biāo)赶虻膬?nèi)容。
package main
import"fmt"
func main(){
var a int = 20/* 聲明實(shí)際變量 */
var ip *int/* 聲明指針變量 */
ip = &a /* 指針變量的存儲(chǔ)地址 */
fmt.Printf("a 變量的地址是: %x\n",&a )
/* 指針變量的存儲(chǔ)地址 */
fmt.Printf("ip 變量的存儲(chǔ)地址: %x\n", ip )
/* 使用指針訪問值 */
fmt.Printf("*ip 變量的值: %d\n",*ip )
}
當(dāng)一個(gè)指針被定義后沒有分配到任何變量時(shí)镣奋,它的值為 nil币呵。 nil 指針也稱為空指針。 nil在概念上和其它語言的null侨颈、None余赢、nil、NULL一樣哈垢,都指代零值或空值妻柒。 一個(gè)指針變量通常縮寫為 ptr耘分。
if(ptr !=nil)/* ptr 不是空指針 */
if(ptr ==nil)/* ptr 是空指針 */
如果一個(gè)指針變量存放的又是另一個(gè)指針變量的地址举塔,則稱這個(gè)指針變量為指向指針的指針變量。 當(dāng)定義一個(gè)指向指針的指針變量時(shí)求泰,第一個(gè)指針存放第二個(gè)指針的地址央渣,第二個(gè)指針存放變量的地址:
import"fmt"
func main(){
var a int
var ptr *int
var pptr **int
a =3000
/* 指針 ptr 地址 */
ptr =&a
/* 指向指針 ptr 地址 */
pptr =&ptr
/* 獲取 pptr 的值 */
fmt.Printf("變量 a = %d\n", a )
fmt.Printf("指針變量 *ptr = %d\n",*ptr )
fmt.Printf("指向指針的指針變量 **pptr = %d\n",**pptr)
}
數(shù)組(Array)
package main
import "fmt"
func main() {
var n [10]int /* n 是一個(gè)長度為 10 的數(shù)組 */
var i, j int
/* 為數(shù)組 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 設(shè)置元素為 i + 100 */
}
/* 輸出每個(gè)數(shù)組元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j])
}
}
結(jié)構(gòu)化類型(struct)
Go 語言中數(shù)組可以存儲(chǔ)同一類型的數(shù)據(jù),但在結(jié)構(gòu)體中我們可以為不同項(xiàng)定義不同的數(shù)據(jù)類型拜秧。 結(jié)構(gòu)體是由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合痹屹。 Go語言的結(jié)構(gòu)體(struct)和其它語言的類(class)有同等的地位。但Go語言放棄了包括繼承在內(nèi)的大量OOP特性枉氮,只保留了組合(compose)這個(gè)最基礎(chǔ)的特性志衍。 所有的Go語言的類型(指針類型除外)都是可以有自己的方法。在這個(gè)背景下聊替,Go語言的結(jié)構(gòu)體(struct)它只是很普通的復(fù)合類型楼肪。
package main
import"fmt"
type Booksstruct{
title string
author string
subject string
book_id int
}
func main(){
var Book1 Books/* 聲明 Book1 為 Books 類型 */
var Book2 Books/* 聲明 Book2 為 Books 類型 */
/* book 1 描述 */
Book1.title ="Go 語言"
Book1.author ="https://tour.go-zh.org"
Book1.subject ="Go 語言教程"
Book1.book_id =6495407
/* book 2 描述 */
Book2.title ="Python 教程"
Book2.author ="https://tour.python-zh.org"
Book2.subject ="Python 語言教程"
Book2.book_id =6495700
/* 打印 Book1 信息 */
printBook(Book1)
/* 打印 Book2 信息 */
printBook(Book2)
}
func printBook( book Books){
fmt.Printf("Book title : %s\n", book.title);
fmt.Printf("Book author : %s\n", book.author);
fmt.Printf("Book subject : %s\n", book.subject);
fmt.Printf("Book book_id : %d\n", book.book_id);
}
構(gòu)造函數(shù)?不需要惹悄。在Go語言中你只需要定義一個(gè)普通的函數(shù)春叫,只是通常以NewXXX來命名,表示“構(gòu)造函數(shù)”:
func NewRect(x, y, width, height float64) *Rect {
return &Rect{x, y, width, height}
}
這一切非常自然泣港,沒有任何突兀之處暂殖。
切片(Slice)
Go語言切片是對數(shù)組的抽象。
Go數(shù)組的長度不可改變当纱,在特定場景中這樣的集合就不太適用呛每,Go中提供了一種靈活,功能強(qiáng)悍的內(nèi)置類型切片("動(dòng)態(tài)數(shù)組"),與數(shù)組相比:
- 切片的長度是不固定的坡氯,可以追加元素晨横,在追加時(shí)可能使切片的容量增大洋腮。
- 切片是可索引的,并且可以由 len()方法獲取長度手形。
- 切片提供了計(jì)算容量的方法 cap()可以測量切片最長可以達(dá)到多少啥供。
- 一個(gè)切片在未初始化之前默認(rèn)為nil,長度為0
- 可以通過設(shè)置下限及上限來設(shè)置截取切片[lower-bound:upper-bound]
package main
import "fmt"
func main() {
/* 創(chuàng)建切片 */
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片從索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默認(rèn)下限為 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默認(rèn)上限為 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int, 0, 5)
printSlice(numbers1)
/* 打印子切片從索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片從索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
//輸出結(jié)果
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允許追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一個(gè)元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同時(shí)添加多個(gè)元素 */
numbers = append(numbers, 2, 3, 4)
printSlice(numbers)
/* 創(chuàng)建切片 numbers1 是之前切片的兩倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷貝 numbers 的內(nèi)容到 numbers1 */
copy(numbers1, numbers)
printSlice(numbers1)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
接口類型(interface)
Go 語言提供了另外一種數(shù)據(jù)類型即接口库糠,它把所有的具有共性的方法定義在一起伙狐,任何其他類型只要實(shí)現(xiàn)了這些方法就是實(shí)現(xiàn)了這個(gè)接口。
package main
import"fmt"
type Phoneinterface{
call()
}
type NokiaPhonestruct{
}
func (nokiaPhone NokiaPhone) call(){
fmt.Println("I am Nokia, I can call you!")
}
type IPhonestruct{
}
func (iPhone IPhone) call(){
fmt.Println("I am iPhone, I can call you!")
}
func main(){
var phone Phone
phone =new(NokiaPhone)
phone.call()
phone =new(IPhone)
phone.call()
}
Map 類型
Map 是一種無序的鍵值對的集合曼玩。Map 最重要的一點(diǎn)是通過 key 來快速檢索數(shù)據(jù)鳞骤,key 類似于索引,指向數(shù)據(jù)的值黍判。 Map 是一種集合,所以我們可以像迭代數(shù)組和切片那樣迭代它篙梢。不過顷帖,Map 是無序的,我們無法決定它的返回順序渤滞,這是因?yàn)?Map 是使用 hash 表來實(shí)現(xiàn)的贬墩。
package main
import "fmt"
func main() {
/* 創(chuàng)建 map */
countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New Delhi"}
fmt.Println("原始 map")
/* 打印 map */
for country := range countryCapitalMap {
fmt.Println("Capital of", country, "is", countryCapitalMap[country])
}
/* 刪除元素 */
delete(countryCapitalMap, "France")
fmt.Println("Entry for France is deleted")
fmt.Println("刪除元素后 map")
/* 打印 map */
for country := range countryCapitalMap {
fmt.Println("Capital of", country, "is", countryCapitalMap[country])
}
}
Any類型
由于Go語言中任何對象實(shí)例都滿足空接口interface{},故此interface{}看起來像是可以指向任何對象的Any類型妄呕。如下:
var v1 interface{}=1// 將int類型賦值給interface{}
var v2 interface{}="abc"http:// 將string類型賦值給interface{}
var v3 interface{}=&v2 // 將*interface{}類型賦值給interface{}
var v4 interface{}=struct{ X int}{1}
var v5 interface{}=&struct{ X int}{1}
當(dāng)一個(gè)函數(shù)可以接受任意的對象實(shí)例時(shí)陶舞,我們會(huì)將其聲明為interface{}。最典型的例子是標(biāo)準(zhǔn)庫fmt中PrintXXX系列的函數(shù)绪励。例如:
func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
類型轉(zhuǎn)換
類型轉(zhuǎn)換用于將一種數(shù)據(jù)類型的變量轉(zhuǎn)換為另外一種類型的變量肿孵。Go 語言類型轉(zhuǎn)換基本格式如下:
type_name(expression)
type_name 為類型,expression 為表達(dá)式疏魏。
package main
import "fmt"
func main() {
var sum int = 17
var count int = 5
var mean float32
mean = float32(sum) / float32(count)
fmt.Printf("mean 的值為: %f\n", mean)
}
類型增加方法
在Go語言中停做,你可以給任意類型(包括內(nèi)置類型,但指針類型除外)增加方法大莫,例如:
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
在Go語言中沒有隱藏的this指針蛉腌。這句話的含義是:
第一,方法施加的目標(biāo)(也就是“對象”)顯式傳遞只厘,沒有被隱藏起來烙丛。
第二,方法施加的目標(biāo)(也就是“對象”)不需要非得是指針羔味,也不用非得叫this河咽。
在這個(gè)例子中,我們定義了一個(gè)新類型Integer介评,它和int沒有本質(zhì)不同库北,只是它為內(nèi)置的int類型增加了個(gè)新方法:Less爬舰。如此,你就可以讓整型看起來像個(gè)類那樣用:
func main() {
var a Integer = 1
if a.Less(2) {
fmt.Println(a, “Less 2”)
}
}
變量
Go語言變量名由字母寒瓦、數(shù)字情屹、下劃線組成,其中首個(gè)字母不能為數(shù)字杂腰。
聲明變量的一般形式是使用var關(guān)鍵字:
var identifier type
//指定變量類型垃你,聲明后若不賦值,使用默認(rèn)值喂很。
var v_name v_type
v_name = value
//根據(jù)值自行判定變量類型惜颇。
var v_name = value
v_name := value
// 省略var, 注意 :=左側(cè)的變量不應(yīng)該是已經(jīng)聲明過的,否則會(huì)導(dǎo)致編譯錯(cuò)誤少辣。
var a int = 10
var b = 10
c : = 10
//類型相同多個(gè)變量, 非全局變量
var x, y int
var ( // 這種因式分解關(guān)鍵字的寫法一般用于聲明全局變量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
//這種不帶聲明格式的只能在函數(shù)體中出現(xiàn)
//g, h := 123, "hello"
func main(){
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}
變量作用域
作用域?yàn)橐崖暶鳂?biāo)識(shí)符所表示的常量凌摄、類型、變量漓帅、函數(shù)或包在源代碼中的作用范圍锨亏。 Go 語言中變量可以在三個(gè)地方聲明:
- 函數(shù)內(nèi)定義的變量稱為局部變量
- 函數(shù)外定義的變量稱為全局變量
- 函數(shù)定義中的變量稱為形式參數(shù)
值類型和引用類型
多數(shù)Go語言中的類型,包括:
基本類型忙干。如byte器予、int、bool捐迫、float32乾翔、float64、string等等施戴。
復(fù)合類型反浓。如數(shù)組(array)、結(jié)構(gòu)體(struct)暇韧、指針(pointer)等勾习。都基于值語義。Go語言中類型的值語義表現(xiàn)得非常徹底懈玻。我們這么說是因?yàn)閿?shù)組(array)巧婶。如果你學(xué)習(xí)過C語言,你會(huì)知道C語言中的數(shù)組(array)比較特別涂乌。通過函數(shù)傳遞一個(gè)數(shù)組的時(shí)候基于引用語義艺栈,但是在結(jié)構(gòu)體中定義數(shù)組變量的時(shí)候是值語義(表現(xiàn)在結(jié)構(gòu)體賦值的時(shí)候,該數(shù)組會(huì)被完整地拷貝一份新的副本)湾盒。
有4種引用類型:slice湿右,map,channel罚勾,interface毅人。
值類型的變量的值存儲(chǔ)在棧(stack)中吭狡。 當(dāng)使用等號(hào) = 將一個(gè)變量的值賦值給另一個(gè)變量時(shí),如:j = i丈莺,實(shí)際上是在內(nèi)存中將 i 的值進(jìn)行了拷貝 可以通過 &i 來獲取變量 i 的內(nèi)存地址划煮,例如:0xf840000040(每次的地址都可能不一樣)。 內(nèi)存地址會(huì)根據(jù)機(jī)器的不同而有所不同缔俄,甚至相同的程序在不同的機(jī)器上執(zhí)行后也會(huì)有不同的內(nèi)存地址弛秋。因?yàn)槊颗_(tái)機(jī)器可能有不同的存儲(chǔ)器布局,并且位置分配也可能不同俐载。 更復(fù)雜的數(shù)據(jù)通常會(huì)需要使用多個(gè)字蟹略,這些數(shù)據(jù)一般使用引用類型保存。
一個(gè)引用類型的變量 r1 存儲(chǔ)的是 r1 的值所在的內(nèi)存地址(數(shù)字)遏佣,或內(nèi)存地址中第一個(gè)字所在的位置 這個(gè)內(nèi)存地址為稱之為指針挖炬,這個(gè)指針實(shí)際上也被存在另外的某一個(gè)字中。 同一個(gè)引用類型的指針指向的多個(gè)字可以是在連續(xù)的內(nèi)存地址中(內(nèi)存布局是連續(xù)的)状婶,這也是計(jì)算效率最高的一種存儲(chǔ)形式茅茂;也可以將這些字分散存放在內(nèi)存中,每個(gè)字都指示了下一個(gè)字所在的內(nèi)存地址太抓。 當(dāng)使用賦值語句 r2 = r1 時(shí),只有引用(地址)被復(fù)制令杈。 如果 r1 的值被改變了走敌,那么這個(gè)值的所有引用都會(huì)指向被修改后的內(nèi)容,在這個(gè)例子中逗噩,r2 也會(huì)受到影響掉丽。
常量
常量是一個(gè)簡單值的標(biāo)識(shí)符,在程序運(yùn)行時(shí)异雁,不會(huì)被修改的量捶障。 常量中的數(shù)據(jù)類型只可以是布爾型、數(shù)字型(整數(shù)型纲刀、浮點(diǎn)型和復(fù)數(shù))和字符串型项炼。 常量的定義格式:
package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" //多重賦值
area = LENGTH * WIDTH
fmt.Printf("面積為 : %d", area)
println()
println(a, b, c)
}
//常量還可以用作枚舉
const (
Unknown = 0
Female = 1
Male = 2
)
//常量可以用len(), cap(), unsafe.Sizeof()常量計(jì)算表達(dá)式的值。
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
iota
iota示绊,特殊常量锭部,可以認(rèn)為是一個(gè)可以被編譯器修改的常量。 在每一個(gè)const關(guān)鍵字出現(xiàn)時(shí)面褐,被重置為0拌禾,然后再下一個(gè)const出現(xiàn)之前,每出現(xiàn)一次iota展哭,其所代表的數(shù)字會(huì)自動(dòng)增加1湃窍。
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //獨(dú)立值闻蛀,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢復(fù)計(jì)數(shù)
i //8
)
fmt.Println(a, b, c, d, e, f, g, h, i)//#0 1 2 ha ha 100 100 7 8
}
運(yùn)算符
運(yùn)算符用于在程序運(yùn)行時(shí)執(zhí)行數(shù)學(xué)或邏輯運(yùn)算。 Go 語言內(nèi)置的運(yùn)算符有:(所有語言通用您市,略過)
- 算術(shù)運(yùn)算符
- 關(guān)系運(yùn)算符
- 邏輯運(yùn)算符
- 位運(yùn)算符
- 賦值運(yùn)算符
- 其他運(yùn)算符
其中 & 返回變量存儲(chǔ)地址 &a; 將給出變量的實(shí)際地址觉痛。 - 指針變量。 *a; 是一個(gè)指針變量
運(yùn)算符優(yōu)先級(jí)
有些運(yùn)算符擁有較高的優(yōu)先級(jí)墨坚,二元運(yùn)算符的運(yùn)算方向均是從左至右秧饮。下表列出了所有運(yùn)算符以及它們的優(yōu)先級(jí),由上至下代表優(yōu)先級(jí)由高到低:
^ !
* / % << >> & &^
+ - | ^
== != < <= >= >
<-
&&
||
條件與循環(huán)語句
package main
import "fmt"
func main() {
var b int = 15
var a int
numbers := [6]int{1, 2, 3, 5}
/* for 循環(huán) */
for a := 0; a < 10; a++ {
fmt.Printf("a 的值為: %d\n", a)
/* 使用 break 語句跳出循環(huán) */
break
/* 跳過此次循環(huán) */
continue
}
for a < b {
a++
fmt.Printf("a 的值為: %d\n", a)
}
for i, x := range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
}
/* 循環(huán) */
LOOP:
for a < 20 {
if a == 15 {
/* 跳過迭代 */
a = a + 1
goto LOOP
}
fmt.Printf("a的值為 : %d\n", a)
a++
}
for true {
fmt.Printf("這是無限循環(huán)泽篮。\n")
}
}
函數(shù)
Go 語言最少有個(gè) main() 函數(shù)盗尸。 你可以通過函數(shù)來劃分不同功能,邏輯上每個(gè)函數(shù)執(zhí)行的是指定的任務(wù)帽撑。 函數(shù)聲明告訴了編譯器函數(shù)的名稱泼各,返回類型,和參數(shù)亏拉。 Go 語言標(biāo)準(zhǔn)庫提供了多種可動(dòng)用的內(nèi)置的函數(shù)扣蜻。例如,len() 函數(shù)可以接受不同類型參數(shù)并返回該類型的長度及塘。如果我們傳入的是字符串則返回字符串的長度莽使,如果傳入的是數(shù)字,則返回?cái)?shù)組中包含的函數(shù)個(gè)數(shù)笙僚。
package main
import "fmt"
func main() {
/* 定義局部變量 */
var a int = 100
var b int = 200
var ret int
/* 調(diào)用函數(shù)并返回最大值 */
ret = max(a, b)
fmt.Printf("最大值是 : %d\n", ret)
}
/* 函數(shù)返回兩個(gè)數(shù)的最大值 */
func max(num1, num2 int) int {
/* 定義局部變量 */
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}
返回多個(gè)值
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
}
參數(shù)
函數(shù)如果使用參數(shù)芳肌,該變量可稱為函數(shù)的形參。 形參就像定義在函數(shù)體內(nèi)的局部變量肋层。 調(diào)用函數(shù)亿笤,可以通過兩種方式來傳遞參數(shù):
值傳遞:值傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對參數(shù)進(jìn)行修改栋猖,將不會(huì)影響到實(shí)際參數(shù)净薛。
引用傳遞:引用傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址傳遞到函數(shù)中,那么在函數(shù)中對參數(shù)所進(jìn)行的修改蒲拉,將影響到實(shí)際參數(shù)肃拜。
默認(rèn)情況下,Go 語言使用的是值傳遞全陨,即在調(diào)用過程中不會(huì)影響到實(shí)際參數(shù)爆班。
匿名函數(shù)與閉包
Go 語言支持匿名函數(shù),可作為閉包(閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù))辱姨。匿名函數(shù)是一個(gè)”內(nèi)聯(lián)”語句或表達(dá)式柿菩。匿名函數(shù)的優(yōu)越性在于可以直接使用函數(shù)內(nèi)的變量,不必申明雨涛。
package main
import "fmt"
func getSequence() func() int {
i := 0
return func() int {
i += 1
return i
}
}
func main() {
/* nextNumber 為一個(gè)函數(shù)枢舶,函數(shù) i 為 0 */
nextNumber := getSequence()
/* 調(diào)用 nextNumber 函數(shù)懦胞,i 變量自增 1 并返回 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
/* 創(chuàng)建新的函數(shù) nextNumber1,并查看結(jié)果 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}
范圍(Range)
Go 語言中 range 關(guān)鍵字用于for循環(huán)中迭代數(shù)組(array)凉泄、切片(slice)躏尉、鏈表(channel)或集合(map)的元素。在數(shù)組和切片中它返回元素的索引值后众,在集合中返回 key-value 對的 key 值胀糜。
package main
import "fmt"
func main() {
//這是我們使用range去求一個(gè)slice的和。使用數(shù)組跟這個(gè)很類似
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//在數(shù)組上使用range將傳入index和值兩個(gè)變量蒂誉。上面那個(gè)例子我們不需要使用該元素的序號(hào)教藻,所以我們使用空白符"_"省略了。有時(shí)侯我們確實(shí)需要知道它的索引右锨。
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//range也可以用在map的鍵值對上括堤。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用來枚舉Unicode字符串。第一個(gè)參數(shù)是字符的索引绍移,第二個(gè)是字符(Unicode的值)本身悄窃。
for i, c := range "go" {
fmt.Println(i, c)
}
}
/*
sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111
*/
遞歸
遞歸,就是在運(yùn)行的過程中調(diào)用自己蹂窖。 Go 語言支持遞歸轧抗。但我們在使用遞歸時(shí),開發(fā)者需要設(shè)置退出條件瞬测,否則遞歸將陷入無限循環(huán)中鸦致。 遞歸函數(shù)對于解決數(shù)學(xué)上的問題是非常有用的,就像計(jì)算階乘涣楷,生成斐波那契數(shù)列等。
package main
import "fmt"
func Factorial(x int) (result int) {
if x == 0 {
result = 1
} else {
result = x * Factorial(x-1)
}
return
}
func main() {
var i int = 15
fmt.Printf("%d 的階乘是 %d\n", i, Factorial(i))
}
查詢
接口查詢
var file1 Writer=...
if file5, ok := file1.(two.IStream); ok {
...
}
這個(gè)if語句的含義是:file1接口指向的對象實(shí)例是否實(shí)現(xiàn)了two.IStream接口呢抗碰?如果實(shí)現(xiàn)了狮斗,則… 接口查詢是否成功,要在運(yùn)行期才能夠確定弧蝇。它不像接口賦值碳褒,編譯器只需要通過靜態(tài)類型檢查即可判斷賦值是否可行。 讓語言內(nèi)置接口查詢看疗,這是一件非常了不起的事情沙峻。
類型查詢
在Go語言中,你還可以更加直接了當(dāng)?shù)卦儐柦涌谥赶虻膶ο髮?shí)例的類型两芳。 就像現(xiàn)實(shí)生活中物種多得數(shù)不清一樣摔寨,語言中的類型也多的數(shù)不清。所以類型查詢并不經(jīng)常被使用怖辆。它更多看起來是個(gè)補(bǔ)充是复,需要配合接口查詢使用删顶。
type Stringerinterface{
String()string
}
func Println(args ...interface{}){
for _, arg := range args {
switch v := v1.(type){
case int:// 現(xiàn)在v的類型是int
case string:// 現(xiàn)在v的類型是string
default:
if v, ok := arg.(Stringer); ok {// 現(xiàn)在v的類型是Stringer
val := v.String()
...
}else{
...
}
}
}
利用反射(reflect)也可以進(jìn)行類型查詢,詳細(xì)可參閱reflect.TypeOf方法相關(guān)文檔淑廊。
錯(cuò)誤處理
Go 語言通過內(nèi)置的錯(cuò)誤接口提供了非常簡單的錯(cuò)誤處理機(jī)制逗余。 error類型是一個(gè)接口類型,這是它的定義:
type error interface{
Error()string
}
我們可以在編碼中通過實(shí)現(xiàn) error 接口類型來生成錯(cuò)誤信息季惩。 函數(shù)通常在最后的返回值中返回錯(cuò)誤信息录粱。使用errors.New 可返回一個(gè)錯(cuò)誤信息:
package main
import "fmt"
// 定義一個(gè) DivideError 結(jié)構(gòu)
type DivideError struct {
dividee int
divider int
}
// 實(shí)現(xiàn) `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定義 `int` 類型除法運(yùn)算的函數(shù)
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 實(shí)現(xiàn)
}
func main() {
// 正常情況
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 當(dāng)被除數(shù)為零的時(shí)候會(huì)返回錯(cuò)誤信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
面向?qū)ο?/h2>
Go 語言的面向?qū)ο缶幊?OOP)非常簡潔而優(yōu)雅。說它簡潔画拾,是因?yàn)樗鼪]有了OOP中很多概念啥繁,比如:類,繼承碾阁、虛函數(shù)输虱、構(gòu)造函數(shù)和析構(gòu)函數(shù)、隱藏的this指針等等脂凶。說它優(yōu)雅宪睹,是**它的面向?qū)ο?OOP)是語言類型系統(tǒng)(type system)中的天然的一部分。整個(gè)類型系統(tǒng)通過接口(interface)串聯(lián)蚕钦,渾然一體亭病。
成員的可訪問性
Go語言對關(guān)鍵字的增加非常吝嗇。在Go語言中沒有private嘶居、protected罪帖、public這樣的關(guān)鍵字。要想某個(gè)符號(hào)可被其他包(package)訪問邮屁,需要將該符號(hào)定義為大寫字母開頭整袁。
type Rectstruct{
X, Y float64
Width,Height float64
}
func (r *Rect) area() float64 {
return r.Width* r.Height
}
成員方法遵循同樣的規(guī)則,Rect的area方法只能在該類型所在的包(package)內(nèi)使用佑吝。 需要強(qiáng)調(diào)的一點(diǎn)是坐昙,Go語言中符號(hào)的可訪問性是包(package)一級(jí)的,而不是類一級(jí)的芋忿。盡管area是Rect的內(nèi)部方法炸客,但是在同一個(gè)包中的其他類型可以訪問到它。這樣的可訪問性控制很粗曠戈钢,很特別痹仙,但是非常實(shí)用。如果Go語言符號(hào)的可訪問性是類一級(jí)的殉了,少不了還要加上friend這樣的關(guān)鍵字开仰,以表示兩個(gè)類是朋友關(guān)系,可以訪問其中的私有成員。
封裝
package main
import "fmt"
type data struct {
val int
}
//只有在你需要修改對象的時(shí)候抖所,才必須用指針梨州。它不是Go語言的約束,而是一種自然約束田轧。
//究其原因暴匠,是因?yàn)镚o和C語言一樣,類型都是基于值傳遞傻粘。要想修改變量的值每窖,只能傳遞指針。
func (p_data *data) set(num int) {
p_data.val = num
}
func (p_data *data) show() {
fmt.Println(p_data.val)
}
func main() {
p_data := &data{4}
p_data.set(5)
p_data.show()
}
繼承
package main
import "fmt"
type parent struct {
val int
}
type child struct {
parent
num int
}
func main() {
var c child
c = child{parent{1}, 2}
fmt.Println(c.num)
fmt.Println(c.val)
}
多態(tài)
package main
import "fmt"
type act interface {
write()
}
type xiaoming struct {
}
type xiaofang struct {
}
func (xm *xiaoming) write() {
fmt.Println("xiaoming write")
}
func (xf *xiaofang) write() {
fmt.Println("xiaofang write")
}
func main() {
var w act
xm := xiaoming{}
xf := xiaofang{}
w = &xm
w.write()
w = &xf
w.write()
}
goroutine和channel
“網(wǎng)絡(luò)弦悉,并發(fā)”是Go語言的兩大feature窒典。Go語言號(hào)稱“互聯(lián)網(wǎng)的C語言”,與使用傳統(tǒng)的C語言相比稽莉,寫一個(gè)Server所使用的代碼更少瀑志,也更簡單。寫一個(gè)Server除了網(wǎng)絡(luò)污秆,另外就是并發(fā)劈猪,相對python等其它語言,Go對并發(fā)支持使得它有更好的性能良拼。 Goroutine和channel是Go在“并發(fā)”方面兩個(gè)核心feature战得。 Channel是goroutine之間進(jìn)行通信的一種方式,它與Unix中的管道類似庸推。
ChannelType=("chan"|"chan""<-"|"<-""chan")ElementType.
var ch chan int
var ch1 chan<-int//ch1只能寫
var ch2 <-chan int//ch2只能讀
channel是類型相關(guān)的常侦,也就是一個(gè)channel只能傳遞一種類型。例如贬媒,上面的ch只能傳遞int聋亡。 創(chuàng)建channel時(shí)可以提供一個(gè)可選的整型參數(shù),用于設(shè)置該channel的緩沖區(qū)大小际乘。該值缺省為0杀捻,用來構(gòu)建默認(rèn)的“無緩沖channel”,也稱為“同步channel”蚓庭。 Channel作為goroutine間的一種通信機(jī)制,與操作系統(tǒng)的其它通信機(jī)制類似仅仆,一般有兩個(gè)目的:同步器赞,或者傳遞消息。
同步
c := make(chan int)// Allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
go func(){
list.Sort()
c <-1// Send a signal; value does not matter.
}()
doSomethingForAWhile()
<-c // Wait for sort to finish; discard sent value.
上面的示例中墓拜,在子goroutine中進(jìn)行排序操作港柜,主goroutine可以做一些別的事情,然后等待子goroutine完成排序。 接收方會(huì)一直阻塞直到有數(shù)據(jù)到來夏醉。如果channel是無緩沖的爽锥,發(fā)送方會(huì)一直阻塞直到接收方將數(shù)據(jù)取出。如果channel帶有緩沖區(qū)畔柔,發(fā)送方會(huì)一直阻塞直到數(shù)據(jù)被拷貝到緩沖區(qū)氯夷;如果緩沖區(qū)已滿,則發(fā)送方只能在接收方取走數(shù)據(jù)后才能從阻塞狀態(tài)恢復(fù)靶擦。
消息傳遞
經(jīng)典的生產(chǎn)者-消費(fèi)者模型
package main
func Producer(queue chan<- int) {
for i := 0; i < 10; i++ {
queue <- i
}
}
func Consumer(queue <-chan int) {
for i := 0; i < 10; i++ {
v := <-queue
fmt.Println("receive:", v)
}
}
func main() {
queue := make(chan int, 1)
go Producer(queue)
go Consumer(queue)
time.Sleep(1e9) //讓Producer與Consumer完成
}
多個(gè)channel
在實(shí)際編程中腮考,經(jīng)常會(huì)遇到在一個(gè)goroutine中處理多個(gè)channel的情況。我們不可能阻塞在兩個(gè)channel玄捕,這時(shí)就該select場了踩蔚。與C語言中的select可以監(jiān)控多個(gè)fd一樣,go語言中select可以等待多個(gè)channel枚粘。
c1 := make(chan string)
c2 := make(chan string)
go func(){
time.Sleep(time.Second*1)
c1 <-"one"
}()
go func(){
time.Sleep(time.Second*2)
c2 <-"two"
}()
for i :=0; i <2; i++{
select{
case msg1 :=<-c1:
fmt.Println("received", msg1)
case msg2 :=<-c2:
fmt.Println("received", msg2)
}
}