关于包(package)
一个目录下可以有多个go文件,一个目录下的go文件最好同属于一个包。
src/hello_go下有两个go文件hi.go和hello.go,首个非注释行应该都写同一个package,如:
package hello
import "fmt"
func main(){
fmt.Println("hello!")
}
可见性规则
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);
标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
go查找远程版本
go list -m -versions $package
空白标识符_
空白符在语法上是用_表示。
_ 实际上是一个只写变量,被用于抛弃值,你不能得到它的值。
这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。如值 5 在:_, b = 5, 7 中被抛弃。
在函数返回值中被抛弃的案例如下:
package main
import "fmt"
func main() {
_, e, f := number()
fmt.Println(e, f)
}
func number()(int, int, string) {
a, b, c := 1, 2, "curry"
return a, b, c
}
输出
2 curry
容器赋值
映射赋值后,目标映射值和源映射值将共享底层的元素。 向其中一个映射中修改元素将体现在另一个映射中。
切片赋值给另一个切片后,它们将共享底层的元素。它们的长度和容量也相等。 但是和映射不同,如果以后其中一个切片改变了长度或者容量,此变化不会体现到另一个切片中。
func main() {
// 创建一个切片slice1
slice1 := []int{1, 2, 3, 4, 5, 6}
// slice2 := slice1
slice2 := append(slice1[:2], slice1[3:]...)
// 使用append函数添加一个元素
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
这段代码会输出
slice1: [1 2 4 5 6 6]
slice2: [1 2 4 5 6]
原因是切片slice1和slice2共享底层数组,在给slice2赋值时slice1[3:]...覆盖了原数组的{3,4,5}所以slice1变成了[1 2 4 5 6 6]
当一个数组被赋值给另一个数组,所有的元素都将被从源数组复制到目标数组。赋值完成之后,这两个数组不共享任何元素。
一个例子:
package main
import "fmt"
func main() {
m0 := map[int]int{0:7, 1:8, 2:9}
m1 := m0
m1[0] = 2
fmt.Println(m0, m1) // map[0:2 1:8 2:9] map[0:2 1:8 2:9]
s0 := []int{7, 8, 9}
s1 := s0
s1[0] = 2
fmt.Println(s0, s1) // [2 8 9] [2 8 9]
a0 := [...]int{7, 8, 9}
a1 := a0
a1[0] = 2
fmt.Println(a0, a1) // [7 8 9] [2 8 9]
}
关于for-range
对于aContainer这个容器作为range
- 如果aContainer是一个数组,那么在遍历过程中对此数组元素的修改不会体现到循环变量中。原因是此数组的副本(被真正遍历的容器)和此数组不共享任何元素。可以用aContainer[k]修改,但是作用的效果在for-range循环结束后才会看到,循环过程中仍然遍历的是一个副本!
- 如果aContainer是一个切片(或者映射),那么在遍历过程中对此切片(或者映射)元素的修改将体现到循环变量中。原因是此切片(或者映射)的副本和此切片(或者映射)共享元素(或条目)。
在下面代码中,虽然在循环中修改了persons[1].name,但是在循环过程中访问p并不会显现出修改的结果。而p.age只是作用在副本上而已,虽然循环时能打印出修改结果,但是循环结束后会发现并没有改变,因为只是作用在了循环体中的p
for i, p := range persons {
// 此修改将不会体现在这个遍历过程中,
// 因为被遍历的数组是persons的一个副本。
persons[1].name = "Jack"
// 此修改不会反映到persons数组中,因为p
// 是persons数组的副本中的一个元素的副本。
p.age = 31
fmt.Println(i, p)
}
fmt.Println("persons:", &persons)
切片克隆的方法
s := []int{1, 2, 3, 4, 5}
sClone := append(s[:0:0], s...)
sClone[0] = -1
fmt.Println("Original slice:", s)//Original slice: [1 2 3 4 5]
fmt.Println("Cloned slice:", sClone)//Cloned slice: [-1 2 3 4 5]
s[:0:0]创造了一个长度和容量都为0的切片,然后向切片增加元素使其扩容,创建出一个基于新的底层数组的切片,所以修改sClone时不会影响s。只要切片容量改变,切片的底层数组就会改变!
进程阻塞测试
package main
import (
"fmt"
"time"
)
func main() {
message := make(chan int)
received := make(chan int)
// receive:=make(chan int)
//发送数据
go func(ch chan<- int) {
fmt.Println("sent message!")
ch <- 1
time.Sleep(1 * time.Second)
fmt.Println("sent message!")
ch <- 2
time.Sleep(1 * time.Second)
fmt.Println("sent message!")
ch <- 3
time.Sleep(1 * time.Second)
close(ch)
}(message)
go func(ch <-chan int) {
fmt.Println("received message!", <-ch)
fmt.Println("received message!", <-ch)
received <- 1
}(message)
fmt.Println("sleep to wait sync!")
time.Sleep(5 * time.Second)
fmt.Println("set a chanel to wait sync!")
<-received
fmt.Println("This will not be printed until receiving", <-message, ".")
}