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