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()    //就可以调用导入包的绑定方法
}

 

posted @ 2022-03-28 12:38  新入世界的小白  阅读(113)  评论(0)    收藏  举报