您好,登錄后才能下訂單哦!
寫給大忙人看的Golang教程(一)
閱讀本文之前,我認(rèn)為你已經(jīng)掌握其他語言基礎(chǔ)并寫出一個(gè)簡(jiǎn)單的項(xiàng)目。
.go
為擴(kuò)展名.main()
方法.\t
制表符\n
換行符\\
一個(gè)斜杠\"
一個(gè)引號(hào)\r
一個(gè)回車// 注釋內(nèi)容
行注釋/* 注釋內(nèi)容 */
多行注釋gofmt -w
格式化代碼Golang的代碼風(fēng)格:
// HelloWorld.go
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
var
關(guān)鍵字定義變量:var 變量名稱 數(shù)據(jù)類型
var 變量名稱 = 值
var
關(guān)鍵字:變量名稱 := 值
, 變量名稱不應(yīng)該是已經(jīng)定義過的變量名稱 := 值
等同于var 變量名稱 數(shù)據(jù)類型 = 值
var 變量名稱, 變量名稱... 數(shù)據(jù)類型
var 變量名稱, 變量名稱, ... = 值, 值, ...
var
關(guān)鍵字多變量聲明和賦值: 變量名稱, 變量名稱, ... := 值, 值, ...
var (
變量名稱 = 值
...
)
使用unsafe.Sizeof()
查看變量占用的空間
package main
import (
"fmt"
"unsafe"
)
func main() {
var x int = 10
fmt.Println("The x size: ", unsafe.Sizeof(x))
// The x size: 8
}
float32
表示單精度,float64
表示雙精度
字符常量使用單引號(hào)
Go中的字符編碼使用UTF-8
類型
bool
類型只能取true
或false
在Go中字符串是不可變的
Go支持使用反引號(hào)輸出原生字符串
目標(biāo)數(shù)據(jù)類型(變量)
。fmt.Sprintf()
字符串格式化函數(shù)。strconv.FormatBool()
、strconv.FormatInt()
、strconv.FormatUint()
、strconv.FormatFloat()
格式化函數(shù)。strconv.ParseBool()
、strconv.ParseInt()
、strconv.ParseUint()
、strconv.ParseFloat()
格式化函數(shù)。package main
import "fmt"
func main() {
var i = 10
var ptr *int = &i
fmt.Println("變量i的內(nèi)存地址是: ", ptr)
// 變量i的內(nèi)存地址是: 0xc00004a080
fmt.Println("變量i的存儲(chǔ)內(nèi)容是: ", *ptr)
// 變量i的存儲(chǔ)內(nèi)容是: 10
}
_
表示空標(biāo)識(shí)符x++
和x--
,沒有++x
和--x
x := a++
fmt.Sacnf()
:使用指定的格式獲取文本fmt.Sacnln()
:以換行為結(jié)束的文本略
if
語句:
if x>12 {
}
// Golang支持直接在condition中定義變量
if x:=12; x>12 {
}
if-else
語句:
if x:=12; x>20 {
}else {
}
if-else if-else
語句:
if x:=12; x>100 {
}else if x>50 {
}else if x>10 {
}else {
}
switch-case-default
語句:
// 每個(gè)分支不需要break語句
// switch也支持在condition中直接定義變量
// case支持多個(gè)表達(dá)式
// 取消break使用fallthrough語句————switch穿透
switch y:=10;y {
case 5:
// something
case 10:
// something
fallthrough
case 20, 25, 30:
// something
default:
// something
}
for
循環(huán)
for i:=1;i<10;i++ {
}
// Golang也提供了for-each或for-range類似的循環(huán)
str := "Hello Golang."
for index, value:=range str {
// index表示索引
// value表示值
}
while
循環(huán)
for {
if condition {
break
}
// something
}
do-while
循環(huán)
for {
// something
if condition {
break
}
}
// 設(shè)置隨機(jī)數(shù)的種子為當(dāng)前的系統(tǒng)時(shí)間
rand.Seed(time.Now().Unix())
// 生成0-99范圍的隨機(jī)數(shù)
randomNumber := rand.Intn(100)
break
語句在多層嵌套中可以通過標(biāo)簽指明要終止到哪一層語句塊:label:
for {
break label
}
continue
語句在多層嵌套中可以通過標(biāo)簽指明要跳出到到哪一層語句塊:label:
for {
continue label
}
goto
語句可以無條件跳轉(zhuǎn),容易造成邏輯混亂,一般不主張使用goto
語句:
label:
for {
goto label
}
return
語句用戶退出函數(shù)
return
// 或
return some
函數(shù)基本語法:
func functionName (paramsList) (returnList) {}
Golang不支持函數(shù)重載
Golang函數(shù)本身也是一種數(shù)據(jù)類型,可以賦值給變量,那么該變量也是函數(shù)類型
Golang函數(shù)可以作為實(shí)參傳入另一個(gè)函數(shù)
Golang支持自定義數(shù)據(jù)類型,使用:type 自定義數(shù)據(jù)類型名 數(shù)據(jù)類型
type myfunc func(int)(int, int)
支持使用_
忽略返回值
支持可變參數(shù)
package main
import "fmt"
func main() {
ret := sum(1, 2, 3)
fmt.Println(ret) //6
}
// 可變參數(shù)
func sum(args...int) int {
sum := 0
for i:=0; i<len(args); i++ {
sum += args[i]
}
return sum
}
? 包的本質(zhì)就是一個(gè)目錄,Go的每一個(gè)文件都必須屬于一個(gè)包。
打包:
package packageName
導(dǎo)入包:
import "packageName"
// 導(dǎo)入多個(gè)包
import (
"packageName"
...
)
// 導(dǎo)入包時(shí)自動(dòng)從GOPATH下面的src下面引入
包支持別名
package main
import f "fmt"
func main() {
f.Println()
}
init
函數(shù)init
函數(shù),它優(yōu)先于main
函數(shù)執(zhí)行,被Go框架調(diào)用。func init() {}
init
函數(shù)再執(zhí)行main
包中的init
函數(shù)// util.HelloWorld.go
package utils
import "fmt"
func init() {
fmt.Println("Util.HelloWorld() init")
}
func HelloWorld()(){
fmt.Println("Hello World")
}
// main.test.go
package main
import (
"StudyGo/utils"
"fmt"
)
func init() {
fmt.Println("Main.main() init")
}
func main() {
utils.HelloWorld()
}
// Util.HelloWorld() init
// Main.main() init
// Hello World
直接調(diào)用
func (paramsList)(returnList){
// something
}()
賦值給一個(gè)變量
x := func (paramsList)(returnList){
// something
}
y := x(paramsList)
閉包就是函數(shù)與其相關(guān)的引用環(huán)境構(gòu)成的實(shí)體
package main
import (
"fmt"
"strings"
)
func main() {
fileName := "file"
fileSuffix := ".mp3"
ms := makeSuffix(fileSuffix)
ret := ms(fileName)
fmt.Println(ret)
}
func makeSuffix(suffix string) func(string) string {
return func (s string) string {
if strings.HasSuffix(s, suffix) {
return s
}else {
return s + suffix
}
}
}
defer是Go語言中的延時(shí)機(jī)制,用于處理關(guān)閉文件句柄等資源釋放操作
package main
import "fmt"
func main() {
SayHello()
}
func SayHello() {
defer fmt.Println("Bye.")
fmt.Println("Hi.")
}
// Hi.
// Bye.
len()
:計(jì)算字符串長(zhǎng)度strconv.Atoi(s string) (i int, err error)
:將字符串轉(zhuǎn)換為整數(shù)strconv.Itoa(i int) string
:將整數(shù)轉(zhuǎn)換為字符串strconv.FormatInt(i int64, base int) string
:將十進(jìn)制轉(zhuǎn)換為其他進(jìn)制strings.Contains(s string, sub string) bool
:判斷字符串是否包含子字符串strings.Count(s string, sub string) int
:統(tǒng)計(jì)字符串中有幾個(gè)子字符串strings.EqualFold(s_0 string, s_1 string) bool
:忽略大小寫比較字符串是否相等strings.Index(s string, sub string) int
:返回子字符串在字符串中的第一個(gè)位置strings.LastIndex(s string, sub string)
:返回子字符串在字符串中的最后一個(gè)位置string.Replace(s string, oldSub string, newSub string, n int) string
:將指定的子字符串替換為其他字符串,n代表替換個(gè)數(shù),-1表示全部,返回新字符串string.ToLower(s string)
:將字符串轉(zhuǎn)換為小寫string.ToUpper(s string)
:將字符串轉(zhuǎn)換為大寫string.Split(s string, sep string) array
:將字符串按照sep分隔string.TrimSpace(s string) string
:刪除字符串兩側(cè)的空格string.Trim(s string, sub string) string
:將字符串兩側(cè)的sub去掉string.TrimLeft(s string, sub string) string
:將字符串左邊的sub刪除string.TrimRight(s string, sub string) string
:將字符串右邊的sub刪除string.HasPrefix(s string, sub string) bool
:判斷s是否以sub開頭string.HasSuffix(s string, sub string) bool
:判斷s是否以sub結(jié)尾time.Time
:表示時(shí)間類型time.Now() struct
:獲取當(dāng)前本地時(shí)間time.Now().Year()
:返回年time.Now().Month()
:返回月,使用int(time.Now().Month())
取得數(shù)字time.Now().Day()
:返回日time.Now().Hour()
:返回時(shí)time.Now().Minute()
:返回分time.Now().Second()
:返回秒time.Now().Format(s string)
:格式化時(shí)間數(shù)據(jù),2006-01-02 15:04:05
表示格式化的格式字符串其中的值不能改變time.Sleep(d Duration)
:休眠函數(shù)
time.Hour
:一小時(shí)time.Minute
:一分鐘time.Second
:一秒time.Millisecond
:一毫秒time.Microsecond
:一微秒time.Nanosecon
:一納秒time.Now().Unix() int64
:返回Unix秒時(shí)間戳time.Now().UnixNano() int64
:返回Unix納秒時(shí)間戳len()
:求長(zhǎng)度new()
:分配內(nèi)存,主要用來分配值類型make()
:分配內(nèi)存,主要用來分配引用類型在Go中捕獲異常的機(jī)制是使用defer
關(guān)鍵字修飾匿名函數(shù),導(dǎo)致匿名函數(shù)最后執(zhí)行,在匿名函數(shù)中調(diào)用recover()
函數(shù),通過返回值是否為nill
來判斷是否發(fā)生異常信息。
package main
import "fmt"
func main() {
ret := ComputeNumber(1, 0)
fmt.Println(ret)
}
func ComputeNumber(n_0 int, n_1 int) int {
defer func() {
if e := recover(); e != nil{
fmt.Println(e)
}
}()
result := n_0 / n_1
return result
}
自定義錯(cuò)誤
使用errors.New(Type) *Type
創(chuàng)建一個(gè)error
類型,panic()
接收一個(gè)空接口類型,輸出錯(cuò)誤信息并結(jié)束運(yùn)行。
package main
import "errors"
func main() {
err := readConfigureFile("config.json")
if err !=nil {
panic(err) // panic: Read config.ini error.
}
}
func readConfigureFile(path string)(err error) {
if path != "config.ini" {
return errors.New("Read config.ini error.")
} else {
return nil
}
}
? 在Go中數(shù)據(jù)是值類型,使用以下方式創(chuàng)建數(shù)組。
var 數(shù)組名稱 [元素個(gè)數(shù)]數(shù)據(jù)類型 = [元素個(gè)數(shù)]數(shù)據(jù)類型{元素}
var 數(shù)組名稱 = [元素個(gè)數(shù)]數(shù)據(jù)類型{元素}
var 數(shù)組名稱 = [...]數(shù)據(jù)類型{元素個(gè)數(shù)}
var 數(shù)組名稱 = [...]數(shù)據(jù)類型{索引:值}
for-each/for-range
遍歷數(shù)組的長(zhǎng)度是固定的,切片 的長(zhǎng)度不是固定的。
var 切片名稱 []數(shù)據(jù)類型
切片名稱[索引:索引]
切片的結(jié)構(gòu):[起始數(shù)據(jù)元素指針, 長(zhǎng)度, 容量]
通過make創(chuàng)建切片:var 切片名稱 []數(shù)據(jù)類型 = make([]數(shù)據(jù)類型, 長(zhǎng)度, 容量)
切片支持普通遍歷和for-range
方式遍歷
使用append()
函數(shù)追加元素到切片末尾,容量不夠時(shí)自動(dòng)擴(kuò)容
使用copy()
函數(shù)拷貝數(shù)組
string
類型底層是個(gè)byte
數(shù)組,也可以進(jìn)行切片處理。string
是不可變的,如果要修改字符串,需要先將字符串轉(zhuǎn)換為切片修改完成后再轉(zhuǎn)換成為字符串。str := "Hello World."
arr := []byte(str)
arr[11] = '!'
str = string(arr)
fmt.Println(str)
var Map名稱 map[KeyType]ValueType
使用make(map[KeyType]ValueType)
分配空間
delete(m map[Type]Type, key Type)
:通過Key刪除元素,如果元素不存在也不會(huì)報(bào)錯(cuò)
清空Map一種是遍歷刪除,一種是make
重新分配空間,使得原來的Map成為垃圾讓GC回收
查找使用value, ok = mapName[Key]
,如果ok
為true
,表示元素存在
for-range
遍歷for key, value := range mapName{
}
Go中的OOP是通過struct
來實(shí)現(xiàn)的
type 類名 struct {
屬性名 數(shù)據(jù)類型
...
}
創(chuàng)建結(jié)構(gòu)體變量
var 變量名稱 結(jié)構(gòu)體類型
var 變量名稱 結(jié)構(gòu)體類型 = 結(jié)構(gòu)體類型{}
變量名稱 := 結(jié)構(gòu)體類型{}
// 下面兩種寫法等價(jià):
var 變量名稱 *結(jié)構(gòu)體名稱 = new(結(jié)構(gòu)體名稱)
var 變量名稱 *結(jié)構(gòu)體名稱 = &結(jié)構(gòu)體名稱
// 在操作屬性、方法的時(shí)候Go進(jìn)行了優(yōu)化,下面兩種寫法是等價(jià)的:
(*變量名稱).屬性 = 值
變量名稱.屬性 = 值
每一個(gè)字段可以加上一個(gè)tag,該tag可以通過反射機(jī)制獲取,常見的場(chǎng)景就是序列化與反序列化
屬性名稱 數(shù)據(jù)類型 `json:Tag名稱`
Go中的類沒有構(gòu)造函數(shù),通常通過工廠模式來實(shí)現(xiàn)
package model
// 如果Name和Age改為name和age,需要為person綁定Getter和Setter方法
type person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func NewPerson(n string, a int)(*person){
return &person{
Name : n,
Age : a,
}
}
package main
import "fmt"
import "StudyGo/model"
func main() {
var tom = model.NewPerson("Tom", 20)
fmt.Println((*tom).Name)
fmt.Println((*tom).Age)
}
在Go中在一個(gè)結(jié)構(gòu)體中嵌套另一個(gè)匿名結(jié)構(gòu)體就認(rèn)為實(shí)現(xiàn)了繼承
type Ani struct {
name string
age int
}
type Cat struct {
Ani
say string
}
結(jié)構(gòu)體變量.數(shù)據(jù)類型 = 值
來訪問接口
type 接口名稱 interface {
方法名稱(參數(shù)列表)(返回值列表)
}
類型斷言
var number float32
var x interface{}
x = t
t = x.(float32) // 判斷一下是否可以轉(zhuǎn)換成為float32類型
func (recv type) funcName (paramsList)(returnList) {
// something
}
recv
表示這個(gè)方法與type
類進(jìn)行綁定,方法內(nèi)通過recv
操作type
類中的字段type
是個(gè)結(jié)構(gòu)體類型recv
是個(gè)結(jié)構(gòu)體類型變量通常為了執(zhí)行效率一般不會(huì)直接傳入結(jié)構(gòu)體類型作為接收器,而是結(jié)構(gòu)體類型指針:
func (dog *Dog) function()(){ // 綁定的是地址,操作時(shí)也要使用地址
// something
}
// 調(diào)用時(shí)
var d Dog
(&d).function()
但是編譯器做出了相關(guān)的優(yōu)化:
var d Dog
d.function()
File.Open(name string) (file *File, err error)
:打開文件File.Close() error
:關(guān)閉文件package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("main/file.txt")
if err != nil {
fmt.Println(err)
}
err = file.Close()
if err != nil {
fmt.Println(err)
}
}
使用bufio.NewReader(file *File) *reader
創(chuàng)建讀取器對(duì)象,調(diào)用bufio.ReadString(end string) (s string, err error)
讀取文件,以end
結(jié)尾。
使用ioutil.ReadFile(path string) ([]byte, error)
:返回nil
,一次性讀取全部文件,不需要手動(dòng)打開和關(guān)閉。
使用os.OpenFile(name string, flag int, perm FileMode)(file *File, err error)
name:文件完整路徑
flag:打開模式
perm:權(quán)限(類Unix生效)
// 打開模式:可以使用"|"進(jìn)行組合
const (
O_RDONLY int = syscall.O_RDONLY // 只讀打開
O_WRONLY int = syscall.O_WRONLY // 只寫打開
O_RDWR int = syscall.O_RDWR // 讀寫打開
O_APPEND int = syscall.O_APPEND // 追加打開
O_CREATE int = syscall.O_CREAT // 如果不存在就創(chuàng)建
O_EXCL int = syscall.O_EXCL // 創(chuàng)建打開,文件必須不存在
O_SYNC int = syscall.O_SYNC // 打開文件用于同步IO
O_TRUNC int = syscall.O_TRUNC // 如果可能,打開文件是清空文件
)
// 權(quán)限:
r,w,x
使用bufio.NewWriter(file *File) *writer
來創(chuàng)建寫入器,使用bufio.Flush()
將緩存同步到文件,使用bufio.WriterString(str string)
來寫入文件。
io.Copy(dst Writer, src Reader)(written int64, err error)
來實(shí)現(xiàn)文件拷貝os.Args []string
保管了所有命令行參數(shù),第一個(gè)參數(shù)是程序名稱。
flag
包可以實(shí)現(xiàn)更加高級(jí)的命令行參數(shù)處理:
var username string
// 綁定參數(shù)
flag.StringVar(&username, "u", "root", "Username")
// -- 保存參數(shù)字符串的地址
// -- 參數(shù)名稱
// -- 默認(rèn)值
// -- 參數(shù)釋義
// 解析參數(shù)
flag.Parse()
結(jié)構(gòu)體、切片、Map等都可以解析為Json字符串,使用encoding/json.Marshal(i interface{},)([]byte, error)
來實(shí)現(xiàn)各種類型到Json數(shù)據(jù);使用encoding/json.Unmarshal(Json字符串, 實(shí)例對(duì)象的引用)
反序列化。
Go語言自帶輕量級(jí)的測(cè)試框架和go test -v
命令來實(shí)現(xiàn)單元測(cè)試和性能測(cè)試。Go的測(cè)試指令會(huì)自動(dòng)識(shí)別以TestXxx
命名的函數(shù):
import "testing"
func TestXxx(t *testing.T){
t.Fatalf(string) // 停止測(cè)試并記錄日志
}
Go主線程可以理解為線程也可以理解為進(jìn)程,一個(gè)Go線程可以包含多個(gè)協(xié)程(微程),Go程具備以下幾點(diǎn)特質(zhì):
有獨(dú)立的??臻g
共享程序堆空間
調(diào)度由用戶控制
主線程是一個(gè)重量級(jí)物理線程,直接作用在CPU上,非常消耗資源,協(xié)程從主線程開啟,是邏輯態(tài)的輕量級(jí)線程,相對(duì)資源消耗少。在Go中可以輕松開啟成千上萬個(gè)協(xié)程,其他語言的并發(fā)機(jī)制一般是線程實(shí)現(xiàn),這就是Go的優(yōu)勢(shì)。使用go
關(guān)鍵字修飾一個(gè)函數(shù)等即可開啟一個(gè)Go程。Go可以充分發(fā)揮多核多CPU的優(yōu)勢(shì),使用runtime.NumCpu()
可以獲取當(dāng)前機(jī)器的CPU個(gè)數(shù),使用runtime.GOMAXPROCS(n int)
設(shè)置可用的CPU數(shù)量。在Go1.8之前需要手動(dòng)設(shè)置,Go1.8以后默認(rèn)使用多CPU。
不同的Go協(xié)程如何實(shí)現(xiàn)通信,下面給出兩種方法:
在Go中,sync
包提供了基本的同步單元,大部分適用于低水平的程序線程,高水平的同步一般使用管道解決。
使用全局變量加鎖
使用sync.Lock
聲明一個(gè)全局變量:
var lock sync.Mutex
使用lock.Lock()
加鎖,使用lock.Unlock()
解鎖。
使用管道
管道的本質(zhì)就是隊(duì)列,使用var 管道名稱 chan 數(shù)據(jù)類型
,channel必須是引用類型,管道使用make
聲明空間以后才可以使用,管道是有數(shù)據(jù)類型區(qū)分的,如果要存儲(chǔ)任意數(shù)據(jù)類型需要聲明為interface{}
類型。
管道使用<-
運(yùn)算符存取數(shù)據(jù):
var MyChannel chan int
MyChannel = make(chan int, 8)
MyChannel <- 8 // 存入數(shù)據(jù)
number := <- MyChannel // 取出數(shù)據(jù)
close(MyChannel) // 關(guān)閉管道,但是可以讀取數(shù)據(jù)
管道容量用完以后不能再存儲(chǔ)數(shù)據(jù);在沒有協(xié)程使用的情況下,如果管道的數(shù)據(jù)用完就會(huì)產(chǎn)生dead lock
錯(cuò)誤。
默認(rèn)情況下管道是雙向的,可讀可寫,聲明只讀/寫管道:
var chan_0 = chan<- int // 只讀
var chan_1 = <-chan int // 只寫
在使用管道讀取數(shù)據(jù)的時(shí)候沒有關(guān)閉可能會(huì)發(fā)生阻塞,如果沒有數(shù)據(jù)會(huì)發(fā)生死鎖現(xiàn)象,因此可以使用select
關(guān)鍵字來解決。
for {
select {
case v <- chan_0 :
// something
default:
// something
}
}
如果有一個(gè)協(xié)程出現(xiàn)panic,將會(huì)導(dǎo)致整個(gè)程序崩潰,因此需要異常處理機(jī)制來維護(hù)。
通過reflect.TypeOf()
獲取變量類型,返回reflect.Type
類型
通過reflect.ValueOf()
獲取變量的值,返回reflect.Value
類型
listen, err := net.Listen("tcp", "0.0.0.0:8888")
if err != nil{
return
}
defer listen.Close()
for {
connect, err := listen.Accept()
if err == nil {
go func (connect net,Conn)(){
defer connect.Close()
buffer := make([]byte, 1024)
num, err := connect.Read(buffer)
if err != nil {
return
}
}()
}
}
connect, err := Dial("tcp", "127.0.0.1:8888")
if err != nil {
return
}
num, err := connect.Write([]byte("Hello"))
connect.Close()
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。