五、结构体(struct)
5.1 定义
结构体是类型中带有成员的复合类型,结构体的定义格式:
type 类型名 struct{
字段名1 字段名1 类型
字段名2 字段名2 类型
...
}
type Point struct{
X int
Y int
//也可以写在一行
X , Y int
}
type可以将各种基本类型定义为自定义类型,struct{} 标识结构体类型。
5.2 实例化
1.基本的实例化形式
var p Point
p.X = 10
p.Y = 20
2.创建指针类型的结构体
p := new(Point)
p.X = 10
p.Y = 20
//p的类型实际上为 *Point 属于指针,但是依然可以使用“.”调用内部成员变量。这是因为Go做了特殊处理,将 p.X 形式转换为 (*p).X
3.取结构体的地址实例化
p := &Point{}
p.X = 10
p.Y = 20
// 进行“&” 取地址操作相当于对该类型进行一次new的实例化操作。p的类型为*Point
5.3 初始换结构体的成员变量
1.使用键值对初始化,适用于成员变量多的。
p := &Point{
X : 10
Y : 20
}
2.使用多个值的列表初始化,适用于成员变量少的。
p := &Point{
10
20
}
3.初始化匿名结构体
p := &struct{ //定义
X int
y int
}{ //赋值
10
20
}
5.4 构造函数
Go语言没有构造函数,自行去创建类似构造函数特性的函数。
5.5 方法
方法是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器(Receiver)。
接收器的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。面向过程实现方法 ,其实就是函数:
type Bag struct{ items []int}
//可以为任何类型添加方法
fucn Insert(b *Bag , itemid int){}
func main(){
bag := new(Bag)
Insert(bag , 1001)
}
结构体方法:
type Bag struct{ items []int}
//为结构体添加方法
fucn (b *Bag) Insert(itemid int){}
func main(){
bag := new(Bag)
bag.Insert(1001)
}
接收器--方法作用的目标
格式:
func (接收器变量 接收器类型) 方法名 (入参列表) (反参列表){函数体}
接收器类型和参数类似,可以是指针类型和非指针类型.
官方建议:接收器中参数变量的命名使用接收器类型的第一个小写字母。
指针类型接收器:
指针类型的接收器是一个结构体的指针组成,调用方法是修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
type Property struct{
value int
}
//指针类型接收器
func (p *Property) SetValue (value int){
p.value = value
}
func (p *Property) Value () int{
return p.value
}
func main(){
p := new(Property)
p.SetVlue(1)
p.Value()
}
非指针类型接收器:
当方法作用于非指针类型接收器,Go语言在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但是修改无效。
type Point struct{
X , Y int
}
//方法类不可以修改p的成员值
func (p Point) Add (other Point) Point {
//只能返回新的结构
return Point{p.X + other.X , p.Y + other.Y}
}
func main(){
p1 := Point{1,1}
p2 := Point{2,2}
result := p1.Add(p2)
}
使用:
1.小对象由于值复制速度比较快,所以适用非指针类型接收器。
2.大对象因为复制性能较低,所以适合使用指针类型接收器。为类型添加方法:
Go语言可以对任何类型添加方法,给一种类型添加方法就像给结构体添加方法一样,因为结构体也是这一种类型。为基本类型添加方法:
//自定义类型
type MyInt int
//为自定义类型添加方法
func (m MyInt) IsZero() bool{}
http包中的类型方法:
package main
import (
"net/http"
"fmt"
"os"
"strings"
)
func main(){
client := &http.Client{}
//创建请求
req , err := http.NewReuqest("POST","http://www.baidu.com",strings.NewReader("key=value"))
if err != nil{
os.Exit(1)
return
}
//为表头添加信息
req.Header.Add("User-Agent" , "myClient")
//开始请求
resp ,err := client.Do(req)
if err != nil{
os.Exit(1)
return
}
//读取服务器返回内容
data , err := ioutil.ReadAll(resp.Body)
fmt.Println(string(Data))
defer resp.Body.Close()
}
5.6 类型内嵌和结构体内嵌
结构体允许其成员字段在声明的时候没有字段名而只有字段类型,这种形式的字段叫类型内嵌或匿名字段。
type Data struct{
int
float32
bool
}
//一个结构体中同类型的匿名字段只能有一个
ins := &Data{
int : 10
float32 : 1.2
bool : true
}
声明结构体内嵌
type BasicColor struct{
R, G, B float32
}
type Color struct {
//将结构体设置为另一个的成员
Basic BasicColor
//可省去成员名 写为: BasicColor
Alpha float32
}
func main(){
var c Color
c.Basic.R = 1
//对只有结构体类型类型的成员,可以直接使用其成员
//c.R = 1
c.Alpha = 0
}
结构体内嵌特性:
1.内嵌的结构体可以直接访问其成员变量(c.R)
2.内嵌的结构体的字段名是它的类型名(c.BasicColor.R)初始化结构体内嵌:
type Wheel struct{
Size int
}
type Car struct{
Wheel
}
func main(){
c := Car{
Wheel : Wheel{
Size : 10
}
}
}
成员名字冲突:
type A struct {
a int
}
type B struct {
a int
}
type C struct {
A
B
}
func main(){
c := C{
A :A{
10
}
B: B{
10
}
}
//正确使用
c.A.a
//错误使用,编译器会报错,提示有歧义
c.a
}
所以建议,尽量不要直接调用结构体内嵌的成员,使用 c.A.a 这种格式比较稳妥。
将数据序列化为JSON:json.Marshal(raw)
JSON反序列化:json.Unmarshal(jsonData , &batteryAndToutch)