Go語言的這些地方都做的還不錯:擁有自動垃圾回收、一個包系統(tǒng)、函數(shù)作為一等公民、詞法作用域、系統(tǒng)調(diào)用接口、只讀的UTF8字符串等。但是Go語言本身只有很少的特性,也不太可能添加太多的特性。例如,它沒有隱式的數(shù)值轉(zhuǎn)換,沒有構(gòu)造函數(shù)和析構(gòu)函數(shù),沒有運算符重載,沒有默認參數(shù),也沒有繼承,沒有泛型,沒有異常,沒有宏,沒有函數(shù)修飾,更沒有線程局部存儲。但是,語言本身是成熟和穩(wěn)定的,而且承諾保證向后兼容:用之前的Go語言編寫程序可以用新版本的Go語言編譯器和標準庫直接構(gòu)建而不需要修改代碼。
我們還可以使用 go build 命令來生成二進制文件:
行分隔符
在 Go 程序中,一行代表一個語句結(jié)束。每個語句不需要像 C 家族中的其它語言一樣以分號 ; 結(jié)尾,因為這些工作都將由 Go 編譯器自動完成。
注釋
單行注釋是最常見的注釋形式,你可以在任何地方使用以 // 開頭的單行注釋。多行注釋也叫塊注釋,均已以 /* 開頭,并以 */ 結(jié)尾。如:
字符串連接
Go 語言的字符串可以通過 + 實現(xiàn):
格式化字符串
Go 語言中使用 fmt.Sprintf 格式化字符串并賦值給新串:
其他運算符
& 返回變量存儲地址 例如 &a; 將給出變量的實際地址;
- 指針變量 例如 *a,是一個指針變量;
條件語句
Go 沒有三目運算符,所以不支持 ?: 形式的條件判斷。
循環(huán)語句
break語句:跳出for或switch;
contonue:跳過下面的for從下一輪開始;
goto:跳到某一行
函數(shù)
Go 語言最少有個 main() 函數(shù)。有且只能有一個
函數(shù)定義
func function_name( [parameter list] ) [return_types] {
函數(shù)體
}
如返回輸入值的2倍
func func_test(a int) int {
return a*=2;
}
如求最大值
func getMax(a,b int) int{
if(a > b) {
return a
} else{
return b
}
}
函數(shù)用法
- 閉包 閉包是匿名函數(shù),可在動態(tài)編程中使用;
- 函數(shù)作為另外一個函數(shù)的實參
- 方法
變量作用域
- 局部;2.全局3.形參
全局變量與局部變量名稱可以相同,但是函數(shù)內(nèi)的局部變量會被優(yōu)先考慮,即就近原則
初始化局部和全局變量
int:0
float:32
pointer:nil
數(shù)組
Go語言數(shù)組需要指定元素類型及元素個數(shù)
格式如下:
var variable_name [SIZE] variable_type
如 var array_init [10] float32
更多例子
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果數(shù)組長度不確定,可以使用 ... 代替數(shù)組的長度,編譯器會根據(jù)元素個數(shù)自行推斷數(shù)組的長度:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
如果設(shè)置了數(shù)組的長度,我們還可以通過指定下標來初始化元素:
// 將索引為 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
訪問數(shù)組元素
var value float32 = balance[9]
指針
我們都知道,變量是一種使用方便的占位符,用于引用計算機內(nèi)存地址。
Go 語言的取地址符是 &,放到一個變量前使用就會返回相應(yīng)變量的內(nèi)存地址。
package main
import "fmt"
func main() {
var a int = 10
fmt.Printf("變量的地址: %x\n", &a )
}
什么是指針
一個指針變量指向了一個值的內(nèi)存地址。
類似于變量和常量,在使用指針前你需要聲明指針。指針聲明格式如下:
var var_name *var-type
例如:
var ip *int
var fp *float32
如何使用指針
指針使用流程:
1.定義指針變量。
2.為指針變量賦值。
3.訪問指針變量中指向地址的值。
當(dāng)一個指針被定義后沒有分配到任何變量時,它的值為 nil。
nil 指針也稱為空指針。
nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。
一個指針變量通常縮寫為 ptr。
結(jié)構(gòu)體
Go 語言中數(shù)組可以存儲同一類型的數(shù)據(jù),但在結(jié)構(gòu)體中我們可以為不同項定義不同的數(shù)據(jù)類型。
結(jié)構(gòu)體是由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合。
結(jié)構(gòu)體表示一項記錄,比如保存圖書館的書籍記錄,每本書有以下屬性:
在Java中叫集合,但是比集合更強大,更像是實體bean,對象,只是沒有方法而已
包括成員的訪問都是和java是一樣的
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 聲明 Book1 為 Books 類型 /
var Book2 Books / 聲明 Book2 為 Books 類型 */
/* book 1 描述 */
Book1.title = "Go 語言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 語言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 語言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(&Book1)
/* 打印 Book2 信息 */
printBook(&Book2)
}
func printBook( book *Books ) {
fmt.Printf( "Book title : %s\n", book.title)
fmt.Printf( "Book author : %s\n", book.author)
fmt.Printf( "Book subject : %s\n", book.subject)
fmt.Printf( "Book book_id : %d\n", book.book_id)
}
Go 語言切片(Slice)
Go 語言切片是對數(shù)組的抽象。
Go 數(shù)組的長度不可改變,在特定場景中這樣的集合就不太適用,Go 中提供了一種靈活,功能強悍的內(nèi)置類型切片("動態(tài)數(shù)組"),與數(shù)組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。
強!!!
切片聲明時不需要指定長度
范圍(Range)
Go 語言中 range 關(guān)鍵字用于 for 循環(huán)中迭代數(shù)組(array)、切片(slice)、通道(channel)或集合(map)的元素。在數(shù)組和切片中它返回元素的索引和索引對應(yīng)的值,在集合中返回 key-value 對。沒看太懂
package main
import "fmt"
func main() {
//這是我們使用range去求一個slice的和。使用數(shù)組跟這個很類似
nums := []int{2, 3, 4}
sum := 0
for , num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//在數(shù)組上使用range將傳入index和值兩個變量。上面那個例子我們不需要使用該元素的序號,所以我們使用空白符""省略了。有時侯我們確實需要知道它的索引。
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//range也可以用在map的鍵值對上。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用來枚舉Unicode字符串。第一個參數(shù)是字符的索引,第二個是字符(Unicode的值)本身。
for i, c := range "go" {
fmt.Println(i, c)
}
}
Map集合
Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索數(shù)據(jù),key 類似于索引,指向數(shù)據(jù)的值。
Map 是一種集合,所以我們可以像迭代數(shù)組和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現(xiàn)的。
2種聲明方式
/* 聲明變量,默認 map 是 nil /
var map_variable map[key_data_type]value_data_type
// 為啥搞個[] 感覺乖乖的 也體現(xiàn)不了 key-value呀
/ 使用 make 函數(shù) */
map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就會創(chuàng)建一個 nil map。nil map 不能用來存放鍵值對
delete函數(shù)
遞歸函數(shù)
類型轉(zhuǎn)換
type_name(expression) 與java順序一致
此外go不支持隱式類型轉(zhuǎn)換 如
var a int32
var b int 64
a = b
會報類型轉(zhuǎn)換錯誤
a = int32(b)
接口
使用實例
/* 定義接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定義結(jié)構(gòu)體 /
type struct_name struct {
/ variables */
}
/* 實現(xiàn)接口方法 /
func (struct_name_variable struct_name) method_name1() [return_type] {
/ 方法實現(xiàn) /
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/ 方法實現(xiàn)*/
}
可以看到 struct方法并不是在struct里實現(xiàn)的,而是通過在參數(shù)里傳遞具體的struct以及變量
如下所示
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
錯誤處理
Go 語言通過內(nèi)置的錯誤接口提供了非常簡單的錯誤處理機制。
error類型是一個接口類型,這是它的定義:
type error interface {
Error() string
}
我們可以在編碼中通過實現(xiàn) error 接口類型來生成錯誤信息。
函數(shù)通常在最后的返回值中返回錯誤信息。使用errors.New 可返回一個錯誤信息:
Go并發(fā)
Go語言支持并發(fā),只需要通過go關(guān)鍵字來開啟goroutine
goroutine是輕量級線程 什么叫輕量級線程 和普通的線程有什么區(qū)別
goroutone語法格式
go 函數(shù)名(參數(shù)列表)
go f(x,y,z)
開啟一個新的goroutine線程
func main() {
go say("world")
say("hello")
}
Go 允許使用 go 語句開啟一個新的運行期線程, 即 goroutine,以一個不同的、新創(chuàng)建的 goroutine 來執(zhí)行一個函數(shù)。 同一個程序中的所有 goroutine 共享同一個地址空間。
啥叫運行期線程? 運行完就結(jié)束這個意思? 共享同一個地址空間? 這么輕量?那不是相當(dāng)于一個線程分成了多個線程來用?
通道 channel
通道(channel)用于兩個 goroutine 之間通過傳遞一個指定類型的值來同步運行和通訊。操作符 <- 用于指定通道的方向,發(fā)送或接收。如果未指定方向,則為雙向通道。
ch <- v // 把 v 發(fā)送到通道 ch
v := <-ch // 從 ch 接收數(shù)據(jù)
// 并把值賦給 v
聲明一個通道很簡單,我們使用chan關(guān)鍵字即可,通道在使用前必須先創(chuàng)建: