Golang核心編程(1)-Go語言簡介及特性


Go語言是谷歌2009發佈的第二款開源編程語言。Go語言專門針對多處理器系統應用程序的編程進行了優化,使用Go編譯的程序可以媲美C或C++代碼的速度,而且更加安全、支持並行進程。Go就是谷歌工程師爲這類程序編寫的一種語言。它不是針對編程初學者設計的,但學習使用它也不是非常困難。Go支持面向對象,而且具有真正的閉包(closures)和反射 (reflection)等功能。在學習曲線方面,派克認爲Go與Java類似,對於Java開發者來說,應該能夠輕鬆學會 Go。之所以將Go作爲一個開源項目發佈,目的是讓開源社區有機會創建更好的工具來使用該語言,例如 Eclipse IDE中的插件。


更多關於Golang核心編程知識的文章請看:Golang核心編程(0)-目錄頁


一、Golang簡介

在這裏插入圖片描述

1.1、什麼是Golang

Golang是Go語言的簡稱。Go語言作爲一門全新的靜態類型開發語言,與當前的開發語言相比具備衆多令人興奮不已的新特性。

在Google,一羣大牛創造了Go語言。早在2007年9月,Go語言還是這幫大牛的20%自由時間的實驗項目。到了2008年5月,Google發現了Go語言的巨大潛力,從而開始全力支持這個項目,讓這批人可以全身心投入Go語言的設計和開發工作中。

Go語言的第一個版本在2009年11月正式對外發布,並在此後的兩年內快速迭代,發展迅猛。第一個正式版本的Go語言於2012年3月28日正式發佈,讓Go語言迎來了第一個引人矚目的里程碑。

1.2、使用Golang完成的著名項目

  • Docker
  • Kubernetes
  • fabric

1.3、Golang的特性

  • 自動垃圾回收
  • 更豐富的內置類型
  • 函數多返回值
  • 錯誤處理
  • 匿名函數和閉包
  • 類型和接口
  • 併發編程
  • 反射
  • 語言交互性
  • 豐富開源庫支持

二、Golang的特性概述

2.1、自動垃圾回收

我們可以先看下不支持垃圾回收的語言的資源管理方式,以下爲一小段C語言代碼:

void foo()
{
char* p = new char[128];
... // 對p指向的內存塊進行賦值
func1( p ) // 使用內存指針
delete[] p;//清除指針
}

各種非預期的原因,比如由於開發者的疏忽導致最後的 delete 語句沒有被調用,都會引發經典而惱人的內存泄露問題。假如該函數被調用得非常頻繁,那麼我們觀察該進程執行時,會發現該進程所佔用的內存會一直瘋長,直至佔用所有系統內存並導致程序崩潰。

和Java、C#等完全面向對象的編程語言一樣,Golang也支持自動的垃圾回收,而不需要像C/C++一樣手動去delete或者調用free()去釋放內存,從語言內部機制的層面去幫助開發者解決令人苦惱的內存泄露問題。

2.2、豐富的內置類型

除了幾乎所有語言都支持的簡單內置類型(比如整型和浮點型等)外,Go語言也內置了一些比較新的語言中內置的高級類型,比如C#和Java中的數組和字符串。除此之外,Go語言還內置了一個對於其他靜態類型語言通常用庫方式支持的字典類型( map )。除此之外還有一個新增的數據類型:數組切片( Slice )。我們可以認爲數組切片是一種可動態增長的數組,和Java中的Vector類比較相似。

2.3、函數多返回值

目前的主流語言中除Python外基本都不支持函數的多返回值功能,Go語言革命性地在靜態開發語言陣營中率先提供了多返回值功能。這個特性讓開發者可以從原來用各種比較彆扭的方式返回多個值的痛苦中解脫出來,既不用再區分參數列表中哪幾個用於輸入,哪幾個用於輸出,也不用再只爲了返回多個值而專門定義一個數據結構,例如返回一個C中的結構體或者Java中的對象。

  • 在Java使用一個Person對象將數據封裝起來並返還:
static class Person{
        private int id;
        private String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
    
    public Person getPerson(int id,String name){
        Person person = new Person();
        person.setId(id);
        person.setName(name);
        return person;
    }
    
  • 在Go語言中使用多返回值
func getPerson(old_id int,old_name string)(new_id int, new_name string){
	new_id = old_id
	new_name = old_name
	return 
}

func main() {
	//接受返回id和name
	id1, name1 := getPerson(1, "arong")
	fmt.Printf("id is  %v and name is %v",id1,name1)
	//只接受返回的name,不接受id
	_, name2 := getPerson(1, "arong")
	fmt.Printf("name is %v",name2)

}

2.4、錯誤處理

Go語言引入了3個關鍵字用於標準的錯誤處理流程,這3個關鍵字分別爲deferpanicrecover 描述Go語言錯誤處理機制的獨特之處。整體上而言與C++和Java等語言中的異常捕獲機制相比,Go語言的錯誤處理機制可以大量減少代碼量,讓開發者也無需僅僅爲了程序安全性而添加大量一層套一層的 try-catch 語句。這對於代碼的閱讀者和維護者來說也是一件很好的事情,因爲可以避免在層層的代碼嵌套中定位業務代碼。

2.5、匿名函數和閉包

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

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

2.6、類型和接口

Go語言的類型定義非常接近於C語言中的結構(struct),甚至直接沿用了 struct 關鍵字。相比而言,Go語言並沒有直接沿襲C++和Java的傳統去設計一個超級複雜的類型系統,不支持繼承和重載,而只是支持了最基本的類型組合功能。

  • Java的接口定義與重寫
public interface Person {
    public void eat();
}

public static void main(String[] args) {
        JavaProgrammer javaProgrammer = new JavaProgrammer();
        javaProgrammer.eat();
    }



   public static class JavaProgrammer implements Person{
        @Override
        public void eat(){
            System.out.println("Java開發者需要這樣重寫一個接口的方法");
        }
    }
  • Golang接口實現和重寫
type Person interface {
	eat()
}

type GoProgramer struct {

}

func(golang *GoProgramer)eat(){
	fmt.Printf("Golang程序員會這樣實現接口並重寫")
}

func main() {
	//創建的GoProgrammer結構體聲明爲Person接口類型
	var golang Person = new(GoProgramer)
	golang.eat()
}

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

2.7、併發編程

Go語言引入了goroutine(協稱)概念,它使得併發編程變得非常簡單。

通過在函數調用前使用關鍵字 go ,我們即可讓該函數以goroutine方式執行goroutine是一種比線程更加輕盈、更省資源的協程。Go語言通過系統的線程來多路派遣這些函數的執行,使得每個用 go 關鍵字執行的函數可以運行成爲一個單位協程。當一個協程阻塞的時候,調度器就會自動把其他協程安排到另外的線程中去執行,從而實現了程序無等待並行化運行。而且調度的開銷非常小,一顆CPU調度的規模不下於每秒百萬次,這使得我們能夠創建大量的goroutine,從而可以很輕鬆地編寫高併發程序,達到我們想要的目的。

Go語言還使用channel(通道)這個概念來輕巧地實現了CSP模型。channel的使用方式比較接近Unix系統中的管道(pipe)概念,可以方便地進行跨goroutine的通信。

  • Golang使用協程
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)
}


2.8、反射

反射(reflection)是在Java語言出現後迅速流行起來的一種概念。通過反射,你可以獲取對象類型的詳細信息,並可動態操作對象

Go語言的反射實現了反射的大部分功能,但沒有像Java語言那樣內置類型工廠,故而無法做到像Java那樣通過類型字符串創建對象實例。在Java中,你可以讀取配置並根據類型名稱創建對應的類型,這是一種常見的編程手法,但在Go語言中這並不被推薦。

反射最常見的使用場景是做對象的序列化>
例如,Go語言標準庫的encoding/json、encoding/xml、encoding/gob、encoding/binary等包就大量依賴於反射功能來實現。

2.9、語言交互性

由於Go語言與C語言之間的天生聯繫,Go語言的設計者們自然不會忽略如何重用現有C模塊的這個問題,這個功能直接被命名爲Cgo。Cgo既是語言特性,同時也是一個工具的名稱。

在Go代碼中,可以按Cgo的特定語法混合編寫C語言代碼,然後Cgo工具可以將這些混合的C代碼提取並生成對於C功能的調用包裝代碼。開發者基本上可以完全忽略這個Go語言和C語言的邊界是如何跨越的。

2.10、豐富開源庫支持

Golang中有相當多的優秀開源第三方庫可以被調用,使用它們的方式很簡單,不如我們需要使用mysql數據庫,那麼可以調用第三方庫去操作:

import (
   _ "database/sql"
   _ "github.com/go-sql-driver/mysql"
)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章