为了账号安全,请及时绑定邮箱和手机立即绑定

Go(5 [struct | tag | 方法 | 继承 ])

标签:
Go

Go struct

  1. 用来自定义复杂数据结构

  2. struct里面可以包含多个字段(属性),字段可以是任意类型

  3. struct类型可以定义方法,注意和函数的区别

  4. struct是值类型,也就是,赋值的时候,就是copy一份,不会修改原来的值, 

  5. struct类型可以嵌套

  6. Go中没有class类型,只有struct类型

  7. make ⽤来分配map、 slice、 channel类型的内存,new用来分配值类型的内存

Struct定义:

声明:

type 标识符 struct{        filed1 int    filed2 string}

访问:和python一样,使用点的方法来访问

定义一个 Student struct

type Test struct{   A int   B int}type Student struct{   //定义类型   Age int   Name string   Sex string   Grader string   Score int   test Int   //Test 是struct类型   sitrstruct Test   //指针类型   c *int}func testStruct()  {var s Student//因为这里s已经定义了。所以不需要:=这种方式了s.Age =18s.Name = "zcq"s.Score = 100s.Sex = "main"s.Grader = "36"//访问s.sitrstruct.A = 100//当struct内部有指针类型的时候,默认是空,直接赋值会报错,需要new创建s.c =new(int)//指针赋值*(s.c) = 100//打印结果fmt.Printf("name:%s,age:%d,,c:%d\n",s.Name,s.Age,*(s.c))fmt.Printf("%+v\n",s)//值类型, 就是改变内容的值,就是copy一份,不会影响原来变量的值s.Name = "rch"fmt.Printf("name:%s\n",s.Name)//s里面的地址赋值给s1,那s1和s是指向同一个内存地址,所以用s1来修改指针,那s也会变s1:=s*(s1.c) = 200fmt.Printf("name:%s,age:%d,,c:%d\n",s.Name,s.Age,*(s.c))}

Go Struct 三种定义方式:

  1. var stu Student

    1. 这种方式操作起来代码量多

  2. var stu *Student = new(Student)

    1. 使用new的方式创建,这种方式,stu就是一个指针类型,

    2. stu .Score = 100//s2,s3的score值都改变了s3:=stu
      1. //需要注意的是:*(s3).Score = 200 是标准访问形式,但是当你s3.Score  这样访问也是的, 那是因为go会检测s3是值类型,还是指针类型,如果是值类型,那go就帮你转换为*(s3) 这样的形式


  3. var stu *Student = &Student{}

    1. 分配内存空间,这种方式可以如果需要初始化一些值,可以直接写在{} 中

其中方法2,3 返回的都是指向结构体的指针,访问形式:

    stu.Name、 stu.Age和stu.Score或者 (*stu).Name、 (*stu).Age

自定义类型:结构体是用户单独定义的类型,不能和其他类型进⾏强制转换

type StudentA struct{   Number int}type Stu StudentAfunc main()  {   var a StudentA   a.Number = 20   //需要使用自定义类型强制转换   var b Stu   b=Stu(a)   fmt.Printf("%v",b)}

Struct内存布局

内存布局:struct中的所有字段在内存是连续的,

代码:


package mainimport "fmt"//内存布局是连续的type Point struct{   x int   y int}type Rect struct{   //p1,p2内存地址连续的   p1 Point   p2 Point}type RectA struct{   //p1,p2 指向的是一个内存地址   //那这2个的内存地址不是连续的   p1 *Point   p2 *Point}func main()  {   //r1不用分配内存,因为定义完了后,内存就已经分配好了,没有指针类型字段   var r1 Rect   //r2里面是2个指针类型,所以需要分配内存,就需要new   var r2 RectA   r2.p1 = new(Point)   r2.p2 = new(Point)   //r1的内存布局   fmt.Printf("ADD:%p\n",&r1.p1.x)   fmt.Printf("ADD:%p\n",&r1.p1.y)   fmt.Printf("ADD:%p\n",&r1.p2.x)   fmt.Printf("ADD:%p\n",&r1.p2.y)   fmt.Println()   //r2的内存布局   fmt.Printf("ADD:%p\n",&r2.p1.x)   fmt.Printf("ADD:%p\n",&r2.p1.y)   fmt.Printf("ADD:%p\n",&r2.p2.x)   fmt.Printf("ADD:%p\n",&r2.p2.y)}


Go struct 构造函数

go struct 没有构造函数,那就需要自己实现,一般可以使用工厂函数,来解决这个问题

type School struct{   Name string   Addr string}func NewSchool(name,addr string) (*School)  {   //& 内存地址, new也是一个意思   //实例化struct   return &School{      Name:name,      Addr:addr,   }   //或者可以用new   //第二种写法   //p:=new(School)   //p.Name = name   //p.Age = age   //return p}func main()  {   s:=School{Name:"aa",Addr:"assaas"}   fmt.Printf(s)}

Struct 中的方法

  1. 访问控制:通过大小写控制,小写不能被其他包外面调用

  2. 方法可以作用在特定类型上

  3. 函数可以随意调用

  4. 函数传参是副本的调用

    

给struct添加方法

实例代码:

type People struct{   Name string   Score int   all int   string}type Student struct{   Name string   Age int   //使用了匿名字段,实现了继承   People   all int}//给people这个struct添加了 Format方法func (p *People) Format() string {   return fmt.Sprintf("name=%s,age=%d",p.Name,p.Score)}//给Student这个struct添加了 方法func (p *Student) Format() string {   return fmt.Sprintf("name=%s,age=%d",p.Name,p.Score)}func mian(){    var s Student    //访问父类中的方法    ret :=s.People.Format()    fmt.Printf(ret)        var d People    //访问d中的方法    res := d.Format()    fmt.Printf("11111",res)    }

三种函数接收方式

  1. 定义函数:

    1. func Add(a, b int) int {   return a + b}func testInt()  {   c := Add(100, 200)   fmt.Println(c)}


  2. 值类型接受方式:

    1. type Int int//这种写法,前面(i Int) 是接受者参数//在函数的前面加了自定义类型,和变量//那Bdd的接受者就是i了, 那Bdd也就是Int类型的一个方法了,func (i Int) Bdd(a, b int) int {   return a + b}func main(){  var a Intd := a.Bdd(100, 200)fmt.Println(d)}


  3. 指针类型接收方式:

    1. func (i *Int) Cdd(a, b int) {   //这里需要做强制转换,因为a,b是小写int, 但i是大写int, 那赋值就需要强制转换   *i = Int(a + b)   //这样a+b的结果就存在i这个变量里面了   return}var e Int//传递给Cdd的只是e的拷贝,如果想修改e的值,那传递的时候,需要传递e的指针地址e.Cdd(200, 300) //&(e).Cdd(200,300) 也是一样的, 因为e调用的时候,发现要传一个地址,那go会自动的转换成&(e),你可以不用写&,因为go帮你做了fmt.Println(e)

    

在来一列struct 添加方法栗子

栗子1:

type Student struct{   Name string   Age int}//struct也是值类型,所以当要修改值,传递给函数的时候,就需要传递指针类型func (s *Student) SiteSet(name string,age int){   s.Name = name   s.Age = age}func teststrcut()  {   //这样就给struct定义了方法   var s Student   s.SiteSet("abc",20)   fmt.Println(s)}

栗子2:

func NewSchool(name,addr string) (*School)  {   return &School{      Name:name,      Addr:addr,   }func (s *School) getaddr() string  {   return s.Addr}func (s *School) Getaddr() string  {   return s.Addr}s :=model.NewSchool("北京","海淀")//调用了实例中的方法fmt.Printf("school_name:%s",s.Getname())fmt.Printf("school_name:%s",s.Getaddr())

Struct 中的tag

我们可以为struct中的每个字段,写上⼀个tag。这个tag可以通过反射的机制获取到,最常⽤的场景就是json序列化和反序列化

如下代码中,Zcq做了tag标记,那json就可以通过反射方式匹配值,json会序列化结构体里面的tag,

  1. key:json 后写的值

  2. value: 赋值的参数


在struct中 首字母如果是小写,那就是私有的,只能在main包里面访问做了tag,那json序列化后的key 可以自定义,解决了:在特殊情况下,必须用小写,但是在go里面小写命名的值,json访问不到的情况,完美
type Student struct{   Name string   Age int   Sex string   ceshi string   //做了tag标记, 还可以写多个值, 往后会学到怎么取这个值   Zcq string `json:"rch";db:"name"`}func main(){        s.Zcq = "zcq"}>>>{"Name":"ZCQ","Age":12,"Sex":"mem","rch":"zcq"}


Json序列化/反序列化

  1. 序列化

    1. //Marshal 序列化//data 是一个bytes的切片,//Marshl是封装data,err :=json.Marshal(s)if err != nil{   fmt.Printf("json.Marshal---error",err)   return}


  2. 反序列化

    1. var s1 Student//Unmarshal :反序列化//需要传入地址的值,直接去修改值,如果传入的是s1,那传入的是一个副本,//err这里不需要:= 了,因为上面已经声明了err =json.Unmarshal(data,&s1)if err!=nil{   fmt.Printf("json.Unmarshal---error",err)   return}fmt.Printf("s1%#v\n",s1)

匿名字段/继承

定义:结构体中字段可以没有名字,即匿名字段,  

注意:不能存在相同的名字

type People struct{   Name string   Score int   all int   //没有名字,那就是匿名字段,不能存在相同名字   string}

继承

注意:在继承中,如果父类和子类都有同一个方法,那么优先访问子类中的方法,

type Student struct{   Name string   Age int   //  People 是上面代码中的struct,     //使用了匿名字段,实现了继承   People   //如果都有相同的字段,那默认会先找本身student里面的字段   //相同持有相同字段的话,就需要s.People.Name 才可以访问到   all int}func test1()  {   var s Student   s.Name = "abc"   s.Age = 100   s.string = "11"   ////如果字段没有名字,就用它的类型,来操作这个字段   //s.int = 2000   //s.People.Name = 100   //继承中可以直接访问   s.Score = 100   fmt.Printf("%#v\n",s)}func testMethod()  {   var s Student   //因为s 继承了People,而且又给People添加了一个方法,所以可以直接访问到这个方法   s.Age = 200   s.People.Name = "anc"   //访问父类中的方法   ret :=s.People.Format()   //访问子类中的方法s.Format()   fmt.Printf(ret)}


    


    



点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消