您的位置:首页 > 编程语言 > Go语言

Go lang学习

2015-12-25 13:27 591 查看
1.在 Go 中,首字母大写的名称是被导出的。

2.函数参数类型在变量名 _之后_

func add(x int, y int) int {

return x + y

}

3.当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。

func add(x, y int) int {

return x + y

}

4.函数可以返回任意数量的返回值。

func swap(x, y string) (string, string) {

return y, x

}

func main() {

a, b := swap("hello", "world")

fmt.Println(a, b)

}

5.命名返回值

Go 的返回值可以被命名,并且像变量那样使用。

返回值的名称应当具有一定的意义,可以作为文档使用。

没有参数的 return 语句返回结果的当前值。也就是`直接`返回。

func split(sum int) (x, y int) {

x = sum * 4 / 9

y = sum - x

return

}

6.变量定义: var 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。

var c, python, java bool

func main() {

var i int

fmt.Println(i, c, python, java)

}

7.初始化变量

变量定义可以包含初始值,每个变量对应一个。

var i, j int = 1, 2

变量在定义时没有明确的初始化时会赋值为_零值_(数值类型为 `0`,布尔类型为 `false`,字符串为 `""`(空字符串))

8.函数内短声明变量

在函数中,`:=` 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。

func main() {

var i, j int = 1, 2

k := 3

c, python, java := true, false, "no!"

fmt.Println(i, j, k, c, python, java)

}

9.基本类型

bool

string

int int8 int16 int32 int64

uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 的别名

rune // int32 的别名

// 代表一个Unicode码

float32 float64

complex64 complex128

10.类型转换: 需要显式转换

var i int = 42

var f float64 = float64(i)

11.常量

常量的定义与变量类似,只不过使用 const 关键字。

常量可以是字符、字符串、布尔或数字类型的值。

常量不能使用 := 语法定义。

const World = "世界"

const Truth = true

12.循环结构

Go 只有一种循环结构——`for` 循环。基本的 for 循环除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中做的一样,而 `{ }` 是必须的。跟 C 或者 Java 中一样,可以让前置、后置语句为空。

func main() {

sum := 0

for i := 0; i < 10; i++ {

sum += i

}

fmt.Println(sum)

}

func main() {

sum := 1

for sum < 1000 {

sum += sum

}

fmt.Println(sum)

}

死循环

for {

}

13. if: if 语句除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中的一样,而 `{ }` 是必须的。

func sqrt(x float64) string {

if x < 0 {

return sqrt(-x) + "i"

}

return fmt.Sprint(math.Sqrt(x))

}

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

}

14.switch: 除非以 fallthrough 语句结束,否则分支会自动终止。switch 的条件从上到下的执行,当匹配成功的时候停止。

func main() {

fmt.Print("Go runs on ")

switch os := runtime.GOOS; os {

case "darwin":

fmt.Println("OS X.")

case "linux":

fmt.Println("Linux.")

default:

// freebsd, openbsd,

// plan9, windows...

fmt.Printf("%s.", os)

}

}

没有条件的 switch: 同 `switch true` 一样。这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。

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.")

}

}

15.defer: defer 语句会延迟函数的执行直到上层函数返回。

func main() {

defer fmt.Println("world")

fmt.Println("hello")

}

输出: hello

world

16.指针 : Go 具有指针。 指针保存了变量的内存地址。

类型 *T 是指向类型 T 的值的指针。其零值是 `nil`。

var p *int

& 符号会生成一个指向其作用对象的指针。

i := 42

p = &i

* 符号表示指针指向的底层的值。

fmt.Println(*p) // 通过指针 p 读取 i

*p = 21 // 通过指针 p 设置 i

与 C 不同,Go 没有指针运算。

func main() {

i, j := 42, 2701

p := &i // point to i

fmt.Println(*p) // read i through the pointer

*p = 21 // set i through the pointer

fmt.Println(i) // see the new value of i

p = &j // point to j

*p = *p / 37 // divide j through the pointer

fmt.Println(j) // see the new value of j

}

17.结构体: 结构体字段使用点号来访问, 也可以通过结构体指针来访问

type Vertex struct {

X int

Y int

}

func main() {

v := Vertex{1, 2}

v.X = 4

fmt.Println(v.X)

}

func main() {

v := Vertex{1, 2}

p := &v

p.X = 1e9

fmt.Println(v)

}

18.数组: 类型
T 是一个有 n 个类型为 T 的值的数组。

var a [10]int 定义变量 a 是一个有十个整数的数组。

func main() {

var a [2]string

a[0] = "Hello"

a[1] = "World"

fmt.Println(a[0], a[1])

fmt.Println(a)

}

19.slice: 一个 slice 会指向一个序列的值,并且包含了长度信息。[]T 是一个元素类型为 T 的 slice。

func main() {

p := []int{2, 3, 5, 7, 11, 13}

fmt.Println("p ==", p)

for i := 0; i < len(p); i++ {

fmt.Printf("p[%d] == %d\n", i, p[i])

}

}

对 slice 切片: slice 可以重新切片,创建一个新的 slice 值指向相同的数组。

s[lo:hi]表示从 lo 到 hi-1 的 slice 元素,含两端

省略下标代表从 0 开始, 省略上标代表到 len(s) 结束

构造 slice: slice 由函数 make 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组:a := make([]int, 5) // len(a)=5

slice 的零值是 `nil`。

func main() {

var z []int

fmt.Println(z, len(z), cap(z))

if z == nil {

fmt.Println("nil!")

}

}

向 slice 添加元素: 内建函数 `append`. append 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice(类似于STL的vector push_back)。

var a []int

printSlice("a", a)

// append works on nil slices.

a = append(a, 0)

for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

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)

}

}

20:map

type Vertex struct {

Lat, Long float64

}

var m map[string]Vertex

func main() {

m = make(map[string]Vertex)

m["Bell Labs"] = Vertex{

40.68433, -74.39967,

}

fmt.Println(m["Bell Labs"])

}

在 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 的元素类型的零值

21.函数的闭包: 函数别名

Go 函数可以是闭包的。闭包是一个函数值,它来自函数体的外部的变量引用。 函数可以对这个引用值进行访问和赋值;换句话说这个函数被“绑定”在这个变量上。

例如,函数 adder 返回一个闭包。每个闭包都被绑定到其各自的 sum 变量上。

func adder() func(int) int {

sum := 0

return func(x int) int {

sum += x

return sum

}

}

func main() {

pos, neg := adder(), adder()

for i := 0; i < 10; i++ {

fmt.Println(

pos(i),

neg(-2*i),

)

}

}

22.类: Go 没有类。然而,仍然可以在结构体类型上定义方法。

type Vertex struct {

X, Y float64

}

func (v *Vertex) Abs() float64 {

return math.Sqrt(v.X*v.X + v.Y*v.Y)

}

//*Vertex 指针类型:避免值拷贝和修改接收者指向的值

func (v *Vertex) Scale(f float64) {

v.X = v.X * f

v.Y = v.Y * f

}

func main() {

v := &Vertex{3, 4}

fmt.Println(v.Abs())

}

你可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。但是,不能对来自其他包的类型或基础类型定义方法。

type MyFloat float64

func (f MyFloat) Abs() float64 {

if f < 0 {

return float64(-f)

}

return float64(f)

}

func main() {

f := MyFloat(-math.Sqrt2)

fmt.Println(f.Abs())

}

接口: 接口类型是由一组方法定义的集合

type Abser interface {

Abs() float64

}

type MyFloat float64

func (f MyFloat) Abs() float64 {

if f < 0 {

return float64(-f)

}

return float64(f)

}

23.错误: 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 表示错误。

24.Web 服务器: 包 http 通过任何实现了 http.Handler 的值来响应 HTTP 请求:

package http

type Handler interface {

ServeHTTP(w ResponseWriter, r *Request)

}

import "net/http"

func main() {

http.HandleFunc("/", hello)

http.ListenAndServe(":8080", nil)

}

func hello(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("hello!"))

}

25.goroutine: goroutine 是由 Go 运行时环境管理的轻量级线程。 go funcname() 开启一个新的 goroutine 执行

每一个goroutine都会默认在heap当中分配一个4k的内存块来作为该goroutine的stack。当goroutine stack满时,golang会再分配一个块内存来补充,从而形成一个无限大的stack。

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

}

26.channel:

channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。

默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。

ch <- v // 将 v 送入 channel ch。

v := <-ch // 从 ch 接收,并且赋值给 v。

(“箭头”就是数据流的方向。)

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) //和 map 与 slice 一样,channel 使用前必须创建make:

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 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。

27. channel range 和 close:

发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过

v, ok := <-ch

之后 ok 会被设置为 `false`。

循环 `for i := range c` 会不断从 channel 接收值,直到它被关闭。

注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`。

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)

}

}

28. select: select 语句使得一个 goroutine 在多个通讯操作上等待。

select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。

当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。为了非阻塞的发送或者接收,可使用 default 分支:

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)

}

29.Web 爬虫: http://go-tour-zh.appspot.com/concurrency/9
30. golang的同步: sync.WaitGroup只有3个方法,Add(),Done(),Wait()。其中Done()是Add(-1)的别名。简单的来说,使用Add()添加计数,Done()减掉一个计数,计数不为0,
阻塞Wait()的运行。

package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
}
for i := 0; i < 100; i++ {
go done(&wg)
}
wg.Wait()
fmt.Println("exit")
}
func add(wg sync.WaitGroup) {
wg.Add(1)
}
func done(wg *sync.WaitGroup) {
wg.Done()
}


如何编写 Go 代码: https://golang.org/doc/code.html
Go标准库包手册: http://golang.org/pkg/
Go入门指南: https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md
Network programming with Go: https://jan.newmarch.name/go/
Writing Web Applications: https://golang.org/doc/articles/wiki/
Go Web 编程: https://github.com/astaxie/build-web-application-with-golang/tree/master/zh
Go 语言规范: https://golang.org/ref/spec


使用Golang 搭建http web服务器: /article/4687494.html


使用Golang实现简单Ping过程: http://www.tuicool.com/articles/E7rArq

使用Golang提供的net包中的相关函数可以快速构造一个IP包并自定义其中一些关键参数,而不需要再自己手动填充IP报 文。

使用 encoding/binary 包可以轻松获取结构体struct的内存数据并且可以规定字节序(这里要用网络字节序BigEndian),而不需要自己去转换字节序。

使用 container/list包,方便进行结果统计

使用time包实现耗时和超时处理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: