写在前面:
近来关于对Golang的讨论有很多,七牛的几个大牛们也断定Go语言在未来将会快速发展,并且很可能会取代Java成为互联网时代最受欢迎的编程语言。Go语言是google推出的编程语言,在已经成功的给世人创造了改变人们生活的操作系统之后,google似乎感觉有必要再为世人带来一款强大的编程语言,而Go语言依靠自己众多友好的特性也不负众望正在被开发者接触,我有幸在学习高性能并发编程的时候认识了Go语言,在了解了Go的一些特性之后决定系统的学习一番。我发现关于Go的学习资料并不多,以至于我需要自己写一些东西。这里的内容大多来自七牛团队的《Go语言编程》一书。这里也会按照书中组织的章节进行学习。当然如果一些知识点事互联网上已经有的,这里直接进行转载了。
-Written by Lingtao in Nanjing.
按照书中说的,Go语言具有以下的特征,下面我们分别来进行介绍。
? 自动垃圾回收
? 更丰富的内置类型
? 函数多返回值
? 错误处理
? 匿名函数和闭包
? 类型和接口
? 并发编程
? 反射
? 语言交互性
从C到C++,从程序性能的角度来考虑,这两种语言允许程序员自己管理内存,包括内存的申请和释放等。因为没有垃圾回收机制所以C/C++运行起来速度很快,但是随着而来的是程序员对内存使用上的很谨小慎微的考虑。因为哪怕一点不小心就可能会导致“内存泄露”使得资源浪费或者“野指针”使得程序崩溃等,尽管C++11后来使用了智能指针的概念,但是程序员仍然需要很小心的使用。后来为了提高程序开发的速度以及程序的健壮性,java和C#等高级语言引入了GC机制,即程序员不需要再考虑内存的回收等,而是由语言特性提供垃圾回收器来回收内存。但是随之而来的可能是程序运行效率的降低。
“Go语言作为一门新生的开发语言,当然不能忽略内存管理这个问题。又因为Go语言没有C++
这么“强大”的指针计算功能,因此可以很自然地包含垃圾回收功能。因为垃圾回收功能的支持,开发者无需担心所指向的对象失效的问题,因此Go语言中不需要delete关键字,也不需要free()方法来明确释放内存”.
其实作为一种新兴的语言,如果仅仅是为了某种特定的用途那么可能其内置类型不是很多,仅需要能够完成我的功能即可,但是Go语言“不仅支持几乎所有语言都支持的简单内置类型(比如整型和浮点型等)外,还支持一些其他的高级类型,比如字典类型,map要知道这些类型在其他语言中都是通过包的形式引入的外部数据类型。数组切片(Slice),类似于C++ STL中的vector,在Go也是一种内置的数据类型作为动态数组来使用。这里满有一个颇为简单的解释:”既然绝大多数开发者都需要用到这个类型,为什么还非要每个人都写一行import语句来包含一个库?”
在C,C++中,包括其他的一些高级语言是不支持多个函数返回值的。但是这项功能又确实是需要的,所以在C语言中一般通过将返回值定义成一个结构体,或者通过函数的参数引用的形式进行返回。而在Go语言中,作为一种新型的语言,目标定位为强大的语言当然不能放弃对这一需求的满足,所以支持函数多返回值是必须的,例如:
> func getName()(firstName, middleName, lastName, nickName string){
> return "May", "M", "Chen", "Babe" } //定义了一个多返回值的函数getName
>
> fn, mn, ln, nn := getName() //调用赋值
> _, _, lastName, _ := getName() //缺省调用
在传统的OOP编程中,为了捕获程序的健壮性需要捕获异常,使用的方法大都是try() catch{}模块,例如, 在下面的java代码中,可能需要的操作是:
Connection conn = ...;
try {
Statement stmt = ...;
...//别的一些异常捕获
finally {
stmt.close();
}
finally {
conn.close();
}
而在Go中引入了三个关键字,分别是 defer、panic和recover,其中使用defer关键字语句的含义是不管程序是否出现异常,均在函数退出时自动执行相关代码。
所以上面你的java代码用Go进程重写只有两行:
conn := ...
defer conn.Close()
另外两个关键词后面再讨论。所以“Go语言的错误处理机制可以大量减少代码量,让开发者也无需仅仅为了程序安全性而添加大量一层套一层的try-catch语句。这对于代码的阅读者和维护者来说也是一件很好的事情,因为可以避免在层层的代码嵌套中定位业务代码。”
关于这个功能介绍的不多,大概就是说Go中的函数也可以作为参数进行传递:
“在Go语言中,所有的函数也是值类型,可以作为参数传递。Go语言支持常规的匿名函数和闭包,比如下列代码就定义了一个名为f的匿名函数,开发者可以随意对该匿名函数变量进行传递和调用:
f := func(x, y int) int {
return x + y
}
”
这个特性是Go在实现OPP时候的一些特性,主要有这么几点:
第一: Go语言没有很复杂的面向对象的概念,即没有继承和重载,其类型更像是C中的struct,并且直接使用了struct关键字,仅仅是最基本的类型组合功能。但是,尽管不支持这些语法特性,但是Go的OOP却同样可以实现这些功能,只是实现的形式上会有不同而已。
即这里需要介绍的“非侵入型”接口的概念。
举个例子:
在C++中,一般会这样定义一个接口和类型的
// 抽象接口
interface IFly
{
virtual void Fly()=0;
};
// 实现类
class Bird : public IFly
{
public:
Bird() {}
virtual ~Bird() {}
void Fly()
{
// 以鸟的方式飞行
}
};
//使用的时候
void main()
{
IFly* pFly = new Bird();
pFly->Fly();
delete pFly;
}需要你自己以虚函数的形式定义一个接口,并且让类型继承这个接口并重写虚方法。在使用的时候需要进行动态绑定。
而在Go中实现相同的功能,你只需要
type Bird struct {
…
}
func (b *Bird) Fly() {
// 以鸟的方式飞行
}
type IFly interface {
Fly()
}
func main() {
var fly IFly = new(Bird)
fly.Fly()
}
可以看出,“虽然Bird类型实现的时候,没有声明与接口IFly的关系,但接口和类型可以直
接转换,甚至接口的定义都不用在类型定义之前,这种比较松散的对应关系可以大幅降低因为接
口调整而导致的大量代码调整工作”。
其实到目前为止,最吸引我的就是这个特性,而且我之前说了如果不是因为要做服务器的高并发我可能根本就不知道Go这种语言,在上一篇文章http://blog.csdn.net/michael_kong_nju/article/details/45420047 中讨论了为什么Go可以实现大规模的并发的原理,这里不做详细的介绍,只给出实现方法,即
“Go语言引入了goroutine概念,它使得并发编程变得非常简单。通过使用goroutine而不是裸用操作系统的并发机制,以及使用消息传递来共享内存而不是使用共享内存来通信,Go语言让并发编程变得更加轻盈和安全。通过在函数调用前使用关键字go,我们即可让该函数以goroutine方式执行,goroutine是一种比线程更加轻盈、更省资源的协程。”
“同时,Go语言实现了CSP(通信顺序进程,Communicating Sequential Process)模型来作为goroutine间的推荐通信方式,在CSP模型中,一个并发系统由若干并行运行的顺序进程组成,每个进程不能对其他进程的变量赋值。进程之间只能通过一对通信原语实现协作。Go语言用channel(通道)这个概念来轻巧地实现了CSP模型。channel的使用方式比较接近Unix系统中的管道(pipe)概念,可以方便地进行跨goroutine的通信。”
“另外,另外,由于一个进程内创建的所有goroutine运行在同一个内存地址空间中,因此如果不同的goroutine不得不去访问共享的内存变量,访问前应该先获取相应的读写锁。Go语言标准库中的sync包提供了完备的读写锁功能。”
这里的反射(reflecttion)和JAVA中的反射类似,可以用来获取对象类型的相信信息,并动态操作对象。因为反射可能会对程序的可读性有很大的干扰,所以,在Go中只是在特别需要反射支持的地方才实现反射的一些功能。“反射最常见的使用场景是做对象的序列化(serialization,有时候也叫Marshal & Unmarshal)。例如,Go语言标准库的encoding/json、encoding/xml、encoding/gob、encoding/binary等包就大量依赖于反射功能来实现。”
这里的交互性主要是和C的交互性,之所以这样是因为Go语言的开发者是最初贝尔实验室创建Unix系统以及C语言的一般人,包括:
肯·汤普逊(Ken Thompson,http://en.wikipedia.org/wiki/Ken_Thompson):设计了B语言和C语言,创建了Unix和Plan 9操作系统,1983年图灵奖得主,Go语言的共同作者。
在Go语言中直接重用了大部份的C模块,这里称为Cgo.Cgo允许开发者混合编写C语言代码,然后Cgo工具可以将这些混合的C代码提取并生成对于C功能的调用包装代码。开发者基本上可以完全忽略这个Go语言和C语言的边界是如何跨越的。
例如书中一个例子,在Go语言中直接调用了C标准库的puts函数。
package main
/*
#include <stdio.h>
*/
import "C"
import "unsafe"
func main() {
cstr := C.CString("Hello, world")
C.puts(cstr)
C.free(unsafe.Pointer(cstr))
}
以上就是书中总结的Go语言的9大特性,这里面我看完之后根据自己的体会以及书中的一些总结进行了汇总。对于想了解Go这么语言,以及准备进入这个领域的人可以对Go有一个大概的认识。如果想深入的学习可以去看七牛云存储团队出的《Go语言编程》。我后面也会继续学习。
原文地址:http://blog.csdn.net/michael_kong_nju/article/details/45423197