語言特性
- 自動垃圾回收
- 更豐富的內(nèi)置類型
- 函數(shù)多返回值
- 錯誤處理
- 匿名函數(shù)和閉包
- 類型和接口
- 并發(fā)編程
- 反射
- 語言交互性
自動垃圾回收
編程中,一個難題就是內(nèi)存管理,如果管理不好對內(nèi)存的使用蔚龙,發(fā)生內(nèi)存泄露可能會引發(fā)嚴重的問題,比如使程序崩潰并炮。C和C++這樣的編程語言补君,提供了指針這樣靈活的類型,但卻必須手動管理內(nèi)存拍霜,增加了編程的復(fù)雜性嘱丢。Java等編程語言本身提供了自動的垃圾回收機制,減少了編程的不少負擔(dān)祠饺。go語言融合了眾多編程語言的優(yōu)秀特點越驻,自然也引入了自動的垃圾回收機制。
所謂垃圾回收道偷,即所有的內(nèi)存分配動作都會被在運行時記錄缀旁,同時任何對該內(nèi)存的使用也都會被記錄,然后垃圾回收器會對所有已經(jīng)分配的內(nèi)存進行跟蹤監(jiān)測勺鸦,一旦發(fā)現(xiàn)有些內(nèi)存已經(jīng)不再被任何人使用并巍,就階段性地回收這些沒人用的內(nèi)存。當(dāng)然换途,因為需要盡量最小化垃圾回收的性能損耗懊渡,以及降低對正常程序執(zhí)行過程的影響,現(xiàn)實中的垃圾回收算法要比這個復(fù)雜得多,比如為對象增加年齡屬性等军拟,但基本原理都是如此剃执。
更豐富的內(nèi)置類型
go語言不僅提供了幾乎所有語言都提供的整形,浮點型等簡單類型吻谋,數(shù)組和字符串等高級類型忠蝗,還提供了字典類型(map),數(shù)組切片(Slice)漓拾。
函數(shù)多返回值
Go語言革命性地在靜態(tài)開發(fā)語言陣營中率先提供了多返回值功能阁最。這個特性讓開發(fā)者可以從原來用各種比較別扭的方式返回多個值的痛苦中解脫出來戒祠,既不用再區(qū)分參數(shù)列表中哪幾個用于輸入,哪幾個用于輸出速种,也不用再只為了返回多個值而專門定義一個數(shù)據(jù)結(jié)構(gòu)姜盈。
go語言函數(shù)返回多個值的簡單示例:
func getName() (firstName, middleName, lastName, nickName string) {
return "May", "M", "Chen", "Babe"
}
因為返回值都已經(jīng)有名字,因此各個返回值也可以用如下方式來在不同的位置進行賦值配阵,從而提供了極大的靈活性:
func getName() (firstName, middleName, lastName, nickName string) {
firstName = "May"
middleName = "M"
lastName = "Chen"
nickName = "Babe"
return
}
并不是每一個返回值都必須賦值馏颂,沒有被明確賦值的返回值將保持默認的空值。
多返回值函數(shù)調(diào)用:
fn, mn, ln, nn := getName()
如果只對該函數(shù)其中的某幾個返回值感興趣的話棋傍,也可以直接用下劃線作為占位符來忽略其他不關(guān)心的返回值:
_, _, lastName, _ := getName()
錯誤處理
Go語言引入了3個關(guān)鍵字用于標準的錯誤處理流程救拉。
- defer
- panic
- recover
與C++和Java等語言中的異常捕獲機制相比, Go語言的錯誤處理機制可以大量減少代碼量瘫拣,讓開發(fā)者也無需僅僅為了程序安全性而添加大量一層套一層的try-catch語句亿絮。
代碼中過多的嵌套,真是讓人抓狂麸拄。
匿名函數(shù)和閉包
在Go語言中派昧,所有的函數(shù)也是值類型,可以作為參數(shù)傳遞拢切。
類型和接口
Go語言不支持繼承和重載蒂萎,而只是支持了最基本的類型組合功能。
Go語言引入了一個無比強大的“非侵入式” 接口的概念淮椰,讓開發(fā)者從以往對C++和Java開發(fā)中的接口管理問題中解脫出來五慈。
go語言簡單的接口使用示例:
type Bird struct {
...
}
func (b *Bird) Fly() { //以鳥的方式飛行
}
在實現(xiàn)Bird類型時完全沒有任何IFly的信息。我們可以在另外一個地方定義這個IFly实苞。
type IFly interface {
Fly()
}
上面兩個定義看起來絲毫沒有關(guān)系豺撑,現(xiàn)在看看如何使用它們:
func main() {
var fly IFly = new(Bird)
fly.Fly()
}
可以看出,雖然Bird類型實現(xiàn)的時候黔牵,沒有聲明與接口IFly的關(guān)系,但接口和類型可以直接轉(zhuǎn)換爷肝,甚至接口的定義都不用在類型定義之前猾浦,這種比較松散的對應(yīng)關(guān)系可以大幅降低因為接口調(diào)整而導(dǎo)致的大量代碼調(diào)整工作。
并發(fā)編程
Go語言引入了goroutine概念灯抛,它使得并發(fā)編程變得非常簡單金赦。通過使用goroutine而不是裸用操作系統(tǒng)的并發(fā)機制,以及使用消息傳遞而不是共享內(nèi)存來通信对嚼,Go語言讓并發(fā)編程變得更加輕盈和安全夹抗。
通過在函數(shù)調(diào)用前使用關(guān)鍵字go,我們即可讓該函數(shù)以goroutine方式執(zhí)行纵竖。goroutine是一種比線程更加輕盈漠烧、更省資源的協(xié)程杏愤。Go語言通過系統(tǒng)的線程來多路派遣這些函數(shù)的執(zhí)行,使得每個用go關(guān)鍵字執(zhí)行的函數(shù)可以運行成為一個單位協(xié)程已脓。當(dāng)一個協(xié)程阻塞的時候珊楼,調(diào)度器就會自動把其他協(xié)程安排到另外的線程中去執(zhí)行,從而實現(xiàn)了程序無等待并行化運行度液。而且調(diào)度的開銷非常小厕宗,這使得我們能夠創(chuàng)建大量的goroutine,從而可以很輕松地編寫高并發(fā)程序堕担。
Go語言實現(xiàn)了CSP(通信順序進程已慢,Communicating Sequential Process)模型來作為goroutine間的推薦通信方式。在CSP模型中霹购,一個并發(fā)系統(tǒng)由若干并行運行的順序進程組成佑惠,每個進程不能對其他進程的變量賦值。進程之間只能通過一對通信原語實現(xiàn)協(xié)作厕鹃。Go語言用channel(通道) 這個概念來輕巧地實現(xiàn)了CSP模型兢仰。channel的使用方式比較接近Unix系統(tǒng)中的管道(pipe)概念,可以方便地進行跨goroutine的通信剂碴。
另外把将,由于一個進程內(nèi)創(chuàng)建的所有g(shù)oroutine運行在同一個內(nèi)存地址空間中,因此如果不同的goroutine不得不去訪問共享的內(nèi)存變量忆矛,訪問前應(yīng)該先獲取相應(yīng)的讀寫鎖察蹲。Go語言標準庫中的sync包提供了完備的讀寫鎖功能。
反射
Go語言的反射實現(xiàn)了反射的大部分功能催训,但沒有像Java語言那樣內(nèi)置類型工廠洽议,所以無法做到像Java那樣通過類型字符串創(chuàng)建對象實例。
反射最常見的使用場景是做對象的序列化(serialization)漫拭。 例如亚兄,Go語言標準庫的encoding/json、encoding/xml采驻、encoding/gob审胚、encoding/binary等包就大量依賴于反射功能來實現(xiàn)。
go語言發(fā)射的簡單示例:
package main
import (
"fmt"
"reflect"
)
type Bird struct {
Name string
LifeExpectance int
}
func (b *Bird) Fly() {
fmt.Println("I am flying...")
}
func main() {
sparrow := &Bird{"Sparrow", 3}
s := reflect.ValueOf(sparrow).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(),
f.Interface())
}
}
輸出結(jié)果:
0: Name string = Sparrow
1: LifeExpectance int = 3
語言交互性
Go語言為了方便的重用現(xiàn)有的C模塊礼旅,提供了Cgo膳叨。
在Go代碼中,可以按Cgo的特定語法混合編寫C語言代碼痘系,然后Cgo工具可以將這些混合的C代碼提取并生成對于C功能的調(diào)用包裝代碼菲嘴。
Go語言使用C模塊的簡單示例:
package main
/*
#include <stdio.h>
*/
import "C"
import "unsafe"
func main() {
cstr := C.CString("Hello, world")
C.puts(cstr)
C.free(unsafe.Pointer(cstr))
}
語言結(jié)構(gòu)
基礎(chǔ)組成
- 包聲明
- 引入包
- 函數(shù)
- 變量
- 語句 & 表達式
- 注釋
簡單示例
學(xué)習(xí)一門語言,從輸出“Hello,world!”開始龄坪。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")/* 輸出Hello, World! */
}
-
package main
定義了包名昭雌。必須在源文件中非注釋的第一行指明這個文件屬于哪個包。package main
表示一個可獨立執(zhí)行的程序悉默,每個 Go 應(yīng)用程序都包含一個名為main
的包城豁。 -
import "fmt"
告訴 Go 編譯器這個程序需要使用fmt
包中的內(nèi)容。 -
func main()
是程序開始執(zhí)行的函數(shù)抄课。main
函數(shù)是每一個可執(zhí)行程序所必須包含的唱星。 -
/*...*/
是多行注釋。你也可以使用單行注釋//
-
fmt.Println("Hello, World!")
跟磨,使用fmt
包中的輸出函數(shù)Println
將“Hello, World!”字符串輸出到控制臺间聊。
執(zhí)行g(shù)o程序
- 在你的機器上,安裝go語言開發(fā)環(huán)境抵拘。
- 在你設(shè)置的工作目錄下哎榴,新建hello.go文件。
- 將上面示例代碼復(fù)制到hello.go文件中僵蛛。
- 進入hello.go所在目錄尚蝌,執(zhí)行g(shù)o run hello.go命令行
$ go run hello.go
Hello, World!