分享

Golang学习笔记

 shopnc 2018-12-03
  1. Go安装
    三种安装方式,源码安装、压缩包安装
  2. GOPATH和工作空间
    作用?工作空间的目录结构
  3. GO命令

    1. go build
      • 如果是普通包,就像我们在1.2节中编写的mymath包那样,当你执行go build之后,它不会产生任何文件。如果你需要在$GOPATH/pkg下生成相应的文件,那就得执行go install。
      • 如果是main包,当你执行go build之后,它就会在当前目录下生成一个可执行文件。如果你需要在$GOPATH/bin下生成相应的文件,需要执行go install,或者使用go build -o 路径/a.exe。
      • 你也可以指定编译输出的文件名。例如1.2节中的mathapp应用,我们可以指定go build -o astaxie.exe,默认情况是你的package名(非main包),或者是第一个源文件的文件名(main包)。
        (注:实际上,package名在Go语言规范中指代码中“package”后使用的名称,此名称可以与文件夹名不同。默认生成的可执行文件名是文件夹名。)
      • go build会忽略目录下以“_”或“.”开头的go文件。
      • 如果你的源代码针对不同的操作系统需要不同的处理,那么你可以根据不同的操作系统后缀来命名文件。例如有一个读取数组的程序,它对于不同的操作系统可能有如下几个源文件:
        array_linux.go array_darwin.go array_windows.go array_freebsd.go
        go build的时候会选择性地编译以系统名结尾的文件(Linux、Darwin、Windows、Freebsd)。例如Linux系统下面编译只会选择array_linux.go文件,其它系统命名后缀文件全部忽略。
        参数的介绍
        • -o 指定输出的文件名,可以带上路径,例如 go build -o a/b/c
        • -i 安装相应的包,编译+go install
        • -a 更新全部已经是最新的包的,但是对标准包不适用
        • -n 把需要执行的编译命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的
        • -p n 指定可以并行可运行的编译数目,默认是CPU数目
        • -race 开启编译的时候自动检测数据竞争的情况,目前只支持64位的机器
        • -v 打印出来我们正在编译的包名
        • -work 打印出来编译时候的临时文件夹名称,并且如果已经存在的话就不要删除
        • -x 打印出来执行的命令,其实就是和-n的结果类似,只是这个会执行
        • -ccflags 'arg list' 传递参数给5c, 6c, 8c 调用
        • -compiler name 指定相应的编译器,gccgo还是gc
        • -gccgoflags 'arg list' 传递参数给gccgo编译连接调用
        • -gcflags 'arg list' 传递参数给5g, 6g, 8g 调用
        • -installsuffix suffix 为了和默认的安装包区别开来,采用这个前缀来重新安装那些依赖的包,-race的时候默认已经是-installsuffix race,大家可以通过-n命令来验证
        • -ldflags 'flag list' 传递参数给5l, 6l, 8l 调用
        • -tags 'tag list' 设置在编译的时候可以适配的那些tag,详细的tag限制参考里面的 Build Constraints
    2. go clean 这个命令是用来移除当前源码包和关联源码包里面编译生成的文件。
    3. go fmt 代码格式化
      用go fmt命令,其实是调用了gofmt,而且需要参数-w,否则格式化结果不会写入文件。gofmt -w -l src,可以格式化整个项目。
      所以go fmt是gofmt的上层一个包装的命令,我们想要更多的个性化的格式化可以参考 gofmt
      gofmt的参数介绍
      • -l 显示那些需要格式化的文件
    • -w 把改写后的内容直接写入到文件中,而不是作为结果打印到标准输出。
    • -r 添加形如“a[b:len(a)] -> a[b:]”的重写规则,方便我们做批量替换
    • -s 简化文件中的代码
    • -d 显示格式化前后的diff而不是写入文件,默认是false
    • -e 打印所有的语法错误到标准输出。如果不使用此标记,则只会打印不同行的前10个错误。
    • -cpuprofile 支持调试模式,写入相应的cpufile到指定的文件
    1. go get 这个命令是用来动态获取远程代码包的
      这个命令在内部实际上分成了两步操作:第一步是下载源码包,第二步是执行go install。下载源码包的go工具会自动根据不同的域名调用不同的源码工具,
      参数 -d 之只下载不安装
    2. go install 这个命令在内部实际上分成了两步操作:第一步是生成结果文件(可执行文件或者.a包),第二步会把编译好的结果移到$GOPATH/pkg或者$GOPATH/bin。
    3. go test 执行这个命令,会自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件。
    4. go tool 下面下载聚集了很多命令
    5. go generate 这个命令是从Go1.4开始才设计的,用于在编译前自动化生成某类代码。
    6. godoc 在Go1.2版本之前还支持go doc命令,但是之后全部已到了godoc这个命令下,需要这样安装go get
    7. go version 查看go当前的版本
      go env 查看当前go的环境变量
      go list 列出当前全部安装的package
      go run 编译并运行Go程序

  4. 开发工具

    • Sublime Text
    • PHPStorm
    • LiteIDE

Go语言基础

Go使用package(和Python的模块类似)来组织代码。main.main()函数(这个函数位于主包)是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者),所以它天生支持多语言。

基础知识

  1. 定义变量
    定义一个名称为“variableName”,类型为”type”的变量 var variableName type
    定义三个类型都是“type”的变量var vname1, vname2, vname3 type
    初始化“variableName”的变量为“value”值,类型是“type” var variableName type = value
    定义三个类型都是”type”的变量,并且分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 var vname1, vname2, vname3 type= v1, v2, v3
    定义三个变量,它们分别初始化为相应的值 vname1为v1,vname2为v2,vname3为v3 编译器会根据初始化的值自动推导出相应的类型 vname1, vname2, vname3 := v1, v2, v3

    流程和函数

    流程控制

  • if Go里面if条件判断语句中不需要括号,如下代码所示

    1
    2
    3
    4
    5
    if x > 10 {
    fmt.Println("x is greater than 10")
    } else {
    fmt.Println("x is less than 10")
    }
  • goto 用goto跳转到必须在当前函数内定义的标签。

    1
    2
    3
    4
    5
    6
    7
    func myFunc() {
    i := 0
    Here: //这行的第一个词,以冒号结束作为标签
    println(i)
    i++
    goto Here //跳转到Here去
    }
  • for Go里面最强大的一个控制逻辑就是for,它即可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下:

    1
    2
    3
    for expression1; expression2; expression3 {
    //...
    }

expression1、expression2和expression3都是表达式,其中expression1和expression3是变量声明或者函数调用返回值之类的,expression2是用来条件判断,expression1在循环开始之前调用,expression3在每轮循环结束之时调用。
通常用法

1
2
3
4
5
6
7
8
9
10
11
package main
import "fmt"
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("sum is equal to ", sum)
}
// 输出:sum is equal to 45

循环

1
2
3
4
sum := 1
for ; sum < 1000; {
sum += sum
}

简写

1
2
3
4
sum := 1
for sum < 1000 {
sum += sum
}

for 中有break 、continue

1
2
3
4
5
6
7
8
for index := 10; index>0; index-- {
if index == 5{
break // 或者continue
}
fmt.Println(index)
}
// break打印出来10、9、8、7、6
// continue打印出来10、9、8、7、6、4、3、2、1

for配合range可以用于读取slice和map的数据:

1
2
3
4
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}

由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_来丢弃不需要的返回值 例如

1
2
3
for _, v := range map{
fmt.Println("map's val:", v)
}

  • switch if 不够用使用switch
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    switch sExpr {
    case expr1:
    some instructions
    case expr2:
    some other instructions
    case expr3:
    some other instructions
    default:
    other code
    }

another

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
integer := 6
switch integer {
case 4:
fmt.Println("The integer was <= 4")
fallthrough
case 5:
fmt.Println("The integer was <= 5")
fallthrough
case 6:
fmt.Println("The integer was <= 6")
fallthrough
case 7:
fmt.Println("The integer was <= 7")
fallthrough
case 8:
fmt.Println("The integer was <= 8")
fallthrough
default:
fmt.Println("default case")
}

函数

函数是Go里面的核心设计,它通过关键字func来声明,它的格式如下:

1
2
3
4
5
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}

关键字func用来声明一个函数funcName
函数可以有一个或者多个参数,每个参数后面带有类型,通过,分隔
函数可以返回多个值
上面返回值声明了两个变量output1和output2,如果你不想声明也可以,直接就两个类型
如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号
如果没有返回值,那么就直接省略最后的返回信息
如果有返回值, 那么必须在函数的外层添加return语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
// 返回a、b中最大值.
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
x := 3
y := 4
z := 5
max_xy := max(x, y) //调用函数max(x, y)
max_xz := max(x, z) //调用函数max(x, z)
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
}

多个返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
//返回 A+B 和 A*B
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
func main() {
x := 3
y := 4
xPLUSy, xTIMESy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}

变参 func myfunc(arg …int) {}
传值与传指针

当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。
传指针使得多个函数能操作同一个对象。
传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)

defer
Panic和Recover

struct类型

Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型person代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之struct。如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type person struct {
name string
age int
}
type person struct {
name string
age int
}
var P person // P现在就是person类型的变量了
P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
P.age = 25 // 赋值"25"给变量P的age属性
fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.

除了上面这种P的声明使用之外,还有另外几种声明使用方式:

  • 1.按照顺序提供初始化值
    P := person{“Tom”, 25}
  • 2.通过field:value的方式初始化,这样可以任意顺序
    P := person{age:24, name:”Tom”}
  • 3.当然也可以通过new函数分配一个指针,此处P的类型为*person
    P := new(person)

面向对象

带有接收者的函数,我们称为method
计算面积的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//原始的函数
package main
import "fmt"
type Rectangle struct {
width, height float64
}
func area(r Rectangle) float64 {
return r.width*r.height
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
fmt.Println("Area of r1 is: ", area(r1))
fmt.Println("Area of r2 is: ", area(r2))
}
//接受者
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继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
import "fmt"
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)
}
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

Go语言里面设计最精妙的应该算interface,它让面向对象,内容组织实现非常的方便
简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为。

并发

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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执行
    }

    runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine

  • channels

    goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:

    1
    2
    3
    4
    5
    6
    ci := make(chan int)
    cs := make(chan string)
    cf := make(chan interface{})
    //channel通过操作符<-来接收和发送数据
    ch <- v // 发送v到channel ch.
    v := <-ch // 从ch中接收数据,并赋值给v

channel例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to 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 // receive from c
fmt.Println(x, y, x + y)
}

Buffered Channels

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import "fmt"
func main() {
c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
//修改为1报如下的错误:
//fatal error: all goroutines are asleep - deadlock!

web基础

  • web工作原理
  • http包建立Web服务器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package main
    import (
    "fmt"
    "net/http"
    "strings"
    "log"
    )
    func sayhelloName(w http.ResponseWriter, r *http.Request) {
    r.ParseForm() //解析参数,默认是不会解析的
    fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
    fmt.Println("path", r.URL.Path)
    fmt.Println("scheme", r.URL.Scheme)
    fmt.Println(r.Form["url_long"])
    for k, v := range r.Form {
    fmt.Println("key:", k)
    fmt.Println("val:", strings.Join(v, ""))
    }
    fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
    }
    func main() {
    http.HandleFunc("/", sayhelloName) //设置访问的路由
    err := http.ListenAndServe(":9090", nil) //设置监听的端口
    if err != nil {
    log.Fatal("ListenAndServe: ", err)
    }
    }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多