title: 初識go語言
tag:
- golang
- 編程語言
categories: notes
簡介
Go語言是一門全新的靜態(tài)類型開發(fā)語言,與當前的開發(fā)語言相比具備眾多令人興奮不已的新特性。最主要的新特性如下:
- 自動垃圾回收
- 更豐富的內(nèi)置類型
- 函數(shù)多返回值
- 錯誤處理
- 匿名函數(shù)和閉包
- 類型和接口
- 并發(fā)編程
- 反射
- 語言交互性
自動垃圾回收
手動管理內(nèi)存的一個問題就是由于指針的到處傳遞而無法確定何時可以釋放該指針的指向的內(nèi)存塊。假如在代碼中的某個位置我們釋放了內(nèi)存,而另一些地方還在使用指向這塊內(nèi)存的指針,那么這些指針就變成了所謂的野指針或者懸空指針,對這些進行任何讀寫操作都會導(dǎo)致不可預(yù)料的后果。就比如以下的c++代碼,進行內(nèi)存釋放。
int *p = new int;
p +=10 ; //這里對指針進行了偏移,因此那塊內(nèi)存不再被引用
// ...... 這里可能發(fā)生針對這塊int內(nèi)存的垃圾回收。
p -=10; //居然又偏移到原來的位置
*p = 10; //如果有垃圾收集,這里就無法保證可以正常運行了
所謂的垃圾回收,即所有的內(nèi)存分配動作都會被在運行時記錄,同時任何對該內(nèi)存的使用也都會被記錄,然后垃圾回收器一會對所有已經(jīng)分配的內(nèi)存進行跟蹤監(jiān)測,一旦發(fā)現(xiàn)有些內(nèi)存已經(jīng)不再使用,就會階段性的回收這些沒人用的內(nèi)存。
更豐富的內(nèi)置類型
除了幾乎所有語言都支持的簡單內(nèi)置類型外,Go語言也內(nèi)置了一些比較新的語言學(xué)中的內(nèi)置的高級類型,比如C#和Java中的數(shù)組和字符串,除此之外,Go語言中還內(nèi)置了一個對于其他靜態(tài)類型語言通常用庫支持的字典類型。另外又一個新增的數(shù)據(jù)結(jié)構(gòu),數(shù)組切片,我們可以認為數(shù)組切片是一種可動態(tài)增長的數(shù)組。
函數(shù)多返回值
目前主流語言中除了Python外基本都不支持函數(shù)的多返回值功能,比如我們?nèi)绻x一個函數(shù)用于返回個人名字信息,而名字信息因為包含多個部分----姓名,中間名和別名,在不支持多返回值的語言中我們又以下兩種做法,要么專門定義一個結(jié)構(gòu)體用于返回,要么以傳出參數(shù)的方式返回多個結(jié)構(gòu)使用指針。
Go語言革命性的在靜態(tài)語言陣營中率先提供了多返回值功能。Go語言中舉例:
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'
}
并不是每一個返回值都必須賦值,沒有明賦值的返回值將保留默認的空值,函數(shù)的調(diào)用也比較簡單
fn,mn,ln,nn := getName()
如果我們只對函數(shù)其中的某幾個返回值感興趣的話。也可以直接用下劃線作為占位符來忽略其他不關(guān)心的返回值。
_,_,lastName,_:=getName()
錯誤處理
Go語言引入了關(guān)鍵字defer用于標準的錯誤處理流程,并提供了內(nèi)置函數(shù)panic,recover完成異常的拋出與捕獲。
匿名函數(shù)和閉包
f := func(x,y int)int{
return x+y
}
接口類型
Go語言的類型定義非常接近于C語言中的結(jié)構(gòu),甚至沿用了struct關(guān)鍵字。相比而言,Go語言并沒有直接沿襲c++和JAVA的傳統(tǒng)去設(shè)計一個超級復(fù)雜的類型系統(tǒng),不支持繼承和重載,而只是支持了最基本的類型組合功能。
Go語言并不是簡單的對面向?qū)ο箝_發(fā)語言做減法,他還引入了一個無比強大的“非侵入式”接口的概念,在C++中,我們通常會這樣來確定接口和類型的關(guān)系。
//抽象接口
interface IFly{
virtual void Fly()=0;
};
//實現(xiàn)類
class Bird:public IFly{
public:
Bird(){}
virtual ~Bird(){}
public:
void Fly(){
//以鳥的方式飛行
}
};
void main(){
IFly *pFly = new Bird();
pFly ->Fly();
delete pFly;
}
顯然,在實現(xiàn)一個接口之前必須先定義該接口,并且將類型和接口緊密綁定,即接口的修改會影響到所有實現(xiàn)了該接口的類型。而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()
}
并發(fā)編程
Go語言引入了goroutine概念,他使得并發(fā)編程便得很簡單,通過使用goroutine而不是裸用操作系統(tǒng)的并發(fā)機制,以及使用消息傳遞來共享內(nèi)存而不是使用共享內(nèi)存來進行通信。
通過在函數(shù)調(diào)用前使用關(guān)鍵字go,我們即可讓該函數(shù)以goroutine方式執(zhí)行,goroutime是一種比線程更加輕盈,更省資源的協(xié)程。Go語言通過系統(tǒng)的線程來多路派遣這些函數(shù)的執(zhí)行,使得每個用go關(guān)鍵字執(zhí)行的函數(shù)可以運行成為一個單位協(xié)程,當一個協(xié)程阻塞的時候,調(diào)度器就會自動把其他協(xié)程安排到另外的線程中去執(zhí)行,從而實現(xiàn)了程序無等待并行化運行。而且調(diào)度的開銷非常小。
Go語言實現(xiàn)了CSP(通信順序進程)模型來作為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包提供了完備的讀寫鎖功能。
一個簡單的例子來演示goroutine和channel的使用方式,這是一個并行計算的例子,用兩個goroutine進行并行的累加計算,待兩個計算過程都完成后打印計算結(jié)果。
package main
import "fmt"
/*
這是一個并行計算的例子,由兩個goroutine進行并行的累加計算
*/
func sum(values []int, resultChan chan int) {
sum := 0
for _, value := range values {
sum += value
}
resultChan <- sum //將計算結(jié)果發(fā)送到channel中
}
func main() {
values := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
resultChan := make(chan int, 2)
go sum(values[:len(values)/2], resultChan)
go sum(values[len(values)/2:], resultChan)
sum1, sum2 := <-resultChan, <-resultChan //接收結(jié)果
fmt.Println("Result:", sum1, sum2, sum2+sum1)
}
反射
通過反射,你可以獲取對象類型的詳細信息,并可動態(tài)操作對象。反射最常見的使用場景是做對象的序列化。
package main
/*
利用反射功能列出某個類型中所有成員變量的值
*/
import (
"fmt"
"reflect"
)
// 創(chuàng)建一個新類型
type Bird struct {
Name string
LifeExpectance int
}
func (b *Bird) Fly() {
fmt.Println("I'm 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())
}
}
語言交互性
由于Go語言和C語言有天生聯(lián)系,Go語言的設(shè)計者們自然不會忽略如何重用現(xiàn)有C模塊的這個問題,這個功能直接被命名為Cgo,Cgo即是語言特性,同時也是一個工具的名稱。
在Go語言中,可以按Cgo的特定語法混合編寫C語言代碼,然后Cgo工具可以將這些混合的C代碼提取并生成對于C功能的調(diào)用包裝代碼。這里需要安裝gcc編譯環(huán)境
package main
/*
在Go語言中調(diào)用C語言標準庫的puts函數(shù)
#include<stdio.h>
#include<stdlib.h>
*/
import (
"C"
"unsafe"
)
func main() {
cstr := C.CString("Hello, world")
C.puts(cstr)
C.free(unsafe.Pointer(cstr))
}