1. Hello World
//1.定义包名。每个Go程序都是由包构成的。main包表示程序的入口包,程序从main包开始运行。
package main
//2.导包。这里导入的是标准库中的fmt包,主要是用来输入输出和格式化字符串
import(
"fmt"
)
//3.main函数。里面调用了fmt.Printin输出helloworld
func main(){
fmt.Println("Hello World")
}
2. 变量
2.1. 变量类型
bool布尔型string字符串- 整型
uint8无符号 8 位整型uint16无符号 16 位整型uint32无符号 32 位整型uint64无符号 64 位整型int8有符号 8 位整型int16有符号 16 位整型int32有符号 32 位整型int64有符号 64 位整型
- 浮点型
float3232位浮点型数float6464位浮点型数complex6432 位实数和虚数complex12864 位实数和虚数
- 其他类型
byte类似uint8rune类似 int32uint32或64位int与uint一样大小uintptr无符号整型,用于存放一个指针
2.2. 变量声明和初始化
- 使用var语句进行变量声明,
var name type - 变量声明可以包含初始值,每个变量对应一个。如果初始化值已存在,则可以省略类型;变量会从初始值中获得类型。
- 短变量声明:在函数中,可以使用
:=在类型明确的地方代替var声明(例如k := 1) - 没有明确初始值的变量声明会被赋予零值(数值型0;布尔型flase;字符串"")
2.3. 常量
- 常量的声明与变量类似,只不过是使用 const 关键字。常量可以是字符串、布尔值或数值。
- 常量不能用
:=声明。
3. 流程控制语句
3.1. if else
go中的if else写法与C/C++写法类似,不同点在于:if后面的小括号()不需要写;大括号{}必须写,不能省略
3.2. for循环
- Go 只有一种循环结构:for 循环。
- 基本的for循环由初始化语句、条件表达式、后置语句三部分组成,用分号隔开,初始化语句和后置语句是可选的。
- 初始化语句通常为一句短变量声明,该变量声明仅在 for 语句的作用域中可见。
- 同样,小括号()不需要写;大括号{}必须写
- 死循环:for后面什么都不写
- 可以用break、continue跳出或继续循环
3.3. switch
- go语言里面的switch分支结构,和C/C++也比较类似。同样的在switch后面的那个变量名,不需要括号
- 不同点:在C++里面,switch case如果不显式添加break,会继续往下跑完所有的case,在go语言中是不需要加break的。
- go语言里面的switch功能更强大,可以使用任意的变量类型,甚至可以用来取代任意的if-else语句。可以在switch后面不加任何的变量,然后在case里面写条件分支。代码逻辑会更为清晰。
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
4. 数组
- 定义格式:var 变量名 [数组长]类型(例如:
var a [10]int,会将变量 a 声明为拥有 10 个整数的数组。) - 数组可以很方便地取特定索引的值或者往特定系引取行储值,也能够直接去打印一个数组
- 数组的长度是其类型的一部分,因此数组不能改变大小。不过,Go 提供了更加便利的方式来使用数组。
package main
import "fmt"
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1]) //Hello World
fmt.Println(a) //[Hello World]
primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes) //[2 3 5 7 11 13]
}
5. 切片
- 切片可以任意更改长度
- 切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:
a[low:high],它会选择一个半开区间,包括第一个元素,但排除最后一个元素。切片下界的默认值为 0,上界则是该切片的长度 - 切片可以用函数
make创建,make 函数会分配一个元素为零值的数组并返回一个引用了它的切片 - 为切片追加新的元素则使用
append,append的结果是一个包含原切片所有元素加上新添加元素的切片。当底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组,返回的切片会指向这个新分配的数组。 len()切片的长度就是它所包含的元素个数。cap()切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
package main
import "fmt"
func main() {
a := make([]int, 2)
a[0] = 0
a[1] = 1
fmt.Println(len(a)) //2
a = append(a, 2, 3, 4)
fmt.Println(a) //[0 1 2 3 4]
fmt.Println(len(a)) //5
fmt.Println(a[2:4]) //[2 3]
fmt.Println(a[:4]) //[0 1 2 3]
fmt.Println(a[2:]) //[2 3 4]
}
6. map(映射)
- 声明:
var mapname map[keytype]valuetype - 在映射a中插入或修改元素:m[key] = elem
- 获取元素:elem = m[key]
- 删除元素:delete(m, key)
- 通过双赋值检测某个键是否存在:elem, ok = m[key]
- 若 key 在 m 中,ok 为 true ;否则,ok 为 false。
- 若 key 不在映射中,那么 elem 是该映射元素类型的零值。
- 同样的,当从映射中读取某个不存在的键时,结果是映射的元素类型的零值。
- 注 :若 elem 或 ok 还未声明,你可以使用短变量声明:elem, ok := m[key]
package main
import "fmt"
func main() {
a := make(map[string]int)
a["one"] = 1
a["two"] = 2
fmt.Println(a) //map[one:1 two:2]
fmt.Println(a["one"]) //1
fmt.Println(a["unknow"]) //0
delete(a, "one")
fmt.Println(a["one"]) //0
r, ok := a["two"]
fmt.Println(r, ok) //2 true
}
7. range
对于一个sice或者一个map,可以用range来快速遍历,这样代码能够更加简洁。range遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值。如果不需要索引的话,可以用下划线来忽略
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5}
for i, num := range nums {
fmt.Println(i, "+", num)
//0 + 1
//1 + 2
//2 + 3
//3 + 4
//4 + 5
}
for i, _ := range nums {
fmt.Print(i) //01234
}
for _, value := range nums {
fmt.Print(value) //12345
}
}
8. 函数
Go语言函数原生支持返回多个值,在实际业务逻辑代码里面几乎所有函数都是返回两个值,第一个是真正的返回结果,第二个值是错误信息
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
9. 指针
Go 拥有指针,指针保存了值的内存地址。
- *T:是指向T类型值的指针。
- & 操作符会生成一个指向其操作数的指针。
- *操作符表示指针指向的底层值。
package main
import "fmt"
func main() {
i, j := 42, 2700
p := &i // 指向i
fmt.Println(*p) // 通过指针读取i的值 42
*p = 21 // 通过指针设置i的值
fmt.Println(i) // 查看i的值 21
p = &j // 指向j
*p = *p / 30 // 通过指针对j进行除法运算
fmt.Println(j) // 查看j的值 90
}
10. 结构体
- 构造时需要传入每个字段的初始值,也可以使用键值对的方式指定初始值
- 结构体字段可以通过结构体指针来访问。
- 如果我们有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段X。Go语言也允许使用隐式间接引用,直接写 p.X 就可以。
- 可以为结构体定义方法,有一点类似其他语言里面的类成员函数。
- 在实现结构体的方法的时候也有两种写法,一种是带指针,一种是不带指针。区别就是如果带指针,那么就可以对这个结构体去做修改。如果不带指针,实际上操作的是一个拷贝,无法对结构体进行修改
package main
import "fmt"
type user struct {
name string
num string
}
// 普通函数
func checkNum(u user, num string) bool {
return u.num == num
}
// 结构体方法
func (u user) checkNum2(num string) bool {
return u.num == num
}
func (u *user) changeNum(num string) {
u.num = num
}
func main() {
a := user{name: "zhangsan", num: "1"}
b := user{"lisi", "2"}
c := user{name: "wangwu"}
c.num = "3"
var d user
d.name = "zhaoliu"
d.num = "4"
fmt.Println(a, b, c, d) //{zhangsan 1} {lisi 2} {wangwu 3} {zhaoliu 4}
fmt.Println(a.name) //zhangsan
fmt.Println(checkNum(a, "2")) //false
fmt.Println(a.checkNum2("2")) //false
a.changeNum("5")
fmt.Println(a.num) //5
}
11. 错误处理
- Go程序使用 error 值来表示错误状态。
- 通常函数会返回一个 error 值,调用的它的代码应当判断这个错误是否等于 nil 来进行错误处理。
- error为nil时表示成功;非nil表示失败。
以两数相除举例:
package main
import "fmt"
// 自定义错误信息结构
type DivideError struct {
errortype int // 错误类型
v1 int // 记录下出错时的除数、被除数
v2 int
}
// 实现接口方法 error.Error()
func (divideError DivideError) Error() string {
if 0 == divideError.errortype {
return "除零错误"
} else {
return "其他未知错误"
}
}
// 除法
func div(a int, b int) (int, *DivideError) {
if b == 0 {
// 返回错误信息
return 0, &DivideError{0, a, b}
} else {
// 返回正确的商
return a / b, nil
}
}
func main() {
// 正确调用
v, err := div(100, 2)
if nil != err {
fmt.Println("(1)fail:", err)
} else {
fmt.Println("(1)succeed:", v)
}
// 错误调用
v, err = div(100, 0)
if nil != err {
fmt.Println("(2)fail:", err)
} else {
fmt.Println("(2)succeed:", v)
}
}
//运行结果:
//(1)succeed: 50
//(2)fail: 除零错误
12. 字符串操作
标准库strings包中有很多常用字符串工具函数
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) //字符串内是否包含另一字符串 true
fmt.Println(strings.Count(a, "l")) //字符串计数 2
fmt.Println(strings.HasPrefix(a, "he")) //判断是否为该字符串开头 true
fmt.Println(strings.HasSuffix(a, "llo")) //判断是否为该字符串结尾 true
fmt.Println(strings.Index(a, "ll")) //查找某个字符串的位置 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) //连接多个字符串 he-llo
fmt.Println(strings.Repeat(a, 2)) //重复字符串 hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) //替换所有匹配上的字符 hEllo
fmt.Println(strings.Split("a-b-c", "-")) //字符串分割 [a b c]
fmt.Println(strings.ToLower(a)) //转换为小写 hello
fmt.Println(strings.ToUpper(a)) //转换为大写 HELLO
fmt.Println(len(a)) //字符串长度 5
b := "你好"
fmt.Println(len(b)) //6
}
13. 字符串格式化
fmt包中包含多种字符串格式相关的方法
例如printf,写法类似C语言,不过在go中,可以使用%v打印任意类型的变量,也可以用%+v打印详细结果,%#v则更详细
14. JSON处理
对于一个已有的结构体,只要保证每个字段的第一个字母大写,那么结构体就能用JSON.marshal序列化变成一个JSON的字符串。序列化之后的字符串也能够用JSON.unmarshal反序列化到一个空变量里。
15. 时间戳
- 获取当前时间
time.now() - 构造带时区的时间
time.date() - 获取某个时间点的年月日小时分钟秒
Year() Month() Day() Hour() Minute() Second() - 把时间格式化成字符串
Format() - 把日期字符串转化为时间
Parse() - 获取时间戳
Unix()
16. 数字解析
- go语言关于字符串和数字类型的转换在strconv包中
ParseInt()将字符串转换为数字- 三个参数:参数1 数字的字符串形式
- 参数2 数字字符串的进制 比如二进制 八进制 十进制 十六进制
- 参数3 返回结果的bit大小 也就是int8 int16 int32 int64
ParseFloat()将字符串转换为数字- 两个参数:参数1 数字的字符串形式
- 参数2 返回结果的bit大小
Atoi()字符串转成整型数字Itoa()整型数字转成字符串- 如果输入不合法,这些函数都会返回error
17. 进程信息
os.Args程序执行时指定的命令行os.Getenv获取环境变量os.Setenv设置环境变量exec.Command执行命令行