Go從入門到精通

本文轉自慕課網
本教程希望以詼諧幽默,誰都能理解的方式去學習Go語言。大膽的嘗試,希望編程是一個有血有肉的事情,而不是枯燥的代碼~

1.0 搭建環境
由於我們 Go官方網站 在我大天朝被和諧了,所以我們只能去 Go語言中文網 來下載了。Go的安裝很簡單,不像Java還要配置一大堆的東西,選擇自己系統的對應版本,下載安裝,像安裝QQ一樣無腦。我們就不再介紹了~如果真的有特別小白的朋友,也可以去百度看看圖文安裝教程~

1.1. 第一個Go程序
每個編程語言的學習,都是從一個"Hello, World."程序開始的,這個例子首次出現於1978年出版的C語言聖經《The C Programming Language》。關於"Hello, World."還有一個很美好的故事,那就是所有的程序員期待着計算機有一天能擁有真正的智能,然後對創造他的人們"發自內心"的說一句,Hello, World.

首先我們新建一個hello.go文件,輸入下面的內容並保存:

hello.go

package main

import "fmt"

func main(){
fmt.Println("Hello, World.")
}
打開命令行,找到hello.go所在的目錄,輸入命令:

提示:本系列教程,以 $ 符號來表示命令行提示符,看到以$符號開頭的命令,說明是在命令行環境下執行的,並非命令本身的符號。

$ go run hello.go
毫無疑問,我們將會看到命令行輸出:

Hello, World.
恭喜你!已經完成了第一個Go語言程序,無論你是否理解,我都要祝賀你,開啓了Go語言的大門,接下來讓我們來做一些更酷的事情吧。

大家加油~

1.2. 編譯可執行文件
Go和Python最大的區別在於,Go是一門編譯型語言而Python是一門解釋型語言。

編譯型編程語言
簡單來說,編譯型編程語言並非簡單的寫好代碼然後就運行。他還需要經過一個編譯的步驟,把寫好的代碼通過語言提供的編譯器,編譯成操作系統方便閱讀而人類幾乎無法閱讀的代碼。這種語言的好處就是,運行速度快。如:Go, Java, C/C++,都是編譯型編程語言。

解釋型編程語言
解釋型編程語言就是,我們寫好的代碼,無需經過任何處理,就可以直接運行在操作系統之上。但是計算機並不能理解我們所寫的代碼,這個時候就需要一個叫做解釋器的東西。解釋器就相當於一個翻譯官一樣,把我們寫的代碼一行一行的翻譯給計算機,計算機從解釋器那裏得到翻譯後的代碼,才能正常執行我們的程序。由於代碼是一句一句解釋執行的,這中間有很多等待的過程,而且還取決於解釋器的執行速度,所以解釋型編程語言的特點就是運行速度慢。如:Python就是解釋型編程語言。

還記得我們上一章所寫的hello.go嗎,來回顧一下:

hello.go

package main

import "fmt"

func main(){
fmt.Println("Hello, World.")
}
既然我們知道Go是一門編譯型語言,我們能不能把hello.go進行編譯,然後再來運行呢?

答案當然是可以的:

$ go build hello.go
如果你使用的是Windows系統,你會發現在hello.go文件所在的目錄下,出現了一個hello.exe的可執行文件,直接運行它或者輸入下面的命令:

$ hello.exe
如果你使用的是Mac或者Linux,則會發現目錄下多了一個hello的文件,輸入下面的命令來運行:

$ ./hello
最終你都能看到,命令行輸出了:

Hello, World.
Go提供了很多命令給我們,其中最常用的兩個命令就是run和build:

go run xx.go 運行指定的go文件

go build xx.go 編譯指定的go文件

1.3. 打開封印之門的鑰匙
有些同學看到我們這一節的名稱,可能會想,車神這是寫教程寫的大腦抽筋了吧,並不是那樣的。我希望編程可以用一種更加簡單,更加方便新手接受和更加有趣的形式展現出來,而不是死板的函數,變量。

我們想要進入一棟房子,需要找到這個房子的大門才能進去,那麼計算機想要進入我們做的程序,是不是也需要一個大門呢?答案是肯定的,那麼我們Go語言的大門在哪裏呢?回顧第一章寫的hello.go:

hello.go

package main

import "fmt"

func main(){
fmt.Println("Hello, World.")
}
很顯然,main()就是我們這個程序的大門。package說明我們這個文件屬於哪個包,可以簡單的把包理解爲一個文件夾。Go語言規定,程序的入口文件,必須輸入main包,而且這個文件必須包含一個main方法。

那麼究竟什麼是方法呢?

方法
方法從字面我們可以理解爲行爲,比如我們人吃飯,睡覺,走路,談戀愛,啪啪啪。這都是方法,是人的行爲。那麼我們的程序也會有一些行爲,比如運行的行爲,輸出一句話的行爲,在屏幕上畫一個圓圈的行爲,這些行爲我們形象的稱它們爲方法或者叫函數。無論是方法還是函數,指的都是我們程序的某一項行爲,那麼這個行爲或許是現實世界存在的,或許是現實世界不存在的。當你開始編程的時候,你就是造物主,你可以給你的程序創建任何行爲。由於Go是Google創造的,所以我們想在Go的世界當一回造物主,就要依靠Google給我們提供的各種超能力。接下來我們就來看看,第一個超能力。

func 關鍵字
Go語言爲我們提供了一個創建方法的關鍵字,這個關鍵字可以理解爲我們的超能力,有了它我們就能給程序創建各種各樣的方法,各種我們喜歡的方法。它的一個簡單的用法就是:

func 你喜歡的名字(){
具體的行爲
}
從今天開始,我們就來體驗當創世神的快感吧。

新建一個Go文件,我把他叫做 yadang.go 。我們像上帝一樣創造了一個叫做亞當的人:

package main

import "fmt"

func main(){
fmt.Println("你好,我是亞當。")
}
讓我們來運行一下:

go run yadang.go
我們的亞當可以說話了,大家注意到我們的亞當,只有一個main方法,這個方法是Go語言規定的,任何一個程序想要啓動,必須包含這個方法。大家可以思考一下,我們的Go在給我們的亞當賦予生命的時候,肯定需要找到main才能啓動,main就相當於打開封印的鑰匙一樣,這個寫法是固定的,也就是說,我們無論創造什麼樣的生命或者是絢爛多姿的世界,都要有一個大門,這個大門就是main方法,main方法只能存在於main這個package中,package就像一個世界,main就是這個世界的中心大陸,而我們的一切生命都起源於這裏。

1.4. 奔跑吧,亞當
上節課我們一起創造了亞當,但是我們的亞當還很低級,他只會說一句話,連走路都不會,這節課我們就讓亞當,奔跑起來。

來看一下我們的亞當,現在是什麼樣子:

yadang.go

package main

import "fmt"

func main(){
fmt.Println("你好,我是亞當。")
}
大家可能注意到 import "fmt",這其實是Go語言提供的包含一系列方法的一個工具。大家可以把它理解爲插件,裝上這個插件,我們的亞當才能使用這個插件裏提供的各種功能,其中一個很重要的功能就是:

fmt.Println()
這個方法可以在屏幕上輸出一句話,我們的亞當就是依靠這個方法來跟大家問好的。

就像我們人類一樣,當你想說話的時候,總是你的大腦想起一句話,然後把這句話送給我們的嘴巴,由我們的嘴巴把這句話說出來。我們並不知道大腦的神經元之間是如何傳遞的,我們並不是人體專家也不是神經學的專家,我們不懂人到底是怎麼說話的,但是我們只需要知道,想說話就通過嘴巴就能說,我們的身體會自動處理一切需要的東西。

同樣的,在我們編程的世界裏也是一樣的,我們不懂怎麼樣才能讓操作系統在屏幕上顯示一句話,我們不瞭解計算機的硬件之間是怎麼通信的,但是Go爲我們做好了一切,它把一切複雜的東西做成一個個方便使用的方法,使得我們不需要成爲計算機專家,也能在電腦屏幕上面輸出一句話。我們不用管背後有多麼複雜的邏輯,只需要使用這簡簡單單的一個方法,就能實現。

亞當會說話了
事實上,我們的亞當目前只有一個主方法,他還沒有任何身爲人的方法,那今天我們就給亞當創造一些方法,讓他看上去更像一個人:

yadang.go

package main

import "fmt"

func main(){
speak()
run()
}

func speak(){
fmt.Println("哇哈哈,我會說話了~")
}

func run(){
fmt.Println("我跑,我跑,我跑跑跑~")
}
可以看到,我們的亞當多了兩個方法,一個叫做speak,一個叫做run。當然你可以把他們改成任何你喜歡的名字,比如叫 shuohua, aaa, hahaha 都可以。但是我們人類說話的方法就叫說話,非常的便於理解,如果我們創造了一個亞當,我們把他說話的方法叫一個毫無意義的名字,那其他人看到亞當的時候,就不知道這是一個什麼方法了,比如下面的樣子:

yadang.go

package main

import "fmt"

func main(){
aaa()
bbb()
}

func aaa(){
fmt.Println("哇哈哈,我會說話了~")
}

func bbb(){
fmt.Println("我跑,我跑,我跑跑跑~")
}
我們來運行一下:

go run yadang.go
同樣我們能夠看到屏幕上輸出了:

哇哈哈,我會說話了~
我跑,我跑,我跑跑跑~
但是我們的亞當看上去是不是變得難以理解了,aaa 和 bbb 是什麼方法?讓人捉摸不透,所以說,大家在給我們的程序增加一些方法的時候,最好起一些有意義的名字,能夠表達這個方法含義的名字,無論是用英語還是漢語拼音都是可以的,只要其他人和自己在閱讀代碼的時候能夠輕易的認出這個方法是做什麼的就可以了。

至此,大家已經掌握瞭如何讓我們的亞當奔跑,但是我們的亞當還缺少一項重要的東西,那就是長相,下一節我們將一起來把我們的亞當變成一個帥氣的猛男子吧~

1.5. 帥氣的猛男子
今天我們將繼續在Go語言大陸造人,上節課我們讓亞當擁有了說話和奔跑的方法,但是我們的亞當還沒有具體的身高,體重。那麼這些東西也能通過方法來實現嗎?答案是否定的,一個人的身高和體重,並不屬於一個人的行爲,而是屬於這個人的屬性。那麼我們的程序也擁有一些屬性,接下來我們就一起看一下如何給程序定義屬性。

來看一下我們的亞當:

yadang.go

package main

import "fmt"

func main(){
speak()
run()
}

func speak(){
fmt.Println("哇哈哈,我會說話了~")
}

func run(){
fmt.Println("我跑,我跑,我跑跑跑~")
}
我們都知道,自然界中的屬性有很多種,比如一個人的身高,可能是180cm,也可能是179.5cm,一個人的體重,可能是60kg,也可能是75.8kg。一個人的胳膊數量,可能是0,也可能是1或者2。一個人的外貌,可能是美麗,可能是一般,可能是醜陋。一個人的是不是有錢,那就只有兩種可能,是或者否。那麼這些數據都擁有不同的類型,可能是整數類型,也可能是小數類型,還可能是文字,負數,和是否類型。

那麼在我們的Go語言世界裏,也存在許許多多的數據類型。

布爾類型
bool

字符類型
string

整數類型
int int8 int16 int32 int64

無符號整數類型
uint uint8 uint16 uint32 uint64 uintptr

uint8 的別名

byte

int32 的別名(代表一個Unicode碼)

rune

浮點型
float32 float64

實數和虛數
complex64 complex128

我們可以看到,在Go語言的世界裏存在這麼多的數據類型,但是常用的也就是我們上面說的,小數類型(浮點型),整數類型,是否類型(布爾型),文字類型(字符類型)。

那麼比較特殊的是跟數字相關的,因爲我們都知道,數字是有範圍的,比如大於1000,大於10000。在我們現實生活中,如果一個人的體重是100kg,那地球就要使用100kg對應的那麼大的空間來讓這個人生活。如果一個人的體重是10000kg,那麼地球就需要耗費10000kg對應的空間來給這個人。那麼,如果一個體重只有60kg的人,他卻被地球分配了一個能容納10000kg體重的空間,是不是就浪費了我們地球有限的土地資源呢?

所以說,在Go語言的世界裏,爲了節約我們寶貴的土地資源(內存),我們需要在給程序定義屬性的時候,選擇合適的大小來分配,這樣纔不會浪費我們計算機的內存。

有符號整數類型
int8 有符號的8位整數,範圍 -128 到127

int16 有符號的16位整數,範圍 -32768 到 32767

int32 有符號的32位整數,範圍 -2147483648 到 2147483647

int64 有符號的64位整數,範圍 -9223372036854775808 到 9223372036854775807

int 有符號的32位或64位整數,範圍根據Go程序運行的操作系統來決定,如果是32位的操作系統就相當於int32,如果是64位的操作系統就相當於int64。那麼今天我們大部分的計算機都是64位的,所以如果直接使用int,而你的電腦剛好是64位的操作系統,那他就等於int64。

這裏所說的位,並不是有幾位數字,而是我們計算機的一個容量單位,8位等於1個字節,1024個字節等於1KB,1024KB就等於1兆。那麼16位就是兩個字節。

那麼假如我們要表示一個人的年齡,很顯然使用 int16 就足夠了,如果使用 int32 就會造成大量的空間浪費,因爲 int32 最大可以表示21億多的數字,很顯然人類的壽命是不可能有這麼大的。反之,如果我們製作一個網絡遊戲,需要來表示人物的***力,我們都知道有些遊戲爲了讓玩家體會到快感,把***力設置的非常高。什麼開局只有一條狗,一刀99億。那麼很顯然,如果是這種變態遊戲的***力,我們用 int32 來表示就不合適了,因爲 int32 無法容納我們的一刀99億,這時候我們就只能使用最大的 int64 來存儲。至於他最高能表示多大的數值,大家有興趣的可以數一下,反正如果換成錢的話,車神大概可以把整個地球買下來。

我們可以看到int類型無論多少位,都是包含負號的,這種數據類型我們成爲有符號整數類型。那麼既然存在有符號整數類型,是不是還有一個無符號整數類型呢?恭喜你答對了,uint就是無符號整數類型。

無符號整數類型
uint8 無符號8位整數,範圍 0 到 255

uint16 無符號16位整數,範圍 0 到 65535

uint32 無符號32位整數,範圍 0 到 4294967295

uint64 無符號64位整數,範圍 0 到 18446744073709551615

uint 無符號的32位或64位整數,和 int 一樣,根據操作系統的位數來決定。

uintptr 無符號整數,用於存放一個指針。關於指針,我們將在後面爲大家詳細講解,這裏僅作爲了解即可,不需要理解。

聰明的同學可能已經發現了,所謂的無符號整數類型,就是把原來存儲負數的那部分空間,給取消了,此消彼長,這樣正數部分就能加上原來負數的那部分空間用來存儲更大的數字了。

所以 uint8 的存儲範圍實際上就是 int8 的 -128 去掉負號然後加127,結果就是255。有興趣的可以驗證一下其他的幾個是不是都等於,有符號的整型去掉負號相加就是無符號整型的取值範圍。

具體的取值範圍大家不需要記憶,只需要記住,100以內用 int8,3萬以內用 int16,21億以內用 int32,大於這個範圍用 int64。如果不存在負數的情況,可以使用無符號整型。無符號整型只需要在有符號整型的範圍基礎上乘以2,就是我們選擇的合適範圍了。如200以內用 uint8,42億以內用 uint32。

身高一米五?
讓我們來看看如何給亞當一個身高的屬性

yadang.go

package main

import "fmt"

var height uint8 = 150

func main(){
hello()
}

func hello(){
fmt.Printf("我是亞當,身高 %v 米", height)
}

func speak(){
fmt.Println("哇哈哈,我會說話了~")
}

func run(){
fmt.Println("我跑,我跑,我跑跑跑~")
}
運行我們的亞當,就會發現輸出了:

我是亞當,身高 150 米
首先我們來看如何使用我們的變量的,var 變量名 類型 = 值。等號和後面的值是可選的,如果不指定值,Go會給這個變量一個默認的值。

另一個新的方法就是在我們 fmt 這個包裏面的 Printf。它可以讓我們在字符串裏面寫一些佔位符,什麼叫佔位符呢?大家可以認爲佔位符就是替你排隊的,比如你買東西有事,你想讓一個人替你站在那裏排隊,等你回來了以後讓那個人出去,你站在他的位置上,就叫佔位符。 %v 就是其中一個佔位符。Printf 會拿着後面的 height 的值去替換 %v,如果有多個佔位符,會按照從前往後的順序,以此替換,比如我們下面看到的。

yadang.go

package main

import (
"fmt"
"reflect"
)

var height uint8 = 150
var weight = 20

func main(){
hello()
}

func hello(){
fmt.Printf("我是亞當,身高 %v 米,體重 %v 噸。", height, weight)
fmt.Println(reflect.TypeOf(height), reflect.TypeOf(weight))
}

func speak(){
fmt.Println("哇哈哈,我會說話了~")
}

func run(){
fmt.Println("我跑,我跑,我跑跑跑~")
}
運行以後發現結果是:

我是亞當,身高 150 米,體重 20 噸。uint8 int
我們發現正常輸出了 我是亞當,身高 150 米,體重 20 噸。 下面的 reflect.TypeOf(height) 是Go給我們提供的另一個方便我們創造一切的工具,這個工具包叫做 reflect 裏面有一個方法 TypeOf 他可以接收一個變量,然後告訴我們這個變量的類型是什麼。

我們發現上面的體重 var weight = 20 我們並沒有指定類型,這是因爲Go會自己根據等號後面的類型去判斷應該給我們的變量一個什麼默認值,這裏我們可以看到,如果是整數的話,Go默認是把它聲明成一個 int 類型的變量了。我們都知道 int 是有符號的類型,根據操作系統位數的不同,最低可以表示21億的正負數,最高就是 int64 可以表示很大的正負數值。而我們的亞當,很顯然不會有這麼重的,所以我們爲了節約珍貴的資源,就像節約我們的土地一樣。就要給一些明知道一定不會超過這個範圍的變量一個合適的類型。

比如我們要做一個日曆系統,其中表示年份的我們就可以使用 uint16 類型,最高可以表示 65535,至少在我們有生之年是看不到那一年了,或許只有我們的亞當才能活這麼多年吧~~當然,如果你們公司需要做一個未來日曆,要求可以查看幾百萬年以後,那就要根據需求使用其他的數據類型了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章