Golang基础知识入门详解
Go語言入門
Go語言入門教程
很多人將 Go 語言 稱為 21 世紀(jì)的 C 語言,因?yàn)?Go 不僅擁有 C 語言的簡潔和性能,而且還很好的提供了 21 世紀(jì)互聯(lián)網(wǎng)環(huán)境下服務(wù)端開發(fā)的各種實(shí)用特性,讓開發(fā)者在語言級(jí)別就可以方便的得到自己想要的東西。
在 Go 語言的版本迭代過程中,語言特性基本上沒有太大的變化,基本上維持在 Go1.1 的基準(zhǔn)上,并且官方承諾,新版本對(duì)老版本下開發(fā)的代碼完全兼容。事實(shí)上,Go 開發(fā)團(tuán)隊(duì)在新增語言特性上顯得非常謹(jǐn)慎,而在穩(wěn)定性、編譯速度、執(zhí)行效率以及 GC 性能等方面進(jìn)行了持續(xù)不斷的優(yōu)化。
Go語言優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
Go很容易學(xué)習(xí)
如果你了解任何一種編程語言,那么通常在學(xué)習(xí)幾個(gè)小時(shí)就能夠掌握 Go 的大部分語法,并在幾天后寫出你的第一個(gè)真正的程序。閱讀并理解實(shí)效 Go 編程,瀏覽一下包文檔,玩一玩 Gorilla 或者 Go Kit 這樣的網(wǎng)絡(luò)工具包,然后你將成為一個(gè)相當(dāng)不錯(cuò)的 Go 開發(fā)者。
這是因?yàn)?Go 的首要目標(biāo)是簡單。當(dāng)我開始學(xué)習(xí) Go,它讓我想起我第一次 發(fā)現(xiàn) Java:一個(gè)簡單的語言和一個(gè)豐富但不臃腫的標(biāo)準(zhǔn)庫。對(duì)比當(dāng)前 Java 沉重的環(huán)境,學(xué)習(xí) Go 是一個(gè)耳目一新的體驗(yàn)。因?yàn)?Go 的簡易性,Go 程序可讀性非常高,雖然錯(cuò)誤處理添加了一些麻煩。
簡單并發(fā)編程
Goroutines 可能是 Go 的最佳特性了。它們是輕量級(jí)的計(jì)算線程,與操作系統(tǒng)線程截然不同。當(dāng) Go 程序執(zhí)行看似阻塞 I/O 的操作時(shí),實(shí)際上 Go 運(yùn)行時(shí)掛起了 goroutine ,當(dāng)一個(gè)事件指示某個(gè)結(jié)果可用時(shí)恢復(fù)它。與此同時(shí),其他的 goroutines 已被安排執(zhí)行。因此在同步編程模型下,我們具有了異步編程的可伸縮性優(yōu)勢。
Goroutines 也是輕量級(jí)的:它們的堆棧隨需求增長和收縮,這意味著有 100 個(gè)甚至 1000 個(gè) goroutines 都不是問題。
channel 是 goroutines 的通信方式:它們提供了一個(gè)便利的編程模型,可以在 goroutines 之間發(fā)送和接收數(shù)據(jù),而不必依賴脆弱的低級(jí)別同步基本體。channels 有它們自己的一套用法模式。但是,channels 必須仔細(xì)考慮,因?yàn)殄e(cuò)誤大小的 channels (默認(rèn)情況下沒有緩沖) 會(huì)導(dǎo)致死鎖。下面我們還將看到,使用通道并不能阻止競爭情況,因?yàn)樗狈Σ豢勺冃浴?/p>
豐富的標(biāo)準(zhǔn)庫
Go 的標(biāo)準(zhǔn)庫非常豐富,特別是對(duì)于所有與網(wǎng)絡(luò)協(xié)議或 API 開發(fā)相關(guān)的: http 客戶端和服務(wù)器,加密,檔案格式,壓縮,發(fā)送電子郵件等等。甚至還有一個(gè) html 解析器和相當(dāng)強(qiáng)大的模板引擎去生成 text & html,它會(huì)自動(dòng)過濾 XSS 攻擊(例如在 Hugo 中的使用)。
各種 APIs 一般都簡單易懂。它們有時(shí)看起來過于簡單:這個(gè)某種程度上是因?yàn)?goroutine 編程模型意味著我們只需要關(guān)心 “看似同步” 的操作。這也是因?yàn)橐恍┩ㄓ玫暮瘮?shù)也可以替換許多專門的函數(shù),就像 我最近發(fā)現(xiàn)的關(guān)于時(shí)間計(jì)算的問題。
Go性能優(yōu)越
Go 編譯為本地可執(zhí)行文件。許多 Go 的用戶來自 Python、Ruby 或 Node.js。對(duì)他們來說,這是一種令人興奮的體驗(yàn),因?yàn)樗麄兛吹椒?wù)器可以處理的并發(fā)請(qǐng)求數(shù)量大幅增加。當(dāng)您使用非并發(fā)(Node.js)或全局解釋器鎖定的解釋型語言時(shí),這實(shí)際上是相當(dāng)正常的。結(jié)合語言的簡易性,這解釋了 Go 令人興奮的原因。
然而與 Java 相比,在原始性能基準(zhǔn)測試中,情況并不是那么清晰。Go 打敗 Java 地方是內(nèi)存使用和垃圾回收。Go 的垃圾回收器的設(shè)計(jì)目的是優(yōu)先考慮延遲,并避免停機(jī),這在服務(wù)器中尤其重要。這可能會(huì)帶來更高的 CPU 成本,但是在水平可伸縮的體系結(jié)構(gòu)中,這很容易通過添加更多的機(jī)器來解決。請(qǐng)記住,Go 是由谷歌設(shè)計(jì)的,他們從不會(huì)在資源上面短缺。
與 Java 相比,Go 的垃圾回收器(GC)需要做的更少:切片是一個(gè)連續(xù)的數(shù)組結(jié)構(gòu),而不是像 Java 那樣的指針數(shù)組。類似地,Go maps 也使用小數(shù)組作為 buckets,以實(shí)現(xiàn)相同的目的。這意味著垃圾回收器的工作量減少,并且 CPU 緩存本地化也更好。
標(biāo)準(zhǔn)化的測試框架
Go 在其標(biāo)準(zhǔn)庫中提供了一個(gè)很好的測試框架。它支持并行測試、基準(zhǔn)測試,并包含許多實(shí)用程序,可以輕松測試網(wǎng)絡(luò)客戶端和服務(wù)器。
缺點(diǎn)
Go忽略了現(xiàn)代語言設(shè)計(jì)的進(jìn)步
在少既是多中,Rob Pike 解釋說 Go 是為了在谷歌取代 C 和 C++,它的前身是 Newsqueak ,這是他在 80 年代寫的一種語言。Go 也有很多關(guān)于 Plan9 的參考,Plan9 是一個(gè)分布式操作系統(tǒng),在貝爾實(shí)驗(yàn)室的 80 年代開發(fā)的。
甚至有一個(gè)直接從 Plan9 獲得靈感的 Go 匯編。為什么不使用 LLVM 來提供目標(biāo)范圍廣泛且開箱即用的體系結(jié)構(gòu)?我此處可能也遺漏了某些東西,但是為什么需要匯編?如果你需要編寫匯編以充分利用 CPU ,那么不應(yīng)該直接使用目標(biāo) CPU 匯編語言嗎?
Go 的創(chuàng)造者應(yīng)該得到尊重,但是看起來 Go 的設(shè)計(jì)發(fā)生在平行宇宙(或者他們的 Plan9 lab?)中發(fā)生的,這些編譯器和編程語言的設(shè)計(jì)在 90 年代和 2000 年中從未發(fā)生過。也可能 Go 是由一個(gè)會(huì)寫編譯器的系統(tǒng)程序員設(shè)計(jì)的。
函數(shù)式編程嗎?不要提它。泛型?你不需要,看看他們用 C++ 編寫的爛攤子!盡管 slice、map 和 channel 都是泛型類型,我們將在下面看到。
Go 的目標(biāo)是替換 C 和 C++,很明顯它的創(chuàng)建者也沒有關(guān)注其他地方。但他們沒有達(dá)到目標(biāo),因?yàn)樵诠雀璧?C 和 C++ 開發(fā)人員沒有采用它。我的猜測是主要原因是垃圾回收器。低級(jí)別 C 開發(fā)人員強(qiáng)烈拒絕托管內(nèi)存,因?yàn)樗麄儫o法控制什么時(shí)間發(fā)生什么情況。他們喜歡這種控制,即使它帶來了額外的復(fù)雜性,并且打開了內(nèi)存泄漏和緩沖溢出的大門。有趣的是,Rust 在沒有 GC 的情況下采用了完全不同的自動(dòng)內(nèi)存管理方法。
Go 反而在操作工具的領(lǐng)域吸引了 Python 和 Ruby 等腳本語言的用戶。他們?cè)?Go 中找到了一種方法,可以提高性能,減少 內(nèi)存/cpu/磁盤 占用。還有更多的靜態(tài)類型,這對(duì)他們來說是全新的。Go 的殺手級(jí)應(yīng)用是 Docker ,它在 devops 世界中引起了廣泛的應(yīng)用。Kubernetes 的崛起加強(qiáng)了這一趨勢。
接口是結(jié)構(gòu)類型
Go 接口 就像 Java 接口或 Scala 和 Rust 特性(traits):它們定義了后來由類型實(shí)現(xiàn)的行為(我不稱之為“類”)。與 Java 接口和 Scala 和 Rust 特性不同,類型不需要顯式地指定接口實(shí)現(xiàn):它只需要實(shí)現(xiàn)接口中定義的所有函數(shù)。所以 Go 的接口實(shí)際上是結(jié)構(gòu)化的。
我們可能認(rèn)為,這是為了允許其他包中的接口實(shí)現(xiàn),而不是它們適用的類型,比如 Scala 或 Kotlin 中的類擴(kuò)展,或 Rust 特性,但事實(shí)并非如此:所有與類型相關(guān)的方法都必須在類型的包中定義。
沒有枚舉
Go 沒有枚舉,在我看來,這是一個(gè)錯(cuò)失的機(jī)會(huì)。iota 可以快速生成自動(dòng)遞增的值,但它看起來更像一個(gè)技巧 而不是一個(gè)特性。實(shí)際上,由于在一系列的 iota 生成的常量中插入一行會(huì)改變下列值的值,這是很危險(xiǎn)的。由于生成的值是在整個(gè)代碼中使用的值,因此這會(huì)導(dǎo)致有趣的(而不是!)意外。
這也意味著沒有辦法讓編譯器徹底檢查 switch 語句,也無法描述類型中允許的值。
沒有泛型
很難想象一種沒有泛型的現(xiàn)代靜態(tài)類型化語言,但這就是你在 Go 中看到的:它沒有泛型…或者更精確地說,幾乎沒有泛型,我們會(huì)看到它比沒有泛型更糟糕。
內(nèi)置的 slice、map、array 和 channel 都是泛型。聲明一個(gè) map[string]MyStruct 清楚地顯示了具有兩個(gè)參數(shù)的泛型類型的使用。這很好,因?yàn)樗试S類型安全編程捕獲各種錯(cuò)誤。
然而,沒有用戶可定義的泛型數(shù)據(jù)結(jié)構(gòu)。這意味著您不能定義可重用的抽象,它可以以類型安全的方式使用任何類型。您必須使用非類型 interface{},并將值轉(zhuǎn)換為適當(dāng)?shù)念愋汀H魏五e(cuò)誤只會(huì)在運(yùn)行時(shí)被抓住,會(huì)導(dǎo)致 panic。對(duì)于 Java 開發(fā)人員來說,這就像回到 回退 Java 5 個(gè)版本到 2004 年。
Go語言基礎(chǔ)
基本數(shù)據(jù)類型
變量和常量
普通賦值:
// var 變量名稱 變量類型 = 值 var num int = 1平行賦值:
var num1,num2 int = 1, 2多行賦值:
var (num1 int = 1num2 int = 2 )整數(shù)類型的命名和寬度
Go 的 整數(shù)類型 一共有 10 個(gè)其中計(jì)算架構(gòu)相關(guān)的整數(shù)類型有兩個(gè),即有符號(hào)的整數(shù)類型 int 和無符號(hào)的整數(shù)類型 uint。在不同計(jì)算架構(gòu)的計(jì)算機(jī)上,它們體現(xiàn)的寬度(存儲(chǔ)某個(gè)類型的值所需要的空間)是不一樣的。空間的單位可以是 bit 也可以是字節(jié) byte。
除了這兩個(gè)計(jì)算架構(gòu)相關(guān)的整數(shù)類型之外,還有 8 個(gè)可以顯式表達(dá)自身寬度的整數(shù)類型:
整數(shù)類型值的表示法
如果以 8 進(jìn)制為變量 num 賦值:
num = 039 // 用"0"作為前綴以表明這是8進(jìn)制表示法如果以 16 進(jìn)制為變量 num 賦值:
num = 0x39浮點(diǎn)類型
浮點(diǎn)數(shù) 類型有兩個(gè):float32/float64。浮點(diǎn)數(shù)類型的值一般由整數(shù)部分、小數(shù)點(diǎn) “.” 和小數(shù)部分組成。另外一種表示方法是在其中加入指數(shù)部分。指數(shù)部分由 “E” 或 “e” 以及帶正負(fù)號(hào)的 10 進(jìn)制整數(shù)表示。
復(fù)數(shù)類型
復(fù)數(shù)類型有兩個(gè):complex64 和 complex128。實(shí)際上,complex64 類型的值會(huì)由兩個(gè) float32 類型的值分別表示復(fù)數(shù)的實(shí)數(shù)部分和虛數(shù)部分。而 complex128 類型的值會(huì)由兩個(gè) float64 類型的值表示復(fù)數(shù)的實(shí)數(shù)部分和虛數(shù)部分。
byte與rune
byte 與 rune 都屬于別名類型。byte 是 uint8 的別名類型,而 rune 是 int32 的別名類型。一個(gè) rune 的類型值即可表示一個(gè) Unicode 字符。一個(gè) Unicode 代碼點(diǎn)通常由 “U+” 和一個(gè)以十六進(jìn)制表示法表示的整數(shù)表示。
字符串類型
字符串 的表示法有兩種,即:原生表示法和解釋型表示法。原生表示法,需用用反引號(hào) “`” 把字符序列包起來,如果用解釋型表示法,則需要用雙引號(hào) “”" 包裹字符序列。
var str1 string = “str” var str1 string = `str`二者的區(qū)別是,前者表示的是所見即所得的(除了回車符)。后者所表示的值中轉(zhuǎn)義符會(huì)起作用。字符串值是不可變的,如果我們創(chuàng)建了一個(gè)此類型的值,就不可能再對(duì)它本身做任何修改。
數(shù)組類型
一個(gè)數(shù)組是可以容納若干相同類型的元素的容器。數(shù)組的長度是固定的。如下聲明一個(gè)數(shù)組類型:
type MyNumbers [3]int類型聲明語句由關(guān)鍵字 type、類型名稱和類型字面量組成,上面這條類型聲明語句實(shí)際上是為數(shù)組類型 [3]int 聲明了一個(gè)別名類型。這使得我們可以把 MyNumbers 當(dāng)作數(shù)組類型 [3]int 來使用。
我們表示這樣一個(gè)數(shù)組類型的值的時(shí)候。應(yīng)該把該類型的類型字面量寫在最左邊,然后用花括號(hào)包裹該值包含的若干元素,各元素之間以(英文半角)逗號(hào)分割,即:
[3]int{1,2,3}現(xiàn)在我們把這個(gè)數(shù)組字面量賦給一個(gè)名為 numbers 的變量:
var numbers = [3]int{1,2,3}這是一條變量聲明語句,它在聲明變量的同時(shí)為該變量賦值,另一種方式是在其中的類型字面量中省略代表其長度的數(shù)組,例:
var numbers = [...]int{1,2,3}可以用如下方式訪問該變量中的任何一個(gè)元素。例:
numbers[0] numbers[1] numbers[2]如果要修改數(shù)組值中的某一個(gè)元素值,可以:
numbers[1] = 4可以用如下方式獲取數(shù)組長度:
var length = len(numbers)如果一個(gè)數(shù)組沒有賦值,則它的默認(rèn)值為
[length]type{0,0,0…}切片類型
切片(slice)與數(shù)組一樣也是可以若干相同類型元素的容器。與數(shù)組不同的是切片類型的長度不確定。每個(gè)切片值都會(huì)將數(shù)組作為其底層數(shù)據(jù)結(jié)構(gòu)。表示切片類型的字面量如:
[]int或者是:
[]string切片類型的聲明可以這樣:
type MySlice []int對(duì)切片值的表示也與數(shù)組值相似
[]int{1,2,3}操作數(shù)組值的方法同樣適用于切片值。還有一種操作數(shù)組的方式叫做“切片”,實(shí)施切片操作的方式就是切片表達(dá)式。例:
var number3 = [5]int{1,2,3,4,5} var slice1 = numbers3[1:4]上例中切片表達(dá)式 numbers3[1:4] 的結(jié)果為 []int{2,3,4} 很明顯被切下的部分不包含元素上界索引指向的元素。實(shí)際上 slice1 這個(gè)切片值的底層數(shù)組正是 number3 的值。我們也可以在切片值上實(shí)施切片操作:
var slice2 = slice1[1:3]除了長度切片值以及數(shù)組值還有另外一個(gè)屬性–容量。數(shù)組的容量總是等于其長度,而切片值的容量往往與其長度不同。如下圖:
如圖所示,一個(gè)切片值的容量即為它的第一個(gè)元素值在其底層數(shù)組中的索引值與該數(shù)組長度的差值的絕對(duì)值。可以使用cap()內(nèi)建函數(shù)獲取數(shù)組、切片、通道類型的值的容量:
var capacity2 int = cap(slice2)切片類型屬于引用類型,它的零值即為 nil,即空值。如果我們只聲明了一個(gè)切片類型而不為它賦值,則它的默認(rèn)值 nil。切片的更多操作方法有些時(shí)候我們可以在方括號(hào)中放入第三個(gè)正整數(shù)。
numbers3[1:4:4]第三個(gè)正整數(shù)為容量上界索引,它意義在于可以把作為結(jié)果的切片值的容量設(shè)置的更小。它可以限制我們通過這個(gè)切片值對(duì)其底層數(shù)組中的更多元素的訪問。上節(jié)中 numbers3 和 slice 的賦值語句如下:
var numbers3 = [5]int{1,2,3,4,5} var slice1 = numbers3[1:4]這時(shí),變量 slice1 的值是 []int{2,3,4}。但是我們可以通過如下操作將其長度延展與其容量相同:
slice1 = slice1[:cap(slice1)]通過此操作,變量 slice1 的值變?yōu)榱?[]int{2,3,4,5},且其長度和容量均為 4。現(xiàn)在 number3 的值中的索引值在 (1,5) 范圍內(nèi)的元素都被體現(xiàn)在了 slice1 的值中。這是以 number3 的值是 slice1 的值的底層數(shù)組為前提的。
這意味著我們可以輕而易舉地通過切片訪問其底層數(shù)組中對(duì)應(yīng)索引值更大的更多元素。如果我們編寫的函數(shù)返回了這樣一個(gè)切片值,那么得到它的程序很可能會(huì)通過這種技巧訪問到本不應(yīng)該暴露給它的元素。如果我們?cè)谇衅屑尤肓说谌齻€(gè)索引(即容量上限索引),如:
var slice1 = numbers3[1:4:4]那么在此之后,我們將無法通過 slice1 訪問到 number3 的值中的第五個(gè)元素。雖然切片值在上述方面受到了其容量的限制。但是我們可以通過另外一種手段對(duì)其進(jìn)行不受限制的擴(kuò)展。這需要用到內(nèi)建函數(shù) append。append 會(huì)對(duì)切片值進(jìn)行擴(kuò)展并返回一個(gè)新的切片值,使用方法如下:
slice1 = append(slice1, 6, 7)通過上述操作,slice1 的值變?yōu)榱?[]int{2,3,4,6,7}。一旦擴(kuò)展操作超出了被操作的切片值的容量,那么該切片的底層數(shù)組就會(huì)被替換。最后一種操作切片的方式是 “復(fù)制”。該操作的實(shí)施方法是調(diào)用 copy 函數(shù)。
該函數(shù)接收兩個(gè)類型相同的切片值作為參數(shù),并把第二個(gè)參數(shù)值中的元素復(fù)制到第一個(gè)參數(shù)值中的相應(yīng)位置(索引值相同)上。這里有兩點(diǎn)需要注意:這種復(fù)制遵循最小復(fù)制原則,即:被復(fù)制的元素的個(gè)數(shù)總是等于長度較短的那個(gè)參值的長度。
與 append 函數(shù)不同,copy 函數(shù)會(huì)直接對(duì)其第一個(gè)參數(shù)值進(jìn)行修改。
通過上述復(fù)制操作,slice4 會(huì)變成 []int{2,3,4,6,7,0,0}。
字典類型
Go 語言的字典(Map)類型是一個(gè)哈希表的實(shí)現(xiàn)。字典類型的字面量如下:
map[K]T其中,“K” 為鍵的類型,而 “T” 則代表元素(值)的類型。如果我們描述一個(gè)鍵類型為 int,值類型為 string 的字典類型的話:
map[int]string字典的鍵類型必須是可比較的,否則會(huì)引起錯(cuò)誤,即鍵不能是切片、字典、函數(shù)類型。
字典值的字面量表示法實(shí)際上與數(shù)組的切片的字面量表示法很相似。最左邊仍然是類型字面量,右邊緊挨著由花括號(hào)包裹且有英文逗號(hào)分隔的鍵值對(duì)。每個(gè)鍵值對(duì)的鍵和值之間由冒號(hào)分隔。以字典類型 map[int]string 為例。他的值的字面量可以是這樣的:
map[int]string{1:"a",2:"b"m,3:"c"}我們可以把這個(gè)值賦給一個(gè)變量:
mm := map[int]string{1:"a",2:"b",3:"c"}可用索引表達(dá)式取出字典中的值:
b := mm[2]可以用索引表達(dá)式賦值:
mm[2] = b + "2"這樣 mm 中鍵為 2 的值變?yōu)榱?“b2”。可以用如下方式向字典中添加一個(gè)鍵值對(duì):
mm[4] = ""對(duì)于字典值來說,如果指定鍵沒有對(duì)應(yīng)的值則默認(rèn)為該類型的空值。所以 mm[5] 會(huì)返回一個(gè) “”。但是這樣的話我們就不知道 mm[5] 到底是 “” 還是 mm[5] 沒有這個(gè)值。所以 go 提供了另外一種寫法:
e, ok := mm[5]針對(duì)字典的索引表達(dá)式可以有兩個(gè)求職結(jié)果,第二個(gè)求職結(jié)果是 bool 類型的。它用于表明字典值中是否存在指定的鍵值對(duì)。 從字典中刪除鍵值對(duì)的方法非常簡單,僅僅是調(diào)用內(nèi)建函數(shù) delete:
delete(mm, 4)無論 mm 中是否存在以 4 為鍵的鍵值對(duì),delete 都刪除。 字典類型屬于引用類型,它的零值即為 nil。
通道類型
通道(Channel)是 Go 語言中一種非常獨(dú)特的數(shù)據(jù)結(jié)構(gòu)。它可用于在不同 Goroutine 之間傳遞類型化的數(shù)據(jù)。并且是并發(fā)安全的。相比之下,之前幾種數(shù)據(jù)類型都不是并發(fā)安全的。
Goroutine 可以被看作是承載可被并發(fā)執(zhí)行的代碼塊的載體。它們由 Go 語言的運(yùn)行時(shí)系統(tǒng)調(diào)度,并依托操作系統(tǒng)線程(又稱內(nèi)核線程)來并發(fā)地執(zhí)行其中的代碼塊。
通道類型的表示方法很簡單,僅由兩部分組成:
chan T在這個(gè)類型字面量中,左邊是代表通道類型的關(guān)鍵字 chan,而右邊則是一個(gè)可變的部分,即代表該通道類型允許傳遞的數(shù)據(jù)的類型(或稱通道的元素類型)。
與其他的數(shù)據(jù)類型不同,我們無法表示一個(gè)通道類型的值,因此,我們無法用字面量來為通道類型的變量賦值。只能通過調(diào)用內(nèi)建函數(shù) make 來達(dá)到目的。make 參數(shù)可接受兩個(gè)參數(shù),第一個(gè)參數(shù)是代表了將被初始化的值的類型的字面量(例: chan int),而第二個(gè)參數(shù)則是值的長度,例如,若我們想要初始化一個(gè)長度為 5 且元素類型為int的通道值,則需要這樣寫:
make(chan int, 5)make 函數(shù)也可以被用來初始化切片類型或字典類型的值。暫存在通道值中的數(shù)據(jù)是先進(jìn)先出。下面,我們聲明一個(gè)通道類型的變量,并為其賦值:
ch1 := make(chan string, 5)這樣一來,我們就可以使用接受操作符 <- 向通道值發(fā)送數(shù)據(jù)了。當(dāng)然,也可以使用它從通道值接收數(shù)據(jù),例如,如果我們要向通道 ch1 發(fā)送字符串 “value1”,那么應(yīng)該這樣做:
ch1 <- “value1"如果我們從 ch1 那里接收字符串,則要這樣:
<- ch1我們可以把接受到字符串賦給一個(gè)變量,如:
value := <- ch1與針對(duì)字典值的索引表達(dá)式一樣,針對(duì)通道值的接受操作也可以有第二個(gè)結(jié)果值:
value, ok := <- ch1這里的 ok 的值是 bool 類型的。它代表了通道值的狀態(tài),true 代表通道值有效,而 false 則代表通道值已無效(或稱已關(guān)閉),更深層次的原因是,如果在接受操作進(jìn)行之前或過程中通道值被關(guān)閉了,則接收操作會(huì)立即結(jié)束并返回一個(gè)該通道值的元素類型的零值。
可以通過函數(shù) close 來關(guān)閉通道:
close(ch1)對(duì)通道值的重復(fù)關(guān)閉會(huì)引發(fā)運(yùn)行時(shí)異常,會(huì)使程序崩潰。在通道值有效的前提下,針對(duì)它的發(fā)送操作會(huì)在通道值已滿(其中緩存的數(shù)據(jù)的個(gè)數(shù)已等于它的長度)時(shí)被阻塞。而向一個(gè)已被關(guān)閉的通道值發(fā)送數(shù)據(jù)會(huì)引發(fā)運(yùn)行時(shí)異常。針對(duì)有效通道值的接收操作會(huì)在它已經(jīng)為空時(shí)被阻塞。通道類型屬于引用類型,它的零值為 nil。
流程控制
條件語句
對(duì)應(yīng)的關(guān)鍵字為 if、 else 和 else if:
if a := 1; a >= 1 {fmt.Println("OK") }選擇語句
對(duì)應(yīng)的關(guān)鍵字為 switch、 case 和 select:
switch i {case 0:fmt.Printf("0")case 1:fmt.Printf("1")case 2:fallthroughcase 3:fmt.Printf("3")case 4, 5, 6:fmt.Printf("4, 5, 6")default:fmt.Printf("Default") }循環(huán)語句
對(duì)應(yīng)的關(guān)鍵字為 for 和 range:
sum := 0 for i := 0; i < 10; i++ {sum += i }跳轉(zhuǎn)語句
func myfunc() {i := 0HERE:fmt.Println(i)i++if i < 10 {goto HERE} }函數(shù)
概述
首先函數(shù)的格式是固定的,func+函數(shù)名+ 參數(shù) + 返回值(可選) + 函數(shù)體。例 :
func main() {fmt.Println("Hello go") }在 golang 中有兩個(gè)特殊的 函數(shù),main 函數(shù)和 init 函數(shù),main 函數(shù)不用介紹在所有語言中都一樣,它作為一個(gè)程序的入口,只能有一個(gè)。init 函數(shù)在每個(gè) package 是可選的,可有可無,甚至可以有多個(gè)(但是強(qiáng)烈建議一個(gè) package 中一個(gè) init 函數(shù)),init 函數(shù)在你導(dǎo)入該 package 時(shí)程序會(huì)自動(dòng)調(diào)用 init 函數(shù),所以 init 函數(shù)不用我們手動(dòng)調(diào)用,另外它只會(huì)被調(diào)用一次,因?yàn)楫?dāng)一個(gè) package 被多次引用時(shí),它只會(huì)被導(dǎo)入一次。
參數(shù)傳遞
-
普通變量
使用普通變量作為函數(shù)參數(shù)的時(shí)候,在傳遞參數(shù)時(shí)只是對(duì)變量值得拷貝,即將實(shí)參的值復(fù)制給變參,當(dāng)函數(shù)對(duì)變參進(jìn)行處理時(shí),并不會(huì)影響原來實(shí)參的值。
-
指針
函數(shù)的變量不僅可以使用普通變量,還可以使用指針變量,使用指針變量作為函數(shù)的參數(shù)時(shí),在進(jìn)行參數(shù)傳遞時(shí)將是一個(gè)地址看唄,即將實(shí)參的內(nèi)存地址復(fù)制給變參,這時(shí)對(duì)變參的修改也將會(huì)影響到實(shí)參的值。
-
數(shù)組
和其他語言不同的是,go語言在將數(shù)組名作為函數(shù)參數(shù)的時(shí)候,參數(shù)傳遞即是對(duì)數(shù)組的復(fù)制。在形參中對(duì)數(shù)組元素的修改都不會(huì)影響到數(shù)組元素原來的值。
-
slice, map, chan
在使用 slice, map, chan 作為函數(shù)參數(shù)時(shí),進(jìn)行參數(shù)傳遞將是一個(gè)地址拷貝,即將底層數(shù)組的內(nèi)存地址復(fù)制給參數(shù) slice, map, chan 。這時(shí),對(duì) slice, map, chan 元素的操作就是對(duì)底層數(shù)組元素的操作。
-
函數(shù)名字
在 go 語言中,函數(shù)也作為一種數(shù)據(jù)類型,所以函數(shù)也可以作為函數(shù)的參數(shù)來使用。
返回值
go 語言可以返回局部變量的指針,因?yàn)?go 語言的回收機(jī)制是當(dāng)銷毀棧上的臨時(shí)數(shù)據(jù)且發(fā)現(xiàn)有被外部引用的棧上變量時(shí),會(huì)自動(dòng)轉(zhuǎn)移到堆上。
閉包
和其他語言類似,golang 也支持閉包函數(shù):
package mainimport "fmt"func adder() func(int) int {sum := 0return func(x int) int {sum += xreturn sum} }func main() {pos, neg := adder(), adder()for i := 0; i < 10; i++ {fmt.Println(pos(i),neg(-2*i),)} }Go語言入門教程總結(jié)
很多人將 Go 語言稱為 21 世紀(jì)的 C 語言,因?yàn)?Go 不僅擁有 C 語言的簡潔和性能,而且還很好的提供了 21 世紀(jì)互聯(lián)網(wǎng)環(huán)境下服務(wù)端開發(fā)的各種實(shí)用特性,讓開發(fā)者在語言級(jí)別就可以方便的得到自己想要的東西。
總結(jié)
以上是生活随笔為你收集整理的Golang基础知识入门详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。