# 结构体 结构体是一个复合类型,用于表示一组数据。 结构体由一系列属性组成,每个属性都有自己的类型和值。 ## 初始化 ```go // 定义一个结构体(类型),每个结构体包含 name、age、hobby 三个元素 type Person struct { name string age int hobby []string } //方式1:先后顺序 var p1 = Person{"lihui", 22, []string{"唱跳", "rap"}} fmt.Println(p1.name, p1.age, p1.hobby) //方式2:关键字 var p2 = Person{name: "lihui", age: 22, hobby: []string{"饺子", "馒头"}} fmt.Println(p2.name, p2.age, p2.hobby) //方式3:先声明再赋值 var p3 Person p3.name = "lihui" p3.age = 18 p3.hobby = []string{"唱跳", "篮球"} fmt.Println(p3.name, p3.age, p3.hobby) ``` ## 结构体指针 ```go type Person struct { name string age int } // 初始化结构体(创建一个结构体对象) p1 := Person{"lihui", 18} fmt.Println(p1.name, p1.age) // 初始化结构体指针 // var p2 *Person = &Person{"lihui", 18} p2 := &Person{"lihui", 18} fmt.Println(p2.name, p2.age) var p3 *Person = new(Person) p3.name = "lihui" p3.age = 18 fmt.Println(p3.name, p3.age) ``` ## 赋值拷贝 其实本质上都拷贝了,只不过由于数据存储方式的不同,导致拷贝的有些是数据,有些是内存地址(指针)。 但是: - 感觉拷贝:字符串、数组、整型等。 - 感觉不拷贝:map、切片。 ## 标签 没啥用,给结构体的元素一个注释 ## 补充 结构体做参数和返回值时,在执行时候都会被重新拷贝一份,如果不想被拷贝,则可以通过指针的形式进行处理。 ## 类型方法 ```go package main import "fmt" // 声明类型 type MyInt int // 为MyInt类型自定义一个指针方法 // 可以是指针/可以是类型:*MyInt MyInt // 不使用对象,可以用 _ 代替 func (_ *MyInt) DoSomething(a1 int, a2 int) int { return a1 + a2 } func do(a1 int,a2 int) int{ return a1 + a2 } func main() { var v1 MyInt = 1 result := v1.DoSomething(1, 2) fmt.Println(result) } ``` ## 方法继承 ```go package main import "fmt" type Base struct { name string } type Son struct { Base // 匿名的方式,如果改成 base Base 则无法继承Base的方法。 age int } // Base结构体的方法 func (b *Base) m1() int { return 666 } // Son结构体的方法 func (s *Son) m2() int { return 999 } func main() { son := Son{age: 18, Base: Base{name: "lihui"}} result1 := son.m1() result2 := son.m2() fmt.Println(result1, result2) } ``` ## 结构体工厂 Go 语言不支持面向对象编程语言中那样的构造方法,但是可以很容易的在 Go 中实现 “构造工厂”方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 new 或 New 开头。假设定义了如下的 File 结构体类型: ```go type File struct { fd int name string } // 20... f := File{10, "xxxxxx"} ``` ```go type File struct { fd int name string } func NewFile(fd int, name string) *File { // 20... return &File{fd, name} } func main() { f1 := NewFile(10, "./test.txt") f2 := File{10, "xxxxxx"} } ``` 在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造函数。 **强制使用工厂方法**,让结构体变为私有,工厂方法变为共有,这样强制所有代码在实例化结构体是都是用工厂方法。 可以用包导入时首字母大写共有的方式来实现。 # 函数 可以把函数当做一个代码块,用于实现某个功能。并且提高代码的重用性和可读性。 ```go func 函数名(参数) 返回值 { 函数体 } ``` 关于函数名需要注意:函数名只能是字母数字下划线组合且数字不能开头,即驼峰式命名 ## 闭包 ```go package main import "fmt" func main() { var functionList []func() for i := 0; i < 5; i++ { function := func() { fmt.Println(i) } functionList = append(functionList, function) } functionList[0]() functionList[1]() functionList[2]() } ``` ## defer延长执行 ```go package main import "fmt" func do() int { fmt.Println("风吹") defer fmt.Println("函数执行完毕了") // 如果在这行之前执行return,那么defer就不再执行 fmt.Println("屁屁凉") return 666 } func main() { ret := do() fmt.Println(ret) } ``` 多个defer 按照写的先后顺序,以倒序执行 ```go package main import "fmt" func other(a1 int, a2 int) { fmt.Println("defer函数被执行了") } func do() int { fmt.Println("风吹") defer fmt.Println("函数执行完毕了") defer other(1, 22) fmt.Println("屁屁凉") return 666 } func main() { ret := do() fmt.Println(ret) } ``` ## 自执行函数 ```go result := func(arg int) int { return arg + 100 }(123) fmt.Println(result) // 223 ``` # 接口 接口,用于约束和泛指数据类型 ```go type 接口名称 interface{ 方法名称() 返回值 } ``` ```go type Base interface { f1() // 定义方法,无返回值 f2() int // 定义方法,返回值int类型 f3() (int, bool) // 定义方法,2个返回值分别是 int、bool类型 f4(n1 int, n2 int) int // 定义方法,需要两个参数,1个返回值 } type empty interface {} // interface{} ``` 接口中的方法只定义,不能编写具体的实现逻辑。