GO语言入门培训资料

作者: [email protected]

版本: 1.0

时间: 2013.06.23



1. Golang简介:

具有接近C语言的性能,又包含php, java等语言的部分高级特性。


2. 安装,环境配置

主要有源码安装、标准包安装和第三方工具安装,具体网上查资料。


1) 配置环境

//linux

export GOROOT=$HOME/go

export GOBIN=$GOROOT/bin

export PATH=$PATH:$GOBIN

export GOPATH=/home/gopath


//windows

一般安装后会自动在环境变量里增加环境配置信息,如果没有需要手动添加


2) GOPATH与工作空间

//linux

export GOPATH=/home/gopath

//windows, 设置环境变更

GOPATH=d:\gopath


$GOPATH 目录约定有三个子目录:

src 存放源代码(比如:.go .c .h .s等)

pkg 编译后生成的文件(比如:.a

bin 编译后生成的可执行文件



3. 程序目录结构


1) 建立包和目录


$GOPATH/src/hello/main.go(包名:"hello"


2) 创建main.go文件


package main


import (

   "fmt"

)


func main() {

  fmt.Printf("Hello, world.")

}



编译运行

go build hello


hello.exe

Hello, world.



3) 引用其它包中的结构或函数


创建包和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())  //调用包中的函数

}



4) 获取远程包


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"   //导入包

)

然后就可以使用此包下的结构或模块函数



4. Go 命令



常用命令

go build

go get


有用命令

go test

go fmt

Go doc


管理命令

go install

go clean



5. Go开发工具


editPlus

LiteIDE

Sublime Text

Vim

Emacs

Eclipse 插件

IntelliJ IDEA 插件




6. Go语言基础


1) 所有25个关键字:


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


2) 变量


//定义一个名称为“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




3) 常量


const constantName = value

//也可以明确指定常量的类型:

const Pi float32 = 3.1415926


const Pi = 3.1415926

const i = 100

const prefix = "abc"


4) 内置基础类型


bool类型

值是truefalse


数值类型

int, uint  //前的gccgccgo编译器在32位和64位平台上都使用32位来表示但未来在64位平台上可能增加到64

rune, int8, int16, int32, int64

byte, uint8, uint16, uint32, uint64

float32, float64

complex128,complex64 复数类型

其中runeint32的别称,byteuint8的别称。


这些类型的变量之间不允许互相赋值或操作

如下的代码会产生错误


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。其实上面yz可同样不用"= iota"

)




一些规则约定

大写字母开头的变量是公用变量;小写字母开头的就是私有变量。

大写字母开头的函数也是一样,相当于class中的带public关键词的公有函数;小写字母开头的就是有private关键词的私有函数。



arrayslicemap


array

定义方式如下:

var arr [n]type



var arr [10]int 

arr[0] = 42   

arr[1] = 13 


a := [3]int{1, 2, 3} // 声明了一个长度为3int数组

b := [10]int{1, 2, 3} // 声明了一个长度为10int数组,其中前三个元素初始化为123,其它默认为0

c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度


slice

slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层arrayslice的声明也可以像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'}


// 声明两个含有byteslice

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是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值,例如上面的aSlicebSlice,如果修改了aSlice中元素的值,那么bSlice相对应的值也会改变。


对于slice有几个有用的内置函数:


len 获取slice的长度

cap 获取slice的最大容量

append slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice

copy 函数copy从源slicesrc中复制元素到目标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



5) make、new操作


make用于内建类型(mapslice channel)的内存分配。new用于各种类型的内存分配


make([]byte, 2, 8)

make(map[string]int)

make(chan string)


new(Structname)




6) 流程和函数



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

}


在循环里面有两个关键操作breakcontinue


breakcontinue还可以跟着标号,用来跳到多重循环中的外层循环


for配合range可以用于读取slicemap的数据:


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,expr3expr4:

    ...

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里参数传递或变量赋值,都是采用值传递,都会生成一个新副本,如果想要引用方式传值,可以使用指针



7) defer


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()

    }()

    ...

}



8) 函数作为值、类型


定义

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)


7. GO异常处理,PanicRecover


1) panic 内建函数


中断并向上层函数抛出异常,函数参数为抛出的异常信息。

此函数可直接调用,也可由运行时错误产生。

如果在最外层一直没有处理此异常,将导致程序退出

defer中的函数将会被执行



2) Recover 内建函数


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)



}



8. main函数和init函数


main() 应用入口函数

init() 包中的每一个go文件都可以定义此函数,会自动被执行




9. import


用于导入外部包


import "fmt"



import(

    "fmt"

"time"

"github.com\trygo\ssss"

)



. 点操作

//在之后使用此包中的函数时可以省略包名

import(

    . "fmt" 

)


别名

import(

    f "fmt" 

)


_操作

//引入该包,而不直接使用包里面的函数, 但包中的init()函数会自动执行

import (

    "database/sql"

_ "github.com/ziutek/mymysql/godrv"


)


10. struct类型


定义结构体:


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   //访问的是被包含结构体中的


11. 面向对象


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重写


只要与被包含类型中的方法名相同就行



12. interface


先这样打个比喻:

其它语言如梦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()  //获取存储在第一个字段里面的值




13. 并发编程


协程 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 个元素)



RangeClose


可以使用range遍历通道中的数据

使用close()内置函数关闭通道


func generate(c chan int){

   //循环生成010,将通过通道发送出去

   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){

   //循环生成010,将通过通道发送出去

   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) //主要是为了看效果

}


14. web, http服务


其实使用gohttp包就可以搭建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. 





你可能感兴趣的:(http,Web,Go,Go,golang,go语言入门培训资料)