周末天气不好,只能宅在家里,于是就顺便看了一下Go语言,觉得比较有意思,所以写篇文章介绍一下。
我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章。所以,下面的文章主要是以代码和注释为主。只需要你对C语言,Unix,Python有一点基础,我相信你会在30分钟左右读完并对Go语言有一些初步了解的。
Hello World
运行
你可以有两种运行方式,
自己的package
你可以使用GOPATH环境变量,或是使用相对路径来import你自己的package。
Go的规约是这样的:
1)
在import中,你可以使用相对路径,如 ./或 ../ 来引用你的package2)
如果没有使用相对路径,那么,go会去找$GOPATH/src/目录。fmt输出格式
fmt包和libc里的那堆使用printf, scanf,fprintf,fscanf 很相似。下面的东西对于C程序员不会陌生。
注意:Println不支持,Printf才支持%式的输出:
当然,也可以使用如\n\t\r这样的和C语言一样的控制字符
变量和常量
变量的声明很像 javascript,使用 var关键字。注意:
go是静态类型的语言,下面是代码:
还有一种定义变量的方式(这让我想到了Pascal语言,但完全不一样)
常量很简单,使用const关键字:
数组
直接看代码(注意其中的for语句,和C很相似吧,就是没有括号了)
运行结果:
数组的切片操作
这个很Python了。
分支循环语句
if语句注意:if 语句没有圆括号,而必需要有花括号
switch 语句注意:switch语句没有break,还可以使用逗号case多个值
for 语句前面你已见过了,下面再来看看for的三种形式:(注意:Go语言中没有while)
关于分号
从上面的代码我们可以看到代码里没有分号。其实,
和C一样,Go的正式的语法使用分号来终止语句。和C不同的是,这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号,因此输入源代码多数时候就不需要分号了。
规则是这样的:如果在一个新行前方的最后一个标记是一个标识符(包括像
int
和
float64
这样的单词)、一个基本的如数值这样的文字、或以下标记中的一个时,会自动插入分号:
break continue fallthrough return ++ -- ) }
通常Go程序仅在
for
循环语句中使用分号,以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句,也需要用分号分开。
注意:
无论任何时候,你都不应该将一个控制结构((if
、for
、switch
或select
)的左大括号放在下一行。如果这样做,将会在大括号的前方插入一个分号,这可能导致出现不想要的结果。
map
map在别的语言里可能叫哈希表或叫dict,下面是和map的相关操作的代码,代码很容易懂
指针
Go语言一样有指针,看代码
Go具有两个分配内存的机制,分别是内建的函数new和make。他们所做的事不同,所应用到的类型也不同,这可能引起混淆,但规则却很简单。
内存分配
new 是一个分配内存的内建函数,但不同于其他语言中同名的new所作的工作,
它只是将内存清零,而不是初始化内存。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址,也就是一个类型为*T的值。用Go的术语来说,就是
它返回了一个指向新分配的类型为T的零值的指针。
make(T,
args
)
函数的目的与
new(T)
不同。它仅用于创建切片、map和chan(消息管道),并返回类型
T
(不是
*T
)的一个
被初始化了的(不是
零)实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如,切片是一个具有三项内容的描述符,包括指向数据(在一个数组内部)的指针、长度以及容量,在这三项内容被初始化之前,切片值为
nil
。对于切片、映射和信道,
make
初始化了其内部的数据结构并准备了将要使用的值。如:
下面的代码分配了一个整型数组,长度为10,容量为100,并返回前10个数组的切片
以下示例说明了
new
和
make
的不同。
函数
老实说,我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满(保持和C一样的不可以吗? 呵呵)
函数返回多个值Go中很多Package 都会返回两个值,一个是正常值,一个是错误,如下所示:
函数不定参数例子很清楚了,我就不多说了
函数闭包nextNum这个函数返回了一个匿名函数,这个匿名函数记住了nextNum中i+j的值,并改变了i,j的值,于是形成了一个闭包的用法
函数的递归和c基本是一样的
结构体
Go的结构体和C的基本上一样,不过在初始化时有些不一样,Go支持带名字的初始化。
结构体方法
不多说了,看代码吧。
注意:Go语言中没有public, protected, private的关键字,所以,
如果你想让一个方法可以被别的包访问的话,你需要把这个方法的第一个字母大写。这是一种约定。
接口和多态
接口意味着多态,下面是一个经典的例子,不用多说了,自己看代码吧。
错误处理 – Error接口
函数错误返回可能是C/C++时最让人纠结的东西的,Go的多值返回可以让我们更容易的返回错误,其可以在返回一个常规的返回值之外,还能轻易地返回一个详细的错误描述。通常情况下,错误的类型是error,它有一个内建的接口。
还是看个示例吧:
程序运行后输出:
错误处理 – Defer
下面的程序对于每一个熟悉C语言的人来说都不陌生(有资源泄露的问题),C++使用RAII来解决这种问题。
Go语言引入了Defer来确保那些被打开的文件能被关闭。如下所示:(这种解决方式还是比较优雅的)
Go的defer语句预设一个函数调用(延期的函数),该调用在函数执行defer返回时立刻运行。该方法显得不同常规,但却是处理上述情况很有效,无论函数怎样返回,都必须进行资源释放。
我们再来看一个defer函数的示例:
被延期的函数以后进先出(LIFO)的顺行执行,因此以上代码在返回时将打印4 3 2 1 0。
总之,我个人觉得defer的函数行为有点怪异,我现在还没有完全搞清楚。
错误处理 – Panic/Recover
对于不可恢复的错误,Go提供了一个内建的panic函数,它将创建一个运行时错误并使程序停止(相当暴力)。该函数接收一个任意类型(往往是字符串)作为程序死亡时要打印的东西。当编译器在函数的结尾处检查到一个panic时,就会停止进行常规的return语句检查。
下面的仅仅是一个示例。实际的库函数应避免panic。如果问题可以容忍,最好是让事情继续下去而不是终止整个程序。
当panic被调用时,它将立即停止当前函数的执行并开始逐级解开函数堆栈,同时运行所有被defer的函数。如果这种解开达到堆栈的顶端,程序就死亡了。但是,也可以使用内建的recover函数来重新获得Go程的控制权并恢复正常的执行。 对recover的调用会通知解开堆栈并返回传递到panic的参量。由于仅在解开期间运行的代码处在被defer的函数之内,recover仅在被延期的函数内部才是有用的。
你可以简单地理解为recover就是用来捕捉Painc的,防止程序一下子就挂掉了。
下面是一个例程,很简单了,不解释了
运行结果如下:(我们可以看到Painc后的for循环就没有往下执行了,但是main的程序还在往下走)
你习惯这种编程方式吗?我觉得有点诡异。呵呵。
好了,上面是是一Go语言相关的编程语法的介绍,我没有事无巨细,只是让你了解一下Go语言是长什么样的。
当然,这还没完,请期待下篇——Go语言的特性。
(本文来自:
http://coolshell.cn/articles/8460.html#jtss-tsina)