go基本语法

关键字

  • defer
    延迟(defer)语句,可以在函数中添加多个defer语句。
    当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。
    特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。
  • go
  • chan
  • fallthrough
  • array, map, slice与channel
//array 数组
a :=[3]int{1, 2, 3} //长度为3
b:=[10]int{1, 2, 3} // 长度为10,赋值前三个,其他的为0

slice内置函数:
len 获取slice的长度,
cap 获取slice的最大容量,
append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice;
append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数。
channel :见并发子标题。

  • iota 枚举
    声明enum的时候采用,它默认开始值是0,const中每增加一行加1。
const (
    x, y = iota, iota // x == 0, y == 0
    w = iota // w == 1
    z // z =2 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此z== 2。其实上面y和z可同样不用"= iota"
)

go实例

package main //当前文件属于哪个包,包名main表示它是一个可独立运行的包,它在编译后会产生可执行文件。
//其他的包生成*.a文件
import (
    "fmt"
)
func main() {
    fmt.Print("hello world")
}

new与make的区别

new :返回值是一个指向新分配类型零值的指针

func new(Type) *Type

make:
为slice,map 或 chan 类型分配内存和初始化一个对象
它的定义比 new 多了一个参数,返回类型的引用而不是指针,而返回值也依赖于具体传入的类型

func make(Type, size IntegerType) Type

make实例

//创建切片也使用make函数,它被分配一个零数组和指向这个数组的切片。
//创建一个初始元素个数为5的数组切片,元素初始值为0
a := make([]int, 5) // len(a)=5
/切片有长度和容量。切片的最大长度就是它的容量。
//指定一个切片的容量,通过第三个参数。

//创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间
    b := make([]int, 5, 10)     // len(b)=5, cap(b)=10
//通过重新切片,可使切片增加。
    b = b[:cap(b)] // len(b)=5, cap(b)=5
    b = b[1:] // len(b)=4, cap(b)=4
//直接创建并初始化包含5个元素的数组切片
 c := []int{1,2,3,4,5}

变参

Go函数支持变参。接受变参的函数是有着不定数量的参数的。

func myfunc(arg ...int) {}
//arg ...int告诉Go这个函数接受不定数量的参数。
//注意,这些参数的类型全部是int。
//在函数体中,变量arg是一个int的slice:
for _, n := range arg {
    fmt.Printf("And the number is: %d\n", n)
}

面向对象

func (r ReceiverType) funcName(parameters) (results)
//传入的是ReceiverType的一个copy,
func (r *ReceiverType) funcName(parameters) (results)
//传入的是ReceiverType的引用,在进行修改时需要使用引用
//当你用指针去访问相应的字段时(虽然指针没有任何的字段),Go知道你要通过指针去获取这个值,因此使用时直接使用r,而不是&r。

在方法前添加所依附的主体

import (
    "fmt"
    _ "math"
)
type Rectangle struct {
    width, height float64
}
func (r Rectangle) area() float64 {
    return r.width * r.height
}
func main() {
    r1 := Rectangle{12, 2}
    fmt.Println("Area of r1 is: ", r1.area()) //对象
}

继承

如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method。

package main
import "fmt"
type Human struct {
    name string
    age int
    phone string
}

type Employee struct {
    Human //匿名字段
    company string
}
//在human上面定义了一个method
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
    sam.SayHi()
}

方法重写

type Human struct {
    name string
    age int
    phone string
}
type Student struct {
    Human //匿名字段
    school string
}
type Employee struct {
    Human //匿名字段
    company string
}
//Human定义method
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Employee的method重写Human的method
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) //Yes you can split into 2 lines here.
}
func main() {
    mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
    mark.SayHi()
    sam.SayHi()
}

interface

  • 实例
package main
import "fmt"
type Human struct {
    name string
    age int
    phone string
}
type Men interface {
    SayHi()
    Sing(lyrics 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)
}
func main() {
    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
    //定义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 = sam
    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, 2)
    x[0], x[1] = sam, mike
    for _, value := range x {
        value.SayHi()
    }
}
  • 空interface
    空interface(interface{})不包含任何的method,,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

  • interface 函数参数

interface的变量可以持有任意实现该interface类型的对象

实例:println()内的参数全是string,实际上是所有的参数实现了string()。

  • 判断变量存储的类型

直接判断是否是该类型的变量:


 value, ok = element.(T),

这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。
如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。


package main

import (

    "fmt"

    "strconv"

)

type Element interface{}

type List []Element

type Person struct {

    name string

    age  int

}

//打印

func (p Person) String() string {

    return "(name: " + p.name + " - age: " + strconv.Itoa(p.age) + " years)"

}

func main() {

    list := make(List, 3)

    list[0] = 1 //an int

    list[1] = "Hello" //a string

    list[2] = Person{"Dennis", 70}

    for index, element := range list {

        switch value := element.(type) {

        case int:

            fmt.Printf("list[%d] is an int and its value is %d\n", index, value)

        case string:

            fmt.Printf("list[%d] is a string and its value is %s\n", index, value)

        case Person:

            fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)

        default:

            fmt.Println("list[%d] is of a different type", index)

        }

    }

}

  • 嵌入interface

type Interface interface {

    sort.Interface //嵌入字段

    Push(x interface{}) //a Push method to push elements into the heap

    Pop() interface{} //a Pop elements that pops elements from the heap

}

函数作为值,类型

在Go中函数也是一种变量,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。

type testInt func(int) bool // 声明了一个函数类型
func isOdd(integer int) bool {
    if integer%2 == 0 {  return false }
    return true
}
func isEven(integer int) bool {
    if integer%2 == 0 {    return true }
    return false
}
// 声明的函数类型在这个地方当做了一个参数f 
func filter(slice []int, f testInt) []int {
    var result []int
    for _, value := range slice {
        if f(value) {  result = append(result, value)  }
    }
    return result
}
func main() {
    slice := []int{1, 2, 3, 4, 5, 7}
    fmt.Println("slice = ", slice)
    odd := filter(slice, isOdd) // 函数当做值来传递了
    fmt.Println("Odd elements of slice are: ", odd)
    even := filter(slice, isEven) // 函数当做值来传递了
    fmt.Println("Even elements of slice are: ", even)
}

函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到testInt这个类型是一个函数类型,然后两个filter函数的参数和返回值与testInt类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。

并发

gorutine

协程,但是它比线程更小。
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。

//通过关键字go就启动了一个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执行
}

channel

实例

//创建
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
// 发送和接受ch <- v // 发送v到channel ch.
v := <-ch // 从ch中接收数据,并赋值给v

Buffered Channels

当 value = 0 时,channel 是无缓冲阻塞读写的,当value > 0 时,channel 有缓冲、是非阻塞的,直到写满 value 个元素才阻塞写入。直到其他goroutine从channel 中读取一些元素,腾出空间。

select

select默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。

package main
import "fmt"
func fibonacci(c, quit chan int) {
    x, y := 1, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}
func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 11; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

超时管理

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
        for {
            select {
                case v := <- c:
                    println(v)
                case <- time.After(5 * time.Second):
                    println("timeout")
                    o <- true
                    break
            }
        }
    }()
    <- o
}

处理goroutine的方法

runtime包中有几个处理goroutine的函数:

  • Goexit
    退出当前执行的goroutine,但是defer函数还会继续调用
  • Gosched
    让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。
  • NumCPU
    返回 CPU 核数量。
  • NumGoroutine
    返回正在执行和排队的任务总数。
  • GOMAXPROCS
    用来设置可以并行计算的CPU核数的最大值,并返回之前的值。

总体笔记

  • 下划线表示符_:
  1. 引用不使用的包
  2. 占位符,占据了某个不需要的变量,从而把它忽略。
  • 循环
for _, v := range map{
    fmt.Println("map's val:", v)
}
  • switch
    使用fallthrough强制执行后面的case代码。
posted @ 2018-07-12 15:29  auyeungcarl  阅读(205)  评论(0编辑  收藏  举报