1. 程式人生 > >go 學習筆記之go是不是面嚮物件語言是否支援面對物件程式設計?

go 學習筆記之go是不是面嚮物件語言是否支援面對物件程式設計?

面向物件程式設計風格深受廣大開發者喜歡,尤其是以 C++, Java 為典型代表的程式語言大行其道,十分流行!

有意思的是這兩中語言幾乎毫無意外都來源於 C 語言,卻不同於 C 的面向過程程式設計,這種面向物件的程式設計風格給開發者帶來了極大的便利性,解放了勞動,鬆耦合,高內聚也成為設計的標準,從而讓我們能夠更加愉快地複製貼上,做程式碼的搬運工,很多第三方工具開箱即用,語義明確,職責清晰,這都是面向物件程式設計的好處!

Go 語言也是來源於 C 語言,不知道你是否也會好奇 Go 語言是否支援面向物件這種程式設計風格呢?

準確的說,Go 既支援面向物件程式設計又不是面嚮物件語言!

是也不是,難道像是薛定諤的貓一樣具有不確定性?

其實這個答案是官方的回答,並不是我個人憑空杜撰而來的,如需瞭解詳情可參考 Is Go an object-oriented language?

為什麼這麼說呢?

Go 支援封裝,卻不支援繼承和多型,所以嚴格按照面向物件規範來說, Go 語言不是面向物件的程式語言.

但是,Go 提供的介面是一種非常簡單上手且更加通用的方式,雖然和其他主流的程式語言表現形式上略有不同,甚至不能實現多型,但 Go 的介面不僅僅適用於結構體,也可以適用於任何資料型別,這無疑是很靈活的!

爭議性比較大的當屬繼承,由於沒有任何關鍵字支援繼承特性,因此是找不到繼承的痕跡.雖然的確存在著某些方式可以將型別嵌入到其他型別中以實現子類化,但那卻不是真正的繼承.

所以說,Go 既支援面向物件的程式設計風格又不完全是面向物件的程式語言.

如果換個角度看問題的話,正是由於沒有繼承特性使得Go 相對於面向物件程式語言更加輕量化,不妨想一想繼承的特性,子類和父類的關係,單繼承還是多繼承,訪問控制權限等問題吧!

如果按照面向物件的程式設計規範,實現封裝特性的那部分應該是類和物件,但這種概念與實現語言的關鍵字class 是密不可分的,然而 Go 並沒有 class 關鍵字而是 C 語言家族的 struct 關鍵字,所以叫做類或物件也不是十分貼切,所以下面的講解過程還是採用結構體吧!

如何定義結構體

stuct 關鍵字宣告結構體,屬性之間回車換行.

比如下面示例中定義了動態陣列結構體,接下來的示例中都會以動態陣列結構體作為演示物件.

type MyDynamicArray struct {
    ptr *[]int
    len int
    cap int
}

Go 語言中定義物件的多屬性時使用直接換行方式而不是分號來分隔?為什麼和其他主流的程式語言不呢?

對於習慣分號結尾的開發者可能一時並不習慣 Go 的這種語法,於是決定探索一下 Go 的程式設計規範!

如果手動新增分號的話,編輯器則會提示分號重複,所以猜想是可能是Go編譯器已經自動添加了分號,並將分號作為語句宣告的分隔符,手動新增分號後,Go 不管不顧還是添加了分號,於是就有了上述的報錯.

這樣做有什麼好處呢?

自己新增分號和編譯器無條件新增分號結果不都是一樣的嗎,更何況其他主流的程式語言都是手動新增分號的啊!

存在多個屬性時直接換行而不是新增分號作為分隔符,對於從未接觸過程式語言的小白來說,可能會省事兒,但是對於已有程式設計經驗的開發者來說,卻需要特別記住不能新增分號,這一點確實有些鬧騰!

如果多個屬性全部寫在一行時,沒有換行符我看你還怎麼區分,此時用逗號分隔還是用分號分隔呢?

首先空格肯定是不能分隔多個屬性的,因此嘗試分號或者逗號是否可以.

根據提示說需要分號或者新的換行符,而換行符是標準形式,所以接下來試一下分號能不能分隔?

編輯器此時沒有報錯或警告資訊,因此在一行上多個屬性之間應該用分號分隔,也就是說 Go 編譯器識別多個屬性仍然是同其他主流的程式語言一樣,使用分號分隔,而開發者卻不能用!

類似於上述的規則記憶很簡單,驗證也比較容易,難點在於理解為什麼?

Go 為什麼會這麼設計?或者說如何理解這種設計思路所代表的語義?

Go 作為一門新的程式語言,不僅體現在具體的語法差異上,更重要的是程式設計思想的特殊性.

正如面向物件中的介面概念一樣,設計者只需要定義抽象的行為並不用關心行為的具體實現.

如果我們也採用這種思路來理解不同的程式語言,那麼就能透過現象看本質了,否則真的很容易陷入語法細節上,進而可能忽略了背後的核心思想.

其實關於結構體的多屬性分隔符問題上,實際上不論採用什麼作為分隔符都行,哪怕就是一個逗號,句號都行,只要能讓編譯器識別到這是不同的屬性就行.

由於大多數主流的程式語言一般採用分號作為分隔符,開發者需要手動編寫分隔號以供編譯器識別,而 Go 語言卻不這麼認為,算了吧,直接換行,我一樣可以識別出來(儘管底層 Go 編譯器進行編譯時仍然是採用分號表示換行的)!

新增或者不新增分號,對於開發者而言,僅僅是一種分隔多個屬性的標誌而已,如果能不新增就能實現,那為什麼還要新增呢?

是什麼,為什麼和怎麼樣是三個基本問題,如果是簡單學習瞭解的話,學會是什麼和怎麼樣就已經足夠了,但是這樣一來學著學著難免會陷入各自為政的場面,也就是說各個程式語言之間沒有任何關係,每一種語言都是獨立存在的?!

世界語言千千萬,程式語言也不少,學了新語言卻沒有利用舊語言,那學習新語言時和純小白有何差異?

學到是學會了,可惜卻對舊語言沒什麼幫助並沒有加深舊語言的理解,只是單純的學習一種全新的語言罷了.

語言是演變創造出來的,不是空中樓閣,是建立在已有體系下逐漸發展演變而來,任何新語言都能或多或少找到舊語言的影子.

所以何不嘗試一下,弄清楚新語言設計的初衷和以及設計時所面臨的問題,然後再看該語言是如何解決問題的,解決的過程稱之為實現細節,我想這種方式應該是一種比較好的學習方式吧!

雖然無法身處語言設計時環境,也不一定明白語言設計時所面臨的挑戰,但先問嘗試著問一下為什麼,不這麼設計行不行諸如此類的問題,應該是一種不錯的開端.

所以接下來的文章都會採用語義性分析的角度,嘗試理解 Go語言背後的設計初衷,同時以大量的輔助性的測試驗證猜想,不再是簡單的知識羅列整理過程,當然必要的知識歸納還是很重要的,這一點自然也不會放棄.

現在動態陣列已經定義完畢,也就是作為設計者的工作暫時告一段落,那作為使用者,如何使用我們的動態陣列呢?

按照面向物件的說法,由類創造出物件的過程叫做例項化,然而我們已經知道 Go 並不是完全的面嚮物件語言,因此為了儘可能避免用面向物件的專業術語去稱呼 Go 的實現細節,我們暫時可以將其理解為結構體型別和結構體變數的關係,以後隨著學習的深入,可能會對這部分有更加深刻的認識.

func TestMyDynamicArray(t *testing.T){
    var arr MyDynamicArray

    // {<nil> 0 0}
    t.Log(arr)
}

上述寫法並沒有特殊強調過,完全是用前幾篇文章中已經介紹過的語法規則實現的,var arr MyDynamicArray 表示宣告型別為 MyDynamicArray 的變數 arr ,此時直接列印該變數的值,得到的是 {<nil> 0 0}.

後兩個值都是 0,自然很好理解,因為在講解 Go 語言中的變數時我們就已經介紹過,Go 的變數型別預設初始化都有相應的零值,int 型別的 len cap 屬性自然就是 0,而 ptr *[]int 是指向陣列的指標,所以是 nil.

等等,有點不對勁,這裡有個設計錯誤,明明叫做動態陣列結果內部卻是切片,這算怎麼回事?

先修正這個錯誤再說,由此可見,一時粗心影響多麼惡劣以至於語義都變了,容我先改正過來!

我們知道要使用陣列必須指定陣列的初始化長度,第一感覺是使用 cap 表示的容量來初始化 *[cap]int 陣列,然而並不可以,編輯器提示說必須使用整型數字.

雖然 capint 型別的變數,但內部陣列 [cap]int 並不能識別這種方式,可能是因為這兩個變數時一塊宣告的,cap[cap]int 都是變數,無法分配.

那如果指定初始化長度應該指定多少呢,如果是 0 的話,語義上正確但和實際使用情況不符合,因為這樣一來內部陣列根據就沒辦法插入了!

所以陣列的初始化長度不能為零,這樣解決了無法運算元組的問題,但語義上又不正確了,因此這種情況下需要維護兩個變數 lencap 的值來確保語義和邏輯正確,其中 len 表示真正的陣列個數,cap 表示內部陣列實際分配的長度,由於這兩個變數至關重要,不應該被呼叫者隨意修改,最多隻能檢視變數的值,所以必須提供一種機制保護變數的值.

接下來,我們嘗試用函式封裝的思路來完成這種需求,程式碼實現如下:

type MyDynamicArray struct {
    ptr *[10]int
    len int
    cap int
}

func TestMyDynamicArray(t *testing.T){
    var myDynamicArray MyDynamicArray

    t.Log(myDynamicArray)

    myDynamicArray.len = 0
    myDynamicArray.cap = 10
    var arr [10]int
    myDynamicArray.ptr = &arr

    t.Log(myDynamicArray)
    t.Log(*myDynamicArray.ptr)
}

var myDynamicArray MyDynamicArray 宣告結構體變數後並設定了結構體的基本屬性,然後操作了內部陣列,實現了陣列的訪問修改.

然而,我們犯了一個典型的錯誤,呼叫者不應該關注實現細節,這不是一個封裝該乾的事!

具體實現細節應該由設計者完成,將有關資料封裝成一個整體對外提供相應的介面,這樣呼叫者才能安全方便地呼叫.

第一步,先將與內部陣列相關的兩個變數進行封裝,對外僅提供訪問介面不提供設定介面,防止呼叫者隨意修改.

很顯然這部分應該是函式來實現,於是乎有了下面的改造過程.

很遺憾,編輯器直接報錯: 必須是型別名稱或是指向型別名稱的指標.

函式不可以放在結構體內,這一點倒是像極了 C 家族,但是 Java 這種衍生家族會覺得不可思議,不管怎麼說,這意味著結構體只能定義結構而不能定義行為!

那我們就把函式移動到結構體外部吧,可是我們定義的函式名叫做 len,而系統也有 len 函式,此時能否正常執行呢?讓我們拭目以待,眼見為實.

除了函式本身報錯外,函式內部的 len 也報錯了,是因為此時的函式和結構體尚未建立起任何聯絡,怎麼可能訪問到 len 屬性呢,不報錯才怪呢!

解決這個問題很簡單,直接將結構體的指標傳遞給 len 函式不就好了,這樣一來函式內部就可以訪問到結構體的屬性了.

從設計的角度上來講,確實解決了函式定義的問題,但是使用者呼叫函式時的使用方法看起來和麵向物件的寫法有些不一樣.

func TestMyDynamicArray(t *testing.T) {
    var myDynamicArray MyDynamicArray

    t.Log(myDynamicArray)

    myDynamicArray.len = 0
    myDynamicArray.cap = 10
    var arr [10]int
    myDynamicArray.ptr = &arr

    t.Log(myDynamicArray)
    t.Log(*myDynamicArray.ptr)

    (*myDynamicArray.ptr)[0] = 1
    t.Log(*myDynamicArray.ptr)

    t.Log(len(&myDynamicArray))
}

面向物件的方法中一般都是通過點操作符 . 訪問屬性或方法的,而我們實現的屬性訪問是 . 但方法卻是典型的函式呼叫形式?這看起來明顯不像是方法嘛!

為了讓普通函式看起來像是面向物件中的方法,Go 做了下面的改變,通過將當前結構體的變數宣告移動到函式名前面,從而實現類似於面嚮物件語言中的 thisself 的效果.

func len(myArr *MyDynamicArray) int {
    return myArr.len
}

此時方法名和引數返回值又報錯了,根據提示說函式名和欄位名不能相同?

真的又是一件神奇的事情,難不成 Go 無法區分函式和欄位?這就不得而知了.

那我們只好修改函式名,改成面向物件中喜聞樂見的方法命名規則,如下:

func (myArr *MyDynamicArray) GetLen() int {
    return myArr.len
}

簡單說一下 Go 的訪問性規則,大寫字母開頭的表示公開的 public 許可權,小寫字母開頭表示私有的 private 許可權,Go 只有這兩類許可權,都是針對包 package 而言,以後會再細說,現在先這麼理解就行了.

按照實驗得到的方法規則,繼續完善其他方法,補充 GetCapIsEmpty 等方法.

現在我們已經解決了私有變數的訪問性問題,對於初始化的邏輯還沒有處理,一般來說,初始化邏輯可以放到建構函式中執行,那 Go 是否支援建構函式呢,以及怎麼才能觸發建構函式?

嘗試按照其他主流的程式語言中建構函式的寫法來編寫 Go 的建構函式 , 沒想到 Go 編譯器直接報錯了,提示重新定義了 MyDynamicArray 型別,以至於影響了其餘部分!

如果修改方法名稱的話,理論上可以解決報錯問題,但是這並不是建構函式的樣子了,難不成 Go 不支援建構函式嗎?

此時,面向物件形式的建構函式轉變成自定義函式實現的建構函式,更加準確的說法,這是一種類似於工廠模式實現的建構函式方式.

func NewMyDynamicArray() *MyDynamicArray {
    var myDynamicArray MyDynamicArray
    return &myDynamicArray
}

難道 Go 語言真的不支援建構函式?

至於是否支援建構函式或者說應該如何支援建構函式,真相不得而知,隨著學習的深入,相信以後一定會有明確的答案,這裡簡單表達一下個人看法.

首先我們知道 Go 的結構體中只能定義資料,而結構體的方法肯定是在結構體外定義的,為了符合面向物件的使用習慣,也就是通過例項物件的點操作符來訪問方法,Go 的方法只能是函式的變體,即普通函式中關於指向結構體變數的宣告部分轉移到函式名前面來實現方法,這種由函式轉變成為方法的模式也符合 Go 一貫的命名規則: 向來是按照人的思維習慣命名,先有輸入再有輸出等邏輯.

結構體的方法從語法和語義的兩個維度上支援了面向物件規範,那麼建構函式想要實現面向物件應該如何做呢?

建構函式正如其名應該是函式,而不是方法,方法由指向自身的引數,這一點建構函式不應該有,否則都有例項物件了還構造毛線啊?

既然建構函式是普通函式,那麼按照面向物件的命名習慣,方法名應該是結構體名,然而真的操作了,編輯器直接就報錯了,所以這不符合面向物件的命名習慣!

如此一來,建構函式的名稱可能並不是結構體型別的名稱,有可能是其他特殊的名稱,最好這個名稱能夠見名知義且具備例項化物件時自動呼叫的能力.

當然這個名稱依賴於 Go 的設計者如何命名,這裡靠猜測是很難猜對的,否則我就是設計者了啊!

除此之外,還有另外一種可能,那就是 Go 並沒有建構函式,想要實現建構函式的邏輯只能另闢蹊徑.

這麼說有沒有什麼靠譜的依據呢?

我想大概是有的,建構函式雖然提供了自動初始化能力,但是如果真的在建構函式中加入複雜的初始化邏輯,無疑會增大以後出錯的排查難度並給使用者帶來一定的閱讀障礙,所以說一定程度上,建構函式很有可能被濫用了!

那是否就意味著不需要構造函數了呢?

也不能這麼說,建構函式除了基本的變數初始化以及簡單的邏輯外,在實際程式設計中還是有一定用途的,為了避免濫用而直接禁用,多少有點飲鴆止渴的感覺吧?

因此,個人的看法是應該可以保留建構函式這種初始化邏輯,也可以換一種思路去實現,或者乾脆直接放棄建構函式轉而由編譯器自動實現建構函式,正如編譯器可以自動新增多欄位之間的分號那樣.

如果開發者真的有建構函式的需求,通過工廠模式或者單例模式等手段總是可以定製結構體初始化的邏輯,所以放棄也未嘗不可!

最後,以上這些純屬個人猜想,目前並不知道 Go 是否存在建構函式,有了解的人,還請明確告訴我答案,個人傾向於不存在建構函式,最多隻提供類似於建構函式初始化的邏輯!

現在,我們已經封裝了結構體的資料,定義了結構體的方法以及實現了結構體的工廠函式.那麼接下來讓我們繼續完善動態陣列,實現陣列的基本操作.

func NewMyDynamicArray() *MyDynamicArray {
    var myDynamicArray MyDynamicArray

    myDynamicArray.len = 0
    myDynamicArray.cap = 10
    var arr [10]int
    myDynamicArray.ptr = &arr

    return &myDynamicArray
}

func TestMyDynamicArray(t *testing.T) {
    myDynamicArray := NewMyDynamicArray()

    t.Log(myDynamicArray)
}

首先將測試用例中的邏輯提取到工廠函式中,預設無參的工廠函式初始化的內部陣列長度為 10 ,後續再考慮呼叫者指定以及實現動態陣列等功能,暫時先實現最基本的功能.

初始化的內部陣列均是零值,因此需要首先提供給外界能夠新增的介面,實現如下:


func (myArr *MyDynamicArray) Add(index, value int) {
    if myArr.len == myArr.cap {
        return
    }

    if index < 0 || index > myArr.len {
        return
    }

    for i := myArr.len - 1; i >= index; i-- {
        (*myArr.ptr)[i+1] = (*myArr.ptr)[i]
    }

    (*myArr.ptr)[index] = value
    myArr.len++
}

由於預設的初始化工廠函式暫時是固定長度的陣列,因此新增元素其實是操作固定長度的陣列,不過這並不妨礙後續實現動態陣列部分.

為了操作方便,再提供插入頭部和插入尾部兩種介面,可以基於動態陣列實現比較高階的資料結構.

func (myArr *MyDynamicArray) AddLast(value int) {
    myArr.Add(myArr.len, value)
}

func (myArr *MyDynamicArray) AddFirst(value int) {
    myArr.Add(0, value)
}

為了方便測試動態陣列的演算法是否正確,因此提供列印方法檢視陣列結構.

由此可見,列印方法顯示的資料結構和真實的結構體資料是一樣的,接下來我們就比較有信心繼續封裝動態陣列了!

func (myArr *MyDynamicArray) Set(index, value int) {
    if index < 0 || index >= myArr.len {
        return
    }

    (*myArr.ptr)[index] = value
}

func (myArr *MyDynamicArray) Get(index int) int {
    if index < 0 || index >= myArr.len {
        return -1
    }

    return (*myArr.ptr)[index]
}

這兩個介面更加簡單,更新陣列指定索引的元素以及根據索引查詢陣列的值.

接下來讓我們開始測試一下動態陣列的全部介面吧!

動態陣列暫時告一段落,不知道你是否好奇為什麼以動態陣列為例講解面向物件?

其實主要是為了驗證上一篇文章中的猜想,也就是切片和陣列的到底是什麼關係?

我覺得切片的底層是陣列,只不過語法層面提供了支援以至於看不出陣列的影子,仙子阿既然學習了面向物件,那麼就用面向物件的方式實現下切片的功能,雖然無法模擬語法級別的實現,但是功能特性完全是可以模仿的啊!

下面還是梳理總結一下本文的只要知識點吧,也就是封裝的實現.

如何封裝結構體

之所以稱之為結構體是因為 Go 的關鍵字是 struct 而不是 class,也是面向物件程式設計風格中唯一支援的特性,繼承和多型都不支援,到時候另開文章細說.

結構體是對資料進行封裝所使用的手段,結構體內只能定義資料而不能定義方法,這些資料有時候被稱為欄位,有時候叫做屬性或者乾脆叫做變數,至於什麼叫法不是特別重要,如何命名和所處的環境語義有關.

type MyDynamicArray struct {
    ptr *[10]int
    len int
    cap int
}

這種結構體內就有三個變數,變數之間直接換行進行分隔而不是分號並換行的形式,剛開始覺得有些怪,不過編輯器一般都很智慧,假如習慣性添加了分號,會提示你進行刪除,所以語法細節上不必在意.

結構體內不支援編寫函式,僅支援資料結構,這樣就意味著資料和行為是分開的,兩者之間的關聯是比較弱的.

func (myArr *MyDynamicArray) IsEmpty() bool {
    return myArr.len == 0
}

這種方式的函式和普通函式略有不同,將包含結構體變數的引數提前到函式名前面,語義上也比較明確,表示的是結構體的函式,為了和普通函式有所區別,這種函式稱之為方法.

其實,單純地就實現功能上看,方法和函式並沒有什麼不同,無外乎呼叫者的使用方式不一樣罷了!

func IsEmpty(myArr *MyDynamicArray) bool {
    return myArr.len == 0
}

之所以是這種設計方式,一方面體現了函式的重要性,畢竟是 Go 語言中的一等公民嘛!

另一方面是為了實現面向物件的語法習慣,不論屬性還是方法,統統用點 . 操作符進行呼叫.

官方的文件中將這種結構體引數稱之為接收者,因為資料和行為是弱關聯的,這裡的接收者充當的就是關聯資料的作用,接收者顧名思義就是接受資料的人,那傳送資料的人又是誰呢?

不言而喻,傳送者應該是呼叫者傳遞的結構體例項物件,結構體變數將資料結構傳送給接收者方法,從而資料和行為聯絡在一起了.

func TestMyDynamicArray(t *testing.T) {
    myDynamicArray := NewMyDynamicArray()

    fmt.Println(myDynamicArray.IsEmpty())
}

好了,以上就是面向物件初體驗中的全部部分,僅僅是這麼一小部分卻耗費我整整三天的時間了,想說換種思維不簡單,寫好一篇文章也不容易啊!

下篇文章中將繼續介紹面向物件的封裝特性,講解更多幹貨,如果覺得本文對你有所幫助,歡迎轉發評論,感覺你的閱讀!

相關推薦

go 學習筆記go物件語言是否支援面對物件程式設計?

面向物件程式設計風格深受廣大開發者喜歡,尤其是以 C++, Java 為典型代表的程式語言大行其道,十分流行! 有意思的是這兩中語言幾乎毫無意外都來源於 C 語言,卻不同於 C 的面向過程程式設計,這種面向物件的程式設計風格給開發者帶來了極大的便利性,解放了勞動,鬆耦合,高內聚也成為設計的標準,從而讓我們

go 學習筆記萬萬沒想到寵物店竟然催生出面向介面程式設計?

到底是要貓還是要狗 在上篇文章中,我們編撰了一則簡短的小故事用於講解了什麼是面向物件的繼承特性以及 Go 語言是如何實現這種繼承語

go 學習筆記有意思的變數和安分的常量

首先希望學習 Go 語言的愛好者至少擁有其他語言的程式設計經驗,如果是完全零基礎的小白使用者,本教程可能並不適合閱讀或嘗試閱讀看看,系列筆記的目標是站在其他語言的角度學習新的語言,理解 Go 語言,進而寫出真正的 Go 程式. 程式語言中一般都有變數和常量的概念,對於學習新語言也是一樣,變數指的是不同程式語言

go 學習筆記陣列還是切片都沒什麼一樣

上篇文章中詳細介紹了 Go 的基礎語言,指出了 Go 和其他主流的程式語言的差異性,比較側重於語法細節,相信只要稍加記憶就能輕鬆從已有的程式語言切換到 Go 語言的程式設計習慣中,儘管這種切換可能並不是特別順暢,但多加練習尤其是多多試錯,總是可以慢慢感受 Go 語言之美! 在學習 Go 的內建容器前,同樣的,

Go學習筆記高階資料型別

高階資料型別,僅僅是做個概念認識,等到其他相關知識的學習時,再著重分析。 1 function 將 function 作為資料型別的語言有很多,函數語言程式設計的核心理念。 function 是“第一等公民”,function 與其他資料型別一樣,處於平等地位,可以賦值給

go 學習筆記初識 go 語言

Go 是一種開源程式語言,可以輕鬆構建簡單,可靠,高效的軟體. 摘錄自 github: https://github.com/golang/go,其中官網(國外): https://golang.org 和官網(國內): https://golang.google.cn/ Go 是 Google 公司

go 學習筆記環境搭建

千里之行始於足下,開始 Go 語言學習之旅前,首先要搭建好本地開發環境,然後就可以放心大膽瞎折騰了. Go 的環境安裝和其他語言安

go 學習筆記工作空間

搭建好 Go 的基本環境後,現在可以正式開始 Go 語言的學習之旅,初學時建議在預設的 GOPATH 工作空間規範編寫程式碼,基本目錄結構大概是這個樣子. . |-- bin | `-- hello.exe |-- pkg | `-- windows_amd64 | `-- github.

go 學習筆記走進Goland編輯器

工欲善其事必先利其器,命令列工具雖然能夠在一定程度上滿足基本操作的需求,但實際工作中總不能一直使用命令列工具進行編碼操作吧? 學習 Go 語言同樣如此,為此需要尋找一個強大的 IDE 整合環境幫助我們快速開發,據我所知,市面上比較流行的可能有三個選擇: LiteIDE X : LiteIDE 是一款簡單,開

go 學習筆記值得特別關注的基礎語法有哪些

在上篇文章中,我們動手親自編寫了第一個 Go 語言版本的 Hello World,並且認識了 Go 語言中有意思的變數和不安分的常量. 相信通過上篇文章的斐波那契數列,你已經初步掌握了 Go 語言的變數和常量與其他主要的程式語言的異同,為了接下來更好的學習和掌握 Go 的基礎語法,下面先簡單回顧一下變數和常量

go 學習筆記詳細說一說封裝是怎麼回事

關注公眾號[雪之夢技術驛站]檢視上篇文章 猜猜看go是不是面嚮物件語言?能不能面向物件程式設計? 雖然在上篇文章中,我們通過嘗試性學習探索了 Go 語言中關於面向物件的相關概念,更確切的說是關於封裝的基本概念以及相關實現. 但那還遠遠不夠,不能滿足於一條路,而是應該儘可能地多走幾條路,只有這樣才能為以後可

go 學習筆記是否支援以及如何實現繼承

熟悉面向物件的小夥伴們可能會知道封裝,繼承和多型是最主要的特性,為什麼前輩們會如此看重這三種特性,真的那麼重要嗎? 什麼是封裝 什麼是封裝,封裝有什麼好處以及怎麼實現封裝? 相信大多數小夥伴們都有自己的理解,簡而言之,言而簡之,封裝是遮蔽內部實現細節,僅僅對外暴露出有價值介面. 正如平時工作中使用的電

go 學習筆記無心插柳柳成蔭的介面和無為而治的空介面

如果你還了解程式設計概念中的介面概念,那麼我建議你最好還是先閱讀上一篇文章.詳情請點選 go 學習筆記之萬萬沒想到寵物店竟然催生出面向介面程式設計? ,否則的話,請自動忽略上文,繼續探索 Go 語言的介面有什麼不同之處. 如無法自動跳轉到公眾號「雪之夢技術驛站」文章,可以點選我的頭像,動動你的小手翻翻歷史文

go 學習筆記僅僅需要一個示例就能講清楚什麼閉包

本篇文章是 Go 語言學習筆記之函數語言程式設計系列文章的第二篇,上一篇介紹了函式基礎,這一篇文章重點介紹函式的重要應用之一: 閉包 空談誤國,實幹興邦,以具體程式碼示例為基礎講解什麼是閉包以及為什麼需要閉包等問題,下面我們沿用上篇文章的示例程式碼開始本文的學習吧! 斐波那契數列是形如 1 1 2 3 5

go 學習筆記10 分鐘簡要理解 go 語言閉包技術

閉包是主流程式語言中的一種通用技術,常常和函數語言程式設計進行強強聯合,本文主要是介紹 Go 語言中什麼是閉包以及怎麼理解閉包. 如果讀者對於 Go 語言的閉包還不是特別清楚的話,可以參考上一篇文章 go 學習筆記之僅僅需要一個示例就能講清楚什麼閉包. 或者也可以直接無視,因為接下來會回顧一下前情概要,現在你

go 學習筆記解讀什麼是defer延遲函式

Go 語言中有個 defer 關鍵字,常用於實現延遲函式來保證關鍵程式碼的最終執行,常言道: "未雨綢繆方可有備無患". 延遲函式就是這麼一種機制,無論程式是正常返回還是異常報錯,只要存在延遲函式都能保證這部分關鍵邏輯最終執行,所以用來做些資源清理等操作再合適不過了. 出入成雙有始有終

glib學習筆記二(續)——GLib核心應用支援:The Main Event Loop

原文地址 描述 The main event loop manages all the available sources of events for GLib and GTK+ applications. These events can come from any n

Go語言學習筆記簡單的幾個排序

1.實現一個插入排序 核心思路是抽到的牌與前面的牌進行比較,如果抽到的牌小,那麼就互相交換位置.(正序排列) 1: func insert_sort(a []int) []int { 2: for i:=1;i<len(a);i++ { 3: for j:=i;j>0

【SpringBoot學習筆記】turbine界顯示監控信息——unable to connect to command metric stream

code color boa cti In star 技術 alt pan [[錯誤現象:]] 界面沒有監控信息,只有一行字:unable to connect to command metric stream ,如下圖: [[可能原因]]: 1)s

Go學習筆記01

源碼 沒事 安裝 下載 命名 cos 代碼包 bin 終端 前言 Go(Golang)是Google開發的一種強靜態類型、編譯型、並發型,並具有垃圾回收功能的編程語言,所以使用Go編寫的程序相比nodejs之類的弱類型語言,可以提前在編譯階段發現錯誤,而且由於沒有類似JVM