一定要努力堵住那悠悠眾口
Go的目錄結(jié)構(gòu):
bin存放編譯后的可執(zhí)行文件业崖;
pkg存放編譯后的包文件屁倔;
src存放項(xiàng)目源文件
一翅娶、包的概念
??包是go語言中不可缺少部分弄屡,在每個(gè)go源碼的第一行進(jìn)行定義,定義方式是:package "包名"搬设,并且該名稱是作為調(diào)用該包時(shí)候所使用的名稱穴店。
包的概念總結(jié):
每個(gè) Go 文件都屬于且僅屬于一個(gè)包。一個(gè)包可以由許多以 .go 為擴(kuò)展名的源文件組成拿穴,因此文件名和包名可能不同泣洞,為了規(guī)范,功能相似或者屬于相同性質(zhì)的源碼歸集到相同的包名中默色。
每個(gè)應(yīng)用程序都必須有且只有一個(gè)包名稱為main球凰,是該程序的入口,執(zhí)行時(shí)候會(huì)尋找包名為main的代碼腿宰。
包與包之間可以被相互調(diào)用呕诉,主要為了減少代碼重復(fù)性。
包一旦被引入就需要被使用吃度,除非引入的時(shí)候忽略它(前面加_表示忽略它,只是用它來初始化)甩挫,如果引入了包,而在代碼中沒用使用椿每,運(yùn)行時(shí)候會(huì)報(bào)錯(cuò)伊者。
二、包的引入
1间护、包引入的方法
方法一:
import "fmt"
import "os"
方法二:
import "fmt"; import "os"
方法三(推薦)
import (
"fmt"
"os"
)
2删壮、引入其他包
??go語言中,引入包的路徑是根據(jù)GOPATH/src 這個(gè)環(huán)境變量作為相對路徑進(jìn)行引入的兑牡,如果GOPATH存在多個(gè),那么go將逐一尋找路徑税灌,直到找到均函,若GOPATH/src未找到則編譯出錯(cuò)亿虽。
示例:
我的GOPATH為:/usr/local/go/awesomeProject
建立包文件:/usr/local/go/awesomeProject/src/day02/eg1/pkg.go
// pkg.go文件內(nèi)容
package add
import "fmt"
func Sum(a int,b int) {
var c int
c = a + b
fmt.Println("res",c)
}
在main中進(jìn)行引入:
package main
import (
"day02/eg1"
"fmt"
)
func main() {
add.Sum(2, 3) // 調(diào)用包
fmt.Println("Hello,World!")
}
三、變量
1苞也、單個(gè)變量的聲明
go語言中洛勉,所有變量都必須先聲明再使用,下面是聲明變量和賦值方法:
- 先聲明后賦值: var <變量名稱> <變量類型>
賦值:變量的賦值格式: <變量名稱> = <表達(dá)式> - 聲明的同時(shí)賦值:var <變量名稱> <變量類型> = <表達(dá)式> (簡寫為 變量名稱 := 表達(dá)式)
package main
import "fmt"
func main(){
var a string // 聲明一個(gè)變量名為a如迟,類型為字符串的變量
a = "wpr" // 對a變量賦值
var age int = 23 // 聲明并賦值變量
fmt.Println(a,age)
}
2收毫、多個(gè)變量聲明
- 先聲明后賦值:var <變量1>,<變量2>,<變量3> <變量類型>
賦值 : <變量1>,<變量2> = 值1,值2 - 聲明同時(shí)賦值 :var <變量1>,<變量2> = 值1,值2
可簡寫為: <變量1>,<變量2> = 值1,值2 - 多個(gè)變量還可以使用var()聲明
package main
import "fmt"
func main() {
var a,b int
a,b = 1,2
var c,d = 3,"wpr" // 類型自動(dòng)推斷
e,f :=4 ,"hello" //簡寫
fmt.Println(a,b,c,d,e,f)
}
使用var()聲明
package main
import "fmt"
func main() {
var (
a int //默認(rèn)值為0
b string // 默認(rèn)值為空字符串("")
c = 3
)
fmt.Println(a,b,c)
}
3、變量名
命名規(guī)則:
- 首字符可以是任意的Unicode字符或者下劃線殷勘;
- 剩余字符可以是Unicode字符此再、下劃線、數(shù)字玲销;
- 不能作為用以下關(guān)鍵字作為變量名
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
4输拇、可見性
??可見性即對包外可見,當(dāng)其他包調(diào)用當(dāng)前包的變量時(shí)候是否允許可見(可訪問)贤斜。(類似python中的對外隱藏策吠,以_開頭)
- 變量開頭字符大寫,表示可見
- 變量開頭字母非大寫瘩绒,則表示私有猴抹,不可見
5、變量的作用域
- 函數(shù)內(nèi)定義的變量稱為局部變量锁荔,其作用域在函數(shù)內(nèi)
- 函數(shù)外定義的變量稱為全局變量蟀给,作用于整個(gè)包,并且該變量首字母大寫堕战,則對其他包可見
四坤溃、常量
常量是一個(gè)簡單值的標(biāo)識符,在程序運(yùn)行時(shí)嘱丢,不會(huì)被修改的量薪介。
常量中的數(shù)據(jù)類型只可以是布爾型、數(shù)字型(整數(shù)型越驻、浮點(diǎn)型和復(fù)數(shù))和字符串型汁政。
定義常量語法:
const 名稱 類型 = value
多個(gè)常量定義語法:
const (
a = 0 // 類型自動(dòng)推倒
b = 1
c = 2
)
iota解釋:
??iota,特殊常量缀旁,可以認(rèn)為是一個(gè)可以被編譯器修改的常量记劈。
??在每一個(gè)const關(guān)鍵字出現(xiàn)時(shí),被重置為0并巍,然后再下一個(gè)const出現(xiàn)之前目木,每出現(xiàn)一次iota,其所代表的數(shù)字會(huì)自動(dòng)增加1懊渡。
iota 可以被用作枚舉值:
const (
a = iota
b = iota
c = iota
)
第一個(gè) iota 等于 0刽射,每當(dāng) iota 在新的一行被使用時(shí)军拟,它的值都會(huì)自動(dòng)加 1;所以 a=0, b=1, c=2 可以簡寫為如下形式:
const (
a = iota
b
c
)
用法:
package main
import "fmt"
func main() {
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 獨(dú)立值誓禁,iota += 1
e // "ha" iota += 1
f = 100 // iota +=1
g // 100 iota +=1
h = iota // 7,恢復(fù)計(jì)數(shù)
i // 8
)
fmt.Println(a,b,c,d,e,f,g,h,i) // 0 1 2 ha ha 100 100 7 8
}
五懈息、數(shù)據(jù)類型
1、布爾型
布爾類型 - 由兩個(gè)預(yù)定義常量組成:true摹恰、false屈芜,默認(rèn)值為false
package main
import "fmt"
func main() {
var (
a bool // 默認(rèn)值為false
b bool = true
)
fmt.Println(a,b) // false true
}
2刻剥、數(shù)字型
整型:
?uint8(無符號 8 位整型 [0 ~ 255])
?uint16(無符號 16 位整型 [0 ~ 65535])
?uint32(無符號 32 位整型 [0 ~ 4294967295])
?uint64(無符號 64 位整型 [0 ~ 18446744073709551615])
?int8(帶符號 8 位整型 [-128 ~ 127])
?int16(帶符號 16 位整型 [-32768 ~ 32767])
?int32(帶符號 32 位整型 [-2147483648 ~ 2147483647])
?int64(帶符號 64 位整型 [-9223372036854775808 ~ 9223372036854775807])
浮點(diǎn)型:
?float32(IEEE-754 32位浮點(diǎn)型數(shù))
?float64(IEEE-754 64位浮點(diǎn)型數(shù))
?complex64(復(fù)數(shù):32 位實(shí)數(shù)和虛數(shù))
?complex128(復(fù)數(shù):64 位實(shí)數(shù)和虛數(shù))
其他數(shù)字類型:
?byte(字符類型,存儲(chǔ)為ascii碼,與uint8相同)
?rune(與int32相同)
?uint(32 或 64 位)
?int(與 uint 大小相同)
?uintptr(無符號整型例嘱,用于存放一個(gè)指針)
3胰苏、字符串類型
雙引號(" ") —— 對特殊字符(如換行\(zhòng)t径缅,\n等)進(jìn)行轉(zhuǎn)義猪腕。
反引號(``) —— 保留原始字符,打印時(shí)候原樣輸出馏颂。
package main
import "fmt"
func main() {
var (
a string = `wpr\n`
b string = "age\n"
)
fmt.Println(a,b) // wpr\n age
}
4示血、派生類型
??指針類型
??數(shù)組類型
??結(jié)構(gòu)類型
??聯(lián)合類型
??函數(shù)類型
??切片類型
??接口類型
5、類型轉(zhuǎn)換
??不同的數(shù)據(jù)類型之間是不允許進(jìn)行賦值或運(yùn)算操作救拉,必須經(jīng)過類型轉(zhuǎn)化才能進(jìn)行運(yùn)算或者賦值难审。
轉(zhuǎn)換方法:數(shù)據(jù)類型()
package main
import "fmt"
func main() {
var a int32 = 1
var b int64 = 64
c:= int64(a)+ b //不轉(zhuǎn)換編譯報(bào)錯(cuò)
fmt.Println(c)
}
六、運(yùn)算符
1亿絮、算數(shù)運(yùn)算符
??+相加 :x + y = z
??-相減 : x - y = z
??*相乘:x * y = z
??% 求余: x % y =z
??++ 自增 :x++
??-- 自減:x--
2告喊、關(guān)系運(yùn)算符
??== 判斷兩個(gè)值是否相等,如果相等返回 True派昧,否則返回 False黔姜。
??!= 判斷兩個(gè)值是否不相等,如果不相等返回 True蒂萎, 否則返回 False秆吵。
??> 判斷左邊值是否大于右邊值,如果是返回 True五慈,否則返回 False纳寂。
??< 判斷左邊值是否小于右邊值,如果是返回 True泻拦,否則返回 False毙芜。
??>= 判斷左邊值是否大于等于右邊值,如果是返回 True争拐,否則返回 False腋粥。
??<= 判斷左邊值是否小于等于右邊值,如果是返回 True,否則返回 False灯抛。
3金赦、邏輯運(yùn)算符
??&& 邏輯 AND 運(yùn)算符。 如果兩邊的操作數(shù)都是 True对嚼,則條件 True,否則為 False绳慎。
??|| 邏輯 OR 運(yùn)算符纵竖。 如果兩邊的操作數(shù)有一個(gè) True,則條件 True杏愤,否則為 False靡砌。
??! 邏輯 NOT 運(yùn)算符。 如果條件為 True珊楼,則邏輯 NOT 條件 False通殃,否則為 True。
4厕宗、位運(yùn)算符
位運(yùn)算符對整數(shù)在內(nèi)存中的二進(jìn)制位進(jìn)行操作画舌。
??& 按位與運(yùn)算符,"&"是雙目運(yùn)算符已慢, 其功能是參與運(yùn)算的兩數(shù)各對應(yīng)的二進(jìn)位相與曲聂。
??| 按位或運(yùn)算符,"|"是雙目運(yùn)算符佑惠,其功能是參與運(yùn)算的兩數(shù)各對應(yīng)的二進(jìn)位相或朋腋。
??^ 按位異或運(yùn)算符,"^"是雙目運(yùn)算符膜楷, 其功能是參與運(yùn)算的兩數(shù)各對應(yīng)的二進(jìn)位相異或旭咽,當(dāng)兩對應(yīng)的二進(jìn)位相異時(shí),結(jié)果為1赌厅。
??<< 左移運(yùn)算符穷绵,"<<"是雙目運(yùn)算符。左移n位就是乘以2的n次方察蹲。其功能把"<<"左邊的運(yùn)算數(shù)的各二進(jìn)位全部左移若干位请垛,由"<<"右邊的數(shù)指定移動(dòng)的位數(shù),高位丟棄洽议,低位補(bǔ)0宗收。
??>> 右移運(yùn)算符,">>"是雙目運(yùn)算符亚兄。右移n位就是除以2的n次方混稽。其功能是把">>"左邊的運(yùn)算數(shù)的各二進(jìn)位全部右移若干位,">>"右邊的數(shù)指定移動(dòng)的位數(shù)。
//假設(shè)A = 60, B = 13;
二進(jìn)制格式表示
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
5匈勋、賦值運(yùn)算
??= 簡單的賦值運(yùn)算符礼旅,將一個(gè)表達(dá)式的值賦給一個(gè)左值
??+= 相加后再賦值 (C += A 等于 C = C + A)
??-= 相減后再賦值 (C -= A 等于 C = C - A)
??*= 相乘后再賦值 (C *= A 等于 C = C * A)
??/= 相除后再賦值 (C /= A 等于 C = C / A)
??%= 求余后再賦值 (C %= A 等于 C = C % A)
??<<= 左移后賦值 (C <<= 2 等于 C = C << 2)
??>>= 右移后賦值 (C >>= 2 等于 C = C >> 2)
??&= 按位與后賦值 (C &= 2 等于 C = C & 2)
??^= 按位異或后賦值 (C ^= 2 等于 C = C ^ 2)
??|= 按位或后賦值 (C |= 2 等于 C = C | 2)
6、變量運(yùn)算符
??& 取變量的地址(&a將給出變量a的實(shí)際地址)
??* 取變量的指針( *a 是指向變量a的指針)
七洽洁、流程控制
1痘系、if-else
if condition1 {
} else if condition2 { //else 一定要和大括號在一行
} else if condition3 {
} else {
}
2、for
一般for循環(huán)
for i := 0; i < 100; i++ { //語句使用分號隔開
}
死循環(huán)(類似python中的while)
for true {
}
//或者
for {
}
使用range遍歷數(shù)組饿自、slice汰翠、map、chan等
// 索引循環(huán)
package main
import "fmt"
func main() {
str := "hello world"
for k := range str{ // 字符串遍歷
fmt.Printf("index=%d val=%c len=%d\n",k,str[k],len(str)) // index=0 val=h len=11...
}
}
// 同時(shí)循環(huán)index昭雌,val
package main
import "fmt"
func main(){
str := "hello world"
for index,val := range str{
fmt.Printf("index=%d val=%c len=%d\n",index,val,len(str)) // k代表索引复唤,v代表值
}
}
3、switch
??switch條件控制烛卧,當(dāng)條件匹配當(dāng)前case時(shí)候佛纫,執(zhí)行當(dāng)前case,不會(huì)進(jìn)行往下執(zhí)行总放,若都沒有匹配到呈宇,則執(zhí)行default,當(dāng)然也可以使用fallthrought關(guān)鍵字強(qiáng)制執(zhí)行下面語句间聊。
switch var {
case 條件一:
case 條件二:
case 條件三:
default:
}
// switch簡單示例
package main
import "fmt"
func main() {
var a = 0
switch a {
case 0:
fmt.Println("this is 0")
case 1:
fmt.Println("this is 1")
case 2:
fmt.Println("this is 2")
default:
fmt.Print("this is default")
}
}//結(jié)果 this is 0
// 多條件判斷case
package main
import "fmt"
func main() {
var a = 0
switch { //這里沒有變量
case a > 0 && a <3: //a in 0-3
fmt.Println("a in 0-3")
case a < -1 || a > 3:
fmt.Println("a <0 or a > 3")
case a == 0:
fmt.Println("a equal 0")
default:
fmt.Print("this is default")
} //結(jié)果 a equal 0
}
// 加入fallthrought
package main
import "fmt"
func main() {
var a = 0
switch {
case a > 0 && a <3: //a in 0-3
fmt.Println("a in 0-3")
case a < -1 || a > 3:
fmt.Println("a <0 or a > 3")
case a == 0:
fmt.Println("a equal 0")
fallthrough //使用fallthrought關(guān)鍵字當(dāng)滿足該條件時(shí)候攒盈,繼續(xù)執(zhí)行下面語句
default:
fmt.Printf("this is default\n")
}
//結(jié)果 a equal 0
//this is default
4、goto&label
label作為一個(gè)標(biāo)記哎榴,語法是字符串+冒號定義型豁,在循環(huán)中使用label可調(diào)至label的定義的位置。
package main
import (
"fmt"
"time"
)
func main() {
LABLE: //定義名稱為LABLE
//fmt.Println("回到label")
for i := 0; i < 10; i++ {
fmt.Println("-->",i)
for true {
I++
if i == 6 {
time.Sleep(time.Second * 5)
break LABLE //跳出循環(huán)到LABLE定義的地方
}
fmt.Println(i)
}
}
}
結(jié)果:--> 0
1
2
3
4
5
goto作用在于跳轉(zhuǎn)到label標(biāo)簽定義的地方繼續(xù)執(zhí)行尚蝌。上述代碼修改為goto迎变,將是死循環(huán)
package main
import (
"fmt"
"time"
)
func main() {
LABLE: //定義名稱為LABLE
//fmt.Println("回到label")
for i := 0; i < 10; i++ {
fmt.Println("-->",i)
for true {
I++
if i == 6 {
time.Sleep(time.Second * 5)
goto LABLE //跳出循環(huán)到LABLE定義的地方
}
fmt.Println(i)
}
}
} //結(jié)果:每5秒打印0,1飘言,2衣形,3,4姿鸿,5
八谆吴、函數(shù)
函數(shù)是go語言編程的核心。特點(diǎn):
??不支持重載苛预,一個(gè)包不能有兩個(gè)名稱一樣的函數(shù)句狼;
??函數(shù)也是一種類型,一個(gè)函數(shù)可以賦值給變量(該特性和python一樣)热某;
??函數(shù)也可以不用起名稱腻菇,這種函數(shù)稱為匿名函數(shù)胳螟;
??go語言函數(shù)可以有多個(gè)返回值;
??使用_標(biāo)識符可忽略返回值筹吐。
函數(shù)定義語法:
func 函數(shù)名(形參1 類型糖耸,形參2 類型,...)(返回值名1 類型丘薛,返回值名2 類型嘉竟,...){
函數(shù)體代碼...
return 返回值1,返回值2
}
// 函數(shù)作為變量
package main
import (
"fmt"
)
func add(a int,b int) int {
return a + b
}
func main() {
c := add
d := c(1,2) // 加括號代表調(diào)用函數(shù)
fmt.Println(d)
} // 結(jié)果:3
// 忽略返回值
package main
import (
"fmt"
)
func add(a int,b int) (int,int) {
return a + b,a
}
func main() {
c := add
d , _ := c(1,2) //使用_忽略第二個(gè)返回值
fmt.Println(d)
}//結(jié)果:3
1洋侨、函數(shù)參數(shù)傳遞方式
值傳遞:一般基本的數(shù)據(jù)類型都是值傳遞周拐,如數(shù)字、字符串凰兑。
引用傳遞:復(fù)雜的數(shù)據(jù)類型,如map审丘、chan吏够、interface,是引用傳遞滩报。
注意:無論是值傳遞還是引用傳遞锅知,傳遞給函數(shù)的都是變量的副本,不過值傳遞是值的拷貝脓钾,引用傳遞拷貝的是地址售睹,一般來說地址拷貝更為高效。而值拷貝取決于拷貝對象大小可训,對象越大昌妹,則性能相對低。
2握截、函數(shù)返回值命名
??go 函數(shù)的返回值或者結(jié)果參數(shù)可以指定一個(gè)名字(名字不是強(qiáng)制的)飞崖,并且像原始的變量那樣使用,就像輸入?yún)?shù)那樣谨胞。如果對其命名固歪,在函數(shù)開始時(shí),它們會(huì)用其類型的零值初始化胯努;如果函數(shù)在不加參數(shù)的情況下執(zhí)行了 return 語句牢裳,結(jié)果參數(shù)的當(dāng)前值會(huì)作為返回值返回。
package main
import "fmt"
//斐波那契數(shù)列實(shí)現(xiàn)
func Factorial(x int) (result int) { //給函數(shù)返回值命名為result
if x <= 1 {
result = 1
} else {
result =Factorial(x - 2) + Factorial(x - 1)
}
return
}
func main() {
b := Factorial(4)
fmt.Println(b)
}//結(jié)果:5
3叶沛、匿名函數(shù)
匿名函數(shù)(對比javascript)即沒有函數(shù)名的函數(shù)蒲讯,只能放在函數(shù)中,可以實(shí)現(xiàn)函數(shù)嵌套定義的功能恬汁。
package main
import "fmt"
func main() {
c := func(a int,b int ) int {
return a + b
}(3,4)
fmt.Println(c)
} // 結(jié)果:7
4伶椿、函數(shù)的可變參數(shù)
go語言中辜伟,也可以像python一樣傳遞可變參數(shù)(意思是傳遞時(shí)候才確定有幾個(gè)參數(shù),定義時(shí)候不知道參數(shù)個(gè)數(shù))脊另,可以認(rèn)為參數(shù)是數(shù)組形式:
func funcname(arg...type) { }
package main
import "fmt"
func add(a int, arg...int) int {
var sum = 0
for i := 0; i < len(arg); i++ {
sum += arg[i] //取參數(shù)的值
}
return sum
}
func main() {
c := add(1) //傳遞一個(gè)參數(shù)
d := add(2,3,4) //傳遞兩個(gè)參數(shù)
fmt.Println(c,d)
}//結(jié)果:0,7
5导狡、defer
當(dāng)函數(shù)中存在defer關(guān)鍵字時(shí)候,一旦函數(shù)返回則執(zhí)行defer語句偎痛,因此旱捧,defer可以做一些資源清理的事情。
特性:
?多個(gè)defer語句踩麦,按照后進(jìn)先出的方式執(zhí)行(棧)枚赡。
?defer語句中的變量,在defer聲明時(shí)候就決定了谓谦。
package main
import "fmt"
func main() {
i := 0
defer fmt.Println(i) // 放入棧
I++
defer fmt.Println(i) //在放入棧贫橙,先執(zhí)行
fmt.Println("wpr")
return
}//結(jié)果:wpr ,1,0
6、命令行參數(shù)
概念:
?命令行參數(shù)(或參數(shù)):是指運(yùn)行程序時(shí)提供的參數(shù)反粥。
?已定義命令行參數(shù):是指程序中通過 flag.Xxx 等這種形式定義了的參數(shù)卢肃。輸入?yún)?shù)時(shí)需要 -flag 形式。
?非 flag(non-flag)命令行參數(shù)(或保留的命令行參數(shù)):不符合 -flag 形式的參數(shù)才顿。- 莫湘、--和 --flag 都屬于 non-flag 參數(shù)。
來自命令行的參數(shù)在程序中通過os.Args(對比python的sys.agrv) 獲取郑气,導(dǎo)入包 os 即可幅垮。其中 os.Args[0] 為執(zhí)行的程序名,os.Args[1] ~ os.Args[n-1] 是具體的參數(shù)尾组。
//示例
test.exe 1 2
//test.exe 為os.Args[0] 忙芒,1是 os.Args[1],2是os.Args[2]
7演怎、go語言的初始化順序
初始化順序規(guī)則:引入的包 —> 包中的常量匕争、變量、init —> 當(dāng)前包的常量爷耀、變量甘桑、init —> main函數(shù)
注意事項(xiàng):
- 當(dāng)前go源文件中, 每一個(gè)被Import的包歹叮, 按其在源文件中出現(xiàn)順序初始化跑杭。
- 如果當(dāng)前包有多個(gè)init在不同的源文件中, 則按源文件名以字典順序從小到大排序咆耿,小的先被執(zhí)行到德谅, 同一包且同一源文件中的init,則按其出現(xiàn)在文件中的先后順序依次初始化; 當(dāng)前包的package level變量常量也遵循這個(gè)規(guī)則萨螺; 其實(shí)準(zhǔn)確來說窄做,應(yīng)是按提交給編譯器的源文件名順序?yàn)闇?zhǔn)愧驱,只是在提交編譯器之前, go命令行工具對源文件名按字典序排序了椭盏。
- init只可以由go runtine自已調(diào)用组砚, 我們在代碼中不可以顯示調(diào)用,也不可以被引用掏颊,如賦給a function variable糟红。
- 包A 引入包B , 包B又引入包C, 則包的初始化順序?yàn)椋?C -> B -> A
- 引入包,必須避免死循環(huán)乌叶,如 A 引 B , B引C, C引A
- 一個(gè)包被其它多個(gè)包引入盆偿,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次准浴。
- 另一個(gè)大原則事扭, 被依賴的總是先被初始化,main包總是被最后一個(gè)初始化乐横,因?yàn)樗偸且蕾噭e的包句旱。