go


                        Go !详细资料见书籍

下载地址:                                                                   链接: https://pan.baidu.com/s/1c2kW4bY 密码: aawn


Go !详细资料见书籍

开始
  hello
    hello world
        package main import "fmt" func main() { fmt.Println("hello,world") }
  Go Playground
    一个搭建在golang.org上的一个web服务
    可以在web上运行go语言程序
    沙盒编译、链接和运行,然后输出返回
    https://play.golang.org
包、变量、函数
  包
    每个Go语言都是由包组成
    package 将程序打包
    import 导入其他包
  导入
    写多个导入语句
        import "fmt" import "math"
    打包导入
        import ( "fmt" "math" )
    建议养成打包导入的好习惯
  导出
    首字母大写的名称是被导出的
    在导入包之后,你只能访问包所导出的名字,任何未导出的名字是不能被包外的代码访问的
    Foo和FOO都是被导出的名称。名称foo是不会被导出的。
  函数
    函数可以没有参数或接受多个参数
        例子
                package main import "fmt" func add(x int, y int) int { return x + y } func main() { fmt.Println(add(2, 3)) }
        扩展
                一个或者多个函数命名参数是同一类型的可以省略
                x int,y int == x,y int
    可以返回任意数量的返回值
        例子
                package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) }
                world hello
  命名返回值
    返回值可以被命名,并且就像在函数体开头声明的变量那样使用
    返回值的名称最好有一定的意义,可以作为文档使用
    没有参数的return语句返回各个返回变量的当前值。(裸返回)
    返回语句最好用短函数:可读性考虑
  变量
    var 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面
    `var` 语句可以定义在包或函数级别
    初始化变量
        变量定义可以包含初始值,每个变量对应一个。
        如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。
    短声明变量
        在函数中,`:=` 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。
        函数外的每个语句都必须以关键字开始(`var`、`func`、等等),`:=` 结构不能使用在函数外。
    基本类型
        int,uint和uintptr类型在32位的系统上一般是32位,而在64位系统上是64位。当你需要使用一个整数类型时,你应该首选int,仅当有特别的理由才使用定长整数类型或者无符号整数类型
    零值
        变量在定义时没有明确的初始化时会赋值为_零值_
        数值类型为 `0`
        布尔类型为 `false`
        字符串为 `""`(空字符串)
    类型转换
        在不同类型之间的项目赋值时需要显式转换
                例子
                package main import ( "fmt" "math" ) func main() { var x, y int = 3, 4 var f float64 = math.Sqrt(float64(x*x + y*y)) var z int = int(f) fmt.Println(x, y, z) }
                3 4 5
    类型推导
        在定义一个变量但不指定其类型时(使用没有类型的 var 或 := 语句), 变量的类型由右值推导得出
        当右值定义了类型时,新变量的类型与其相同:
                variint j:=i//j也是一个int
        但是当右边包含了未指名类型的数字常量时,新的变量就可能是int、float64或complex128。 这取决于常量的精度:
                i:=42//int f:=3.142//float64 g:=0.867+0.5i//complex128
  常量
    常量的定义与变量类似,只不过使用 const 关键字。
    常量可以是字符、字符串、布尔或数字类型的值。
    常量不能使用 := 语法定义。
    数值常量
        数值常量是高精度的 _值_
        一个未指定类型的常量由上下文来决定其类型
        (int可以存放最大64位的整数,根据平台不同有时会更少。)
流程控制语句
  for
    Go 只有一种循环结构——`for` 循环。
    基本的for循环包含三个由分号分开的组成部分
        初始化语句:在第一次循环执行前被执行
                一般是一个短变量声明,这里声明的变量仅在整个for循环语句可见
        循环条件表达式:每轮迭代开始前被求值
                如果条件表达式的值变为false,那么迭代将终止
        后置语句:每轮迭代后被执行
    基本的 for 循环除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中做的一样,而 `{ }` 是必须的
    跟 C 或者 Java 中一样,可以让前置、后置语句为空。
    循环初始化语句和后置语句都是可选的。
    for 是 Go 的 “while”
        基于此可以省略分号:C 的 while 在 Go 中叫做 `for`。
    死循环
        如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环。
        package main func main() { for { } }
  if
    就像for循环一样,Go 的if语句也不要求用( )将条件括起来,同时,{ }还是必须有的。
        package main import ( "fmt" "math" ) func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) }
                1.4142135623730951 2i
    便捷语句
        跟for一样,if语句可以在条件之前执行一个简单语句
        由这个语句定义的变量的作用域仅在if范围之内。
    if 和 else
        在 if 的便捷语句定义的变量同样可以在任何对应的 else 块中使用
                package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // 这里开始就不能使用 v 了 return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }
                27 >= 20 9 20
  switch
    一个结构体(`struct`)就是一个字段的集合
    除非以 fallthrough 语句结束,否则分支会自动终止
    switch 的条件从上到下的执行,当匹配成功的时候停止。
    Golang 中无需使用 break 语句来跳出 switch。另外,switch 可以没有条件
    没有条件的 switch同 `switch true` 一样
        这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。
                package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } }
  defer
    defer 语句会延迟函数的执行直到上层函数返回
    延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用
    defer 栈
        延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用
                package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") }
                counting done 9 8 7 6 5 4 3 2 1 0
复杂类型
  指针
    Go 具有指针。 指针保存了变量的内存地址
    类型 *T 是指向类型 T 的值的指针。其零值是 `nil`。
        var p *int
    符号会生成一个指向其作用对象的指针。
        i := 42
        p = i
    符号表示指针指向的底层的值。
        fmt.Println(*p) //通过指针p读取i
        *p=21 //通过指针p设置i
        这也就是通常所说的“间接引用”或“非直接引用”。
    与 C 不同,Go 没有指针运算。
    注释
        j=*i;//说明j是普通变量,i是指针变量,意思是把i指向的变量中的值赋值给j
        j=&i//说明j是指针变量,i是普通变量,取i的地址然后赋值给j,这样j就指向i了
        j=*&i//相当于i赋值给了j
  结构体
    一个结构体(struct)就是一个字段的集合。
        package main import "fmt" type Vertex struct { //结构struct 类型type X int Y int } func main() { fmt.Println(Vertex{1, 2}) }
                {1 2}
    结构体字段
        结构体字段使用点号来访问
                package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }
                4
    结构体指针
        结构体字段可以通过结构体指针来访问
        通过指针间接的访问是透明的
                package main import "fmt" type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} p := &v p.X = 1e9 fmt.Println(v) }
                {1000000000 2}
    结构体文法
        结构体文法表示通过结构体字段的值作为列表来新分配一个结构体
        使用 Name: 语法可以仅列出部分字段
        特殊的前缀 & 返回一个指向结构体的指针
  数组
    类型 [n]T 是一个有 n 个类型为 T 的值的数组。
    var a [10]int
        定义变量 a 是一个有十个整数的数组。
    数组的长度是其类型的一部分,因此数组不能改变大小
    slice
        一个 slice 会指向一个序列的值,并且包含了长度信息
        []T 是一个元素类型为 T 的 slice。本质上就是不指定大小。
        slice不存储任何数据,它只是描述了底层数组的一部分。
        数组具有固定大小。另一方面,slice是对数组元素的特殊处理,动态大小,数组元素视图选取灵活。在实践中,切片比数组更常见。
                package main import "fmt" func main() { primes := [6]int{2,3,5,7,11,13} var s []int = primes[1:4] fmt.Println(s) }
                切片
                可以重新切片,创建一个新的 slice 值指向相同的数组
                s[lo:hi]
                package main import "fmt" func main() { p := []int{2, 3, 5, 7, 11, 13} fmt.Println("p ==", p) fmt.Println("p[1:4] ==", p[1:4]) // 省略下标代表从 0 开始 fmt.Println("p[:3] ==", p[:3]) // 省略上标代表到 len(s) 结束 fmt.Println("p[4:] ==", p[4:]) }
                p == [2 3 5 7 11 13] p[1:4] == [3 5 7] p[:3] == [2 3 5] p[4:] == [11 13]
                [3 5 7]
        构造 slice
                slice 由函数 make 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组
                a := make([]int, 5) // len(a)=5
                为了指定容量,可传递第三个参数到 `make`:
                b := make([]int, 0, 5) // len(b)=0, cap(b)=5 b = b[:cap(b)] // len(b)=5, cap(b)=5 b = b[1:] // len(b)=4, cap(b)=4
        nil slice
                slice 的零值是 `nil`。
                一个 nil 的 slice 的长度和容量是 0。
        向 slice 添加元素
                内建函数 `append`
                func append(s []T, vs ...T) []T
                append 的第一个参数 s 是一个类型为 T 的数组,其余类型为 T 的值将会添加到 slice。
                append 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。
                如果 s 的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。
        Slice 边界默认值
                当选择slice的范围时,你可以忽略声明低边界或者高边界。低边界的默认值是0,高边界的默认值是数组的长度
    range
        for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。
                package main import "fmt" var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } }
                2**0 = 1 2**1 = 2 2**2 = 4 2**3 = 8 2**4 = 16 2**5 = 32 2**6 = 64 2**7 = 128
        可以通过赋值给 _ 来忽略序号和值。
        如果只需要索引值,去掉“, value”的部分即可。
                package main import "fmt" func main() { pow := make([]int, 10) for i := range pow { pow[i] = 1 << uint(i) } for _, value := range pow { fmt.Printf("%d\n", value) } }
                1 2 4 8 16 32 64 128 256 512
    map
        map 映射键到值。
        map 在使用之前必须用 make 而不是 new 来创建;会自动完成初始化,值为 nil 的 map 是空的,并且不能赋值。
        map 的文法跟结构体文法相似,不过必须有键名。
                package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, } func main() { fmt.Println(m) }
                map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
        如果顶级的类型只有类型名的话,可以在文法的元素中省略键名。
                package main import "fmt" type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, } func main() { fmt.Println(m) }
        修改 map
                在 map m 中插入或修改一个元素: m[key] = elem
                获得元素: elem = m[key]
                删除元素: delete(m, key)
                通过双赋值检测某个键存在: elem, ok = m[key]
                如果 key 在 m 中,`ok` 为 true 。否则, ok 为 `false`,并且 elem 是 map 的元素类型的零值
                当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值
                如果elem和ok之前没有被声明,可以直接使用自动类型推导 elem,ok:=m[key]
  函数值
    函数也是值。他们可以像其他值一样传递,同样,函数值可以作为函数的参数或者返回值。
        package main import ( "fmt" "math" ) func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(3, 4)) }
                5
    函数的闭包
        Go 函数可以是闭包的
        闭包是一个函数值,它来自函数体的外部的变量引用
         函数可以对这个引用值进行访问和赋值;换句话说这个函数被“绑定”在这个变量上
        例如,函数adder返回一个闭包。每个返回的闭包都被绑定到其各自的sum变量上。
方法和接口
  方法
    Go 没有类。然而,仍然可以在结构体类型上定义方法
    方法接收者 出现在 func 关键字和方法名之间的参数中。
    可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体
    不能对来自其他包的类型或基础类型定义方法。 1
    更真实的还原了方法的本质,同样的功能,我们用普通函数也可以实现
    接收者为指针的方法
        方法可以与命名类型或命名类型的指针关联
        刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上
        两个原因
                首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)
                其次,方法可以修改接收者指向的值
        使用指针接收者的方法,可以改变指针接收者指向的值。因为方法通常会修改他们的接收者的值,所以指针接收者通常比值接收者更常见。
        使用值接收者时,Scale方法在原来Vertex类型的拷贝上进行操作。例如Scale方法想要修改main方法中声明的Vertex值,就必须要使用指针接收者。
  接口
    接口类型是由一组方法定义的集合。
    接口类型的值可以存放实现这些方法的任何值。
  隐式接口
    一个类型通过实现接口的方法来实现一个接口
        意思就是不需要明确的关键字 implements 这种来表明实现关系
    隐式的接口把接口的定义和接口的实现做了彻底分离,接口可以出现在任何包中,不需要任何准备
    类型通过实现那些方法来实现接口。 没有显式声明的必要;所以也就没有关键字“implements“。
    隐式接口解藕了实现接口的包和定义接口的包:互不依赖。
    无需在每一个实现上增加新的接口名称
    包 io 定义了 Reader 和 `Writer`
  Stringers
    一个普遍存在的接口是 fmt 包中定义的 Stringer。
    type Stringer struct { String() string }
    Stringer 是一个可以用字符串描述自己的类型。`fmt`包 (还有许多其他包)使用这个来进行输出
  错误
    Go 程序使用error值来表示错误状态。
    与fmt.Stringer类似,error类型是一个内建接口:
    type error interface{ Error() string }
    通常函数会返回一个error值,调用的它的代码应当判断这个错误是否等于nil, 来进行错误处理。
    i, err := strconv.Atoi("42") if err != nil { fmt.Printf("couldn't convert number: %v\n", err) } fmt.Println("Converted integer:", i)
    error 为 nil 时表示成功;非 nil 的 error 表示错误。
  Readers
    io 包指定了 io.Reader 接口, 它表示从数据流结尾读取。
    Go 标准库包含了这个接口的许多实现, 包括文件、网络连接、压缩、加密等等
    io.Reader 接口有一个 Read 方法: func (T) Read(b []byte) (n int, err error)
    Read 用数据填充指定的字节 slice,并且返回填充的字节数和错误信息。 在遇到数据流结尾时,返回 io.EOF 错误。
  Web 服务器
    包 http 通过任何实现了 http.Handler 的值来响应 HTTP 请求:
    package http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) }
  图片
    Package image 定义了 Image 接口:
    package image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
并发
  goroutine
    goroutine 是由 Go 运行时环境管理的轻量级线程。
    go f(x, y, z)
    开启一个新的 goroutine 执行
    f(x, y, z)
    f , x , y 和 z 是当前 goroutine 中定义的,但是在新的 goroutine 中运行 `f`。
    goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。(在接下来的内容中会涉及到。)
    例子
        package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") say("hello") }
  channel
    channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。
    ch <- v // 将 v 送入 channel ch。 v := <-ch // 从 ch 接收,并且赋值给 v。
    (“箭头”就是数据流的方向。)
    和 map 与 slice 一样,channel 使用前必须创建:
    ch := make(chan int)
    默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
    例子
        package main import "fmt" func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // 将和送入 c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // 从 c 中获取 fmt.Println(x, y, x+y) }
  缓冲 channel
    channel 可以是 _带缓冲的_。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel:
    ch := make(chan int, 100)
    向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。
    修改例子使得缓冲区被填满,然后看看会发生什么。
    例子
        package main import "fmt" func main() { c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) }
  range 和 close
    发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过
    v, ok := <-ch
    之后 ok 会被设置为 `false`。
    循环 `for i := range c` 会不断从 channel 接收值,直到它被关闭。
    注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`。
    例子
        package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } }
  select
    select 语句使得一个 goroutine 在多个通讯操作上等待。
    select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。
    例子
        package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 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 < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
  默认选择
    当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。
    为了非阻塞的发送或者接收,可使用 default 分支:
    select { case i := <-c: // 使用 i default: // 从 c 读取会阻塞 }
    例子
        package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } }
推荐书籍
  go web编程
    谢孟君
  go语言编程
    许式伟
  go并发编程实战(第二版)
    郝琳
推荐go web框架
  beego
    作者 谢孟军
四月 07, 2017. Created by XMind
版权声明:若无特殊注明,本文皆为《hornets's blog 飘落的黄蜂》原创,转载请保留文章出处。
本文链接:gohttp://hornets.pw/?post=29
正文到此结束

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 害羞 调皮 鄙视 示爱 大哭 开心 偷笑 嘘 奸笑 委屈 抱抱 愤怒 思考 日了狗

评论信息框

吃奶的力气提交吐槽中...

赶紧抢沙发咯