Go 编程入门

一、程序结构

1、第一行非注释行用 package xxx  定义包名。

2、import 用于导入程序使用的包

3、文件中的 首字母大写的标识符是对外暴露导出的,首字母小写的标识符为只对内有效的。

4、变量的声明格式为

1、var identifier1, identifier1, ... type
2、var identifier = 1   根据值决定类型
3、identifier := value  省略 var 直接用 := 申明变量并初始化

5、iota 特殊的变量,可以理解为能被编译器修改的常量,每次遇到const关键字则重置为0,const中每新增一行常量申明将使用iota计数一次。

const (
    a = iota
    b = iota
    c = iota
)
或
const (
    a = iota
    b
    c
)
上面两者都是等价的, a = 0, b = 1, c = 2

6、switch 接口  case 后自带break,只要匹配成功则不会执行其他case,如果需要执行其他的可以使用 fallthrough

switch var {
    case val1:
        ...
    case val2:
        ...
    case val3:
        ...
        fallthrough  //会执行下一个case
    case val4:
        ...
    default:
        ...
}

二、函数

1、函数申明格式

func 函数名字(形式参数列表)(返回值列表){
    函数体实现
}

2、Go 函数可以支持多返回值,并且可以为返回值命名,但是不支持混合使用,即要么返回值都命名要不都不命名。

func swap(a int, b int)(x int, y int){// 此处返回两个值,x,y
    x = b;
    y = a;
}

3、Go 中函数参数传递除了 指针、切片、map 等引用型对象是引用传递否则都是按值传递。

4、Go 支持可变参数格式如下

func myfunc(args ...int){
    for _, arg := range args {
        fmt.Println(arg)
    }
}
//可变参数以 ...type 方式定义,实际上 args 就是一个切片,但是这种方式避免了调用者手动将多个参数组装成切片。
//另外如果需要传递不同类型参数 可以将类型定义为  ...interface{}
//如果要讲可变参数再次传递 直接 args ...
// example
func test(args ...interface{}){
    myfunc(args...)
}

5、Go 中支持 defer (延迟执行),通过defer申明的语句将在函数结束后多个defer语句按后进先出的顺序执行

func main() {
    fmt.Println("defer begin")
    // 将defer放入延迟调用栈
    defer fmt.Println(1)
    defer fmt.Println(2)
    // 最后一个放入, 位于栈顶, 最先调用
    defer fmt.Println(3)
    fmt.Println("defer end")
}
// 输出
// defer begin
// defer end
// 3
// 2
// 1

6、Go 用异常处理使用 panic 函数触发程序宕机,可以使用 recover 恢复 panic 触发的宕机。panic 会打印宕机的堆栈信息

package main
import (
    "fmt"
    "runtime"
)
// 崩溃时需要传递的上下文信息
type panicContext struct {
    function string // 所在函数
}
// 保护方式允许一个函数
func ProtectRun(entry func()) {
    // 延迟处理的函数
    defer func() {
        // 发生宕机时,获取panic传递的上下文并打印
        err := recover()
        switch err.(type) {
        case runtime.Error: // 运行时错误
            fmt.Println("runtime error:", err)
        default: // 非运行时错误
            fmt.Println("error:", err)
        }
    }()
    entry()
}
func main() {
    fmt.Println("运行前")
    // 允许一段手动触发的错误
    ProtectRun(func() {
        fmt.Println("手动宕机前")
        // 使用panic传递上下文
        panic(&panicContext{
            "手动触发panic",
        })
        fmt.Println("手动宕机后")
    })
    // 故意造成空指针访问错误
    ProtectRun(func() {
        fmt.Println("赋值宕机前")
        var a *int
        *a = 1
        fmt.Println("赋值宕机后")
    })
    fmt.Println("运行后")
}
// ====== 代码输出结果如下:
// 运行前
// 手动宕机前
// error: &{手动触发panic}
// 赋值宕机前
// runtime error: runtime error: invalid memory address or nil pointer dereference
// 运行后

7、Go 提供了Test 功能做单元测试,使用规则如下

  • 测试用例文件不会参与正常源码的编译,不会被包含到可执行文件中;
  • 测试用例的文件名必须以_test.go结尾;
  • 需要使用 import 导入 testing 包;
  • 测试函数的名称要以TestBenchmark开头,后面可以跟任意字母组成的字符串,但第一个字母必须大写,例如 TestAbc(),一个测试用例文件中可以包含多个测试函数;
  • 单元测试则以(t *testing.T)作为参数,性能测试以(t *testing.B)做为参数;
  • 测试用例文件使用go test命令来执行,源码中不需要 main() 函数作为入口,所有以_test.go结尾的源码文件内以Test开头的函数都会自动执行。
// demo.go
package demo
// 根据长宽获取面积
func GetArea(weight int, height int) int {
    return weight * height
}

// demo_test.go
package demo
import "testing"
func TestGetArea(t *testing.T) {
    area := GetArea(40, 50)
    if area != 2000 {
        t.Error("测试失败")
    }
}
func BenchmarkGetArea(t *testing.B) {
    for i := 0; i < t.N; i++ {
        GetArea(40, 50)
    }
}

// go test -v 单元测试
// go test -bench="." 性能测试
// go test -cover 覆盖测试

三、结构体

1、结构体定义格式

type 类型名 struct{
    字段1 字段1类型
    字段2 字段2类型
    ...
}

2、结构体实例化方式

1、 var ins T  // T 为结构体类型
2、 ins := new(T) // ins 为 *T 指针类型
3、ins := &T{}   // go 中对结构体进行取地址操作,视为进行一次 new 操作,所以 ins 还是指针类型

3、结构体初始化

1、键值对初始化
ins := 结构体类型名{
    字段1 : 字段1值
    字段2 : 字段2值
}

2、使用多个值的列表
ins := 结构体类型名{
    字段1值,
    字段2值,
}


4、Go 中是没有构造函数的概念,所以一般使用命名函数初始化结构体的过程当成构造函数

type Cat struct {
    Color string
    Name  string
}
func NewCatByName(name string) *Cat {
    return &Cat{
        Name: name,
    }
}
func NewCatByColor(color string) *Cat {
    return &Cat{
        Color: color,
    }
}

5、结构体方法

func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) {
    函数体
}
type Bag struct {
    items []int
}
func (b *Bag) Insert(itemid int) {
    b.items = append(b.items, itemid)
}

接收器分为指针类型和非指针类型,区别在于,非指针类型的只能读取结构体值但无法修改结构体的值。

type Point struct {
    X int
    Y int
}
// 非指针接收器的加方法
func (p Point) Add(other Point) Point {
    // 成员值与参数相加后返回新的结构
    return Point{p.X + other.X, p.Y + other.Y}
}

6、结构体内嵌

package main
import "fmt"
type innerS struct {
    in1 int
    in2 int
}
type outerS struct {
    b int
    c float32
    int // anonymous field
    innerS //anonymous field
}
func main() {
    outer := new(outerS)
    outer.b = 6
    outer.c = 7.5
    outer.int = 60
    outer.in1 = 5
    outer.in2 = 10
    fmt.Printf("outer.b is: %d\n", outer.b)
    fmt.Printf("outer.c is: %f\n", outer.c)
    fmt.Printf("outer.int is: %d\n", outer.int)
    fmt.Printf("outer.in1 is: %d\n", outer.in1)
    fmt.Printf("outer.in2 is: %d\n", outer.in2)
    // 使用结构体字面量
    outer2 := outerS{6, 7.5, 60, innerS{5, 10}}
    fmt.Printf("outer2 is:", outer2)
}

7、Go 自带垃圾回收GC机制,可以给对象指针设置在对象被gc时的回调函数。SetFinalizer

func SetFinalizer(x, f interface{})

四、接口

1、接口的申明方式

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

2、接口类型之间的转换

t,ok := i.(T) //i 代表接口变量,T 代表转换的目标类型,t 代表转换后的变量, ok 代表是否转换成功

五、包 package

1、Go中使用包管理代码,每个源文件都必须属于一个包,申明文件属于哪个包用

package packageName

2、导入包

1、普通方式
import "包的路径"  //可以为相对路径也可以是绝对路径,绝对路径以 GOPATH/src/ 开始

//还可以一次导入多个包
import (
    "path/package1"
    "path/package2"
)

2、
//还可以给导入包取别名
import othername "packagename1"

3、
//还可以直接将包的内容合并到当前程序中,这样包中的内容就不需要通过包名使用了,有点像c++ using namespace std;这种写法
import . "fmt"

3、匿名引用格式,只会执行包的初始化的init函数,不会使用包内部的数据。
import _ "fmt"

3、包内标识符如果希望外部能访问则需要将标识符首字母大写。

4、包可以定义init 函数,他会在 main函数执行前被调用。

五、并发

1、goroutine  直接 go func() 就能启动一个并行的携程

2、通道  channel

var 通道变量 chanel 动刀类型 //申明
通道实例 := make( chan 数据类型) //创建通道

往通道发送数据的方式为:
通道变量 <- 值  //把数据往通道发送时,如果一直没有接受则发送操作会持续阻塞。

接收通道数据的方式为
1、阻塞接收   data := <-ch
2、非阻塞接收  data, ok := <-ch   
3、接收任意数据,忽略接收数据   <-ch
4、循环接收 for data := range ch {}

2.1 单向通道,单向通道是一种对通道使用上的一种限制,当通道传递给函数时我们通过申明单向通道来限制他的使用方式

var 通道实例 chan<- 元素类型    // 只能发送通道
var 通道实例 <-chan 元素类型    // 只能接收通道

2.2 缓冲通道,创建时如果不指定缓冲大小,则默认为无缓冲的通道,则需要发送和接收方都阻塞等待。

通道实例 := make(chan 通道类型, 缓冲大小) //缓冲通道创建方法

缓冲通道的特性为:

  • 带缓冲通道被填满时,尝试再次发送数据时发生阻塞。
  • 带缓冲通道为空时,尝试接收数据时发生阻塞。

2.3 通道超时机制

select {
    case <-chan1:
    // 如果chan1成功读到数据,则进行该case处理语句
    case chan2 <- 1:
    // 如果成功向chan2写入数据,则进行该case处理语句
    default:
    // 如果上面都没有成功,则进入default处理流程
}
原理是select从上到下评估每个发送和接收语句,如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有如下两种可能的情况:
如果给出了 default 语句,那么就会执行 default 语句,同时程序的执行会从 select 语句后的语句中恢复;
如果没有 default 语句,那么 select 语句将被阻塞,直到至少有一个通信可以进行下去。
package main
import (
    "fmt"
    "time"
)
func main() {
    ch := make(chan int)
    quit := make(chan bool)
    //新开一个协程
    go func() {
        for {
            select {
            case num := <-ch:
                fmt.Println("num = ", num)
            case <-time.After(3 * time.Second):  //time.After 会返回一个通道 3s 后收到数据
                fmt.Println("超时")
                quit <- true
            }
        }
    }() //别忘了()
    for i := 0; i < 5; i++ {
        ch <- i
        time.Sleep(time.Second)
    }
    <-quit
    fmt.Println("程序结束")
}

六、反射  reflect

1、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值