├── chapter_11 ├── README.md ├── Hello.java └── main.go ├── chapter_07 ├── p1 │ └── package01.go ├── main.go └── README.md ├── chapter_12 ├── README.md └── main.go ├── chapter_09 ├── main.go └── README.md ├── .gitignore ├── chapter_08 ├── main.go └── README.md ├── chapter_15 ├── main.go └── README.md ├── chapter_14 └── main.go ├── chapter_10 ├── README.md └── main.go ├── chapter_13 ├── main.go └── README.md ├── chapter_04 └── README.md ├── chapter_03 └── README.md ├── README.md ├── chapter_05 └── README.md ├── chapter_02 └── README.md ├── chapter_01 └── README.md └── chapter_06 └── README.md /chapter_11/README.md: -------------------------------------------------------------------------------- 1 | # Go 指针 2 | 3 | Go 语言是值传递。 4 | 5 | ## Go 中的指针操作 6 | 7 | `*` 8 | 9 | 1. 在类型前面声明可以定义一个指针变量 10 | 2. 在取一个指针变量的值的时候可以使用 * 11 | 3. 为指针变量赋值 12 | 13 | `&` 14 | 15 | & 符号就是对变量取地址 -------------------------------------------------------------------------------- /chapter_07/p1/package01.go: -------------------------------------------------------------------------------- 1 | package p1 2 | 3 | import "fmt" 4 | 5 | func Foo() { 6 | fmt.Println("foo") 7 | } 8 | 9 | func bar() { 10 | fmt.Println("bar") 11 | } 12 | 13 | func init() { 14 | fmt.Println("init") 15 | } 16 | -------------------------------------------------------------------------------- /chapter_12/README.md: -------------------------------------------------------------------------------- 1 | # Go 数组 2 | 3 | ```java 4 | int[] arr1; 5 | int arr2[]; 6 | 7 | int[] arr3 = new int[2]; 8 | int arr4[] = new int[2]; 9 | 10 | int arr5[] = {1,2,3}; 11 | int[] arr6 = {1,2,3}; 12 | 13 | arr5[0]; 14 | ``` -------------------------------------------------------------------------------- /chapter_11/Hello.java: -------------------------------------------------------------------------------- 1 | public class Hello { 2 | 3 | private static void change(Integer num) { 4 | num = 2333; 5 | System.out.println(num); 6 | } 7 | 8 | public static void main(String[] args) { 9 | Integer num = 1; 10 | change(num); 11 | System.out.println(num); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /chapter_07/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | fm "fmt" 5 | 6 | "github.com/biezhi/lets-golang/chapter_07/p1" 7 | ) 8 | 9 | // Hello some thing 10 | /** 11 | * hello world 12 | */ 13 | func Hello() { 14 | 15 | } 16 | 17 | func main() { 18 | // hello 19 | // strings.Trim 20 | fm.Println("Hello, World!") 21 | fm.Println("王爵的技术小黑屋:biezhi.me") 22 | p1.Foo() 23 | // p1.bar() 24 | // long a = 123L; 25 | // int b = 234; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /chapter_09/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func main() { 9 | var v1 bool 10 | v1 = true 11 | v2 := (1 == 2) 12 | fmt.Println(v1, v2) 13 | var b bool 14 | b = 1 != 0 15 | fmt.Println(b) 16 | 17 | var value1 int32 18 | value2 := 125 // 被推断为int类型 19 | value1 = int32(value2) 20 | fmt.Println(value1, value2) 21 | 22 | var i int32 23 | var j int64 24 | i, j = 1, 2 25 | if i == 1 || j == 2 { 26 | fmt.Println("==") 27 | } 28 | 29 | fmt.Println("=============") 30 | var fvalue1 float32 31 | fvalue1 = 122 32 | fvalue2 := 122.0 33 | fmt.Println(fvalue1, fvalue2) 34 | fmt.Printf("%T, %T \n", fvalue1, fvalue2) 35 | 36 | fmt.Println(math.Dim(1, 2)) 37 | 38 | var cp1 complex64 = 12 + 10i 39 | fmt.Println(cp1) 40 | } 41 | -------------------------------------------------------------------------------- /chapter_12/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func change(a *[3]int) { 6 | a[1] = 233 7 | } 8 | 9 | func main() { 10 | arr1 := [...]int{1, 2, 3} 11 | 12 | //ptrArr := [3]*int{1:new(int)} 13 | // 14 | //fmt.Println("ptrArr[1]:", *ptrArr[1]) 15 | change(&arr1) 16 | 17 | //fmt.Println("ptrArr[1]:", *ptrArr[1]) 18 | //arr1[1] = 233 19 | fmt.Println(arr1) 20 | // 21 | //for i:=0; i c { 30 | fmt.Printf("cap: %d -> %d \n", c, n) 31 | c = n 32 | } 33 | } 34 | } 35 | 36 | func copySlice_() { 37 | data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 38 | s := data[8:] 39 | s2 := data[:5] 40 | copy(s2, s) 41 | fmt.Println("s:", s) 42 | fmt.Println("s2:", s2) 43 | fmt.Println("data:", data) 44 | } 45 | 46 | func change(s []int) { 47 | s[0] = 100 48 | } 49 | 50 | func main() { 51 | slice := []int{0, 1, 2, 3} 52 | fmt.Println(slice) 53 | change(slice) 54 | fmt.Println(slice) 55 | } 56 | -------------------------------------------------------------------------------- /chapter_10/README.md: -------------------------------------------------------------------------------- 1 | # 字符串和字符 2 | 3 | ## 字符串 4 | 5 | - Go 中的字符串根据需要占用 1 至 4 个字节 6 | - 和 C/C++ 一样,Go 中的字符串是根据长度限定,而非特殊字符。 7 | - str[i]的方式只对纯 ASCII 码的字符串有效。 8 | - 和字符串有关的包:strings, strconv 9 | - 解释字符串 10 | - 非解释字符串 11 | 12 | ## strings 和 strconv 包 13 | 14 | - 前缀和后缀: `strings.HasPrefix(s, prefix string) bool` 15 | - 字符串包含关系: `strings.Contains(s, substr string) bool` 16 | - 判断子字符串或字符在父字符串中出现的位置: `strings.Index(s, str string) int` 17 | - 字符串替换: `strings.Replace(str, old, new, n) string` 18 | - 统计字符串出现次数: `strings.Count(s, str string) int` 19 | - 重复字符串: `strings.Repeat(s, count int) string` 20 | - 修改字符串大小写: `strings.ToLower(s) string` 21 | - 修剪字符串: `strings.TrimSpace(s)` 22 | - 分割字符串: `strings.Split(s, sep)` 23 | - 字符串与其它类型的转换: `strconv.Itoa(i int) string` 24 | 25 | ## bytes 包 26 | 27 | - bytes 包和字符串包十分类似,而且它还包含一个十分有用的类型 Buffer。这是一个 bytes 的定长 buffer,提供 Read 和 Write 方法,因为读写不知道长度的 bytes 最好使用 buffer。 28 | - Buffer 可以这样定义:var buffer bytes.Buffer 或者 new 出一个指针:var r *bytes.Buffer = new(bytes.Buffer) 或者通过函数:func NewBuffer(buf []byte) *Buffer,这就创建了一个 Buffer 对象并且用 buf 初始化好了;NewBuffer 最好用在从 buf 读取的时候使用。 29 | - 通过 buffer 串联字符串:类似于 Java 的 StringBuilder 类 创建一个 Buffer,通过 buffer.WriteString(s) 方法将每个 string s 追加到后面,最后再通过 buffer.String() 方法转换为 string 30 | 31 | ## 字符类型 32 | 33 | **byte和rune** 34 | 35 | - byte(实际上是uint8的别名),代表 `UTF-8` 字符串的单个字节的值 36 | - rune(int32),代表单个 `Unicode` 字符 37 | 38 | -------------------------------------------------------------------------------- /chapter_10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | var s string = "hello world \n" 11 | var s2 string = `helle world \n` 12 | fmt.Println(s) 13 | fmt.Println(s2) 14 | fmt.Println(len(s)) 15 | fmt.Printf("%c\n", s[0]) 16 | fmt.Printf("%c\n", s[len(s)-3]) 17 | 18 | // 字符串的修改 19 | str := "hello world" 20 | strArr := []byte(str) 21 | strArr[0] = 'X' 22 | fmt.Println(str) 23 | fmt.Println(string(strArr)) 24 | 25 | u := "王爵的技术小黑屋" 26 | us := []rune(u) 27 | us[0] = 'w' 28 | fmt.Println(u) 29 | fmt.Println(string(us)) 30 | 31 | u2 := "王爵的技术小黑屋" 32 | us2 := []byte(u2) 33 | us2[0] = 'w' 34 | fmt.Println(u2) 35 | fmt.Println(string(us2)) 36 | 37 | // 字符串拼接 38 | foo := "hello" + "world" + string(3) 39 | fmt.Println(foo) 40 | 41 | // 字符串的遍历 42 | str = "hello 世界" 43 | for i, n := 0, len(str); i < n; i++ { 44 | var ch2 byte = str[i] 45 | fmt.Printf("%d - %c , ", i, ch2) 46 | } 47 | fmt.Println("\n==================") 48 | for i, ch := range str { 49 | fmt.Printf("%d: %v, %c \n", i, ch, ch) // ch 是 rune类型 50 | // fmt.Printf("%c .", ch) 51 | } 52 | 53 | fmt.Println("是否以 hello 开头:", strings.HasPrefix(str, "hello")) 54 | fmt.Println("是否包含世界:", strings.Contains(str, "世界")) 55 | fmt.Println("o出现的位置:", strings.Index(str, "o")) 56 | 57 | // bytes.Buffer 58 | var buffer bytes.Buffer 59 | buffer.WriteString("hello") 60 | fmt.Println(buffer.String()) 61 | } 62 | -------------------------------------------------------------------------------- /chapter_13/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func initSlice() { 6 | s1 := []int{1, 2, 3} 7 | fmt.Println(s1, len(s1), cap(s1)) 8 | 9 | s2 := []int{0, 1, 2, 5: 100} 10 | fmt.Println(s2, len(s2), cap(s2)) 11 | } 12 | 13 | func makeSlice() { 14 | s1 := make([]int, 3) 15 | fmt.Println(s1, len(s1), cap(s1)) 16 | 17 | s2 := make([]int, 3, 6) 18 | fmt.Println(s2, len(s2), cap(s2)) 19 | } 20 | 21 | func emptySlice() { 22 | var nilSlice []int 23 | emptySlice := []int{} 24 | fmt.Printf("len = %d, cap = %d, %#v\n", len(nilSlice), cap(nilSlice), nilSlice) 25 | fmt.Printf("len = %d, cap = %d, %#v\n", len(emptySlice), cap(emptySlice), emptySlice) 26 | } 27 | 28 | func arraySlice() { 29 | data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 30 | fmt.Println("expression\tslice\t\t\t\t\tlen\tcap\t") 31 | // 省略了 low 32 | fmt.Printf("data[:6:8]\t%v\t\t\t%d\t%d\n", data[:6:8], len(data[:6:8]), cap(data[:6:8])) 33 | // 省略了 high max 34 | fmt.Printf("data[5:]\t%v\t\t\t\t%d\t%d\n", data[5:], len(data[5:]), cap(data[5:])) 35 | // 省略了 low max 36 | fmt.Printf("data[:3]\t%v\t\t\t\t\t%d\t%d\n", data[:3], len(data[:3]), cap(data[:3])) 37 | // 全部省略 38 | fmt.Printf("data[:]\t\t%v\t%d\t%d\n", data[:], len(data[:]), cap(data[:])) 39 | s1 := data[:] 40 | s1[0] = 200 41 | fmt.Println(s1) 42 | fmt.Println(data) 43 | } 44 | 45 | func main() { 46 | reSlice() 47 | } 48 | 49 | func reSlice() { 50 | s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 51 | s1 := s[2:5] 52 | // 2 3 4 53 | fmt.Println(s1, len(s1), cap(s1)) 54 | s1[1] = 100 55 | // 2 100 4 56 | s2 := s1[2:6] 57 | // 4 5 6 7 58 | fmt.Println("s2:", s2) 59 | s2[2] = 200 60 | fmt.Println(s) 61 | } 62 | -------------------------------------------------------------------------------- /chapter_04/README.md: -------------------------------------------------------------------------------- 1 | # 编写第一个 Go 库 2 | 3 | 让我们编写一个库,并让 hello 程序来使用它。 4 | 5 | 同样,第一步还是选择包路径(我们将使用 `github.com/biezhi/stringutil`) 并创建包目录: 6 | 7 | ```bash 8 | $ mkdir $GOPATH/src/github.com/biezhi/stringutil 9 | ``` 10 | 11 | 接着,在该目录中创建名为 `reverse.go` 的文件,内容如下: 12 | 13 | ```bash 14 | // stringutil 包含有用于处理字符串的工具函数。 15 | package stringutil 16 | 17 | // Reverse 将其实参字符串以符文为单位左右反转。 18 | func Reverse(s string) string { 19 | r := []rune(s) 20 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { 21 | r[i], r[j] = r[j], r[i] 22 | } 23 | return string(r) 24 | } 25 | ``` 26 | 27 | 现在用 `go build` 命令来测试该包的编译: 28 | 29 | ```bash 30 | $ go build github.com/biezhi/stringutil 31 | ``` 32 | 33 | 当然,若你在该包的源码目录中,只需执行: 34 | 35 | ```bash 36 | $ go build 37 | ``` 38 | 39 | 即可。这不会产生输出文件。想要输出的话,必须使用 go install 命令,它会将包的对象放到工作空间的 pkg 目录中。 40 | 41 | 确认 `stringutil` 包构建完毕后,修改原来的 hello.go 文件(它位于 `$GOPATH/src/github.com/biezhi/hello`)去使用它: 42 | 43 | ```bash 44 | package main 45 | 46 | import ( 47 | "fmt" 48 | 49 | "github.com/biezhi/stringutil" 50 | ) 51 | 52 | func main() { 53 | fmt.Printf(stringutil.Reverse("!oG ,olleH")) 54 | } 55 | ``` 56 | 57 | 无论是安装包还是二进制文件,go 工具都会安装它所依赖的任何东西。 因此当我们通过 58 | 59 | ```bash 60 | $ go install github.com/biezhi/hello 61 | ``` 62 | 63 | 来安装 hello 程序时,`stringutil` 包也会被自动安装。 64 | 65 | 运行此程序的新版本,你应该能看到一条新的,反向的信息: 66 | 67 | ```bash 68 | $ hello 69 | Hello, Go! 70 | ``` 71 | 72 | 做完上面这些步骤后,你的工作空间应该是这样的: 73 | 74 | ```bash 75 | bin/ 76 | hello # 可执行命令 77 | pkg/ 78 | linux_amd64/ # 这里会反映出你的操作系统和架构 79 | github.com/biezhi/ 80 | stringutil.a # 包对象 81 | src/ 82 | github.com/biezhi/ 83 | hello/ 84 | hello.go # 命令源码 85 | stringutil/ 86 | reverse.go # 包源码 87 | ``` 88 | 89 | 注意 `go install` 会将 `stringutil.a` 对象放到 `pkg/linux_amd64` 目录中,它会反映出其源码目录。 这就是在此之后调用 go 工具,能找到包对象并避免不必要的重新编译的原因。 `linux_amd64` 这部分能帮助跨平台编译,并反映出你的操作系统和架构。 90 | 91 | Go的可执行命令是静态链接的;在运行Go程序时,包对象无需存在。 92 | 93 | -------------------------------------------------------------------------------- /chapter_03/README.md: -------------------------------------------------------------------------------- 1 | # Hello Go 程序 2 | 3 | ## 包路径 4 | 5 | 标准库中的包有给定的短路径,比如 `"fmt"` 和 `"net/http"`。 对于你自己的包,你必须选择一个基本路径,来保证它不会与将来添加到标准库, 或其它扩展库中的包相冲突。 6 | 7 | 如果你将你的代码放到了某处的源码库,那就应当使用该源码库的根目录作为你的基本路径。 例如,若你在 GitHub 上有账户 `github.com/biezhi` 那么它就应该是你的基本路径。 8 | 9 | 注意,在你能构建这些代码之前,无需将其公布到远程代码库上。只是若你某天会发布它, 这会是个好习惯。在实践中,你可以选择任何路径名,只要它对于标准库和更大的Go生态系统来说, 是唯一的就行。 10 | 11 | > fmt包含有格式化 I/O 函数,类似于 C 语言的 printf 和 scanf。格式字符串的规则来源于 C 但更简单一些。 12 | 13 | 我们将使用 `github.com/biezhi` 作为基本路径。在你的工作空间里创建一个目录, 我们将源码存放到其中: 14 | 15 | ```bash 16 | $ mkdir -p $GOPATH/src/github.com/biezhi/hello 17 | ``` 18 | 19 | 接着,在该目录中创建名为 `hello.go` 的文件,其内容为以下Go代码: 20 | 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | func main() { 27 | fmt.Println("Hello, Go") 28 | } 29 | ``` 30 | 31 | 现在你可以用 go 工具构建并安装此程序了: 32 | 33 | ```bash 34 | $ go install github.com/biezhi/hello 35 | ``` 36 | 37 | 注意,你可以在系统的任何地方运行此命令。go 工具会根据 GOPATH 指定的工作空间,在 `github.com/biezhi/hello` 包内查找源码。 38 | 39 | 若在从包目录中运行 `go install`,也可以省略包路径: 40 | 41 | ```bash 42 | $ cd $GOPATH/src/github.com/biezhi/hello 43 | $ go install 44 | ``` 45 | 46 | 此命令会构建 hello 命令,产生一个可执行的二进制文件。 接着它会将该二进制文件作为 `hello`(在 Windows 下则为 `hello.exe`)安装到工作空间的 bin 目录中。 在我们的例子中为 `$GOPATH/bin/hello`,具体一点就是 $HOME/go/bin/hello。 47 | 48 | go 工具只有在发生错误时才会打印输出,因此若这些命令没有产生输出, 就表明执行成功了。 49 | 50 | 现在,你可以在命令行下输入它的完整路径来运行它了: 51 | 52 | ```bash 53 | $ $GOPATH/bin/hello 54 | Hello, Go 55 | ``` 56 | 57 | 若你已经将 `$GOPATH/bin` 添加到 `PATH` 中了,只需输入该二进制文件名即可: 58 | 59 | ```bash 60 | $ hello 61 | Hello, Go. 62 | ``` 63 | 64 | 若你使用源码控制系统,那现在就该初始化仓库,添加文件并提交你的第一次更改了。 再次强调,这一步是可选的:你无需使用源码控制来编写Go代码。 65 | 66 | ```bash 67 | $ cd $GOPATH/src/github.com/biezhi/hello 68 | $ git init 69 | Initialized empty Git repository in /Users/biezhi/course/golang/src/github.com/biezhi/hello/.git/ 70 | $ git add hello.go 71 | $ git commit -m "initial commit" 72 | [master (root-commit) 0b4507d] initial commit 73 | 1 file changed, 1 insertion(+) 74 | create mode 100644 hello.go 75 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 给 Java 程序员的 Go 私房菜 2 | 3 | 该系列内容是针对 Java 程序员到 Go 的上手资料,实践优先。 4 | 5 | ## 内容大纲 6 | 7 | - [#01 为什么是 Go?](chapter_01) | [视频](https://www.youtube.com/watch?v=ByPureWggN0&t=2s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=2) 8 | - [#02 搭建 Go 环境](chapter_02) | [视频](https://www.youtube.com/watch?v=NcOfkV6ZaJw&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=3) 9 | - [#03 Hello Go 程序](chapter_03) | [视频](https://www.youtube.com/watch?v=G694P63p1DQ&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=4) 10 | - [#04 编写第一个 Go 库](chapter_04) | [视频](https://www.youtube.com/watch?v=m6UBZe31TJY&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=5) 11 | - [#05 Go 中的包管理](chapter_05) | [视频](https://www.youtube.com/watch?v=bjbVIq7xGNg&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=6) 12 | - [#06 Go 中的命令](chapter_06) | [视频](https://www.youtube.com/watch?v=jgOl-Y7VNtY&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=7) 13 | - [#07 文件名、关键字与标识符](chapter_07) | [视频](https://www.youtube.com/watch?v=dxKI5kxdKcY&t=404s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=8) 14 | - [#08 变量、常量](chapter_08) | [视频](https://www.youtube.com/watch?v=uuumrA3n6ys&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=9) 15 | - [#09 基本数据类型](chapter_09) | [视频](https://www.youtube.com/watch?v=zkyuSq33uiM&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=10) 16 | - [#10 字符串和字符](chapter_10) | [视频](https://www.youtube.com/watch?v=ZJfbz7-xLBM&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=11) 17 | - [#11 Go 指针](chapter_11) | [视频](https://www.youtube.com/watch?v=WIsLX_NL5cU&t=45s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=12) 18 | - [#12 Go 数组](chapter_12) | [视频](https://www.youtube.com/watch?v=UXgcNtpSI1U&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=13) 19 | - [#13 Go Slice 1](chapter_13) | [视频](https://www.youtube.com/watch?v=x394gc0Uw2o&t=2s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=14) 20 | - [#14 Go Slice 2](chapter_14) | [视频](https://www.youtube.com/watch?v=H-lQD2s1MRo&t=0s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=15) 21 | - [#15 Go Map](chapter_15) | [视频](https://www.youtube.com/watch?v=kFdvY2stYnY&t=6s&list=PLK2w-tGRdrj6IlCTN6jvMBlGfJUtuU6u3&index=16) 22 | 23 | -------------------------------------------------------------------------------- /chapter_05/README.md: -------------------------------------------------------------------------------- 1 | # Go 中的包管理 2 | 3 | ## 包的命名 4 | 5 | go 语言的包的命名,遵循简洁、小写、和 go 文件所在目录同名的原则,这样就便于我们引用,书写以及快速定位查找。 6 | 7 | ```go 8 | package main 9 | 10 | import "net/http" 11 | 12 | func main() { 13 | http.ListenAndServe("127.0.0.1:8080",handler); 14 | } 15 | ``` 16 | 17 | **以域名命名包** 18 | 19 | 20 | ```bash 21 | package main 22 | 23 | import "biezhi.me/utils" 24 | ``` 25 | 26 | **使用 Github 账号命名包** 27 | 28 | ```go 29 | package main 30 | 31 | import "github.com/biezhi/utils" 32 | ``` 33 | 34 | 这就是换成github.com命名的方式。 35 | 36 | ## main 包 37 | 38 | 当把一个go文件的包名声明为 main 时,就等于告诉go编译程序,我这个是一个可执行的程序,那么go编译程序就会尝试把它编译为一个二进制的可执行文件。 39 | 40 | 一个main的包,一定会包含一个main()函数,这种我们也不陌生,比如C和Java都有main()函数,它是一个程序的入口,没这个函数,程序就无法执行。 41 | 42 | > 在go语言里,同时要满足main包和包含main()函数,才会被编译成一个可执行文件。 43 | 44 | ## 导入包 45 | 46 | ```go 47 | import "fmt" 48 | ``` 49 | 50 | ```go 51 | import ( 52 | "net/http" 53 | "fmt" 54 | ) 55 | ``` 56 | 57 | > 对于多于一个路径的包名,在代码中引用的时候,使用全路径最后一个包名作为引用的包名,比如net/http,我们在代码使用的是http,而不是net。 58 | 59 | 编译器会使用我们设置的这两个路径,再加上import导入的相对全路径来查找磁盘上的包,比如我们导入的fmt包,编译器最终找到的是/usr/local/go/fmt这个位置。 60 | 61 | 值得了解的是:对于包的查找,是有优先级的,编译器会优先在GOROOT里搜索,其次是GOPATH,一旦找到,就会马上停止搜索。如果最终都没找到,就报编译异常了。 62 | 63 | ## 远程包导入 64 | 65 | ```go 66 | import "github.com/biezhi/moe" 67 | ``` 68 | 69 | 1. 在 `GOPATH` 下搜索 70 | 2. go get 下载 71 | 72 | ## 命名导入 73 | 74 | ```go 75 | package main 76 | 77 | import ( 78 | "fmt" 79 | myfmt "mylib/fmt" 80 | ) 81 | 82 | func main() { 83 | fmt.Println() 84 | myfmt.Println() 85 | } 86 | ``` 87 | 88 | ```go 89 | package main 90 | import ( 91 | _ "mylib/fmt" 92 | ) 93 | ``` 94 | 95 | ## 包的init函数 96 | 97 | **init 函数** 98 | 99 | ```go 100 | package mysql 101 | 102 | import ( 103 | "database/sql" 104 | ) 105 | 106 | func init() { 107 | sql.Register("mysql", &MySQLDriver{}) 108 | } 109 | ``` 110 | 111 | 因为我们只是想执行这个mysql包的init方法,并不想使用这个包,所以我们在导入这个包的时候,需要使用_重命名包名,避免编译错误。 112 | 113 | **静默导入** 114 | 115 | ```go 116 | import "database/sql" 117 | import _ "github.com/go-sql-driver/mysql" 118 | 119 | db, err := sql.Open("mysql", "user:password@/dbname") 120 | ``` 121 | 122 | -------------------------------------------------------------------------------- /chapter_15/README.md: -------------------------------------------------------------------------------- 1 | # Go 语言Map(集合) 2 | 3 | Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。 4 | 5 | Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。 6 | 7 | ## 定义 Map 8 | 9 | 可以使用内建函数 make 也可以使用 map 关键字来定义 Map: 10 | 11 | ```go 12 | /* 声明变量,默认 map 是 nil */ 13 | var map_variable map[key_data_type]value_data_type 14 | 15 | /* 使用 make 函数 */ 16 | map_variable := make(map[key_data_type]value_data_type) 17 | ``` 18 | 19 | 如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对 20 | 21 | ## 实例 22 | 23 | 下面实例演示了创建和使用map: 24 | 25 | ```go 26 | package main 27 | 28 | import "fmt" 29 | 30 | func main() { 31 | var countryCapitalMap map[string]string /*创建集合 */ 32 | countryCapitalMap = make(map[string]string) 33 | 34 | /* map插入key - value对,各个国家对应的首都 */ 35 | countryCapitalMap [ "France" ] = "Paris" 36 | countryCapitalMap [ "Italy" ] = "罗马" 37 | countryCapitalMap [ "Japan" ] = "东京" 38 | countryCapitalMap [ "India " ] = "新德里" 39 | 40 | /*使用键输出地图值 */ for country := range countryCapitalMap { 41 | fmt.Println(country, "首都是", countryCapitalMap [country]) 42 | } 43 | 44 | /*查看元素在集合中是否存在 */ 45 | captial, ok := countryCapitalMap [ "美国" ] /*如果确定是真实的,则存在,否则不存在 */ 46 | /*fmt.Println(captial) */ 47 | /*fmt.Println(ok) */ 48 | if (ok) { 49 | fmt.Println("美国的首都是", captial) 50 | } else { 51 | fmt.Println("美国的首都不存在") 52 | } 53 | } 54 | ``` 55 | 56 | 以上实例运行结果为: 57 | 58 | ```shell 59 | France 首都是 Paris 60 | Italy 首都是 罗马 61 | Japan 首都是 东京 62 | India 首都是 新德里 63 | 美国的首都不存在 64 | ``` 65 | 66 | ## delete() 函数 67 | 68 | delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下: 69 | 70 | ```go 71 | package main 72 | 73 | import "fmt" 74 | 75 | func main() { 76 | /* 创建map */ 77 | countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"} 78 | 79 | fmt.Println("原始地图") 80 | 81 | /* 打印地图 */ 82 | for country := range countryCapitalMap { 83 | fmt.Println(country, "首都是", countryCapitalMap [ country ]) 84 | } 85 | 86 | /*删除元素*/ delete(countryCapitalMap, "France") 87 | fmt.Println("法国条目被删除") 88 | 89 | fmt.Println("删除元素后地图") 90 | 91 | /*打印地图*/ 92 | for country := range countryCapitalMap { 93 | fmt.Println(country, "首都是", countryCapitalMap [ country ]) 94 | } 95 | } 96 | ``` 97 | 98 | 以上实例运行结果为: 99 | 100 | ```shell 101 | 原始地图 102 | India 首都是 New delhi 103 | France 首都是 Paris 104 | Italy 首都是 Rome 105 | Japan 首都是 Tokyo 106 | 法国条目被删除 107 | 删除元素后地图 108 | Italy 首都是 Rome 109 | Japan 首都是 Tokyo 110 | India 首都是 New delhi 111 | ``` -------------------------------------------------------------------------------- /chapter_02/README.md: -------------------------------------------------------------------------------- 1 | # 搭建 Go 环境 2 | 3 | ## 安装 4 | 5 | **系统需求** 6 | 7 | gcc 编译器支持以下操作系统及架构。在开始前,请确保你的系统满足这些需求。 若你的OS及架构不在此列表中,那么 gccgo 可能支持你的设置,详情请访问设置并使用 [gccgo](https://golang.org/doc/install/gccgo)。 8 | 9 | ![image](https://user-images.githubusercontent.com/3849072/49286514-5b31c580-f4d5-11e8-927e-b4a1e27248bb.png) 10 | 11 | - 仅当你打算用cgo时才需要gcc。 12 | - 只需为 Xcode安装命令行工具即可。 若你已经安装了Xcode 4.3+,只需从下载配置面板的组件标签内安装它即可。 13 | 14 | 下载此 [压缩包](https://golang.org/dl/) 并提取到 `/usr/local` 目录,在 `/usr/local/go` 中创建 Go目录。例如: 15 | 16 | ```bash 17 | tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz 18 | ``` 19 | 20 | 该压缩包的名称可能不同,这取决于你安装的Go版本和你的操作系统以及处理器架构。 21 | 22 | (此命令必须作为 root 或通过 sudo 运行) 23 | 24 | 要将 `/usr/local/go/bin` 添加到 `PATH` 环境变量, 你需要将此行添加到你的 `/etc/profile`(全系统安装)或 `$HOME/.profile` 文件中: 25 | 26 | **安装到指定位置** 27 | 28 | Go 二进制发行版假定它们会被安装到 `/usr/local/go` (或Windows下的 `c:\Go`)中,但也可将Go工具安装到不同的位置。 此时你必须设置 `GOROOT` 环境变量来指出它所安装的位置。 29 | 30 | 例如,若你将 Go 安装到你的 home 目录下,你应当将以下命令添加到 `$HOME/.profile` 文件中: 31 | 32 | ```bash 33 | export GOROOT=/usr/local/go 34 | export PATH=$PATH:$GOROOT/bin 35 | ``` 36 | 37 | > 注:GOROOT 仅在安装到指定位置时才需要设置。 38 | 39 | ## 测试安装 40 | 41 | ```bash 42 | go version 43 | ``` 44 | 45 | ## 代码组织 46 | 47 | ### 概览 48 | 49 | - Go程序员通常将所有的Go代码保存在一个_工作区_中。 50 | - 工作区包含许多版本控制存储库 (例如,由Git管理)。 51 | - 每个存储库都包含一个或多个包。 52 | - 每个软件包由一个目录中的一个或多个Go源文件组成。 53 | - 包的目录路径决定了它的导入路径。 54 | 55 | 请注意,这与其他编程环境不同,在这些编程环境中,每个项目都有一个单独的工作区,工作区与版本控制存储库紧密相关。 56 | 57 | ### 工作区 58 | 59 | go 工具为公共代码仓库中维护的开源代码而设计。 无论你会不会公布代码,该模型设置工作环境的方法都是相同的。 60 | 61 | Go 代码必须放在工作空间内。它其实就是一个目录,其中包含三个子目录: 62 | 63 | * `src` 目录包含Go的源文件,它们被组织成包(每个目录都对应一个包) 64 | * `pkg` 目录存放编译好的库文件, 主要是*.a文件 65 | * `bin` 目录包含可执行命令 66 | 67 | go 工具用于构建源码包,并将其生成的二进制文件安装到 pkg 和 bin 目录中。 68 | 69 | src 子目录通常包会含多种版本控制的代码仓库(例如 Git), 以此来跟踪一个或多个源码包的开发。 70 | 71 | 以下例子展现了实践中工作空间的概念: 72 | 73 | ```bash 74 | bin/ 75 | streak # 可执行命令 76 | todo # 可执行命令 77 | pkg/ 78 | linux_amd64/ 79 | code.google.com/p/goauth2/ 80 | oauth.a # 包对象 81 | github.com/nf/todo/ 82 | task.a # 包对象 83 | src/ 84 | code.google.com/p/goauth2/ 85 | .hg/ # mercurial 代码库元数据 86 | oauth/ 87 | oauth.go # 包源码 88 | oauth_test.go # 测试源码 89 | github.com/nf/ 90 | streak/ 91 | .git/ # git 代码库元数据 92 | oauth.go # 命令源码 93 | streak.go # 命令源码 94 | todo/ 95 | .git/ # git 代码库元数据 96 | task/ 97 | task.go # 包源码 98 | todo.go # 命令源码 99 | ``` 100 | 101 | 此工作空间包含三个代码库(goauth2、streak 和 todo),两个命令(streak 和 todo) 以及两个库(oauth 和 task)。 102 | 103 | 命令和库从不同的源码包编译而来。稍后我们会对讨论它的特性。 104 | 105 | ## GOPATH 106 | 107 | - GOPATH 环境变量指定了你的工作空间位置。它或许是你在开发Go代码时, 唯一需要设置的环境变量。 108 | 109 | 首先创建一个工作空间目录,并设置相应的 GOPATH。你的工作空间可以放在任何地方, 在此文档中我们使用 `$HOME/work`。注意,它绝对不能和你的Go安装目录相同。 (另一种常见的设置是 `GOPATH=$HOME`。) 110 | 111 | ```bash 112 | export GOPATH=$HOME/course/golang 113 | export PATH=$PATH:$GOROOT/bin:$GOPATH/bin 114 | ``` 115 | 116 | ## 开发工具 117 | 118 | - [vscode](https://code.visualstudio.com/) 119 | - [Goland](https://www.jetbrains.com/go/) 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /chapter_01/README.md: -------------------------------------------------------------------------------- 1 | # Go 语言介绍 2 | 3 | ## 什么是 Go? 4 | 5 | Go语言是由Google开发的一个开源项目,目的之一为了提高开发人员的编程效率。 Go语言语法灵活、简洁、清晰、高效。它对的并发特性可以方便地用于多核处理器 和网络开发,同时灵活新颖的类型系统可以方便地编写模块化的系统。go可以快速编译, 同时具有垃圾内存自动回收功能,并且还支持运行时反射。Go是一个高效、静态类型, 但是又具有解释语言的动态类型特征的系统级语法。 6 | 7 | 非系统底层语言的出现已经有十多年的时间, 而在这期间计算环境已经发生了巨大变化。以下是几大发展趋势: 8 | - 计算机运算速度有极大提升, 但是软件开发效率没有明显提高。 9 | - 依赖管理是今天软件开发一个重要部分, 但像C语言中的“header files”阻碍了简单的依赖分析和快速的编译。 10 | - 与类似Java, C++等带有重类型系统的语言相比, 弱类型语言编程的兴起促使越来越多的开发者使用诸如Python或JavaScript这些动态类型语言来进行编程。 11 | - 像垃圾回收和并行计算这些基础概念在当前流行的系统语言中并没有得到很好的支持。 12 | - 多核计算机的出现引起了更多的担忧和疑惑。 13 | 14 | 我们相信值得去尝试开发一种支持并发、垃圾回收并且能被快速编译的新语言。对于以上的几点: 15 | 16 | 能够在一台单机上快速编译(如几秒内)GO的一个大项目。 17 | Go提供了这样一个软件开发模型: 使依赖分析更简单, 而避免了类似C那样在文件和库上使用依赖的不便。 18 | Go的类型系统没有层次结构, 所有没有把时间花在定义类型关系上。同样, 虽然Go有静态类型, 它也尽量使类型显得更加轻量(相对于其他经典Object-Oriented语言来说)。 19 | Go提供了完整的垃圾回收机制, 并且在并发执行和通讯方面提供了基础支持。 20 | 基于自身设计, GO在多核机器上的软件系统的构建给出了方案。 21 | 22 | ## 吉祥物从何而来? 23 | 24 | 吉祥物的logo是由Renée French来设计的, 他同时还设计了Glenda, the Plan 9 bunny。地鼠是从她几年前穿的WFMU T-shirt获得灵感而设计的。该logo在Creative Commons Attribution 3.0许可内使用。 25 | 26 | ## 你为什么要创造一种新的语言? 27 | 28 | Go之所以诞生, 是对现有语言和系统编程环境不满的一种驱动释然。编程变得如此困难, 而语言的选择也变得迷茫。高效编译, 高效执行, 易于编程这三者在同一个主流语言中不可同时兼得。开发者被动选择了动态类型语言(如Python/JavaScript)以进行安全有效的开发,而不是C++或Java。 29 | 30 | Go尝试既做到解析型、动态类型语言那样易于开发, 也做到静态、编译语言一样高效安全。同样它侧重支持网络及多核编程。 31 | 最后, 希望通过Go来加速编译: 在单台机器上最多需要几秒钟时间来完成一个大项目的编译流程。要达到这些目标,需要解决大量这样的问题: 一个富于表达而且轻巧的类型系统;并发和垃圾回收等等。现有的库或者工具并不能很好的解决这些问题, 因此新的语言应运而生。 32 | 33 | ## 设计原则 34 | 35 | 现在的编程包含了太多的记账, 重复和文档性工作。 36 | 如同Dick Gabriel所说, 阅读老的程序就像一个善言的研究员和一个善于学习的机器同事之间的一个安静的对话,而不是和编译器之间的一场争吵。但谁会想到诡辩会带来噪音呢” sophistication(复杂化?)是有原因的—谁也不想回到老的语言—但它是否能更平和的实现呢? 37 | 38 | Go尝试减少输入的代码数量。 透过它的设计, 我们尝试减少混乱和复杂度。在Go中, 没有前置声明和头文件;所有对象/变量都是只声明一次。 39 | 初始化方式易读, 自动化而且易用。语法干净, 关键字轻量。可以使用 := (声明并定义)的结构来简化普通表达式: foo.Foo* myFoo = new(foo.Foo)。而最基本的, Go的类型没有层次结构, 也就不需要声明类型之间的关系。这种简化机制使得Go轻松做到富于表达,易于理解。 40 | 41 | 另外一个重要的原则是保持概念正交。可以实现任何类型的方法; 42 | 结构体用来表示数据而接口用来表示抽象;等等. 正交性使得组合操作时更容易清楚其中发生了什么。 43 | 44 | ## 它能干吗? 45 | 46 | - Docker:无人不知的虚拟华平台,开源的应用容器引擎,借助该引擎,开发者可以打包他们的应用,移植到任何平台上。 47 | - kubernetes:Google出品,用于调度和管理docker的开源容器管理系统,利用他,可以方便的管理你的docker实例,哪怕非常多,也是目前最流行的docker管理系统。 48 | - lantern 蓝灯 49 | - gogs:一款基于git的代码托管系统,类似于github和gitlab,不过其小巧易用,功能强大,部署方便,也有不少用户在使用。 50 | - grafana:一款开源监控度量的看板系统,可以接Graphite,Elasticsearch,InfluxDB等数据源,定制化很高。 51 | - etcd:一款分布式的,可靠的K-V存储系统,使用简单,速度快,又安全。 52 | - influxdb:可伸缩的数据库,使用场景主要用来存储测量数据,事件点击以及其他等实时分析数据,用来做监控性能很不错。 53 | - caddy:快速的,跨平台的HTTP/2 Web服务器。 54 | - beego:国产开源的高性能Web框架,让你快速的开发Go Web应用服务。 55 | 56 | https://www.jianshu.com/p/f814504c8f05 57 | 58 | ## 优势 59 | 60 | **部署简单。** 61 | 62 | Go 编译生成的是一个静态可执行文件,除了 glibc 外没有其他外部依赖。这让部署变得异常方便:目标机器上只需要一个基础的系统和必要的管理、监控工具,完全不需要操心应用所需的各种包、库的依赖关系,大大减轻了维护的负担。这和 Python 有着巨大的区别。由于历史的原因,Python 的部署工具生态相当混乱【比如 setuptools, distutils, pip, buildout 的不同适用场合以及兼容性问题】。官方 PyPI 源又经常出问题,需要搭建私有镜像,而维护这个镜像又要花费不少时间和精力。 63 | 64 | **并发性好。** 65 | 66 | Goroutine 和 channel 使得编写高并发的服务端软件变得相当容易,很多情况下完全不需要考虑锁机制以及由此带来的各种问题。单个 Go 应用也能有效的利用多个 CPU 核,并行执行的性能好。这和 Python 也是天壤之比。多线程和多进程的服务端程序编写起来并不简单,而且由于全局锁 GIL 的原因,多线程的 Python 程序并不能有效利用多核,只能用多进程的方式部署;如果用标准库里的 multiprocessing 包又会对监控和管理造成不少的挑战【我们用的 supervisor 管理进程,对 fork 支持不好】。部署 Python 应用的时候通常是每个 CPU 核部署一个应用,这会造成不少资源的浪费,比如假设某个 Python 应用启动后需要占用 100MB 内存,而服务器有 32 个 CPU 核,那么留一个核给系统、运行 31 个应用副本就要浪费 3GB 的内存资源。 67 | 68 | **良好的语言设计。** 69 | 70 | 从学术的角度讲 Go 语言其实非常平庸,不支持许多高级的语言特性;但从工程的角度讲,Go 的设计是非常优秀的:规范足够简单灵活,有其他语言基础的程序员都能迅速上手。更重要的是 Go 自带完善的工具链,大大提高了团队协作的一致性。比如 gofmt 自动排版 Go 代码,很大程度上杜绝了不同人写的代码排版风格不一致的问题。把编辑器配置成在编辑存档的时候自动运行 gofmt,这样在编写代码的时候可以随意摆放位置,存档的时候自动变成正确排版的代码。此外还有 gofix, govet 等非常有用的工具。 71 | 72 | **执行性能好。** 73 | 74 | 虽然不如 C 和 C++,但通常比原生 Python 应用还是高一个数量级的,适合编写一些瓶颈业务。内存占用也非常省。 75 | 76 | ## 资料 77 | 78 | - https://studygolang.com/articles/7001 79 | -------------------------------------------------------------------------------- /chapter_09/README.md: -------------------------------------------------------------------------------- 1 | # Go 基本数据类型 2 | 3 | Go 语言有以下几种基础类型: 4 | 5 | - 布尔类型:bool,占1字节 6 | - 整型:int8、byte(uint8)、int16、int、uint、uintptr等 7 | - 浮点类型:float32、float64 8 | - 复数类型:complex64、complex128 9 | - 字符串:string 10 | - 字符类型:rune(int32) 11 | - 错误类型:error 12 | 13 | 还有下面的复合类型: 14 | 15 | - 指针(pointer) 16 | - 数组(array) 17 | - 切片(slice) 18 | - 字典(map) 19 | - 通道(chan) 20 | - 结构体(struct) 21 | - 接口(interface) 22 | 23 | ## 布尔类型 24 | 25 | - true 26 | - false 27 | 28 | ## 整数 29 | 30 | - int8(-128 -> 127) 31 | - int16(-32768 -> 32767) 32 | - int32(-2,147,483,648 -> 2,147,483,647) 33 | - int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807) 34 | 35 | ## 无符号整数 36 | 37 | - uint8(0 -> 255) 38 | - uint16(0 -> 65,535) 39 | - uint32(0 -> 4,294,967,295) 40 | - uint64(0 -> 18,446,744,073,709,551,615) 41 | 42 | ## 浮点型(IEEE-754 标准) 43 | 44 | - float32(+- 1e-45 -> +- 3.4 * 1e38) 45 | - float64(+- 5 * 1e-324 -> 107 * 1e308) 46 | 47 | > IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。 48 | > 49 | > https://zh.wikipedia.org/wiki/IEEE_754 50 | 51 | **复数类型** 52 | 53 | 1. complex64 54 | 32 位实数和虚数 55 | 2. complex128 56 | 64 位实数和虚数 57 | 58 | ```go 59 | var value1 complex64 // 由2个float32构成的复数类型 60 | value1 = 3.2 + 12i 61 | value2 := 3.2 + 12i // value2是complex128类型 62 | value3 := complex(3.2, 12) // value3结果同value2 63 | var value4 complex128 = 3.2 + 12i 64 | ``` 65 | 66 | ## 一些示例 67 | 68 | ```go 69 | // Bool类型 70 | var my_bool bool = true 71 | 72 | // 字符串类型 73 | var my_string string = "hello, world!" 74 | 75 | // int: 有符号整形,根据系统架构自动判断是int8,int16,int32还是int64 76 | // 比如当前系统是64位系统,则为int64 77 | var my_int_min int = -9223372036854775808 78 | var my_int_max int = 9223372036854775807 79 | 80 | // int8: 有符号 8 位整型 (-128 到 127) 81 | var my_int8_min int8 = -128 82 | var my_int8_max int8 = 127 83 | 84 | // int16: 有符号 16 位整型 (-32768 到 32767) 85 | var my_int16_min int16 = -32768 86 | var my_int16_max int16 = 32767 87 | 88 | // int32: 有符号 32 位整型 (-2147483648 到 2147483647) 89 | var my_int32_min int32 = -2147483648 90 | var my_int32_max int32 = 2147483647 91 | 92 | // int64: 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) 93 | var my_int64_min int64 = -9223372036854775808 94 | var my_int64_max int64 = 9223372036854775807 95 | 96 | // uint: 无符号整形,根据系统架构自动判断是uint8,uint16,uint32还是uint64 97 | // 比如当前系统是64位系统,则为uint64 98 | var my_uint_min uint = 0 99 | var my_uint_max uint = 18446744073709551615 100 | 101 | // uint8: 无符号 8 位整型 (0 到 255) 102 | var my_uint8_min uint8 = 0 103 | var my_uint8_max uint8 = 255 104 | 105 | // uint16: 无符号 16 位整型 (0 到 65535) 106 | var my_uint16_min uint16 = 0 107 | var my_uint16_max uint16 = 65535 108 | 109 | // uint32: 无符号 32 位整型 (0 到 4294967295) 110 | var my_uint32_min uint32 = 0 111 | var my_uint32_max uint32 = 4294967295 112 | 113 | // uint64: 无符号 64 位整型 (0 到 18446744073709551615) 114 | var my_uint64_min uint64 = 0 115 | var my_uint64_max uint64 = 18446744073709551615 116 | 117 | // uintptr: 无符号整型,用于存放一个指针,可以足够保存指针的值的范围。和uint范围相同,根据系统架构自动判断 118 | var my_uintptr_min uintptr = 0 119 | var my_uintptr_max uintptr = 18446744073709551615 120 | 121 | // byte: uint8的别名 122 | var my_byte_min byte = 0 123 | var my_byte_max byte = 255 124 | 125 | // rune: int32的别名。代表1个unicode码 126 | var my_rune_min rune = -2147483648 127 | var my_rune_max rune = 2147483647 128 | 129 | // float32: 单精度浮点数,在C语言里等同于float 130 | // float64: 双精读浮点数,在C语言里等同于double 131 | // 如果不写类型,则为float64(暂时不知道这个是根据系统架构判断还是默认就是float64) 132 | // 从结果可以看出: 133 | // float32只能容纳8位数字(包括小数点前后数字,不包括小数点,超过8位会将四舍五入保留8位) 134 | // float64可以容纳比较多的数字(具体暂时还没测),而且这种双精度我也一直没搞懂,很复杂 135 | // 当符合要求时候会自动用科学计数法来表示,要注意 136 | var my_float32 float32 = 10086.141592653 137 | var my_float64 float64 = 10086.141592653 138 | var my_float_auto = 10086.141592653 139 | ``` -------------------------------------------------------------------------------- /chapter_08/README.md: -------------------------------------------------------------------------------- 1 | # 变量 常量 2 | 3 | ## **var** 4 | 5 | --- 6 | 7 | 变量声明用 `var` 关键字,可以用在函数级别,也可以用在包级别,比如 8 | 9 | ```go 10 | package main 11 | 12 | var c, python, java bool //这个就是在包级别 13 | 14 | func main() { 15 | var i int //这个就是在函数级别 16 | } 17 | ``` 18 | 19 | **注意:** 20 | 21 | `var` 多个变量时不能为每个变量设定类型(无论类型是否相同,无论是否有带初始值)比如: 22 | 23 | | 语句 | 结果 | 24 | | :-- | :-- | 25 | | var i int, j int | 报错 | 26 | | var i int, j int = 1, 2 | 报错 | 27 | | var i string, j int | 报错 | 28 | | var i string, j int = "a", 1 | 报错 | 29 | 30 | var多个变量带初始值,如果类型不同,就不能指定变量类型,比如: 31 | 32 | | 语句 | 结果 | 33 | | :-- | :-- | 34 | | var i, j = "aa", 2 | 正确 | 35 | | var i, j int = "aa", 2 | 报错 | 36 | 37 | var是否跟类型(包括var单个变量或者var多个变量): 38 | 39 | 1. 跟类型: 40 | 1. 带初始值: OK 41 | 42 | 单个变量例子:`var i int = 1` 43 | 44 | 多个变量例子:`var i, j int = 1, 2` 45 | 46 | 2. 不带初始值: 根据类型自动设初始值,如int初始值为0,string初始值为""(空字符) 47 | 48 | 单个变量例子:`var i int`,则 i 为 0 49 | 50 | 多个变量例子:`var i, j int`,则 i 和 j 为 0 51 | 52 | 2. 不跟类型: 53 | 1. 带初始值: 根据初始值自动判断类型,如 1 为 int 类型,"a" 为 string 类型 54 | 55 | 单个变量例子:`var i = 1`,则 i 为 int 类型 56 | 57 | 多个变量例子: 58 | 59 | 1. `var i, j = 1, 2`,则 i 和 j 为 int 类型 60 | 61 | 2. `var i, j = true, "a"`,则 i 为 bool 类型,j 为 string 类型 62 | 63 | 2. 不带初始值: 报错 64 | 65 | 单个变量例子:`var i`,会报错 66 | 67 | 多个变量例子:`var i, j`,会报错 68 | 69 | 也可以这么声明 70 | 71 | ```text 72 | var ( 73 | A int = 100 74 | B string 75 | ) 76 | ``` 77 | 78 | **和 const 区别** 79 | 80 | `var` 可以不赋值(会自动赋值初始值),而 `const` 必须赋值 81 | 82 | ## **:=** 83 | 84 | --- 85 | 86 | `:=` 是短声明变量,也叫简洁赋值语句 87 | 88 | 函数内,`:=` 用在明确类型的地方(但不能显式指定类型),可以用于替代`var`定义。 89 | 90 | 函数外(即包级别)的每个语句都必须以关键字开始(`var`、`func`等),`:=` 不能使用在函数外,如: 91 | 92 | ```go 93 | var k = 3 //OK 94 | k := 3 //报错 95 | 96 | func main() { 97 | k := 3 //OK 98 | var k int = 3 //OK 99 | k int := 3 //报错 100 | var k := 3 //报错 101 | var k int := 3 //报错 102 | } 103 | ``` 104 | 105 | ## 作用域 106 | 107 | 1. 在包级别定义的变量,是全局变量,即在main函数里对这个变量做的修改,或者在其他函数里对这个变量做的修改,都是真实改了这个变量的值 108 | 2. 在函数里(包括 main 函数或其他函数)定义的变量,只在自己函数里有效,若在其他函数里对这个变量做修改,会提示 undefined 109 | 3. 可以存在变量名相同,但作用域不同的多个变量。(个人觉得太晦涩,不建议这么用) 110 | 4. 函数内部的函数(即函数值),有前后顺序要求 111 | 112 | **例1. 包级别的变量能否被函数引用?** 113 | 114 | ```go 115 | var A int = 1 116 | 117 | func foo() { 118 | fmt.Println(A) 119 | } 120 | 121 | func main() { 122 | foo() 123 | } 124 | ``` 125 | 126 | **例2. 包级别的函数是否有顺序要求?** 127 | 128 | ```go 129 | var A int = 1 130 | 131 | func main() { 132 | foo() 133 | } 134 | 135 | func foo() { 136 | fmt.Println(A) 137 | } 138 | ``` 139 | 140 | **例3. 一个函数里的变量能否被包级别的另一个函数引用?** 141 | 142 | ```go 143 | func foo() { 144 | fmt.Println(A) 145 | } 146 | 147 | func main() { 148 | var A int = 1 149 | foo() 150 | } 151 | ``` 152 | 153 | **例4. 一个函数里的变量能否被内嵌的函数值引用?** 154 | 155 | ```go 156 | func main() { 157 | var A int = 1 158 | foo := func () { 159 | fmt.Println(A) 160 | } 161 | foo() 162 | } 163 | ``` 164 | 165 | **例5. 函数值是否有顺序要求?** 166 | 167 | ```go 168 | func main() { 169 | foo := func () { 170 | fmt.Println(A) 171 | } 172 | var A int = 1 173 | foo() 174 | } 175 | ``` 176 | 177 | **例6. 包级别的变量能否在函数内部的函数值里引用?** 178 | 179 | ```go 180 | var A int = 1 181 | func main() { 182 | foo := func () { 183 | A = 2 184 | } 185 | foo() 186 | fmt.Println(A) 187 | } 188 | ``` 189 | 190 | **例7. 函数值嵌套函数值,还能否引用外部变量?** 191 | 192 | ```go 193 | func main() { 194 | A := 1 195 | foo := func () { 196 | bar := func () { 197 | A = 2 198 | } 199 | bar() 200 | } 201 | foo() 202 | fmt.Println(A) 203 | } 204 | ``` 205 | 206 | **例8. 是否可以存在不同作用域,但名字相同的变量(本例十分经典)?** 207 | 208 | ```go 209 | func main() { 210 | A := 1 211 | foo := func () { 212 | A = 2 213 | A := 3 214 | A = 4 215 | fmt.Println(A) 216 | } 217 | foo() 218 | fmt.Println(A) 219 | } 220 | ``` 221 | 222 | **例9. 本例与例8相似,若一开始就声明同名的变量名,那么还能否引用外部同名变量?** 223 | 224 | ```go 225 | func main() { 226 | A := 1 227 | foo := func () { 228 | A := 3 229 | A = 2 230 | fmt.Println(A) 231 | } 232 | foo() 233 | fmt.Println(A) 234 | } 235 | ``` 236 | 237 | ## 常量 238 | 239 | **声明** 240 | 241 | --- 242 | 243 | 用 `const` 声明,无论在包级别还是函数级别。不能用 `:=` 244 | 245 | 常量只能是字符串、布尔、数字类型(整数、浮点数、复数)的值 246 | 247 | ```go 248 | const foo = "bar" 249 | ``` 250 | 251 | 上面声明一个常量,常量名叫foo,值是"bar",foo的类型自动推导为string 252 | 253 | 也可以`#!go const foo string = "bar"`来直接指定类型 254 | 255 | 注意%T是查看类型,即string还是int,而看不出是常量还是变量 256 | 257 | 也可以这么声明 258 | 259 | ```text 260 | const ( 261 | A int = 100 262 | B = "hello" 263 | ) 264 | ``` 265 | 266 | **和 var 的区别** 267 | 268 | `var` 可以不赋值(会自动赋值初始值),而 `const` 必须赋值 269 | 270 | -------------------------------------------------------------------------------- /chapter_13/README.md: -------------------------------------------------------------------------------- 1 | # Go Slice 2 | 3 | Go 语言切片是对数组的抽象。 4 | 5 | Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。 6 | 7 | ```cgo 8 | struct Slice{ 9 | 10 | byte* array; // actual data 11 | uintgo len; // number of elements 12 | uintgo cap; // allocated number of elements 13 | }; 14 | ``` 15 | 16 | - 引用类型。但自身是结构体,值拷贝传递。 17 | - 属性 len 表示可用元素数量,读写操作不能超过该限制。 18 | - 属性 cap 表示最大扩容量,不能超出数组限制。 19 | - 如果 slice == nil,那么 len、cap 都为 0。 20 | 21 | ## 定义切片 22 | 23 | 你可以声明一个未指定大小的数组来定义切片: 24 | 25 | ```go 26 | var identifier []type 27 | ``` 28 | 29 | 切片不需要说明长度。 30 | 31 | 或使用 `make()` 函数来创建切片: 32 | 33 | ```go 34 | var slice1 []type = make([]type, len) 35 | ``` 36 | 37 | 也可以简写为 38 | 39 | ```go 40 | slice1 := make([]type, len) 41 | ``` 42 | 43 | 也可以指定容量,其中capacity为可选参数。 44 | 45 | ```go 46 | make([]T, length, capacity) 47 | ``` 48 | 49 | 这里 len 是数组的长度并且也是切片的初始长度。 50 | 51 | ## 切片初始化 52 | 53 | ```go 54 | s :=[] int {1,2,3 } 55 | ``` 56 | 57 | 直接初始化切片,`[]` 表示是切片类型,`{1,2,3}` 初始化值依次是 `1,2,3`. 其 `cap=len=3` 58 | 59 | ```go 60 | s := arr[:] 61 | ``` 62 | 63 | 初始化切片s,是数组arr的引用 64 | 65 | ```go 66 | s := arr[startIndex:endIndex] 67 | ``` 68 | 69 | 将 arr 中从下标 `startIndex` 到 `endIndex-1` 下的元素创建为一个新的切片 70 | 71 | ```go 72 | s := arr[startIndex:] 73 | ``` 74 | 75 | 缺省 `endIndex` 时将表示一直到 arr 的最后一个元素 76 | 77 | ```go 78 | s := arr[:endIndex] 79 | ``` 80 | 81 | 缺省 `startIndex` 时将表示从 arr 的第一个元素开始 82 | 83 | ```go 84 | s1 := s[startIndex:endIndex] 85 | ``` 86 | 87 | 通过切片 s 初始化切片 `s1` 88 | 89 | ```go 90 | s := make([]int,len,cap) 91 | ``` 92 | 93 | 通过内置函数 `make()` 初始化切片 s, []int 标识为其元素类型为 int 的切片 94 | 95 | ## len() 和 cap() 函数 96 | 97 | 切片是可索引的,并且可以由 len() 方法获取长度。 98 | 99 | 切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。 100 | 101 | 以下为具体实例: 102 | 103 | ```go 104 | package main 105 | 106 | import "fmt" 107 | 108 | func main() { 109 | var numbers = make([]int,3,5) 110 | 111 | printSlice(numbers) 112 | } 113 | 114 | func printSlice(x []int){ 115 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 116 | } 117 | ``` 118 | 119 | 以上实例运行输出结果为: 120 | 121 | ```go 122 | len=3 cap=5 slice=[0 0 0] 123 | ``` 124 | 125 | ## 空(nil)切片 126 | 127 | 一个切片在未初始化之前默认为 nil,长度为 0,实例如下: 128 | 129 | ```go 130 | package main 131 | 132 | import "fmt" 133 | 134 | func main() { 135 | var numbers []int 136 | 137 | printSlice(numbers) 138 | 139 | if(numbers == nil){ 140 | fmt.Printf("切片是空的") 141 | } 142 | } 143 | 144 | func printSlice(x []int){ 145 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 146 | } 147 | ``` 148 | 149 | 以上实例运行输出结果为: 150 | 151 | ```shell 152 | len=0 cap=0 slice=[] 153 | 切片是空的 154 | ``` 155 | 156 | ## 切片截取 157 | 158 | 可以通过设置下限及上限来设置截取切片 `[lower-bound:upper-bound]`,实例如下: 159 | 160 | ```go 161 | package main 162 | 163 | import "fmt" 164 | 165 | func main() { 166 | /* 创建切片 */ 167 | numbers := []int{0,1,2,3,4,5,6,7,8} 168 | printSlice(numbers) 169 | 170 | /* 打印原始切片 */ 171 | fmt.Println("numbers ==", numbers) 172 | 173 | /* 打印子切片从索引1(包含) 到索引4(不包含)*/ 174 | fmt.Println("numbers[1:4] ==", numbers[1:4]) 175 | 176 | /* 默认下限为 0*/ 177 | fmt.Println("numbers[:3] ==", numbers[:3]) 178 | 179 | /* 默认上限为 len(s)*/ 180 | fmt.Println("numbers[4:] ==", numbers[4:]) 181 | 182 | numbers1 := make([]int,0,5) 183 | printSlice(numbers1) 184 | 185 | /* 打印子切片从索引 0(包含) 到索引 2(不包含) */ 186 | number2 := numbers[:2] 187 | printSlice(number2) 188 | 189 | /* 打印子切片从索引 2(包含) 到索引 5(不包含) */ 190 | number3 := numbers[2:5] 191 | printSlice(number3) 192 | 193 | } 194 | 195 | func printSlice(x []int){ 196 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 197 | } 198 | ``` 199 | 200 | 执行以上代码输出结果为: 201 | 202 | ```go 203 | len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8] 204 | numbers == [0 1 2 3 4 5 6 7 8] 205 | numbers[1:4] == [1 2 3] 206 | numbers[:3] == [0 1 2] 207 | numbers[4:] == [4 5 6 7 8] 208 | len=0 cap=5 slice=[] 209 | len=2 cap=9 slice=[0 1] 210 | len=3 cap=7 slice=[2 3 4] 211 | ``` 212 | 213 | ## append() 和 copy() 函数 214 | 215 | 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。 216 | 217 | 下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。 218 | 219 | ```go 220 | package main 221 | 222 | import "fmt" 223 | 224 | func main() { 225 | var numbers []int 226 | printSlice(numbers) 227 | 228 | /* 允许追加空切片 */ 229 | numbers = append(numbers, 0) 230 | printSlice(numbers) 231 | 232 | /* 向切片添加一个元素 */ 233 | numbers = append(numbers, 1) 234 | printSlice(numbers) 235 | 236 | /* 同时添加多个元素 */ 237 | numbers = append(numbers, 2,3,4) 238 | printSlice(numbers) 239 | 240 | /* 创建切片 numbers1 是之前切片的两倍容量*/ 241 | numbers1 := make([]int, len(numbers), (cap(numbers))*2) 242 | 243 | /* 拷贝 numbers 的内容到 numbers1 */ 244 | copy(numbers1,numbers) 245 | printSlice(numbers1) 246 | } 247 | 248 | func printSlice(x []int){ 249 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 250 | } 251 | ``` 252 | 253 | 以上代码执行输出结果为: 254 | 255 | ```shell 256 | len=0 cap=0 slice=[] 257 | len=1 cap=1 slice=[0] 258 | len=2 cap=2 slice=[0 1] 259 | len=5 cap=6 slice=[0 1 2 3 4] 260 | len=5 cap=12 slice=[0 1 2 3 4] 261 | ``` 262 | 263 | -------------------------------------------------------------------------------- /chapter_06/README.md: -------------------------------------------------------------------------------- 1 | # Go 中的命令 2 | 3 | ## Go 命令概览 4 | 5 | ```bash 6 | » go 7 | Go is a tool for managing Go source code. 8 | Usage: 9 | go command [arguments] 10 | The commands are: 11 | build compile packages and dependencies 12 | clean remove object files 13 | doc show documentation for package or symbol 14 | env print Go environment information 15 | bug start a bug report 16 | fix run go tool fix on packages 17 | fmt run gofmt on package sources 18 | generate generate Go files by processing source 19 | get download and install packages and dependencies 20 | install compile and install packages and dependencies 21 | list list packages 22 | run compile and run Go program 23 | test test packages 24 | tool run specified go tool 25 | version print Go version 26 | vet run go tool vet on packages 27 | Use "go help [command]" for more information about a command. 28 | Additional help topics: 29 | c calling between Go and C 30 | buildmode description of build modes 31 | filetype file types 32 | gopath GOPATH environment variable 33 | environment environment variables 34 | importpath import path syntax 35 | packages description of package lists 36 | testflag description of testing flags 37 | testfunc description of testing functions 38 | Use "go help [topic]" for more information about that topic. 39 | ``` 40 | 41 | ## go build 42 | 43 | ```bash 44 | usage: go build [-o output] [-i] [build flags] [packages] 45 | ``` 46 | 47 | 48 | ```bash 49 | go build 50 | go build . 51 | go build hello.go 52 | ``` 53 | 54 | **编译指定包** 55 | 56 | ```bash 57 | go build github.com/biezhi/stringutil 58 | ``` 59 | 60 | **编译所有包** 61 | 62 | ```bash 63 | go build github.com/biezhi/stringutil/... 64 | ``` 65 | 66 | **查看环境信息** 67 | 68 | ```bash 69 | » go env 70 | GOARCH="amd64" 71 | GOBIN="" 72 | GOCACHE="/Users/biezhi/Library/Caches/go-build" 73 | GOEXE="" 74 | GOHOSTARCH="amd64" 75 | GOHOSTOS="darwin" 76 | GOOS="darwin" 77 | GOPATH="/Users/biezhi/course/golang" 78 | GORACE="" 79 | GOROOT="/usr/local/go" 80 | GOTMPDIR="" 81 | GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64" 82 | GCCGO="gccgo" 83 | CC="clang" 84 | CXX="clang++" 85 | CGO_ENABLED="1" 86 | CGO_CFLAGS="-g -O2" 87 | CGO_CPPFLAGS="" 88 | CGO_CXXFLAGS="-g -O2" 89 | CGO_FFLAGS="-g -O2" 90 | CGO_LDFLAGS="-g -O2" 91 | PKG_CONFIG="pkg-config" 92 | GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 93 | ``` 94 | 95 | `GOOS` 是目标操作系统,它的值为: 96 | 97 | - darwin 98 | - freebsd 99 | - linux 100 | - windows 101 | - android 102 | - dragonfly 103 | - netbsd 104 | - openbsd 105 | - plan9 106 | - solaris 107 | 108 | `GOARCH` 指的是目标处理器的架构,目前支持的有: 109 | 110 | - arm 111 | - arm64 112 | - 386 113 | - amd64 114 | - ppc64 115 | - ppc64le 116 | - mips64 117 | - mips64le 118 | - s390x 119 | 120 | **交叉编译** 121 | 122 | ```bash 123 | GOOS=linux GOARCH=amd64 go build github.com/biezhi/hello 124 | ``` 125 | 126 | > 具体组合参考 https://golang.org/doc/install/source#environment 127 | 128 | 更多关于go build的用户可以通过以下命令查看: 129 | 130 | ```bash 131 | go help build 132 | ``` 133 | 134 | ## go clean 135 | 136 | ```bash 137 | usage: go clean [-i] [-r] [-n] [-x] [build flags] [packages] 138 | ``` 139 | 140 | 141 | ```bash 142 | go help clean 143 | ``` 144 | 145 | ## go run 146 | 147 | ```bash 148 | ➜ ~ go help run 149 | usage: go run [build flags] [-exec xprog] gofiles... [arguments...] 150 | Run compiles and runs the main package comprising the named Go source files. 151 | A Go source file is defined to be a file ending in a literal ".go" suffix. 152 | By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. 153 | If the -exec flag is given, 'go run' invokes the binary using xprog: 154 | 'xprog a.out arguments...'. 155 | If the -exec flag is not given, GOOS or GOARCH is different from the system 156 | default, and a program named go_$GOOS_$GOARCH_exec can be found 157 | on the current search path, 'go run' invokes the binary using that program, 158 | for example 'go_nacl_386_exec a.out arguments...'. This allows execution of 159 | cross-compiled programs when a simulator or other execution method is 160 | available. 161 | For more about build flags, see 'go help build'. 162 | ``` 163 | 164 | **接收参数** 165 | 166 | ```go 167 | package main 168 | 169 | import ( 170 | "fmt" 171 | "os" 172 | ) 173 | 174 | func main() { 175 | fmt.Println("Input Args:",os.Args[1]) 176 | } 177 | ``` 178 | 179 | ## go install 180 | 181 | ```bash 182 | ➜ hello go help install 183 | usage: go install [build flags] [packages] 184 | Install compiles and installs the packages named by the import paths, 185 | along with their dependencies. 186 | ``` 187 | 188 | ## go get 189 | 190 | **下载GO依赖库** 191 | 192 | ```bash 193 | go get github.com/biezhi/moe 194 | ``` 195 | 196 | `go get` 支持大多数版本控制系统(VCS),如Git。 197 | 198 | **更新依赖** 199 | 200 | ```bash 201 | go get -u github.com/biezhi/moe 202 | ``` 203 | 204 | **查看进度** 205 | 206 | ```bash 207 | go get -v github.com/biezhi/moe 208 | ``` 209 | 210 | 关于go get 命令的更多用法,可以使用如下命令查看: 211 | 212 | ```bash 213 | go help get 214 | ``` 215 | 216 | ## go fmt 217 | 218 | ```go 219 | func main() { 220 | fmt.Println("Input Args:",os.Args[1]) 221 | } 222 | ``` 223 | 224 | ```bash 225 | » gofmt -h 226 | usage: gofmt [flags] [path ...] 227 | -cpuprofile string 228 | write cpu profile to this file 229 | -d display diffs instead of rewriting files 230 | -e report all errors (not just the first 10 on different lines) 231 | -l list files whose formatting differs from gofmt's 232 | -r string 233 | rewrite rule (e.g., 'a[b:len(a)] -> a[b:]') 234 | -s simplify code 235 | -w write result to (source) file instead of stdout 236 | ``` 237 | 238 | `go fmt` 为我们统一了代码风格,这样我们在整个团队协作中发现,所有代码都是统一的,像一个人写的一样。所以我们的代码在提交到git库之前,一定要使用go fmt进行格式化,现在也有很多编辑器也可以在保存的时候,自动帮我们格式化代码。 239 | 240 | ## go vet 241 | 242 | 1. `Printf` 这类的函数调用时,类型匹配了错误的参数。 243 | 2. 定义常用的方法时,方法签名错误。 244 | 3. 错误的结构标签。 245 | 4. 没有指定字段名的结构字面量。 246 | 247 | ```go 248 | package main 249 | 250 | import ( 251 | "fmt" 252 | ) 253 | 254 | func main() { 255 | fmt.Printf(" 哈哈",3.14) 256 | } 257 | ``` 258 | 259 | ```bash 260 | usage: go vet [-n] [-x] [build flags] [packages] 261 | ``` 262 | 263 | ## go test 264 | 265 | 该命令用于Go的单元测试,它也是接受一个包名作为参数,如果没有指定,使用当前目录。 266 | go test运行的单元测试必须符合go的测试要求。 267 | 268 | 1. 写有单元测试的文件名,必须以_test.go结尾。 269 | 2. 测试文件要包含若干个测试函数。 270 | 3. 这些测试函数要以Test为前缀,还要接收一个*testing.T类型的参数。 271 | 272 | ```go 273 | package main 274 | 275 | import "testing" 276 | 277 | func TestAdd(t *testing.T) { 278 | if Add(1,2) == 3 { 279 | t.Log("1+2=3") 280 | } 281 | if Add(1,1) == 3 { 282 | t.Error("1+1=3") 283 | } 284 | } 285 | ``` 286 | 287 | 更多关于go test命令的使用,请通过如下命令查看。 288 | 289 | ```bash 290 | go help test 291 | ``` 292 | 293 | 以上这些,主要时介绍的go这个开发工具常用的命令,熟悉了之后可以帮助我们更好的开发编码。 294 | 295 | 其他 `go` 命令的介绍,比如 `package` 是什么等等,可以直接使用 `go help [topic]` 命令查看。 296 | 297 | ```bash 298 | Additional help topics: 299 | c calling between Go and C 300 | buildmode description of build modes 301 | filetype file types 302 | gopath GOPATH environment variable 303 | environment environment variables 304 | importpath import path syntax 305 | packages description of package lists 306 | testflag description of testing flags 307 | testfunc description of testing functions 308 | Use "go help [topic]" for more information about that topic. 309 | ``` 310 | -------------------------------------------------------------------------------- /chapter_07/README.md: -------------------------------------------------------------------------------- 1 | # 文件名、关键字与标识符 2 | 3 | ## Go 标记 4 | 5 | Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。 6 | 7 | ```go 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | fmt.Println("hello, world") 14 | } 15 | ``` 16 | 17 | ## 行分隔符 18 | 19 | 在 Go 程序中,一行代表一个语句结束。每个语句不需要像 Java 那样以分号 `;` 结尾,因为这些工作都将由 Go 编译器自动完成。 20 | 21 | 以下为两个语句: 22 | 23 | ```go 24 | fmt.Println("Hello, World!") 25 | fmt.Println("王爵的技术小黑屋:biezhi.me") 26 | ``` 27 | 28 | ## 注释 29 | 30 | 注释不会被编译,每一个包应该有相关注释。 31 | 32 | ```go 33 | // 单行注释 34 | /* 35 | Author by 王爵的技术小黑屋 36 | 我是多行注释 37 | */ 38 | ``` 39 | 40 | 单行注释是最常见的注释形式,你可以在任何地方使用以 `//` 开头的单行注释。多行注释也叫块注释,均已以 `/*` 开头,并以 `*/` 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。 41 | 42 | ## 标识符 43 | 44 | 标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(`A~Z` 和 `a~z`)数字(`0~9`)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。 45 | 46 | 以下是有效的标识符: 47 | 48 | ```bash 49 | mahesh kumar abc move_name a_123 50 | myname50 _temp j a23b9 retVal 51 | ``` 52 | 53 | 以下是无效的标识符: 54 | 55 | - `1ab`(以数字开头) 56 | - `case`(Go 语言的关键字) 57 | - `a+b`(运算符是不允许的) 58 | 59 | ## 关键字 60 | 61 | 下面列举了 Go 代码中会使用到的 25 个关键字或保留字: 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar
100 | 101 | 之所以刻意地将 Go 代码中的关键字保持的这么少,是为了简化在编译过程第一步中的代码解析。和其它语言一样,关键字不能够作标识符使用。 102 | 103 | 除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符,其中包含了基本类型的名称和一些基本的内置函数,它们的作用都将在接下来的章节中进行进一步地讲解。 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
appendboolbytecapclosecomplexcomplex64complex128uint16
copyfalsefloat32float64imagintint8int16uint32
int32int64iotalenmakenewnilpanicuint64
printprintlnrealrecoverstringtrueuintuint8uintptr
151 | 152 | 程序一般由关键字、常量、变量、运算符、类型和函数组成。 153 | 154 | 程序中可能会使用到这些分隔符:括号 `()`,中括号 `[]` 和大括号 `{}`。 155 | 156 | 程序中可能会使用到这些标点符号:`.`、`,`、`;`、`:` 和 `...`。 157 | 158 | 程序的代码通过语句来实现结构化。每个语句不需要像 C 家族中的其它语言一样以分号 `;` 结尾,因为这些工作都将由 Go 编译器自动完成。 159 | 160 | 如果你打算将多个语句写在同一行,它们则必须使用 `;` 人为区分,但在实际开发中我们并不鼓励这种做法。 161 | 162 | ## 包的导入与可见性 163 | 164 | **每一段代码只会被编译一次** 165 | 166 | 一个 Go 程序是通过 `import` 关键字将一组包链接在一起。 167 | 168 | `import "fmt"` 告诉 Go 编译器这个程序需要使用 `fmt` 包(的函数,或其他元素),`fmt` 包实现了格式化 IO(输入/输出)的函数。包名被封闭在半角双引号 `""` 中。如果你打算从已编译的包中导入并加载公开声明的方法,不需要插入已编译包的源代码。 169 | 170 | 如果需要多个包,它们可以被分别导入: 171 | 172 | ```go 173 | import "fmt" 174 | import "os" 175 | ``` 176 | 177 | 或: 178 | 179 | ```go 180 | import "fmt"; import "os" 181 | ``` 182 | 183 | 但是还有更短且更优雅的方法(被称为因式分解关键字,该方法同样适用于 const、var 和 type 的声明或定义): 184 | 185 | ```go 186 | import ( 187 | "fmt" 188 | "os" 189 | ) 190 | ``` 191 | 192 | **可见性规则** 193 | 194 | 当标识符以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用,就像 Java 中的 `public`;标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的,类似 Java 中的 `private`。 195 | 196 | 你可以通过使用包的别名来解决包名之间的名称冲突,或者说根据你的个人喜好对包名进行重新设置,如:`import fm "fmt"`。下面的代码展示了如何使用包的别名: 197 | 198 | ```go 199 | package main 200 | 201 | import fm "fmt" // alias3 202 | 203 | func main() { 204 | fm.Println("hello, world") 205 | } 206 | ``` 207 | 208 | **注意事项** 209 | 210 | 如果你导入了一个包却没有使用它,则会在构建程序时引发错误,如 `imported and not used: os`,这正是遵循了 Go 的格言:“没有不必要的代码!“。 211 | 212 | ## 函数 213 | 214 | 这是定义一个函数最简单的格式: 215 | 216 | ```go 217 | func functionName() 218 | ``` 219 | 220 | 你可以在括号 `()` 中写入 0 个或多个函数的参数(使用逗号 `,` 分隔),每个参数的名称后面必须紧跟着该参数的类型。 221 | 222 | 函数里的代码(函数体)使用大括号 `{}` 括起来。 223 | 224 | 左大括号 `{` 必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会出现错误提示: 225 | 226 | ```bash 227 | build-error: syntax error: unexpected semicolon or newline before { 228 | ``` 229 | 230 | **Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误** 231 | 232 | 右大括号 `}` 需要被放在紧接着函数体的下一行。如果你的函数非常简短,你也可以将它们放在同一行: 233 | 234 | ```go 235 | func Sum(a, b int) int { return a + b } 236 | ``` 237 | 238 | 对于大括号 `{}` 的使用规则在任何时候都是相同的(如:if 语句等)。 239 | 240 | 因此符合规范的函数一般写成如下的形式: 241 | 242 | ```go 243 | func functionName(parameter_list) (return_value_list) { 244 | ... 245 | } 246 | ``` 247 | 248 | 其中: 249 | 250 | - parameter_list 的形式为 (param1 type1, param2 type2, …) 251 | - return_value_list 的形式为 (ret1 type1, ret2 type2, …) 252 | 253 | 只有当某个函数需要被外部包调用的时候才使用大写字母开头,并遵循 Pascal 命名法;否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。 254 | 255 | 下面这一行调用了 `fmt` 包中的 `Println` 函数,可以将字符串输出到控制台,并在最后自动增加换行字符 `\n`: 256 | 257 | ```go 258 | fmt.Println("hello, world") 259 | ``` 260 | 261 | 使用 `fmt.Print("hello, world\n")` 可以得到相同的结果。 262 | 263 | ## Go 程序的一般结构 264 | 265 | 下面的程序可以被顺利编译但什么都做不了,不过这很好地展示了一个 Go 程序的首选结构。这种结构并没有被强制要求,编译器也不关心 main 函数在前还是变量的声明在前,但使用统一的结构能够在从上至下阅读 Go 代码时有更好的体验。 266 | 267 | 总体程序结构如下: 268 | 269 | - 在完成包的 import 之后,开始对常量、变量和类型的定义或声明。 270 | - 如果存在 init 函数的话,则对该函数进行定义(这是一个特殊的函数,每个含有该函数的包都会首先执行这个函数)。 271 | - 如果当前包是 main 包,则定义 main 函数。 272 | - 然后定义其余的函数,首先是类型的方法,接着是按照 main 函数中先后调用的顺序来定义相关函数,如果有很多函数,则可以按照字母顺序来进行排序。 273 | 274 | ```go 275 | package main 276 | 277 | import ( 278 | "fmt" 279 | ) 280 | 281 | const c = "C" 282 | 283 | var v int = 5 284 | 285 | type T struct{} 286 | 287 | func init() { // 包级别的初始化 288 | } 289 | 290 | func main() { 291 | var a int 292 | Func1() 293 | // ... 294 | fmt.Println(a) 295 | } 296 | 297 | func (t T) Method1() { 298 | //... 299 | } 300 | 301 | func Func1() { // 导出函数 Func1 302 | //... 303 | } 304 | ``` 305 | 306 | Go 程序的执行(程序启动)顺序如下: 307 | 308 | 1. 按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程: 309 | 2. 如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。 310 | 3. 然后以相反的顺序在每个包中初始化常量和变量,如果该包含有 init 函数的话,则调用该函数。 311 | 4. 在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。 312 | 313 | ## 类型转换 314 | 315 | 在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数): 316 | 317 | ```go 318 | valueOfTypeB = typeB(valueOfTypeA) 319 | ``` 320 | 321 | **类型 B 的值 = 类型 B(类型 A 的值)** 322 | 323 | 示例: 324 | 325 | ```go 326 | a := 5.0 327 | b := int(a) 328 | ``` 329 | 330 | 但这只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(例如将 int16 转换为 int32)。 331 | 332 | 当从一个取值范围较大的转换到取值范围较小的类型时(例如将 int32 转换为 int16 或将 float32 转换为 int),会发生精度丢失(截断)的情况。当编译器捕捉到非法的类型转换时会引发编译时错误,否则将引发运行时错误。 333 | 334 | 具有相同底层类型的变量之间可以相互转换: 335 | 336 | ```go 337 | var a IZ = 5 338 | c := int(a) 339 | d := IZ(c) 340 | ``` 341 | 342 | ## Go 命名规范 343 | 344 | 干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。Go 语言中对象的命名也应该是简洁且有意义的。 345 | 346 | 像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码的可读性。名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符。 347 | 348 | 返回某个对象的函数或方法的名称一般都是使用名词,没有 `Get...` 之类的字符,如果是用于修改某个对象,则使用 `SetName`。 349 | 350 | 有必须要的话可以使用大小写混合的方式,如 `MixedCaps` 或 `mixedCaps`,而不是使用下划线来分割多个名称。 --------------------------------------------------------------------------------