Go结构和方法

结构

定制类型

GO通过类型别名和结构体的形式支持用户自定义类型
一个带属性的结构体试图表示一个现实世界中的实体
结构体是符合类型,也是值类型,因此可以通过new来创建
在一个结构体中,字段名字必须是唯一的
GO中没有类的概念,因此在GO中结构体有着更为重要的地位

结构体定义

type identifier struct {
    field1 type1
    field2 type2
    ...
}

结构体字段类型可以是如下:
结构体本身、函数、接口

数组可以看作一种结构体,不过它使用下标而不是具体字段

结构体类型以及结构体字段遵循可见性原则,一个导出的结构体类型中有些字段是可见的,有些不是,这是可能的

new关键字

使用new,给一个新的结构体变量分配内存,返回已分配内存的指针

// 结构体类型指针
var t *T = new(T)
变成
var t *T
t = new(T) // 分配内存没必要在一开始的时候就做
var t *T = &T{x,y} // 结构体类型指针,底层仍然使用new
var t T = T{x,y} // 结构体类型

Go语言“选择器”

无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的选择器来引用结构体字段

结构体类型实例和一个指向它的指针的内存布局

type Point struct {x,y int}

使用new初始化:
在这里插入图片描述
作为结构体字面量初始化:
在这里插入图片描述

通过解指针的方式来设置值
在这里插入图片描述

结构体的内存布局

Go中,结构体和它所包含的数据在内存中是以连续块的形式存在的,即使结构体中嵌套有其它结构体,这在性能上带来了很大优势

Go中的指针,有可能指针和指针对应的数据存储在不同的内存空间中,

使用结构体实现的数据结构

1.链表
// 单向链表

type Node struct {
    data    float64
    su      *Node
}

// 双向链表

type Node struct {
    pr      *Node
    data    float64
    su      *Node
}

2.二叉树

type Tree strcut {
    le      *Tree
    data    float64
    ri      *Tree
}

结构体转换

类型转换遵循严格的规则
需要进行显示转换

结构体工厂

Go不支持面向对象编程语言中那样的构造子方法,为了方便,通常会为类型定义一个工厂,按惯例,工厂的名字以new或New开头,
在这里插入图片描述

map struct vs new() 和 make()

make : slices 、maps
new:struct

make一个结构体变量,会引发一个编译错误,这还不算太糟糕
new一个映射,会在赋值的时候引发运行时错误,因为new之后返回一个指向空值的指针,***它尚未分配内存***,所以在使用map的时候要特别谨慎

带标签的结构体

在这里插入图片描述

匿名字段和内嵌结构体

结构体内没有显示名称的字段,只有字段类型,类型就是字段的名字,即结构体可以包含内嵌结构体
这个可以和OO中的继承概念相比较
Go中的继承通过内嵌和组合来实现,所以在Go中,相比较于继承,组合更受青睐
在这里插入图片描述

ambiguous selector outer.in1

结论:
多个匿名字段中的key,不能有重复,
另外对于一种数据类型,在一个结构体中只能有一个匿名字段
当两个字段拥有相同的名字时,外层名字会覆盖内层名字,这提供了一种重载字段或方法的方式
如果同一级别出现两个相同的字段,不使用就不会报错,使用了就报错
在这里插入图片描述

方法

Go方法定义

作用在接收者上的一个函数,接收者是某种类型的变量,因此方法是一种特殊类型的函数

可以有方法的类型:
几乎可以是任何类型,结构体类型、函数类型、可以是int、bool、string或数组的别名类型
接收者类型:
不能是一个接口类型,因为接口是一个抽象定义,而方法是具体实现,会引发编译错误
能是指针类型,但是它可以是任何其他允许类型的指

一个类型+它的方法等价于面向对象的一个类,类型的代码和方法可以不放在一起,可以存在不同的源文件中,唯一的要求,他们必须是同一个包的

方法不允许重载,一个类型只能有一个给定名称的方法,但是基于接收者类型是有重载的,多个接收者可以有形同名称的方法

如果 recv 是一个指针,Go 会自动解引用。
如果方法不需要使用 recv 的值,可以用 _ 替换它
recv 就像是面向对象语言中的 this 或 self,但是 Go 中并没有这两个关键字。随个人喜好,你可以使用 this 或 self 作为 receiver 的名字。

函数和方法的区别

函数将变量作为参数:Function1(recv)
方法在变量上被调用:recv.Method1()

指针或值作为接收者

在这里插入图片描述
注意 Go 为我们做了探测工作,我们自己并没有指出是否在指针上调用方法,Go 替我们做了这些事情。
b1 是值而 b2 是指针,方法都支持运行了。

方法和未导出字段

类型导出了,但是字段没有被导出

提供 getter 和 setter 方法。对于 setter 方法使用 Set 前缀,对于 getter 方法只使用成员名。

并发访问对象

对象的字段不应该由2个或2个以上的不同线程在同一时间取改变
为了安全并发访问,1.可以使用sync中的方法 2.通过 goroutines 和 channels 探索另一种方式??

内嵌类型的方法和继承

在这里插入图片描述
和PHP中的继承区别:

PHP中的接口里面的方法都是抽象方法,实现的子类中,必须实现接口中的全部方法,而在Go里面,接口相当于是一个普通的类,嵌套接口的类型中,不需要去实现全部的方法
但是如果不是嵌套,而是使用类型去实现一个接口的实例,然后将实例赋值给一个接口类型的变量,该实例就必须实现接口的所有方法,不然会报错
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190813104033981.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzIyNjMxNDMz,size_16,color_FFFFFF,t_70)

上图中的红色框中的stockPosition就是一个实现了接口的实例,必须实现接口的所有方法

Go在类型中嵌套功能,Go支持多继承

在这里插入图片描述

通用方法和方法命名

编写规范的Go程序,应该遵守一些约定,就像标准库里面的通用方法一样,这样会让Go开发的软件具有一定的一致性和可读性

举个例子:
如果需要一个 convert-to-string 方法,应该命名为 String(),而不是 ToString()

和其他面向对象语言相比Go的类型和方法

1.Go不要一个显示的类定义,通过作用于同一个类型的一组方法集来隐式定义的
2.Go中同属于一个类型的方法可以不和类型的定义放在一起
3.继承的好处代码复用和多态,代码复用Go中通过组合和委托实现,多态使用接口实现
这也叫组件编程

类型的String()方法和格式化描述符

在广泛使用一个自定义类型的时候,最好为它定义

垃圾回收 SetFinalizer

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值