golang基础

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 mainfunc 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 mainimport ("fmt"
)func main() {var power int		//定义了一个 int 类型的变量 powerpower = 9000//以上两行可以合并为//var power int = 9000fmt.Printf("It's over %d\n", power)
}

方法二:使用:=

Go 提供了一个方便的短变量声明运算符 := ,它可以自动推断变量类型,但是这种声明运算符只能用于局部变量,不可用于全局变量:

power := 9000

注意:=表示的是声明并赋值,所以在相同作用域下,相同的变量不能被声明两次

此外, go 支持多个变量同时赋值(使用 = 或者 :=):

func main() {name, power := "Goku", 9000fmt.Printf("%s's power is over %d\n", name, power)
}

多个变量赋值的时候,只要其中有一个变量是新的,就可以使用:=。例如:

func main() {power := 1000fmt.Printf("default power is %d\n", power)name, power := "Goku", 9000fmt.Printf("%s's power is over %d\n", name, power)
}

注意:Go 不允许在程序中有声明了但未使用的变量。例如:

func main() {name, power := "Goku", 1000fmt.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,默认是lens1 := slice[1:2]    //x=1,y=2,z=6 len=1 cap=5fmt.Println(s1)fmt.Println("cap of s1", cap(s1))s2 := slice[:3:4]   //x=0,y=3,z=4 len=3 cap=4fmt.Println(s2)fmt.Println("cap of s2", cap(s2))s3 := slice[1:3:5]  //x=1,y=3,z=5 len=2 cap=4fmt.Println(s3)fmt.Println("cap of s3", cap(s3))}

切片的内存布局

读写操作实际目标是底层数组,只需注意索引号的差别。

在这里插入图片描述

append内置函数

append :向 slice 尾部添加数据,返回新的 slice 对象

package mainimport ("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 mainimport ("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 0xc00000c060second append:
arr:  [0 0 0 100 0] slice:  [0 0 0 100 200]
0xc000072030 0xc00007e040

copy

函数 copy 在两个 slice 间复制数据,复制长度以 len 小的为准。两个 slice 可指向同一底层数组,允许元素区间重叠。

package mainimport ("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
2new关键字m := *new(map[string]string)
3make关键字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 mainimport "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为truevalue, ok := amap["xz"]fmt.Println("value:", value, "ok:", ok)//也可以在声明的时候填充元素intmap := map[string]int{"1": 1,"2": 2,     //注意这个逗号一定要在}intmap["3"] = 3for 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 mainimport "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的mapuserInfo := 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 mapmapSlice[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 mainimport "fmt"func main() {//sliceMap的key为string,value为string类型的slicesliceMap := 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 stringage int32address 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 personperson2.name = "xiaoli"person2.age = 20person2.address = "shanghai"fmt.Printf("%#v\n", person2)
}
使用new关键字来实例化

可以通过使用new关键字对结构体进行实例化,得到的是一个结构体类型的指针。 格式如下:

package main
import "fmt"type person struct{name stringage int32address 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 stringage int32address 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"}
使用值的列表初始化

这种方式要注意:

  1. 必须初始化结构体的所有字段。
  2. 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  3. 该方式不能和键值初始化方式混用。
func main() {person5 := person{"xiaozhao", 28, "chongqing",}fmt.Printf("type of person5: %T\n", person5)    //type of person5: main.personfmt.Printf("%#v\n", person5)        //main.person{name:"xiaozhao", age:28, address:"chongqing"}
}

匿名结构体

在定义一些临时数据结构等场景下还可以使用匿名结构体。

package mainimport ("fmt"
)func main() {var user struct{name string; age int}user.name = "xiaozhanguser.age = 18fmt.Printf("%#v\n", user)
}

结构体的内存布局

构造函数

Go语言的结构体没有构造函数,我们可以自己实现。 例如,下方的代码就实现了一个person的构造函数。 因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型

package main
import "fmt"type person struct{name stringage int32address 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 (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体
}

其中:

  1. 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
  2. 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
  3. 方法名、参数列表、返回参数:具体格式与函数定义相同。

方法与函数的区别是,函数不属于任何类型,方法属于特定的类型

package main
import "fmt"type person struct{name stringage int32address 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 stringage int32address 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"}

可见值类型的接收者无法改变实例的成员值

什么时候应该使用指针类型接收者?
  1. 需要修改接收者中的值
  2. 接收者是拷贝代价比较大的大对象
  3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

任意类型添加方法

在Go语言中,接收者的类型不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法

注意: 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。

package main
import "fmt"type myInt int func main() {var i myInt = 10i.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 stringage int
}type student struct{personInfo personclassName 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 stringage int
}type address struct{province stringcity string
}type student struct{personInfo personclassName stringaddress     //匿名结构体
}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 stringage intcreateTime string
}type address struct{province stringcity stringcreateTime string
}type student struct{personInfo personclassName stringaddress     //匿名结构体
}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一样直接用结构体名称personfmt.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 mainimport ("fmt""encoding/json"
)//定义一个结构体
type Hero struct{Name string `json:"hero_name"` //起别名为:hero_nameAge int `json:"hero_age"`Birthday stringSal float64Skill 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":"教武当剑法!!"}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树莓派4B 零起点(三) 树莓派 VNC 远程桌面配置(2) 配置X11模式

目录 一、配置 VNC Server为X11 1、关闭已启动的VNC Server (如之前未开启,此步可以忽略) 2、切换 VNC Server的模式为 X11 二、开启 X11 VncServer 三、修改树莓派VNC Server的连接模式 1、切换到 root 账号 2、修改VNC Server X11的配置 3、设置VNC密码…

引人入胜的教育视频

对于一家专注于数字自动化和能源管理的跨国公司,我们制作了引人入胜的教育视频,帮助房主选择适合他们需求的电气产品。我们的团队审查并定稿文本,录制并编辑配音,选择背景音乐,设计图形,并制作了演示如何安…

MYSQL基础_02_MySQL环境搭建

第02章_MySQL环境搭建 1. MySQL的卸载 步骤1:停止MySQL服务 在卸载之前,先停止MySQL8.0的服务。按键盘上的“Ctrl Alt Delete”组合键,打开“任务管理器”对话框,可以在“服务”列表找到“MySQL8.0”的服务,如果现…

Springboot校园美食推荐系统的开发-计算机毕业设计源码44555

摘要 随着人们生活水平的提高,人们对美食的要求也越来越高,对各类美食信息需求越来越大。因此,结合计算机快速发展、普及,在此基础上制作一个页面简单、美观,功能实用的校园美食推荐系统势在必行,满足用户分享美食的需…

前端nvm的安装和使用nodejs多版本管理2024

nvm的安装和使用 1、简介 nvm是一个管理nodejs版本的工具。在实际的开发中,项目的开发依赖需要的nodejs版本运行环境不同,此时我们就需要使用nvm来进行不同nodejs版本的切换。其实就是一个方便的node版本管理工具。 注意:如果有安装过node&a…

nw.js 如何调用activeX控件 (控件是C++编写的dll文件)

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

第1章Hello world 4/5:对比Rust/Java/C++创建和运行Hello world全过程:运行第一个程序

讲动人的故事,写懂人的代码 1.7 对比Rust/Java/C++创建和运行Hello world全过程 有了会听懂人类的讲话,还能做记录的编程助理艾极思,他们三人的讨论内容,都可以变成一份详细的会议纪要啦。 接下来,我们一起看看艾极思是如何记录下赵可菲创建和运行Java程序Hello world,…

本地搭建支持语音和文本的中英文翻译服务-含全部源代码

实现目标 1、支持文本中英文互译; 2、支持中文语音输入; 3、支持英文语言输入; 进阶(未实现) 4、优化web界面; 5、优化语音输入js实现逻辑; 6、增加语音输入自纠错模型,纠正语音识别…

代码随想录算法训练营第三十二天| 122.买卖股票的最佳时机II,55. 跳跃游戏 ,45.跳跃游戏II

122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; class Solution {public int maxProfit(int[] prices) {if(prices.length 0){return 0;}int min prices[0];int result 0;for(int i1;i<prices.length;i){if(prices[i] > min){result (prices[i]…

“双一流名校”苏州大学计算机专业好考吗?苏州大学计算机考研考情分析

苏州大学&#xff08;Soochow University&#xff09;&#xff0c;简称“苏大”&#xff0c;坐落于历史文化名城苏州&#xff0c;国家“211工程”重点建设高校&#xff0c;国家国防科技工业局和江苏省人民政府共建高校&#xff0c;国家“双一流”世界一流学科建设高校&#xff…

wooyun_2015_110216-Elasticsearch-vulfocus

1.原理 ElasticSearch具有备份数据的功能&#xff0c;用户可以传入一个路径&#xff0c;让其将数据备份到该路径下&#xff0c;且文件名和后缀都可控。 所以&#xff0c;如果同文件系统下还跑着其他服务&#xff0c;如Tomcat、PHP等&#xff0c;我们可以利用ElasticSearch的备…

从零开始,手把手教你文旅产业策划全攻略

如果你想深入了解文旅策划的世界&#xff0c;那么有很多途径可以获取知识和灵感。 首先&#xff0c;阅读一些专业书籍也是一个不错的选择。书店或图书馆里有许多关于文旅策划的书籍&#xff0c;它们通常涵盖了策划的基本理论、方法和实践案例。通过阅读这些书籍&#xff0c;你…

集成学习模型对比优化—银行业务

1.Data Understanding 2.Data Exploration 3.Data Preparation 4.Training Models 5.Optimization Model 集成学习模型对比优化—银行业务 1.Data Understanding import pandas as pd from matplotlib import pyplot as plt import seaborn as sns df pd.read_csv(&quo…

LeetCode136只出现一次的数字

题目描述 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 解析 需要想到异或运算&#…

堆排序经典问题【TopK】

前言 在上文我们讲了堆排序的实现&#xff08;点此调整&#xff09;&#xff0c;我们先简单回顾一下。 在进行堆排序之前&#xff0c;需要建一个堆&#xff0c;由于排序是将堆顶数据与堆底交换&#xff0c;所以排升序建大堆&#xff0c;降序建小堆。 堆排序的代码 //向下调整…

【FreeRTOS】创建第一个多任务程序

创建第1个多任务程序 韦东山 Freertos学习 第一个多任务程序创建 1. 目标 创建两个任务&#xff0c;任务A运行Led_Test&#xff0c;任务B运行LCD_Test。 硬件平台&#xff1a;DShanMCU-F103开发板 2. 接口函数 创建任务的API函数 不同操作系统有不同的创建API函数 FreeRTO…

常见的api:BigDecima

一.计算中的小数 float和double占有的位置是有限的 二.BigDecima的作用 1.用于小数的精确计算 2.用来表示很大的小数 三.使用(传入小数) BigDecimal b1 new BigDecimal(0.01);BigDecimal b2 new BigDecimal(0.09);System.out.println(b1);System.out.println(b2); 不精确&…

币安用户达2亿,代币BNB创新高,赵长鹏成“美国最富囚犯” 苹果迈向AI新纪元:芯片、应用与大模型三线作战

赵长鹏坐牢第一个月&#xff0c;越坐越富。 在币安联合创始人赵长鹏入狱服刑的第一个月&#xff0c;币安代币BNB创下了历史新高&#xff0c;使得赵长鹏成为美国联邦监狱中史上“最富囚犯”。与此同时&#xff0c;币安用户数量也到达2亿“里程碑”。 根据CoinGecko的数据&…

让GNSSRTK不再难【第二天-第3部分】

第11讲 定位方程构建以及最小二乘 11.1 定位方程重构 历史讲中我们已经初步构建了单点定位的先验残差&#xff1a; p i s P i s − ( X s − X 0 ) 2 ( Y s − Y 0 ) 2 ( Z s − Z 0 ) 2 c δ t r − I i s − T i s − ϵ P i s p_i^s P_i^s - \sqrt{(X^s - X_0)^2 (Y…