Go语言学习要点总结

包##

Go语言程序是通过package来组织的。例如最简单的HelloWorld程序:

package main
import "fmt"
func main() {
    fmt.Printf("hello world");
}

其中package <pkgname>这一行告诉我们当前文件属于哪个包,这里的main则表示它是一个可以独立运行的包,名为main的包在编译之后会产生可执行文件,其他的包最后都会生成pkgname.a文件。

要使用其他包中的函数,则需要导入该包,即例中的import fmt,包中有很多导出函数,当要使用包中的函数时,使用pkgName.funcName语法即可。

值得一提的时,Go语言通过首字母的大小写来控制包中的变量或者函数是否导出,大写表示可以导出,小写表示包的私有数据。而不是依靠像public,private这样的关键字,Go的核心理念是简洁。


变量声明##

Go语言的变量声明采用了类型在后的方式,例如:
var var1 int声明了一个int类型的变量,var关键字是Go最基本的定义变量方式。
Go语言能够自动推导变量类型,这意味着,如果在变量声明的时候,同时赋值的话,可以直接这样写:

a := 1      // 定义一个int型变量a
b := 1.0    // 定义一个float型变量b

多返回值函数##

Go语言的函数支持多返回值,例如:

 func Swap(a int, b int) (int, int) {
    return b, a
 }

如果给返回值指定了名字,也可用以下写法:

func SumAndProduct(a int, b int) (s int, p int) {
    s = a + b
    p = a * b
    return  // 不用写出返回值
}

需要注意的一点是:Go语言强制了{必须和语句在同一行。


method##

通过method,Go语言也可以支持面向对象,method语法如下:
func (r ReceiveType) funcName(parameters) (results)
上面的语法表示funcName函数是附属在类型ReceiveType上的,下面给出一个简单的例子:

package main
import (
    "fmt"
    "math"
)

type Rectangle struct {
    width, height float64
}

type Circle struct {
    radius float64
}

func (r Rectangle) area() float64 {
    return r.width * r.height
}

func (c Circle) area() float64 {
    return c.radius * c.radius * math.Pi
}

func main() {
    r1 := Rectangle{12, 2}
    r2 := Rectangle{9, 4}
    c1 := Circle{10}
    c2 := Circle{25}

    fmt.Println("Area of r1 is: ", r1.area())
    fmt.Println("Area of r2 is: ", r2.area())
    fmt.Println("Area of c1 is: ", c1.area())
    fmt.Println("Area of c2 is: ", c2.area())
}
  • method可以访问接收者的字段
  • 通过.方式调用method
  • method支持继承,这里就不展开了

Interface##

如果说method提供了Go语言面向对象的支持,那么interface则是某种意义上Go对泛型编程的支持。

那么究竟什么是interface呢?简单来说interface就是一组method的组合,我们通过interface的来定义对象的一组行为。下面的例子演示了语法:

package main
import "fmt"

type Human struct {
    name string
    age int
    phone string
}

type Student struct {
    Human //匿名字段
    school string
    loan float32
}

type Employee struct {
    Human //匿名字段
    company string
    money float32
}

//Human实现SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Human实现Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

//Employee重载Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone)
    }

// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    Tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}

    //定义Men类型的变量i
    var i Men

    //i能存储Student
    i = mike
    fmt.Println("This is Mike, a Student:")
    i.SayHi()
    i.Sing("November rain")

    //i也能存储Employee
    i = Tom
    fmt.Println("This is Tom, an Employee:")
    i.SayHi()
    i.Sing("Born to be wild")

    //定义了slice Men
    fmt.Println("Let's use a slice of Men and see what happens")
    x := make([]Men, 3)
    //这三个都是不同类型的元素,但是他们实现了interface同一个接口
    x[0], x[1], x[2] = paul, sam, mike

    for _, value := range x{
        value.SayHi()
    }
}

Go规定如果某个对象实现了某个接口的所有方法,此对象就实现了此接口。即所谓的duck-typing:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。

上面的例子定义了接口Men,由于Student和Employee都实现了Men中的所有方法,Student和Employee就实现了Men接口。我们就能用一个Men类型的变量存储Student和Employee对象。

注:因为Student和Employee都包含一个匿名的Human字段,那么它们默认就已经实现了Human中的方法(继承),但是可以通过例子中的方式对该方法进行重写。

空interface###

空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。例如:

// 定义a为空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存储任意类型的数值
a = i
a = s

并发##

Go语言最大的亮点在于,它在语言层面就支持了并行。这就是goroutine语法。

goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。

如何使用goroutine可见下例:

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") //开一个新的Goroutines执行
    say("hello") //当前Goroutines执行
}

// 以上程序执行后将输出:
// hello
// world
// hello
// world
// hello
// world
// hello
// world
// hello

经常与goroutine搭配使用的还有channel,channel有点类似Unix shell中的双向管道,可以通过它收发数据,这些值必须是特定的类型:channel类型。主要是为了解决goroutines之间的同步问题。

默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得Goroutines同步变的更加的简单,而不需要显式的lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。

当然Go也支持channel的缓冲大小,即channel可以存储多少元素。例如ch := make(chan bool, 4),创建了可以存储4个元素的bool型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。

闭包##

Go语言支持闭包,详见博文闭包

posted on 2014-09-03 15:23  joy.zhuang  阅读(293)  评论(0编辑  收藏  举报

导航