《Go入门指南》笔记
Map 删除:delete(map1, "Washington")
make()
container:list/ring
runtime
reflectruntime
如何强制使用工厂方法?
Go 语言不支持面向对象编程语言中那样的构造子方法,但是可以很容易的在 Go 中实现 “构造子工厂” 方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 new 或 New 开头。
将对象声明称私有(即小写开头),调用Newxx()方法。
// 注意为私有方法
type matrix struct {
...
}
func NewMatrix(params) *matrix {
m := new(matrix) // 初始化 m
return m
}
匿名字段和内嵌结构体?
当两个字段拥有相同的名字(可能是继承来的名字)时该怎么办呢?
- 外层名字会覆盖内层名字(但是两者的内存空间都保留),这提供了一种重载字段或方法的方式;
- 如果相同的名字在同一级别出现了两次,如果这个名字被程序使用了,将会引发一个错误(不使用没关系)。没有办法来解决这种问题引起的二义性,必须由程序员自己修正。
在一个对象 obj 被从内存移除前执行一些特殊操作?
如果需要在一个对象 obj 被从内存移除前执行一些特殊操作,比如写到日志文件中,可以通过如下方式调用函数来实现:
runtime.SetFinalizer(obj, func(obj *typeObj))
func(obj *typeObj)typeObjobjfunc
SetFinalizer
接口命名约定?
[e]rPrinterReaderWriterLoggerConvertererRecoverableableI.NETJava
一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。
接口类型判断?
Type-switch
func classifier(items ...interface{}) {
for i, x := range items {
switch x.(type) {
case bool:
fmt.Printf("Param #%d is a bool\n", i)
case float64:
fmt.Printf("Param #%d is a float64\n", i)
case int, int64:
fmt.Printf("Param #%d is a int\n", i)
case nil:
fmt.Printf("Param #%d is a nil\n", i)
case string:
fmt.Printf("Param #%d is a string\n", i)
default:
fmt.Printf("Param #%d is unknown\n", i)
}
}
}
如何使用接口来获取通用型?
func Sort(data Sorter) {
for pass := 1; pass < data.Len(); pass++ {
for i := 0;i < data.Len() - pass; i++ {
if data.Less(i+1, i) {
data.Swap(i, i + 1)
}
}
}
}
type Sorter interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
intSorter
type IntArray []int
func (p IntArray) Len() int { return len(p) }
func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
a := sort.IntArray(data) //conversion to type IntArray from package sort
sort.Sort(a)
切片注意事项?
复制数据切片至空接口切片,必须要显式复制
切片的底层指向一个数组,该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候,底层的数组内存才会被释放,这种特性有时会导致程序占用多余的内存。
Map注意事项?
make()
判断key是否存在 : val1, isPresent = map1[key1]
map中删除key: delete(map1, key1)
Map排序:如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包),然后可以使用切片的 for-range 方法打印出所有的 key 和 value
方法相关小技巧?
在 Go 中,类型就是类(数据和关联的方法)。Go 不知道类似面向对象语言的类继承的概念。继承有两个好处:代码复用和多态。
在 Go 中,代码复用通过组合和委托实现,多态通过接口的使用来实现:有时这也叫 组件编程(Component Programming)。
许多开发者说相比于类继承,Go 的接口提供了更强大、却更简单的多态行为。
读写数据?
如何读写一个文件?
从命令行读入数据?
文件拷贝 io.Copy()
从命令行读取参数 : os.Args[1:]
os.OpenFile outputWriter := bufio.NewWriter(outputFile)
错误处理?
永远不要忽略错误,否则可能会导致程序崩溃!!
创建error: fmt.Errorf() / errors.New()
runtime.ErrorRuntimeError()
panic 会导致栈被展开直到 defer 修饰的 recover () 被调用或者程序中止。
package main
import (
"fmt"
)
func badCall() {
panic("bad end")
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\r\n", e)
}
}()
badCall()
fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
}
func main() {
fmt.Printf("Calling test\r\n")
test()
fmt.Printf("Test completed\r\n")
}
Calling test
Panicing bad end
Test completed
自定义包中的错误处理和 panicking?
这是所有自定义包实现者应该遵守的最佳实践:
1)在包内部,总是应该从 panic 中 recover:不允许显式的超出包范围的 panic ()
2)向包的调用者返回错误值(而不是 panic)。
在包内部,特别是在非导出函数中有很深层次的嵌套调用时,对主调函数来说用 panic 来表示应该被翻译成错误的错误场景是很有用的(并且提高了代码可读性)。
包内panic;包外error
// parse.go
package parse
import (
"fmt"
"strings"
"strconv"
)
// A ParseError indicates an error in converting a word into an integer.
type ParseError struct {
Index int // The index into the space-separated list of words.
Word string // The word that generated the parse error.
Err error // The raw error that precipitated this error, if any.
}
// String returns a human-readable error message.
func (e *ParseError) String() string {
return fmt.Sprintf("pkg parse: error parsing %q as int", e.Word)
}
// Parse parses the space-separated words in in put as integers.
func Parse(input string) (numbers []int, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
fields := strings.Fields(input)
numbers = fields2numbers(fields)
return
}
func fields2numbers(fields []string) (numbers []int) {
if len(fields) == 0 {
panic("no words to parse")
}
for idx, field := range fields {
num, err := strconv.Atoi(field)
if err != nil {
panic(&ParseError{idx, field, err})
}
numbers = append(numbers, num)
}
return
}
使用一种用闭包处理错误的模式?
func check(err error) { if err != nil { panic(err) } }
// errorhandler:这是一个包装函数。接收一个 fType1 类型的函数 fn 并返回一个调用 fn 的函数。里面就包含有 defer/recover 机制
func errorHandler(fn fType1) fType1 {
return func(a type1, b type2) {
defer func() {
if err, ok := recover().(error); ok {
log.Printf(“run time panic: %v”, err)
}
}()
fn(a, b)
}
}
如何启动外部命令和程序?
StartProcess
这个函数返回被启动进程的 id(pid),或者启动失败返回错误。
exec.Command(name string, arg ...string)Run()CommandRun()
单元测试和基准测试?
写测试用例
不要通过共享内存来通信,而通过通信来共享内存
xxx
通道channel
make
func compute(ch chan int){
ch <- someComputation() // when it completes, signal on the channel.
}
func main(){
ch := make(chan int) // allocate a channel.
go compute(ch) // stat something in a goroutines
doSomethingElseForAWhile()
result := <- ch
}
学习研究下素数打印这个函数
后台服务模式:
// Backend goroutine.
func backend() {
for {
select {
case cmd := <-ch1:
// Handle ...
case cmd := <-ch2:
...
case cmd := <-chStop:
// stop server
}
}
}
协程与恢复
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work) // start the goroutine for that work
}
}
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Printf("Work failed with %s in %v", err, work)
}
}()
do(work)
}
使用锁还是通道?
- 使用锁的情景:
- 访问共享数据结构中的缓存信息
- 保存应用程序上下文和状态信息数据
- 使用通道的情景:
- 与异步操作的结果进行交互
- 分发任务
- 传递数据所有权
惰性生成器的实现?
package main
import (
"fmt"
)
type Any interface{}
type EvalFunc func(Any) (Any, Any)
func main() {
evenFunc := func(state Any) (Any, Any) {
os := state.(int)
ns := os + 2
return os, ns
}
even := BuildLazyIntEvaluator(evenFunc, 0)
for i := 0; i < 10; i++ {
fmt.Printf("%vth even: %v\n", i, even())
}
}
func BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {
retValChan := make(chan Any)
loopFunc := func() {
var actState Any = initState
var retVal Any
for {
retVal, actState = evalFunc(actState)
retValChan <- retVal
}
}
retFunc := func() Any {
return <- retValChan
}
go loopFunc()
return retFunc
}
func BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {
ef := BuildLazyEvaluator(evalFunc, initState)
return func() int {
return ef().(int)
}
}
使用通道实现Future模式。类似于java里面的future?
每一个协程执行完返回一个channel对象,然后最后从每一个channel中读取数据。
package gorout
type Matrix struct {
}
func InverseProduct(a Matrix, b Matrix) Matrix{
a_inv_future := InverseFuture(a) // start as a goroutine
b_inv_future := InverseFuture(b) // start as a goroutine
a_inv := <-a_inv_future
b_inv := <-b_inv_future
return Product(a_inv, b_inv)
}
func InverseFuture(a Matrix) (chan Matrix){
future := make(chan Matrix)
go func() {
future <- Inverse(a)
}()
return future
}
func Inverse(a Matrix) Matrix{
return a
}
func Product(a,b Matrix) (c Matrix){
return c
}
如何设计良好的错误处理,避免错误检测使代码变得混乱?
使用recover来终止panic。
使用闭包的方式解决错误。这样子啊子程序中错误处理简化成一个check(); 最后包装一层。
func check(err error) { if err != nil { panic(err) } }
func errorHandler(fn fType1) fType1 {
return func(a type1, b type2) {
defer func() {
if err, ok := recover().(error); ok {
log.Printf(“run time panic: %v”, err)
}
}()
fn(a, b)
}
}
结构体初始化?
当结构体的命名以大写字母开头时,该结构体在包外可见。
通常情况下,为每个结构体定义一个构建函数,并推荐使用构建函数初始化结构体
如何通过一个通道让主程序等待直到协程完成?(信号量模式)
ch := make(chan int) // Allocate a channel.
// Start something in a goroutine; when it completes, signal on the channel.
go func() {
// doSomething
ch <- 1 // Send a signal; value does not matter.
}()
doSomethingElseForAWhile()
<-ch // Wait for goroutine to finish; discard sent value.
简单的超时模版?
timeout := make(chan bool, 1)
go func() {
time.Sleep(1e9) // one second
timeout <- true
}()
select {
case <-ch:
// a read from ch has occurred
case <-timeout:
// the read from ch has timed out
}
如何使用输入通道和输出通道代替锁?
func Worker(in, out chan *Task) {
for {
t := <-in
process(t)
out <- t
}
}
如何取消取消耗时很长的同步调用?
ch := make(chan error, 1)
go func() { ch <- client.Call("Service.Method", args, &reply) } ()
select {
case resp := <-ch
// use resp and reply
case <-time.After(timeoutNs):
// call timed out
break
}
如何在程序出错时,终止程序?
if err != nil {
fmt.Printf(“Program stopping with error %v”, err)
os.Exit(1)
}
// or
if err != nil {
panic(“ERROR occurred: “ + err.Error())
}