Go語言開發(四)、Go語言面向對象
一、結構體和方法
1、結構體的定義
在結構體中可以為不同項定義不同的數據類型。
結構體是由一系列具有相同類型或不同類型的數據構成的數據集合。
結構體定義需要使用type和struct語句。struct語句定義一個新的數據類型,結構體有中有一個或多個成員。type語句設定了結構體的名稱。結構體的格式如下:
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
結構體類型用於變量聲明的語法格式如下:variable_name := structure_variable_type {value1, value2...valuen}
二叉樹節點的數據結構定義如下:
package main
import "fmt"
type TreeNode struct{
Value int
Left,Right *TreeNode
}
func main() {
var root TreeNode
fmt.Println(root)
}
2、結構體成員的訪問
如果要訪問結構體成員,需要使用點號(.)操作符,格式為:"結構體.成員名"。
結構體類型變量使用struct關鍵字定義,實例如下:
func main() { var root TreeNode root.Value = 0 root.Left = &TreeNode{Value:1} root.Right = &TreeNode{2,nil,nil} root.Left.Left = &TreeNode{Value:3} root.Print() }
3、結構體方法的定義
結構體方法定義在結構體作用域外,需要在函數聲明中指定接收者。
func (variable_name struct_variable_type) function_name(parameters){
//函數體
}
如二叉樹節點的遍歷打印函數如下:
func (node TreeNode) Print(){ fmt.Println(node.Value, " ") } func (node *TreeNode)traverse(){ if node != nil{ //遞歸遍歷左子樹 node.Left.traverse() node.Print() //遞歸遍歷右子樹 node.Right.traverse() } }
4、結構體指針
如果結構體的方法中需要對結構體成員的值進行修改,必須使用結構體指針作為方法的接收者。如果結構體過大也要考慮使用結構體指針作為方法的接收者。值接收者和指針接收者都可以接收值、指針傳遞的結構體。
nil指針也可以調用方法。
package main
import (
"fmt"
)
type TreeNode struct{
Value int
Left,Right *TreeNode
}
func createTreeNode(value int) *TreeNode{
return &TreeNode{Value:value}
}
func (node TreeNode) Print(){
fmt.Println(node.Value, " ")
}
func (node *TreeNode)traverse(){
if node != nil{
//遞歸遍歷左子樹
node.Left.traverse()
node.Print()
//遞歸遍歷右子樹
node.Right.traverse()
}
}
func (node *TreeNode)setValue(value int){
if node!= nil{
node.Value = value
}else {
fmt.Println("The node is nil.")
}
}
func main() {
var root TreeNode
root.Value = 0
root.Left = &TreeNode{Value:1}
root.Right = &TreeNode{2,nil,nil}
root.Left.Left = &TreeNode{Value:3}
root.traverse()
root.setValue(100)
root.traverse()
}
二、包和封裝
1、包簡介
包用於組織Go源代碼,提供了更好的可重用性與可讀性。由於包提供了代碼的封裝,因此使得Go應用程序易於維護。
Go語言的面向對象只支持封裝,不支持繼承和多態。
Go語言使用CamelCase命名方法對函數進行命名,函數名稱的首字母大寫表示public,小寫表示private。
訪問權限是針對包的,Go語言中每個目錄是一個包,包名與目錄名可以不相同。如果目錄下有一個main函數,目錄只能有一個main包,main包包含可執行入口。
為結構體定義的方法必須在一個包內,但可以是不同的文件。
2、包的定義
所有可執行的Go程序都必須包含一個main函數,作為程序運行的入口。main函數應該放置於main包中。
包的定義語法如下:package packagename
指定某一源文件屬於一個包,應該放在每一個源文件的第一行。
導入一個已存在的包的語法如下:import "packagename"?
屬於某一個包的源文件都應該放置於一個單獨命名的文件夾裏。按照Go語言的慣例,應該用包名命名包的文件夾。
在packagename文件夾中,所有文件都會以package packagename作為開頭,因為文件夾中所有文件Go語言文件都屬於packagename包。
3、包的導入
為了使用自定義包,必須要先導入包。導入自定義包的語法為import path。必須指定自定義包相對於工作區內?src?文件夾的相對路徑。
Go語言中導入了包,卻不在代碼中使用包,是非法的。在程序開發階段,常常會先導入包,而暫不使用,可以使用空白標識符?。
var = packagename.method代碼可以屏蔽錯誤。
如果導入一個包,只為了確保包進行了初始化,而無需使用包中的任何函數或變量,如需要確保調用包的init函數,而不需要在代碼中使用包,可以使用空白標識符。import (_ "packagename")
在使用import導入包的時候,如果發生包命名沖突,可以在import的名稱前面加一個包的別名處理。使用方法如下:import (packageAnotherName "packagename")
4、init函數
所有包都可以包含一個init函數。init函數不應該有任何返回值類型和參數,在用戶代碼中也不能顯式地調用。init函數的形式如下:func init() { }
init函數可用於執行初始化任務,也可用於在開始執行前驗證程序的正確性。
包的初始化順序如下:
A、首先初始化包級別(Package Level)的變量
B、緊接著調用init函數。包可以有多個init函數(在一個文件或分布於多個文件中),按照編譯器解析的順序進行調用。
C、如果一個包導入另一個包,會最先初始化被導入的包。
D、一個包可以被導入多次,但只會被初始化一次。
main包的初始化順序為:
A、首先初始化被導入的包。
B、接著初始化包級別的變量。
C、調用main包的init函數。
D、最後調用main函數。
三、擴展已有類型
Go語言中使用定義別名和組合來擴展已有的類型。
1、使用組合擴展
可以通過定義一個新的類型,內部組合了要擴展類型的對象對已有類型進行擴展。如對TreeNode類型進行擴展,增加一個後序遍歷的方法。
//使用組合擴展TreeNode類型
type BinTreeNode struct{
node *TreeNode
}
//BinTreeNode的方法
func (binTreeNode *BinTreeNode)postOrderTraverse(){
if binTreeNode != nil && binTreeNode.node != nil{
left := BinTreeNode{binTreeNode.node.Right}
left.postOrderTraverse()
right := BinTreeNode{binTreeNode.node.Left}
right.postOrderTraverse()
node := binTreeNode.node
node.Print()
}
}
2、使用別名擴展
可以對已有類型定義一個別名,通過對別名類型增加新的方法實現對已有類型的擴展。
//定義TreeNode的別名
type PreOrderTreeNode TreeNode
//定義PreOrderTreeNode類型的方法
func (pNode *PreOrderTreeNode)preOrderTraverse(){
if pNode != nil{
node := (*TreeNode)(pNode)
node.Print()
//打印左子樹
left := (*PreOrderTreeNode)(pNode.Left)
left.preOrderTraverse()
//打印右子樹
right := (*PreOrderTreeNode)(pNode.Right)
right.preOrderTraverse()
}
}
3、程序實例
package main
import (
"fmt"
)
type TreeNode struct{
Value int
Left,Right *TreeNode
}
func createTreeNode(value int) *TreeNode{
return &TreeNode{Value:value}
}
func (node TreeNode) Print(){
fmt.Println(node.Value, " ")
}
func (node *TreeNode)traverse(){
if node != nil{
//遞歸遍歷左子樹
node.Left.traverse()
node.Print()
//遞歸遍歷右子樹
node.Right.traverse()
}
}
func (node *TreeNode)setValue(value int){
if node!= nil{
node.Value = value
}else {
fmt.Println("The node is nil.")
}
}
//使用組合擴展TreeNode類型
type PostOderTreeNode struct{
node *TreeNode
}
//BinTreeNode的方法
func (binTreeNode *PostOderTreeNode)postOrderTraverse(){
if binTreeNode != nil && binTreeNode.node != nil{
left := PostOderTreeNode{binTreeNode.node.Right}
left.postOrderTraverse()
right := PostOderTreeNode{binTreeNode.node.Left}
right.postOrderTraverse()
node := binTreeNode.node
node.Print()
}
}
//定義TreeNode的別名
type PreOrderTreeNode TreeNode
//定義PreOrderTreeNode類型的方法
func (pNode *PreOrderTreeNode)preOrderTraverse(){
if pNode != nil{
node := (*TreeNode)(pNode)
node.Print()
//打印左子樹
left := (*PreOrderTreeNode)(pNode.Left)
left.preOrderTraverse()
//打印右子樹
right := (*PreOrderTreeNode)(pNode.Right)
right.preOrderTraverse()
}
}
func main() {
var root TreeNode
root.Value = 0
root.Left = &TreeNode{Value:1}
root.Right = &TreeNode{2,nil,nil}
root.Left.Left = &TreeNode{Value:3}
root.traverse()
root.setValue(100)
root.traverse()
fmt.Println()
rootItem1 := PostOderTreeNode{&root}
rootItem1.postOrderTraverse()
fmt.Println()
rootItem2 := (PreOrderTreeNode)(root)
rootItem2.preOrderTraverse()
}
四、GO環境變量
1、GOROOT
GOROOT環境變量是go的安裝路徑。
GOROOT=/usr/local/go
export GOROOT
要執行go命令和go工具, 需要配置go的可執行文件的路徑:export $PATH:$GOROOT/bin
如果是windows需要使用;符號分割兩個路徑, mac和類unix使用:符號分割。
2、GOPATH
go install/go get和 go的工具等會用到GOPATH環境變量。
GOPATH是Go語言開發的工作空間,作為編譯後二進制的存放目的地和import包時的搜索路徑。
GOPATH表示代碼包所在的地址,可以設置多個。
GOPATH環境變量默認在當前用戶主目錄下的go目錄,所有項目和第三方庫都放在同一個GOPATH下。
GOPATH用來存放Go源碼,Go的可運行文件,以及相應的編譯之後的包文件。所以這個目錄下面有三個子目錄:src、bin、pkg
GOPATH允許多個目錄,當有多個目錄時,請註意分隔符,多個目錄的時候Windows是分號,Linux系統是冒號,當有多個GOPATH時,默認會將?go get?的內容放在第一個目錄下。
$GOPATH?目錄約定有三個子目錄:
A、src目錄存放源代碼(比如:.go .c .h .s等)
B、pkg目錄存放編譯後生成的package(比如:.a)
C、bin目錄存放編譯後生成的可執行文件
不能把GOPATH設置成go的安裝路徑,可以自己在用戶目錄下創建一個目錄, 如go。
GOPATH=/home/user/go:/home/user/dev
export GOPATH
為了使用方便,通常需要將所有工作空間的bin路徑添加到PATH環境變量中,如:export $PATH:$GOPATH/bin
如果$GOPATH有多個工作目錄,使用?${GOPATH//://bin:}/bin?添加所有的bin目錄。export $PATH:${GOPATH//://bin:}/bin
GOPATH有兩個目錄(一個用於存放第三方包,一個用戶開發),如果使用?go工具進行第三方包的安裝,默認會安裝到第一個目錄 (/home/user/go),如果在/home/user/dev中寫代碼,使用g工具(go install,?go build) 會將二進制包安裝到/home/user/dev中。
GOPATH設置兩個目錄的優點在於第一個目錄作為第三方包的存放位置,第二個目錄作為開發者自己的工作空間。第三方的GOPATH放置到第一位,go 安裝工具會將其作為默認的位置。
當使用go命令搜索包時,首先搜索?$GOROOT路徑,然後是$GOPATH/src路徑。
3、遠程包
go語言有一個獲取遠程包的工具就是go get,目前go get支持多數開源社區(例如:github、googlecode、bitbucket、Launchpad)。go get github.com/xxx/xxx
go get -u 參數可以自動更新包,而且當go get的時候會自動獲取該包依賴的其它第三方包,默認會安裝到$GOPATH的第一個目錄。
在代碼中使用遠程包與使用本地包一樣。import?"github.com/xxx/xxx"
Go語言開發(四)、Go語言面向對象