版本: 1.0
时间: 2013.06.23
具有接近C语言的性能,又包含php, java等语言的部分高级特性。
主要有源码安装、标准包安装和第三方工具安装,具体网上查资料。
//linux
export GOROOT=$HOME/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN
export GOPATH=/home/gopath
//windows
一般安装后会自动在环境变量里增加环境配置信息,如果没有需要手动添加
//linux
export GOPATH=/home/gopath
//windows, 设置环境变更
GOPATH=d:\gopath
$GOPATH 目录约定有三个子目录:
src 存放源代码(比如:.go .c .h .s等)
pkg 编译后生成的文件(比如:.a)
bin 编译后生成的可执行文件
$GOPATH/src/hello/main.go(包名:"hello")
package main
import (
"fmt"
)
func main() {
fmt.Printf("Hello, world.")
}
编译运行
go build hello
hello.exe
Hello, world.
创建包和go文件
$GOPATH/src/hello/model/user.go
package model
func getUsername() string{
return “jonn”
}
修改main.go
package main
import (
"fmt"
"hello/model" //导入包
)
func main() {
fmt.Printf("Hello, world, %v", model.GetUsername()) //调用包中的函数
}
go get -u github.com/trygo/ssss
这样就会自动从github.com源码库中下载并安装github.com/trygo/ssss模块程序。位置在:
d:\gopath\src\github.com\trygo\ssss
import (
"github.com/trygo/ssss" //导入包
)
然后就可以使用此包下的结构或模块函数
常用命令
go build
go get
有用命令
go test
go fmt
Go doc
管理命令
go install
go clean
editPlus
LiteIDE
Sublime Text
Vim
Emacs
Eclipse 插件
IntelliJ IDEA 插件
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
//定义一个名称为“variableName”,类型为"type"的变量
var variableName type
定义多个变量
//定义三个类型都是“type”的三个变量
var vname1, vname2, vname3 type
定义变量并初始化值
//初始化“variableName”的变量为“value”值,类型是“type”
var variableName type = value
也可以这样定义,省略了类型
var vname1, vname2, vname3 = v1, v2, v3
还可以这样定义, 类型和var关键字都省略
vname1, vname2, vname3 := v1, v2, v3
如:
var i int
var c string
var j = 100
m,n := 20,30
变量通配符
_(下划线)
_, b := 12, 23
const constantName = value
//也可以明确指定常量的类型:
const Pi float32 = 3.1415926
const Pi = 3.1415926
const i = 100
const prefix = "abc"
bool类型,
值是true或false
数值类型
int, uint //前的gcc和gccgo编译器在32位和64位平台上都使用32位来表示, 但未来在64位平台上可能增加到64位
rune, int8, int16, int32, int64
byte, uint8, uint16, uint32, uint64
float32, float64
complex128,complex64 复数类型
其中rune是int32的别称,byte是uint8的别称。
这些类型的变量之间不允许互相赋值或操作
如下的代码会产生错误
var a int8
var b int32
c:=int32(a) + b
字符串 string
var s string
字符串连接运算 "+"
s = "abc" + "123"
错误类型 error
err := errors.New("error info")
指针类型
所有基本类型和结构体都可以为其定义对应指针类型,go中的指针不能进行指针运算。
var a *int8
var b *int32
var user *User
//给指针赋值
var v int8= 20
a = &v
iota枚举
o里面有一个关键字iota,这个关键字用来声明enum的时候采用,它默认开始值是0,每调用一次加1
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用"= iota"
)
一些规则约定
大写字母开头的变量是公用变量;小写字母开头的就是私有变量。
大写字母开头的函数也是一样,相当于class中的带public关键词的公有函数;小写字母开头的就是有private关键词的私有函数。
array、slice、map
array
定义方式如下:
var arr [n]type
var arr [10]int
arr[0] = 42
arr[1] = 13
a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度
slice
slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。
// 和声明array一样,只是少了长度
var fslice []int
//定义并初始化数据
slice := []byte {'a', 'b', 'c', 'd'}
从一个数组或一个已经存在的slice中再次声明
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// 声明两个含有byte的slice
var a, b []byte
// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]
// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]
slice是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值,例如上面的aSlice和bSlice,如果修改了aSlice中元素的值,那么bSlice相对应的值也会改变。
对于slice有几个有用的内置函数:
len 获取slice的长度
cap 获取slice的最大容量
append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice
copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数
a = append(a, ‘4’)
map
var mapname map[keyType]valueType
var numbers map[string] int
//另一种map声明方式
numbers := make(map[string]int)
//赋值
numbers["one"] = 1
numbers["ten"] = 10
numbers["three"] = 3
make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配
make([]byte, 2, 8)
make(map[string]int)
make(chan string)
new(Structname)
if
if 条件表达式 {
} else if 条件表达式 {
} else if 条件表达式 {
} else {
}
goto
func myFunc() {
i := 0
Here: //这行的第一个词,以冒号结束作为标签
println(i)
i++
goto Here //跳转到Here去
}
for
for expression1; expression2; expression3 {
//...
}
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
在循环里面有两个关键操作break和continue
break和continue还可以跟着标号,用来跳到多重循环中的外层循环
for配合range可以用于读取slice和map的数据:
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
for _, v := range map{
fmt.Println("map's val:", v)
}
switch
switch sExpr {
case expr1:
...
case expr2,expr3,expr4:
...
case expr5:
...
fallthrough
case expr6:
...
default:
other code
}
函数
func funcName(input1 type1, input2 type2) type {
return value
}
func funcName(input1 type1, input2 type2) (type1, type2){
return value1, value2
}
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
return value1, value2
}
Go的函数支持多返回值
变参
func myfunc(args ...int) {
//args是一切片类型
for _, n := range args {
fmt.Printf("And the number is: %d\n", n)
}
}
Go里参数传递或变量赋值,都是采用值传递,都会生成一个新副本,如果想要引用方式传值,可以使用指针
go特别的地方之一,在defer中的语句会在函数执行结束或抛出异常之前执行
func ReadWrite() bool {
file.Open("file")
defer file.Close() //之后不管程序在什么地方返回,都会执行file.close()操作
if failureX {
return false
}
if failureY {
return false
}
return true
}
如果想在defer中执行多条语句
func ReadWrite() bool {
file1.Open("file1")
file2.Open("file2")
defer func(){
file1.Close()
file2.Close()
}()
...
}
定义
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
如:
type testInt func(int) bool // 声明了一个函数类型
然后所有具有相同参数和返回值的函数都可以赋值给testInt
func isOdd(integer int) bool {
if integer%2 == 1 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
testInt = isOdd
testInt(10)
中断并向上层函数抛出异常,函数参数为抛出的异常信息。
此函数可直接调用,也可由运行时错误产生。
如果在最外层一直没有处理此异常,将导致程序退出
defer中的函数将会被执行
recover仅在延迟函数(defer)中有效, 可以用于抓住panic抛出的信息
例:
type User struct{
Name string
}
func check(user *User) {
if user == nil {
panic("user not exit")
}
}
func testRecover(user *User){
defer func(){
if r := recover(); r != nil {
fmt.Println(r)
}
}()
check(user)
fmt.Printf("user (%v) is exit\n", user.Name)
}
func main() {
testRecover(nil);
user := new(User)
user.Name = "try"
testRecover(user)
}
main() 应用入口函数
init() 包中的每一个go文件都可以定义此函数,会自动被执行
用于导入外部包
import "fmt"
或
import(
"fmt"
"time"
"github.com\trygo\ssss"
)
. 点操作
//在之后使用此包中的函数时可以省略包名
import(
. "fmt"
)
别名
import(
f "fmt"
)
_操作
//引入该包,而不直接使用包里面的函数, 但包中的init()函数会自动执行
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
定义结构体, 如:
type Person struct {
Name string
Age int
}
创建和初始化:
p := new(Person) //使用new操作符创建, 值为系统默认,一般数值类型的属性会设置为0, 串类型的设置为长度为0的空串
p := &Person{Age:25, Name:"Bob"} //创建并初始化值, 而且返回结构指针,p是指针类型
当然也可以这样,不过不建议这样写,这样会进行一次结构赋值操作, p是值类型
p := Person{Age:25, Name:"Bob"}
struct的匿名字段
可以理解为面向对象中类型包含关系
type Grade struct{
Algebra float32
Chemistry float32
}
type Student struct {
Name string
Age int
Grade
}
初始化
s := &Student{Name:”Bob”, Age:18, Grade:Grade{Algebra:59.5, Chemistry:99.9}}
fmt.Printf("name %v\n", s.Name)
fmt.Printf("Algebra %v\n", s.Algebra)
相同属性名
如果当前结构体与被包含结构体的属性名相同,默认优先访问当前结构体中的属性值
如果想要访问被包含结构体的属性值,加上被包含结构名即可
假设Student和Grade中都有TotalVal 属性,那么:
s := &Student{Name:”Bob”, Age:18, Grade:Grade{Algebra:59.5, Chemistry:99.9,}}
s.TotalVal //访问的是当前结构体中的
s.Grade.TotalVal //访问的是被包含结构体中的
Go中的面向对象,其实并不是完全意义上的面向对象,不过就开发而言已经够用了,而且有些特性更先进,更灵活
给结构类型添加方法
type Rectangle struct {
Width, Height float64
}
func (this *Rectangle) Area() float64 {
return this.Width * this.Height
}
给内置类型添加方法
type IntVector []int //定义一个Int类型的容器
//添加方法
func (this *IntVector) Size() int{
return len(*this)
}
func (this *IntVector) Add(val int) *IntVector{
*this = append(*this, val)
return this
}
func main() {
v := new(IntVector)
v.Add(3)
v.Add(5)
fmt.Printf("IntVector %v\n", v)
fmt.Printf("IntVector size %v\n", v.Size())
}
结果为:
IntVector &[3 5]
IntVector size 2
给基本类型添加方法
type Count int //定义一个计数器
//添加方法
func (this *Count) Inc(){
*this += 1
}
func (this Count) GetValue() int{
return int(this)
}
func main() {
var c Count
c.Inc()
c.Inc()
fmt.Printf("Count value %v\n", c)
fmt.Printf("Count value %v\n", c.GetValue())
}
结果为:
Count value 2
Count value 2
method继承
结构体中定义的字段可以继承,同样,方法也可以继承
type Grade struct{
Algebra float32
Chemistry float32
}
func (this *Grade) Total() float32 {
return this.Algebra + this.Chemistry
}
type Student struct {
Name string
Age int
Grade
}
func main() {
s := &Student{Name:"Bob", Age:18, Grade:Grade{Algebra:59.5, Chemistry:99.9}}
fmt.Printf("Name %v\n", s.Name)
fmt.Printf("Algebra %v\n", s.Algebra)
fmt.Printf("Total %v\n", s.Total())
}
结果为:
Name Bob
Algebra 59.5
Total 159.4
method重写
只要与被包含类型中的方法名相同就行
先这样打个比喻:
“
其它语言如梦c++,java中的接口,就象是国家给你颁发的各种证书,如:毕业证,程序员证书等,然后,你要去政府部门找工作,面试官问你,你是博士文凭吗,你有高级程序员证书吗,如果你有,好,就把你收了,然后安排写程序的相关工作给你做。
go中的接口就象是你去某些私营企业找工作,面试官问你,你会用golang语言写程序吗,你说能,那好,把你收了,然后安排写程序的相关工作给你做。
”
go的接口是,只要你能做,那就可以把任务交给你做。其它许多语言中的接口是,必须事先实现某特定接口,才能够把任务交给你去做
type Rectangle struct {
Width, Height float64
}
func (this *Rectangle) Area() float64 {
return this.Width * this.Height
}
type Circle struct {
Radius float64
}
func (this *Circle) Area() float64 {
return this.Radius * this.Radius * math.Pi
}
type Shape interface{
Area() float64
}
func main() {
r := &Rectangle{Width:10, Height:10}
fmt.Printf("Rectangle area %v\n", r.Area())
c := &Circle{Radius:20}
fmt.Printf("Circle area %v\n", c.Area())
var s Shape //定义接口变更
s = r;
fmt.Printf("Shape area %v\n", s.Area())
s = c
fmt.Printf("Shape area %v\n", s.Area())
}
运行结果:
Rectangle area 100
Circle area 1256.6370614359173
Shape area 100
Shape area 1256.6370614359173
空interface
就是没有定义任何方法的空接口
var a interface{}
定义a为一个空接口变量, a可以存任何值,基本数据类型,内置结构类型,自定义结构类型等
func main() {
var a interface{}
var i int = 8
s := "Hello world"
a = i
fmt.Printf("interface a value is : %v\n", a)
a = s
fmt.Printf("interface a value is : %v\n", a)
a = &Rectangle{Width:10, Height:10}
fmt.Printf("interface a value is : %v\n", a)
}
结果为:
interface a value is : 8
interface a value is : Hello world
interface a value is : &{10 10}
interface作为函数参数
如果是空接口,那么表示函数可以接收或返回任何类型的数据,
interface变量存储的类型
如何检查接口变量是什么类型呢
Comma-ok断言
value, ok = element.(T)
如:
r := &Rectangle{Width:10, Height:10}
var s Shape
s = r;
//下面检查s是否Rectangle类型
val, ok := s.(*Rectangle)
if ok {
//如果返回ok, val中就是Rectangle类型值了
fmt.Printf("is Rectangle, area is : %v\n", val.Area())
}
switch测试
r := &Rectangle{Width:10, Height:10}
c := &Circle{Radius:20}
var ss [2]Shape
ss[0] = r
ss[1] = c
for i:=0; i<len(ss); i++{
switch val := ss[i].(type) {
case *Rectangle:
fmt.Printf("is Rectangle, area is : %v\n", val.Area())
case *Circle:
fmt.Printf("is Circle, area is : %v\n", val.Area())
default:
fmt.Printf("other Shape, area is : %v\n", ss[i].Area())
}
}
interface继承
可能将Shape接口重新设计,如
type Areaer interface{
Area() float64
}
type Drawer interface{
Draw()
}
type Shape interface{
Areaer
Drawer
}
反射
go也有类型反射功能,具体功能是由reflect包中的方法实现的
t := reflect.TypeOf(s) //返回reflect.Type结构体
v := reflect.ValueOf(s) //返回reflect.Value结构体
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签
name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
协程 goroutine
是一种轻量级线程,程序里是不能直接操作系统线程的,系统线程的分配管理都由go语言自己来维护。
协程是由go来管理维护的,效率非常高,并行处理几十万个协程没有压力
可将任何函数以并行方式执行,只要在前面加上go关键字就行,如:
func func1(){
...
}
go func1()
相关的几个runtime函数
runtime.Gosched() 手动将CPU时间片让给别人
runtime.GOMAXPROCS(n) 手动设置最大使用多少个系统线程来处理协程
channels通道
用于在协程之间传递数据
ci := make(chan int) //定义int类型的通道
cs := make(chan string) //定义string类型通道
cf := make(chan interface{}) //定义interface{}类型通道
channel通过操作符<-来接收和发送数据
ci <- val //将值val发送给ci通道
val := <- ci //从ci通道中接收数据并赋值给val
Buffered Channels
带缓冲区的通道
ch := make(chan type, value)
value == 0 ! 无缓冲(阻塞)
value > 0 ! 缓冲(非阻塞,直到value 个元素)
Range和Close
可以使用range遍历通道中的数据
使用close()内置函数关闭通道
func generate(c chan int){
//循环生成0到10,将通过通道发送出去
for i:=0; i<10; i++{
c <- i
}
}
func main(){
c := make(chan int, 10)
go generate(c) //启动程,并将通道做为参数绘给函数
for i := range c {
fmt.Println(i)
}
}
Select
使用select可以更灵活方式的处理通道中的数据
默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行。
如:
func generate(c chan int){
//循环生成0到10,将通过通道发送出去
for i:=0; i<5; i++{
fmt.Println("src val is ", i)
c <- i
time.Sleep(time.Second*1)
}
close(c) //其最好不在这里关闭通道
}
func square(c chan int, quit chan int){
for{
select {
case v := <- c:
c <- v * v
case <-quit:
fmt.Println("square squit")
return
}
}
}
func main(){
runtime.GOMAXPROCS(4)
src := make(chan int) // 生成值的通道
ch := make(chan int) // 请求值平方的通道
quit := make(chan int) //用于发送退出消息的通道
go generate(src)
go square(ch, quit)
//runtime.Gosched()
func(){
for {
select {
case v,ok := <- src:
if !ok{
fmt.Println("exit")
return
}
ch <- v //如果从src通道中成功取到值,就将值发送给in通道
case v,ok := <- ch:
if !ok{
return
}
fmt.Println("square val is ", v)
}
}
}()
quit <- 0
//time.Sleep(time.Second*1) //主要是为了看效果
}
其实使用go的http包就可以搭建web服务器, 而且常用功能都有相应的库支持,一些第三方的go web框架也就是把这些系统库组装组装而已
使用 "net/http" 包创建http web服务
//定义一个http响应函数
func hello(w http.ResponseWriter, r *http.Request) {
//r.ParseForm() //解析数据, 如果使用 r.Form 的话,默认是不解析的
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello try!")
}
func main() {
http.HandleFunc("/", hello) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
第三方web框架
web.go 小巧灵活,请求在函数中处理, Web.Go 跟 web.py 类似,但使用的是 Go 编程语言实现的 Web 应用开发框架。
Golanger
beego
...
另外自己也搞了一个WEB框架,放在 github.com/trygo/ssss 上,用起来非常简单
支持MVC,类型内方法路由,JSON/JSON(JQueryCallback)/XML服务,模板,静态文件输出。暂时不支持会话管理模块和正则路由。
//sssshello.go
package main
import (
"github.com/trygo/ssss"
)
type MainController struct {
ssss.Controller
}
func (this *MainController) Hello() {
this.RenderText("hello world")
}
func (this *MainController) Hi() {
this.RenderHtml("<html><body>Hi</body></html>")
}
func main() {
ssss.Register("GET|POST", "/", &MainController{}, "Hello")
ssss.Register("GET|POST", "/hi", &MainController{}, "Hi")
var cfg ssss.Config
//cfg.HttpAddr = "0.0.0.0"
cfg.HttpPort = 8080
ssss.Run(&cfg)
}
End Thank.