Google在2012年推出正式版本的golang,作为一门新推出的程序设计语言golang的大量新特性深深的吸引了众多开发者。作为一门年轻的静态类型程序设计语言,golang集合了静态语言的众多优点的同时也摒弃了不少之前程序设计语言中陋习,大大提高了程序的开发效率。
作为一门新的程序设计语言, golang推出了不少新特性。在深入学习golang应用程序开发之前,有必要对这门语言的一些主要特性进行大致的了解。
之前的程序设计语言如C/C++没有垃圾回收机制,内存的申请和释放都需要依靠程序员显式控制。这样容易出现由于编程过程中的疏忽或则异常导致申请的内存空间没有被正确地释放掉。如果当前正在的使用的指针指向的内存空间由于失误导致被释放,那么程序接下来访问该指针将出现未定义的行为。
golang的垃圾回收机制不是采用简单的引用计数,而是采用标记清除的方式。在清除已经标记的内存空间时会出现”Stop the world”的现象,虽然golang的垃圾回收算法没有Java那么成熟和丰富,但是相较于C/C++的内存管理,已经大大释放的程序员的负担。
之前的静态类型语言诸如C/C++、Java是不支持函数(方法)返回多个返回值。但是在以往的编程实践中,一个函数多返回值的需求还是存在的。在不支持多返回值的程序设计语言中,为了实现函数返回多个值,一般是将需要返回的数据抽象成一个类或则结构体整体返回回去,或则将C/C++的指针(Java的对象)作为实参调用函数,在函数体内修改参数的值实现的。这样的实现方式导致代码的可读性变差,需要通过分析函数体的实现确定哪些参数作为输入,哪些参数作为输出。
golang内置对函数多返回值的支持有效得改善了这类需求,提高了代码的可读性。golang多返回值的实现十分简洁明了,通过给函数返回值列表中的返回值变量赋值,可以轻松地控制返回值的个数。
import "fmt"
func main() {
// 忽略乘法的返回值
sum, _ := calculate(1, 3)
fmt.Println("sum:", sum)
}
/** * 多返回值函数的实现 */
func calculate(operand1 int, operand2 int) (sum, mul int) {
sum = operand1 + operand2
mul = operand1 * operand2
return
}
golang摒弃了C++和Java中复杂的类型系统,不支持类型的继承。golang类型的定义和C语言中的结构体十分类似。在接口实现方面,传统的Java语言中需要先定义接口的类型,接口实现类必须实现接口声明的方法。而golang在实现接口时完全摒弃了这套复杂而笨重的机制,无需将接口和类型绑定在一起。
/** * 类型定义 */
type Bird struct {
kind string
height int
speed float64
}
/** * 接口的定义 */
type IFly interface {
Fly()
}
func (* Bird) Fly() {
}
func main() {
var fly IFly = new(Bird)
fly.Fly()
}
传统的编程语言如Java或则C++并发支持的最低粒度为线程。在这类型程序设计语言中,一般将单个线程或则多个线程映射到一个内核线程。所以线程的切换必然会引起内核线程的切换,加大并发时切换的开销,最终限制了并发的数量。而golang通过引入goroutine机制将并发的最小粒度细化到协程,每个用go关键字执行的函数都运行成一个单位的协程,通过在线程内派发调度协程提高了程序的并发度。
golang通过引入channel的概念高效的实现协程之间的通信,channel的概念类似于Unix系统的pipeline(管道)。在并发同步方面golang引入了关键字sync实现访问的控制。
func main() {
var resultChannel chan int = make(chan int, 2)
var slice []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
go calculate(slice[:3], resultChannel)
go calculate(slice[3:], resultChannel)
sum1 := <- resultChannel
sum2 := <- resultChannel
fmt.Println(sum1, "+",sum2, "=", sum1 + sum2)
}
func calculate(values []int, resultChannel chan int) {
var sum int = 0
for _, value := range values {
sum += value
}
resultChannel <- sum
}
反射最早由Java语言引入的,Java生态中大量框架使用Java的反射机制。通过反射可以遍历获取类型中的属性的类型信息,golang的反射在序列化和反序列化的时候用的比较多,golang的反射机制很像Java语言中的反射机制,但是不支持通过类型字符串创建类型实例。反射大量的使用会导致代码的可读性变差,所以golang是不建议大量的使用反射。
在C/C++、Java中基本数据类型可以概括为bool、byte、char、int、long、float、double这几种基本数据类型。对于复数、Map类型的支持需要引入相应的库或则包,由于实际编程实践中大量的使用到map,所以golang创造性的将map作为语言内置的基本数据类型,大大方便了程序开发人员。而slice的引入大大简化了数组的使用,在C/C++和Java中数组是不可变长的,golang通过引入slice的概念,极大的简化了类似可变数组结构的使用。
func main() {
// 直接创建slice
var slice1 []int = []int{1, 2, 3, 4}
fmt.Println(slice1)
// 通过内置函数make创建slice,并指定capacity
var slice2 = make([] int, 5)
copy(slice2, slice1)
fmt.Println(slice2)
// 通过已有的slice创建新的slice
slice3 := slice1[:2]
fmt.Println(slice3)
var simpleMap map[int] string = map[int]string {}
fmt.Println(simpleMap)
// 添加map元素
simpleMap[0] = "China"
fmt.Println(simpleMap)
simpleMap[1] = "USA"
fmt.Println(simpleMap)
// 通过内置函数delete删除元素
delete(simpleMap, 1)
fmt.Println(simpleMap)
}
golang通过引入defer、panic和recover三个关键字处理标准错误流程。golang的错误处理简洁明了,和Java/C++的try catch机制相比更为简化和清晰,大大提高了处理错误代码片段的可读性。