Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】(3)https://developer.aliyun.com/article/1534260
5.7.2、指针类型接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。
type person struct{ name string age int8 city string } // 构造器 func newPerson(name,city string,age int8) *person{ return &person{ name : name, city : city, age : age, } } // person 的方法 func (p person) eat(){ fmt.Println("人会吃饭") } // 指针类型的接收者 func (p *person) setName(name string){ p.name = name } func main(){ p := newPerson("张三","北京",22) p.eat() // 人会吃饭 p.setName("李四") fmt.Printf("%#v\n", p) // &main.person{name:"李四", age:22, city:"北京"} }
注意:结构体是值类型的,并不是引用类型!所以当方法的接收者为指针类型时,才能真正操作结构体实例,否则就相当于拷贝了一份,在方法里自娱自乐。
5.7.3、指针类型接收者
这就是自娱自乐的场景,如果希望使用方法修改结构体实例的属性,那么一定不能使用这种方法。
type person struct{ name string age int8 city string } // 构造器 func newPerson(name,city string,age int8) *person{ return &person{ name : name, city : city, age : age, } } // person 的方法 func (p person) eat(){ fmt.Println("人会吃饭") } // 非指针类型的接收者 func (p person) setName(name string){ p.name = name } func main(){ p := newPerson("张三","北京",22) p.eat() // 人会吃饭 p.setName("李四") fmt.Printf("%#v\n", p) // &main.person{name:"张三", age:22, city:"北京"} }
5.8、嵌套结构体
一个结构体中可以嵌套包含另一个结构体或结构体指针:
type Person struct{ name string age int8 addr Address } type Address struct{ province string city string } func main(){ p := Person{ name :"张三", age : 22, addr : Address{ province: "山西省", city : "晋中", }, } fmt.Printf("%#v\n", p) // main.Person{name:"张三", age:22, addr:main.Address{province:"山西省", city:"晋中"}} }
5.9、结构体的继承
type Animal struct{ category string } func (a *Animal) move(){ fmt.Printf("%s会移动",a.category) } type Dog struct{ feet int8 *Animal } func main(){ dog := Dog{ feet : 4, Animal: &Animal{ category : "狗", }, } dog.move() // 狗会移动 }
5.10、结构体字段的可见性
结构体中字段大写开头表示可公开访问,小写表示私有。
5.11、结构体 JSON 序列化
我们可以使用 "encoding/json" 包下的 json.Marshal() 函数将结构体转换为 JSON 字符串。该函数接受一个参数,即要转换的结构体对象。如果转换成功,它将返回一个包含JSON数据的字节切片和一个错误值。
注意:结构体的属性必须都为公开的(属性首字母大写),否则无法序列化为 json!
package main import ( "encoding/json" "fmt" ) //Student 学生 type Student struct { ID int Gender string Name string } func main() { student1 := Student{ ID : 1, Gender: "男", Name : "刘海柱", } //JSON序列化:结构体-->JSON格式的字符串 data, err := json.Marshal(student1) if err != nil { fmt.Println("json marshal failed") return } fmt.Println(string(data)) // {"ID":1,"Gender":"男","Name":"刘海柱"} //JSON反序列化:JSON格式的字符串-->结构体 str := `{"ID":1,"Gender":"男","Name":"刘海柱"}` student2 := &Student{} err = json.Unmarshal([]byte(str), student2) if err != nil { fmt.Println("json unmarshal failed!") return } fmt.Printf("%#v\n",student2) // &main.Student{ID:1, Gender:"男", Name:"刘海柱"} }
5.12、结构体标签 Tag
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:
`key1:"value1" key2:"value2"`
结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔
注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。
package main import ( "encoding/json" "fmt" ) //Student 学生 type Student struct { ID int `json:"id"` Gender string Name string // 私有属性不能被 json 访问 } func main() { student1 := Student{ ID : 1, Gender: "男", Name : "刘海柱", } //JSON序列化:结构体-->JSON格式的字符串 data, err := json.Marshal(student1) if err != nil { fmt.Println("json marshal failed") return } fmt.Println(string(data)) // {"id":1,"Gender":"男","Name":"刘海柱"} }
上面,我们给 ID 属性添加了一个标签 'json:"id"',这样当使用 json 序列化的时候就可以使用我们指定的字段名 "id" 了。