面向对象
Go 的优势
- 可以编译成机器码,不依赖其他库
- 静态类型语言,有动态语言的感觉。静态语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉-就是很多的包可以使用,写起来效率很高
- 语言层面支持并发,这是 Go 语言的最大的特色,天生支持并发(天生的基因和后来的整容是有区别的)
- 内置 runtime,支持垃圾回收机制
- 简单易学,有C语言的基因,Go 语言有 25 个关键字,但是表达能力极强
- 丰富的标准库,内置大量库,特别是网络库非常强大
- 内置强大工具,使 review 变的简单,可以使代码格式一模一样
- 跨平台编译,可以不依赖与系统信息
- 内置 C 支持
Go 适合
- 服务器编程
- 分布式系统,数据库代理器
- 网络编程
- 云平台,国内的七牛云存储的关键服务就是使用Go语言开发的
Go 的缺点
- Go的 import 包不支持版本,升级容易导致项目不可运行,所以需要自己控制相应的版本信息
- Go的 gotoutine 一旦启动,不同的 goroutine 之间切换不收程序的控制,runtime 调度的时候,需要严谨的逻辑,
- 不然 goroutine 休眠,过一段时间逻辑结束了,突然冒出来又执行了,会导致逻辑出错等情况
- GC 延迟有点大
Go 语言的面向对象特性
Go 语言的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要的时候,你可以给内置类型“增加”新方法。儿在实现某个具体的接口时,无需从该接口继承,只需要实现该接口要求的所有方法即可。任何类型都可以被Any 类型引用。Any 类型就是空接口,即interface{}。
给类型添加方法
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func main() {
fmt.Println("Hello")
var a, b Integer = 1,2
fmt.Println(a,b)
res := a.Less(b)
fmt.Println(res)
}
上面给 Integer 添加了一个 Less 方法第一个 a 是给 a 添加方法,b 是参数,bool 是函数返回值。
在 Go 语言中,面向对象的神秘面纱不复存在,不像 C++ 和 Java,方法都默认有一个隐藏的 this 指针,Python 中方法中的 self 和 this 指针的作用是一样的。Go 中方法施加的目标(对象),显示传递;不需要非得是指针,也不用非得叫this。只有在需要修改对象的时候才有必要使用指针。比如:
func (a *Integer) Add(b Integer) {
*a += b
}
Go 语言和 C 语言是一样的,都是基于值传递的。要想修改变量的值,只能传递指针。
值语义和引用语义
Go 语言大多数类型都基于值语义:基本类型+符合类型(数组、结构体、指针)。和 C 语言不同的,数组的传递也是基于值语义的,而且很纯粹。比如:
var arrayA [3]int = [3]int{1,2,3}
arrayB := arrayA
arrayB[1]++
fmt.Println(arrayA,arrayB)
会看到输出,对 B 中元素的操作,对 A 并没有任何影响。这表明在将 A 赋值给 B 的时候是数组内容的完整复制,如果想要表达引用,需要使用指针: arrayB := &arrayA
数组切片、map、channel、接口看起来想引用类型
结构体
Go 中的结构体和其他语言的类有相同的地位,但是 Go 语言中没有继承在内的大量面向对象的特性,只保留了组合(composition)这个最基础的特性。比如下面的例子对 log 的使用
type Job struct {
Command string
*log.Logger
}
func NewJob(command string, logger *log.Logger) *Job {
return &Job{command,logger}
}
func (job *Job)Start(){
job.Println("Starting now....")
job.Println("end")
}
func main() {
mylog := log.New(os.Stdout, "",log.LstdFlags)
j := NewJob("test",mylog)
j.Start()
}
定义的 Job 结构体包含一个匿名的 log.Logger 指针,在合适的赋值之后可以在 Job 类型的所有成员方法中可以很舒适的借用所有 log.Logger 提供的方法。类似于 Java 和 C++ 中继承了 log.Logger
Go 语言中并没有其他语言中的 public 等权限访问控制的关键字。要使某个符号对其他包可见,需要将该符号定义为以大写字母开头,就想上面定义的 Job 结构体首字母需要大写。成员方法的访问性遵循同样的规则,如果上面的 Start 函数定义为 start,则该函数只能在该类型所在的包内使用。
接口
Go 语言中的接口不需要像 C++ 和 Java 中的使用:或者 implements 关键字来实现接口,在 Go 语言中,一个类只需要实现了接口要求的所有函数,那么这个类就实现了该接口。下面的例子:
//定义一个文件接口
type IFile interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
Close() error
}
type IReader interface {
Read(buf []byte) (n int, err error)
}
type IWriter interface {
Write(buf []byte) (n int, err error)
}
type ICloser interface {
Close() error
}
//定义文件类
type File struct {
filename string
fid int16
}
//定义文件的几个方法
func (f *File)Read(buf []byte) (n int, err error){
fmt.Println("File Opened")
return 0,nil
}
func (f *File)Write(buf []byte) (n int,err error) {
fmt.Println("File Writed")
return 0,nil
}
func (f *File)Close() error {
fmt.Println("File Closed")
return nil
}
func main() {
bytes := []byte{0,1,2,3,4,5,6,7,8,9}
var file1 IFile = new(File)
file1.Read(bytes)
var file2 IReader = new(File)
file2.Read(bytes)
var file3 IWriter = new(File)
file3.Write(bytes)
var file4 ICloser = new(File)
file4.Close()
}
File 类实现了上面的所有接口,所以可以直接进行赋值。Go 语言的接口是非侵入式接口:
- Go 语言标准库不需要绘制类库的继承图,在 Go 中,类的继承树并无意义,只有知道这个类实现了那些方法,每个方法是什么含义即可
- 实现类的时候,只需要关系自己应该提供那些方法,不用再纠结接口需要拆的多细才合理,按需定义
- 不用为了实现一个即可而导入一个包,因为多引用一个外部的包就意味着更多的耦合
接口还可以通过组合来实现:
type IReaderWriter interface {
IReader
IWriter
}
和下面的实现是一样的:
type IReaderWriter interface {
Read(buf []byte) (n int, err error)
Write(buf []byte) (n int, err error)
}
Go 语言中的 Any 类型
Java 语言中所有的对象都继承自 Object,而 Go 语言中的任何对象的实例都满足空接口 interface{},所以 interface{}看起来就想指向任何对象的 Any 类型。
var v1 interface{} = 1
var v2 interface{} = "abc"
var v3 interface{} = &v2
var v4 interface{} = struct { x int }{1}
面向对象三大特性
有过 C++ 语言学习经历的朋友都知道,面向对象主要包括了三个基本特征:封装、继承和多态。封装,就是指运行的数据和函数绑定在一起,C++ 中主要是通过 this 指针来完成的;继承,就是指 class 之间可以相互继承属性和函数;多态,主要就是用统一的接口来处理通用的逻辑,每个 class 只需要按照接口实现自己的回调函数就可以了。
作为集大成者的 Go 语言,自然不会在面向对象上面无所作为。相比较 C++、java、C# 等面向对象语言而言,它的面向对象更简单,也更容易理解。下面,我们不妨用三个简单的例子来说明一下 go 语言下的面向对象是什么样的。
封装特性
package main
import "fmt"
type data struct {
val int
}
func (p_data* data)set(num int) {
p_data.val = num
}
func (p_data* data)show() {
fmt.Println(p_data.val)
}
func main() {
p_data := &data{4}
p_data.set(5)
p_data.show()
}
继承特性
package main
import "fmt"
type parent struct {
val int
}
type child struct {
parent
num int
}
func main() {
var c child
c = child{parent{1}, 2}
fmt.Println(c.num)
fmt.Println(c.val)
}
多态特性
package main
import "fmt"
type act interface {
write()
}
type xiaoming struct {
}
type xiaofang struct {
}
func (xm *xiaoming) write() {
fmt.Println("xiaoming write")
}
func (xf *xiaofang) write() {
fmt.Println("xiaofang write")
}
func main() {
var w act;
xm := xiaoming{}
xf := xiaofang{}
w = &xm
w.write()
w = &xf
w.write()
}