go語言之結構體、跨平臺編譯、方法
一、結構體
1.定義(type 結構體名字 struck{})
一系列屬性的集合
2、建立結構體
基本使用
可以在包外建立,然後引用,注意欄位名首字母大寫才能從包外引用。此時在目錄下建立一個entity資料夾並在裡面建立.go指令碼寫結構體,然後建立和entity同一個級別的.go指令碼使用entity裡面的結構體
//在entity下的.go指令碼建立結構體 package entity //在包中定義結構體,建立命名結構體 type Person struct { Name string Age int Sex string }
包外使用結構體
package main import ( "fmt" "go/day2/strcture/entity" ) //使用結構體 //已經在entity定義了結構體 func main() { //結構體的使用 //var per entity.Person //表示宣告一個變數,型別為結構體裡面的Person結構體型別 //fmt.Println(per) //per.Name = "aaa" //per.Age = 18 //fmt.Println(per) //定義並賦值 //var per entity.Person = entity.Person{Name: "aaa"}//不按位置,就會少傳 //var per1 entity.Person = entity.Person{"aaa",18,"男"} //按位置,可以全傳 //fmt.Println(per1) }
匿名結構體:匿名結構體(定義在內部,只使用一次),用於當定義多個變數時,並且在一次使用
package main import "fmt" func main() { //匿名結構體(定義在內部,只使用一次) //用於1.當定義多個變數時,並且在一次使用 a := struct { HobbyId int64 HobbyNamestring }{HobbyId: 1,HobbyName: "球"} fmt.Println(a.HobbyName) a.HobbyName = "足球" fmt.Println(a.HobbyName) }
結構體零值:是值型別,copy傳遞,在函式中修改不會影響原來的,只有引用欄位影響
package main import ( "fmt" "go/day2/strcture/entity" ) func main() { //結構體零值,值型別,在函式中修改不會影響原來的 var per entity.Person fmt.Println(per) //屬性零值,值型別,引數傳遞,copy傳遞,在函式中修改不會影響原來的 { 0 } test3(per) fmt.Println(per) //{ 0 } 此時並未對函式造成影響 //訪問結構體欄位:結構體.欄位 (注意欄位大小寫) } func test3(per entity.Person) { per.Age=99 fmt.Println(per) // { 99 } }
結構體指標
package main import ( "fmt" "go/day2/strcture/entity" ) func main() { //結構體指標 //var per *entity.Person //fmt.Println(per) //指標零值<nil> //結構指標定義並初始化 var per *entity.Person = &entity.Person{} fmt.Println(per) //&{ 0 } //把per的名字欄位改 (*per).Name = "bbb" //支援直接使用(陣列也一樣) //per.Name = "bbb" fmt.Println(per) //&{bbb 0 } }
匿名欄位:用於欄位提升。欄位匿名(如下面結構體就寫了型別string和int,前面沒有欄位名),型別就是欄位名
package main import "fmt" //定義一個結構體,內涵匿名欄位,匿名欄位型別就是欄位名,所以型別不能重複,此時Sex就不能去掉。可做變數提升(面向物件的繼承) type Person1 struct { string int Sex string }
func main() { //匿名欄位,欄位沒名字只有型別 per := Person1{"aaa",18,"男"} //欄位匿名,型別就是欄位名 fmt.Println(per) }
結構體巢狀
package main import "fmt" //結構體巢狀 type Person2 struct { Name string Age int Sex string Hobby //此時Hobby為匿名欄位,但其前面也可以加屬性名,如下 ////Hobby Hobby Hobby Hobby巢狀下面的結構體Hobby代表屬性名,Hobby代表型別,巢狀Hobby結構體 } type Hobby struct { HobbyId int HobbyName string } func main() { //結構體巢狀 //var per4 Person2 = Person2{"aaa",18,"男",Hobby{1,"球球"}} //巢狀的結構體直接用花括號傳就是 var per4 Person2 = Person2{Name:"aaa",Age:18,Sex: "男",Hobby:Hobby{HobbyId: 1,HobbyName: "球球"}} //直接指明引數傳 fmt.Println(per4.Name) fmt.Println(per4.Hobby.HobbyName) }
欄位提升
package main import "fmt" //結構體巢狀 type Person3 struct { Name string Age int Sex string Hobby //Hobby Hobby巢狀下面的結構體Hobby代表屬性名,Hobby代表型別,巢狀Hobby結構體 } type Hobby struct { HobbyId int HobbyName string } func main() { //欄位提升 var per Person3 = Person3{Name:"aaa",Age:18,Sex: "男",Hobby:Hobby{HobbyId: 1,HobbyName: "球球"}} //列印愛好的名字(Hobby是一個匿名欄位,會提升和第一個巢狀體一樣的層面) //由於提升Hobby下面兩種列印結果一樣 fmt.Println(per.HobbyName) fmt.Println(per.Hobby.HobbyName) }
結構體相等性
package main import "fmt" //結構體相等性 type Person5 struct { Name string Age int Sex string //包含不可比較欄位 //c []int } func main() { //結構體相等性,如果結構體每欄位都是可比較的(值型別),則該結構體可以比較,如果包含不可比較的(引用型別),則不可比較 per1 := Person5{Name: "aaa"} per2 := Person5{Name: "aaa"} fmt.Println(per1==per2) //未包含引用型別true fmt.Println(per1==per2) //包含引用型別直接報錯 }
二、跨平臺編譯
直接在程式碼終端輸入命令
windows編譯mac和Linux64可執行檔案
SET CGO_ENABLED=0 SET GOOS=darwin SET GOARCH=amd64 go build main.go SET CGO_ENABLED=0 SET GOOS=linux SET GOARCH=amd64 go build main.go
mac編譯Windows和liunx
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
Linux編譯mac和windows
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
三、方法
特殊函式,在函式的基礎上加了一些東西,
在 func 這個關鍵字和方法名中間加入了一個特殊的接收器型別,接收器可以是結構體型別,也可以是非結構體型別
1、定義一個方法:
package main import "fmt" type Person2 struct { Name string Age int Sex string } //定義一個方法 : (p Person2) 繫結給了Person2結構體的物件 func (p Person2) printName() { //在方法內可以使用p fmt.Println(p.Name) } func main() { per := Person2{} per.Name = "aaa" per.printName() //繫結給物件的方法,相當於printName()是Person2的方法.方法的特殊之處在於自動傳值 }
有值方法(值型別,值接收器)
package main import "fmt" type Person2 struct { Name string Age int Sex string } //有值的方法(p此時為值型別,改動不會對原資料修改) func (p Person2) ChangeName(name string) { //此時就修改name,呼叫Person2{}的方法ChangeName,就會傳值給name p.Name = name fmt.Println(name) //bbb } func main() { per := Person2{Name: "aaa"} per.ChangeName("bbb") //傳入要改的值 fmt.Println(per) //並沒有修改 }
有值方法(引用型別,指標接收器)
package main import "fmt" type Person2 struct { Name string Age int Sex string } //有值的方法(p此時為值型別,改動不會對原資料修改) func (p *Person2) ChangeAge(age int) { //此時就修改name,呼叫Person2{}的方法ChangeName,就會傳值給name p.Age = age fmt.Println(age) //19 //fmt.Println((*p).Age) 這個方法也可以,但是指標此時可以直接使用,所以推薦上面這種方法 } func main() { per := Person2{Age: 18} per.ChangeAge(19) //傳入要改的值 fmt.Println(per) //此時已經修改,因為指標型別為引用型別,一變全變 { 19 } }
注意:二者使用場景,當拷貝結構體代價過於昂貴,考慮下一個結構體有很多欄位,在方法內使用這個結構體作為值接收器需要拷貝這個結構體,這種情況下使用指標。指標用得多
2、匿名欄位方法:方法提升
package main import "fmt" //匿名欄位的方法 type Person2 struct { Name string Age int Sex string Hobby //匿名欄位 } type Hobby struct { Id int Name string } //給結構體繫結方法 func (p Person2)printName() { fmt.Println(p.Name) } //func (h Hobby)printHobbyName() { func (h Hobby)printName() { fmt.Println(h.Name) } func main() { per := Person2{Name: "AAA",Hobby:Hobby{Name: "足球"}} fmt.Println(per.Name) //AAA //per.printHobbyName() //hobby是匿名欄位,方法也提升了 足球 //如果方法重名了,優先使用結構體自己的 per.printName() //AAA per.Hobby.printName() //足球 }
3、在方法中使用值接收器 與 在函式中使用值引數
package main import "fmt" type Person2 struct { Name string Age int Sex string } //在方法中使用值接收器 func (p Person2)printName() { fmt.Println(p.Name) //aaa } func (p Person2)changeName(name string) { p.Name=name fmt.Println(p) } //在函式中使用值引數 func printName(p Person2) { fmt.Println(p.Name) } func main() { per1:=Person2{Name: "aaa"} per1.printName() printName(per1) //aaa //per1:=&Person2{Name: "aaa"} //per1是個指標 //per1.printName() //printName(*per1) //小研究 //per1:=&Person2{Name: "aaa"} //per1是個指標 //per1.changeName("bbb") //fmt.Println(per1) //值收器:可以用值來調,也可以用指標來調 //函式的值引數,只能傳值 }
4、在方法中使用指標收器 與 在函式中使用指標引數
package main import "fmt" //7 在方法中使用指標接收器 與 在函式中使用指標引數 type Person2 struct { Name string Age int Sex string } //在方法中使用值接收器 func (p *Person2)printName() { fmt.Println(p.Name) } func (p *Person2)changeName(name string) { p.Name=name fmt.Println(p) } //在函式中使用指標引數 func printName(p *Person2) { //fmt.Println((*p).Name) fmt.Println(p.Name) } func main() { per1:=Person2{Name: "aaa"} per1.printName() //值可以來呼叫 printName(&per1) //per1:=&Person2{Name: "aaa"} //per1.printName() //指標可以來呼叫 //printName(per1) //小研究 //per1:=Person2{Name: "aaa"} //per1.changeName("bbb") //fmt.Println(per1) //per1:=&Person2{Name: "aaa"} //per1.changeName("bbb") //fmt.Println(per1)} }
對3和4總結:不管是值型別接收器還是指標型別接收器,都可以用值來呼叫,或者指標來呼叫。
不管是值還是指標來呼叫,只要是值型別接收器,改的就是新的,只要是指標型別接收器,改的是原來的
5、非結構體繫結方法(只能是自己定義的非結構體)
package main import "fmt" //8 非結構體上的方法(不允許)自己定義的型別可以繫結方法 //在int型別上繫結一個add方法 //不允許 //func (i int)add(){ // i=i+1 // i++ // //i+=1 //} // 可以在自定義的型別上繫結方法 type Myint int //定義自己的非結構體 //給自己定義的非結構體繫結方法add() func (i *Myint)add(){ (*i)=(*i)+1 //i++ //i+=1 } func main() { //8 非結構體上繫結方法 var a Myint =10 fmt.Println(a) a.add() a.add() a.add() a.add() fmt.Println(a) //var b =11 ////fmt.Println(a+b) //型別不匹配 // //c:=a+Myint(b) //fmt.Println(a+Myint(b)) //型別匹配 //d:=int(a)+b //fmt.Println(int(a)+b) //型別匹配 }
四、介面(type 介面名字 interface{})
go也是鴨子型別:我現在有個鴨子類,內有speak方法 有run方法, 子類只要實現了speak和run,我就認為子類是鴨子
1、定義
面向物件的領域裡,介面一般這樣定義:介面定義一個物件的行為,規範子類物件的行為。
是一系列方法的集合(規範行為)
2、定義介面
package main import "fmt" type Duck interface { //speak(name string)(int) //speak()方法,第一個括號為引數,第二個括號為返回值型別.若沒有就不寫,只寫一個括號即可 speck() //speak()方法 run() } //定義一個普通鴨子結構體 type PDuck struct { name string sex string age int } //定義一個唐老鴨結構體 type TDuck struct { name string sex string age int wife string } //讓唐老鴨和普通鴨子都實現Duck介面 //結構體實現一個介面:只要繫結介面中的所有方法,就叫實現該介面 func (p PDuck)speak() { fmt.Println("普通鴨子嘎嘎叫,普通鴨子名字叫",p.name) } func (p PDuck)run() { fmt.Println("普通鴨子歪歪走,普通鴨子名字叫",p.name) } //唐老鴨也實現Duck介面 func (p TDuck)speak() { fmt.Println("唐老鴨說話,唐老鴨子名字叫",p.name) } func (p TDuck)run() { fmt.Println("唐老鴨人路,唐老鴨子名字叫",p.name) } func main() { //1 得到一個普通鴨子物件 pduck:=PDuck{"狗子","男",1} pduck.run() pduck.speak() ////2 得到一個堂老鴨子物件 tduck:=TDuck{"aaa","男",1,"小劉"} tduck.run() tduck.speak() //侵入式介面(介面沒了,子類報錯)和非侵入是介面(介面沒了,不影響程式碼,go語言中的介面是非侵入式的) }
3、把介面型別轉成struct屬性,(這樣自有方法也可以使用)
package main import "fmt" type Duck interface { //speak(name string)(int) //speak()方法,第一個括號為引數,第二個括號為返回值型別.若沒有就不寫,只寫一個括號即可 //speck() //speak()方法 run() } //定義一個普通鴨子結構體 type PDuck struct { name string sex string age int } //定義一個唐老鴨結構體 type TDuck struct { name string sex string age int wife string } //讓唐老鴨和普通鴨子都實現Duck介面 //結構體實現一個介面:只要繫結介面中的所有方法,就叫實現該介面 func (p PDuck)speak() { fmt.Println("普通鴨子嘎嘎叫,普通鴨子名字叫",p.name) } func (p PDuck)run() { fmt.Println("普通鴨子歪歪走,普通鴨子名字叫",p.name) } //唐老鴨也實現Duck介面 func (p TDuck)speak() { fmt.Println("唐老鴨說話,唐老鴨子名字叫",p.name) } func (p TDuck)run() { fmt.Println("唐老鴨走路,唐老鴨子名字叫",p.name) } func main() { //型別斷言(一般不用這個) //var duck Duck =TDuck{"aaa","男",1,"小劉"} ////斷言是TDuck型別 ////v, ok := duck.(TDuck) //////斷言成功,ok是true,v就是TDuck結構體物件 ////fmt.Println(v) ////fmt.Println(v.name) ////fmt.Println(ok) // ////斷言失敗 //var v PDuck //var ok bool //v, ok = duck.(PDuck) ////斷言失敗,ok是false,v是PDuck型別的空置,因為沒有複製 //fmt.Println(ok) //fmt.Println(v) //一般採用Switch,基於型別斷言來做的 var duck Duck =TDuck{"aaa","男",1,"小劉"} //var duck Duck =PDuck{"aaa","男",1} test4(duck) } //使用switch,選擇成功,拿到結構體物件 func test4(duck Duck) { switch v:=duck.(type) { case PDuck: fmt.Println(v.name) //此時判斷的v後,即可拿到v的結構體物件,就可以使用其對應的方法。如v.name fmt.Println("我是普通鴨子") case TDuck: fmt.Println(v.wife) fmt.Println("我是唐老鴨") default: fmt.Println(v) fmt.Println("我是鴨子這個類") } }
4、空介面
package main import "fmt" type Empty interface { } func main() { //空介面(沒有任何方法,所有資料型別都實現了空介面) var a int=10 var b string="aaa" var c [3]int var e Empty //空介面型別 e=a e=b e=c fmt.Println(e) fmt.Println(1,"xxx") test5(a) test5(b) test5(c) //匿名空介面 test6(10) test6("lll") //var duck TDuck =TDuck{"aaa","男",1,"小劉"} 上面程式碼的鴨子型別也可以傳給匿名空介面 //test6(duck) //8 之前學過的集合,切片等型別,都可以放介面型別 //var a[3]Duck //a[1]=PDuck{} //a[2]=TDuck{} //var a map[string]interface{}= make(map[string]interface{}) 集合 //a["name"]="lqz" //a["age"]=19 //a["duck"]=PDuck{} } //因為是空介面,所以傳a,b,c都可以,2.0出了泛型。 func test5(z Empty) { switch v:=z.(type) { case string: fmt.Println("我是字串") fmt.Println(v) case int: fmt.Println("我是int") fmt.Println(v) case [3]int: fmt.Println("我是陣列") fmt.Println(v) } } func test6(b interface{}) { fmt.Println(b) }
介面高階應用:
1、實現多個介面
package main import "fmt" //1 實現多個介面 type Duck interface { speak() //speak()方法 run() } type Animal interface { eat() sleep() } //定義一個普通鴨子結構體 type PDuck struct { name string sex string age int } //定義一個唐老鴨結構體 type TDuck struct { name string sex string age int wife string } //讓唐老鴨和普通鴨子都實現Duck介面 //結構體實現一個介面:只要繫結介面中的所有方法,就叫實現該介面 func (p PDuck)speak() { fmt.Println("普通鴨子嘎嘎叫,普通鴨子名字叫",p.name) } func (p PDuck)run() { fmt.Println("普通鴨子歪歪走,普通鴨子名字叫",p.name) } //唐老鴨也實現Duck介面 func (p TDuck)speak() { fmt.Println("唐老鴨說話,唐老鴨子名字叫",p.name) } func (p TDuck)run() { fmt.Println("唐老鴨走路,唐老鴨子名字叫",p.name) } //唐老鴨實現Animal介面 func (p TDuck)sleep() { fmt.Println("唐老鴨說人話,唐老鴨子名字叫",p.name) } func (p TDuck)eat() { fmt.Println("唐老鴨人走路,唐老鴨子名字叫",p.name) } func main() { //1 實現多個介面 var t TDuck=TDuck{} var a Animal var d Duck //一旦轉到某個介面上,只能使用介面的方法,自身屬性和自身方法需要型別斷言後才能使用 a=t //只能呼叫到a介面的方法 d=t //只能呼叫到d介面的方法 }
2、介面巢狀
package main import "fmt" //介面巢狀 type Duck interface { Animal speak() //speak()方法 run() } type Animal interface { eat() sleep() } //定義一個普通鴨子結構體 type PDuck struct { name string sex string age int } //讓唐老鴨和普通鴨子都實現Duck介面 //結構體實現一個介面:只要繫結介面中的所有方法,就叫實現該介面 func (p PDuck)speak() { fmt.Println("普通鴨子嘎嘎叫,普通鴨子名字叫",p.name) } func (p PDuck)run() { fmt.Println("普通鴨子歪歪走,普通鴨子名字叫",p.name) } //普通鴨實現Animal介面 func (p PDuck)sleep() { fmt.Println("唐老鴨說人話,唐老鴨子名字叫",p.name) } func (p PDuck)eat() { fmt.Println("唐老鴨人走路,唐老鴨子名字叫",p.name) } func main() { // var t PDuck=PDuck{} var a Animal var d Duck //一旦轉到某個介面上,只能使用介面的方法,自身屬性和自身方法需要型別斷言後才能使用 a=t //只能呼叫到a介面的方法,此時只能調Animal的方法 d=t //能呼叫Animal和PDuck的四個方法,因為PDuck嵌套了Animal }
3、介面零值:nil
var a Animal //nil 為引用型別 fmt.Println(a)
五、自定義集合型別
package main import "fmt" //定義MySet型別 type MySet map[interface{}]bool //判斷元素是否存在 func (m MySet) isExist(a interface{})bool { return m[a] } //返回set長度 func (m MySet) len() int { return len(m) } //設定值 func (m MySet) set(a interface{}) { m[a] = true } //刪除值 func (m MySet) delete(a interface{}) { delete(m, a) } //測試程式碼 func main() { //建立一個set var a MySet = make(MySet) //相當於 var a MySet = make(map[interface{}]bool) //列印set的長度 //fmt.Println(a.len()) //放入一個值 a.set(1) //放入一個相同值 a.set(1) a.set("aaa") a.set("aaa") a.set("aaa") a.set("aaa") a.set("aaa") a.set("aaa") //無論放幾個。列印長度,還是1 //fmt.Println(a.len()) //判斷1是否存在 //fmt.Println(a.isExist(2)) ////刪除1 a.delete(1) ////判斷1是否存在 fmt.Println(a.isExist(1)) //false fmt.Println(a.len()) //1 for i,_:=range a{ fmt.Println(i) //aaa } }
六、make和new的區別
new返回type型別的指標,make直接返回type型別的值
package main //make和new的區別 type PDuck1 struct { name string sex string age int } func main() { //make是引用型別初始化的時候用的 //var per *PDuck1 =new(PDuck1) //new 是返回指向這個型別的指標 //fmt.Println(per) //上面的程式碼可以用new來創造 //var per1 =&PDuck1{} //fmt.Println(per1) //var per2 = make([]int,3,4) //make是具體的造引用型別 //new是造指向這個型別的指標 //var per2 *[]int= new([]int) //fmt.Println(per2) //(*per2)=append((*per2),99) //fmt.Println(per2) }
七、結構體取代類的使用
先去包裡面定義一個結構體並繫結方法
package Person import "fmt" type Person struct { Name string Age int Sex string } func New(Name string,Age int,Sex string) Person{ return Person{Name ,Age,Sex} } //繫結方法 func (p Person)PrintName() { fmt.Println(p.Name) }
匯入包並使用包裡面的繫結方法
package main import ( person "go/day2/strcture/Person" "fmt" ) func main() { per :=person.New("aaa",19,"男") // //var per Person = new Person("aaa",19,"男") fmt.Println(per) per.PrintName() //就可以呼叫匯入包的繫結方法 }