Golang語言特性

好久沒有更新博客了,前段時間博主一個再找實習,所以一直沒有更新自己的博客,以後會堅持每週一篇2016年剛開始,博主就給自己制定了一個學習計劃,學習golang ,至於爲什麼學習golang呢,這篇文章就是博主學習golang的原因就是因爲他的特性,博主是看這本《Go語言編程》學習Golang的,Go語言編程

Go語言最主要的特性:
自動垃圾回收
更豐富的內置類型
函數多返回值
錯誤處理
匿名函數和閉包
類型和接口
併發編程
反射
語言交互性

1,Golang自動回收機制

Go語言作爲一門新生的開發語言,當然不能忽略內存管理這個問題。又因爲Go語言沒有C++
這麼“強大”的指針計算功能,因此可以很自然地包含垃圾回收功能。因爲垃圾回收功能的支持,
開發者無需擔心所指向的對象失效的問題,因此Go語言中不需要delete關鍵字,也不需要free()
方法來明確釋放內存。例如,對於以上的這個C語言例子,如果使用Go語言實現,我們就完全不
用考慮何時需要釋放之前分配的內存的問題,系統會自動幫我們判斷,並在合適的時候(比如CPU
相對空閒的時候)進行自動垃圾收集工作。
非常著名的內存檢查工具,比如Rational Purify、 Compuware BoundsChecker和英特爾的Parallel Inspector等
垃圾回收,即所有的內存分配動作都會被在運行時記錄,同時任何對
該內存的使用也都會被記錄,然後垃圾回收器會對所有已經分配的內存進行跟蹤監測,一旦發現
有些內存已經不再被任何人使用,就階段性地回收這些沒人用的內存。當然,因爲需要儘量最小
化垃圾回收的性能損耗,以及降低對正常程序執行過程的影響,現實中的垃圾回收算法要比這個
複雜得多,比如爲對象增加年齡屬性等,但基本原理都是如此。
Go語言作爲一門新生的開發語言,當然不能忽略內存管理這個問題。又因爲Go語言沒有C++
這麼“強大”的指針計算功能,因此可以很自然地包含垃圾回收功能。因爲垃圾回收功能的支持,
開發者無需擔心所指向的對象失效的問題,因此Go語言中不需要delete關鍵字,也不需要free()
方法來明確釋放內存。例如,對於以上的這個C語言例子,如果使用Go語言實現,我們就完全不
用考慮何時需要釋放之前分配的內存的問題,系統會自動幫我們判斷,並在合適的時候(比如CPU
相對空閒的時候)進行自動垃圾收集工作。

2,GO更豐富的內置類型

除了幾乎所有語言都支持的簡單內置類型(比如整型和浮點型等)外, Go語言也內置了一
些比較新的語言中內置的高級類型,比如C#和Java中的數組和字符串。除此之外, Go語言還內置
了一個對於其他靜態類型語言通常用庫方式支持的字典類型(map)。 Go語言設計者對爲什麼內
置map這個問題的回答也頗爲簡單:既然絕大多數開發者都需要用到這個類型,爲什麼還非要每
個人都寫一行import語句來包含一個庫?這也是一個典型的實戰派觀點,與很多其他語言的學
院派氣息迥然不同。

3Go函數多返回值

函數多返回值
Go語言革命性地在靜態開發語言陣營中率先提供了多返回值功能。這個特性讓開發者可以
從原來用各種比較彆扭的方式返回多個值的痛苦中解脫出來,既不用再區分參數列表中哪幾個用
於輸入,哪幾個用於輸出,也不用再只爲了返回多個值而專門定義一個數據結構。
在Go語言中,上述的例子可以修改爲以下的樣子:

func getName()(firstName, middleName, lastName, nickName string){
return "May", "M", "Chen", "Babe"
}

因爲返回值都已經有名字,因此各個返回值也可以用如下方式來在不同的位置進行賦值,從
而提供了極大的靈活性:

func getName()(firstName, middleName, lastName, nickName string){
firstName = "May"
middleName = "M"
lastName = "Chen"
nickName = "Babe"
return
}

並不是每一個返回值都必須賦值,沒有被明確賦值的返回值將保持默認的空值。而函數的調
用相比C/C++語言要簡化很多:
fn, mn, ln, nn := getName()
如果開發者只對該函數其中的某幾個返回值感興趣的話,也可以直接用下劃線作爲佔位符來
忽略其他不關心的返回值。下面的調用表示調用者只希望接收lastName的值,這樣可以避免聲
明完全沒用的變量:
, , lastName, _ := getName()

4,Go的錯誤處理

Go語言引入了3個關鍵字用於標準的錯誤處理流程,這3個關鍵字分別爲defer、 panic和
recover。本書的“序”已經用示例展示了defer關鍵字的強大之處,在第2章中我們還會詳細
描述Go語言錯誤處理機制的獨特之處。整體上而言與C++和Java等語言中的異常捕獲機制相比,
Go語言的錯誤處理機制可以大量減少代碼量,讓開發者也無需僅僅爲了程序安全性而添加大量
一層套一層的try-catch語句。這對於代碼的閱讀者和維護者來說也是一件很好的事情,因爲可
以避免在層層的代碼嵌套中定位業務代碼。 2.6節將介紹Go語言中的錯誤處理機制。

5,Go的匿名函數和閉包

在Go語言中,所有的函數也是值類型,可以作爲參數傳遞。 Go語言支持常規的匿名函數和
閉包,比如下列代碼就定義了一個名爲f的匿名函數,開發者可以隨意對該匿名函數變量進行傳
遞和調用:

f := func(x, y int) int {
return x + y
}

6,Go類型和接口

Go語言的類型定義非常接近於C語言中的結構(struct),甚至直接沿用了struct關鍵字。相
比而言, Go語言並沒有直接沿襲C++和Java的傳統去設計一個超級複雜的類型系統,不支持繼承
和重載,而只是支持了最基本的類型組合功能。
巧妙的是,雖然看起來支持的功能過於簡潔,細用起來你卻會發現, C++和Java使用那些復
雜的類型系統實現的功能在Go語言中並不會出現無法表現的情況,這反而讓人反思其他語言中
引入這些複雜概念的必要性。我們在第3章中將詳細描述Go語言的類型系統。
Go語言也不是簡單的對面向對象開發語言做減法,它還引入了一個無比強大的“非侵入式”
接口的概念,讓開發者從以往對C++和Java開發中的接口管理問題中解脫出來。在C++中,我們
通常會這樣來確定接口和類型的關係:

// 抽象接口
interface IFly
{
virtual void Fly()=0;
};
// 實現類
class Bird : public IFly
{
public:
Bird()
{}
virtual ~Bird()
{}
public:
void Fly()
{
// 以鳥的方式飛行
}
};
void main()
{
IFly* pFly = new Bird();
pFly->Fly();
delete pFly;
}

顯然,在實現一個接口之前必須先定義該接口,並且將類型和接口緊密綁定,即接口的修改
會影響到所有實現了該接口的類型,而Go語言的接口體系則避免了這類問題:

type Bird struct {
...
}
func (b *Bird) Fly() {
// 以鳥的方式飛行
}

我們在實現Bird類型時完全沒有任何IFly的信息。我們可以在另外一個地方定義這個IFly
接口:

type IFly interface {
Fly()
}
這兩者目前看起來完全沒有關係,現在看看我們如何使用它們:
func main() {
var fly IFly = new(Bird)
fly.Fly()
}

可以看出,雖然Bird類型實現的時候,沒有聲明與接口IFly的關係,但接口和類型可以直
接轉換,甚至接口的定義都不用在類型定義之前,這種比較鬆散的對應關係可以大幅降低因爲接
口調整而導致的大量代碼調整工作。

7,Go 併發編程

Go語言引入了goroutine概念,它使得併發編程變得非常簡單。通過使用goroutine而不是裸用
操作系統的併發機制,以及使用消息傳遞來共享內存而不是使用共享內存來通信, Go語言讓並
發編程變得更加輕盈和安全。
通過在函數調用前使用關鍵字go,我們即可讓該函數以goroutine方式執行。 goroutine是一種
比線程更加輕盈、更省資源的協程。 Go語言通過系統的線程來多路派遣這些函數的執行,使得
每個用go關鍵字執行的函數可以運行成爲一個單位協程。當一個協程阻塞的時候,調度器就會自
動把其他協程安排到另外的線程中去執行,從而實現了程序無等待並行化運行。而且調度的開銷
非常小,一顆CPU調度的規模不下於每秒百萬次,這使得我們能夠創建大量的goroutine,從而可
以很輕鬆地編寫高併發程序,達到我們想要的目的。
Go語言實現了CSP(通信順序進程, Communicating Sequential Process)模型來作爲goroutine
間的推薦通信方式。在CSP模型中,一個併發系統由若干並行運行的順序進程組成,每個進程不
能對其他進程的變量賦值。進程之間只能通過一對通信原語實現協作。 Go語言用channel(通道)
這個概念來輕巧地實現了CSP模型。 channel的使用方式比較接近Unix系統中的管道(pipe)概念,
可以方便地進行跨goroutine的通信。
另外,由於一個進程內創建的所有goroutine運行在同一個內存地址空間中,因此如果不同的
goroutine不得不去訪問共享的內存變量,訪問前應該先獲取相應的讀寫鎖。 Go語言標準庫中的
sync包提供了完備的讀寫鎖功能。

示例代碼:

package main
import "fmt"
func sum(values [] int, resultChan chan int) {
sum := 0
for _, value := range values {
sum += value
}
resultChan <- sum // 將計算結果發送到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 // 接收結果
fmt.Println("Result:", sum1, sum2, sum1 + sum2)
}

8,Go的 反射

反射(reflection)是在Java語言出現後迅速流行起來的一種概念。通過反射,你可以獲取對
象類型的詳細信息,並可動態操作對象。反射是把雙刃劍,功能強大但代碼可讀性並不理想。若
非必要,我們並不推薦使用反射。
Go語言的反射實現了反射的大部分功能,但沒有像Java語言那樣內置類型工廠,故而無法做
到像Java那樣通過類型字符串創建對象實例。在Java中,你可以讀取配置並根據類型名稱創建對
應的類型,這是一種常見的編程手法,但在Go語言中這並不被推薦。
反射最常見的使用場景是做對象的序列化(serialization,有時候也叫Marshal & Unmarshal)。
例如, Go語言標準庫的encoding/json、 encoding/xml、 encoding/gob、 encoding/binary等包就大量
依賴於反射功能來實現。

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())
}
}

9,GO語言交互性

由於Go語言與C語言之間的天生聯繫, Go語言的設計者們自然不會忽略如何重用現有C模塊
的這個問題,這個功能直接被命名爲Cgo。 Cgo既是語言特性,同時也是一個工具的名稱。
在Go代碼中,可以按Cgo的特定語法混合編寫C語言代碼,然後Cgo工具可以將這些混合的C
代碼提取並生成對於C功能的調用包裝代碼。開發者基本上可以完全忽略這個Go語言和C語言的
邊界是如何跨越的。
與Java中的JNI不同, Cgo的用法非常簡單,比如代碼清單1-3就可以實現在Go中調用C語言標
準庫的puts函數。
package main

/*
#include <stdio.h>
*/
import "C"
import "unsafe"
func main() {
cstr := C.CString("Hello, world")
C.puts(cstr)
C.free(unsafe.Pointer(cstr))
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章