首先声明,Golang不能算是一种面向对象的语言。因为:
- golang只支持封装,不支持继承和多态
- golang只有struct,没有class
结构(struct)创建在堆上还是栈上?
- cpp中,局部变量分配在栈上,在外界也要使用的变量要分配到堆上,并且要手动释放
- java中,对象都分配在堆上,有对应的垃圾回收机制
- go中不需要知道分配在堆上还是栈上。比如返回了局部变量的地址,那么是分配在堆上,并有对应的垃圾回收,这些都是编译器自己识别并实现的
值接收者vs指针接收者
- 要改变内容必须使用指针接收者
- 结构过大也考虑使用指针接收者
- 一致性:如果有指针接收者,最好都是指针接收者
- 值接收者是go语言特有,值/指针接收者均可接受值/指针
封装
- 名字一般使用CamelCase形式
- 首字母大写:public
- 首字母小写:private
- 首字母大小写是针对包(package)来说的
包
- 每个目录一个包
- main包包含可执行入口,如果这个包里有main函数,那么这个目录下只能有一个main包
- 为结构定义的方法必须放在同一个包内,可以是不同文件
如何扩充系统类型或者别人的类型
- 定义别名(见代码2.1)
- 使用组合(见代码1.3)
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