2003 建设网站,网页素材图标,做旅游网站多少钱,网络维护图片结构体的定义
以学生结构体为例#xff0c;如下#xff1a;
type Student struct {name stringage int
}
同类型的可以写在一行#xff1a;
type Student struct {name,course stringage int
}
结构体初始化
方法1:使用var关键字
package mainimport fmt如下
type Student struct {name stringage int
}
同类型的可以写在一行
type Student struct {name,course stringage int
}
结构体初始化
方法1:使用var关键字
package mainimport fmttype Student struct {name stringage int
}func main() {var s Studentfmt.Printf(s%#v\n, s)
}
方法2:使用值或键值对初始化
方法2.1使用值初始化写法
s: Student{cc,18,
}
如果{}在一行逗号可以省略
s:Student{cc,18}
方法2.2使用键值对初始化写法
s:Student{name:cc,age:18,
}
方法2.3对结构体指针进行键值对初始化
s:Student{name:cc,age:18,
}
demo
package mainimport fmttype Student struct {name stringage int
}func main() {//方法1var sStudent{name:cc,age:18,}//方法2.1s:Student{cc,18,}//方法2.2s:Student{name:cc,age:18,}fmt.Printf(s%#v\n, s) //方法2.3结构体指针s:Student{name:cc,age:18,}fmt.Printf(s%#v\n, s) }
方法3给结构体成员赋值的方式进行初始化
demo
package mainimport (fmt
)type Student struct {name stringage int
}func main() {var s Studentfmt.Printf(s%v\n, s)s.name ccs.age 18fmt.Printf(p1%v\n, s)fmt.Printf(p1%#v\n, s)
}
匿名结构体与结构体的匿名字段
在一些临时数据结构等场景下可以使用匿名结构体
import (fmt
)func main() {var user struct {Name stringAge int}user.Name pprof.cnuser.Age 18fmt.Printf(%#v\n, user)
}
输出结果
struct { Name string; Age int }{Name:pprof.cn, Age:18}
结构体的字段定义时可以没有字段名这种字段被称为匿名字段
package mainimport (fmt
)//Person 结构体
type Person struct {stringint
}func main() {p1 : Person{pprof.cn,18,}fmt.Printf(%#v\n, p1) //main.Person{string:pprof.cn, int:18}fmt.Println(p1.string, p1.int) //pprof.cn 18
}
匿名字段 默认采用类型名作为字段名结构体要求字段名称必须唯一因此一个结构体中 同种类型的匿名字段只能有一个 。
结构体指针
除了上面几种实例化结构体方式还可以通过new实例化结构体返回指向结构体的指针
package mainimport (fmt
)type person struct {name stringcity stringage int8
}func main() {var p2 new(person)fmt.Printf(%T\n, p2) fmt.Printf(p2%#v\n, p2)
}
输出结果
*main.person
p2main.person{name:, city:, age:0}
Go语言中支持对结构体指针直接使用.来访问结构体的成员
package mainimport (fmt
)type person struct {name stringcity stringage int8
}func main() {var p2 new(person) //p2 是一个结构体指针p2.name 测试 //对结构体指针直接使用.来访问结构体的成员p2.age 18p2.city 北京fmt.Printf(p2%#v\n, p2)
}
输出结果
p2main.person{name:测试, city:北京, age:18}
p2.name 测试其实在底层是(*p2).name 测试这是Go语言帮我们实现的语法糖。
取结构体的地址实例化
使用对结构体变量进行取地址操作相当于对该结构体类型进行了一次 new 实例化操作。
package mainimport (fmt
)type person struct {name stringcity stringage int8
}func main() {p3 : person{}fmt.Printf(%T\n, p3) //*main.personfmt.Printf(p3%#v\n, p3) //p3main.person{name:, city:, age:0}p3.name 博客p3.age 30p3.city 成都fmt.Printf(p3%#v\n, p3) //p3main.person{name:博客, city:成都, age:30}
}
输出结果
*main.person
p3main.person{name:, city:, age:0}
p3main.person{name:博客, city:成都, age:30}
结构体作为函数参数
实例1将结构体作为函数参数
package mainimport fmttype Books struct {title stringauthor stringsubject stringbook_id int
}func main() {var Book1 Books /* 声明 Book1 为 Books 类型 */var Book2 Books /* 声明 Book2 为 Books 类型 *//* book 1 描述 */Book1.title Go 语言Book1.author www.runoob.comBook1.subject Go 语言教程Book1.book_id 6495407/* book 2 描述 */Book2.title Python 教程Book2.author www.runoob.comBook2.subject Python 语言教程Book2.book_id 6495700/* 打印 Book1 信息 */printBook(Book1)/* 打印 Book2 信息 */printBook(Book2)
}func printBook(book Books) {fmt.Printf(Book title : %s\n, book.title)fmt.Printf(Book author : %s\n, book.author)fmt.Printf(Book subject : %s\n, book.subject)fmt.Printf(Book book_id : %d\n, book.book_id)
}
输出结果
Book title : Go 语言
Book author : www.runoob.com
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : www.runoob.com
Book subject : Python 语言教程
Book book_id : 6495700
实例2将结构体指针作为函数参数
package mainimport fmttype Books struct {title stringauthor stringsubject stringbook_id int
}func main() {var Book1 Books /* 声明 Book1 为 Books 类型 */var Book2 Books /* 声明 Book2 为 Books 类型 *//* book 1 描述 */Book1.title Go 语言Book1.author www.runoob.comBook1.subject Go 语言教程Book1.book_id 6495407/* book 2 描述 */Book2.title Python 教程Book2.author www.runoob.comBook2.subject Python 语言教程Book2.book_id 6495700/* 打印 Book1 信息 *///地址就是一个指针指针的实质就是一个地址printBook(Book1)/* 打印 Book2 信息 */printBook(Book2)
}
func printBook(book *Books) {fmt.Printf(Book title : %s\n, book.title)fmt.Printf(Book author : %s\n, book.author)fmt.Printf(Book subject : %s\n, book.subject)fmt.Printf(Book book_id : %d\n, book.book_id)
}
结构体内存布局
结构体内存布局是连续的
package mainimport fmttype test struct {a int8b int8c int8d int8
}func main() {n : test{1, 2, 3, 4,}fmt.Printf(n.a %p\n, n.a)fmt.Printf(n.b %p\n, n.b)fmt.Printf(n.c %p\n, n.c)fmt.Printf(n.d %p\n, n.d)
}
输出结果
n.a 0xc00000a198
n.b 0xc00000a199
n.c 0xc00000a19a
n.d 0xc00000a19b
构造函数
构造函数是一种特殊的函数用来在对象实例化的时候初始化对象的成员变量。
package mainimport fmttype person struct {name stringcity stringage int8
}func main() {p9 : newPerson(pprof.cn, 测试, 90)fmt.Printf(%#v\n, p9)}//构造函数用于初始化结构体person
func newPerson(name, city string, age int8) *person {return person{name: name,city: city,age: age,}
}
输出结果
main.person{name:pprof.cn, city:测试, age:90}
方法和接收器
1、为结构体添加方法
package maintype Bag struct {items []int
}//定义在背包结构体上的名为Insert的方法
func (b *Bag) Insert(itemid int) {b.items append(b.items, itemid)
}
func main() {b : new(Bag)//在结构体b上调用方法b.Insert(1001)
}
2、接收器——方法作用的对象
2、1指针类型的接收器
指针类型的接收器由一个结构体的指针组成更接近于面向对象中的 this 或者 self。
由于指针的特性调用方法时修改接收器指针的任意成员变量在方法结束后修改都是有效的。
在下面的例子使用定义一个 Property 结构体为 Property 添加 SetValue() 方法以封装设置属性的过程通过属性的 Value() 方法可以重新获得属性的数值使用属性时通过 SetValue() 方法的调用可以达成修改属性值的效果。
package main
import fmt
// 定义结构体 Property
type Property struct {value int // 属性值
}
// 设置 Property 值
func (p *Property) SetValue(v int) {// 修改p的成员变量p.value v
}
// 取 Property 值
func (p *Property) Value() int {return p.value
}
func main() {// 实例化 Property p : new(Property)// 设置值p.SetValue(100)// 打印值fmt.Println(p.Value())
}
2、2 非指针类型的接收器
当方法作用于值类型接收者时Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值但修改操作只是针对副本无法修改接收者变量本身。需要辅以 return赋值 实现真正的修改。
package main
import (fmt
)
// 定义点结构
type Point struct {X intY int
}
// 非指针接收器的加方法
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)// 输出结果fmt.Println(result)
}
输出结果
{3 3}
由于例子中使用了非指针接收器Add() 方法变得类似于只读的方法Add() 方法内部不会对成员进行任何修改。
指针和非指针接收器的使用场景
在计算机中小对象由于值复制时的速度较快所以适合使用非指针接收器。大对象因为复制性能较低适合使用指针接收器在接收器和参数间传递时不进行复制只是传递指针。
什么时候应该使用指针类型接收者
需要修改接收者中的值接收者是拷贝代价比较大的大对象保证一致性如果有某个方法使用了指针接收者那么其他的方法也应该使用指针接收者。
嵌套结构体
一个结构体中可以嵌套包含另一个结构体或结构体指针
package mainimport (fmt
)//Address 地址结构体
type Address struct {Province stringCity string
}//User 用户结构体
type User struct {Name stringGender stringAddress Address
}func main() {user1 : User{Name: pprof,Gender: 女,Address: Address{Province: 黑龙江,City: 哈尔滨,},}fmt.Printf(user1%#v\n, user1) //user1main.User{Name:pprof, Gender:女, Address:main.Address{Province:黑龙江, City:哈尔滨}}
}
输出结果
user1main.User{Name:pprof, Gender:女, Address:main.Address{Province:黑龙江, City:哈尔滨}}
嵌套匿名结构体
package mainimport (fmt
)//Address 地址结构体
type Address struct {Province stringCity string
}//User 用户结构体
type User struct {Name stringGender stringAddress //匿名结构体
}func main() {var user2 Useruser2.Name pprofuser2.Gender 女user2.Address.Province 黑龙江 //通过匿名结构体.字段名访问user2.City 哈尔滨 //可以直接访问匿名结构体的字段名fmt.Printf(user2%#v\n, user2) //user2main.User{Name:pprof, Gender:女, Address:main.Address{Province:黑龙江, City:哈尔滨}}
}
输出结果
user2main.User{Name:pprof, Gender:女, Address:main.Address{Province:黑龙江, City:哈尔滨}}
当访问结构体成员时会先在结构体中查找该字段找不到再去匿名结构体中查找。
嵌套结构体的字段名冲突
嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
package mainimport (fmt
)//Address 地址结构体
type Address struct {Province stringCity stringCreateTime string
}//Email 邮箱结构体
type Email struct {Account stringCreateTime string
}//User 用户结构体
type User struct {Name stringGender stringAddressEmail
}func main() {var user3 Useruser3.Name pprofuser3.Gender 女// user3.CreateTime 2019 //ambiguous selector user3.CreateTimeuser3.Address.CreateTime 2000 //指定Address结构体中的CreateTimeuser3.Email.CreateTime 2000 //指定Email结构体中的CreateTimefmt.Printf(user3%#v\n, user3)
}
输出结果
user3main.User{Name:pprof, Gender:女, Address:main.Address{Province:, City:, CreateTime:2000}, Email:main.Email{Account:, CreateTime:2000}}
如果直接使用user3.CreateTime 2000会报错ambiguous selector user3.CreateTime
结构体的继承
package mainimport (fmt
)//动物结构体
type Animal struct {name string
}//动物结构体的move方法
func (a *Animal) move() {fmt.Printf(%s会动\n, a.name)
}//狗结构体
type Dog struct {Feet int8*Animal //通过嵌套匿名结构体实现继承
}func (d *Dog) wang() {fmt.Printf(%s会汪汪汪~\n, d.name)
}func main() {d1 : Dog{Feet: 4,Animal: Animal{ //注意嵌套的是结构体指针name: 乐乐,},}d1.wang() d1.move()
}
输出结果
乐乐会汪汪汪~
乐乐会动
结构体与JSON序列化
什么是对象序列化呢
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化它将流转换为对象。这两个过程结合起来可以轻松地存储和传输数据。
通俗讲呢对象序列化就是指将对象的状态转换为字符串。
Json语法规则
数据在键值对中数据由逗号分隔花括号保存对象方括号保存数组
json格式1
{ firstName:John , lastName:Doe }
json格式2
{employees: [{ firstName:John , lastName:Doe },{ firstName:Anna , lastName:Smith },{ firstName:Peter , lastName:Jones }]
}
json格式3
var employees [{ firstName:Bill , lastName:Gates },{ firstName:George , lastName:Bush },{ firstName:Thomas , lastName: Carter }
];
demo:
package mainimport (encoding/jsonfmt
)//Student 学生
type Student struct {ID intGender stringName string
}//Class 班级
type Class struct {Title stringStudents []*Student
}func main() {c : Class{Title: 101,Students: make([]*Student, 0, 200),}for i : 0; i 10; i {stu : Student{Name: fmt.Sprintf(stu%02d, i),Gender: 男,ID: i,}c.Students append(c.Students, stu)}//JSON序列化结构体--JSON格式的字符串data, err : json.Marshal(c)if err ! nil {fmt.Println(json marshal failed)return}fmt.Printf(json:%s\n, data)//JSON反序列化JSON格式的字符串--结构体str : {Title:101,Students:[{ID:0,Gender:男,Name:stu00}, {ID:1,Gender:男,Name:stu01},{ID:2,Gender:男,Name:stu02},{ID:3,Gender:男,Name:stu03},{ID:4,Gender:男,Name:stu04},{ID:5,Gender:男,Name:stu05},{ID:6,Gender:男,Name:stu06},{ID:7,Gender:男,Name:stu07},{ID:8,Gender:男,Name:stu08},{ID:9,Gender:男,Name:stu09}]}c1 : Class{}err json.Unmarshal([]byte(str), c1)if err ! nil {fmt.Println(json unmarshal failed!)return}fmt.Printf(%#v\n, c1)
}
输出结果
json:{Title:101,Students:[{ID:0,Gender:男,Name:stu00},{ID:1,Gender:男,Name:stu01},{ID:2,Gender:男,Name:stu02},{ID:3,Gender:男,Name:stu03},{ID:4,Gender:男,Name:stu04},{ID:5,Gender:男,Name:stu05},{ID:6,Gender:男,Name:stu06},{ID:7,Gender:男,Name:stu07},{ID:8,Gender:男,Name:stu08},{ID:9,Gender:男,Name:stu09}]}
main.Class{Title:101, Students:[]*main.Student{(*main.Student)(0xc0000749c0), (*main.Student)(0xc0000749f0), (*main.Student)(0xc000074a20), (*main.Student)(0xc000074a50), (*main.Student)(0xc000074ab0), (*main.Student)(0xc000074ae0), (*main.Student)(0xc000074b10), (*main.Student)(0xc000074b40), (*main.Student)(0xc000074b70), (*main.Student)(0xc000074ba0)}}
结构体标签Tag
Tag是结构体的元信息可以在运行的时候通过反射的机制读取出来。
Tag在结构体字段的后方定义由一对反引号包裹起来具体的格式如下
key1:value1 key2:value2
结构体标签由一个或多个键值对组成。键与值使用冒号分隔值用双引号括起来。键值对之间使用一个空格分隔。 注意事项 为结构体编写Tag时必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差一旦格式写错编译和运行时都不会提示任何错误通过反射也无法正确取值。例如不要在key和value之间添加空格。
例如我们为Student结构体的每个字段定义json序列化时使用的Tag
package mainimport (encoding/jsonfmt
)//Student 学生
type Student struct {ID int json:id //通过指定tag实现json序列化该字段时的keyGender string //json序列化是默认使用字段名作为keyname string //私有不能被json包访问
}func main() {s1 : Student{ID: 1,Gender: 女,name: pprof,}data, err : json.Marshal(s1)if err ! nil {fmt.Println(json marshal failed!)return}fmt.Printf(json str:%s\n, data)
}
输出结果
json str:{id:1,Gender:女}