go语言不支持面向对象编程,因此使用结构体来实现Python中的class,可以在结构体中定义多个字段(属性),为结构体实现方法、实例化等。
结构体--struct,描述一系列具有相同类型或不同类型组成的数据集合。
定义结构体:
type struct_name struct {
field_name type
field_name type
...
field_name type
}
实例化结构体:
实例化的时候不需要为每个字段都赋值,没有显性赋值的变量被赋予默认值
struct_name{
field:value,
field:value,
..., //右括号另起一行,最后一行要加上逗号
}
结构体方法的实现:
go语言只需要在普通函数前面加一个接收者(receiver),写在函数名前面的括号里,如下:
func (s ReceiverType) funcName([parameters]) [results] {
方法内容
}
传递值类型:func (s Student) change_schools()
传递引用类型:func (s *Student) change_schools()
两种传递类型的区别:使用值类型作为接收者,它传递进来的是一份完全的拷贝,那么s跟外部调用s1是没有联系的,在方法里面修改s,不会影响外部的s1;使用引用类型作为接收者,它传递进来的是指向外部s1的地址,在方法里面修改s,会影响到外部变量
结构体匿名字段:以类型的名字作为字段的名字
type struct_name struct{
string
int
...
}
匿名结构体:没有名字的结构体,不需要type关键字去定义,定义结构体的时候就实例化对象
变量名 := struct{
//结构体的成员属性
}{
//初始化属性成员
}
补充一个知识点:%v与%+v的区别
//只会输出结构体的值
fmt.Printf("%v\n", s)
//输出结构体字段和值的映射关系
fmt.Printf("%+v\n", s)
内存对齐
cpu去取结构体的时候,不是按照一个字节一个字节去取的,不同的平台cpu有不同的访问力度:32位操作系统--cpu每一次访问4个字节;64位操作系统--cpu每一次访问8个字节
内存对齐是为了减少cpu对内存的访问次数,提高cpu读取内存数据的效率,而做的内存上的填充,以空间换时间,如果内存不对齐,访问相同的数据需要更多的访问内存次数,效率就变低了
内存对齐规则:
一、结构体第一成员变量偏移量为0,后面的成员变量的偏移量等于成员变量和成员对齐系数两者中较小的那个值的最小整数倍,如果不满足规则,编译器就会在前面填充为0的字节空间
二、结构体本身也需要内存对齐,其大小等于各成员变量占用内存最大的和编译器默认对齐系数两者中较小的那个值的最小整数倍
程序员可以通过调整成员变量的顺序减少结构体占用的大小,节省内存,一个大原则:将占用大小大的变量放在前面
补充知识点:
数据类型的大小和对齐系数
unsafe包:
Sizeof函数 -- 看占用大小
Alignof函数 -- 对齐系数
继承
一个结构体可以存放另外一个结构体,以此来达到继承的效果
模拟继承:
type Person struct{
name string
age int
}
//模拟继承
type Teacher struct{
Person
subject string
name string
}
实例化时,先实例化父类,再实例化子类
取结构体的属性时,推荐使用t.Person.age这种写法,不会产生歧义而出现错误,如果使用t.age这种写法,如果继承的结构体中有多个age,那么这种写法会产生歧义
空结构体
空结构体:不占用空间,节省内存,起占位作用,其他类型如int、string在申明时就会分配空间,其中零值也会分配空间
空结构作用如下:
实现方法的接受者
实现集合
实现空channel
实现方法的接受者 --没有属性,只有方法:
//定义
type Empty struct{}
//方法
func (t *Empty) Hello(){
fmt.Println("Hello world!")
}
func main(){
var t Empty
t.Hello()
}
实现集合:
type Set map[string]struct{}
func (s Set) Append(k string) {
s[k] = struct{}{}
}
func (s Set) Remove(k string) {
delete(s, k)
}
func main() {
s1 := Set{}
s1.Append("a")
s1.Remove("a")
}
多态
多态--一个接口的不同形态,go语言使用接口interface实现多态,崇尚鸭子类型
定义接口:
空接口不需要做任何实现,因此任何数据类型都满足这个空接口
type 接口名字 interface{
方法1(参数列表)[返回值]
方法2(参数列表)[返回值]
...
}
接口的使用:
type PayInterface interface {
pay()
}
type ZFB struct {
name string
}
func (z *ZFB) pay() {
fmt.Println("this is ZFB pay method")
}
type WX struct {
name string
}
func (z *WX) pay() {
fmt.Println("this is WX pay method")
}
func main(){
z := ZFB{"zhifubao"}
w := WX{"weixin"}
payment(&z)
payment(&w)
}
//公共接口--必须实现接口中的所有方法
func payment(p PayInterface){
p.pay()
}