前段時間準(zhǔn)備面試国拇,學(xué)院里面考試以及家里面有些事情,還有我懶,耽擱了怀各。
現(xiàn)在一切就緒,可以開新坑了~嘿嘿嘿
大部分資料都源自于互聯(lián)網(wǎng) 但是大部分都是自己手打(侵刪)
首先確保各位的GOPATH的版本大于等于1.8 保證環(huán)境的一致性
本人也是初學(xué)go語言术浪,如果有錯誤希望各位指正瓢对。
基本程序
首先我們先寫一個最最最簡單的hello word吧
package main //包,表明代碼所在的模塊(包)
import "fmt" //引?代碼依賴
//功能實現(xiàn)
func main() {
fmt.Println("Hello World!")
}
以上胰苏,我們很容易看出(如果你有其他語言的基礎(chǔ)的話)程序的入口
1.一定是在main包下:也就是package main //包硕蛹,表明代碼所在的模塊(包)
2.必須要是在main方法:func main()
- 其實文件名不一定是main.go
此時肯定會有人問,既然是函數(shù)肯定會有返回值啦~
比如c語言默認(rèn)會追加一個return
對硕并,有道理但是
Go 中 main 函數(shù)不?持任何返回值
如果你需要有返回狀態(tài)法焰,你可以通過
通過 os.Exit 來返回狀態(tài)
常量和變量
變量
接觸常量和變量的時候我們先規(guī)定以后編寫代碼的時候用test程序
也就是
源碼?件以 _test 結(jié)尾:xxx_test.go
測試?法名以 Test 開頭:func TestXXX(t *testing.T) {…}
這樣就不用重復(fù)開很多項目了(真棒)
和java的junit單元測試一樣好用,還能打印日志
先寫一個斐波那契數(shù)列吧
func TestFibList(t *testing.T) {
// var a int = 1
// var b int = 1
// var (
// a int = 1
// b = 1
// )
a := 1
// a := 1
b := 1
t.Log(a)
for i := 0; i < 5; i++ {
t.Log(" ", b)
tmp := a
a = b
b = tmp + a
}
}
變量有多種賦值方式(在上面)
? 賦值可以進(jìn)??動類型推斷
? 在?個賦值語句中可以對多個變量進(jìn)?同時賦值
注意 a := 1 只能用于局部變量倔毙,不能作用于全局變量
常量
const: 關(guān)鍵字 使得被起修飾的變量可讀不可寫 保證其狀態(tài)
同時:iota (偷懶的寫法 如果不愿意一直+1 這個可以替代) 我暫時還沒用它寫過復(fù)雜的程序
基本數(shù)據(jù)類型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr(正數(shù))
byte // alias for uint8
rune // alias for int32,represents a Unicode code point
float32 float64
complex64 complex128
類型轉(zhuǎn)化
- Go 語?不允許隱式類型轉(zhuǎn)換
- 別名和原有類型也不能進(jìn)?隱式類型轉(zhuǎn)換
func TestImplicit(t *testing.T) {
var a int32 = 1
var b int64
b = int64(a)
var c MyInt
c = MyInt(b)
t.Log(a, b, c)
}
以上是顯示類型轉(zhuǎn)換 如果直接b=a就會報錯
所以編程的過程中需要小心
指針類型
- 不?持指針運算(所以別想著指針操作下一個數(shù)組單位)
- string 是值類型埃仪,其默認(rèn)的初始化值為空字符串,?不是 nil
func TestPoint(t *testing.T) {
a := 1
aPtr := &a
//aPtr = aPtr + 1
t.Log(a, aPtr)
t.Logf("%T %T", a, aPtr)
}
運算符
就不多提 有編程經(jīng)驗的直接跳過
不過這塊著重一點就是 Go 語?沒有前置的 ++陕赃,- -
(作用的話卵蛉,我猜應(yīng)該是增加代碼的可讀性吧)
循環(huán)
也不多提
Go 語?僅?持循環(huán)關(guān)鍵字 for
n := 0
for n < 5 {
n++
fmt.Println(n)
}
怎么感覺好像更難讀了1?不過沒事,習(xí)慣就好了
switch條件
- 條件表達(dá)式不限制為常量或者整數(shù)么库;
- 單個 case 中傻丝,可以出現(xiàn)多個結(jié)果選項, 使?逗號分隔;
- 與 C 語?等規(guī)則相反诉儒,Go 語?不需要?break來明確退出?個 case葡缰;如果需要執(zhí)行下一個case的話 可以加fallthrough關(guān)鍵字
- 可以不設(shè)定 switch 之后的條件表達(dá)式,在此種情況下忱反,整個 switch 結(jié)
構(gòu)與多個 if…else… 的邏輯作?等同
數(shù)組和切片
這個很重要 以后有機(jī)會開個新坑形容一下底層原理
數(shù)組的聲明
var a [3]int //聲明并初始化為默認(rèn)零值
a[0] = 1
b := [3]int{1, 2, 3} //聲明同時初始化
元素的遍歷
func TestTravelArray(t *testing.T) {
a := [...]int{1, 2, 3, 4, 5} //不指定元素個數(shù)
for idx/*索引*/, elem/*元素*/ := range a {
fmt.Println(idx, elem)
}
//當(dāng)然 你要是不想要ind(索引的話)可以變?yōu)開 不使用它就行
}
數(shù)組的截取
a[開始索引(包含), 結(jié)束索引(不包含)]
(簡單來記就是左閉右開)
a := [...]int{1, 2, 3, 4, 5}
a[1:2] //2
a[1:3] //2,3
a[1:len(a)] //2,3,4,5
a[1:] //2,3,4,5
a[:3] //1,2,3
![X~VMAEVUTITZ3TFJ_O%2)6.png
這個挺復(fù)雜的后面詳細(xì)講泛释,初學(xué)者會使用就行
切片的聲明
var s0 []int
s0 = append(s0, 1)
s := []int{}
s1 := []int{1, 2, 3}
s2 := make([]int, 2, 4)
/*[]type, len, cap
其中l(wèi)en個元素會被初始化為默認(rèn)零值,未初始化元素不可以訪問
*/
切?共享存儲結(jié)構(gòu)
數(shù)組和切片的區(qū)別
- 容量是否可伸縮
- 是否可以進(jìn)??較
Map基礎(chǔ)
本節(jié)也很重要 不過作為初學(xué)者只要掌握使用即可
Map 聲明
m := map[string]int{"one": 1, "two": 2, "three": 3}
m1 := map[string]int{}
m1["one"] = 1
m2 := make(map[string]int, 10 /*Initial Capacity*/)
Map還有一個很有意思的點温算,就是假如我設(shè)置一個key但是并未設(shè)置value 那么這個value設(shè)置為0值
此時 好處當(dāng)然就是防止空指針異常呀
但是壞處也有怜校,就是我這么知道這個key的value是真的為空還是就為0?
以下代碼中 ok 為true存在 否則不存在
func TestAccessNotExistingKey(t *testing.T) {
m1 := map[int]int{}
t.Log(m1[1])
m1[2] = 1
t.Log(m1[2])
m1[1] = 2
if v, ok := m1[3]; ok {
t.Logf("Key 3's value is %d", v)
} else {
t.Log("key 3 is not existing.")
}
}
遍歷map
func TestTravelMap(t *testing.T) {
m1 := map[int]int{1: 1, 4: 4, 3: 9}
for k, v := range m1 {
t.Log(k, v)
}
}
Map的擴(kuò)展
Map 與??模式
? Map 的 value 可以是?個?法
? 與 Go 的 Dock type 接??式?起米者,可以?便的實現(xiàn)單??法對象的??模式
實現(xiàn) Set
Go 的內(nèi)置集合中沒有 Set 實現(xiàn)韭畸, 可以 map[type]bool
- 元素的唯?性
- 基本操作
- 添加元素
- 判斷元素是否存在
- 刪除元素
- 元素個數(shù)
func TestMapWithFunValue(t *testing.T) {
m := map[int]func(op int) int{}
m[1] = func(op int) int { return op }
m[2] = func(op int) int { return op * op }
m[3] = func(op int) int { return op * op * op }
t.Log(m[1](2), m[2](2), m[3](2))
}
用Map實現(xiàn)set
func TestMapForSet(t *testing.T) {
mySet := map[int]bool{}
mySet[1] = true
n := 3
if mySet[n] {
t.Logf("%d is existing", n)
} else {
t.Logf("%d is not existing", n)
}
mySet[3] = true
t.Log(len(mySet))
delete(mySet, 1)
n = 1
if mySet[n] {
t.Logf("%d is existing", n)
} else {
t.Logf("%d is not existing", n)
}
}
string
- string 是數(shù)據(jù)類型宇智,不是引?或指針類型
- string 是只讀的 byte slice蔓搞,len 函數(shù)可以它所包含的 byte 數(shù)
- string 的 byte 數(shù)組可以存放任何數(shù)據(jù)
函數(shù)
- 可以有多個返回值
- 所有參數(shù)都是值傳遞:slice,map随橘,channel 會有傳引?的錯覺
- 函數(shù)可以作為變量的值
- 函數(shù)可以作為參數(shù)和返回值
func returnMultiValues() (int, int) {
return rand.Intn(10), rand.Intn(20)
}
func timeSpent(inner func(op int) int) func(op int) int {
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent:", time.Since(start).Seconds())
return ret
}
}
func slowFun(op int) int {
time.Sleep(time.Second * 1)
return op
}
func TestFn(t *testing.T) {
a, _ := returnMultiValues()
t.Log(a)
tsSF := timeSpent(slowFun)
t.Log(tsSF(10))
}
可變參數(shù)
func sum(ops ...int) int {
s := 0
for _, op := range ops {
s += op
}
return s
}
最終拿到的就是所有參數(shù)之和
defer 函數(shù)
func TestDefer(t *testing.T) {
defer func() {
t.Log("Clear resources")
}()
t.Log("Started")
panic("Fatal error”) //defer仍會執(zhí)?
}
?向?qū)ο缶幊?/h1>
結(jié)構(gòu)體定義
type Employee struct {
Id string
Name string
Age int
}
實例創(chuàng)建及初始化
e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
e2 := new(Employee) //注意這?返回的引?/指針喂分,相當(dāng)于 e := &Employee{}
e2.Id = “2" //與其他主要編程語?的差異:通過實例的指針訪問成員不需要使?->
e2.Age = 22
e2.Name = “Rose"
//第?種定義?式在實例對應(yīng)?法被調(diào)?時,實例的成員會進(jìn)?值復(fù)制
func (e Employee) String() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
//通常情況下為了避免內(nèi)存拷?我們使?第?種定義?式
func (e *Employee) String() string {
return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age)
}
定義交互協(xié)議
接?與依賴
用java實現(xiàn)
Programmer.java
public interface Programmer {
String WriteCodes() ;
}
GoProgrammer.java
public class GoProgrammer implements Programmer {
@Override
public String WriteCodes() {
return "fmt.Println(\"Hello World\")";
} }
Task.java
public class Task{
public static void main(String[] args) {
Programmer prog = new GoProgrammer();
String codes = prog.WriteCodes();
System.out.println(codes);
} }
定義 實現(xiàn) 調(diào)用接口的過程
但是在go語言中机蔗,我們可以使用鴨子類型
接?定義
type Programmer interface {
WriteHelloWorld()
}
接?實現(xiàn)
type GoProgrammer struct {
}
func (p *GoProgrammer) WriteHelloWorld() Code {
return "fmt.Println(\"Hello World!\")"
}
我們發(fā)現(xiàn)這個是通過組合的方式完成接口的實現(xiàn)的
Go 接?
與其他主要編程語?的差異
- 接?為??侵性蒲祈,實現(xiàn)不依賴于借?定義
- 所以接?的定義可以包含在接?使?者包內(nèi)
接?變量
自定義類型
就自定義的 比如struct 然后就能當(dāng)做一個特殊類型使用(這個有c語言基礎(chǔ)能理解)
擴(kuò)展與復(fù)?
復(fù)合
與其他主要編程語?的差異
Go 不?持繼承甘萧,但可以通過復(fù)合的?式來復(fù)?
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Println("...PET")
}
func (p *Pet) SpeakTo(host string) {
p.Speak()
fmt.Println(" ", host)
}
type Dog struct {
Pet
}
func (d *Dog) Speak() {
fmt.Println("Wang!")
}
func TestDog(t *testing.T) {
dog := new(Dog)
dog.SpeakTo("Chao")
}
可以通過組合對程序進(jìn)行繼承
匿名類型嵌?
與其他主要編程語?的差異
它不是繼承,如果我們把“內(nèi)部 struct ”看作?類梆掸,把“外部 struct” 看作?類扬卷,
會發(fā)現(xiàn)如下問題:
- 不?持?類替換
- ?類并不是真正繼承了?類的?法
? ?類的定義的?法?法訪問?類的數(shù)據(jù)和?法
多態(tài)
空接?與斷?
- 空接?可以表示任何類型
- 通過斷?來將空接?轉(zhuǎn)換為制定類型
v, ok := p.(int) //ok=true 時為轉(zhuǎn)換成功
Go 接?最佳實踐
傾向于使??的接?定義,很多接
?只包含?個?法
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
較?的接?定義酸钦,可以由多個?接
?定義組合?成
type ReadWriter interface {
Reader
Writer
}
只依賴于必要功能的最?接?
func StoreData(reader Reader) error {
…
}
Go 的錯誤機(jī)制
- 沒有異常機(jī)制
- error 類型實現(xiàn)了 error 接?
type error interface {
Error() string
}
- 可以通過 errors.New 來快速創(chuàng)建錯誤實例
errors.New("n must be in the range [0,100]")
panic
? panic ?于不可以恢復(fù)的錯誤
? panic 退出前會執(zhí)? defer 指定的內(nèi)容
panic vs. os.Exit
? os.Exit 退出時不會調(diào)? defer 指定的函數(shù)
? os.Exit 退出時不輸出當(dāng)前調(diào)?棧信息
recover
Java
try{
…
}catch(Throwable t){
}
C++
try{
…
}catch(…){
}
recover
defer func() {
if err := recover(); err != nil {
//恢復(fù)錯誤
}
}()
init ?法
? 在 main 被執(zhí)?前怪得,所有依賴的 package 的 init ?法都會被執(zhí)?
? 不同包的 init 函數(shù)按照包導(dǎo)?的依賴關(guān)系決定執(zhí)?順序
? 每個包可以有多個 init 函數(shù)
? 包的每個源?件也可以有多個 init 函數(shù),這點?較特殊
Thead vs. Groutine
- 創(chuàng)建時默認(rèn)的 stack 的??
? JDK5 以后 Java Thread stack 默認(rèn)為1M
? Groutine 的 Stack 初始化??為2K - 和 KSE (Kernel Space Entity) 的對應(yīng)關(guān)系
? Java Thread 是 1:1
? Groutine 是 M:N
![UXJQWHW]2OSZG67]IKT~W00.png](https://upload-images.jianshu.io/upload_images/24046024-5f98801ae75eeb49.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)