文件组成
特点:
- Go是一门编译型语言,Go语言的工具链将源代码及其依赖转换成计算机的机器指令(译注:静态编译)
- Go语言原生支持Unicode,它可以处理全世界任何语言的文本
组成:
- 通过包(package)的方式组织代码
import
- 组成程序的声明种类
- 常量 -
const
- 变量 -
var
- 函数 -
func
- 类型 -
type
- 常量 -
- 初始化函数
init
- 可执行程序入口函数
main
例子:
// 声明一个可执行程序(main 比较特殊)
package main
// 通过包组织代码运行和调用
import "fmt"
// 常量 & 变量
const LEVEL = "V5"
var name string
// 初始化
func init() {
name = "中国"
}
// 程序执行入口
func main() {
fmt.Println(name, LEVEL, ":Hello word!")
}
基础语法
命名
一个名字必须以一个字母(Unicode字母)或下划线开头
,后面可以跟任意数量的字母、数字或下划线
区分大小写
开头字母的大小写决定了名字在包外的可见性。大写表示外部可见
( 中文汉字现在默认为小写,未来不一定),否则为包内可见
尽量使用驼峰式命名
方式,避免缩写单词大小写混用(setByCas => setByCAS
)
保留关键字如下,保留关键字不允许使用到命名中:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
预定义名称(可以使用作为命名):
内建常量: true false iota nil
内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool string
byte // 自动类型转换, 默认:int8 中文:int32,可用ASCII码 10进制、16进制、单引号 表示
rune // int32 的别名,代表一个Unicode码
error
内建函数: make len cap new append copy close delete
complex real imag
panic recover
变量
语法
var 变量名[ [*]类型] = 值
值和类型可以省略其中一个:
- 省略类型: 根据初始化表达式来推导变量的类型信息。
- 省略值 : 用零值初始化该变量。
短变量声明
- 声明和初始化
局部变量
- 允许部分变量已经声明;执行部分初始化,部分声明+初始化
- 不允许全部变量都声明;全部声明的变量不应该用
短变量声明
;应该用元组赋值-多重赋值
- 类型根据表达式来自动推导
零值
数值类型 `0`
布尔类型 `false`
字符串 `""`(空字符串)
接口或引用类型(包括slice、指针、map、chan和函数) nil
数组或结构体等聚合类型 每个元素或字段都是对应该类型的零值
指针
一个指针的值是另一个变量的地址。一个指针对应变量在内存中的存储位置。
p := &s
fmt.Printf("类型:%T \n", p)
fmt.Printf("指针变量地址:%p 指针变量内容:%v 调用时的地址:%p \n", &p, p, &*p)
fmt.Printf("底层数据地址:%p \n", &s)
// 类型:*string
// 指针地址:0xc000006030 指针内容:0xc00003a1f0 调用时的地址:0xc00003a1f0
// 底层数据地址:0xc00003a1f0
生命周期
- 全局变量:位于包一级的变量。它们的生命周期和整个程序的运行周期是一致的。
- 局部变量:位于函数内的变量。每次从创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收
变量逃逸
:
针对局部变量的回收特性, 如果将指向短生命周期对象的指针保存到具有长生命周期的对象中,特别是保存到全局变量时,会阻止对短生命周期对象的垃圾回收(从而可能影响程序的性能) 。
说人话:当全局变量
指针指向局部变量
的内存地址(global = &part),就会导致内存不回收。
赋值
- 类型必须完全匹配,在赋值语句左边的变量和右边最终的求到的值必须有相同的数据类型
- nil可以赋值给任何指针或引用类型的变量
元组赋值
允许同时更新多个变量的值(多重赋值
可以用于交换两个变量)- 先右后左
- 将右边各个表达式的值赋值给左边对应位置的各个变量
类型
type 类型名字 底层类型
包
包的惯例
- 必须在头部声明自己的包名
- 每个包全部都在一个单独的目录里。一个目录不存在多个包、一个包不会存在多个目录。
- 包名应保持小写、简洁
main包
- 编译程序会将这种名字的包编译为二进制可执行文件。 可执行程序必须有一个
main
的包 - 必须存在一个
main()
函数
导入
导入使用关键字 import
;导入的方式分为以下三种:
- 远程导入
import "github.com/spf13/viper"
- 命名导入
import myviper "mylib/viper"
。一般用于解决冲突或者别名 - 空白标识符导入
import _ "db/driver/mysql"
。一般用于初始化代码才会如此
init函数
- 每个包可以包含多个
init()
函数。- 支持单个文件多个
- 支持多个文件多个
- 在
main()
之前运行
PS - 部分场景:在 init()
中初始化向某个插件注册功能。可执行程序处引用包含init()
的文件、包
执行顺序
- import subPkg. 引入包
- const. 常量定义
- var. 变量定义
- subPkg.init(). 引入包执行 init
- 以导入声明的顺序初始化
- init(). 优先包含 main函数的 init(), 然后其他 init()
- main(). 当前main
常见工具
go env
.查看环境变量go list
.查看依赖包go build
.编译包和依赖项go build -race
用竞争检测器来编译并执行
go run
.编译并运行Go程序go vet
.代码错误检查go fmt
.代码格式化go get
.下载 && 安装软件包和依赖(远程)go install
.编译 && 安装软件包和依赖项(本地)
数据类型
Go语言将数据类型分为四类:基础类型
、复合类型
、引用类型
和接口类型
。
- 基础类型:数字、字符串和布尔型。
- 复合类型:数组、结构体 是通过组合简单类型,来表达更加复杂的数据结构。
- 引用类型:指针、切片、映射、函数、通道
- 接口类型:接口
基础类型
运算符
优先级排序:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
%(取模):仅用于整数
间的运算。取模结果符号与被取模数值一致
-5%3 // -2
-5%-3 // -2
/(除法):用于整数时会采用去尾法
5/2 // 2
5.0/2.0 // 2.5
算数运算时,高位溢出直接丢弃
,无关符号
var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"
类型转换时:
- 大尺寸的整数类型转为一个小尺寸的整数类型 (int32 -> int16)
- 将一个浮点数转为整数,可能会改变数值或丢失精度 (float64 -> int32)
整型
带符号和无符号的不同位整型:int8、int16、int32、int64、uint8、uint16、uint32、uint64
int、uint
rune 通常用于表示一个Unicode码点(等价于 int32)
byte 字节内容(**等价于 uint8 **)
*uintptr 一般是写底层用的
浮点型
float32、float64
复数
complex64、complex128
布尔值
true、false
字符串
8位字节序列构成的字符串,约定但不必须是utf-8编码的文本。 字符串可以为空但不能是nil,其值不可变
字符串可以用==和<进行比较;比较通过逐个字节比较
完成的,因此比较的结果是字符串自然编码的顺序。
文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列
len函数 可以返回一个字符串中的字节数目(不是rune字符数目)
len("汉") // 3. 中文 utf8 下为三个字节
连接通过 "+"号:
fmt.Println("你" + "好")
Unicode 符号和二进制编码的对应关系;
UTF-8 针对Unicode的可变长度字符
编码方式。最长的符号达到6个字节了(汉字一般是3个,还有其他的)
- 目前最长的符号达到6个字节了
- 汉字一般3字节,但还有其他的4字节;详见unicode码表
- mysql中常见的 utf8mb4 是为了兼容宽字节(也就是4个字节)的情况
tempStr := "汉1"
fmt.Printf("rune(char):%q\n", []rune(tempStr)) // rune(char):['汉' '1']
fmt.Printf("rune(hex):%x\n", []rune(tempStr)) // rune(hex):[6c49 31] -- Unicode 码值
fmt.Printf("bytes(hex):% x\n", []byte(tempStr)) // bytes(hex):e6 b1 89 31 -- UTF-8 编码后
// len 访问时
fmt.Println(len(tempStr)) // 4 => 字节数
// 访问指定偏移量时
fmt.Println(tempStr[2]) // 137 => 字节序访问
// 直接遍历字符串的时候
for _, val := range tempStr {
fmt.Println(val) // 27721 49 => unicode码值
}
// 上面的三个步骤说明如下:
// 1. 按照一个个字符拆分
// 2. 字符转为 unicode码表
// 3. 使用UTF-8转换为字节序
fmt