一、面向对象
1.1、初识golang面向对象
golang支持面向对象编程(OOP)。
golang没有类(class),go语言的结构体(struct)和其他编程语言的类(class)有同等的地位。简单理解golang是根据struct来实现面向对象编程(OOP)的。
golang有面向对象的继承 ,封装 和多态的特性
golang面向对象很优雅,通过(interface )关联,耦合性低,很灵活。也就是说golang中面向接口编程是非常重要的特性。
二、结构体(struct)和结构体变量(值类型)
2.1、结构体(struct)和结构体变量的快速入门(更多方式:详见2.4)
Go// 定义结构体变量,也叫做"对象" type Cat struct { Name string Age int Color string } func main() { // 创建一个结构体变量"cat",类型为"Cat",是一个struct var cat1 Cat fmt.Println(cat1) // 返回的默认值:{ 0 } ,string默认值为"",int默认值为0,所以这里显示只有一个0 // 给变量的"属性"赋值 cat1.Color="yellow" cat1.Age=2 cat1.Name="dandan" fmt.Println(cat1) // 返回值:{dandan 2 yellow} 分别对应Name,Age,Color的值 fmt.Println(cat1.Name) // 返回值:dandan fmt.Println(cat1.Age) // 返回值:2 fmt.Println(cat1.Color) // 返回值:yellow fmt.Printf("%T",cat1) // 类型:main.Cat }
2.2、结构体变量在内存中的布局(重要)
1.声明一个结构体变量时,数据结构已经存在了,数据的值就是不同类型的默认值;
2.结构体变量是值类型(相互数据不影响,如果想要相互数据影响的话,详见:2.5)
2.3、结构体( struct**)声明**
type 结构体名称 struct {
field1 type
field2 type
}
2.3.1、结构体( struct**)声明例子**
结构体名称首字母大写,那么结构体可以在其他包被使用
结构体(字段|属性)首字母大写,那么结构体字段可以在其他包被使用
不同结构体变量的字段是独立的,互不影响
type Cat struct {
Name string // (字段|属性)是 string类型
Age int // (字段|属性)是 int类型
Color string // (字段|属性)是 string类型
}
2.3.2、创建一个结构体变量后,如果没有给字段赋值,那么会有一个默认值:
布尔类型的默认值是:false
string类型的默认值是:""
int类型的默认值是:0
指针,slice(切片)和map的默认值都是nil,即没有分配内存空间(在使用时,需要先make)
例子如下:
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int par1 *int // 指针 slice1 []int // 切片 map1 map[string]string // map } func main() { // 定义结构体变量 var test Test fmt.Println(test.par1) // 默认值:<nil> fmt.Println(test.slice1) // 默认值:[] fmt.Println(test.map1) // 默认值:map[] if test.par1 == nil { fmt.Println("test.par1 is nil") } if test.slice1 == nil { fmt.Println("test.slice1 is nil") } if test.map1 == nil { fmt.Println("test.map1 is nil") } // 依次输出: // test.par1 is nil // test.slice1 is nil // test.map1 is nil // 此时给"结构体变量"直接赋值,一定会报错。需要先给切片make,在使用 test.slice1=make([]int,2) test.slice1[0]=111 test.slice1[1]=222 fmt.Println(test.slice1) // [111 222] //需要先给map make,在使用 test.map1=make(map[string]string) test.map1["name"]="sudada" test.map1["age"]="18" fmt.Println(test.map1) // map[age:18 name:sudada] }
2.4、结构体变量的声明和结构体变量字段使用
2.4.1、方式1(先声明结构体变量,然后赋值)
var test struct
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { var test Test test.Name="sudada" test.Age=18 fmt.Println(test) // {sudada 18} }
2.4.2、方式1的延伸(先声明结构体变量,然后赋值)
var test = struct{}
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { var test = Test{} // 简写 test := Test{} test.Name="sudada" test.Age=18 fmt.Println(test) // {sudada 18} }
2.4.3、方式2(声明结构体变量,同时赋值)
var test = struct{Name: "sudada",Age: 18,}
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { var test = Test{ Name: "sudada", Age: 18, } fmt.Println(test) // {sudada 18} }
2.4.4、方式3(结构体变量是一个指针时,然后赋值)
var test *struct = new(struct)
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { // 结构体变量是一个"指针" var test *Test = new(Test) // 可以写成:var test = new(Test) // 给"指针"类型的"结构体变量"字段赋值的方式(标准写法) (*test).Name="sudada" (*test).Age=18 fmt.Println(*test) // {sudada 18} // go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18 test.Name="wang" test.Age=28 fmt.Println(*test) // {wang 28} }
2.4.5、方式3的延伸(结构体变量是一个指针时,然后赋值)
var test *struct = &struct{}
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { // 结构体变量是一个"指针" var test *Test = &Test{} // 给"指针"类型的"结构体变量"字段赋值的方式(标准写法) (*test).Name="sudada" (*test).Age=18 fmt.Println(*test) // {sudada 18} // go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18 test.Name="wang" test.Age=28 fmt.Println(*test) // {wang 28} }
2.4.6、方式4(结构体变量是一个指针,同时赋值)
var test *struct = &Test{Name:"sudada", Age:28,}
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { // 结构体变量是一个"指针",声明时赋值 var test *Test = &Test{ Name:"sudada", Age:28, } fmt.Println(*test) // {sudada 28} // go为了程序使用方便,针对"指针"类型的结构体变量,底层做了优化:test.Age=18 == (*test).Age=18 test.Name="wang" test.Age=28 fmt.Println(*test) // {wang 28} }
2.5、结构体变量的将指针赋值给另外一个结构体变量时,内存分配机制
"test2指针"对应的"值"是"test1的指针地址"(那么修改test1或者test2时,2边的值都会跟着改变)
Go// 定义结构体变量,也叫做"对象" type Test struct { Name string Age int } func main() { var test1 = Test{} test1.Name="sudada" test1.Age=18 // 把test1的"指针"赋值给test2 fmt.Printf("%p\n",&test1) // test1的指针是:0xc000008048 var test2 *Test = &test1 fmt.Printf("%p\n",&test2) // test2的指针是:0xc00006a028(test2的指针存放的值就是test1的指针) fmt.Printf("%p\n",test2) // test2的值(等于test1的指针)是:0xc000008048 fmt.Println(*test2) // test2:{sudada 18} // 修改"指针"的值时,test1和test2对应的值都会改变 test2.Name="wang" fmt.Println(test1) // test1:{wang 18} fmt.Println(*test2) // test2:{wang 18} }
2.6、结构体struct 注意事项
2.6.1、结构体和其他类型进行转换时,需要有完全相同的字段(字段名字,字段个数和字段类型)
Gotype A struct { Name string Age int } type B struct { Name string Age int } func main() { var a A var b B // 这样强制转换不会报错(A B结构体字段名称,字段个数和字段类型相同) a = A(b) fmt.Println(a,b) // { 0} { 0} }
2.6.2、结构体进行type重新定义(相当于取别名),goloang认为是新的数据,但是相互间可以强转
Go// 结构体1 type Student struct { Name string Age int } // 结构体2 type Stu Student func main() { var stu1 Student var stu2 Stu // 这样强制转换不会报错(Student Stu结构体字段名称,字段个数和字段类型相同) stu1 = Student(stu2) fmt.Println(stu1,stu2) // { 0} { 0} }
2.6.3、结构体的每个字段都可以写一个tag,该tag可以通过反射机制获取
type student struct {
Name string `json:"name"`
Age int `json:"age"`
Sex string `json:"sex"`
}
Goimport ( "encoding/json" "fmt" ) // 结构体1 type student struct { Name string `json:"name"` Age int `json:"age"` Sex string `json:"sex"` } func main() { var stu1 = student{"sudada",18,"nan",} fmt.Println(stu1) // {sudada 18 nan} // 把结构体变量stu1序列化(这里传入"结构体字段"必须大写,如果不大写"结构体字段",那么json.Marshal读取不到值) jsonStr, err := json.Marshal(stu1) if err != nil { fmt.Println("报错: ",err) } else { fmt.Println(string(jsonStr)) // 默认返回的是bytes类型的切片,需要使用string做一下转换 // 返回值(json格式):{"Name":"sudada","Age":18,"Sex":"nan"} // 结构体字段使用tag之后: // 返回值(json格式):{"name":"sudada","age":18,"sex":"nan"} } }
三、方法
3.1、什么是方法?
glang中的方法是作用在指定的数据类型上的,即:和指定的数据类型绑定,因此自定义类型,都可以有方法。而不仅仅是struct
3.2、