├── .DS_Store ├── .gitignore ├── .image └── Golang.png ├── .nojekyll ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── _config.yml ├── _coverpage.md ├── _sidebar.md ├── chapter1.md ├── chapter10.md ├── chapter11.md ├── chapter12.md ├── chapter13.md ├── chapter14.md ├── chapter15.md ├── chapter16.md ├── chapter17.md ├── chapter18.md ├── chapter19.md ├── chapter2.md ├── chapter20 lambda.md ├── chapter21 framework.md ├── chapter3.md ├── chapter4.md ├── chapter5.md ├── chapter6.md ├── chapter7.md ├── chapter8.md ├── chapter9.md ├── go_mod.md ├── index.html └── 更新日志.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zoeminghong/go-library/1c214ec04bf9fc31557342e8e100f029592b59fc/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | log/ 2 | logs/ 3 | *.log 4 | tmp/ 5 | *.tmp 6 | *.bak 7 | 8 | target/ 9 | 10 | .mvn/ 11 | mvnw 12 | mvnw.cmd 13 | 14 | .idea/ 15 | out/ 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | 21 | .settings/ 22 | .classpath 23 | .project 24 | .apt_generated 25 | .factorypath 26 | .springBeans 27 | -------------------------------------------------------------------------------- /.image/Golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zoeminghong/go-library/1c214ec04bf9fc31557342e8e100f029592b59fc/.image/Golang.png -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zoeminghong/go-library/1c214ec04bf9fc31557342e8e100f029592b59fc/.nojekyll -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at 617405347@qq.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 2 | 3 | # go-library 4 | 5 | ### 为什么要学习Go 6 | 7 | - 支持并发,这是Go与生俱来的特性 8 | - 简单易学,与C语言类似,学过Java的也推荐学习,性能可以于C比肩 9 | - 静态类型语言,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题 10 | - 跨平台编译 11 | - 内嵌C支持 12 | 13 | ### Go可以做些什么 14 | 15 | - 网络编程,开发Web应用、API应用等 16 | - 内存数据库,前一段时间Google开发的groupcache,couchbase的部分组建 17 | - 云平台,目前国外很多云平台在采用Go开发 18 | 19 | ### 更新日志 20 | 21 | [更新日志](https://github.com/zoeminghong/go-library/blob/master/更新日志.md) 22 | 23 | 该教程主要是针对想要Go入门学习的,够用就好 24 | 25 | 如果想要进一步学习可能不是特别合适 26 | 27 | 欢迎关注我微信公众号,第一时间通知你,更新内容 28 | 29 | ![http://7xp64w.com1.z0.glb.clouddn.com/qrcode_for_gh_3e33976a25c9_258.jpg](http://7xp64w.com1.z0.glb.clouddn.com/qrcode_for_gh_3e33976a25c9_258.jpg) 30 | 31 | 也可以通过新浪微博:**@迹_Jason** 与我交流 32 | 33 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](.image/Golang.png) 2 | 3 | # Go Libray 4 | 5 | > Go 语言入门简易教程 6 | 7 | 轻松上手,一学就会 8 | 9 | [GitHub](https://github.com/zoeminghong/go-library) 10 | [Get Started](#/README) -------------------------------------------------------------------------------- /_sidebar.md: -------------------------------------------------------------------------------- 1 | * [首页](README.md) 2 | 3 | * [环境配置](chapter1.md) 4 | 5 | * [HelloWorld](chapter2.md) 6 | 7 | * [变量与常量](chapter3.md) 8 | 9 | * [基本数据类型与运算符](chapter4.md) 10 | 11 | * [条件语句](chapter5.md) 12 | 13 | * [循环语句](chapter6.md) 14 | 15 | * [defer](chapter7.md) 16 | 17 | * [数组](chapter8.md) 18 | 19 | * [指针](chapter9.md) 20 | 21 | * [结构体](chapter10.md) 22 | 23 | * [切片(Slice)](chapter11.md) 24 | 25 | * [范围(Range)](chapter12.md) 26 | 27 | * [集合(Map)](chapter13.md) 28 | 29 | * [函数](chapter14.md) 30 | 31 | * [方法](chapter15.md) 32 | 33 | * [接口](chapter16.md) 34 | 35 | * [反射](chapter17.md) 36 | 37 | * [错误处理](chapter18.md) 38 | 39 | * [自定义类型](chapter19.md) 40 | 41 | ​ 42 | 43 | -------------------------------------------------------------------------------- /chapter1.md: -------------------------------------------------------------------------------- 1 | ### GO环境配置 2 | 3 | > 首先声明一下,由于本人的条件的限定,以下的所有操作都是在Windows系统下完成。当然,不同的系统也就配置有点差异。 4 | 5 | #### 1、安装Go的安装包 6 | 7 | 下载地址:[GO](https://golang.org/dl/) 8 | 9 | 安装步骤就不在多说什么了,一路到底 10 | 11 | #### 2、配置环境变量 12 | 13 | 我的电脑——右键“属性”——“高级系统设置”——“环境变量”——“系统变量” 14 | 15 | `假设GO安装于D盘根目录` 16 | 17 | **新建:** 18 | 19 | - GOBIN:Go安装路径的bin路径(例:D:\Go\bin) 20 | - GOPATH:Go工程的路径(例:E:\go)。如果有多个,就以分号分隔添加 21 | - GOROOT:Go安装路径(例:D:\Go) 22 | 23 | **修改:** 24 | 25 | - Path:添加上Go安装路径,以分号结尾 26 | 27 | **3、查看是否安装配置成功** 28 | 29 | 在命令行中输入 30 | 31 | ```shell 32 | go env 33 | ``` 34 | 35 | 就可以查看得到go的配置信息 36 | 37 | -------------------------------------------------------------------------------- /chapter10.md: -------------------------------------------------------------------------------- 1 | ### 结构体 2 | 3 | Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 4 | 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。 5 | 6 | ```go 7 | type struct_variable_type struct { 8 | member definition; 9 | member definition; 10 | ... 11 | member definition; 12 | } 13 | ``` 14 | 15 | 一旦定义了结构体类型,它就能用于变量的声明 16 | 17 | ```go 18 | variable_name := structure_variable_type {value1, value2...valuen} 19 | ``` 20 | 21 | **初始化结构体** 22 | 23 | ```go 24 | 1.按照顺序提供初始化值 25 | P := person{"Tom", 25} 26 | 2.通过field:value的方式初始化,这样可以任意顺序 27 | P := person{age:24, name:"Tom"} 28 | 3.new方式,未设置初始值的,会赋予类型的默认初始值 29 | p := new(person) 30 | p.age=24 31 | ``` 32 | 33 | **访问结构体成员** 34 | 35 | ```go 36 | package main 37 | 38 | import "fmt" 39 | 40 | type Books struct { 41 | title string 42 | author string 43 | subject string 44 | book_id int 45 | } 46 | 47 | func main() { 48 | var Book1 Books /* 声明 Book1 为 Books 类型 */ 49 | var Book2 Books /* 声明 Book2 为 Books 类型 */ 50 | 51 | /* book 1 描述 */ 52 | Book1.title = "Go 语言" 53 | Book1.author = "www.runoob.com" 54 | Book1.subject = "Go 语言教程" 55 | Book1.book_id = 6495407 56 | 57 | /* book 2 描述 */ 58 | Book2.title = "Python 教程" 59 | Book2.author = "www.runoob.com" 60 | Book2.subject = "Python 语言教程" 61 | Book2.book_id = 6495700 62 | 63 | /* 打印 Book1 信息 */ 64 | fmt.Printf( "Book 1 title : %s\n", Book1.title) 65 | fmt.Printf( "Book 1 author : %s\n", Book1.author) 66 | fmt.Printf( "Book 1 subject : %s\n", Book1.subject) 67 | fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) 68 | 69 | /* 打印 Book2 信息 */ 70 | fmt.Printf( "Book 2 title : %s\n", Book2.title) 71 | fmt.Printf( "Book 2 author : %s\n", Book2.author) 72 | fmt.Printf( "Book 2 subject : %s\n", Book2.subject) 73 | fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id) 74 | } 75 | ``` 76 | 77 | `结果` 78 | 79 | ```go 80 | Book 1 title : Go 语言 81 | Book 1 author : www.runoob.com 82 | Book 1 subject : Go 语言教程 83 | Book 1 book_id : 6495407 84 | Book 2 title : Python 教程 85 | Book 2 author : www.runoob.com 86 | Book 2 subject : Python 语言教程 87 | Book 2 book_id : 6495700 88 | ``` 89 | 90 | **结构体作为函数参数** 91 | 92 | ```go 93 | ackage main 94 | 95 | import "fmt" 96 | 97 | type Books struct { 98 | title string 99 | author string 100 | subject string 101 | book_id int 102 | } 103 | 104 | func main() { 105 | var Book1 Books /* 声明 Book1 为 Books 类型 */ 106 | var Book2 Books /* 声明 Book2 为 Books 类型 */ 107 | 108 | /* book 1 描述 */ 109 | Book1.title = "Go 语言" 110 | Book1.author = "www.runoob.com" 111 | Book1.subject = "Go 语言教程" 112 | Book1.book_id = 6495407 113 | 114 | /* book 2 描述 */ 115 | Book2.title = "Python 教程" 116 | Book2.author = "www.runoob.com" 117 | Book2.subject = "Python 语言教程" 118 | Book2.book_id = 6495700 119 | 120 | /* 打印 Book1 信息 */ 121 | printBook(Book1) 122 | 123 | /* 打印 Book2 信息 */ 124 | printBook(Book2) 125 | } 126 | 127 | func printBook( book Books ) { 128 | fmt.Printf( "Book title : %s\n", book.title); 129 | fmt.Printf( "Book author : %s\n", book.author); 130 | fmt.Printf( "Book subject : %s\n", book.subject); 131 | fmt.Printf( "Book book_id : %d\n", book.book_id); 132 | } 133 | ``` 134 | 135 | **结构体的匿名字段** 136 | 137 | 在类型中,使用不写字段名的方式,使用另一个类型 138 | 139 | ```go 140 | type Human struct { 141 | name string 142 | age int 143 | weight int 144 | } 145 | type Student struct { 146 | Human // 匿名字段,那么默认Student就包含了Human的所有字段 147 | speciality string 148 | } 149 | func main() { 150 | // 我们初始化一个学生 151 | mark := Student{Human{"Mark", 25, 120}, "Computer Science"} 152 | // 我们访问相应的字段 153 | fmt.Println("His name is ", mark.name) 154 | fmt.Println("His age is ", mark.age) 155 | fmt.Println("His weight is ", mark.weight) 156 | fmt.Println("His speciality is ", mark.speciality) 157 | // 修改对应的备注信息 158 | mark.speciality = "AI" 159 | fmt.Println("Mark changed his speciality") 160 | fmt.Println("His speciality is ", mark.speciality) 161 | // 修改他的年龄信息 162 | fmt.Println("Mark become old") 163 | mark.age = 46 164 | fmt.Println("His age is", mark.age) 165 | // 修改他的体重信息 166 | fmt.Println("Mark is not an athlet anymore") 167 | mark.weight += 60 168 | fmt.Println("His weight is", mark.weight) 169 | } 170 | ``` 171 | 172 | > 可以使用"."的方式进行调用匿名字段中的属性值 173 | > 174 | > 实际就是字段的继承 175 | > 176 | > 其中可以将匿名字段理解为字段名和字段类型都是同一个 177 | > 178 | > 基于上面的理解,所以可以`mark.Human = Human{"Marcus", 55, 220} `和`mark.Human.age -= 1` 179 | > 180 | > 若存在匿名字段中的字段与非匿名字段名字相同,则最外层的优先访问,就近原则 181 | 182 | 通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。 183 | 184 | **结构体指针** 185 | 186 | ```go 187 | var struct_pointer *Books 188 | ``` 189 | 190 | 以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前 191 | 192 | ```go 193 | struct_pointer = &Book1; 194 | ``` 195 | 196 | 使用结构体指针访问结构体成员,使用 "." 操作符 197 | 198 | ```go 199 | struct_pointer.title; 200 | ``` 201 | 202 | ```go 203 | package main 204 | 205 | import "fmt" 206 | 207 | type Books struct { 208 | title string 209 | author string 210 | subject string 211 | book_id int 212 | } 213 | 214 | func main() { 215 | var Book1 Books /* Declare Book1 of type Book */ 216 | var Book2 Books /* Declare Book2 of type Book */ 217 | 218 | /* book 1 描述 */ 219 | Book1.title = "Go 语言" 220 | Book1.author = "www.runoob.com" 221 | Book1.subject = "Go 语言教程" 222 | Book1.book_id = 6495407 223 | 224 | /* book 2 描述 */ 225 | Book2.title = "Python 教程" 226 | Book2.author = "www.runoob.com" 227 | Book2.subject = "Python 语言教程" 228 | Book2.book_id = 6495700 229 | 230 | /* 打印 Book1 信息 */ 231 | printBook(&Book1) 232 | 233 | /* 打印 Book2 信息 */ 234 | printBook(&Book2) 235 | } 236 | func printBook( book *Books ) { 237 | fmt.Printf( "Book title : %s\n", book.title); 238 | fmt.Printf( "Book author : %s\n", book.author); 239 | fmt.Printf( "Book subject : %s\n", book.subject); 240 | fmt.Printf( "Book book_id : %d\n", book.book_id); 241 | } 242 | ``` 243 | 244 | 结构体实例化也可以是这样的 245 | 246 | ```go 247 | package main 248 | 249 | import "fmt" 250 | 251 | type Books struct { 252 | } 253 | 254 | func (s Books) String() string { 255 | return "data" 256 | } 257 | func main() { 258 | fmt.Printf("%v\n", Books{}) 259 | } 260 | ``` 261 | 262 | -------------------------------------------------------------------------------- /chapter11.md: -------------------------------------------------------------------------------- 1 | ### 切片(Slice) 2 | 3 | Go 语言切片是对数组的抽象。 4 | Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大 5 | 6 | 切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由 7 | 8 | 从概念上面来说slice像一个结构体,这个结构体包含了三个元素: 9 | 10 | 1. 指针,指向数组中slice指定的开始位置 11 | 2. 长度,即slice的长度 12 | 3. 最大长度,也就是slice开始位置到数组的最后位置的长度 13 | 14 | **定义切片** 15 | 16 | ```go 17 | var identifier []type 18 | ``` 19 | 20 | 切片不需要说明长度。 21 | 或使用make()函数来创建切片: 22 | 23 | ```go 24 | var slice1 []type = make([]type, len) 25 | 也可以简写为 26 | slice1 := make([]type, len) 27 | ``` 28 | 29 | ```go 30 | make([]T, length, capacity) 31 | ``` 32 | 33 | **初始化** 34 | 35 | ```go 36 | s[0] = 1 37 | s[1] = 2 38 | s[2] = 3 39 | ``` 40 | 41 | ```go 42 | s :=[] int {1,2,3 } 43 | ``` 44 | 45 | ```go 46 | s := arr[startIndex:endIndex] 47 | ``` 48 | 49 | 将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片(**前闭后开**),长度为endIndex-startIndex 50 | 51 | ```go 52 | s := arr[startIndex:] 53 | ``` 54 | 55 | 缺省endIndex时将表示一直到arr的最后一个元素 56 | 57 | ```go 58 | s := arr[:endIndex] 59 | ``` 60 | 61 | 缺省startIndex时将表示从arr的第一个元素开始 62 | 63 | **len() 和 cap() 函数** 64 | 切片是可索引的,并且可以由 len() 方法获取长度 65 | 切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少 66 | 67 | ```go 68 | package main 69 | 70 | import "fmt" 71 | 72 | func main() { 73 | var numbers = make([]int,3,5) 74 | 75 | printSlice(numbers) 76 | } 77 | 78 | func printSlice(x []int){ 79 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 80 | } 81 | ``` 82 | 83 | `结果` 84 | 85 | ```go 86 | len=3 cap=5 slice=[0 0 0] 87 | ``` 88 | 89 | **空切片** 90 | 91 | 一个切片在未初始化之前默认为 nil,长度为 0 92 | 93 | ```go 94 | package main 95 | 96 | import "fmt" 97 | 98 | func main() { 99 | var numbers []int 100 | 101 | printSlice(numbers) 102 | 103 | if(numbers == nil){ 104 | fmt.Printf("切片是空的") 105 | } 106 | } 107 | 108 | func printSlice(x []int){ 109 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 110 | } 111 | ``` 112 | 113 | `结果` 114 | 115 | ```go 116 | len=0 cap=0 slice=[] 117 | 切片是空的 118 | ``` 119 | 120 | ```go 121 | package main 122 | 123 | import "fmt" 124 | 125 | func main() { 126 | /* 创建切片 */ 127 | numbers := []int{0,1,2,3,4,5,6,7,8} 128 | printSlice(numbers) 129 | 130 | /* 打印原始切片 */ 131 | fmt.Println("numbers ==", numbers) 132 | 133 | /* 打印子切片从索引1(包含) 到索引4(不包含)*/ 134 | fmt.Println("numbers[1:4] ==", numbers[1:4]) 135 | 136 | /* 默认下限为 0*/ 137 | fmt.Println("numbers[:3] ==", numbers[:3]) 138 | 139 | /* 默认上限为 len(s)*/ 140 | fmt.Println("numbers[4:] ==", numbers[4:]) 141 | 142 | numbers1 := make([]int,0,5) 143 | printSlice(numbers1) 144 | 145 | /* 打印子切片从索引 0(包含) 到索引 2(不包含) */ 146 | number2 := numbers[:2] 147 | printSlice(number2) 148 | 149 | /* 打印子切片从索引 2(包含) 到索引 5(不包含) */ 150 | number3 := numbers[2:5] 151 | printSlice(number3) 152 | 153 | } 154 | 155 | func printSlice(x []int){ 156 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 157 | } 158 | ``` 159 | 160 | `结果` 161 | 162 | ```go 163 | len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8] 164 | numbers == [0 1 2 3 4 5 6 7 8] 165 | numbers[1:4] == [1 2 3] 166 | numbers[:3] == [0 1 2] 167 | numbers[4:] == [4 5 6 7 8] 168 | len=0 cap=5 slice=[] 169 | len=2 cap=9 slice=[0 1] 170 | len=3 cap=7 slice=[2 3 4] 171 | ``` 172 | 173 | **append() 和 copy() 函数** 174 | 175 | append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice 176 | copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数 177 | 178 | append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩 179 | 余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原 180 | 数组的内容将保持不变;其它引用此数组的slice则不受影响 181 | 182 | 下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法 183 | 184 | ```go 185 | package main 186 | 187 | import "fmt" 188 | 189 | func main() { 190 | var numbers []int 191 | printSlice(numbers) 192 | 193 | /* 允许追加空切片 */ 194 | numbers = append(numbers, 0) 195 | printSlice(numbers) 196 | 197 | /* 向切片添加一个元素 */ 198 | numbers = append(numbers, 1) 199 | printSlice(numbers) 200 | 201 | /* 同时添加多个元素 */ 202 | numbers = append(numbers, 2,3,4) 203 | printSlice(numbers) 204 | 205 | /* 创建切片 numbers1 是之前切片的两倍容量*/ 206 | numbers1 := make([]int, len(numbers), (cap(numbers))*2) 207 | 208 | /* 拷贝 numbers 的内容到 numbers1 */ 209 | copy(numbers1,numbers) 210 | printSlice(numbers1) 211 | } 212 | 213 | func printSlice(x []int){ 214 | fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) 215 | } 216 | ``` 217 | 218 | `结果` 219 | 220 | ```go 221 | len=0 cap=0 slice=[] 222 | len=1 cap=2 slice=[0] 223 | len=2 cap=2 slice=[0 1] 224 | len=5 cap=8 slice=[0 1 2 3 4] 225 | len=5 cap=12 slice=[0 1 2 3 4] 226 | ``` 227 | 228 | > numbers1与numbers两者不存在联系,numbers发生变化时,numbers1是不会随着变化的。也就是说copy方法是不会建立两个切片的联系的 -------------------------------------------------------------------------------- /chapter12.md: -------------------------------------------------------------------------------- 1 | ### 范围(Range) 2 | 3 | Go 语言中 range 关键字用于for循环中迭代数组(array)、切片(slice)、链表(channel)或集合(map)的元素。在数组和切片中它返回元素的索引值,在集合中返回 key-value 对的 key 值 4 | 5 | ```go 6 | package main 7 | import "fmt" 8 | func main() { 9 | //这是我们使用range去求一个slice的和。使用数组跟这个很类似 10 | nums := []int{2, 3, 4} 11 | sum := 0 12 | for _, num := range nums { 13 | sum += num 14 | } 15 | fmt.Println("sum:", sum) 16 | //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。 17 | for i, num := range nums { 18 | if num == 3 { 19 | fmt.Println("index:", i) 20 | } 21 | } 22 | //range也可以用在map的键值对上。 23 | kvs := map[string]string{"a": "apple", "b": "banana"} 24 | for k, v := range kvs { 25 | fmt.Printf("%s -> %s\n", k, v) 26 | } 27 | //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。 28 | for i, c := range "go" { 29 | fmt.Println(i, c) 30 | } 31 | } 32 | ``` 33 | 34 | `结果` 35 | 36 | ```go 37 | sum: 9 38 | index: 1 39 | a -> apple 40 | b -> banana 41 | 0 103 42 | 1 111 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /chapter13.md: -------------------------------------------------------------------------------- 1 | ### 集合(Map) 2 | 3 | Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值 4 | Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的,也是引用类型 5 | 6 | 使用map过程中需要注意的几点: - map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必 7 | 须通过key获取 - map的长度是不固定的,也就是和slice一样,也是一种引用类型 - 内置的len函数同样适用于 8 | map,返回map拥有的key的数量 - map的值可以很方便的修改,通过numbers["one"]=11可以很容易的把key为 9 | one的字典值改为11 10 | 11 | **定义 Map** 12 | 可以使用内建函数 make 也可以使用 map 关键字来定义 Map: 13 | 14 | ```go 15 | /* 声明变量,默认 map 是 nil */ 16 | var map_variable map[key_data_type]value_data_type 17 | 18 | /* 使用 make 函数 */ 19 | map_variable = make(map[key_data_type]value_data_type) 20 | ``` 21 | 22 | ```go 23 | rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 } 24 | ``` 25 | 26 | 如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对 27 | 28 | ```go 29 | package main 30 | 31 | import "fmt" 32 | 33 | func main() { 34 | var countryCapitalMap map[string]string 35 | /* 创建集合 */ 36 | countryCapitalMap = make(map[string]string) 37 | 38 | /* map 插入 key-value 对,各个国家对应的首都 */ 39 | countryCapitalMap["France"] = "Paris" 40 | countryCapitalMap["Italy"] = "Rome" 41 | countryCapitalMap["Japan"] = "Tokyo" 42 | countryCapitalMap["India"] = "New Delhi" 43 | 44 | /* 使用 key 输出 map 值 */ 45 | for country := range countryCapitalMap { 46 | fmt.Println("Capital of",country,"is",countryCapitalMap[country]) 47 | } 48 | 49 | /* 查看元素在集合中是否存在 */ 50 | captial, ok := countryCapitalMap["United States"] 51 | /* 如果 ok 是 true, 则存在,否则不存在 */ 52 | if(ok){ 53 | fmt.Println("Capital of United States is", captial) 54 | }else { 55 | fmt.Println("Capital of United States is not present") 56 | } 57 | } 58 | ``` 59 | 60 | `结果` 61 | 62 | ```go 63 | Capital of France is Paris 64 | Capital of Italy is Rome 65 | Capital of Japan is Tokyo 66 | Capital of India is New Delhi 67 | Capital of United States is not present 68 | ``` 69 | 70 | **delete() 函数** 71 | delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key 72 | 73 | ```go 74 | package main 75 | 76 | import "fmt" 77 | 78 | func main() { 79 | /* 创建 map */ 80 | countryCapitalMap := map[string] string {"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"} 81 | 82 | fmt.Println("原始 map") 83 | 84 | /* 打印 map */ 85 | for country := range countryCapitalMap { 86 | fmt.Println("Capital of",country,"is",countryCapitalMap[country]) 87 | } 88 | 89 | /* 删除元素 */ 90 | delete(countryCapitalMap,"France"); 91 | fmt.Println("Entry for France is deleted") 92 | 93 | fmt.Println("删除元素后 map") 94 | 95 | /* 打印 map */ 96 | for country := range countryCapitalMap { 97 | fmt.Println("Capital of",country,"is",countryCapitalMap[country]) 98 | } 99 | } 100 | ``` 101 | 102 | `结果` 103 | 104 | ```go 105 | 原始 map 106 | Capital of France is Paris 107 | Capital of Italy is Rome 108 | Capital of Japan is Tokyo 109 | Capital of India is New Delhi 110 | Entry for France is deleted 111 | 删除元素后 map 112 | Capital of Italy is Rome 113 | Capital of Japan is Tokyo 114 | Capital of India is New Delhi 115 | ``` 116 | 117 | **ok-idiom** 118 | 119 | 使用ok-idiom获取值,可知道key/value是否存在 120 | 121 | ```go 122 | package main 123 | 124 | import ( 125 | "fmt" 126 | ) 127 | 128 | func main() { 129 | m := make(map[string]int) 130 | m["a"] = 1 131 | x, ok := m["b"] 132 | fmt.Println(x, ok) 133 | x, ok = m["a"] 134 | fmt.Println(x, ok) 135 | } 136 | 137 | ``` 138 | 139 | `结果` 140 | 141 | ```go 142 | 0 false 143 | 1 true 144 | ``` 145 | 146 | **make、new操作** 147 | 148 | make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配 149 | 内建函数new本质上说跟其它语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要: 150 | new返回指针 151 | 152 | 内建函数make(T, args)与new(T)有着不同的功能,make只能创建slice、map和channel,并且返回一个有初 153 | 始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice为nil。对于slice、map和channel来说,make初始化了内部的数据结构,填充适当的值。 154 | 155 | make返回初始化后的(非零)值。 -------------------------------------------------------------------------------- /chapter14.md: -------------------------------------------------------------------------------- 1 | ### 函数 2 | 3 | go语言至少有一个main函数 4 | 5 | ```go 6 | func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { 7 | //这里是处理逻辑代码 8 | //返回多个值 9 | return value1, value2 10 | } 11 | ``` 12 | 13 | - func:函数由 func 开始声明 14 | - funcName:函数名称,函数名和参数列表一起构成了函数签名。 15 | - input1 type1, input2 type2:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。 16 | - output1 type1, output2 type2:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。 17 | - 上面返回值声明了两个变量output1和output2,如果你不想声明也可以,直接就两个类型。 18 | - 如果只有一个返回值且不声明返回值变量,那么你可以省略包括返回值的括号(即一个返回值可以不声明返回类型) 19 | - 函数体:函数定义的代码集合。 20 | 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | func main() { 27 | /* 定义局部变量 */ 28 | var a int = 100 29 | var b int = 200 30 | var ret int 31 | 32 | /* 调用函数并返回最大值 */ 33 | ret = max(a, b) 34 | 35 | fmt.Printf( "最大值是 : %d\n", ret ) 36 | } 37 | 38 | /* 函数返回两个数的最大值 */ 39 | func max(num1, num2 int) int { 40 | /* 定义局部变量 */ 41 | var result int 42 | 43 | if (num1 > num2) { 44 | result = num1 45 | } else { 46 | result = num2 47 | } 48 | return result 49 | } 50 | ``` 51 | 52 | `结果` 53 | 54 | ```go 55 | 最大值是 : 200 56 | ``` 57 | 58 | > Go 函数可以返回多个值 59 | 60 | ```go 61 | package main 62 | 63 | import "fmt" 64 | 65 | func swap(x, y string) (string, string) { 66 | return y, x 67 | } 68 | 69 | func main() { 70 | a, b := swap("Mahesh", "Kumar") 71 | fmt.Println(a, b) 72 | } 73 | ``` 74 | 75 | ```go 76 | func SumAndProduct(A, B int) (add int, Multiplied int) { 77 | add = A+B 78 | Multiplied = A*B 79 | return 80 | } 81 | ``` 82 | 83 | Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参: 84 | 85 | ```go 86 | func myfunc(arg ...int) {} 87 | ``` 88 | 89 | `arg ...int`告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是int。在函数体中,变量arg是一 90 | 个int的slice: 91 | 92 | ```go 93 | for _, n := range arg { 94 | fmt.Printf("And the number is: %d\n", n) 95 | } 96 | ``` 97 | 98 | go语言函数的参数也是存在**值传递**和**引用传递** 99 | 100 | 函数运用场景 101 | 102 | **值传递** 103 | 104 | ```go 105 | package main 106 | 107 | import ( 108 | "fmt" 109 | "math" 110 | ) 111 | 112 | func main(){ 113 | /* 声明函数变量 */ 114 | getSquareRoot := func(x float64) float64 { 115 | return math.Sqrt(x) 116 | } 117 | 118 | /* 使用函数 */ 119 | fmt.Println(getSquareRoot(9)) 120 | 121 | } 122 | ``` 123 | 124 | **引用传递** 125 | 126 | 这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内 127 | 存。只有add1函数知道x变量所在的地址,才能修改x变量的值。所以我们需要将x所在地址&x传入函数,并将函数的参数的类型由int改为*int,即改为指针类型,才能在函数中修改x变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子 128 | 129 | ```go 130 | package main 131 | import "fmt" 132 | //简单的一个函数,实现了参数+1的操作 133 | func add1(a *int) int { // 请注意, 134 | *a = *a+1 // 修改了a的值 135 | return *a // 返回新值 136 | } f 137 | unc main() { 138 | x := 3 139 | fmt.Println("x = ", x) // 应该输出 "x = 3" 140 | x1 := add1(&x) // 调用 add1(&x) 传x的地址 141 | fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4" 142 | fmt.Println("x = ", x) // 应该输出 "x = 4" 143 | } 144 | ``` 145 | 146 | - 传指针使得多个函数能操作同一个对象。 147 | - 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。 148 | - **Go语言中slice,map这三种类型的实现机制类似指针**,所以可以直接传递,而不用取地址后传递 149 | 指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针) 150 | 151 | **闭包** 152 | 153 | Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。 154 | 155 | ```go 156 | package main 157 | 158 | import "fmt" 159 | 160 | func getSequence() func() int { 161 | i:=0 162 | return func() int { 163 | i+=1 164 | return i 165 | } 166 | } 167 | 168 | func main(){ 169 | /* nextNumber 为一个函数,函数 i 为 0 */ 170 | nextNumber := getSequence() 171 | 172 | /* 调用 nextNumber 函数,i 变量自增 1 并返回 */ 173 | fmt.Println(nextNumber()) 174 | fmt.Println(nextNumber()) 175 | fmt.Println(nextNumber()) 176 | 177 | /* 创建新的函数 nextNumber1,并查看结果 */ 178 | nextNumber1 := getSequence() 179 | fmt.Println(nextNumber1()) 180 | fmt.Println(nextNumber1()) 181 | } 182 | ``` 183 | 184 | `结果` 185 | 186 | ```go 187 | 1 188 | 2 189 | 3 190 | 1 191 | 2 192 | ``` 193 | 194 | **函数做为值** 195 | 196 | 在Go中函数也是一种变量,我们可以通过type来定义它 197 | 198 | **同种方法:**参数类型、个数、顺序相同,返回值相同 199 | 200 | ```go 201 | package main 202 | import "fmt" 203 | type testInt func(int) bool // 声明了一个函数类型 204 | func isOdd(integer int) bool { 205 | if integer%2 == 0 { 206 | return false 207 | } 208 | return true 209 | } 210 | func isEven(integer int) bool { 211 | if integer%2 == 0 { 212 | return true 213 | } 214 | return false 215 | } 216 | 217 | func filter(slice []int, f testInt) []int { 218 | var result []int 219 | for _, value := range slice { 220 | if f(value) { 221 | result = append(result, value) 222 | } 223 | } 224 | return result 225 | } 226 | func main(){ 227 | slice := []int {1, 2, 3, 4, 5, 7} 228 | fmt.Println("slice = ", slice) 229 | odd := filter(slice, isOdd) // 函数当做值来传递了 230 | fmt.Println("Odd elements of slice are: ", odd) 231 | even := filter(slice, isEven) // 函数当做值来传递了 232 | fmt.Println("Even elements of slice are: ", even) 233 | } 234 | ``` 235 | 236 | `type testInt func(int) bool`就是将该种函数类型赋值给testInt 237 | 238 | **一般步骤:** 239 | 240 | 1. 定义一个函数类型 241 | 2. 实现定义的函数类型 242 | 3. 作为参数调用 243 | 244 | 有点接口的感觉 245 | 246 | > 函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到testInt这个类型是一个函数类型,然后两个filter函数的参数和返回值与testInt类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活 247 | > 248 | 249 | **Panic和Recover** 250 | Go没有像Java那样的异常机制,它不能抛出异常,而是使用了panic和recover机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有panic的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢? 251 | `Panic` 252 | 是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数F调用panic,函数F的执行被中 253 | 断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,此时程序退出。恐慌可以直接调用panic产 254 | 生。也可以由运行时错误产生,例如访问越界的数组。 255 | `Recover` 256 | 是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在延迟函数中有效。在正常 257 | 的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入恐慌,调用 258 | recover可以捕获到panic的输入值,并且恢复正常的执行。 259 | 260 | 下面这个函数演示了如何在过程中使用panic 261 | 262 | ```go 263 | var user = os.Getenv("USER") 264 | func init() { 265 | if user == "" { 266 | panic("no value for $USER") 267 | } 268 | } 269 | ``` 270 | 271 | 下面这个函数检查作为其参数的函数在执行时是否会产生panic: 272 | 273 | ```go 274 | func throwsPanic(f func()) (b bool) { 275 | defer func() { 276 | if x := recover(); x != nil { 277 | b = true 278 | } 279 | }() 280 | f() //执行函数f,如果f中出现了panic,那么就可以恢复回来 281 | return 282 | } 283 | ``` -------------------------------------------------------------------------------- /chapter15.md: -------------------------------------------------------------------------------- 1 | ### 方法 2 | 3 | Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集 4 | 5 | ```go 6 | func (variable_name variable_data_type) function_name() [return_type]{ 7 | /* 函数体*/ 8 | } 9 | ``` 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | ) 17 | 18 | /* 定义函数 */ 19 | type Circle struct { 20 | radius float64 21 | } 22 | 23 | func main() { 24 | var c1 Circle 25 | c1.radius = 10.00 26 | fmt.Println("Area of Circle(c1) = ", c1.getArea()) 27 | } 28 | 29 | //该 method 属于 Circle 类型对象中的方法 30 | func (c Circle) getArea() float64 { 31 | //c.radius 即为 Circle 类型对象中的属性 32 | return 3.14 * c.radius * c.radius 33 | } 34 | ``` 35 | 36 | `结果` 37 | 38 | ```go 39 | Area of Circle(c1) = 314 40 | ``` 41 | 42 | ```go 43 | package main 44 | 45 | import ( 46 | "fmt" 47 | "math" 48 | ) 49 | 50 | type Rectangle struct { 51 | width, height float64 52 | } 53 | type Circle struct { 54 | radius float64 55 | } 56 | 57 | func (r Rectangle) area() float64 { 58 | return r.width * r.height 59 | } 60 | func (c Circle) area() float64 { 61 | return c.radius * c.radius * math.Pi 62 | } 63 | func main() { 64 | r1 := Rectangle{12, 2} 65 | r2 := Rectangle{9, 4} 66 | c1 := Circle{10} 67 | c2 := Circle{25} 68 | fmt.Println("Area of r1 is: ", r1.area()) 69 | fmt.Println("Area of r2 is: ", r2.area()) 70 | fmt.Println("Area of c1 is: ", c1.area()) 71 | fmt.Println("Area of c2 is: ", c2.area()) 72 | } 73 | ``` 74 | 75 | `结果` 76 | 77 | ```go 78 | Area of r1 is: 24 79 | Area of r2 is: 36 80 | Area of c1 is: 314.1592653589793 81 | Area of c2 is: 1963.4954084936207 82 | ``` 83 | 84 | - 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样 85 | - method里面可以访问接收者的字段 86 | - 调用method通过.访问,就像struct里面访问字段一样 87 | 88 | #### 变量作用域 89 | 90 | 作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。 91 | 92 | Go 语言中变量可以在三个地方声明: 93 | 94 | - 函数内定义的变量称为局部变量 95 | - 函数外定义的变量称为全局变量 96 | - 函数定义中的变量称为形式参数 97 | 98 | **局部变量** 99 | 100 | 在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。 101 | 102 | **全局变量** 103 | 104 | 在函数体外声明的变量称之为全局变量,首字母大写全局变量可以在整个包甚至外部包(被导出后)使用。 105 | 106 | ```go 107 | package main 108 | 109 | import "fmt" 110 | 111 | /* 声明全局变量 */ 112 | var g int 113 | 114 | func main() { 115 | 116 | /* 声明局部变量 */ 117 | var a, b int 118 | 119 | /* 初始化参数 */ 120 | a = 10 121 | b = 20 122 | g = a + b 123 | 124 | fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g) 125 | } 126 | ``` 127 | 128 | `结果` 129 | 130 | ```go 131 | 结果: a = 10, b = 20 and g = 30 132 | ``` 133 | 134 | **形式参数** 135 | 136 | 形式参数会作为函数的局部变量来使用 137 | 138 | **指针作为接收者** 139 | 140 | 若不是以指针作为接收者,实际只是获取了一个copy,而不能真正改变接收者的中的数据 141 | 142 | ```go 143 | func (b *Box) SetColor(c Color) { 144 | b.color = c 145 | } 146 | ``` 147 | 148 | `例` 149 | 150 | ```go 151 | package main 152 | 153 | import ( 154 | "fmt" 155 | ) 156 | 157 | type Rectangle struct { 158 | width, height int 159 | } 160 | 161 | func (r *Rectangle) setVal() { 162 | r.height = 20 163 | } 164 | 165 | func main() { 166 | p := Rectangle{1, 2} 167 | s := p 168 | p.setVal() 169 | fmt.Println(p.height, s.height) 170 | } 171 | ``` 172 | 173 | `结果` 174 | 175 | ```go 176 | 20 2 177 | ``` 178 | 179 | 如果没有那个*,则值就是`2 2` 180 | 181 | **method继承** 182 | 183 | method是可以继承的,如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method 184 | 185 | ```go 186 | package main 187 | 188 | import "fmt" 189 | 190 | type Human struct { 191 | name string 192 | age int 193 | phone string 194 | } 195 | type Student struct { 196 | Human //匿名字段 197 | school string 198 | } 199 | type Employee struct { 200 | Human //匿名字段 201 | company string 202 | } 203 | 204 | func (h *Human) SayHi() { 205 | fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) 206 | } 207 | func main() { 208 | mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} 209 | sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} 210 | mark.SayHi() 211 | sam.SayHi() 212 | } 213 | ``` 214 | 215 | `结果` 216 | 217 | ```go 218 | Hi, I am Mark you can call me on 222-222-YYYY 219 | Hi, I am Sam you can call me on 111-888-XXXX 220 | ``` 221 | 222 | **method重写** 223 | 224 | ```go 225 | package main 226 | 227 | import "fmt" 228 | 229 | type Human struct { 230 | name string 231 | age int 232 | phone string 233 | } 234 | type Student struct { 235 | Human //匿名字段 236 | school string 237 | } 238 | type Employee struct { 239 | Human //匿名字段 240 | company string 241 | } 242 | 243 | //Human定义method 244 | func (h *Human) SayHi() { 245 | fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) 246 | } 247 | 248 | //Employee的method重写Human的method 249 | func (e *Employee) SayHi() { 250 | fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, 251 | e.company, e.phone) //Yes you can split into 2 lines here. 252 | } 253 | func main() { 254 | mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} 255 | sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} 256 | mark.SayHi() 257 | sam.SayHi() 258 | } 259 | ``` 260 | 261 | `结果` 262 | 263 | ```go 264 | Hi, I am Mark you can call me on 222-222-YYYY 265 | Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX 266 | ``` 267 | 268 | #### 总结 269 | 270 | - 方法是可以继承和重写的 271 | - 存在继承关系时,按照就近原则,进行调用 272 | 273 | -------------------------------------------------------------------------------- /chapter16.md: -------------------------------------------------------------------------------- 1 | ### 接口 2 | 3 | 它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口 4 | 5 | 接口定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口 6 | 7 | ```go 8 | /* 定义接口 */ 9 | type interface_name interface { 10 | method_name1 [return_type] 11 | method_name2 [return_type] 12 | method_name3 [return_type] 13 | ... 14 | method_namen [return_type] 15 | } 16 | 17 | /* 定义结构体 */ 18 | type struct_name struct { 19 | /* variables */ 20 | } 21 | 22 | /* 实现接口方法 */ 23 | func (struct_name_variable struct_name) method_name1() [return_type] { 24 | /* 方法实现 */ 25 | } 26 | ... 27 | func (struct_name_variable struct_name) method_namen() [return_type] { 28 | /* 方法实现*/ 29 | } 30 | ``` 31 | 32 | ```go 33 | package main 34 | 35 | import ( 36 | "fmt" 37 | ) 38 | 39 | type Phone interface { 40 | call() 41 | } 42 | 43 | type NokiaPhone struct { 44 | } 45 | 46 | func (nokiaPhone NokiaPhone) call() { 47 | fmt.Println("I am Nokia, I can call you!") 48 | } 49 | 50 | type IPhone struct { 51 | } 52 | 53 | func (iPhone IPhone) call() { 54 | fmt.Println("I am iPhone, I can call you!") 55 | } 56 | 57 | func main() { 58 | var phone Phone 59 | 60 | phone = new(NokiaPhone) 61 | phone.call() 62 | 63 | phone = new(IPhone) 64 | phone.call() 65 | 66 | } 67 | ``` 68 | 69 | `结果` 70 | 71 | ```go 72 | I am Nokia, I can call you! 73 | I am iPhone, I can call you! 74 | ``` 75 | 76 | - interface可以被任意的对象实现 77 | - 一个对象可以实现任意多个interface 78 | - 任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface 79 | 80 | **interface值** 81 | 82 | ```go 83 | package main 84 | 85 | import "fmt" 86 | 87 | type Human struct { 88 | name string 89 | age int 90 | phone string 91 | } 92 | type Student struct { 93 | Human //匿名字段 94 | school string 95 | loan float32 96 | } 97 | type Employee struct { 98 | Human //匿名字段 99 | company string 100 | money float32 101 | } //Human实现Sayhi方法 102 | func (h Human) SayHi() { 103 | fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) 104 | } //Human实现Sing方法 105 | func (h Human) Sing(lyrics string) { 106 | fmt.Println("La la la la...", lyrics) 107 | } //Employee重写Human的SayHi方法 108 | func (e Employee) SayHi() { 109 | fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, 110 | e.company, e.phone) //Yes you can split into 2 lines here. 111 | } 112 | 113 | // Interface Men被Human,Student和Employee实现 114 | // 因为这三个类型都实现了这两个方法 115 | type Men interface { 116 | SayHi() 117 | Sing(lyrics string) 118 | } 119 | 120 | func main() { 121 | mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} 122 | paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} 123 | sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000} 124 | Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000} 125 | //定义Men类型的变量i 126 | var i Men 127 | //i能存储Student 128 | i = mike 129 | fmt.Println("This is Mike, a Student:") 130 | i.SayHi() 131 | i.Sing("November rain") 132 | //i也能存储Employee 133 | i = Tom 134 | fmt.Println("This is Tom, an Employee:") 135 | i.SayHi() 136 | i.Sing("Born to be wild") 137 | //定义了slice Men 138 | fmt.Println("Let's use a slice of Men and see what happens") 139 | x := make([]Men, 3) 140 | //T这三个都是不同类型的元素,但是他们实现了interface同一个接口 141 | x[0], x[1], x[2] = paul, sam, mike 142 | for _, value := range x { 143 | value.SayHi() 144 | } 145 | } 146 | 147 | ``` 148 | 149 | `结果` 150 | 151 | ```go 152 | This is Mike, a Student: 153 | Hi, I am Mike you can call me on 222-222-XXX 154 | La la la la... November rain 155 | This is Tom, an Employee: 156 | Hi, I am Sam, I work at Things Ltd.. Call me on 444-222-XXX 157 | La la la la... Born to be wild 158 | Let's use a slice of Men and see what happens 159 | Hi, I am Paul you can call me on 111-222-XXX 160 | Hi, I am Sam, I work at Golang Inc.. Call me on 444-222-XXX 161 | Hi, I am Mike you can call me on 222-222-XXX 162 | ``` 163 | 164 | 那么interface里面到底能存什么值呢?如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个 165 | interface的任意类型的对象。例如上面例子中,我们定义了一个Men interface类型的变量m,那么m里面可以存 166 | Human、Student或者Employee值 167 | 168 | > 当然,使用指针的方式,也是可以的 169 | > 170 | > 但是,接口对象不能调用实现对象的属性 171 | 172 | **interface函数参数** 173 | 174 | interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思 175 | 考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数 176 | 177 | **嵌入interface** 178 | 179 | ```go 180 | package main 181 | 182 | import "fmt" 183 | 184 | type Human interface { 185 | Len() 186 | } 187 | type Student interface { 188 | Human 189 | } 190 | 191 | type Test struct { 192 | } 193 | 194 | func (h *Test) Len() { 195 | fmt.Println("成功") 196 | } 197 | func main() { 198 | var s Student 199 | s = new(Test) 200 | s.Len() 201 | } 202 | ``` 203 | 204 | `结果` 205 | 206 | ```go 207 | 成功 208 | ``` 209 | 210 | `例` 211 | 212 | ```go 213 | package test 214 | 215 | import ( 216 | "fmt" 217 | ) 218 | 219 | type Controller struct { 220 | M int32 221 | } 222 | 223 | type Something interface { 224 | Get() 225 | Post() 226 | } 227 | 228 | func (c *Controller) Get() { 229 | fmt.Print("GET") 230 | } 231 | 232 | func (c *Controller) Post() { 233 | fmt.Print("POST") 234 | } 235 | ``` 236 | 237 | ```go 238 | package main 239 | 240 | import ( 241 | "fmt" 242 | "test" 243 | ) 244 | 245 | type T struct { 246 | test.Controller 247 | } 248 | 249 | func (t *T) Get() { 250 | //new(test.Controller).Get() 251 | fmt.Print("T") 252 | } 253 | func (t *T) Post() { 254 | fmt.Print("T") 255 | } 256 | func main() { 257 | var something test.Something 258 | something = new(T) 259 | var t T 260 | t.M = 1 261 | // t.Controller.M = 1 262 | something.Get() 263 | } 264 | ``` 265 | 266 | `结果` 267 | 268 | ```go 269 | T 270 | ``` 271 | 272 | Controller实现了所有的Something接口方法,当结构体T中调用Controller结构体的时候,T就相当于Java中的继承,T继承了Controller,因此,T可以不用重写所有的Something接口中的方法,因为父构造器已经实现了接口。 273 | 274 | 如果Controller没有实现Something接口方法,则T要调用Something中方法,就要实现其所有方法。 275 | 276 | 如果`something = new(test.Controller)`则调用的是Controller中的Get方法。 277 | 278 | T可以使用Controller结构体中定义的变量 279 | 280 | #### 总结 281 | 282 | 接口对象不能调用接口实现对象的属性 283 | 284 | -------------------------------------------------------------------------------- /chapter17.md: -------------------------------------------------------------------------------- 1 | ### 反射 2 | 3 | 所谓反射就是动态运行时的状态。我们一般用到的包是reflect包 4 | 5 | 使用reflect一般分成三步: 6 | 7 | 首先需要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数) 8 | 9 | ```go 10 | t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素 11 | v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值 12 | ``` 13 | 14 | 获取反射值能返回相应的类型和数值 15 | 16 | ```go 17 | var x float64 = 3.4 18 | v := reflect.ValueOf(x) 19 | fmt.Println("type:", v.Type()) 20 | fmt.Println("kind is float64:", v.Kind() == reflect.Float64) 21 | fmt.Println("value:", v.Float()) 22 | ``` 23 | 24 | 如果是struct的话,可以使用Elem() 25 | 26 | ```go 27 | tag := t.Elem().Field(0).Tag //获取定义在struct里面的Tag属性 28 | name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值 29 | ``` 30 | 31 | 修改 32 | 33 | ```go 34 | var x float64 = 3.4 35 | p := reflect.ValueOf(&x) 36 | v := p.Elem()//必须的步骤 37 | v.SetFloat(7.1) 38 | ``` -------------------------------------------------------------------------------- /chapter18.md: -------------------------------------------------------------------------------- 1 | ### 错误处理 2 | 3 | Go 语言通过内置的错误接口提供了非常简单的错误处理机制。 4 | 5 | ```go 6 | type error interface { 7 | Error() string 8 | } 9 | ``` 10 | 11 | 我们可以在编码中通过实现 error 接口类型来生成错误信息。 12 | 函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息 13 | 14 | ```go 15 | func Sqrt(f float64) (float64, error) { 16 | if f < 0 { 17 | return 0, errors.New("math: square root of negative number") 18 | } 19 | // 实现 20 | } 21 | ``` 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | ) 29 | 30 | // 定义一个 DivideError 结构 31 | type DivideError struct { 32 | dividee int 33 | divider int 34 | } 35 | 36 | // 实现 `error` 接口 37 | func (de *DivideError) Error() string { 38 | strFormat := ` 39 | Cannot proceed, the divider is zero. 40 | dividee: %d 41 | divider: 0 42 | ` 43 | return fmt.Sprintf(strFormat, de.dividee) 44 | } 45 | 46 | // 定义 `int` 类型除法运算的函数 47 | func Divide(varDividee int, varDivider int) (result int, errorMsg string) { 48 | if varDivider == 0 { 49 | dData := DivideError{ 50 | dividee: varDividee, 51 | divider: varDivider, 52 | } 53 | errorMsg = dData.Error() 54 | return 55 | } else { 56 | return varDividee / varDivider, "" 57 | } 58 | 59 | } 60 | 61 | func main() { 62 | 63 | // 正常情况 64 | if result, errorMsg := Divide(100, 10); errorMsg == "" { 65 | fmt.Println("100/10 = ", result) 66 | } 67 | // 当被除数为零的时候会返回错误信息 68 | if _, errorMsg := Divide(100, 0); errorMsg != "" { 69 | fmt.Println("errorMsg is: ", errorMsg) 70 | } 71 | 72 | } 73 | ``` 74 | 75 | `结果` 76 | 77 | ```go 78 | 100/10 = 10 79 | errorMsg is: 80 | Cannot proceed, the divider is zero. 81 | dividee: 100 82 | divider: 0 83 | ``` -------------------------------------------------------------------------------- /chapter19.md: -------------------------------------------------------------------------------- 1 | ### 自定义类型 2 | 3 | 使用关键字type就可以开始自定义类型,包括基于现有基础类型创建,或者是结构体、函数类型等 4 | 5 | ```go 6 | type name int8 7 | ``` 8 | 9 | 多个type还可以合成一个组 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | ) 17 | 18 | type ( 19 | user struct { 20 | name string 21 | age uint8 22 | } 23 | event func(string) bool 24 | ) 25 | 26 | func main() { 27 | u := user{"Jason", 20} 28 | fmt.Println(u) 29 | var f event = func(s string) bool { 30 | fmt.Println(s) 31 | return s != "" 32 | } 33 | f("abc") 34 | 35 | } 36 | 37 | ``` 38 | 39 | `结果` 40 | 41 | ```go 42 | {Jason 20} 43 | abc 44 | ``` 45 | 46 | 注意 47 | 48 | ```go 49 | package main 50 | 51 | import ( 52 | "fmt" 53 | ) 54 | 55 | type data int 56 | 57 | func main() { 58 | var d data = 10 59 | var x int = d 60 | fmt.Println(x) 61 | fmt.Println(d == x) 62 | 63 | } 64 | ``` 65 | 66 | > d虽然底层的数据类型是int,但其与int是两个不同的概念,不能作为相同的数据类型看待 67 | 68 | **未命名类型** 69 | 70 | 与有明确标识符的bool、int、string等类型相比,数组、切片、字典、通道等类型与具体元素类型或长度等属性有关,故称作未命名类型。当然,可用type为其提供具体的名称,将其改变为命名类型 71 | 72 | - 具有相同基类型的指针 73 | - 具有相同元素类型和长度的数组 74 | - 具有相同元素类型的切片 75 | - 具有相同键值类型的字典 76 | - 具有相同数据类型及操作方向的通道 77 | - 具有相同字段序列(字段名、字段类型、标签、以及字段顺序)的结构体 78 | - 具有相同签名(参数的返回值列表,不包括参数名)的函数 79 | - 具有相同方法集(方法名、方法签名、不包括顺序)的接口 80 | 81 | 未命名类型转换规则 82 | 83 | - 所属类型相同 84 | - 基础类型相同,且其中一个是未命名类型 85 | - 数据类型相同,将双向通道给单向通道,且其中一个为未命名类型 86 | - 将默认值nil赋值给切片、字典、通道、指针、函数或接口 87 | - 对象实现了目标接口 88 | 89 | ```go 90 | package main 91 | 92 | import ( 93 | "fmt" 94 | ) 95 | 96 | type data [2]int 97 | 98 | func main() { 99 | var d data = [2]int{1, 2} 100 | fmt.Println(d) 101 | a := make(chan int, 2) 102 | var b chan<- int = a 103 | b <- 2 104 | 105 | } 106 | ``` 107 | 108 | `结果` 109 | 110 | ```go 111 | [1 2] 112 | ``` -------------------------------------------------------------------------------- /chapter2.md: -------------------------------------------------------------------------------- 1 | ### 关于GO 2 | 3 | ```go 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | /* 输出 */ 10 | fmt.Println("Hello, World!") 11 | } 12 | ``` 13 | #### package 14 | 15 | - 在同一个包下面的文件属于同一个工程文件,不用`import`包,可以直接使用 16 | - 在同一个包下面的所有文件的package名,都是一样的 17 | - 在同一个包下面的文件`package`名都建议设为是该目录名,但也可以不是 18 | 19 | #### import 20 | 21 | import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包的函数,fmt 包实现了格式化 IO(输入/输出)的函数 22 | 23 | 可以是相对路径也可以是绝对路径,推荐使用绝对路径(起始于工程根目录) 24 | 25 | 1. 点操作 26 | 我们有时候会看到如下的方式导入包 27 | 28 | ```go 29 | import( 30 | . "fmt" 31 | ) 32 | ``` 33 | 34 | 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调 35 | 36 | 用的`fmt.Println("hello world")`可以省略的写成`Println("hello world")` 37 | 38 | 2. 别名操作 39 | 别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字 40 | 41 | ```go 42 | import( 43 | f "fmt" 44 | ) 45 | ``` 46 | 47 | 别名操作的话调用包函数时前缀变成了我们的前缀,即`f.Println("hello world")` 48 | 49 | 3. _操作 50 | 这个操作经常是让很多人费解的一个操作符,请看下面这个import 51 | 52 | ```go 53 | import ( 54 | "database/sql" 55 | _ "github.com/ziutek/mymysql/godrv" 56 | ) 57 | ``` 58 | 59 | _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数 60 | 61 | #### main与init 62 | 63 | - 这两个函数在定义时不能有任何的参数和返回值 64 | - 虽然一个package里面可以写任意多个init函数,但推荐只用一个 65 | - Go程序会自动调用init()和main() 66 | - 每个package中的init函数都是可选的,但package main就必须包含一个main函数 67 | - 先调用init函数,再调用main函数 68 | - 运行程序,必须要运行存在main函数的go文件 69 | 70 | `初始化顺序:` 71 | 72 | 程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。 73 | 74 | #### 注释 75 | 76 | - 单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释 77 | - 多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段 78 | 79 | #### 标识符 80 | 81 | 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就**可以被外部包的代码所使用**(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);**标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的**(像面向对象语言中的 private ) 82 | 83 | #### 语句的结尾 84 | 85 | Go语言中是不需要类似于Java需要冒号结尾,默认一行就是一条数据 86 | 87 | 如果你打算将多个语句写在同一行,它们则必须使用 **;** 人为区分 88 | 89 | -------------------------------------------------------------------------------- /chapter20 lambda.md: -------------------------------------------------------------------------------- 1 | # Lambda 2 | 3 | 作为从Java转Go的开发者,已经习惯Java的Lambda表达式方式。Go 下是否存在如下的实现。 4 | 5 | ## Functional 6 | 7 | 如下是一种通过匿名函数实现方式,下面是网上找的例子。 8 | 9 | ```go 10 | func Test(n int, x string) { 11 | fmt.Println(n, x) 12 | } 13 | func main() { 14 | myFunc := func(x string) { Test(123, x) } 15 | myFunc("hello") 16 | } 17 | ``` 18 | 19 | ## Stream API 20 | 21 | Stream API,我找到如下几种开源实现方式: 22 | 23 | - https://github.com/vladimirvivien/automi 24 | - https://github.com/jucardi/go-streams 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /chapter21 framework.md: -------------------------------------------------------------------------------- 1 | # Framework 2 | 3 | ## Web 4 | 5 | 参照:https://github.com/mingrammer/go-web-framework-stars 6 | 7 | - Gin 8 | - Beego 9 | - Kit 10 | 11 | ## ORM 12 | 13 | - Gorm 14 | - GeeORM -------------------------------------------------------------------------------- /chapter3.md: -------------------------------------------------------------------------------- 1 | ### 变量与常量 2 | 3 | #### 变量与常量的定义 4 | 5 | > 以字母或下划线开头,由一个或多个字母、数字、下划线组成 6 | 7 | #### 变量声明 8 | 9 | 第一种,指定变量类型,声明后若不赋值,使用默认值 10 | 11 | ```go 12 | var name type 13 | name = value 14 | ``` 15 | 16 | 第二种,根据值自行判定变量类型 17 | 18 | ```go 19 | var name = value 20 | ``` 21 | 22 | 第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误 23 | 24 | ```go 25 | name := value 26 | 27 | // 例如 28 | var a int = 10 29 | var b = 10 30 | c : = 10 31 | ``` 32 | 33 | > 这种方式它只能被用在函数体内,而不可以用于全局变量的声明与赋值 34 | 35 | `示例` 36 | 37 | ```go 38 | package main 39 | var a = "Hello" 40 | var b string = "World" 41 | var c bool 42 | 43 | func main(){ 44 | println(a, b, c) 45 | } 46 | ``` 47 | 48 | `结果` 49 | 50 | ```go 51 | Hello World false 52 | ``` 53 | 54 | #### 多变量声明 55 | 56 | 第一种,以逗号分隔,声明与赋值分开,若不赋值,存在默认值 57 | 58 | ```go 59 | var name1, name2, name3 type 60 | name1, name2, name3 = v1, v2, v3 61 | ``` 62 | 63 | 第二种,直接赋值,下面的变量类型可以是不同的类型 64 | 65 | ```go 66 | var name1, name2, name3 = v1, v2, v3 67 | ``` 68 | 69 | 第三种,集合类型 70 | 71 | ```go 72 | var ( 73 | name1 type1 74 | name2 type2 75 | ) 76 | ``` 77 | 78 | #### 注意事项 79 | 80 | 如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。 81 | 82 | 如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。 83 | 84 | 如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a: 85 | 86 | ```go 87 | func main() { 88 | var a string = "abc" 89 | fmt.Println("hello, world") 90 | } 91 | ``` 92 | 93 | 尝试编译这段代码将得到错误 a declared and not used 94 | 95 | 此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用 96 | 97 | 在同一个作用域中,已存在同名的变量,则之后的声明初始化,则退化为赋值操作。但这个前提是,最少要有一个新的变量被定义,且在同一作用域,例如,下面的y就是新定义的变量 98 | 99 | ```go 100 | package main 101 | 102 | import ( 103 | "fmt" 104 | ) 105 | 106 | func main() { 107 | x := 140 108 | fmt.Println(&x) 109 | x, y := 200, "abc" 110 | fmt.Println(&x, x) 111 | fmt.Print(y) 112 | } 113 | 114 | ``` 115 | 116 | `结果` 117 | 118 | ```go 119 | 0xc04200a2b0 120 | 0xc04200a2b0 200 121 | abc 122 | ``` 123 | 124 | **空白标识符 _ 也被用于抛弃值**,如值 5 在:_, b = 5, 7 中被抛弃 125 | 126 | _ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值 127 | 128 | 并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1) 129 | 130 | #### 常量声明 131 | 132 | 常量是一个简单值的标识符,在程序运行时,不会被修改的量。 133 | 134 | 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型 135 | 136 | 不曾使用的常量,在编译的时候,是不会报错的 137 | 138 | 显示指定类型的时候,必须确保常量左右值类型一致,需要时可做显示类型转换。这与变量就不一样了,变量是可以是不同的类型值 139 | 140 | ```go 141 | const identifier [type] = value 142 | ``` 143 | 144 | ```go 145 | 显式类型定义: const b string = "abc" 146 | 隐式类型定义: const b = "abc" 147 | ``` 148 | 149 | ```go 150 | package main 151 | 152 | import "fmt" 153 | 154 | func main() { 155 | const LENGTH int = 10 156 | const WIDTH int = 5 157 | var area int 158 | const a, b, c = 1, false, "str" //多重赋值 159 | 160 | area = LENGTH * WIDTH 161 | fmt.Printf("面积为 : %d", area) 162 | println() 163 | println(a, b, c) 164 | } 165 | ``` 166 | 167 | `结果` 168 | 169 | ```go 170 | 面积为 : 50 171 | 1 false str 172 | ``` 173 | 174 | 常量可以作为枚举 175 | 176 | ```go 177 | const ( 178 | Unknown = 0 179 | Female = 1 180 | Male = 2 181 | ) 182 | ``` 183 | 常量组中如不指定类型和初始化值,则与上一行非空常量右值相同 184 | 185 | ```go 186 | package main 187 | 188 | import ( 189 | "fmt" 190 | ) 191 | 192 | func main() { 193 | const ( 194 | x uint16 = 16 195 | y 196 | s = "abc" 197 | z 198 | ) 199 | fmt.Printf("%T,%v\n", y, y) 200 | fmt.Printf("%T,%v\n", z, z) 201 | } 202 | ``` 203 | 204 | `结果` 205 | 206 | ```go 207 | uint16,16 208 | string,abc 209 | ``` 210 | 211 | ### iota 212 | 213 | iota,特殊常量,可以认为是一个可以被编译器修改的常量 214 | 215 | > 在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1 216 | 217 | iota 可以被用作枚举值: 218 | 219 | ```go 220 | const ( 221 | a = iota 222 | b = iota 223 | c = iota 224 | ) 225 | ``` 226 | 227 | 第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式: 228 | 229 | ```go 230 | const ( 231 | a = iota 232 | b 233 | c 234 | ) 235 | ``` 236 | 237 | **iota 用法** 238 | 239 | ```go 240 | package main 241 | 242 | import "fmt" 243 | 244 | func main() { 245 | const ( 246 | a = iota //0 247 | b //1 248 | c //2 249 | d = "ha" //独立值,iota += 1 250 | e //"ha" iota += 1 251 | f = 100 //iota +=1 252 | g //100 iota +=1 253 | h = iota //7,恢复计数 254 | i //8 255 | ) 256 | fmt.Println(a,b,c,d,e,f,g,h,i) 257 | } 258 | ``` 259 | 260 | `结果` 261 | 262 | ``` 263 | 0 1 2 ha ha 100 100 7 8 264 | ``` 265 | 266 | 如果中断iota自增,则必须显式恢复。且后续自增值按行序递增 267 | 268 | 自增默认是int类型,可以自行进行显示指定类型 269 | 270 | 数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址 -------------------------------------------------------------------------------- /chapter4.md: -------------------------------------------------------------------------------- 1 | ### Go基本数据类型与运算符 2 | 3 | #### 基本数据类型 4 | 5 | ##### 数值类型型 6 | 7 | **1、整型** 8 | 9 | - uint8 10 | 无符号 8 位整型 (0 到 255) 11 | - uint16 12 | 无符号 16 位整型 (0 到 65535) 13 | - uint32 14 | 无符号 32 位整型 (0 到 4294967295) 15 | - uint64 16 | 无符号 64 位整型 (0 到 18446744073709551615) 17 | - int8 18 | 有符号 8 位整型 (-128 到 127) 19 | - int16 20 | 有符号 16 位整型 (-32768 到 32767) 21 | - int32 22 | 有符号 32 位整型 (-2147483648 到 2147483647) 23 | - int64 24 | 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) 25 | 26 | **2、浮点型** 27 | 28 | - float32 29 | 30 | IEEE-754 32位浮点型数 31 | 32 | - float64 33 | 34 | IEEE-754 64位浮点型数 35 | 36 | - complex64 37 | 38 | 32 位实数和虚数 39 | 40 | - complex128 41 | 42 | 64 位实数和虚数 43 | 44 | **3、其他** 45 | 46 | - byte 47 | 48 | 类似 uint8 49 | 50 | - rune 51 | 52 | 类似 int32 53 | 54 | - uint 55 | 56 | 32 或 64 位 57 | 58 | - int 59 | 60 | 与 uint 一样大小 61 | 62 | - uintptr 63 | 64 | 无符号整型,用于存放一个指针 65 | 66 | ##### 布尔型 67 | 68 | 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true 69 | 70 | ##### 字符串型 71 | 72 | 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本 73 | 74 | ```go 75 | var str string 76 | str = "Hello World" 77 | ``` 78 | 79 | #### 运算符 80 | 81 | ##### 算术运算符 82 | 83 | ```go 84 | + - * / %(求余) ++ -- 85 | ``` 86 | 87 | ##### 关系运算符 88 | 89 | ```go 90 | == != > < >= <= 91 | ``` 92 | 93 | ##### 逻辑运算符 94 | 95 | | 运算符 | 描述 | 96 | | ---- | ---------------------------------------- | 97 | | && | 所谓逻辑与运算符。如果两个操作数都非零,则条件变为真 | 98 | | \|\| | 所谓的逻辑或操作。如果任何两个操作数是非零,则条件变为真 | 99 | | ! | 所谓逻辑非运算符。使用反转操作数的逻辑状态。如果条件为真,那么逻辑非操后结果为假 | 100 | 101 | ##### 位运算符 102 | 103 | | A | B | A&B | A\|B | A^B | 104 | | ---- | ---- | ---- | ---- | ---- | 105 | | 0 | 0 | 0 | 0 | 0 | 106 | | 0 | 1 | 0 | 1 | 1 | 107 | | 1 | 1 | 1 | 1 | 0 | 108 | | 1 | 0 | 0 | 1 | 1 | 109 | 110 | 这里最难理解的就是^了,只要认为AB两者都相同的时候,为0,其他都为1 111 | 112 | | 运算 | 描述 | 示例 | 113 | | ---- | --------------------------------- | ---------------------------------- | 114 | | & | 二进制与操作副本位的结果,如果它存在于两个操作数 | (A & B) = 12, 也就是 0000 1100 | 115 | | \| | 二进制或操作副本,如果它存在一个操作数 | (A \| B) = 61, 也就是 0011 1101 | 116 | | ^ | 二进制异或操作副本,如果它被设置在一个操作数但不能同时是比特 | (A ^ B) = 49, 也就是 0011 0001 | 117 | | << | 二进制左移位运算符。左边的操作数的值向左移动由右操作数指定的位数 | A << 2 will give 240 也就是 1111 0000 | 118 | | >> | 二进制向右移位运算符。左边的操作数的值由右操作数指定的位数向右移动 | A >> 2 = 15 也就是 0000 1111 | 119 | 120 | ##### 赋值运算符 121 | 122 | | 运算符 | 描述 | 示例 | 123 | | ---- | -------------------------------- | --------------------------------- | 124 | | = | 简单的赋值操作符,分配值从右边的操作数左侧的操作数 | C = A + B 将分配A + B的值到C | 125 | | += | 相加并赋值运算符,它增加了右操作数左操作数和分配结果左操作数 | C += A 相当于 C = C + A | 126 | | -= | 减和赋值运算符,它减去右操作数从左侧的操作数和分配结果左操作数 | C -= A 相当于 C = C - A | 127 | | *= | 乘法和赋值运算符,它乘以右边的操作数与左操作数和分配结果左操作数 | C *= A is equivalent to C = C * A | 128 | | /= | 除法赋值运算符,它把左操作数与右操作数和分配结果左操作数 | C /= A 相当于 C = C / A | 129 | | %= | 模量和赋值运算符,它需要使用两个操作数的模量和分配结果左操作数 | C %= A 相当于 C = C % A | 130 | | <<= | 左移位并赋值运算符 | C <<= 2 相同于 C = C << 2 | 131 | | >>= | 向右移位并赋值运算符 | C >>= 2 相同于 C = C >> 2 | 132 | | &= | 按位与赋值运算符 | C &= 2 相同于 C = C & 2 | 133 | | ^= | 按位异或并赋值运算符 | C ^= 2 相同于 C = C ^ 2 | 134 | | \|= | 按位或并赋值运算符 | C \|= 2 相同于 C = C \| 2 | -------------------------------------------------------------------------------- /chapter5.md: -------------------------------------------------------------------------------- 1 | ### 条件语句 2 | 3 | #### If语句 4 | 5 | 格式: 6 | 7 | ```go 8 | if 布尔表达式 { 9 | /* 在布尔表达式为 true 时执行 */ 10 | } 11 | ``` 12 | 13 | ```go 14 | if 布尔表达式 { 15 | /* 在布尔表达式为 true 时执行 */ 16 | } else { 17 | /* 在布尔表达式为 false 时执行 */ 18 | } 19 | ``` 20 | 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | func main() { 27 | /* 定义局部变量 */ 28 | var a int = 10 29 | 30 | /* 使用 if 语句判断布尔表达式 */ 31 | if a < 20 { 32 | /* 如果条件为 true 则执行以下语句 */ 33 | fmt.Printf("a 小于 20\n" ) 34 | } 35 | fmt.Printf("a 的值为 : %d\n", a) 36 | } 37 | ``` 38 | 39 | #### switch 语句 40 | 41 | switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。 42 | switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加break。 43 | 44 | 而如果switch没有表达式,它会匹配true 45 | 46 | Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。 47 | 48 | 变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 49 | 您可以**同时测试多个可能符合条件的值,使用逗号分割它们**,例如:case val1, val2, val3。 50 | 51 | ```go 52 | switch var1 { 53 | case val1: 54 | ... 55 | case val2: 56 | ... 57 | default: 58 | ... 59 | } 60 | ``` 61 | 62 | ```go 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | /* 定义局部变量 */ 69 | var grade string = "B" 70 | var marks int = 90 71 | 72 | switch marks { 73 | case 90: grade = "A" 74 | case 80: grade = "B" 75 | case 50,60,70 : grade = "C" 76 | default: grade = "D" 77 | } 78 | 79 | switch { 80 | case grade == "A" : 81 | fmt.Printf("优秀!\n" ) 82 | case grade == "B", grade == "C" : 83 | fmt.Printf("良好\n" ) 84 | case grade == "D" : 85 | fmt.Printf("及格\n" ) 86 | case grade == "F": 87 | fmt.Printf("不及格\n" ) 88 | default: 89 | fmt.Printf("差\n" ); 90 | } 91 | fmt.Printf("你的等级是 %s\n", grade ); 92 | } 93 | ``` 94 | 95 | 如需贯通后续的case,就添加fallthrough 96 | 97 | ```go 98 | package main 99 | 100 | import ( 101 | "fmt" 102 | ) 103 | 104 | type data [2]int 105 | 106 | func main() { 107 | switch x := 5; x { 108 | default: 109 | fmt.Println(x) 110 | case 5: 111 | x += 10 112 | fmt.Println(x) 113 | fallthrough 114 | case 6: 115 | x += 20 116 | fmt.Println(x) 117 | 118 | } 119 | 120 | } 121 | 122 | ``` 123 | 124 | `结果` 125 | 126 | ```go 127 | 15 128 | 35 129 | ``` 130 | 131 | #### Type Switch 132 | 133 | switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。 134 | 135 | ```go 136 | switch x.(type){ 137 | case type: 138 | statement(s); 139 | case type: 140 | statement(s); 141 | /* 你可以定义任意个数的case */ 142 | default: /* 可选 */ 143 | statement(s); 144 | } 145 | ``` 146 | 147 | ```go 148 | package main 149 | 150 | import "fmt" 151 | 152 | func main() { 153 | var x interface{} 154 | 155 | switch i := x.(type) { 156 | case nil: 157 | fmt.Printf(" x 的类型 :%T",i) 158 | case int: 159 | fmt.Printf("x 是 int 型") 160 | case float64: 161 | fmt.Printf("x 是 float64 型") 162 | case func(int) float64: 163 | fmt.Printf("x 是 func(int) 型") 164 | case bool, string: 165 | fmt.Printf("x 是 bool 或 string 型" ) 166 | default: 167 | fmt.Printf("未知型") 168 | } 169 | } 170 | ``` 171 | 172 | `结果` 173 | 174 | ```go 175 | x 的类型 : 176 | ``` 177 | 178 | #### select 语句 179 | 180 | select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 181 | 182 | ```go 183 | package main 184 | 185 | import "fmt" 186 | 187 | func main() { 188 | var c1, c2, c3 chan int 189 | var i1, i2 int 190 | select { 191 | case i1 = <-c1: 192 | fmt.Printf("received ", i1, " from c1\n") 193 | case c2 <- i2: 194 | fmt.Printf("sent ", i2, " to c2\n") 195 | case i3, ok := (<-c3): // same as: i3, ok := <-c3 196 | if ok { 197 | fmt.Printf("received ", i3, " from c3\n") 198 | } else { 199 | fmt.Printf("c3 is closed\n") 200 | } 201 | default: 202 | fmt.Printf("no communication\n") 203 | } 204 | } 205 | ``` 206 | 207 | `结果` 208 | 209 | ```go 210 | no communication 211 | ``` 212 | 213 | - 每个case都必须是一个通信 214 | 215 | - 所有channel表达式都会被求值 216 | 217 | - 所有被发送的表达式都会被求值 218 | 219 | - 如果任意某个通信可以进行,它就执行;其他被忽略。 220 | 221 | - 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 222 | 223 | - 否则: 224 | 225 | 如果有default子句,则执行该语句。 226 | 227 | 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。 228 | 229 | -------------------------------------------------------------------------------- /chapter6.md: -------------------------------------------------------------------------------- 1 | ### 循环语句 2 | 3 | #### For语句 4 | 5 | ```go 6 | for init; condition; post { } 7 | ``` 8 | 9 | ```go 10 | for condition { } 11 | ``` 12 | 13 | 效果与while相似 14 | 15 | ```go 16 | for { } 17 | ``` 18 | 19 | 效果与for(;;) 一样 20 | 21 | for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环 22 | 23 | ```go 24 | for key, value := range oldMap { 25 | newMap[key] = value 26 | } 27 | ``` 28 | 29 | ```go 30 | package main 31 | 32 | import "fmt" 33 | 34 | func main() { 35 | 36 | var b int = 15 37 | var a int 38 | 39 | numbers := [6]int{1, 2, 3, 5} 40 | 41 | /* for 循环 */ 42 | for a := 0; a < 10; a++ { 43 | fmt.Printf("a 的值为: %d\n", a) 44 | } 45 | 46 | for a < b { 47 | a++ 48 | fmt.Printf("a 的值为: %d\n", a) 49 | } 50 | 51 | for i,x:= range numbers { 52 | fmt.Printf("第 %d 位 x 的值 = %d\n", i,x) 53 | } 54 | } 55 | ``` 56 | 57 | `结果` 58 | 59 | ```go 60 | a 的值为: 0 61 | a 的值为: 1 62 | a 的值为: 2 63 | a 的值为: 3 64 | a 的值为: 4 65 | a 的值为: 5 66 | a 的值为: 6 67 | a 的值为: 7 68 | a 的值为: 8 69 | a 的值为: 9 70 | a 的值为: 1 71 | a 的值为: 2 72 | a 的值为: 3 73 | a 的值为: 4 74 | a 的值为: 5 75 | a 的值为: 6 76 | a 的值为: 7 77 | a 的值为: 8 78 | a 的值为: 9 79 | a 的值为: 10 80 | a 的值为: 11 81 | a 的值为: 12 82 | a 的值为: 13 83 | a 的值为: 14 84 | a 的值为: 15 85 | 第 0 位 x 的值 = 1 86 | 第 1 位 x 的值 = 2 87 | 第 2 位 x 的值 = 3 88 | 第 3 位 x 的值 = 5 89 | 第 4 位 x 的值 = 0 90 | 第 5 位 x 的值 = 0 91 | ``` 92 | 93 | #### 跳出循环的语句 94 | 95 | break:跳出循环体 96 | 97 | continue:跳出一次循环 98 | 99 | goto:可以无条件地转移到过程中指定的行 100 | 101 | ```go 102 | package main 103 | 104 | import "fmt" 105 | 106 | func main() { 107 | /* 定义局部变量 */ 108 | var a int = 10 109 | 110 | /* 循环 */ 111 | LOOP: for a < 20 { 112 | if a == 15 { 113 | /* 跳过迭代 */ 114 | a = a + 1 115 | goto LOOP 116 | } 117 | fmt.Printf("a的值为 : %d\n", a) 118 | a++ 119 | } 120 | } 121 | ``` 122 | 123 | `结果` 124 | 125 | ```go 126 | a的值为 : 10 127 | a的值为 : 11 128 | a的值为 : 12 129 | a的值为 : 13 130 | a的值为 : 14 131 | a的值为 : 16 132 | a的值为 : 17 133 | a的值为 : 18 134 | a的值为 : 19 135 | ``` -------------------------------------------------------------------------------- /chapter7.md: -------------------------------------------------------------------------------- 1 | ### defer 2 | 3 | 即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题 4 | 5 | - 如果有很多调用defer,那么defer是采用`后进先出`模式 6 | - 在离开所在的方法时,执行(报错的时候也会执行) 7 | 8 | ```go 9 | func ReadWrite() bool { 10 | file.Open("file") 11 | defer file.Close() 12 | if failureX { 13 | return false 14 | } i 15 | f failureY { 16 | return false 17 | } 18 | return true 19 | } 20 | ``` 21 | 22 | 最后才执行`file.Close()` 23 | 24 | `示例` 25 | 26 | ```go 27 | package main 28 | 29 | import "fmt" 30 | 31 | func main() { 32 | a := 1 33 | b := 2 34 | defer fmt.Println(b) 35 | fmt.Println(a) 36 | } 37 | ``` 38 | 39 | `结果` 40 | 41 | ```go 42 | 1 43 | 2 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /chapter8.md: -------------------------------------------------------------------------------- 1 | ### 数组 2 | 3 | Go 语言提供了数组类型的数据结构。 4 | 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。 5 | 6 | 数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推。 7 | 8 | **声明和初始化数组** 9 | 10 | ```go 11 | var variable_name [SIZE] variable_type 12 | ``` 13 | 14 | ```go 15 | var balance [10] float32 16 | ``` 17 | 18 | ```go 19 | var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 20 | ``` 21 | 22 | 初始化数组中 {} 中的元素个数不能大于 [] 中的数字。 23 | 如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小: 24 | 25 | ```go 26 | var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0} 27 | ``` 28 | 29 | ```go 30 | balance[4] = 50.0 31 | ``` 32 | 33 | **访问数组元素** 34 | 35 | ```go 36 | float32 salary = balance[9] 37 | ``` 38 | 39 | ```go 40 | package main 41 | 42 | import "fmt" 43 | 44 | func main() { 45 | var n [10]int /* n 是一个长度为 10 的数组 */ 46 | var i,j int 47 | 48 | /* 为数组 n 初始化元素 */ 49 | for i = 0; i < 10; i++ { 50 | n[i] = i + 100 /* 设置元素为 i + 100 */ 51 | } 52 | 53 | /* 输出每个数组元素的值 */ 54 | for j = 0; j < 10; j++ { 55 | fmt.Printf("Element[%d] = %d\n", j, n[j] ) 56 | } 57 | } 58 | ``` 59 | 60 | `结果` 61 | 62 | ```go 63 | Element[0] = 100 64 | Element[1] = 101 65 | Element[2] = 102 66 | Element[3] = 103 67 | Element[4] = 104 68 | Element[5] = 105 69 | Element[6] = 106 70 | Element[7] = 107 71 | Element[8] = 108 72 | Element[9] = 109 73 | ``` 74 | 75 | **向函数传递数组** 76 | 77 | `第一种` 78 | 79 | ```go 80 | void myFunction(param [10]int) 81 | { 82 | . 83 | . 84 | . 85 | } 86 | ``` 87 | 88 | `第二种` 89 | 90 | ```go 91 | void myFunction(param []int) 92 | { 93 | . 94 | . 95 | . 96 | } 97 | ``` 98 | 99 | ```go 100 | func getAverage(arr []int, int size) float32 101 | { 102 | var i int 103 | var avg, sum float32 104 | 105 | for i = 0; i < size; ++i { 106 | sum += arr[i] 107 | } 108 | 109 | avg = sum / size 110 | 111 | return avg; 112 | } 113 | ``` 114 | 115 | #### 多维数组 116 | 117 | ```go 118 | var threedim [5][10][4]int 119 | ``` 120 | 121 | 三维数组 122 | 123 | ```go 124 | a = [3][4]int{ 125 | {0, 1, 2, 3} , /* 第一行索引为 0 */ 126 | {4, 5, 6, 7} , /* 第二行索引为 1 */ 127 | {8, 9, 10, 11} /* 第三行索引为 2 */ 128 | } 129 | ``` 130 | 131 | ```go 132 | int val = a[2][3] 133 | ``` 134 | 135 | -------------------------------------------------------------------------------- /chapter9.md: -------------------------------------------------------------------------------- 1 | ### 指针 2 | 3 | 我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。 4 | 5 | 一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。 6 | 7 | Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。 8 | 9 | ```go 10 | package main 11 | 12 | import "fmt" 13 | 14 | func main() { 15 | var a int = 10 16 | 17 | fmt.Printf("变量的地址: %x\n", &a ) 18 | } 19 | ``` 20 | 21 | `结果` 22 | 23 | ```go 24 | 变量的地址: 20818a220 25 | ``` 26 | 27 | 声明指针 28 | 29 | ```go 30 | var var_name *var-type 31 | ``` 32 | 33 | var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。 34 | 35 | ```go 36 | var ip *int /* 指向整型*/ 37 | var fp *float32 /* 指向浮点型 */ 38 | ``` 39 | 40 | ```go 41 | package main 42 | 43 | import "fmt" 44 | 45 | func main() { 46 | var a int= 20 /* 声明实际变量 */ 47 | var ip *int /* 声明指针变量 */ 48 | 49 | ip = &a /* 指针变量的存储地址 */ 50 | 51 | fmt.Printf("a 变量的地址是: %x\n", &a ) 52 | 53 | /* 指针变量的存储地址 */ 54 | fmt.Printf("ip 变量的存储地址: %x\n", ip ) 55 | 56 | /* 使用指针访问值 */ 57 | fmt.Printf("*ip 变量的值: %d\n", *ip ) 58 | } 59 | ``` 60 | 61 | `结果` 62 | 63 | ```go 64 | a 变量的地址是: 20818a220 65 | ip 变量的存储地址: 20818a220 66 | *ip 变量的值: 20 67 | ``` 68 | 69 | `示例说明` 70 | 71 | ```go 72 | package main 73 | 74 | import "fmt" 75 | 76 | type name int8 77 | type first struct { 78 | a int 79 | b bool 80 | name 81 | } 82 | 83 | func main() { 84 | a := new(first) 85 | a.a = 1 86 | a.name = 11 87 | fmt.Println(a.b, a.a, a.name) 88 | } 89 | ``` 90 | 91 | `结果` 92 | 93 | ```go 94 | false 1 11 95 | ``` 96 | 97 | > 未初始化的变量自动赋上初始值 98 | 99 | ```go 100 | package main 101 | 102 | import "fmt" 103 | 104 | type name int8 105 | type first struct { 106 | a int 107 | b bool 108 | name 109 | } 110 | 111 | func main() { 112 | var a = first{1, false, 2} 113 | var b *first = &a 114 | fmt.Println(a.b, a.a, a.name, &a, b.a, &b, (*b).a) 115 | } 116 | ``` 117 | 118 | `结果` 119 | 120 | ```go 121 | false 1 2 &{1 false 2} 1 0xc042068018 1 122 | ``` 123 | 124 | > 获取指针地址在指针变量前加&的方式 125 | 126 | 127 | 128 | **Go 空指针** 129 | 当一个指针被定义后没有分配到任何变量时,它的值为 nil。 130 | nil 指针也称为空指针。 131 | nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。 132 | 一个指针变量通常缩写为 ptr。 133 | 134 | 空指针判断: 135 | 136 | ```go 137 | if(ptr != nil) /* ptr 不是空指针 */ 138 | if(ptr == nil) /* ptr 是空指针 */ 139 | ``` 140 | 141 | **指针数组** 142 | 143 | ```go 144 | package main 145 | 146 | import "fmt" 147 | 148 | const MAX int = 3 149 | 150 | func main() { 151 | 152 | a := []int{10,100,200} 153 | var i int 154 | 155 | for i = 0; i < MAX; i++ { 156 | fmt.Printf("a[%d] = %d\n", i, a[i] ) 157 | } 158 | } 159 | ``` 160 | 161 | `结果` 162 | 163 | ```go 164 | a[0] = 10 165 | a[1] = 100 166 | a[2] = 200 167 | ``` 168 | 169 | 有一种情况,我们可能需要保存数组,这样我们就需要使用到指针。 170 | 171 | ```go 172 | package main 173 | 174 | import "fmt" 175 | 176 | const MAX int = 3 177 | 178 | func main() { 179 | a := []int{10,100,200} 180 | var i int 181 | var ptr [MAX]*int; 182 | 183 | for i = 0; i < MAX; i++ { 184 | ptr[i] = &a[i] /* 整数地址赋值给指针数组 */ 185 | } 186 | 187 | for i = 0; i < MAX; i++ { 188 | fmt.Printf("a[%d] = %d\n", i,*ptr[i] ) 189 | } 190 | } 191 | ``` 192 | 193 | `结果` 194 | 195 | ```go 196 | a[0] = 10 197 | a[1] = 100 198 | a[2] = 200 199 | ``` 200 | 201 | **指针的指针** 202 | 203 | 如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。 204 | 205 | ```go 206 | var ptr **int; 207 | ``` 208 | 209 | ```go 210 | package main 211 | 212 | import "fmt" 213 | 214 | func main() { 215 | 216 | var a int 217 | var ptr *int 218 | var pptr **int 219 | 220 | a = 3000 221 | 222 | /* 指针 ptr 地址 */ 223 | ptr = &a 224 | 225 | /* 指向指针 ptr 地址 */ 226 | pptr = &ptr 227 | 228 | /* 获取 pptr 的值 */ 229 | fmt.Printf("变量 a = %d\n", a ) 230 | fmt.Printf("指针变量 *ptr = %d\n", *ptr ) 231 | fmt.Printf("指向指针的指针变量 **pptr = %d\n", **pptr) 232 | } 233 | ``` 234 | 235 | `结果` 236 | 237 | ```go 238 | 变量 a = 3000 239 | 指针变量 *ptr = 3000 240 | 指向指针的指针变量 **pptr = 3000 241 | ``` 242 | 243 | **指针作为函数参数** 244 | 245 | ```go 246 | package main 247 | 248 | import "fmt" 249 | 250 | func main() { 251 | /* 定义局部变量 */ 252 | var a int = 100 253 | var b int= 200 254 | 255 | fmt.Printf("交换前 a 的值 : %d\n", a ) 256 | fmt.Printf("交换前 b 的值 : %d\n", b ) 257 | 258 | /* 调用函数用于交换值 259 | * &a 指向 a 变量的地址 260 | * &b 指向 b 变量的地址 261 | */ 262 | swap(&a, &b); 263 | 264 | fmt.Printf("交换后 a 的值 : %d\n", a ) 265 | fmt.Printf("交换后 b 的值 : %d\n", b ) 266 | } 267 | 268 | func swap(x *int, y *int) { 269 | var temp int 270 | temp = *x /* 保存 x 地址的值 */ 271 | *x = *y /* 将 y 赋值给 x */ 272 | *y = temp /* 将 temp 赋值给 y */ 273 | } 274 | ``` 275 | 276 | `结果` 277 | 278 | ```go 279 | 交换前 a 的值 : 100 280 | 交换前 b 的值 : 200 281 | 交换后 a 的值 : 200 282 | 交换后 b 的值 : 100 283 | ``` 284 | 285 | -------------------------------------------------------------------------------- /go_mod.md: -------------------------------------------------------------------------------- 1 | # GO Mod 2 | 3 | 配置环境变量,一种方式直接修改 `.bash_profie` 文件,还有一种是下面的命令方式。 4 | 5 | ``` 6 | export GOPATH="/usr/local/go" 7 | export GOROOT="/usr/local/go/" 8 | export GOBIN="/usr/local/go/bin" 9 | export PATH="$GOROOT/bin:$GOPATH::$PATH" 10 | ``` 11 | 12 | ```routeros 13 | #修改 GOBIN 路径(可选) 14 | go env -w GOBIN=$HOME/bin 15 | #打开 Go modules 16 | go env -w GO111MODULE=on 17 | #设置 GOPROXY,开启依赖下载加速 18 | go env -w GOPROXY=https://goproxy.cn,direct 19 | ``` 20 | 21 | 在使用模块的时候, `GOPATH` 是无意义的,不过它还是会把下载的依赖储存在 `GOPATH/src/mod` 中,也会把 `go install` 的结果放在 `GOPATH/bin`(如果 `GOBIN` 不存在的话) 22 | 23 | - `go mod download` 下载模块到本地缓存,缓存路径是 `$GOPATH/pkg/mod/cache` 24 | - `go mod edit` 是提供了命令版编辑 `go.mod` 的功能,例如 `go mod edit -fmt go.mod` 会格式化 `go.mod` 25 | - `go mod graph` 把模块之间的依赖图显示出来 26 | - `go mod init` 初始化模块(例如把原本dep管理的依赖关系转换过来) 27 | - `go mod tidy` 增加缺失的包,移除没用的包 28 | - `go mod vendor` 把依赖拷贝到 `vendor/` 目录下 29 | - `go mod verify` 确认依赖关系 30 | - `go mod why` 解释为什么需要包和模块 31 | 32 | ``` 33 | // 初始化mod,会生成一个 go.mod 文件 34 | go mod init [项目路径] 35 | // 下载并添加依赖到go.mod文件中 36 | go build, go test 37 | // 查看module下的所有依赖 38 | go list -m all 39 | // 更新稳定版依赖 40 | go get [rsc.io/sampler] 41 | // 指定版本依赖 42 | go list -m -versions rsc.io/sampler 43 | rsc.io/sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99 44 | go get rsc.io/sampler@v1.3.1 45 | // 清理无用的依赖 46 | go mod tidy 47 | ``` 48 | 49 | **Relation:** https://www.codeleading.com/article/18321671970/ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /更新日志.md: -------------------------------------------------------------------------------- 1 | ### 更新日志 2 | 3 | ##### 2016/11/27 4 | 5 | 优化变量和常量部分内容 6 | 7 | 增加map类型内容 8 | 9 | 新增自定义类型 --------------------------------------------------------------------------------