重读一遍go圣经
1.2. 命令行参数
自增语句 i++,++或者--只能放在i的后面,只有这样是合法的,而不是像其他语言的,可以放在前面。 获取命令输入的参数,使用os.args[1:]
1.3. 查找重复的行
获取标准输入的值,使用 bufio.NewScanner(os.Stdin)
打印各种类型
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 变量的自然形式(natural format)
%T 变量的类型
2.2 声明
var const 2.4. 赋值
2.4.1 自增和自减是语句,而不是表达式
2.6. 包和文件
在Go语言中,一个简单的规则是:如果一个名字是大写字母开头的,那么该名字是导出的
3.3. 复数
复数包含两种类型: complex64和complex128
3.5. 字符串
1, len(s)返回字符串的长度 2, 字符串是不可修改的,任何修改字符串的操作都是不允许的 3, range处理字符串
for i, r := range "Hello, 世界" {
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
4,获取出现的最后一个字符的位置 strings.LastIndex(s, ".")
4.1. 数组
1, 当调用一个函数的时候,传递的数组是复制的数组,这里go和其他语言对于数组的处理是不一样的。感觉很奇怪,不知道这样设置到底有什么作用。
4.2. Slice
slice 就是对数组的一个引用片段,实际上底层还是数组。任何修改slice都会直接作用于底层的数组上面。
完全不明白为啥要搞出来一个slice,为啥不和其它语言一样,使用正常的数组?? 添加数组的时候,如果超过了数组的cap长度,会在内部重新开设一个新的数组。
创建数组可以使用make设定创建的Slice的len()和cap(). var s = make([]int, 16, 32)
4.3. Map
1, age, ok := ages["bob"] 返回的ok是一个boolean值,有可能会和0相等,这个只能用来判断是否存在,不能用来和值为0的比较。
2, map的key类型必须是可以进行比较的,进行判断的,除了slice, function,map其他类型都可以作为key
5.3. 多返回值
bare return 使得函数变的难以理解,不宜过多的使用。
5.4. 错误
任何的I/O的读写,都会可能发生错误的可能。
5.5. 函数值
函数之间是不能比较的,函数也不能作为map的key值
5.6. 匿名函数
什么是匿名函数? 就是一个没有名字的函数,所谓匿名就是没有函数名字。
5.8. Deferred函数
deferred函数解决了什么问题? 1, 简化代码查看逻辑,越简单越不容易出错。
5.10. Recover捕获异常
如何确定哪些应该recover,哪些不应该被recover?
就是一个相互约定的过程,约定哪些panic value可以被恢复,哪些不应该被恢复,完全是一个约定的过程。
6.1. 方法声明
什么是方法?
就是把一个函数赋值给一个type struct类型
6.2. 基于指针对象的方法
m = nil
fmt.Println(m.Get("item")) // ""
m.Add("item", "3") // panic: assignment to entry in nil map
对Get的最后一次调用中,nil接收器的行为即是一个空map的行为。我们可以等价地将这个操作写成Value(nil).Get("item"),但是如果你直接写nil.Get("item")的话是无法通过编译的,因为nil的字面量编译器无法判断其准备类型。
难道是m = nil 存在一个ㅍ_ㅍ)隐士的转换,m=Value(nil)?
6.3. 通过嵌入结构体来扩展类型
1, 通过内嵌,来达到组合多个struct的目的。
2,匿名的struct在引入别的包的struct的时候,引入了包里的函数,比如如下所示:
var cache = struct {
sync.Mutex
mapping map[string]string
}{
mapping: make(map[string]string),
}
可以使用cache.lock() 直接调用。
6.4. 方法值和方法表达式
方法值就是一个绑定了接收器的函数
6.5. 示例: Bit数组
bit数组通常用b来表示某个值,通过除来获取该值所在的位置,通过求余来获取该值所在的某个比特位上的位置。
6.6. 封装
尽管这个版本的IntSet在本质上是一样的,他也可以允许其它包中可以直接读取并编辑这个slice。换句话说,相对*s这个表达式会出现在所有的包中,s.words只需要在定义IntSet的包中出现(译注:所以还是推荐后者吧的意思)。 应该就是推荐后者的意思。
7.1. 接口约定
接口只需要关心它能做什么,而不需要关心它到底它里边是什么结构,只需要关心它能够做什么
7.2. 接口类型
通过单个接口来组合多个功能的接口。
7.3. 实现接口的条件
接口和接口之间也可以相互赋值
T类型的值可以调用*T类型的方法,仅仅只是编译器给转换了,而不是因为T类型的值拥有 *T 类型的方法
通过定义接口来进行分组,而不是通过定义类型来进行分组。
7.7. http.Handler接口
如果没有问题,那为什么还要看书? 看完之后,如果没有任何问题,是不是就是白看了? 看书为了解惑,解惑之后,如果没有新的疑惑是不是也白看了?
7.8. error接口
error是一个简单的只有一个Error方法的接口
7.10. 类型断言
f := w.(*os.File) *os.File 指T,也就是说这里断言T是指具体的类型。
第二种,如果相反断言的类型T是一个接口类型,然后类型断言检查是否x的动态类型满足T。如果这个检查成功了,动态值没有获取到;这个结果仍然是一个有相同类型和值部分的接口值,但是结果有类型T。换句话说,对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保护了接口值内部的动态类型和值的部分。
在下面的第一个类型断言后,w和rw都持有os.Stdout因此它们每个有一个动态类型*os.File,但是变量w是一个io.Writer类型只对外公开出文件的Write方法,然而rw变量也只公开它的Read方法。
书中的这句话但是“变量w是一个io.Writer类型只对外公开出文件的Write方法,然而rw变量也只公开它的Read方法。”我感觉应该是不对的,英文是 whereas rw exposes its Read method too. 应该是rw不仅输出了Write方法还有Read方法。
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
w = new(ByteCounter)
rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method
7.11. 基于类型断言区别错误类型
程序是否健壮,一个程序的真正的健壮是特别难的事情,但是要做的尽量的健壮。 根据错误的具体类型来判断具体的错误
7.12. 通过类型断言询问行为
让我们假装这是一个web服务器的核心部分并且我们的性能分析表示这个内存分配使服务器的速度变慢。这里我们可以避免掉内存分配么?
因为这里产生了一次内存分配,然后使用完之后,又重新丢弃了,产生了浪费。
通过类型断言可以判断某个接口值是否具有某法方法,比如下面:
func writeString(w io.Writer, s string) (n int, err error) {
type stringWriter interface {
WriteString(string) (n int, err error)
}
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s) // avoid a copy
}
return w.Write([]byte(s)) // allocate temporary copy
}
7.13. 类型开关
接口两种方式
1, 第一种注重接口定义的方法, 也就是接口本身定义的行为。
2, 第二种就是利用类型断言,通过判断的接口的不同类型,实现不同的行为
7.14. 示例: 基于标记的XML解码
在基于标记的样式中,解析器消费输入和产生一个标记流;
这里翻译应该有问题,应该是在在基于标记的样式中,解析器解析输入的消费信息,然后输出一个token标记。