Appearance
Golang语言基础
基础语法要点
rune
complex 复数
gofunc euler() { cmplx.Exp(1i * math.Pi) + 1 // cmplx.Pow(math.E, 1i * math.Pi) + 1 }强制类型转换: 所有的转换必须显式声明
整数转换为浮点数: 浮点数不能精确表示所有的数, 比如, 他有可能没有办法精确的表示3
浮点数转换为整数: 4.99999会被转换为4而不是5, 解决方案并没有一个通用的办法, 而是要具体问题具体分析。一般的, 把结果四舍五入, 使用math.Round, 然后再转整数就可以了。
由于浮点数本身无法精确表示一个数, 所以这个问题无法完美解决。在精度要求高的场合, 我们采用定点数, 比如go语言有big.Float
枚举: go没有特殊的枚举关键字, 一般就使用const来定义
goconst ( cpp = iota // 自增 _ java python golong javascript )文件读取
gofunc main() { const filename = "abc.txt" contents, err = ioutil.ReadFile(filename) if err != nil { fmt.Println(err) } else { fmt.Println("%s\n", contents) } } func printFile(filename string) { file, err := os.Open(filename) if err != nil { panic(err) } scanner := bufio.NewScanner(file) for scanner.Scan() { fmt.Println(scanner.Text()) } }switch: go中switch不需要break, 除非使用fallthrough, 同时switch后可以没有表达式
for的条件里不需要括号, 条件可以省略初始条件, 结束条件, 递增表达式。没有while
gofunc convertToBin(n int) string { result := "" for ; n > 0; n /= 2{ lsb := n % 2 result = strconv.Itoa(lsb) + result } return result }go语言的指针是不能计算的。数组是值类型, 通常不使用, 而使用切片。
面向对象
结构体
go语言仅支持封装, 不支持继承和多态
go语言没有class, 只有struct。只有使用指针才可以改变内容, nil指针也可以调用方法。
Gotype treeNode struct { value int left, rigth *treeNode } func createNode(value int) *treeNode { return &treeNode{value: value} } // 为结构定义方法 func (node treeNode) print() { fmt.Print(node.value) } // 使用指针作为方法接收者 func (node *treeNode) setValue(value int) { node.value = value } func main() { var root treeNode root = treeNode{value : 3} root.left = treeNode{} root.right = treeNode{5, nil, nil} // nodes := []treeNode { // {value: 3}, // {}, // {6, nil, nil}, // } root.right.left = new(treeNode) // 使用工厂函数进行定义, 返回了局部变量的地址 root.right.right = createTreeNode(2) root.print() root.left.setValue(4) }
接口
接口即描述了一个方法能做什么事情, 也即不再关心实现类的具体实现
Go
func getRetriever() retriever {
return testing.Retriever{}
}
type retriever interface {
Get(string) string
}
func main() {
var r retriever = getRetriever()
fmt.Println(r.Get("url"))
}duck typing
"像鸭子走路, 像鸭子叫(长得像鸭子), 那么就是鸭子。"
描述事物的外部行为而非内部结构
严格来说go属于结构化类型系统, 类似duck typing
Go语言的接口是由使用者定义, 并由使用者决定使用哪个实现类, 与java的由实现类实现者告知使用者的方式有较大逻辑上的区别。
interface {}
类似于泛型, 在type后类型的部分接interface{}表达为不限定类型
Go语言标准接口
Stringer: 同等于Java的 toString
Reader和Writer: 不仅可以读写文件, 还可以读写网络
函数式编程
函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。在函数式编程中,函数是第一类对象(一等公民),意思是说一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。
个人理解: 函数式编程的要点就在于, 将功能尽可能的包装成一个函数, 通过返回值或者其他语言中的类似于回调函数的概念, 进行嵌入。
go
package main
import (
"fmt"
"strings"
"io"
"bufio"
)
// 功能函数封装
type intGen func() int
// 功能实现函数
func fibonacci() intGen {
// 自由变量, 类似于初始化
a, b := 0, 1
return func () int {
// 局部变量
a, b = b, a + b
return a
}
}
// 实现intGen下的Read接口, 实现生产者
func (g intGen) Read(p []byte) (n int, err error ){
next := g()
if next > 10000 {
return 0, io.EOF
}
s := fmt.Sprintf("%d\n", next)
return strings.NewReader(s).Read(p)
}
// 将实现的Read接口提供给消费者
func printFileContents(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
// 创建实现函数
f := fibonacci()
// 使用实现接口
printFileContents(f)
}错误处理与资源管理
defer调用
确保在函数结束时发生
多个defer的执行顺序是先进后出
Gofunc writeFile(filename string) { file, err := os.Create(filename) if err != nil { panic(err) } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() f := fib.Fibonacci() for i :=0; i < 20; i++ { fmt.Fprintln(writer, f()) } }
错误处理
error: 本身是一个类型, 和int, string等没有什么区别
可以通过err.(*os.PathError)来获取 err的详细信息
Goif err != nil { if pathError, ok := err.(*os.PathError); !ok { panic(err) } else { fmt.Printf("%s, %s, %s\n", pathError.Op, pathError.Path, pathErrpr.Err) } return }自定义err,
err = errors.New("error message")服务器统一处理
gopackage main import ( "net/http" "os" "io/ioutil" ) // 通过url获取文件 func main() { // 获取url http.HandleFunc("/list/", // res和req func(writer http.ResponseWriter, request *http.Request) { // 获取文件信息 path := request.URL.Path[len("/list/"):] // 获取相应文件, 开启文件流 file, err := os.Open(path) if err != nil { // 普通err打印 panic(err) // 网络异常打印 http.Error(writer, err.Error(), http.StatusInternalServerError) // 网络异常改, 保护了内部的错误信息不会往外传递 switch { case os.IsNotExist(err): log.Warn("Error handling request: %s", err.Error()) http.Error( writer, http.StatusText(http.StatusNotFound), http.StatusNotFound ) } return } // 关闭文件流 defer file.close() // 读入文件流 all, err := ioutil.ReadAll(file) if err != nil { panic(err) } // 写入response writer.Writer(all) }) err := http.ListenAndServe(":8888", nil) if err != nil { panic(err) } }error也是可以往外扔的, 接收的函数用errWrapper(fn())接。
panic: 会停止当前函数, 并一直向上返回, 执行每一层的defer。如果没遇见recover, 程序就退出
recover: 仅在defer调用中使用, 获取panic的值, 如果无法处理可重新panic
性能调优
go test -bench . -cpuprofile cpu.out测试, 获取性能数据
gofunc BenchmarkSubstr(b *testing.B) { // b.N 系统设定值 for i:=0; i< b.N; i ++ {} //重置计时器, 可以用来排除准备用代码的运行时间 b.ResetTimer() }go tool pprof cpu.out 查看性能日志文件, cpu.out就是性能日志文件
(pprof) web 需要安装Graphviz
Channel
go
func createWorker(id int) chan int {
c := make(chan int)
go func() {
for {
fmt.Printf("Worker %d received %c\n", id, <-c)
}
}()
return c
}
func chanDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i] = createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + 1
}
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}channel也是一等公民, 可以类似于变量被使用
缓冲channel
Go
func bufferedChannel() {
c := make(chan int, 3)
go worker(0, c)
c <- 1
c <- 2
c <- 3
}关闭channel
go
func worker(id int, c chan int) {
for {
n ,ok := <-c
if !ok {
break
}
fmt.Printf("Worker %d received %d\n", id, n)
}
}
// 发送方close, 接受方进行检测
func channelClose() {
c := make(chan int, 3)
go worker(0, c)
c <- 1
c <- 2
c <- 3
close(c)
}Select调度
go
select {
case n := <-c1:
fmt.Println("Received from c1:", n)
case n := <-c2:
fmt.Println("Received from c2:", n)
default:
fmt.Println("No value received")
}