用Go寫Android應用(3) - Go語言速成

用Go寫Android應用(3) - Go語言速成

Go快餐

下面我們將Go與C/C++/Java的一些比較不同的地方提煉一下,讓大家可以快速上手。然后在實踐中繼續學習。

Go是支持GC的

好的方面是,不用自己管理內存了。
不好的方面是,GC影響性能的話,要想辦法優化啊。

Go的變量定義類型在后面

例:
變量:

var i int = 10

常量

const ClassFile string = FilePath + "Test.class"

struct也是在后面

定義自定義類型的struct,也不像C語言一樣在前面,跟系統類型一樣,放到后面。前面有個type關鍵字。

例:

type ELFFile struct {
    eiClass      int
    littleEndian bool
    osABI        string
}

類型推斷和函數返回多個值

在函數里面使用時,可以使用定義和賦值合一的辦法,就是使用:=運算符。這時候不需要指定類型,因為可以通過后面的語句來推斷出類型。

例:

    buf, err := ioutil.ReadFile(elfFileName)

從上邊的例子我們還可以看到,Go語言支持函數返回多個參數。如果有的參數并不重要,可以使用特殊變量"_",不理它就是了。

未使用的變量和包將導致編譯不過

在Go中,如果引用了包不用,或者是定義了變量不使用,不是產生警告,而是直接導致編譯失敗!

大寫開頭是public,小寫開頭是private

Go語言沒有額外定義public和private限定符。如果一個變量或函數以大寫字母開頭,比如"Println",那么它就是public的,如果小寫開頭就是private的。

數組賦值會做拷貝

小心,將一個數組的值賦給另一個數組,會引發對數組的復制喲。

流程控制中可以不用小括號

if語句,for循環等控制語句中的小括號是可以省略不寫的。

例:if后面的判斷不用小括號

    if err != nil {
        fmt.Println("Error reading ELF:", err)
    }

switch默認帶break

Go語言的switch不需要寫break,break是默認的行為。相反,如果不需要break,需要加一個fallthrough語句取消掉默認的break.

Go語言有指針

默認是傳值復制,如果需要傳引用的,請用指針吧。

Go語言有goto

保持一個方向,盡量避免跳來跳去吧。
同時,Go語言也是支持break和continue的,而且二者都是可以帶標號跳轉的。goto可以留到最后再用。

Go語言只有for循環這一種

Go語言沒有提供while和do while循環,更沒有do until之類的。一切都是for循環。
死循環就是:

    for ;; {
        ...
    }

main函數和init函數

Go應用的入口點是main包的main函數。
每個package可以寫一個init函數,會被自動調用。

Go語言沒有this指針

需要明確指定對象,沒有隱藏的this指針潛規則可以用。

例,必須直接指定對象:

func (elfFile *ELFFile) ParseEIClass_v2(value byte) {
    if value == 1 {
        elfFile.eiClass = 32
        fmt.Println("It is 32-bit")
    } else if value == 2 {
        elfFile.eiClass = 64
        fmt.Println("It is 64-bit")
    } else {
        elfFile.eiClass = 0
        fmt.Println("unknown format, neither 32-bit nor 64-bit")
    }
}

Go的做法是把潛規則變成明文,在普通函數定義的前面,加上接收對象的聲明。

非侵入式的接口設計

鴨子原則,只要一個東西,走起來像鴨子,叫起來像鴨子,我們就可以認為它是一只鴨子。

Go語言的interface就是這么設計的。
我們來看一個例子,假設有三種虛擬機,都支持athrow方法:

package main

type Hotspot struct {
}

type Dalvik struct {
}

type AndroidRuntime struct {
}

func (vm Hotspot) athrow() {

}

func (dalvik Dalvik) athrow() {
    dalvik.throw()
}

func (dalvik Dalvik) throw() {

}

func (art AndroidRuntime) athrow() {
    art.pDeliverException()

}

func (art AndroidRuntime) pDeliverException() {

}

但是上面三種虛擬機的實現是不同的,Hotspot是直接支持這條指令,Dalvik是調用自己的throw指令,而ART是調用pDeliverException過程。
但不管怎樣,它們都聲稱支持athorw這個方法調用。
于是我們可以聲明一個interface叫SupportException,定義這一個方法。
從此以后,各種JVM的實現,都可以賦給一個SupportException類型的變量。
我們看使用的例子:

type SupportException interface {
    athrow()
}

func throwException() {
    hotspot := Hotspot{}
    dalvik := Dalvik{}
    art := AndroidRuntime{}

    var jvm1 SupportException
    var jvm2 SupportException
    var jvm3 SupportException

    jvm1 = hotspot
    jvm1.athrow()

    jvm2 = dalvik
    jvm2.athrow()

    jvm3 = art
    jvm3.athrow()
}

也是就說,定義類的時候,根本不用管接口的定義,只要實現就好了。最后再從各個類的實現中總結出接口來就好。

defer延遲執行

defer提供了函數級延時執行的機制度。就相當于函數級的finally,一定會被執行到。
比如打開文件成功后,就可以先defer一個關閉文件或者channel的操作。

func dex2oat(ch chan bool, dexFile string) {
    defer close(ch)
    ch <- dex2oatImpl(dexFile)
    fmt.Println("Dex2OAT finished!")
}

函數小的時候可能還得記得,大了之后defer的作用就顯現出來了,可以避免忘事兒。

引用類型

切片(slice)

Go語言的數組是只讀的。
數組可以用兩種方式來定義長度:

  • 一是直接指定長度
  • 二是讓Go來計算長度

直接給定長度,我們可以這么寫:

    magic := [4]byte{0x7f, 'E', 'L', 'F'}

如果我們懶得算有幾個,或者太長不好算,就可以給3個點,請編譯器幫我們算:

    magic := [...]byte{0x7f, 'E', 'L', 'F'}

為什么需要寫3個點,而不能直接給個空的方括號呢?
因為如果方括號為空,就不是數組了,而變成另外一種類型,叫做切片。

例:定義切片:

magic2 := buf[0 : 3]

buf是個數組,magic2是從第0個元素到第3個元素的切片。

如果是從頭開始,冒號前面可以省略,如果是切片到最后一個元素為止,剛冒號后面可以省略。如果做一個完整的切片,頭尾都可以省略。

下面的函數展示了如何從切片中消費一個字節,然后返回一個新的切片:

func ReadU1_v2(data []byte) (byte, []byte) {
    if data == nil {
        return 0, nil
    } else if len(data) > 1 {
        return data[0], data[1:]
    } else {
        return data[0], nil
    }
}

切片的屬性

切片其實是一個有三個數據組成的數據結構:

  • 指向數組的指針
  • 切片的長度,如上例,可以通過len()函數獲取
  • 切片的最大容量,可通過cap()函數來獲取

切片的函數

  • append:向切片追加一個或多個元素。相當于實現了動態數組的功能。如果切片所指向的原數組的容量不足,超出了切片的cap,則會為其分配一個新的數組。原數組不變。
  • copy,切片之間做數據復制。

map

Go語言內建對map的支持。

  • map也是一種引用類型,如果兩個map指向同一底層數據結構,則一個改變,另一個也改變。
  • map通過鍵-值對進行賦值
  • 鍵可以是任意的實現了==與!=操作的類型
  • map是無序的,不能遍歷

引用類型的內存分配

map,slice還有最后要講的channel可以通過make函數進行內存分配。

用戶自定義類型

前面講過了沒有this指針的事情,這里再總結一下。

定義用戶自定義類型

通過struct關鍵字來定義:

type ELFFile struct {
    elfFileName  string
    eiClass      int
    littleEndian bool
    osABI        string
}

使用自定義類型

直接當成普通類型使用就好了。最簡單的方法就是直接用:=賦給一個變量使用,也省得指定類型了。
可以通過鍵:值的方式來賦初值。
例:

elfFile := ELFFile{elfFileName: OatFile}

為自定義類型定義方法

前面講過了,給普通函數前面加一個對象接收者的聲明就可以了。

例:

func (elfFile *ELFFile) ParseEiData_v2(value byte) {
    switch value {
    case 1:
        elfFile.littleEndian = true
        fmt.Println("It is Little Endian")
        IsLittleEndian = true
    case 2:
        elfFile.littleEndian = false
        fmt.Println("It is Big Endian")
        IsLittleEndian = false
    default:
        fmt.Println("Unknow Endian, the value is:", value)
    }
}

Go的多作務機制

Go語言從語言層面天生就支持并發。通過go語句,每個函數都可以運行在一個Goroutine中,類似于一個線程。
多個Goroutine之間通過Channel來發送消息來實現通信。

我們舉個簡單的例子,實現一個Future模式吧。讓三個dex2oat作務并發:

func dex2oat(ch chan bool, dexFile string) {
    ch <- dex2oatImpl(dexFile)
    fmt.Println("Dex2OAT finished!")
}

func dex2oatImpl(dexFile string) bool {
    return true
}

上面的dex2oat函數傳入一個bool型的Channel,我們通過這個Channel向調用者返回結果。

調用者的代碼如下:

    channels := make([]chan bool, 3)
    for i := 0; i < len(channels); i++ {
        channels[i] = make(chan bool)
    }
    go dex2oat(channels[0], "Test1.dex")
    go dex2oat(channels[1], "Test2.dex")
    go dex2oat(channels[2], "Test3.dex")

    for _, ch := range channels {
        value := <-ch
        fmt.Println("The result is ", value)
    }

首先是new一個Channel數組,然后make Channel對象。
接著通過go關鍵字去開三個goroutine去分別執行dex2oat。
于是主任務就阻塞等待3個子任務分別返回,最后相當于把結果join在一起,再繼續往下執行。

小結

總結一下,Go的核心內容就上面這么多。
當然,其中的細節我們都沒有展開。希望給大家留個印象就是Go語言還是很容易上手的。


Go語言關鍵字

這張圖如果看不清的話,我們將其拆成兩張圖,再注掉分支流程那部分的局部圖:


Go語言關鍵字-除分支之外

分支流程部分的放大圖:
Go語言關鍵字-分支部分
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • 出處---Go編程語言 歡迎來到 Go 編程語言指南。本指南涵蓋了該語言的大部分重要特性 Go 語言的交互式簡介,...
    Tuberose閱讀 18,467評論 1 46
  • 官方網站:https://golang.org/標準庫文檔:https://golang.org/pkg/在線編碼...
    技術學習閱讀 2,329評論 2 39
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 天氣漸熱,又到了開空調躲濕熱的時候。不過,在開空調前,你必須知道這些小竅門,就能既省電又健康。 ●開空調時別忘拉上...
    再見貳哥哥閱讀 133評論 0 0
  • 這周的書單是《認知盈余》,作者是Clay Shirky,被稱為“互聯網革命最偉大的思考者”。因為馬化騰的推薦,很多...
    StevenFU閱讀 1,187評論 0 0