Go安装及配置环境
下载最新的 zip 文件: go#.#.#.windows-amd64.zip ,这里的 #.#.# 是 Go 的最新版本号。
解压缩 go#.#.#.windows-amd64.zip 文件到你选择的位置。比如D:\Go
在系统中设置两个环境变量:GOROOT和GOPATH
GOPATH 指向的是你的工作目录。
GOROOT 指向的是go的源文件的存放目录,也就是D:\GO,添加 %GOROOT%\bin 到系统的 PATH 环境变量。
测试是否配置成功:
然后打开一个 cmd 命令终端,输入 go version
。 你会得到一个 go version go1.3.3 windows/amd64 的输出,即表示 Go 安装完成。
运行一个go程序
package main
func main() {
println("hello world!") //使用内置函数 println 打印出字符串。
}
将上述内容保存为一个xxx.go文件,然后打开一个 shell 或者终端,进入到文件保存的目录内,通过敲入以下命令来运行程序:
go run xxx.go
如果 golang 环境配置正确,将打印出hello world!。
Go 是一门编译型语言,直接运行go run
命令已经包含了编译和运行。它使用一个临时目录来构建程序,执行完后清理掉临时目录。可以执行以下命令来查看临时文件的位置:
go run --work xxx.go
可以使用go build xxx.go
命令来编译代码,这将产生一个可执行文件 xxx.exe
。
在开发中,既可以使用 go run
也可以使用 go build
。但正式部署的时候,应该部署 go build
产生的二进制文件,然后执行它。
入口函数main
在 go 中,程序入口必须是 main
函数,并且在 main
包内,也就是文件第一行的package main
。
import导入包
import
关键字被用于去声明文件中代码要使用的包。
在 Go 中,关于导包是很严格的。如果你导入了一个包却没有使用将会导致编译不通过。
Go 的标准库已经有了很好的文档。可以访问 golang.org/pkg/fmt/#Println
去看更多关于 PrintLn
函数的信息。
也可以在本地获取文档:
godoc -http=:6060
然后浏览器中访问 http://localhost:6060
变量及声明
方法一:使用var
下面是 Go 中声明变量和赋值最明确的方法,但也是最冗长的方法:
package main
import (
"fmt"
)
func main() {
var power int //定义了一个 int 类型的变量 power
power = 9000
//以上两行可以合并为
//var power int = 9000
fmt.Printf("It's over %d\n", power)
}
方法二:使用:=
Go 提供了一个方便的短变量声明运算符 :=
,它可以自动推断变量类型,但是这种声明运算符只能用于局部变量,不可用于全局变量:
power := 9000
注意:=
表示的是声明并赋值,所以在相同作用域下,相同的变量不能被声明两次
此外, go 支持多个变量同时赋值(使用 =
或者 :=
):
func main() {
name, power := "Goku", 9000
fmt.Printf("%s's power is over %d\n", name, power)
}
多个变量赋值的时候,只要其中有一个变量是新的,就可以使用:=
。例如:
func main() {
power := 1000
fmt.Printf("default power is %d\n", power)
name, power := "Goku", 9000
fmt.Printf("%s's power is over %d\n", name, power)
}
注意:Go 不允许在程序中有声明了但未使用的变量。例如:
func main() {
name, power := "Goku", 1000
fmt.Printf("default power is %d\n", power)
}
不能通过编译,因为 name
是一个被声明但是未被使用的变量,就像 import 的包未被使用时,也将会导致编译失败。
函数声明
函数声明格式:
func 函数名([参数名,参数类型]…) ([返回值类型]…){
}
注意在go中是先写参数名,再写参数类型的。。
以下三个函数:一个没有返回值,一个有一个int类型的返回值,一个有两个返回值(int型和bool型)。
func log(message string) {
}
func add(a int, b int) int {
}
//或者可以写成
//func add(a, b int) int {
//}
func power(name string) (int, bool) {
}
我们可以像这样使用最后一个:
value, exists := power("goku") //用两个变量value和exists来接收power函数的两个返回值
if exists == false {
// 处理错误情况
}
有时候,我们仅仅只需要关注其中一个返回值。就可以将其他的返回值赋值给空白符_
:
_, exists := power("goku")
if exists == false {
// handle this error case
}
_
,空白标识符实际上返回值并没有赋值。所以可以一遍又一遍地使用 _
而不用管它的类型。
slice切片
slice创建和初始化
切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
var 或者 :=
var slice1 []int = []int{0,1,2,3,4,5}
var slice2 = []int{0,1,2,3,4,5}
slice3 := []int{0,1,2,3,4,5} //局部
make
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
使用 make 动态创建slice,避免了数组必须用常量做长度的麻烦。还可用指针直接访问底层数组,退化成普通数组操作。
从数组切片
arr := [5]int{1, 2, 3, 4, 5} //这里是[...],所以是个数组
slice := []int{} //这里是[],没有指明长度,所以是个切片
// 切片操作前包后不包
slice = arr[1:4]
fmt.Println(slice)
package main
import "fmt"
func main() {
slice := []int{0,1,2,3,4,5}
//a[x:y:z] 切片内容 [x:y] len:y-x cap:z-x
//不写z,默认是len
s1 := slice[1:2] //x=1,y=2,z=6 len=1 cap=5
fmt.Println(s1)
fmt.Println("cap of s1", cap(s1))
s2 := slice[:3:4] //x=0,y=3,z=4 len=3 cap=4
fmt.Println(s2)
fmt.Println("cap of s2", cap(s2))
s3 := slice[1:3:5] //x=1,y=3,z=5 len=2 cap=4
fmt.Println(s3)
fmt.Println("cap of s3", cap(s3))
}
切片的内存布局
读写操作实际目标是底层数组,只需注意索引号的差别。
append内置函数
append :向 slice 尾部添加数据,返回新的 slice 对象。
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 1, 5)
fmt.Printf("%p\n", &s1)
s2 := append(s1, 1)
fmt.Printf("%p\n", &s2)
s3 := append(s2, 2, 3)
fmt.Printf("%p\n", &s3)
fmt.Println(s1, s2, s3)
fmt.Println(&s1[0], &s2[0], &s3[0])
}
输出结果:
0xc00000c060
0xc00000c080
0xc00000c0a0
[0] [0 1] [0 1 2 3]
0xc000072030 0xc000072030 0xc000072030
可以看到s1,s2,s3是三个不同的对象!但是他们底层的数组是同一个数组
append后超出原来的slice的cap
超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。
package main
import (
"fmt"
)
func main() {
arr := [5]int{}
slice := arr[:3:4]
fmt.Println("arr: ", arr, "slice: ", slice)
slice = append(slice, 100)
fmt.Println("first append:")
fmt.Println("arr: ", arr, "slice: ", slice) //会发现arr中的内容也被改成了100,说明此时slice和arr实际上是一个数组,slice是arr的一个切片
fmt.Println(&arr[0], &slice[0]) //0xc00008c030 0xc00008c030数组的首地址是一样的
fmt.Printf("%p %p\n", &arr, &slice) //0xc00008c030 0xc000086020直接取地址两者是不一样的,slice是一个结构体,相当于再包了一层,结构体里面有个属性指向了底层数组的首地址
fmt.Println()
fmt.Println("second append:")
//slice再追加一个就查出cap了
slice = append(slice, 200)
fmt.Println("arr: ", arr, "slice: ", slice) //到这里arr里的美容不变了,slice的内容变了,说明两者不是同一块内存空间了
fmt.Println(&arr[0], &slice[0]) //0xc00008c030 0xc0000a4040可以看到数组的首地址不一样了
}
输出结果:
arr: [0 0 0 0 0] slice: [0 0 0]
first append:
arr: [0 0 0 100 0] slice: [0 0 0 100]
0xc000072030 0xc000072030
0xc000072030 0xc00000c060
second append:
arr: [0 0 0 100 0] slice: [0 0 0 100 200]
0xc000072030 0xc00007e040
copy
函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准。两个 slice 可指向同一底层数组,允许元素区间重叠。
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 5)
s2 := []int{1,2,3}
s3 := []int{4,5,6,7}
fmt.Println(s1, s2, s3)
copy(s1, s2)
fmt.Println("s2 -> s1:",s1)
copy(s2, s3)
fmt.Println("s3 -> s2:",s2)
copy(s3, s1)
fmt.Println("s1 -> s3:",s3)
}
输出结果:
[0 0 0 0 0] [1 2 3] [4 5 6 7]
s2 -> s1: [1 2 3 0 0]
s3 -> s2: [4 5 6]
s1 -> s3: [1 2 3 0]
slice遍历
func main() {
slice := []int{5,4,3,2,1,0}
for index, value := range slice{
fmt.Printf("index:%v, value:%v\n", index, value)
}
}
输出结果:
index:0, value:5
index:1, value:4
index:2, value:3
index:3, value:2
index:4, value:1
index:5, value:0
string and slice
string底层就是一个byte的数组,因此可以进行切片操作。但是string本身是不可变的,因此要改变string中的字符需要先转为数组,修改后再转回来。
package main
import "fmt"
func main() {
/*
英文字符串
*/
str := "Hello World!"
// s := str[:8]
// s[6] = 'G' //string本身是不可变的,这里会报错:./main.go:15:10: cannot assign to s[6]
s := []byte(str) //string转换成字符数组
s[6] = 'G'
s = s[:8]
s = append(s, '!')
fmt.Println(s) //打印出来是字符数组
str = string(s)
fmt.Println(str) //转回string再打印
/*
含有中文字符用[]rune数组
*/
str2 := "你好,世界。"
s2 := []rune(str2)
s2[5] = '!'
s2 = append(s2, '!')
str2 = string(s2)
fmt.Println(str2)
}
打印结果:
[72 101 108 108 111 32 71 111 33]
Hello Go!
你好,世界!!
指针
和C语言的指针差不多
Map
map的声明&初始化
序号 | 方式 | 示例 |
---|---|---|
1 | 声明方式 | var m map[string]string |
2 | new关键字 | m := *new(map[string]string) |
3 | make关键字 | m := make(map[string]string, [cap]) |
4 | 直接创建 | m := map[string]string{"": ""} |
由于map是引用类型,因此使用声明方式创建map时在使用之前必须初始化。以上四种方式中最常用的是后面两种使用方式,第二种基本不会使用。
//方式一
// userInfo := map[string]string{}
userInfo := map[string]string{"name":"xiaoming","age":"24"}
userInfo["name"] = "xiaozhang"
userInfo["age"] = "20"
// data := make(map[int]int, 10)
data := make(map[int]int)
data[100] = 998
data[200] = 999
//方式二
// 声明,nil
var row map[int]int
row = data
注意:键不重复 & 键必须可哈希(int/bool/float/string/array)
map的基本使用
package main
import "fmt"
func main() {
//map类型的变量默认初始值为nil,需要使用make()函数来分配内存。
//语法为: make(map[KeyType]ValueType, [cap])
amap := make(map[string]string, 5)
amap["xm"] = "xiaoming"
amap["xh"] = "xiaohong"
fmt.Println("获取长度:", len(amap))
//map的遍历
for k,v := range amap{
fmt.Println(k, v)
}
//判断某个key是否存在,存在的话ok为true
value, ok := amap["xz"]
fmt.Println("value:", value, "ok:", ok)
//也可以在声明的时候填充元素
intmap := map[string]int{
"1": 1,
"2": 2, //注意这个逗号一定要在
}
intmap["3"] = 3
for k, v := range intmap{
fmt.Println(k, v)
}
//删除某个键值对,使用delete函数
fmt.Println("delete key=2的项")
delete(intmap, "2")
for k, v := range intmap{
fmt.Println(k, v)
}
}
输出结果:
获取长度: 2
xm xiaoming
xh xiaohong
value: ok: false
1 1
2 2
3 3
delete key=2的项
1 1
3 3
元素为map类型的slice
package main
import "fmt"
func main() {
//map切片,使用make为mapSlice分配内存
mapSlice := make([]map[string]string, 3)
fmt.Println("初始mapSlice:")
for i, v := range mapSlice{
fmt.Println(i, v)
}
//这里直接声明并初始化了一个名为userInfo的map
userInfo := map[string]string{"name": "xiaoming", "age": "24"}
mapSlice[0] = userInfo //所以这里可以直接赋值给mapSlice[0]
mapSlice[1] = make(map[string]string, 3) //要先为mapSlice[1]指向的map分配内存,否则报错panic: assignment to entry in nil map
mapSlice[1]["name"] = "xiaozhang" //然后就可以直接往mapSlice[1]指向的map中写东西了
mapSlice[1]["age"] = "23"
mapSlice[1]["gender"] = "1"
fmt.Println()
for i, v := range mapSlice{
fmt.Println(i, v)
}
}
输出结果:
初始mapSlice:
0 map[]
1 map[]
2 map[]
0 map[age:24 name:xiaoming]
1 map[age:23 gender:1 name:xiaozhang]
2 map[]
值为切片类型的map
package main
import "fmt"
func main() {
//sliceMap的key为string,value为string类型的slice
sliceMap := make(map[string][]string, 3)
fmt.Println("初始sliceMap:")
for k, v := range sliceMap{
fmt.Println(k, v)
}
sliceMap["slice1"] = []string{"a", "b", "c"}
str := [4]string{"hello", "world", "!"}
sliceMap["slice2"] = str[:3]
fmt.Println()
fmt.Println("sliceMap:")
for k, v := range sliceMap{
fmt.Println(k, v)
}
}
输出结果:
初始sliceMap:
sliceMap:
slice1 [a b c]
slice2 [hello world !]
结构体
结构体的定义和实例化
定义
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
其中
1.类型名:标识自定义结构体的名称,在同一个包内不能重复。
2.字段名:表示结构体字段名。结构体中的字段名必须唯一。
3.字段类型:表示结构体字段的具体类型。
只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。
package main
import "fmt"
//结构体的定义:使用type和struct关键字
type person struct{
name string
age int32
address string
}
func main() {
//常用的结构体实例化方式一:使用键值对初始化
person1 := person{
name: "xiaozhang",
age: 19,
address: "ningbo",
}
fmt.Println("person1,name: ", person1.name)
fmt.Printf("type of person1: %T\n", person1)
//实例化方式二
//结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。
var person2 person
person2.name = "xiaoli"
person2.age = 20
person2.address = "shanghai"
fmt.Printf("%#v\n", person2)
}
使用new关键字来实例化
可以通过使用new关键字对结构体进行实例化,得到的是一个结构体类型的指针。 格式如下:
package main
import "fmt"
type person struct{
name string
age int32
address string
}
func main() {
//使用new关键字创建实例,得到的是一个person类型的指针
person3 := new(person)
fmt.Printf("type of person3: %T\n", person3)
person3.name = "xiaowang" //GO里面可以直接用.来调用指针指向的结构体的成员
(*person3).address = "beijing" //person3.name其实在底层是(*person3).name,这是GO实现的的语法糖
fmt.Printf("%#v\n", person3)
}
输出结果:
type of person3: *main.person
&main.person{name:"xiaowang", age:0, address:"beijing"}
使用取地址符&来实例化
使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。
package main
import "fmt"
type person struct{
name string
age int32
address string
}
func main() {
//使用&创建实例,得到的是一个person类型的指针
person4 := &person{
name: "xiaochen",
age: 28,
address: "chengdu",
}
fmt.Printf("type of person4: %T\n", person4)
fmt.Printf("%#v\n", person4)
}
输出结果:
type of person4: *main.person
&main.person{name:"xiaochen", age:28, address:"chengdu"}
使用值的列表初始化
这种方式要注意:
- 必须初始化结构体的所有字段。
- 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
- 该方式不能和键值初始化方式混用。
func main() {
person5 := person{
"xiaozhao", 28, "chongqing",
}
fmt.Printf("type of person5: %T\n", person5) //type of person5: main.person
fmt.Printf("%#v\n", person5) //main.person{name:"xiaozhao", age:28, address:"chongqing"}
}
匿名结构体
在定义一些临时数据结构等场景下还可以使用匿名结构体。
package main
import (
"fmt"
)
func main() {
var user struct{name string; age int}
user.name = "xiaozhang
user.age = 18
fmt.Printf("%#v\n", user)
}
结构体的内存布局
构造函数
Go语言的结构体没有构造函数,我们可以自己实现。 例如,下方的代码就实现了一个person的构造函数。 因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。
package main
import "fmt"
type person struct{
name string
age int32
address string
}
func main() {
person1 := newPerson("xiaozhang", "ningbo", 28) //调用构造函数,注意这里的参数顺序
fmt.Printf("%#v\n", person1)
}
//构造函数
func newPerson(name, address string, age int32) *person{
return &person{
name: name,
age: age,
address: address,
}
}
方法和接收者
Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
其中:
- 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
- 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:具体格式与函数定义相同。
方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。
package main
import "fmt"
type person struct{
name string
age int32
address string
}
func main() {
person1 := newPerson("xiaozhang", "ningbo", 28) //调用构造函数,注意这里的参数顺序
fmt.Printf("%#v\n", person1)
person1.Dream()
}
//构造函数
func newPerson(name, address string, age int32) *person{
return &person{
name: name,
age: age,
address: address,
}
}
//person类型有一个Dream()方法
func (p person) Dream() {
fmt.Printf("%s在做梦。\n", p.name)
}
输出结果:
&main.person{name:"xiaozhang", age:28, address:"ningbo"}
xiaozhang在做梦。
指针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为person类型添加一个SetAge方法,来修改实例变量的年龄。
值类型的接收者
当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
package main
import "fmt"
type person struct{
name string
age int32
address string
}
func main() {
person1 := newPerson("xiaozhang", "ningbo", 28) //调用构造函数,注意这里的参数顺序
person1.setAge(19)
person1.setAge2(20)
fmt.Printf("%#v\n", person1)
}
//构造函数
func newPerson(name, address string, age int32) *person{
return &person{
name: name,
age: age,
address: address,
}
}
//接收者为指针类型
func (p *person) setAge(age int32){
p.age = age
}
//接收者为值类型
func(p person) setAge2(age int32){
p.age = age
}
输出结果:
&main.person{name:"xiaozhang", age:19, address:"ningbo"}
可见值类型的接收者无法改变实例的成员值
什么时候应该使用指针类型接收者?
- 需要修改接收者中的值
- 接收者是拷贝代价比较大的大对象
- 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
任意类型添加方法
在Go语言中,接收者的类型不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法
注意: 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。
package main
import "fmt"
type myInt int
func main() {
var i myInt = 10
i.sayHi()
fmt.Printf("my type is %T and my value is %v", i, i)
}
func (m myInt) sayHi(){
fmt.Printf("hi~ ")
}
输出结果:
hi~ my type is main.myInt and my value is 10
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中**同种类型的匿名字段只能有一个**。
package main
import "fmt"
type person struct{
string //只有类型,没有字段名
int
}
func main() {
p := person{
"zhangsan",
18,
}
fmt.Println(p)
fmt.Println(p.string, p.int)
}
嵌套结构体
一个结构体中可以嵌套包含另一个结构体或结构体指针。
package main
import "fmt"
type person struct{
name string
age int
}
type student struct{
personInfo person
className string
}
func main() {
p := person{"zhangsan", 18,}
fmt.Printf("%#v\n", p)
stu := newStudent(p, "3班")
fmt.Printf("%#v\n", stu)
}
func newStudent(p person, cn string) *student{
return &student{
personInfo: p,
className: cn,
}
}
输出结果:
main.person{name:"zhangsan", age:18}
&main.student{personInfo:main.person{name:"zhangsan", age:18}, className:"3班"}
嵌套匿名结构体
当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
package main
import "fmt"
type person struct{
name string
age int
}
type address struct{
province string
city string
}
type student struct{
personInfo person
className string
address //匿名结构体
}
func main() {
p := person{"zhangsan", 18,}
fmt.Printf("%#v\n", p)
stu := newStudent(p, "3班")
fmt.Printf("%#v\n", stu)
stu.address.province = "浙江省" //通过 匿名结构体.字段名 来访问
stu.city = "宁波市" //直接访问匿名结构体的字段名
fmt.Printf("%#v\n", stu)
}
func newStudent(p person, cn string) *student{
return &student{
personInfo: p,
className: cn,
}
}
输出结果:
main.person{name:"zhangsan", age:18}
&main.student{personInfo:main.person{name:"zhangsan", age:18}, className:"3班", address:main.address{province:"", city:""}}
&main.student{personInfo:main.person{name:"zhangsan", age:18}, className:"3班", address:main.address{province:"浙江省", city:"宁波市"}}
嵌套结构体中的字段名冲突
嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
package main
import "fmt"
type person struct{
name string
age int
createTime string
}
type address struct{
province string
city string
createTime string
}
type student struct{
personInfo person
className string
address //匿名结构体
}
func main() {
p := person{"zhangsan", 18, "",}
fmt.Printf("%#v\n", p)
stu := newStudent(p, "3班")
fmt.Printf("%#v\n", stu)
stu.address.province = "浙江省" //通过 匿名结构体.字段名 来访问
stu.city = "宁波市" //直接访问匿名结构体的字段名
stu.address.createTime = "2024-06-05"
stu.personInfo.createTime = "2024-06-04" //注意非匿名结构体要通过字段名来访问,也就是这里要用personInfo,不能像address一样直接用结构体名称person
fmt.Printf("%#v\n", stu)
}
func newStudent(p person, cn string) *student{
return &student{
personInfo: p,
className: cn,
}
}
结构体的”继承“
Go语言中通过嵌套匿名结构体指针也可以实现继承。
package main
import "fmt"
//Animal
type Animal struct {
name string
}
func (a *Animal) move() {
fmt.Printf("%s会动!\n", a.name)
}
//Dog
type Dog struct {
color string
*Animal //通过嵌套匿名结构体实现继承
}
func (d *Dog) wang() {
fmt.Printf("%s会叫,汪汪汪~\n", d.name)
}
func main() {
d := &Dog{
color: "黑色",
Animal: &Animal{ //注意嵌套的是结构体指针
name: "小黑",
},
}
d.wang() //小黑会叫,汪汪汪~
d.move() //小黑会动!
}
结构体与JSON序列化
json序列化是指,将有 key-value
结构的数据类型(比如结构体,map,切片)序列化成json字符串的操作。
package main
import (
"fmt"
"encoding/json"
)
//定义一个结构体
type Hero struct{
Name string `json:"hero_name"` //起别名为:hero_name
Age int `json:"hero_age"`
Birthday string
Sal float64
Skill string
}
func testStruct() {
hero := Hero{
Name:"张三丰",
Age:88,
Birthday:"2009-11-11",
Sal:8000.0,
Skill:"教武当剑法!!",
}
//将monster序列化
data, err := json.Marshal(&hero)
if err != nil{
fmt.Printf("序列号错误 err=%v\n",err)
}
//输出序列化后的结果
fmt.Printf("序列化后=%v\n",string(data))
}
func main() {
testStruct()
}
输出结果:
序列化后={"hero_name":"张三丰","hero_age":88,"Birthday":"2009-11-11","Sal":8000,"Skill":"教武当剑法!!"}