Golang极简入门教程(一):基本概念
安装Golang
在http://golang.org/dl/可以下载到Golang。安装文档:http://golang.org/doc/install。
HelloGo
我们先创建一个文件hello.go:
packagemain import"fmt" funcmain(){ fmt.Printf("helloGolang\n"); }
执行此程序:
gorunhello.go
包
Golang程序由包(packages)组成,程序从main包开始运行:
packagemain
此语句表示此文件属于main包(多个源文件可以属于同一个包)。import语句后接上包所在的路径(被叫做包路径或导入路径),一个目录中放置一个包,通常的做法是,目录名和包名相同:
import( "fmt" "math/rand" )
这里的“fmt”和“math/rand”为包路径(导入路径)。上面的import语句也可以这样写:
import"fmt" import"math/rand"
我们导入了包之后,就可以通过“包名.name”来引用导出的name了,例如:
import"fmt" //fmt包导出了Printf fmt.Printf("helloGolang\n");
在Golang中,一个名字如果首字母大写则表示此名字被导出。
函数
packagemain import"fmt" funcadd(xint,yint)int{ returnx+y } funcmain(){ fmt.Println(add(42,13)) }
需要注意的就是,变量名在类型之前,这和很多语言都不一样。另外xint,yint也可以写为x,yint:
funcadd(x,yint)int{ returnx+y }
函数可以返回多个值:
packagemain import"fmt" funcswap(x,ystring)(string,string){ returny,x } funcmain(){ a,b:=swap("hello","world") fmt.Println(a,b) }
返回值可以被指定变量名,并且像变量一样使用:
packagemain import"fmt" funcsplit(sumint)(x,yint){ x=sum*4/9 y=sum-x return } funcmain(){ fmt.Println(split(17)) }
可以看到split函数直接使用return语句而不用带参数。
变量
变量的声明使用var语句:
variint varc,python,javabool
变量在声明时可以进行初始化:
varx,yint=1,2 vari,j=true,"hello"
我们看到,初始化时可以指定也可以不指定变量类型。
按照Golang的语法,在函数外的任何结构(construct)都通过一个关键字开始,例如变量使用var关键字开始,函数使用func关键字开始。在函数内,变量赋值可以使用:=操作符:
packagemain funcmain(){ varx,yint=1,2 i,j:=true,"hello" }
:=操作符左边为变量,右边为值。
数据类型
基本数据类型:
1.bool
2.string
3.intint8int16int32int64
4.uintuint8uint16uint32uint64
5.uintptr
6.byte(等价于uint8)
7.rune(等价于int32,用于表示一个unicodecodepoint)
8.float32float64
9.complex64complex128
类型转换使用表达式T(v),含义为将v转换为类型T:
variint=42 varffloat64=float64(i) i:=42 f:=float64(i)
类型转换总需要显式的进行。
使用const来声明常量:
constPi=3.14 const( Big=1<<100 Small=Big>>99 )
控制语句
for语句
Golang使用(且只使用)for来进行循环(没有while语句):
packagemain funcmain(){ sum:=0 fori:=0;i<10;i++{ sum+=i } //这种写法等价于C/C++等语言中的while语句 forsum<1000{ sum+=sum } }
区别于C/C++等语言,使用for语句时不需要()并且{}是必须的(后面谈到的if、switch在此语法处理上也是一样的)。如果需要无限循环,那么使用:
for{ }
if语句
if语句可以在执行条件判断前带一个语句(这常被叫做if带上一个短语句),此语句中变量的生命周期在if语句结束后结束。例如:
packagemain import( "fmt" "math/rand" ) funcmain(){ ifn:=rand.Intn(6);n<=2{ fmt.Println("[0,2]",n) }else{ fmt.Println("[3,5]",n) } //这里开始无法使用变量n }
switch
packagemain import( "fmt" "runtime" ) funcmain(){ fmt.Print("Gorunson") //switch类似if可以带上一个短语句 switchos:=runtime.GOOS;os{ case"darwin": fmt.Println("OSX.") case"linux": fmt.Println("Linux.") default: //freebsd,openbsd, //plan9,windows... fmt.Printf("%s.",os) } }
不像C/C++等语言,Golang中无需使用break语句来跳出switch。另外,switch可以没有条件:
packagemain import( "fmt" "time" ) funcmain(){ t:=time.Now() switch{ caset.Hour()<12: fmt.Println("Goodmorning!") caset.Hour()<17: fmt.Println("Goodafternoon.") default: fmt.Println("Goodevening.") } }
defer
一个defer语句能够将一个函数调用加入一个列表中(这个函数调用被叫做deferred函数调用),在当前函数调用结束时调用列表中的函数。范例:
funcCopyFile(dstName,srcNamestring)(writtenint64,errerror){ src,err:=os.Open(srcName) iferr!=nil{ return } defersrc.Close() dst,err:=os.Create(dstName) iferr!=nil{ return } deferdst.Close() returnio.Copy(dst,src) }
deferred函数调用按先进后出的顺序执行:
packagemain import"fmt" funcmain(){ fori:=0;i<5;i++{ //输出43210 deferfmt.Print(i) } }
结构(structs)
结构是一个域的集合:
packagemain import"fmt" typeVertexstruct{ Xint Yint } funcmain(){ v:=Vertex{1,2} v.X=4 fmt.Println(v) }
Golang中是存在指针的,但是指针不支持算术运算:
p:=Vertex{1,2}//{1,2}为structliteral q:=&p//q类型为*Vertex q.X=2//直接访问域X
就像上面看到的,struct的literal由{}包裹,在structliteral中我们可以使用Name:这样的语法来为特定域设置值:
typeVertexstruct{ X,Yint } r:=Vertex{X:3}//这时候Y为0
new函数
我们可以通过表达式new(T)分配一个被初始化为0且类型为T的值,并且返回指向此值的指针,用法如下:
varp*T=new(T) p:=new(T)
更详尽的例子:
packagemain import"fmt" typeVertexstruct{ X,Yint } funcmain(){ v:=new(Vertex) fmt.Println(v) v.X,v.Y=11,9 fmt.Println(v) }
数组和slice
[n]T在Golang中是一个类型(就像*T一样),表示一个长度为n的数组其元素类型为T。范例:
packagemain import"fmt" funcmain(){ vara[2]string a[0]="Hello" a[1]="World" fmt.Println(a[0],a[1]) fmt.Println(a) }
注意,数组长度无法被改变。
slice是一个数据结构,其指向一个数组某个连续的部分。slice用起来很像数组。[]T为slice类型,其中元素类型为T:
packagemain import"fmt" funcmain(){ //构建一个slice p:=[]int{2,3,5,7,11,13} fmt.Println("p==",p) fori:=0;i<len(p);i++{ fmt.Printf("p[%d]==%d\n",i,p[i]) } }
表达式s[lo:hi]用于创建一个slice,新创建的slice的元素为s中[lo,hi)位置的元素。
创建slice使用make函数(而不是用new函数,因为需要设置额外的参数来控制slice的创建):
//len(a)为5 a:=make([]int,5)
这里make函数会创建一个数组(其元素初始化为0)并返回一个slice指向此数组。make可以带第三个参数,用于指定容量:
//len(b)为0 //cap(b)为5 b:=make([]int,0,5) b=b[:cap(b)]//len(b)=5,cap(b)=5 b=b[1:]//len(b)=4,cap(b)=4
一个没有值的slice是nil,长度和容量都为0。
packagemain import"fmt" funcmain(){ varz[]int fmt.Println(z,len(z),cap(z)) ifz==nil{ fmt.Println("nil!") } }
range
range被用在for中来迭代一个slice或者一个map:
packagemain import"fmt" vars=[]int{1,2,3} funcmain(){ fori,v:=ranges{ fmt.Println(i,v) } //只需要值,使用_忽略索引 for_,v:=ranges{ fmt.Println(v) } //只需要索引 fori:=ranges{ fmt.Println(i) } }
map
map用于映射key到value(值)。map可以通过make来创建(而非new):
packagemain import"fmt" typeVertexstruct{ Lat,Longfloat64 } varmmap[string]Vertex funcmain(){ m=make(map[string]Vertex) m["BellLabs"]=Vertex{ 40.68433,-74.39967, } fmt.Println(m["BellLabs"]) }
mapiteral很像structliteral:
varm=map[string]Vertex{ //这里Vertex可以省略不写 "BellLabs":Vertex{ 40.68433,-74.39967, }, "Google":Vertex{ 37.42202,-122.08408, }, }
使用[]来访问map中的值,使用delete来删除map中的值:
m[key]=elem elem=m[key] delete(m,key)
如果需要检查map中某key是否存在使用:
elem,ok=m[key]
elem表示key的值(key不存在时,elem为0),ok表示key是否存在。
闭包
Golang中函数也是一个值(就像int值一样),且函数可以是一个闭包。闭包是一个引用了外部变量的函数。看一个例子:
packagemain import"fmt" funcadder()func(int)int{ sum:=0 //返回一个闭包,此闭包引用了外部变量sum returnfunc(xint)int{ sum+=x returnsum } } funcmain(){ a:=adder() fmt.Println(a(9527)) }