首先声明,Golang不能算是一种面向对象的语言。因为:

  1. golang只支持封装,不支持继承和多态
  2. golang只有struct,没有class

结构(struct)创建在堆上还是栈上?

  1. cpp中,局部变量分配在栈上,在外界也要使用的变量要分配到堆上,并且要手动释放
  2. java中,对象都分配在堆上,有对应的垃圾回收机制
  3. go中不需要知道分配在堆上还是栈上。比如返回了局部变量的地址,那么是分配在堆上,并有对应的垃圾回收,这些都是编译器自己识别并实现的

值接收者vs指针接收者

  1. 要改变内容必须使用指针接收者
  2. 结构过大也考虑使用指针接收者
  3. 一致性:如果有指针接收者,最好都是指针接收者
  4. 值接收者是go语言特有,值/指针接收者均可接受值/指针

封装

  1. 名字一般使用CamelCase形式
  2. 首字母大写:public
  3. 首字母小写:private
  4. 首字母大小写是针对包(package)来说的

  1. 每个目录一个包
  2. main包包含可执行入口,如果这个包里有main函数,那么这个目录下只能有一个main包
  3. 为结构定义的方法必须放在同一个包内,可以是不同文件

如何扩充系统类型或者别人的类型

  1. 定义别名(见代码2.1)
  2. 使用组合(见代码1.3)
1

1.1 tree/node.go:

package tree

import (
    "fmt"
    )

type Node struct {
    Value int
    Left, Right *Node
}

// 定义结构体的方法,(node treeNode)是接收者,表示print()方法是给treeNode来用的
func (node Node) Print() {
    fmt.Print(node.Value)
    fmt.Println()
}

// 注意如果不是指针接收者,那么是值传递,改变不了值
func (node *Node) SetValue(value int) {
    if node == nil {
        fmt.Println("Setting value to nil node. Ignored.")
        return
    }
    node.Value = value
}

// 自定义工厂函数:相当于自己写的构造函数。go没有构造函数
func CreateNode(value int) *Node {
    return &Node{Value: value} // 可以返回局部变量的地址
}

1.2 tree/traversal.go:

package tree

// 前序遍历
func (node *Node) Traverse() {
    if node == nil {
        return
    }
    node.Left.Traverse()
    node.Print()
    node.Right.Traverse()
}

1.3 tree/entry/entry.go:

package main

import (
    "fmt"
    "learngo/tree"
)

// 扩充类型方法1:组合方式

type myTreeNode struct {
    node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
    if myNode == nil || myNode.node == nil {
        return
    }
    left := myTreeNode{myNode.node.Left}
    right := myTreeNode{myNode.node.Right}

    left.postOrder()
    right.postOrder()
    myNode.node.Print()
}

func main() {
    nodes := []tree.Node{
        {Value: 3},
        {},
        {6, nil, nil},
    }

    fmt.Println(nodes)
    var root tree.Node
    root = tree.Node{Value: 3}
    root.Left = &tree.Node{}
    root.Right = &tree.Node{5, nil, nil}
    root.Right.Left = new(tree.Node)
    root.Left.Right = tree.CreateNode(2)
    fmt.Println(root)

    root.Right.Left.SetValue(4)
    root.Right.Left.Print()

    // 值接收者传地址也可以,自动取值出来使用
    pRoot := &root
    pRoot.Print()
    pRoot.SetValue(200)
    pRoot.Print()
    // nil指针也可以调用方法
    fmt.Println("use nil ptr:")
    var pRoot2 *tree.Node
    pRoot2.SetValue(200)
    pRoot2 = &root
    pRoot2.SetValue(300)
    pRoot2.Print()

    fmt.Println("Pre Traversing:")
    root.Traverse()
    fmt.Println()
    // 扩充类型方法1:组合方式
    fmt.Println("Post Traversing:")
    myRoot := myTreeNode{&root}
    myRoot.postOrder()
    fmt.Println()
}

1.4 Output:

[{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> <nil>}]
{3 0xc42009c0a0 0xc42009c0c0}
4
3
200
use nil ptr:
Setting value to nil node. Ignored.
300
Pre Traversing:
0
2
300
4
5

Post Traversing:
2
0
4
5
300
2

2.1 queue/queue.go:

package queue

// 扩充类型方法2:使用别名

type Queue []int

func (q *Queue) Push(v int) {
    *q = append(*q, v)
}

func (q *Queue) Pop() int {
    head := (*q)[0]
    *q = (*q)[1:]
    return head
}

func (q *Queue) IsEmpty() bool {
    return len(*q) == 0
}

2.2 queue/entry/main.go:

package main

import (
    "fmt"
    "learngo/queue"
)

func main() {
    q := queue.Queue{1}

    q.Push(2)
    q.Push(3)
    fmt.Println(q.Pop())
    fmt.Println(q.Pop())
    fmt.Println(q.IsEmpty())
    fmt.Println(q.Pop())
    fmt.Println(q.IsEmpty())
}

2.3 Output:

1
2
false
3
true