1. 程式人生 > 其它 >go語言之結構體、跨平臺編譯、方法

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
        HobbyName 
string }{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()    //就可以呼叫匯入包的繫結方法
}