Go語言中能夠基於一些基本的數據類型,使用type
關鍵字定義自定義類型的數據 。html
自定義類型定義了一個全新的類型,該新類型具備基本數據類型的特性。自定義類型定義的方法以下:程序員
type TypeName Type //將 NewType定義爲int 類型 type NewType int
NewType
是一個新的類型,其具備int
的特性。面試
類型別名是Go1.9
版本添加的新功能。利用類型別名的功能,能夠給一些基本的數據類型定義一些讓讀者見名知意的名字,從而提升代碼的可讀性。類型別名定義的方法以下:編程
type TypeAlias = Type
Go語言中的rune
和byte
就是類型別名,它們的定義以下:json
type byte = uint8 type rune = int32
自定義類型:自定義類型定義了一個全新的類型,其繼承了基本類型的全部特性,而且能夠實現新類型的特有的一些方法。數組
類型別名:只存在代碼編寫的過程,代碼編譯之後根本不存在這個類型別名。其做用用來提升代碼的可讀性。編程語言
以下代碼,體現了兩者的區別:函數
//自定義類型 type NewInt int //類型別名 type MyInt = int func main(){ var a NewInt var b MyInt var c int // c = a //? 可使用強制類型轉換c = int(a) c = b //c和b是同一類型 fmt.Println("type of a:%T\n", a) //type of a:main.NewInt fmt.Println("type of b:%T\n", b) //type of b:int }
在Go語言中可使用基本數據類型表示一些事物的屬性,可是若是想表達一個事物的所有或部分屬性,好比說一個學生(學號、姓名、年齡、班級等),這時單一的基本數據類型就不可以知足需求。ui
Go語言中提供了一種自定義數據類型,能夠將多個基本數據類型或引用類型封裝在一塊兒,這種數據類型叫struct
結構體。Go語言也是經過struct
來實現面向對象的。this
struct
來實現OOP特性的;this
指針等;// 建立一個結構體類型的student type student struct { name string age int gender string hobby []string } func main() { // 建立一個student實例 var viktor = student{ name: "viktor", age: 24, gender: "男", hobby: []string{"乒乓球", "羽毛球"}, } fmt.Println(viktor) // {viktor 24 男 [乒乓球 羽毛球]} // 分別取出viktor實例中的每一個字段 fmt.Println(viktor.name) // viktor fmt.Println(viktor.age) // 24 fmt.Println(viktor.gender) // 男 fmt.Println(viktor.hobby) // [乒乓球 羽毛球] }
基本語法:
type StructName struct { field1 type field2 type }
示例,聲明一個學生的結構體Student
:
type Student struct { Name string Age int Score float32 }
struct
中封裝了一些基本數據類型的變量,咱們稱之爲結構體字段或者是該結構體的屬性。
字段是結構體的一個組成部分,通常是基本數據類型、數組,也能夠是引用類型,甚至是struct
(嵌套結構體)等。
注意事項和細節說明:
字段聲明語法同變量;
在建立一個結構體變量後,若是沒有給字段賦值,都對應一個零值(默認值):布爾類型是false,數值是0,字符串是"",數組的默認值和它的元素類型相關,好比 score [3]int
則爲[0, 0, 0]
,指針、slice和map的零值都是nil
,即尚未分配空間。
type Person struct { Name string Age int Scores [5]float64 ptr *int slice []int map1 map[string]string } func main() { //定義結構體變量 var p1 Person fmt.Println(p1) if p1.ptr == nil { fmt.Println("ok1") } if p1.slice == nil { fmt.Println("ok2") } if p1.map1 == nil { fmt.Println("ok3") } p1.slice = make([]int, 10) p1.slice[0] = 100 p1.map1 = make(map[string]string) p1.map1["key1"] = "tom" fmt.Println(p1) }
能夠這樣理解,聲明一個結構體相似創造一個模具,若是想要真正的描述一個事物,那麼就得使用這個模具來製造一個事物,這個製造事物的過程稱爲 建立結構體變量或者結構體實例化。結構體的實例化有四種方式:
type Person struct { Name string Age int } func main() { //定義結構體變量 var p1 Person fmt.Println(p1) }
//使用值列表初始化 p2 := Person{"tom", 20} fmt.Println(p2)
struct
是值類型,那麼就可使用new
關鍵字定義一個結構體指針:
var p3 *Person = new(Person) (*p3).Name = "smith" p3.Name = "john" (*p3).Age = 20 p3.Age = 30 fmt.Println(p3)
//使用鍵值對初始化 var person *Person = &Person{ Name : "tom", Age : 19, } //也能夠經過字段訪問的形式進行賦值 (*p3).Name = "scott" p3.Name = "scott~" (*p3).Age = 20 p3.Age = 30 fmt.Println(p3)
說明:
(*結構體指針).字段名
;person.Name
作了轉化(*person).Name
。type Point struct { x, y int } type Rect struct { leftUp, rightDown Point } type Rect2 struct { leftUp, rightDown *Point } func main() { r1 := Rect(Point{1,2}, Point{3,4}) //r1有四個int,在內存中是連續分佈 fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p", &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y) //r2有兩個*Point類型,這兩個*Point類型的自己地址也是連續的,可是其指向的地址不必定是連續的 r2 := Rect(&Point{10,20}, Point{30,40}) fmt.Printf("r2.leftUp 自己地址=%p r2.rightDown 自己地址=%p", &r2.leftUp, &r2.rightDown) fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p", r2.leftUp, r2.rightDown) }
struct
的每一個字段上,能夠寫上一個tag
,該tag
能夠經過反射機制獲取,常見的使用場景:序列化和反序列化。type Monster struct { Name string `json:"name"` Age int `json:"age"` Skill string `json:"skill"` } func main() { //建立一個Monster實例 monster := Monster{"蜘蛛精", 200, "吐絲"} //將monster序列化 jsonStr, err := json.Marshal(monster) if err != nil { fmt.Println("json 處理錯誤", err) } fmt.Println("jsonStr", string(jsonStr)) }
下面代碼的執行結果?
type student struct { name string age int } func main() { m := make(map[string]*student) stus := []student{ {name: "李四", age: 18}, {name: "張三", age: 23}, {name: "李明", age: 9000}, } for _, stu := range stus { m[stu.name] = &stu } for k, v := range m { fmt.Println(k, "=>", v.name) } }
方法是什麼?在聲明瞭一個結構體後,好比說Person
結構體,那麼這我的都有哪些功能,或者說都有什麼能力,這些功能或者能力就是一個結構體的方法。
Golang中的方法是做用在指定的數據類型上(即,和指定的數據類型綁定),所以自定義類型,均可以有方法,而不只僅是struct
。以下示例:
//MyInt 將int定義爲自定義MyInt類型 type MyInt int //SayHello 爲MyInt添加一個SayHello的方法 func (m MyInt) SayHello() { fmt.Println("Hello, 我是一個int。") } func main() { var m1 MyInt m1.SayHello() //Hello, 我是一個int。 m1 = 100 fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt }
方法的聲明語法:
//聲明一個自定義類型struct type A struct { Num int } //聲明A類型的方法 func (a A) test() { //其中的(a A)表示test方法和A類型綁定 fmt.Println(a.Num) }
舉例說明:
type Person struct { Name string } func (p Person) test() { fmt.Println("test() name=", p.Name) } func main() { var p Person //實例化 p.Name = "tom" p.test() //調用方法 }
test
方法和Person
類型綁定;
test
方法只能經過Person
類型的變量來調用,不能直接調用,也不能使用其它類型變量來調用;
func (p Person) test() {...}
,其中p
表示哪一個Person
變量調用,這個p就是它的副本,表明接收者。這點和函數傳參很是類似,而且p
能夠有程序員任意指定;
在3.1中 提到,方法要和指定自定義類型的變量綁定,那個這個綁定方法的變量被稱爲接收者,而方法的傳參機制被這個接收者的類型不一樣分爲值類型的接收者和指針類型的接收者,下面分別來看這這兩方式的傳參機制:
指針類型的接收者由一個結構體的指針組成,因爲指針特性,調用方法時修改接收者的任意成員變量,在方法接收後,修改都是有效的。例如爲Person
結構體添加一個SetAge
方法,來修改實例中的年齡:
//Person 結構體 type Person struct { name string age int8 } // SetAge 設置p的年齡 // 使用指針接收者 func (p *Person) SetAge(newAge int8) { p.age = newAge } func main() { p1 := NewPerson("小王子", 25) fmt.Println(p1.age) // 25 p1.SetAge(30) fmt.Println(p1.age) // 30 }
當方法和值類型接收者綁定是,Go語言會在代碼運行時將接收者的值複製一份 。在值類型接收者的方法中能夠獲取接收者的成員值,但修改操做只是針對副本,沒法修改接收者變量自己。
// SetAge2 設置p的年齡 // 使用值接收者 func (p Person) SetAge2(newAge int8) { p.age = newAge } func main() { p1 := NewPerson("張三", 25) p1.Dream() fmt.Println(p1.age) // 25 p1.SetAge2(30) // (*p1).SetAge2(30) fmt.Println(p1.age) // 25 }
聲明方式不同
func 函數名(形參列表) (返回值列表) {...}
func (變量 自定義類型) 函數名(形參列表) (返回值列表) {...}
調用方式不同
函數名(實參列表)
變量.方法名(實參列表)
對於普通函數,接收者爲值類型時,不能講指針類型的數據直接傳遞,反之亦然;
type Person struct { Name string } func test01(p Person) { fmt.Println(p.Name) } func test02(p *Person) { fmt.Println(p.Name) } func main() { var p = Person{"tom"} test01(p) test02(&p) }
struct
的方法),接收者爲值類型時,能夠直接用指針類型的變量調用方法,反之亦然;func (p Person) test03() { p.Name = "jack" fmt.Println(p.Name) //jack } func (p *Person) test03() { p.Name = "jerry" fmt.Println(p.Name) //jerry } func main() { p := Person{"viktor"} p.test03() fmt.Println(p.Name) // viktor (&p).test03() //從形式上傳入地址,可是本質扔然是值拷貝 fmt.Println(p.Name) // viktor (&p).test04() fmt.Println(p.Name) // jerry p.test04() //等價於(&p).test04(),從形式上是傳入值類型,可是本事仍然是地址拷貝 }
對於方法來講,無論調用形式如何,真正決定是之拷貝仍是地址拷貝,看這個方法是和哪一種類型綁定,也就是接收者的類型是值類型仍是指針類型。
Golang的結構體沒有構造函數,一般可使用工廠模式來解決這個問題。
首選,在Golang語言中公有和私有變量這一說法。若是說一個包中的變量的首字母爲小寫,在其餘包若是引入這個包,就不能訪問這個變量;若是這個變量的變量名爲大寫字母,那麼能夠直接訪問。
一樣對於自定義的struct
類型也是,而工廠模式,就是爲了解決變量的首字母爲小寫的結構體可以被其它包引用的問題。
//student.go屬於model包 package model //定義一個結構體 type student struct { Name string Score float64 } func NewStudent(n string, s float64) *stuent { return &student{ Name : n, Score : s, } } //mian.go中聲明一個student實例,並初始化 func main() { var stu = model.NewStudent("viktor", 86.6) fmt.Println(*stu) fmt.Println("name=", stu.Name, "score=", stu.Score) }
另外,若是結構體中的某個字段的首字母也爲小寫該如何訪問?
//student.go屬於model包 package model //定義一個結構體 type student struct { Name string score float64 } func NewStudent(n string, s float64) *stuent { return &student{ Name : n, Score : s, } } func (s *student) GetScore() float64 { return s.score } //mian.go中聲明一個student實例,並初始化 func main() { var stu = model.NewStudent("viktor", 86.8) fmt.Println(*stu) fmt.Println("name=", stu.Name, "score=", stu.GetScore()) }
原文出處:https://www.cnblogs.com/dabric/p/12361979.html