go筑基篇(二)—语法
【1】基础知识
一、包
Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如 fmt、os、io 等。
Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。
二、import关键字
导入可分单行导入和多行导入
1、单行导入
import "包1"
import "包2"
2、多行导入
当多行导入时,包名在 import 中的顺序不影响导入效果,格式如下
import( "包2" "包1" )
import告诉go编译器这个程序需要使用哪些包的函数,如import "fmt"需要使用fmt包的函数,fmt包实现了格式化I/O的函数
import包的引入格式:
1、省略引用(点操作)
也叫省略引用格式,这种格式相当于把包直接合并到当前程序中
import(. "fmt") func main() { Println("hello world") }
使用点操作导入包后,在使用包中的函数时可以省略包名前缀。如fmt.Println(),可以直接写为Println()
缺点:包很多时会搞不清楚函数是哪个包下的,实际使用时不建议这么写
2、别名引用
给包重新命名
import(f "fmt") func main() { f.Println("hello world") }
3、匿名引用(_操作)
在引用某个包时,如果只是希望执行包初始化的 init 函数,而不使用包内部的数据时,可以使用匿名引用格式。
匿名导入的包与其他方式导入的包一样都会被编译到可执行文件中
4、标准引用格式
使用标准格式引用包,但是代码中却没有使用包,编译器会报错
注意:
- 一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
- 包不能出现环形引用的情况,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,则编译不能通过。
- 包的重复引用是允许的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。这种场景相当于重复引用了 d,这种情况是允许的,并且 Go 编译器保证包 d 的 init 函数只会执行一次。
三、源码文件分类

1、命令源码文件
命令源码文件是go程序的入口。
特点:声明自己属于main包,并且包含main主函数,这个主函数没有参数,也没有返回值。
一个包中不要放多个命令源码文件,虽然命令源码文件可以单独分开go run运行,但无法通过go build和go install。
命令源码文件往往是单独放在一个代码包当中的。
2、库源码文件
不具备命令源码文件特征的普通源码文件
库源码文件被安装后,相应的归档文件(.a文件)会被存放到当前工作区的pkg目录下。
通俗讲就是程序执行时所需其它一些包的源码文件,go语言是通过包管理的,一个程序的执行需要多个包支持
3、测试源码文件
固定以_test.go为后缀的源码文件,并且必须包含Test或Benchmark名称前缀的函数
Test为前缀的函数,它的参数必须是testing包下T类型指针对象

四、命名规范
命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。 Go语言从语法层面进行了以下限定:任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。
- 当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Analysize,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);
- 命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )
1、包命名
建议package的名字和目录名保持一致,尽量采取简短有意义的包名。包名应该为小写字母,不要使用下划线或混合大小写
package main
2、文件命名
单词小写,用下划线分隔单词。
my_hello.go
3、结构体命名
采用驼峰命名法,首字母根据访问控制大写或小写
struct 申明和初始化格式采用多行,例如下面:
type Address struct { Province string City string ZipCode int PhoneNumber string } addr := Address{ "四川", "成都", 610000, "0", }
4、接口命名
类似结构体,单个单词的结构名往往以“er“为后缀
type writer interface{ Write([]byte) error
5、变量命名
类似结构体,一般遵循驼峰法,首字母根据访问控制大写或小写,但遇到特有名词时,需要遵循以下规则:
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 appService
- 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头
声明变量的一般形式是使用 var 关键字:
var name type var isExist bool var hasConflict bool var canManage bool var allowGitHook bool
注:Go语言和许多编程语言不同,它在声明变量时将变量的类型放在变量的名称之后
Go语言的基本类型有:
- bool
- string
- int、int8、int16、int32、int64
- uint、uint8、uint16、uint32、uint64、uintptr
- byte // uint8 的别名
- rune // int32 的别名 代表一个 Unicode 码
- float32、float64
- complex64、complex128
6、常量命名
使用全部大写字母组成,使用下划线分词
const APP_VERSION = "1.0"
若是枚举类型,需要先创建类型
type Scheme string const ( HTTP Scheme = "http" HTTPs Scheme = "https" )
五、注释
行注释: //
多行注释:/*开头,*/结尾
1、包注释
包注释应包含
//包的基本简介(包名、简介)
//创建者,格式创建人:姓名
//创建时间,格式yyyyMMdd

2、结构(接口)注释
每个自定义的结构体或接口都应该有注释,对结构体进行简要说明,格式:结构体名 结构体说明。结构体内的每个成员变量都要有说明

3、函数(方法)注释

4、代码逻辑注释
对关键位置或局部复杂的逻辑进行注释

注意:中英文混合注释时英文要与中文之间有空格,即时标点符号与英文之间也需要使用空格分隔

六、代码风格
tab键缩进,默认4个空格,可用ctrl+alt+L进行格式化
默认一行一条语句,不需要;结尾,若多条语句写在一行则需要
go语言强制左大括号不换行,换行会报错
if a>0 { }
可以在terminal终端使用
go fmt
对程序格式化
如果当前开发包引入了多种类型的包:如标准库包、程序内部包、第三方包,建议不同类型按此顺序分组,并空格分离

测试:
单元测试文件名命名规范为example_test.go
测试用例的函数名称必须以Test开头,如TestExmple
每个重要的函数都要首先编写测试用例,测试用例和正规代码一起提交方便进行回归测试
【2】常用命令讲解
一、go run
专门用来运行命令源码文件的
参数:
-a:强制编译相关代码,无论是否编译过
-v:列出被编译的代码包的名称
-a -v:列出所有被编译的代码包的名称
-p n:并行编译,其中n为并行的数量 如-p 2
-n:打印编译过程中所需运行的命令,但并不执行这些命令
-x:打印编译过程中所需运行的命令,并执行这些命令
-work:显示编译时创建的临时工作目录的路径,并且不删除这个临时工作目录
使用方式:如go run -v [fileName].go
go run -n helloWorld.go //查看编译过程中运行的一些命令
go run -work helloWorld.go //查看编译时创建的临时目录
临时目录里文件机构

其中.a为归档文件,exe里是可执行文件。
go run命令在第二次运行时,如果发现导入的代码包没有任何变化时,go run不会再次编译这个导入的代码包,直接静态链接进来
二、go build
主要用于测试编译。在包的编译过程中,若有需要,会同时编译与之相关联的包。
go build库源码文件不会产生任何文件(go build只做检查性的编译,而不会输出任何结果文件,利用这个特性来检查非命令源码文件的有效性),go build命令行文件在当前目录会生成一个可执行文件,再执行go install命令会把生成的可执行文件挪到bin目录,或直接go build -o 路径\可执行文件(如:go build -o ..\..\bin\hello.exe)编译当前文件输出可执行文件到指定目录并命名。
go build默认编译当前文件夹下所有go文件,可以指名文件名编译单个文件。
观看编译过程中运行的命令
D:\go_www\test\src\hello>go build -n

go run执行了可执行文件,go build只是把库源码文件编译了一遍,然后把可执行文件移动到了当前文件夹中
三、go install
编译并安装代码包或源码文件
观察编译过程中运行的命令:
D:\go_www\test\src\hello>go install -n
分2步:
1、生成结果文件(可执行文件或者.a包)
2、把编译好的结果移到$GOPATH/pkg(如果是.a归档包移动到此目录)或者$GOPATH/bin(如果是可执行文件移动到此目录)。
如果go install的是库源码文件,产生的是.a为后缀的归档文件。如果go install的是命令源码文件产生的是.exe为后缀的可执行文件。

四、go get
从远程代码仓库下载并且编译安装代码包。
写程序用到第三方包都通过go get 拉取(类似php中composer),下载到$GOPATH第一个工作区的src目录
go get参数:
| 标记名称 | 标记描述 |
|---|---|
| -d | 让命令程序只执行下载动作,而不执行安装动作。 |
| -f | 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了。 |
| -fix | 让命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。 |
| -insecure | 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。 |
| -t | 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包。 |
| -u | 让命令利用网络来更新已有代码包及其依赖包。默认情况下,该命令只会从网络上下载本地不存在的代码包,而不会更新已有的代码包。 |
拉取包演示(-x参数可以让我们看到go get命令的执行过程)
D:\go_www\test>go get -x github.com/go-errors/errors //不需要加上https://协议。-x参数可以让我们看到go get的执行过程
观察执行过程可看到
git clone -- https://github.com/go-errors/errors D:\go_www\test\src\github.com\go-errors\errors,下载完后对其进行了编译等操作
可以看到$GOPATH工作区的src目录已经存在相应的文件。go get命令还会把库源码文件编译成归档文件移动到pkg目录。pkg父目录是windows_amd64,这个文件夹是跟相关的操作平台相关的,平台是什么酒就据什么平台命名。

图形示例:

五、其它命令
| 命令 | 解释 | 使用举例 |
| go fmt | 格式化写好的代码,因为IDE有相应格式化操作,很少用 | |
| go clean | 移除当前源码包编译生成的文件 | |
| go doc | 查看包的文档 | 查看http包go doc net/http,查看包里的函数go doc fmt Printf |
| godoc | godoc命令可以在本机启动一个web服务,通过浏览器查看Go语言标准库和项目依赖库的文档 |
go在1.13之前是自带godoc的,之后的版本需要自行安装。格式godoc -http=<域名>:<端口号> 需要先安装godoc mkdir -p .\src\golang.org\x |
| go test | 自动读取源码目录下*_test.go的文件,生成并运行测试用的可执行文件 |
【3】语法
一、变量声明
变量必须先定义才能使用
变量的类型和赋值类型必须一致
同一作用域变量名不能重复
简短定义方式左边必须有一个新的变量,且不能定义全局变量
变量定义了就要使用,否则报错
变量定义但不赋值默认零值(即默认值,字符串默认值'',整型、浮点型默认值0)
变量的本质是一小块内存,变量是存储特定类型的值而提供给内存位置的名称
1、先声明指定类型变量,再赋值 var age int age = 32 fmt.Printf("年龄%d", age) 2、写在一行 var age2 int = 22; fmt.Printf("年龄%d", age2) 3、不指定类型,使用类型推导 var height = 1.71 fmt.Printf("类型%T,值:%f", height, height) 4、省略var,简短格式声明变量 sum := 80 fmt.Println(sum)
但若var sum int,然后 sum := 80会报错,因为:=就有定义变量的意思,而前面已经使用var sum int定义过变量了。
:=的左边必须有新的变量,如age,height := 20, 123则正确,因为多了一个未定义的新变量height
多变量同时赋值
1、先声明为同一类型,后赋值 var m ,n int m = 171 n=20 fmt.Println(m,n) 2、声明时直接赋值 var p,k int = 12,57
fmt.Println(p, k) 3、使用类型推断给不同类型变量赋值 var height,age = "171cm",24 fmt.Println(height,age) 4、使用组赋值,一次定义一组值 var ( userName = "cdf" sexy ="male" ) fmt.Println(userName, sexy))
Printf基本格式化操作
1、数字格式化输出,把数字格式化为十进制、二进制、八进制、十六进制
var age = 23 fmt.Printf("my sge is %d \n",age) //整数 %d fmt.Printf("30`s binary %b \n",age) //输出整数的二进制值 fmt.Printf("30`s octal %o \n",age) //输出整数的八进制值 fmt.Printf("30`s hex %x \n",age) //输出整数的十六进制值
2、浮点型格式化输出
var pi = 3.1 fmt.Printf("类型是:%T,数值是:%f", pi, pi)//浮点类型格式输出,我的年龄是:32类型是:float64,数值是:15.100000类 fmt.Printf("类型是:%T,数值是:%.2f", pi, pi)//类型是:float64,数值是:15.10
3、字符串格式化输出
var name = "cf" fmt.Printf("类型是:%T,数值是:%s", name, name) //我的年龄是:32类型是:string,数值是:cf
4、布尔类型格式化输出
var married = true fmt.Printf("类型是:%T,数值是:%t", married, married)//类型是:bool,数值是:true
格式化样式
| 动 词 | 功 能 |
|---|---|
| %v | 原样打印 |
| %+v | 在 %v 基础上,对结构体字段名和值进行展开 |
| %#v | 输出 Go 语言语法格式的值 |
| %T | 输出 Go 语言语法格式的类型和值 |
| %% | 输出 % 本体 |
| %b | 整型以二进制方式显示 |
| %o | 整型以八进制方式显示 |
| %d | 整型以十进制方式显示 |
| %x | 整型以十六进制方式显示 |
| %X | 整型以十六进制、字母大写方式显示 |
| %U | Unicode 字符 |
| %t | 布尔类型 |
| %s | 字符串 |
| %f | 浮点数 |
| %c | 打印字符 |
| %p | 指针,十六进制方式显示 |
打印字符
a := 'A'(如果使用双引号引起来就是字符串类型了)
fmt.Printf("类型%T,值%d,字符:%c \n", a, a) \\类型int32,值65,字符:A
二、引用
变量值改变时,地址不会变
var m int m = 171 fmt.Printf("变量值:%d,地址:%p\n", m, &m) m = 134 fmt.Printf("变量值:%d,地址:%p", m, &m)
三、变量默认值
var m int fmt.Printf("变量值:%d,地址:%p\n", m, &m) var n float32 fmt.Printf("变量值:%f,地址:%p\n", n, &n) var s1 string fmt.Printf("变量值:%s,地址:%p\n", s1, &s1) var arr []int fmt.Printf("变量值:%a,地址:%p\n", arr, &arr) println(arr == nil)

变量声明,不使用会报错。包导入,不使用会报错
四、常量
用const定义的值的标识符,程序执行过程中,数值不能改变,一般常量名大写
显示类型定义:const name string = "cdf"
隐式类型定义:const name = "cdf",不指定类型会进行类型推导
多重赋值:
const APPID, EXPIRE= "qweererqw1qse",123
println(APPID, EXPIRE)
常量定义不使用不会报错
注:变量定义后不使用程序会报错,但常量定义后不使用程序不报错
定义一组常量
const A, B, C = 1, 2, 3 const ( a = "abc" b = len(a) c )
一组常量中,如果某个常量没有赋值,默认和上一行一致
如上面差c值为3
枚举类型(就是一组相关值,如男女,春夏秋冬、星期一~星期日等)
const { MAN = 1 MALE = 0 }
iota
iota是特殊常量,可以认为是一个可以被编译器修改的常量。可以理解为const定义的一组常量中的索引值。第一个iota等于0,第二个iota等于1,以此类推
const ( a = iota b = "cdf" c = iota d ) println(a, b, c, d) //0 cdf 2 3
五、变量输入
分别用Scanln、Scanf、bufio读取键盘输入
var x int var y float32 fmt.Println("Scanln读取输入") fmt.Scanln(&x, &y) //读取键盘输入,通过操作地址赋值 fmt.Printf("接收的输入的第一个值%d,第二个值%f\n", x, y)
fmt.Println("Scanf读取输入") fmt.Scanf("%d,%f", &x, &y) //接收格式化的数据输入 fmt.Printf("接收的输入的第一个值%d,第二个值%f", x, y)
fmt.Println("bufio读取输入") reader := bufio.NewReader(os.Stdin) str,err := reader.ReadString('\n') //读到什么位置,读到换行位置。换行只能使用单引号 fmt.Printf("读取的值%s,错误%v",str, err)
浙公网安备 33010602011771号