文章目录
Go语言是谷歌2009发布的第二款开源编程语言。Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。Go就是谷歌工程师为这类程序编写的一种语言。它不是针对编程初学者设计的,但学习使用它也不是非常困难。Go支持面向对象,而且具有真正的闭包(closures)和反射 (reflection)等功能。在学习曲线方面,派克认为Go与Java类似,对于Java开发者来说,应该能够轻松学会 Go。之所以将Go作为一个开源项目发布,目的是让开源社区有机会创建更好的工具来使用该语言,例如 Eclipse IDE中的插件。
更多关于Golang核心编程知识的文章请看:Golang核心编程(0)-目录页
一、Golang简介
1.1、什么是Golang
Golang是Go语言的简称。Go语言作为一门全新的静态类型开发语言,与当前的开发语言相比具备众多令人兴奋不已的新特性。
在Google,一群大牛创造了Go语言。早在2007年9月,Go语言还是这帮大牛的20%自由时间的实验项目。到了2008年5月,Google发现了Go语言的巨大潜力,从而开始全力支持这个项目,让这批人可以全身心投入Go语言的设计和开发工作中。
Go语言的第一个版本在2009年11月正式对外发布,并在此后的两年内快速迭代,发展迅猛。第一个正式版本的Go语言于2012年3月28日正式发布,让Go语言迎来了第一个引人瞩目的里程碑。
1.2、使用Golang完成的著名项目
- Docker
- Kubernetes
- fabric
1.3、Golang的特性
- 自动垃圾回收
- 更丰富的内置类型
- 函数多返回值
- 错误处理
- 匿名函数和闭包
- 类型和接口
- 并发编程
- 反射
- 语言交互性
- 丰富开源库支持
二、Golang的特性概述
2.1、自动垃圾回收
我们可以先看下不支持垃圾回收的语言的资源管理方式,以下为一小段C语言代码:
void foo()
{
char* p = new char[128];
... // 对p指向的内存块进行赋值
func1( p ) // 使用内存指针
delete[] p;//清除指针
}
各种非预期的原因,比如由于开发者的疏忽导致最后的 delete 语句没有被调用,都会引发经典而恼人的内存泄露问题。假如该函数被调用得非常频繁,那么我们观察该进程执行时,会发现该进程所占用的内存会一直疯长,直至占用所有系统内存并导致程序崩溃。
和Java、C#等完全面向对象的编程语言一样,Golang也支持自动的垃圾回收,而不需要像C/C++一样手动去delete或者调用free()去释放内存,从语言内部机制的层面去帮助开发者解决令人苦恼的内存泄露问题。
2.2、丰富的内置类型
除了几乎所有语言都支持的简单内置类型(比如整型和浮点型等)外,Go语言也内置了一些比较新的语言中内置的高级类型,比如C#和Java中的数组和字符串。除此之外,Go语言还内置了一个对于其他静态类型语言通常用库方式支持的字典类型( map )
。除此之外还有一个新增的数据类型:数组切片( Slice )
。我们可以认为数组切片是一种可动态增长的数组,和Java中的Vector类比较相似。
2.3、函数多返回值
目前的主流语言中除Python外基本都不支持函数的多返回值功能,Go语言革命性地在静态开发语言阵营中率先提供了多返回值功能。这个特性让开发者可以从原来用各种比较别扭的方式返回多个值的痛苦中解脱出来,既不用再区分参数列表中哪几个用于输入,哪几个用于输出,也不用再只为了返回多个值而专门定义一个数据结构,例如返回一个C中的结构体或者Java中的对象。
- 在Java使用一个
Person
对象将数据封装起来并返还:
static class Person{
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public Person getPerson(int id,String name){
Person person = new Person();
person.setId(id);
person.setName(name);
return person;
}
- 在Go语言中使用多返回值
func getPerson(old_id int,old_name string)(new_id int, new_name string){
new_id = old_id
new_name = old_name
return
}
func main() {
//接受返回id和name
id1, name1 := getPerson(1, "arong")
fmt.Printf("id is %v and name is %v",id1,name1)
//只接受返回的name,不接受id
_, name2 := getPerson(1, "arong")
fmt.Printf("name is %v",name2)
}
2.4、错误处理
Go语言引入了3个关键字用于标准的错误处理流程,这3个关键字分别为defer
、 panic
和recover
描述Go语言错误处理机制的独特之处。整体上而言与C++和Java等语言中的异常捕获机制相比,Go语言的错误处理机制可以大量减少代码量,让开发者也无需仅仅为了程序安全性而添加大量一层套一层的 try-catch 语句。这对于代码的阅读者和维护者来说也是一件很好的事情,因为可以避免在层层的代码嵌套中定位业务代码。
2.5、匿名函数和闭包
在Go语言中,所有的函数也是值类型,可以作为参数传递。Go语言支持常规的匿名函数和闭包,比如下列代码就定义了一个名为 f 的匿名函数,开发者可以随意对该匿名函数变量进行传递和调用:
f := func(x, y int) int {
return x + y
}
2.6、类型和接口
Go语言的类型定义非常接近于C语言中的结构(struct),甚至直接沿用了 struct 关键字
。相比而言,Go语言并没有直接沿袭C++和Java的传统去设计一个超级复杂的类型系统,不支持继承和重载,而只是支持了最基本的类型组合功能。
- Java的接口定义与重写
public interface Person {
public void eat();
}
public static void main(String[] args) {
JavaProgrammer javaProgrammer = new JavaProgrammer();
javaProgrammer.eat();
}
public static class JavaProgrammer implements Person{
@Override
public void eat(){
System.out.println("Java开发者需要这样重写一个接口的方法");
}
}
- Golang接口实现和重写
type Person interface {
eat()
}
type GoProgramer struct {
}
func(golang *GoProgramer)eat(){
fmt.Printf("Golang程序员会这样实现接口并重写")
}
func main() {
//创建的GoProgrammer结构体声明为Person接口类型
var golang Person = new(GoProgramer)
golang.eat()
}
可以看出,虽然 GoProgrammer
类型实现的时候,没有声明与接口 Person
的关系,但接口和类型可以直接转换,甚至接口的定义都不用在类型定义之前,这种比较松散的对应关系可以大幅降低因为接口调整而导致的大量代码调整工作。
2.7、并发编程
Go语言引入了goroutine(协称)
概念,它使得并发编程变得非常简单。
通过在函数调用前使用关键字 go
,我们即可让该函数以goroutine方式执行。goroutine是一种比线程更加轻盈、更省资源的协程。Go语言通过系统的线程来多路派遣这些函数的执行,使得每个用 go 关键字执行的函数可以运行成为一个单位协程。当一个协程阻塞的时候,调度器就会自动把其他协程安排到另外的线程中去执行,从而实现了程序无等待并行化运行。而且调度的开销非常小,一颗CPU调度的规模不下于每秒百万次,这使得我们能够创建大量的goroutine,从而可以很轻松地编写高并发程序,达到我们想要的目的。
Go语言还使用channel(通道)
这个概念来轻巧地实现了CSP模型。channel的使用方式比较接近Unix系统中的管道(pipe)概念,可以方便地进行跨goroutine的通信。
- Golang使用协程
package main
import "fmt"
func sum(values [] int, resultChan chan int) {
sum := 0
for _, value := range values {
sum += value
}
resultChan <- sum // 将计算结果发送到channel中
}
func main() {
values := [] int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
resultChan := make(chan int, 2)
go sum(values[:len(values)/2], resultChan)
go sum(values[len(values)/2:], resultChan)
sum1, sum2 := <-resultChan, <-resultChan // 接收结果
fmt.Println("Result:", sum1, sum2, sum1 + sum2)
}
2.8、反射
反射(reflection)
是在Java语言出现后迅速流行起来的一种概念。通过反射,你可以获取对象类型的详细信息,并可动态操作对象。
Go语言的反射实现了反射的大部分功能,但没有像Java语言那样内置类型工厂,故而无法做到像Java那样通过类型字符串创建对象实例。在Java中,你可以读取配置并根据类型名称创建对应的类型,这是一种常见的编程手法,但在Go语言中这并不被推荐。
反射最常见的使用场景是做对象的序列化>
例如,Go语言标准库的encoding/json、encoding/xml、encoding/gob、encoding/binary等包就大量依赖于反射功能来实现。
2.9、语言交互性
由于Go语言与C语言之间的天生联系,Go语言的设计者们自然不会忽略如何重用现有C模块的这个问题,这个功能直接被命名为Cgo。Cgo既是语言特性,同时也是一个工具的名称。
在Go代码中,可以按Cgo的特定语法混合编写C语言代码,然后Cgo工具可以将这些混合的C代码提取并生成对于C功能的调用包装代码。开发者基本上可以完全忽略这个Go语言和C语言的边界是如何跨越的。
2.10、丰富开源库支持
Golang中有相当多的优秀开源第三方库可以被调用,使用它们的方式很简单,不如我们需要使用mysql数据库,那么可以调用第三方库去操作:
import (
_ "database/sql"
_ "github.com/go-sql-driver/mysql"
)