Go笔记1-认识Go语言
Thu, Apr 28, 2016#认识Go语言
##前言 这两天再看一些关于并发的资料,我原本是Android程序员,但是并发的话题引起了我的兴趣。在Java的开发生态当中,可以用作并发的当属NIO包,cocurrent包了。但是它们学习起来难度曲线较高,然而Scala又进入我的眼中,发现Scala有一个Akka库可以作为高性能的并发库使用。不过Scala语言本身较为复杂,希望在有更多的空隙的时间去了解Scala。另外,就是Go语言了。这个语言天生就为并发而生,我认为它最令人激动的就是语法更精炼简洁,go关键字把所有需要并发的事情负责到底,是个很有意思的语言。
##语言特性 它具有以下现代语言的优秀特性,以另一种思维方式定义这个全新的语言 - 自动垃圾回收(gc) - 更加丰富的内置类型 - 函数多返回值 - 错误处理(defer) - 匿名函数和闭包(lambda) - 类型和接口 - 并发编程(go关键字) - 反射 - 语言交互性(Cgo)
自动垃圾回收 用过Java的都知道,Java自动垃圾回收相对于C/C++ 手动分配内存释放内存省了多少调试内存问题的时间,提高了多少生产力。 丰富的内置类型 与其他静态类型语言不同的是,在其他语言看来需要用库的映射(map)这个类型,Go语言认为它的实用性很强,因此将其作为内置类型。另外,还增加了数组切片(slice)类型,这个类型很形象,切片可长可短可大可小,它和C++的vector或者Java的List类型有点像。不过Go将这个类型作为原生的内置类型。让数组不再是一个死板的大小,而是一个长度可以灵活变化的数据类型。 函数多返回值 特性,我认为这个也是一个两点。我在用Java写Android应用的时候有时候经常会遇到一个函数在经过一定的计算的结束后返回多个值,然而Java并没有提供这样的特性,不光Java没有,C/C++,C#等主流语言也都没有,Python提供了,但是远水解决不了近火。通常我们遇到这样的情况,C语言程序员无非定义新的结构体来接纳多个结果,然后返回新的结构体实例,C++和Java可以定义新的业务实体类或者使用map(Map)来解决这个问题。但是这些都让我们为之唏嘘,我们要新定义新的数据结构去适应我们的计算和业务,然而这个结构或许只使用一次。因此Go,也同Python,提供了多返回值这个特性。 错误处理 Go语言引入了3个关键字用于标准的错误处理流程,这3个关键字分别为defer、 panic和recover。与C++和Java等语言中的异常捕获机制相比,Go语言的错误处理机制可以大量减少代码量。C++和Java中,C++情况还好一些,异常机制属于外部库特性,并不是所有的库都会使用异常机制。然而Java使用异常已经到了一种水漫金山的感觉。因为异常是Java内置特性,我们不得不经常使用一层套一层的try-catch语句来维持代码的健壮性,然而丧失了代码的可读性和可维护性。然而我们在重构时却又不得不为了将来的可扩展性将代码分层封装,将Exceptions层层抛出,向浮升的气泡一样,最后还是逃脱不了处理异常的陷阱。当然,并不是说try-catch机制不好,而是很多情况下其实有更好更简洁的解决方式解 决逻辑发生例外行为的问题。 匿名函数和闭包我相信,一个新的现代编程语言如果没有lambda表达式那么肯定会收到学院派的批评。这个批评可能不止是学院派,业界也会批评。在技术飞速发展的今天,高并发编程、响应式编程越来越受到前、后、移动端的今天,匿名函数或者闭包这种简洁的语言特性越来越受到推崇。后来居上的C#先于Java推出lambda表达式,Python推出后也支持了lambda表达式,C++11标准宣布支持lambda表达式,JVM系下的语言中,Scala天生支持匿名函数,并且在语言层面推崇OOP和FP双重编程范式;Groovy作为Java的妹妹,也支持了闭包特性,而且语言十分之简练优秀,后来Java看这大势所趋,推出Java 8时宣布支持lambda表达式,更有甚者Clojure,干脆把自己定为Lisp的远房亲戚,整个语言的核心就是匿名函数。列举这么多例子,也就是为了证明一件事,既然这个语言是为云和并发而生,那么这个特性没理由不加,况且还有erlang虎视眈眈(笑)。 类型和接口 Go的类型定义类似于C中的结构体(struct),Go的作者之一就是C的作者,看来Go的作者对struct情有独钟啊,不但如此,Go还对C++和Java的面向对象的特性大量裁减,不允许继承,不允许重载函数,连代表抽象性的接口(interface)也成为了自动适配类型了,只要类型的定义的模型和接口相同,就可以强制直接造型为这个接口的实例,直接消灭了类型和接口造型的依赖,我真的不知道该说这种面向对象的思想是一种进步还是一种倒退呢?我真的不敢说,因为这毕竟是C语言之父和很多科学家凭借这么多年的经验设计出来的语言。 原生的并发编程支持 《Go语言编程》一书中这样说:“Go语言引入了goroutine概念,它使得并发编程变得非常简单。通过使用goroutine而不是裸用操作系统的并发机制,以及使用消息传递来共享内存而不是使用共享内存来通信, Go语言让并发编程变得更加轻盈和安全。”多线程并发和锁的控制是我长久以来最头疼的一件事情了,尤其是对于Java这种完全面向对象的语言来说,控制线程是否阻塞的时机很重要,否则会造成性能低下等问题,控制多线程对象时还要考虑原子性和并发修改等诸多问题。Go语言提供了go关键字并且引入了goroutine概念,让并发更加容易。 反射 是在Java语言出现后迅速流行起来的一种概念。通过反射,你可以获取对象类型的详细信息,并可动态操作对象。反射是把双刃剑,功能强大但代码可读性并不理想。若非必要,我们并不推荐使用反射。对于我来说,Java中我喜欢用反射,反射机制可以更多的扩展语言本身,Java中的反射与Groovy的元编程,都可以对我们已写的代码进行动态修改和扩展,配合运行时注解可以实现特别多的扩展功能。但是Go语言编程一书上并不推荐我们使用反射来做类似Java里的事情。 语言交互性由于Go语言与C语言之间的天生联系, Go语言的设计者们自然不会忽略如何重用现有C模块的这个问题,这个功能直接被命名为Cgo。 Cgo既是语言特性,同时也是一个工具的名称。在Go代码中,可以按Cgo的特定语法混合编写C语言代码,然后Cgo工具可以将这些混合的C代码提取并生成对于C功能的调用包装代码。开发者基本上可以完全忽略这个Go语言和C语言的边界是如何跨越的。然而C和C++之间的互操作了解的人就可以说:他们之间几乎没有墙。也就是说我们可以用一句很形象的话说:Go,C,C++是一个次元的(笑),毕竟他们都是编译型语言。但是对于Java和C/C++的JNI操作接口来说,那简直是一种灾难,用上面的语气说:Java和C/C++根本是两个次元的。这么说从代码上,从编译产物上都说的过去,毕竟他们中间隔着一层JVM,需要运行时翻译执行。包括Android也是,Dalvik虚拟机字节码和JVM字节码都是需要运行时进行翻译执行的,需要通过JNI标准接口去对接C语言,而不是在编译期就可以像Go一样无缝对接C/C++的库。 其他瞩目的性质可以直接通过go run命令将代码编译为本地二进制执行文件。Windows下是exe文件,Linux下是一个二进制文件。并且在编译时完全凭借目录结构和包名来推导工程结构和源码构建顺序,甚至还可以知道编译目标是一个库还是一个可执行文件,编译出我们想要的结果,而不必像C那样写CMake或者makefile。我认为这简直是一大创举!C/C++项目可能需要cmake,跨平台项目可能需要编写(或者工具自动生成)冗长的makefile,然后make命令去通过构建文件确定依赖顺序进行编译。Java项目需要有maven或者gradle等构建工具去为项目解析依赖并下载依赖库,最终编译构建为最终可部署的项目包,Scala中SBT也充当了Java的gradle这个角色。Go却可以为我们自动推导依赖并确定顺序进行编译构建,省掉大量时间。 调试 我想阅读到这里的人应该能想象得到,go的调试实际上可以直接使用gdb进行调试。
##小结 从这些特性和其他的语言对比来看,Go语言确实让我们感觉而耳目一新,我认为作为业界的工程师来讲,没有理由去拒绝一有既拥有各种语言的各种优点又有一些其他语言不具有的特点的语言,不但如此,它还如此简练。开源、跨平台的好处不说了。早期的版本可能性能不尽如人意,但是它依然在火热的发展进程当中,我认为将来的Go语言必定会有越来越宽的市场,越来越完善的生态系统。