Go语言系列二

字符串

与其它主要编程语言的差异

  • string是数据类型,不是引用或指针类型
  • string是只读的byte slice,len函数可以是它包含的byte数
  • string的byte数组可以存放任何数据

Unicode UTF8

  • Unicode是一种字符集(code point)
  • UTF8是unicode的存储实现(转换为字节序列的规则)

编码与存储

字符 "中"
Unicode 0x4E2D
UTF-8 0xE4B8AD
string/[]byte [0xE4, 0xB8, 0xAD]

常用字符串函数

函数:一等公民

与其它主要编程语言的差异

  • 1> 可以有多个返回值
  • 2> 所有参数都是值传递: slice, map, channel 会有传引用的错觉
  • 3> 函数可以作为变量的值
  • 4> 函数可以作为参数和返回值

学习函数式编程

  • book <计算机程序的构造和解释>

函数: 可变参数及defer

可变参数

func sum(ops ...int) int {
    s := 0
    for _, op := range ops {
        s += op
    }
    return s
}

defer 函数

func TestDefer(t *testing.T) {
    defer func() {
        t.Log("Clear resources")
    }()
    t.Log("Started")
    panic("Fatal error")	//defer仍会执行
}

封装数据和行为

结构体定义

type Employee struct {
    Id string
    Name string
    Age int
}

实例创建及初始化

e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
e2 := new(Employee)		//注意这里返回的引用/指针,相当于 e := &Employee{}
e2.Id = "2"				//与其它主要编程语言的差异:通过实例的指针访问成员不需要使用->
e2.Age = 22
e2.Name = "Rose"

行为(方法)定义

与其它主要编程语言的差异

type Employee struct {
    Id string
    Name string
    Age int
}

//第一种定义方式在实例对应方法被调用时,实例的成员会进行值复制
func (e Employee) String() string {
    return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

//通常情况下为避免内存拷贝我们使用第二种定义方式
func (e *Employee) String() string {
    return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age)
}

Duck Type 式接口实现

//接口定义
type Programmer interface {
    WriteHelloWorld() Code
}

//接口实现
type GoProgrammer struct {
    
}

func (p *GoProgrammer) WriteHelloWorld() Code {
    return "fmt.Println(\"Hello World!\")"
}

Go 接口

与其它主要编程语言的差异

  • 1> 接口为非入侵性, 实现不依赖与接口定义
  • 2> 所以接口的定义可以包含在接口使用者包内

接口变量

自定义类型

1. type IntConvertionFn fun(n int)	int
2. type MyPoint int

多态

空接口与断言

  • 1> 空接口可以表示任何类型
  • 2> 通过断言来将空接口转换为制定类型
v, ok := p.(int)	//ok=true 时为转换成功

Go接口最佳实践

倾向于使用小的接口定义,很多接口只包含一个方法

type Reader interface {
    Read(p []byte)(n int, err error)
}

type Writer interface {
    Write(p []byte)(n int, err error)
}

较大的接口定义,可以由多个小接口定义组合而成

type ReadWriter interface {
    Reader
    Writer
}

只依赖于必要功能的最小接口

func StoreData(reader Reader) error {
    ...
}

Go的错误机制

与其它主要编程语言的差异

  • 1> 没有异常机制
  • 2> error类型实现了error接口
  • 3> 可以通过errors.New来快速创建错误实例
type error interface {
    Error() string
}

errors.New("n must be in the range[0, 11]")

最佳实践

定义不同的错误变量,以便于判断错误类型

var LessThanTwoError error = errors.New("n must be greater than 2")
var GreaterThanHundredError error = errors.New("n must be less than 100")
...
func TestGetFibonacci(t *testing.T) {
    var list []int
    list, err := GetFibonacci(-10)
    if err == LessThanTwoError {
        t.Error("Need a larger number")
    }
    if err == GreaterThanHundredError {
        t.Error("Nedd a larger number")
    }
}

及早失败,避免嵌套

panic

  • 1> panic 用于不可以恢复的错误
  • 2> panic退出前会执行defer指定的内容

**panic vs os.Exit **

  • 1> os.Exit退出时不会调用defer指定的函数
  • 2> os.Exit退出时不输出当前调用栈信息

recover

/* !< java */
try {
    ...
} catch(Throwable t) {
    
}
/* !< C++ */
try {
    ...
} catch(...) {
    
}
/* !< golang */
defer func() {
    if err := recover(); err != nil {
        //恢复错误
    }
}()

最常见的"错误恢复"

defer func {
    if err := recover(); err != nil {
        log.Error("recoverd panic", err)
    }
}()
//这种错误修复非常危险

当心! recover成为恶魔

  • 1> 形成僵尸服务进程,导致health check失效
  • 2> "Let it Crash!"往往是我们恢复不确定性错误的最好方法
posted @ 2019-09-28 10:59  电院院长  阅读(195)  评论(0编辑  收藏  举报