结构体
自定义类型和类型别名
基于一个内置的类型自定义一个自己的类型,类型别名只在代码编写过程中有效
package main
type myint int
type youint = int //类型别名
func main(){
var n myint
n = 100
fmt.Println(n)
fmt.Printf("%T\n" , n)
var m youint
m = 100
fmt.Println(m)
fmt.Printf("%T\n" , m)
}
rune 和 byte
package main
func main(){
var c rune
c = '你'
fmt.Println(c)
fmt.Printf("%T\n" , c)
var c1 byte
c1 = 'a'
fmt.Println(c1)
fmt.Printf("%T\n" , c1)
}
结构体定义
使用type
和 struct
关键字来定义结构体,结构体都是值类型,赋值的时候都是拷贝
type 类型名 struct {
字段名 字段类型
字段名 字段类型
}
其中:
类型名:标识自定义结构体的名称,在同一个包内不能重复
字段名:表示结构体字段名。结构体中的字段名必须唯一
字段类型:表示结构体字段的具体类型
package main
import "fmt"
type person struct{
name string
age int
gender string
hobby []string
}
func main(){
var p person
p.name = "xxxx"
p.age = 20
p.gender = "男"
p.hobby = []string{"足球" , "篮球"}
fmt.Println(p)
//访问变量p的字段
fmt.Println(p.name)
fmt.Printf("%T\n" , p)
}
匿名结构体
在定义一些临时数据结构等场景下还可以使用匿名结构体,用于临时场景,值类型的
var s struct{name string; age int}
s.name = "xxxx"
s.age = 18
package main
import "fmt"
func main(){
var user struct{Name string; Age int}
user.Name = "xxxx"
user.Age = 18
fmt.Printf("%#v\n" , user)
}
package main
import "fmt"
type person struct{
name , sex string
}
func f(p person){
p.name = "xxx"
}
func main(){
var p person
p.name ="yyy"
p.sex = "1"
f(p)
fmt.Println(p.name) // yyy
}
指针类型的结构体
我们还可以通过new
关键字对结构体进行实例化,得到的是结构体的地址。
var p2 = new(person)
fmt.Printf("%T\n" , p2)
fmt.Printf("p2 = %#v\n" , p2)
需要注意的是在Go语言中支持对结构体指针直接使用.
来访问结构体的成员
var p2 = new(person)
p2.name = "xxx"
p2.age = 28
p2.city = "xxx"
fmt.Printf("p2 = %#v\n" , p2)
package main
import "fmt"
type person struct{
name , sex string
}
func f(p *person){
(*p).sex = "2" //语法糖 p.sex = "2"
}
func main(){
var p person
p.name = "yyy"
p.sex = "1"
f(&p)
fmt.Println(p.sex)
}
package main
func main(){
var a int = 100
b := &a
fmt.Printf("type:a:%T type b:%T\n" , a ,b)
//打印十六进制的内存地址
fmt.Printf("%p\n" , &a)
fmt.Printf("%p\n" , b)
fmt.Printf("%v\n" , b)
fmt.Printf("%p\n" , &b)
}
key-value 初始化
package main
import "fmt"
type person struct{
name,sex string
}
func main(){
var p = &person{
name : "xxxxx",
sex : "男",
}
**使用值列表的形式初始化,值的顺序要和结构体定义时的顺序一致**
p1 := &person{
"xxxx",
"1",
}
fmt.Printf("%#v\n" , p)
}
取结构体的地址实例化
使用&
对结构体进行取地址操作相当于对结构体类型进行了一次new
实例化操作。
new 与make的区别?
结构体的字段在内存中是连续的
package main
import "fmt"
type x struct{
a int8,
b int8,
c int8,
}
func main(){
n := x{
a:int8(10),
b:int8(20),
c:int8(30),
}
fmt.Printf("%p\n" , &n.a)
fmt.Printf("%p\n" , &n.b)
fmt.Printf("%p\n" , &n.c)
}
内存对齐?
构造函数
返回一个结构体变量的函数
package main
import "fmt"
type person struct{
name string
age int
}
//构造函数约定成俗都是以new开头的
//返回的是结构体还是结构体的指针
//当结构体比较大(字段比较多)的时候,尽量使用结构体指针减少程序内存开销
func newPerson(name string , age int) *person{
return &person{
name : name,
age : age,
}
}
func main(){
p1 := newPerson("xxxx" , 18)
p2 := newPerson("yyyy" , 19)
fmt.Println(p1 , p2)
}
方法和接受者
Go语言中方法Method
是一种作用于特定类型变量的函数,这种特定类型变量叫做接收者Receiver
。接受者的概念类似于其他语言的this
或者self
。
方法定义格式
func(接收者变量 , 接收者类型) 方法名(参数列表)(返回参数){
函数体
}
注意:
- 接受者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self ,this之类的命名。例如person类型的接收者变量应该命名为p
Connector
类型的接收者变量应该命名为c等. - 接收者类型:接受者类型和参数类似,可以是指针类型和非指针类型。
- 方法名,参数列表 ,返回参数:具体格式与函数定义相同。
例子:
package main
type person struct{
name string
}
//构造方法
func newPerson(name string) person{
return person{
name : name,
}
}
func(p person)say(){
fmt.Println(p.name)
}
func main(){
p := newPerson("xxx")
p.say()
}
注意:
标示符:变量名 函数名 类型名 方法名
Go语言中如果标示符首字母是大写的,就表示对外部包可见(公有的)
并且要求有注释例如:Person 是一个人的结构体(Person后一定要有空格)
值接收者和指针接受者
package main
import "fmt"
type person struct{
name string
age int
}
//构造函数
func newPerson(name string , age int)person{
return person{
name : name,
age : age,
}
}
func (p *person)nextage(){
p.age++
}
func main(){
p1 := newPerson("小李" , 18)
p1.nextage()
fmt.Println(p1.age)
}
什么时候使用值接收者什么时候使用指针接收着
- 需要修改接收者中的值
- 接收者是拷贝代价比较大的大对象
- 保证一致性,如果有某个方法使用了指针接收者,那么其他方法也应该使用指针接收者
任意类型添加方法
在Go语言中接收者的类型可以是任意类型,不仅仅是结构体,任何类型都可以拥有方法。
package main
import "fmt"
type myInt int
func (m myInt)say(){
fmt.Println("你好int")
}
func main(){
var m myInt //myInt(100)
m.say()
m = 100
fmt.Println("%#v , %T\n" , m ,m)
}
注意:
非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段
package main
import "fmt"
type person struct{
string
int
}
func main(){
p1 := person{
"xxxx",
18,
}
fmt.Printf("%#v\n" , p1)
fmt.Println(p1.string , p1.int)
}
嵌套结构体
一个结构体可以嵌套包含另一个结构体或结构体指针
package main
import "fmt"
type address struct{
province string
city string
}
type person struct{
name string
age int
addr address //如果不起名字 叫匿名嵌套结构体 可以直接 p1.city
}
type company struct{
name string
age int
addr address
}
func main(){
p1 := person{
name : "xxx",
age : 18
addr : address{ //匿名嵌套结构体 address:address
province : "上海",
city : "浦东",
},
}
fmt.Println(p1) //fmt.Println(p1.city)
}
注意:
要防止匿名嵌套结构体字段冲突
结构体的继承
Go语言中使用结构体也可以实现其他编程语言中面向对象的继承
package main
import "fmt"
type person struct{
name string
}
type student struct {
id int
person
}
func (p person) eat(){
fmt.Printf("%s会吃饭:" , p.name)
}
func (s student)study(){
fmt.Printf("%s会学习:\n" , s.name)
}
func main(){
s1 := student{
id : 1,
person:person{
name : "小李",
},
}
fmt.Println(s1)
s1.eat()
s1.study()
}
结构体与JSON
序列化和反序列化 标示符的可见性(字段首字母大写可见 小写不可见)
package main
import "fmt"
import "encoding/json"
type person struct{
Name string `json:"name" db:"name" ini:"name"` //加tag当使用json去解析的时候用name代替Name
Age int `json:"age"`
}
func main(){
p1 := person{
Name : "jak",
Age : 18,
}
//序列化
b,err := json.Marshal(p1)
if err !=nill{
fmt.Printf("err:%v\n" , err)
return
}
//反序列化
var p2 person
str := `{"name":"jack" , "age":18}`
json.Unmarshal([]byte(str) , &p2) //传指针是为了在json.Unmarshal中修改p2的值
fmt.Printf("%#v" , p2)
fmt.Printf("%#v",string(b))
}