-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
golang #86
Comments
gobookvariablevar x int32 = 0; 在 function 里可以简写 x := 0; functionfunc (this *Type) func_name(x int, y int) (ret int){
ret = x + y
} 变长参数func name(args ...int){
}
// spread
arr := [5]int{ 1,2,3,4,5 }
name(arr...) defer / panicfunc main(){
defer func(){
if err := recover() {
// blabla
}
}()
} fo
arrayvar x [5]int
var y [5]int{
1, 2, 3, 4, 5
} slice// len = 0
var x []int
// len = 5
x := make([]int, 5)
// 底层 capacity = 10
// len(x) = 5
x := make([]int, 5, 10) 使用 array 创建 slice// 类似 js Array.prototype.slice 包含 start, 不包含 end
x := arr[start:end] slice functions
Mapx := map[string]string{
"hello": "world"
}
x := make(map[string]string) 方法
获取数据value, ok := map["key"] pointer
struct// 多个同类型参数
// 可以 collapse
type Person struct{
x,y,z float64
}
// 初始化
var p Person
// new
p := make(Person)
p := Person{ 0,0,1 }
func (this *Person) calc() float64{
return this.x + this.y + this.z
} channelch := make(chan int, 1); // 1 is the capacity select看哪个 chan 先返回数据 select {
case <- chan1:
// blabla
case <- chan2:
// blabla
} channel direction// Now c can only be sent to. Attempting to receive from c will result in a compiler error.
func pinger(c chan<- string)
// receive
func printer(c <-chan string) |
goplhttps://docs.ruanjiadeng.com/gopl-zh/index.html ch2指针
在 golang 中, 返回函数中的局部变量地址也是安全的 func fn() *int {
v := 1
return &v
} 局部变量 赋值x = 1
*p = true
元组赋值 类型
|
格式 | 说明 |
---|---|
%d |
输出10进制 |
%b |
输出2进制 |
%o |
输出8进制 |
%x / %X |
输出16进制 |
%[1]d |
还是使用第一个格式化输入 |
%#x |
输出 0x , 8进制输出头部 0 , x 输出 0x , X 输出 0X |
其他
%c
输出单个字符%q
输出带""
浮点数
float32
/ float64
- float32 & float64 能表示从很小到很大的数, 但是 float32 能表示的正整数并不是很大(float32 有效的 bit 位只有23个,
2 << 24
赋值给 float32 值, 计算就不精确了) - 使用
%g
/%e
/%f
打印浮点数, 例如%8.3f
总长度8, 小数点后保留3位小数
复数
go 提供
complex64
/complex128
类型complex(real, imag)
/real(x)
/imag(x)
内置函数
字符串
包
unicode
unicode/utf8
bytes
strings
strconv
Note:
- 字符串一旦生成, 不可改变了.
len(s)
返回的是字节数目, 并不是字符个数s[index]
返回字节值, index 超出 len, 则会 panicutf8.RuneCountInString(s)
返回字符个数utf8.DecodeRuneInString
从字节数组中解码 rune 字符
go for i := 0; i < len(s); { r, size := utf8.DecodeRuneInString(s[i:]) fmt.Printf("%d\t%c\n", i, r) i += size }
for index,c := range "Hello, 世界" { //blabla }
range可以自动解码[]rune(str)
将string转为rune数组string(codepoint int32)
进行 utf8 编码, 如果码点无效, 则使用\uFFFD
代替byte[](s)
转换会分配新的字节数组用于字符串数据的拷贝
helper
Contains
/ Join
/ Index
/ HasPrefix
... 等 helper
bytes
包 & strings
包都提供这些 helper, 输入参数不同.
转换
strconv.Itoa
:int
tostring
strconv.FormatInt
:int
tostring
fmt.Sprintf(format, inputs)
strconv.Atoi
:string
toint
strconv.ParseInt(s string, 进制 int, size int)
: parseInt
常量
常量表达式的值在编译期计算
无类型常量
许多常量没有一个确定的基础类型. 编译器为无明确基础类型的数字提供比基础类型更高精度的算数运算.
// TODO: 无类型常量, 不是很懂
iota
iota
表示自增
const (
Mon = iota // = 0
Tue
Wed
Thu
Fri
Sat
Sun
)
也可以
const (
Mon = iota + 1 // = 0 + 1
Tue
...
)
ch4
数组
- 因为长度固定, 很少使用数组, 而是使用 slice 切片
// 固定长度
arr := [3]int{ 1,2,3 }
// `...` 使用内容决定长度
arr := [...]int{
1,2,3,4,5
}
// 使用 index: val
// arr[1], arr[9] = 1, 9; 其余为 0
arr := [...]int{
1: 1,
9: 9
}
比较
数组是可以使用 ==
/ !=
进行比较的, 当内部每个元素都相等时, 数组相等
slice
- slice 没有固定长度
- 多个 slice 可以共享底层数组存储
len
返回 slice 长度cap
返回 slice 容量- 初始化 slice 时, 不指定长度, 编译器会为 slice 创建合适的存储
- slice 不能使用
==
比较, 对于byte[]
可以使用bytes.Equal
进行比较 - slice 是间接引用, 一个固定的 slice 在不同的时间可能包含不同的元素, 因为底层数组的元素可能被修改
slice != nil
是唯一合法的相等比较make([]T, len, cap)
append 函数
append(slice, value...)
append值可能会导致slice重新分配内存, 增大 capacity
map
- map 是哈希表的一个引用, 可写为
map[K]V
- K 类型必须支持
==
比较, 判断 key 是否存在 - 可以使用
make(map[K]V)
/map[k]V{ key: val }
创建 - get/set
map[k]
/map[k] = v
- del
delete(map, k)
for k,v := range map
迭代顺序每次都会变, 防止你依赖 :joy
get
通过key作为索引下标来访问map将产生一个value。如果key在map中是存在的,那么将得到与key对应的value;如果key不存在,那么将得到value对应类型的零值
v, ok := map[k] // 这样才对, v 不会为 nil, k 不存在, ok 为 false
struct
syntax
type Foo struct {
ID Int
Name String
}
var foo Foo = Foo{}
var pf *Foo = &foo
- 相同类型的字段可以合并
foo.prop
&pf.prop
均可以, 即指针也可以直接使用.
操作符- 一个名为
S
的struct不能再包括一个S
类型的成员, 但是可以包含*S
类型的成员 Foo{ val1, val2, ... }
给定所有属性值Foo{ prop: val }
给定某些属性- 使用
%#v
打印结构体, 会打印每一个字段
比较
如果结构体成员全部是可以比较的, 那么结构体也是可以比较的. 使用
==
比较结构体, 将比较两个结构体的所有成员.
由于可以比较, 结构体也可以作为 map 的 key
结构体嵌入
type Circle struct {
x,y,radius int
}
type Wheel struct {
Circle
other int
}
这样 Circle
嵌入到了 Wheel
中, 这样
-
w.Circle.x
&w.x
均可以访问 -
使用
Circle
初始化Wheel
的时候, 必须使用w := Wheel{ Circle: Circle{ 1,2 }, other: 3 }
JSON
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
json.Marshal()
/json.MarshalIndent()
只有可导出的成员才会出现在导出值中.json:"导出字段"
tag可以设置导出字段json:"导出ziduan, omitempty"
忽略空值json.Unmarshal(json_string, 结构体)
text & template
t := template
.New('t')
.Funcs(template.FuncMap{
foo: funcFoo
})
.Parse(template_string)
t.Execute(os.Stdout, data)
ch5 函数
多返回值
func fn() (int, error) {
return 1, nil
}
错误
常用错误处理策略
- 传播错误, 函数 body 中某个function call返回的error, 变成这个函数的error
- 忽略错误, 进行重试 / 只打印错误信息 / 完全忽略
- 输出错误信息, 并退出. 可以使用
log.Fatalf
达到目的
闭包
像 js 一样, 在循环中, 如果引用迭代变量, 最后值是迭代退出的值, 例如
fns := []func() int{}
for _, v := range []int{1,2,3,4,5} {
fns = append(fns, func(i int){
return v
})
}
// 这样最后 return 都是最后的值.
// 可以使用
for _, v := range []int{1,2,3,4,5} {
v := v // 使用迭代变量v 初始化内部变量v
// 或者像 js 一样, 使用自执行函数
}
可变参数
func add(values ...int) (ret int) {
for _, v := range values {
ret += v
}
return
}
ints := []int{ 1,2,3,4 }
add(ints...) // 使用 spread
defer
/ panic
/ recover
- defer 在
return
之后执行, 甚至可以用来修改返回值 defer fn1(); defer fn2()
:fn2()
->fn1()
顺序执行- panic / recover 一般表示严重错误, 一般 API 都是返回 error,
Must
开头API, 表示遇到 error panic
ch6 方法
type Point struct {
radius int
}
func (this Point) Area() {
return math.PI * this.radius * this.radius
}
- 这里的
this
叫做 receiver p.Area
被赋值给一个fn
变量后,this
还是指向p
, 没有 js 里call
/apply
一说- 无论 receiver形参 是
T
/*T
, 无论实参是T
/*T
, 均可以调用 - 嵌入的结构体的方法可以直接使用
ch7 接口
type Writer interface {
Write(p []byte) (n int, err error)
}
-
接口是合约, 只要实现了接口里所有的方法, 就可认为实现了接口
-
接口也可以像 struct 一样进行嵌入, 输出接口会包含被嵌入接口所定义的方法
-
表达一个类型属于某个接口只要这个类型实现这个接口
var w Writer w = Foo{} // Foo 有 Write 方法即可 type ReadWriter{ Writer Read() []byte } var rw ReadWriter w = rw // OK. rw.Write Exists rw = w // error, w.Read not Exists
接口值
https://docs.ruanjiadeng.com/gopl-zh/ch7/ch7-05.html
package main
import (
"bytes"
"fmt"
"io"
)
// 一个具体类型值a 转换成接口值b 时
// 这时接口
// - 动态类型为a的类型, 即 *bytes.Buffer
// - 动态值为 nil
// 但是 b != nil
func main() {
var a *bytes.Buffer
fmt.Printf("a is nil: %v\n", a == nil) // true
b := io.Writer(a)
fmt.Printf("b is nil: %v\n", b == nil) // false
a = new(bytes.Buffer)
fmt.Printf("a is nil: %v\n", a == nil) // false
}
常用接口
sort.Interface
Len
Less
Swap
http.Handler
:http.ListenAndServe(address, handler)
ServeHTTP(w ResponseWriter, r *Request)
http.NewServeMux()
一个ServerMux
类似router
, 然后-
http.HandleFunc
将一个func(ResponseWrite, *Request)
转换为一个http.Handle
-
mux.Handle(path string, handler http.Handler)
于是一般mux.Handle('/hello', http.HandlerFunc(func(w ResponseWriter, r *Request) { // blabla })) // 需要手动将一个函数转换为 http.Handler 接口类型值 // 注意是 `http.HandlerFunc`
-
mux.HandleFunc("/hello", func(w ResponseWriter, r *Request){ })
-
http.HandleFunc()
/http.DefaultServeMux
/http.ListenAndServe(address, nil)
使用内置的默认 mux
-
error
Error() string
errors.New(message)
fmt.Errorf(format, args)
类型断言
var w io.Writer
w = os.Stdout
f := w.(*os.File) // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer
ch8
ch = make(chan int) // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3
close(ch) // 关闭
ch <- data // send
d := <- ch // receive
<- ch // 丢弃
不带缓存的 channel
发送将导致发送者阻塞, 直至接收者接收值
带缓存的channel
- 发送往队列队尾添加数据, 没超过队列容量时, 可以无阻塞添加
- 接收从队列对首删除数据, 当没有数据可以被删除时, 接收阻塞
单方向channel
func(ch chan<-int)
表示只能往ch <- int
发送 intfunc(ch <-chan int)
标示只能用于接收d := <-ch
select
select{
case <- ch1:
//
case <- ch2:
//
case <- time.After(10 * time.Second):
// 超时
default:
//
}
ch9锁
sync.Mutex
互斥锁
- 使用
defer mu.Unlock()
来保证锁被释放
sync.RWMutex
读写锁
允许多个只读操作并行执行,但写操作会完全互斥。这种锁叫作“多读单写”锁(multiple readers, single writer lock),Go语言提供的这样的锁是sync.RWMutex
- mu.RLock / mu.RUnlock
内存同步
编译器可以随意修改指令顺序, 不同的 goroutine 顺序不能保证, 编译器只需保证同一线程 / goroutine 是有序的即可
单例
mu : sync.Mutex{}
if instance == nil {
mu.Lock()
if instance == nil {
// create instance
}
defer mu.Unlock()
}
sync.Once
var o sync.Once
func instance(){
o.Do(createInstance)
return instance
}
竞态条件检测
$ go build/run/test -race
ch10 包
工具
go get [-u] url
url
的返回内容<meta name="go-import" content="golang.org/x/net git https://go.googlesource.com/net">
即可定义真正的 get path-u
表示确保是最新的
go build
编译go install
保存编译结果, 而不是丢弃go run
go list <导入路径>
列出包- 使用
...
表示任意路径, 如go list ...xml...
列出包含xml
的包 - 使用
-json
表示输出 son
- 使用
go doc
ch11 测试
在包目录内, 所有以
_test.go
为后缀名的源文件并不是 go build 构建包的一部分, 它们是go test
测试的一部分.
有三种类型的测试函数
- 测试函数: 以
Test
为函数名前缀, 用于测试函数的逻辑行为是否正确 - 基准测试函数: 以
Benchmark
为函数名前缀, 用于衡量一些函数的性能, go test 会多次运行基准函数以计算一个平均的执行时间. - 示例函数: 以
Example
为函数名前缀, 提供一个由编译器保证正确性的示例文档.
测试函数
func TestName(t *testing.T) {
// ...
}
go test
命令如果没有指定包, 那么将采用当前目录对应的包,go test -v
打印每个测试函数的名称和运行时间-run="foo|bar"
对应一个正则表达式, 只有匹配的测试函数才会被运行
扩展测试包
package foo // 逻辑
package foo_test // 用于测试
测试覆盖率
# 生成 coverage
$ go test -coverprofile=c.out
# 生成 html
$ go tool cover -html=c.out
基准测试
import "testing"
func BenchmarkIsPalindrome(b *testing.B) {
for i := 0; i < b.N; i++ {
IsPalindrome("A man, a plan, a canal: Panama")
}
}
- 参数类型是
*testing.B
go test -bench="正则"
-benchmem
包含内存信息
profile
# 采集数据
$ go test -cpuprofile=cpu.out
$ go test -blockprofile=block.out
$ go test -memprofile=mem.out
# 运行 profile
$ go tool pprof -text -nodecount=10 ./http.test cpu.log
示例函数
func ExampleIsPalindrome() {
fmt.Println(IsPalindrome("A man, a plan, a canal: Panama"))
fmt.Println(IsPalindrome("palindrome"))
// Output:
// true
// false
}
- 主要作为文档
go test
会运行示例函数, 会检测该函数的标准输出是否与注释匹配- 提供 playground
write zip file
package main
import (
"archive/zip"
"fmt"
"log"
"os"
"strings"
"syscall"
)
var stdlog = fmt.Println
func main() {
apk := os.ExpandEnv("$HOME/workspace/fengjr/android_pack/mobile-website-release.apk")
stdlog("APK location: ", apk)
content := `{"chanel": "some"}`
e := writeChannelInfo(apk, content)
if e != nil {
log.Fatal(e)
}
s, e := getChannelInfo(apk)
if e != nil {
log.Fatal(e)
}
stdlog(s)
}
func getChannelInfo(apk string) (s string, e error) {
r, e := zip.OpenReader(apk)
defer r.Close()
if e != nil {
return
}
for _, x := range r.File {
// if x.Name.
if !strings.Contains(x.Name, "/") {
stdlog(x.Name)
}
// if x.Name == "META-INF/channel_info" {
// rc, _ := x.Open()
// defer rc.Close()
// data := make([]byte, 10000)
// _, e = rc.Read(data)
// s = string(data)
// return
// }
}
return
}
func writeChannelInfo(apk string, content string) (e error) {
file, e := os.OpenFile(apk, syscall.O_RDWR, 0)
defer file.Close()
if e != nil {
return
}
w := zip.NewWriter(file)
defer w.Close()
entry, e := w.Create("hello")
if e != nil {
return
}
_, e = entry.Write([]byte(content))
return
} |
go modbasichttps://books.studygolang.com/gopl-zh/ch10/ch10-02.html vendorvendor 就是不依赖全局 gopath, 类似于把 go module /
|
go env除了在 shell 初始化脚本中导出环境变量, go 还会默认使用
|
No description provided.
The text was updated successfully, but these errors were encountered: