GO語言系列(五)- 結構體和接口

結構體(Struct)

Go中struct的特色python

  • 1. 用來自定義複雜數據結構golang

  • 2. struct裏面能夠包含多個字段(屬性)json

  • 3. struct類型能夠定義方法,注意和函數的區分數據結構

  • 4. struct類型是值類型app

  • 5. struct類型能夠嵌套ide

  • 6. Go語言沒有class類型,只有struct類型函數

1、struct的定義

1.struct的聲明

type 標識符 struct {
       field1 type
       field2 type
}  

例子佈局

type Student struct {
       Name string
       Age int
Score int
}

2. struct 中字段訪問:和其餘語言同樣,使用點  

var stu Student

stu.Name = 「tony」
stu.Age = 18
stu.Score=20

fmt.Printf(「name=%s age=%d score=%d」, stu.Name, stu.Age, stu.Score

3.  struct定義的三種形式:

a. var stu Student
b. var stu *Student = new (Student)
c. var stu *Student = &Student{}

其中b和c返回的都是指向結構體的指針,訪問形式以下:this

stu.Name、stu.Age和stu.Score或者 (*stu).Name、(*stu).Age等

例子spa

package main

import "fmt"

type Student struct {
    Name  string
    Age   int32
    score float32 // 外部的包訪問不了這個字段
}

func main() {
    // 結構體的三種定義方式
    // 方式一
    var stu Student
    stu.Name = "zhangyafei"
    stu.Age = 24
    stu.score = 88

    fmt.Printf("Name: %p\n", &stu.Name) // string佔10字節
    fmt.Printf("Age: %p\n", &stu.Age)   // int佔8字節  int32佔4字節
    fmt.Printf("score: %p\n", &stu.score)

    // 方式二
    var stu1 *Student = &Student{
        Age:  20,
        Name: "ZhangYafei",
    }
    fmt.Println(stu1)
    fmt.Println(stu1.Name)

    // 方式三
    var stu2 = Student{
        Age:  20,
        Name: "Fei",
    }
    fmt.Println(stu2)
    fmt.Println(stu2.Age)

}

// Name: 0xc000004460
// Age: 0xc000004470
// score: 0xc000004478

// Age int32
// Name: 0xc000050400
// Age: 0xc000050410
// score: 0xc000050414
// &{ZhangYafei 20 0}
// {Fei 20 0}
struct的定義示例

2、struct的初始化

1. struct的內存佈局

 struct中的全部字段在內存是連續的,佈局以下:

2. 鏈表定義

type Student struct {
    Name string
    Next* Student
}

每一個節點包含下一個節點的地址,這樣把全部的節點串起來了,一般把鏈表中的第一個節點叫作鏈表頭

3. 雙鏈表定義

type Student struct {
    Name string
    Next* Student
    Prev* Student
}

若是有兩個指針分別指向前一個節點和後一個節點,咱們叫作雙鏈表

4. 二叉樹定義

type Student struct {
    Name string
    left* Student
    right* Student
}

若是每一個節點有兩個指針分別用來指向左子樹和右子樹,咱們把這樣的結構叫作二叉樹

5. 結構體是用戶單獨定義的類型,不能和其餘類型進行強制轉換

type Student struct {
Number int
}

type Stu Student //alias

var a Student
a = Student(30)

var b Stu
a = b

例子

package main

import (
    "fmt"
    "math/rand"
)

type Student struct {
    Name  string
    Age   int
    Score float32
    next  *Student
}

func trans(p *Student) {
    // 遍歷鏈表
    for p != nil {
        fmt.Println(*p)
        p = p.next
    }
}

func insertTail(p *Student) {
    // 尾插法
    var tail = p
    for i := 0; i < 10; i++ {
        stu := &Student{
            Name:  fmt.Sprintf("stu%d", i),
            Age:   rand.Intn(100),
            Score: rand.Float32() * 100,
        }
        tail.next = stu
        tail = stu
    }
}

func insertHead(head **Student) {
    // 頭插法
    for i := 0; i < 10; i++ {
        stu := &Student{
            Name:  fmt.Sprintf("stu%d", i),
            Age:   rand.Intn(100),
            Score: rand.Float32() * 100,
        }
        stu.next = *head
        *head = stu
    }
}

func delNode(p *Student) {
    var prev *Student = p
    for p != nil {
        if p.Name == "stu6" {
            prev.next = p.next
            break
        }
        prev = p
        p = p.next
    }
}

func addNode(p *Student, newNode *Student) {
    for p != nil {
        if p.Name == "stu6" {
            newNode.next = p.next
            p.next = newNode
            break
        }
        p = p.next
    }
}

func main() {
    // var head *Student = &Student{}
    var head *Student = new(Student)
    head.Name = "ZhangYafei"
    head.Age = 2
    head.Score = 88

    // 尾插
    // insertTail(head)
    // 頭插
    insertHead(&head)
    // 遍歷
    trans(head)
    // 刪除
    delNode(head)
    trans(head)

    // 指定位置插入節點
    var newNode *Student = new(Student)
    newNode.Name = "newstu"
    newNode.Age = 34
    newNode.Score = 100
    addNode(head, newNod)
}
鏈表的頭插、尾插、遍歷、刪除和指定位置插入
package main

import "fmt"

type Student struct {
    Name  string
    Age   int
    Score float32
    left  *Student
    right *Student
}

func PreOrdertrans(root *Student) {
    if root == nil {
        return
    }
    // 打印這棵樹的節點
    fmt.Println(root)
    // 遞歸遍歷左子樹
    PreOrdertrans(root.left)
    // 遞歸遍歷右子樹
    PreOrdertrans(root.right)
}

func InOrdertrans(root *Student) {
    if root == nil {
        return
    }
    // 遞歸遍歷左子樹
    InOrdertrans(root.left)
    // 打印這棵樹的節點
    fmt.Println(root)
    // 遞歸遍歷右子樹
    InOrdertrans(root.right)
}

func PostOrdertrans(root *Student) {
    if root == nil {
        return
    }
    // 遞歸遍歷左子樹
    PostOrdertrans(root.left)
    // 遞歸遍歷右子樹
    PostOrdertrans(root.right)
    // 打印這棵樹的節點
    fmt.Println(root)
}
func main() {
    var root *Student = new(Student)
    root.Name = "Zhangyafei"
    root.Age = 18
    root.Score = 88

    var left1 *Student = new(Student)
    left1.Name = "left1"
    left1.Age = 18
    left1.Score = 88

    root.left = left1

    var right1 *Student = new(Student)
    right1.Name = "right1"
    right1.Age = 18
    right1.Score = 88

    root.right = right1

    var left2 *Student = new(Student)
    left2.Name = "left2"
    left2.Age = 18
    left2.Score = 88

    left1.left = left2

    fmt.Println("前序遍歷:")
    PreOrdertrans(root)
    fmt.Println("中序遍歷:")
    InOrdertrans(root)
    fmt.Println("後序遍歷:")
    PostOrdertrans(root)
}
二叉樹的前、中、後序遍歷
package main

import "fmt"

type integer int

type Student struct {
    Number int
}

type Stu Student //alias 別名

func main() {
    var i integer = 1000
    var j int = 100
    // 變量操做必須同類型,須要強制轉換類型
    j = int(i)
    fmt.Println(j)

    var a Student
    a = Student{30}

    var b Stu
    a = Student(b)
    fmt.Println(a)
}
變量的強制類型轉換

3、工廠模式

golang中的struct沒有構造函數,通常可使用工廠模式來解決這個問題

Package model
type student struct {
Name stirng
Age int
}

func NewStudent(name string, age int) *student {
return &student{
Name:name,
Age:age,
}
}

Package main
S := new (student)
S := model.NewStudent(「tony」, 20)

 4、struct中的tag

 咱們能夠爲struct中的每一個字段,寫上一個tag。這個tag能夠經過反射的
機制獲取到,最經常使用的場景就是json序列化和反序列化

type student struct {
    Name stirng 「this is name field」
    Age int 「this is age field」
}

示例

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name  string `json:"name"` // json打包的時候用name
    Age   int    `json:"age"`
    Score int    `json:"score"`
}

func main() {
    var stu Student = Student{
        Name:  "ZhangYafei",
        Age:   24,
        Score: 88,
    }
    data, err := json.Marshal(stu)
    if err != nil {
        fmt.Println("json encoder stu failed, err", err)
        return
    }
    fmt.Println(string(data))
}

// {"name":"ZhangYafei","age":24,"score":88}
json序列化

5、匿名字段

1. 結構體中字段能夠沒有名字,即匿名字段

type Car struct {
    Name stirng
    Age int 
}

type Train struct {
    Car
    Start time.Time
    int
}

2. 匿名字段衝突處理

type Car struct {
    Name string
    Age int 
}

type Train struct {
    Car
    Start time.Time
    Age int
}
type A struct {
    a int
}

type B struct {
    a int
    b int
}

type C struct {
    A
    B
}

示例

package main

import (
    "fmt"
    "time"
)

type Cart1 struct {
    name string
    age  int
}

type Cart2 struct {
    name string
    age  int
}

type Train struct {
    Cart1
    Cart2
    int
    start time.Time
    age   int
}

func main() {
    var t Train
    // 訪問匿名字段
    // 方式一
    t.Cart1.name = "001"
    t.Cart1.age = 300
    t.Cart2.name = "002"
    t.Cart2.age = 400
    // 方式二
    // t.name = "train"
    t.age = 100
    t.int = 200

    fmt.Println(t)
}
訪問匿名字段

6、方法

1. Golang中的方法是做用在特定類型的變量上,所以自定義類型,均可以有方法,而不只僅是struct

定義:func (recevier type) methodName(參數列表)(返回值列表){}

2. 方法的調用

type A struct {
a int
}
func (this A) test() {
fmt.Println(this.a)
}

var t A
t.test()

3. 方法和函數的區別

函數調用: function(variable, 參數列表)
方法:variable.function(參數列表)

4. 指針receiver vs 值receiver

  本質上和函數的值傳遞和地址傳遞是同樣的

5. 方法的訪問控制,經過大小寫控制

6.繼承

若是一個struct嵌套了另外一個匿名結構體,那麼這個結構能夠直接訪問匿名結構體的方法,從而實現了繼承。

7. 組合和匿名字段

若是一個struct嵌套了另外一個匿名結構體,那麼這個結構能夠直接訪問匿名結構體的方法,從而實現了繼承。若是一個struct嵌套了另外一個有名結構體,那麼這個模式就叫組合。

8. 多重繼承

若是一個struct嵌套了多個匿名結構體,那麼這個結構能夠直接訪問多個匿名結構體的方法,從而實現了多重繼承。21. 實現String()

若是一個變量實現了String()這個方法,那麼fmt.Println默認會調用這個變量的String()進行輸出。

示例

package main

import "fmt"

type integer int

func (p integer) print() {
    fmt.Println("p is", p)
}

func (p *integer) set(b integer) {
    *p = b
}

type Student struct {
    Name  string
    Age   int
    Score int
    sex   int
}

func (p *Student) init(name string, age int, score int) {
    p.Name = name
    p.Age = age
    p.Score = score
    fmt.Println(p)
}

func (p Student) get() Student {
    return p
}

func main() {
    var stu Student
    stu.init("stu", 10, 200)
    stu1 := stu.get()
    fmt.Println(stu1)

    var a integer
    a = 10
    a.print()

    a.set(1000)
    a.print()
}
自定義方法
package main

import "fmt"

type Car struct {
    weight int
    name   string
}

func (self *Car) Run() {
    fmt.Println(self, "is running")
}

type Bike struct {
    Car
    lunzi int
}

type Train struct {
    c Car
}

func main() {
    var a Bike
    a.weight = 100
    a.name = "bike"
    a.lunzi = 2

    fmt.Println(a)
    a.Run()

    var b Train
    b.c.weight = 100
    b.c.name = "train"
    b.c.Run()
}
繼承
package main

import "fmt"

type Car struct {
    weight int
    name   string
}

func (self *Car) Run() {
    fmt.Println(self, "is running")
}

type Bike struct {
    Car
    lunzi int
}

type Train struct {
    c Car
}

func (self Train) String() string {
    str := fmt.Sprintf("name=[%s] weight=[%d]", self.c.name, self.c.weight)
    return str
}

func main() {
    var a Bike
    a.weight = 100
    a.name = "bike"
    a.lunzi = 2

    fmt.Println(a)
    a.Run()

    var b Train
    b.c.weight = 100
    b.c.name = "train"
    b.c.Run()
    fmt.Printf("%s", b)
}
實現String方法

接口

1、Go中的接口

1.定義

Interface類型能夠定義一組方法,可是這些不須要實現。而且interface不能包含任何變量。

type example interface{
    Method1(參數列表) 返回值列表    
    Method2(參數列表) 返回值列表
…
}

interface類型默認是一個指針

type example interface{

  Method1(參數列表) 返回值列表
  Method2(參數列表) 返回值列表
…
}

var a example
a.Method1() 

2. 接口實現

  • a. Golang中的接口,不須要顯示的實現。只要一個變量,含有接口類型中的全部方法,那麼這個變量就實現這個接口。所以,golang中沒有implement相似的關鍵字
  • b. 若是一個變量含有了多個interface類型的方法,那麼這個變量就實現了多個接口。
  • c. 若是一個變量只含有了1個interface的方部分方法,那麼這個變量沒有實現這個接口。

3. 多態

一種事物的多種形態,均可以按照統一的接口進行操做

4. 接口嵌套

type ReadWrite interface {
               Read(b Buffer) bool
               Write(b Buffer) bool
} 
type Lock interface {
               Lock()
               Unlock() 
} 
type File interface {
               ReadWrite
               Lock 
               Close() 
}  

2、類型斷言

1. 類型斷言

  因爲接口是通常類型,不知道具體類型,若是要轉成具體類型能夠採用如下方法進行轉換:

var t int
var x interface{}
x = t
y = x.(int)   //轉成int
var t int
var x interface{}
x = t
y, ok = x.(int)   //轉成int,帶檢查

2. 練習,寫一個函數判斷傳入參數的類型

func classifier(items ...interface{}) {
          for i, x := range items { 
                  switch x.(type) {
                   case bool:       fmt.Printf(「param #%d is a bool\n」, i)
                   case float64:    fmt.Printf(「param #%d is a float64\n」, i)
                   case int, int64: fmt.Printf(「param #%d is an int\n」, i)
                   case nil: fmt.Printf(「param #%d is nil\n」, i)
                   case string: fmt.Printf(「param #%d is a string\n」, i)
                    default: fmt.Printf(「param #%d’s type is unknown\n」, i)
            }
}

 

3. 類型斷言,採用type switch方式

4.空接口

 空接口沒有任何方法,因此全部類型都實現了空接口。Interface{}

var a int
var b interface{}
b = a 

示例

package main

import "fmt"

type People struct {
    name string
    age  int
}

type Test interface {
    Print()
    Sleep()
}

type Student struct {
    name  string
    age   int
    score int
}

func (self *Student) Print() {
    fmt.Println("name:", self.name)
    fmt.Println("age:", self.age)
    fmt.Println("score:", self.score)
}

func (self People) Print() {
    fmt.Println("name:", self.name)
    fmt.Println("age:", self.age)
}

func (self People) Sleep() {
    fmt.Println("people is sleep")
}

func (self Student) Sleep() {
    fmt.Println("student is sleep")
}

func main() {
    var t Test
    var stu Student = Student{
        name:  "Zhangyafei",
        age:   24,
        score: 88,
    }
    t = &stu
    t.Print()

    var people People = People{
        name: "people",
        age:  24,
    }
    t = people
    t.Print()
    t.Sleep()
}
接口示例

擴展:實現一個圖書管理系統,具備如下功能:

  • a. 書籍錄入功能,書籍信息包括書名、副本數、做者、出版日期
  • b. 書籍查詢功能,按照書名、做者、出版日期等條件檢索
  • c. 學生信息管理功能,管理每一個學生的姓名、年級、身份證、性別、借了什麼書等信息
  • d. 借書功能,學生能夠查詢想要的書籍,進行借出

參考

package model

import (
    "errors"
    "time"
)

var (
    ErrStockNotEnough = errors.New("stock is not enough")
)

type Book struct {
    Name       string
    Total      int
    Author     string
    CreateTime time.Time
}

func CreateBook(name string, total int, author string, createTime time.Time) (b *Book) {
    b = &Book{
        Name:       name,
        Total:      total,
        Author:     author,
        CreateTime: createTime,
    }
    return
}

func (self *Book) canBorrow(c int) bool {
    return self.Total >= c
}

func (self *Book) Borrow(c int) (err error) {
    if self.canBorrow(c) == false {
        err = ErrStockNotEnough
        return
    }
    self.Total -= c
    return
}

func (self *Book) Back(c int) (err error) {
    self.Total += c
    return
}
book.go
package model

import (
    "errors"
)

var (
    ErrNotFoundBook = errors.New("not found book")
)

type Student struct {
    Name  string
    Grade string
    Id    string
    Sex   string
    books []*BorrowItem
}

type BorrowItem struct {
    book *Book
    num  int
}

func CreateStudent(name, grade, id, sex string) *Student {
    stu := &Student{
        Name:  name,
        Grade: grade,
        Id:    id,
        Sex:   sex,
    }
    return stu
}

func (self *Student) AddBook(b *BorrowItem) {
    self.books = append(self.books, b)
}

func (self *Student) DelBook(b *BorrowItem) (err error) {
    for i := 0; i < len(self.books); i++ {
        if self.books[i].book.Name == b.book.Name {
            if b.num == self.books[i].num {
                front := self.books[0:i]
                left := self.books[i+1:]
                front = append(front, left...)
                self.books = front
                return
            }
            self.books[i].num -= b.num
            return
        }
    }
    err = ErrNotFoundBook
    return
}

func (self *Student) GetBookList() []*BorrowItem {
    return self.books
}
stu.go
相關文章
相關標籤/搜索