Go structpython
用来自定义复杂数据结构json
struct里面能够包含多个字段(属性),字段能够是任意类型数据结构
struct类型能够定义方法,注意和函数的区别ide
struct是值类型,也就是,赋值的时候,就是copy一份,不会修改原来的值, 函数
struct类型能够嵌套布局
Go中没有class类型,只有struct类型spa
make ⽤来分配map、 slice、 channel类型的内存,new用来分配值类型的内存
指针
Struct定义:orm
声明:继承
type 标识符 struct{ filed1 int filed2 string }
访问:和python同样,使用点的方法来访问
定义一个 Student struct
type Test struct{ A int B int } type Student struct{ //定义类型 Age int Name string Sex string Grader string Score int test Int //Test 是struct类型 sitrstruct Test //指针类型 c *int } func testStruct() { var s Student //由于这里s已经定义了。因此不须要:=这种方式了 s.Age =18 s.Name = "zcq" s.Score = 100 s.Sex = "main" s.Grader = "36" //访问 s.sitrstruct.A = 100 //当struct内部有指针类型的时候,默认是空,直接赋值会报错,须要new建立 s.c =new(int) //指针赋值 *(s.c) = 100 //打印结果 fmt.Printf("name:%s,age:%d,,c:%d\n",s.Name,s.Age,*(s.c)) fmt.Printf("%+v\n",s) //值类型, 就是改变内容的值,就是copy一份,不会影响原来变量的值 s.Name = "rch" fmt.Printf("name:%s\n",s.Name) //s里面的地址赋值给s1,那s1和s是指向同一个内存地址,因此用s1来修改指针,那s也会变 s1:=s *(s1.c) = 200 fmt.Printf("name:%s,age:%d,,c:%d\n",s.Name,s.Age,*(s.c)) }
Go Struct 三种定义方式:
var stu Student
这种方式操做起来代码量多
var stu *Student = new(Student)
使用new的方式建立,这种方式,stu就是一个指针类型,
stu .Score = 100 //s2,s3的score值都改变了 s3:=stu
//须要注意的是:*(s3).Score = 200 是标准访问形式,可是当你s3.Score 这样访问也是的, 那是由于go会检测s3是值类型,仍是指针类型,若是是值类型,那go就帮你转换为*(s3) 这样的形式
var stu *Student = &Student{}
分配内存空间,这种方式能够若是须要初始化一些值,能够直接写在{} 中
其中方法2,3 返回的都是指向结构体的指针,访问形式:
stu.Name、 stu.Age和stu.Score或者 (*stu).Name、 (*stu).Age
自定义类型:结构体是用户单独定义的类型,不能和其余类型进⾏强制转换
type StudentA struct{ Number int } type Stu StudentA func main() { var a StudentA a.Number = 20 //须要使用自定义类型强制转换 var b Stu b=Stu(a) fmt.Printf("%v",b) }
Struct内存布局
内存布局:struct中的全部字段在内存是连续的,
代码:
package main import "fmt" //内存布局是连续的 type Point struct{ x int y int } type Rect struct{ //p1,p2内存地址连续的 p1 Point p2 Point } type RectA struct{ //p1,p2 指向的是一个内存地址 //那这2个的内存地址不是连续的 p1 *Point p2 *Point } func main() { //r1不用分配内存,由于定义完了后,内存就已经分配好了,没有指针类型字段 var r1 Rect //r2里面是2个指针类型,因此须要分配内存,就须要new var r2 RectA r2.p1 = new(Point) r2.p2 = new(Point) //r1的内存布局 fmt.Printf("ADD:%p\n",&r1.p1.x) fmt.Printf("ADD:%p\n",&r1.p1.y) fmt.Printf("ADD:%p\n",&r1.p2.x) fmt.Printf("ADD:%p\n",&r1.p2.y) fmt.Println() //r2的内存布局 fmt.Printf("ADD:%p\n",&r2.p1.x) fmt.Printf("ADD:%p\n",&r2.p1.y) fmt.Printf("ADD:%p\n",&r2.p2.x) fmt.Printf("ADD:%p\n",&r2.p2.y) }
Go struct 构造函数
go struct 没有构造函数,那就须要本身实现,通常可使用工厂函数,来解决这个问题
type School struct{ Name string Addr string } func NewSchool(name,addr string) (*School) { //& 内存地址, new也是一个意思 //实例化struct return &School{ Name:name, Addr:addr, } //或者能够用new //第二种写法 //p:=new(School) //p.Name = name //p.Age = age //return p } func main() { s:=School{Name:"aa",Addr:"assaas"} fmt.Printf(s) }
Struct 中的方法
访问控制:经过大小写控制,小写不能被其余包外面调用
方法能够做用在特定类型上
函数能够随意调用
函数传参是副本的调用
给struct添加方法
实例代码:
type People struct{ Name string Score int all int string } type Student struct{ Name string Age int //使用了匿名字段,实现了继承 People all int } //给people这个struct添加了 Format方法 func (p *People) Format() string { return fmt.Sprintf("name=%s,age=%d",p.Name,p.Score) } //给Student这个struct添加了 方法 func (p *Student) Format() string { return fmt.Sprintf("name=%s,age=%d",p.Name,p.Score) } func mian(){ var s Student //访问父类中的方法 ret :=s.People.Format() fmt.Printf(ret) var d People //访问d中的方法 res := d.Format() fmt.Printf("11111",res) }
三种函数接收方式
定义函数:
func Add(a, b int) int { return a + b } func testInt() { c := Add(100, 200) fmt.Println(c) }
值类型接受方式:
type Int int //这种写法,前面(i Int) 是接受者参数 //在函数的前面加了自定义类型,和变量 //那Bdd的接受者就是i了, 那Bdd也就是Int类型的一个方法了, func (i Int) Bdd(a, b int) int { return a + b } func main(){ var a Int d := a.Bdd(100, 200) fmt.Println(d) }
指针类型接收方式:
func (i *Int) Cdd(a, b int) { //这里须要作强制转换,由于a,b是小写int, 但i是大写int, 那赋值就须要强制转换 *i = Int(a + b) //这样a+b的结果就存在i这个变量里面了 return } var e Int //传递给Cdd的只是e的拷贝,若是想修改e的值,那传递的时候,须要传递e的指针地址 e.Cdd(200, 300) //&(e).Cdd(200,300) 也是同样的, 由于e调用的时候,发现要传一个地址,那go会自动的转换成&(e),你能够不用写&,由于go帮你作了 fmt.Println(e)
在来一列struct 添加方法栗子
栗子1:
type Student struct{ Name string Age int } //struct也是值类型,因此当要修改值,传递给函数的时候,就须要传递指针类型 func (s *Student) SiteSet(name string,age int){ s.Name = name s.Age = age } func teststrcut() { //这样就给struct定义了方法 var s Student s.SiteSet("abc",20) fmt.Println(s) }
栗子2:
func NewSchool(name,addr string) (*School) { return &School{ Name:name, Addr:addr, } func (s *School) getaddr() string { return s.Addr } func (s *School) Getaddr() string { return s.Addr } s :=model.NewSchool("北京","海淀") //调用了实例中的方法 fmt.Printf("school_name:%s",s.Getname()) fmt.Printf("school_name:%s",s.Getaddr())
Struct 中的tag
咱们能够为struct中的每一个字段,写上⼀个tag。这个tag能够经过反射的机制获取到,最常⽤的场景就是json序列化和反序列化
以下代码中,Zcq作了tag标记,那json就能够经过反射方式匹配值,json会序列化结构体里面的tag,
key:json 后写的值
value: 赋值的参数
在struct中 首字母若是是小写,那就是私有的,只能在main包里面访问 作了tag,那json序列化后的key 能够自定义,解决了:在特殊状况下,必须用小写,可是在go里面小写命名的值,json访问不到的状况,完美
type Student struct{ Name string Age int Sex string ceshi string //作了tag标记, 还能够写多个值, 日后会学到怎么取这个值 Zcq string `json:"rch";db:"name"` } func main(){ s.Zcq = "zcq" } >>> {"Name":"ZCQ","Age":12,"Sex":"mem","rch":"zcq"}
Json序列化/反序列化
序列化
//Marshal 序列化 //data 是一个bytes的切片, //Marshl是封装 data,err :=json.Marshal(s) if err != nil{ fmt.Printf("json.Marshal---error",err) return }
反序列化
var s1 Student //Unmarshal :反序列化 //须要传入地址的值,直接去修改值,若是传入的是s1,那传入的是一个副本, //err这里不须要:= 了,由于上面已经声明了 err =json.Unmarshal(data,&s1) if err!=nil{ fmt.Printf("json.Unmarshal---error",err) return } fmt.Printf("s1%#v\n",s1)
匿名字段/继承
定义:结构体中字段能够没有名字,即匿名字段,
注意:不能存在相同的名字
type People struct{ Name string Score int all int //没有名字,那就是匿名字段,不能存在相同名字 string }
继承
注意:在继承中,若是父类和子类都有同一个方法,那么优先访问子类中的方法,
type Student struct{ Name string Age int // People 是上面代码中的struct, //使用了匿名字段,实现了继承 People //若是都有相同的字段,那默认会先找自己student里面的字段 //相同持有相同字段的话,就须要s.People.Name 才能够访问到 all int } func test1() { var s Student s.Name = "abc" s.Age = 100 s.string = "11" ////若是字段没有名字,就用它的类型,来操做这个字段 //s.int = 2000 //s.People.Name = 100 //继承中能够直接访问 s.Score = 100 fmt.Printf("%#v\n",s) } func testMethod() { var s Student //由于s 继承了People,并且又给People添加了一个方法,因此能够直接访问到这个方法 s.Age = 200 s.People.Name = "anc" //访问父类中的方法 ret :=s.People.Format() //访问子类中的方法s.Format() fmt.Printf(ret) }