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) }