1. 前言
在大一学C语言时我们就接触过结构体,go语言中同样也有结构体,并在日常项目中发挥着重要作用。go语言中没有类的感念概念,也不支持“类”的继承等面向对象的概念,结构体和接口方法配合使用更具灵活性。
结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。
2. 结构体的定义
使用关键字 type、struct:
// 定义Person 为用户struct类型
type Person struct {
Name string
Address string
Age int
}
同类型的字段也可以写在一行
type Person struct {
Name, Address string
Age int
}
3. 声明结构体类型的变量
当声明了结构体类型就可以使用这个类型创建值。
使用结构类型声明变量,并初始化为零值:
var variable_name Person
使用结构体字面量声明变量,并初始化为非零值
// 声明 Person 类型的变量,并初始化所有字段
variable_name := Person{
Name: "雨中笑",
Address: “yzx-fjl.cn”,
Age: 1,
}
某些字段没有初始值的时候,该字段可不写。没有指定初始值的字段的值就是该字段类型的零值
使用值的列表初始化
variable_name := Person{
"雨中笑",
“yzx-fjl.cn”,
1,
}
这种方式初始化需要初始化结构体的所有字段、填充顺序需要一致
结构体也是值类型,因此可以通过 new 函数来创建:
variable_name := new(Person)
new 函数的特点是只能把内存初始化为零值并返回其指针
对结构体指针进行键值对初始化
使用&对结构体进行取地址
variable_name := &Person{}
相当于对该结构体类型进行了一次new实例化操作。
4. 类型别名
如Mysql的别名是MysqlDB
var MysqlDB *Mysql
type Mysql struct {
conn *gorm.DB
}
5. 匿名字段
结构体可以包含一个或多个匿名字段,但同型的匿名字段只能有一个,匿名字段默认采用类型名作为字段名。
package main
import "fmt"
type Person struct {
name string
string
int // 匿名字段
}
func main() {
p := Person{"雨中笑","匿名字段",20,}
fmt.Printf("%#v\n", p) //输出:main.Person{string:"雨中笑", string:"匿名字段", int:20}
fmt.Println(p.string, p.int) //输出:雨中笑 匿名字段
}
6. 内嵌结构体
结构体也是一种数据类型,所以它同样可以作为匿名字段使用:
type Person struct {
Name string
Age int
Address string
}
type Student struct {
ID int
Person
}
下面的代码可以声明并初始化 Student 类型的变量:
st := Student {
StudentID: 1,
Person: Person{"雨中笑", 1, "yzx-fjl.cn"},
}
当遇到内嵌结构体存在相同的字段名时,指定具体的内嵌结构体的字段就可,如:
type Person struct {
Name string
Age int
Address string
}
type Teacher struct {
Name string
Age int
Address string
}
type Student struct {
ID int
Person
Teacher
}
func main() {
var student Student
student.Person.Name = "person名"
student.Teacher.Name = "teacher名"
}
7. 结构体作为函数参数
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
func main() {
printPerson(Person{Name: "你好"}) //输出:Person name : 你好
printPerson1(&Person{Name: "雨中笑"}) //输出:Person1 name : 雨中笑
}
// 用结构体做参数
func printPerson(person Person) {
fmt.Printf("Person name : %s\n", person.Name)
}
// 用结构体指针类型做参数
func printPerson1(person *Person) {
fmt.Printf("Person1 name : %s\n", person.Name)
}
一般结构体比较复杂的话,值拷贝性能开销会比较大。
8. 结构体作为函数返回值
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
func main() {
person := printPerson()
person1 := printPerson1()
fmt.Println(person.Name)
fmt.Println(person1.Name)
}
// 用结构体做返回值
func printPerson() Person {
return Person{Name: "你好"}
}
// 用结构体指针类型做返回值
func printPerson1() *Person {
return &Person{Name: "雨中笑"}
}
输出:
你好
雨中笑
9. 结构体与JSON
接收和传送数据时,有时会对结构体做json处理,Go语言自带的json包可以方便的 JSON 读取和写入 JSON 数据
json.Marshal(v interface{}) 将传入的结构体生成JSON数据
json.Unmarshal(data []byte, v interface{}) 将JSON解组到结构中,解组匹配传入对象
package main
import (
"encoding/json"
"fmt"
)
type Info struct {
Srcid string `json:"srcid"`
Location string `json:"location"`
}
type Ip struct {
Srcid string
QueryID string
Data []*Info
ResultNum string
}
func main() {
body := Ip{
Srcid: "5809",
QueryID: "2610195558",
ResultNum: "1",
Data:make([]*Info, 0, 1),
}
body.Data = append(body.Data, &Info{Location: "广东省 移动"})
//JSON序列化:结构体-->JSON格式的字符串
data, err := json.Marshal(body)
if err != nil {
fmt.Println("json marshal failed")
return
}
fmt.Printf("json:%s\n", data)
//JSON反序列化:JSON格式的字符串-->结构体
str := `{"Srcid": "5809","QueryID": "2610195558","Data": [{"srcid": "5809","location": "广东省 移动"}],"ResultNum": "1"}`
ipList := &Ip{}
err = json.Unmarshal([]byte(str), ipList)
if err != nil {
fmt.Println("json unmarshal failed!")
return
}
fmt.Printf("%#v\n", ipList)
}
输出:
json:{"Srcid":"5809","QueryID":"2610195558","Data":[{"srcid":"","location":"广东省 移动"}],"ResultNum":"1"}
&main.Ip{Srcid:"5809", QueryID:"2610195558", Data:[]*main.Info{(*main.Info)(0x11004210)}, ResultNum:"1"}
10. 获取外部接口数据解析
这里写一个例子,通过访问IP接口获得数据,再经行解析使用
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type Info struct {
Srcid string `json:"srcid"` //指定tag实现json序列化该字段时的key
Location string `json:"location"`
}
type Ip struct {
Srcid string
QueryID string
Data []Info
ResultNum string
}
func main() {
ip := "117.136.31.45"
addr := fmt.Sprintf("https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=%s&resource_id=5809", ip)
resp, err := http.Get(addr)
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
var ipList Ip
err = json.Unmarshal(body, &ipList)
if err != nil {
fmt.Println(err)
}
fmt.Println(ipList)
fmt.Println(ip, "地址:", ipList.Data[0].Location)
}
输出:
{"Srcid":"5809","ResultCode":"0","status":"0","QueryID":"3329069979","Result":[{"DisplayData":{"strategy":{"tempName":"ip","precharge":"0","ctplOrPhp":"1"},"resultData":{"tplData":{"srcid":"5809","resourceid":"5809","OriginQuery":"117.136.31.45","origipquery":"117.136.31.45","query":"117.136.31.45","origip":"117.136.31.45","location":"\u5e7f\u4e1c\u7701 \u79fb\u52a8","userip":"","showlamp":"1","tplt":"ip","titlecont":"IP\u5730\u5740\u67e5\u8be2","realurl":"http:\/\/www.ip138.com\/","showLikeShare":"1","shareImage":"1","data_source":"AE"},"extData":{"tplt":"ip","resourceid":"5809","OriginQuery":"117.136.31.45"}}},"ResultURL":"http:\/\/www.ip138.com\/","Weight":"2","Sort":"1","SrcID":"5809","ClickNeed":"0","SubResult":[],"SubResNum":"0","ar_passthrough":[],"RecoverCacheTime":"0"}],"data":[{"srcid":"5809","resourceid":"5809","OriginQuery":"117.136.31.45","origipquery":"117.136.31.45","query":"117.136.31.45","origip":"117.136.31.45","location":"\u5e7f\u4e1c\u7701 \u79fb\u52a8","userip":"","showlamp":"1","tplt":"ip","titlecont":"IP\u5730\u5740\u67e5\u8be2","realurl":"http:\/\/www.ip138.com\/","showLikeShare":"1","shareImage":"1"}],"ResultNum":"1"}
{5809 3329069979 [{5809 广东省 移动}] 1}
117.136.31.45 地址: 广东省 移动