Go语言开发(四)、Go语言面向对象

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"

原文地址:http://blog.51cto.com/9291927/2130132

时间: 2024-10-11 06:14:08

Go语言开发(四)、Go语言面向对象的相关文章

Go语言开发(十四)、Go语言常用标准库四

Go语言开发(十四).Go语言常用标准库四 一.heap 1.heap简介 heap仅仅提供了最小堆的操作,没有提供堆的数据结构,堆的数据结构必须由开发者自己实现.heap提供了一个heap.Interface接口来作为堆的操作和堆的数据结构(开发者自己实现)之间的桥梁,堆的数据结构必须满足此接口: type Interface interface { sort.Interface Push(x interface{}) // add x as element Len() Pop() inter

Go语言开发(六)、Go语言闭包

Go语言开发(六).Go语言闭包 一.函数式编程 1.函数式编程简介 函数式编程是一种编程模型,将计算机运算看作是数学中函数的计算,并且避免了状态以及变量的概念.在面向对象思想产生前,函数式编程已经有数十年的历史.随着硬件性能的提升以及编译技术和虚拟机技术的改进,一些曾被性能问题所限制的动态语言开始受到关注,Python.Ruby和Lua等语言都开始在应用中崭露头角.动态语言因其方便快捷的开发方式成为很多人喜爱的编程语言,伴随动态语言的流行,函数式编程也开始流行. 2.函数式编程的特点 函数式编

Go语言开发学习教程

Go语言开发学习教程 Go语言开发学习教程目录如下: Go语言开发(一).Go语言简介http://blog.51cto.com/9291927/2126775Go语言开发(二).Go语言基础http://blog.51cto.com/9291927/2127825Go语言开发(三).Go语言内置容器http://blog.51cto.com/9291927/2129969Go语言开发(四).Go语言面向对象http://blog.51cto.com/9291927/2130132Go语言开发(

001-iOS开发前奏-C语言笔记

学习目标 1.[了解]操作系统 2.[了解]应用软件 3.[了解]操作系统的分类和市场占有份额 4.[了解]iOS操作系统 5.[了解]应用软件开发的分类 6.[了解]UNIX常用命令 7.[掌握]如何开发第一个C语言程序 一.操作系统 我们的计算机是由很多种硬件设备组成的,比如CPU.内存.硬盘.网卡.主板.声卡.......如果计算机只是仅仅有这些硬件设备,这样能不能正常使用? CPU:负责计算.处理数据 内存:存储数据 (临时) 硬盘:存储数据 (永久) 网卡:接收.发送网络数据 声卡:输

Swift 新语言开发

全书目录: 一.Welcome to Swift 二.Language Guide 三.Language Reference /* 译者的废话: 几个小时前熬夜看了WWDC,各种激动,今年很有料啊!当看到Swift出来的时候,瞬间傻眼,又要学习新语言了.这篇文章来自苹果官方的<The Swift Programming Language>一书,500页左右,在苹果官网有下载.Swift大家都没实际用过,本翻译一定是有各种错漏的,各位多多包涵,我会不断更新修正的. --(博客园.新浪微博)葛布林

在Windows平台搭建C语言开发环境的多种方式

新接触C语言,如何进行C语言开发环境的搭建值得思考并整理 注:本文知识来源于  Windows 平台搭建C语言集成开发环境 - 极客学院 一.在Windows平台配置GNU环境 二.在Windows平台使用Sublime Test开发C语言程序 三.在Windows平台使用VisualStudio开发C语言程序 四.在Windows平台搭建EclipseCDT集成开发环境 五.在Windows平台搭建Clion集成开发环境 一.在Windows平台配置GNU环境 GNU http://www.g

C++ 软件开发多国语言解决方案汇总

暂时汇总出了以下几种方法 以Unicode为核心 采用 GNU gettext  基于Qt的多语言开发工具:Qt Linguist  以Unicode为核心 参考:http://www.ibm.com/developerworks/cn/linux/l-cn-ccppglb/ 多国语言的存在,使程序员在编码处理上花费了大量时间和精力:然而各种各样的乱码问题,如 XML 格式错误.文本显示异常.解析器异常等依然层出不穷.特别的,相对于 JAVA 语言,C/C++ 在处理编码问题上有更大的困难.本文

Swift语言指南(四)--类型安全和类型推断

Swift是一门类型安全语言,类型安全语言需要代码里值的类型非常明确.如果你的代码中有部分值需要String类型,你就不能错误地传递Int. 鉴于Swift的类型安全,编译代码时,Swift会执行类型检查并将任何类型不匹配的地方标记为错误,使你在开发当中尽可能早的捕获并修正错误. 类型检查有助于你在操作不同值的类型时避免犯错.但这并不意味着你必须在声明每一个常量或变量时去检查类型,如果你不检查所需值的类型,Swift会执行类型推断来计算出相应地类型. 类型推断让编译器在编译代码时,根据你提供的值

GO语言的进阶之路-面向对象编程

GO语言的进阶之路-面向对象编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 当你看完这篇文章之时,我可以说你的Golang算是入门了,何为入门?就是你去看Docker 源码能看懂60%的语法结构,因为涉及一些unix的代码可能没有Linux运维基础的同学在学习的时候会很吃力,看起来也会带来一定的难度,如果有时间的话我会给大家解析Docker部门精辟的源码.好了,回归正题吧,我们今天要学习的内容是什么呢?即面向对象编程.当然,不要用屌丝的心态来说:"那要是没对象的还咋编程呢