Go语言快速上手-基础语言
1. 什么是GO语言
- 高性能、高并发
- 语法简单、学习曲线平缓
- 快速上手
- 丰富的标准库
- 稳定性
- 完善的工具链
- 编译、代码格式化、包管理、单元测试等测试
- 静态链接
- 可以快速运行,部署非常简单
- 快速编译
- 编译时间很快->修改一行一秒启动
- 跨平台
- 很多奇怪的东西上运行
- 垃圾回收
- 和java类似,无需考虑内存的释放
快速上手: 实现一个简单的http服务器
package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir(".")))
http.ListenAndServe(":8080", null)
}
2. 为什么是Go
1.最初使用的 Python,由于性能问题换成了Go2.C++不太适合在线Web业务 3.早期团队非Java 背景 4.性能比较好 5.部署简单、学习成本低 6.内部 RPC和HTTP框架的推广
3. GO入门
3.1 Go
配置Go的开发环境 已下载,略
3.2 HelloWorld
讲解略
package main
import (
"fmt"
)
func main() {
fmt.Println("helloword")
}
3.3 变量&常量
和C/C++类似 变量是程序运行时可以改变其值的存储位置,而常量则是程序运行时不可更改的值。
package main
import (
"fmt"
"math"
)
func main() {
var a = "initial" //声明方法一
var b, c int = 1, 2
var d = true
var e float64
f := float32(e) //声明方法二
g := a + "foo"
fmt.Println(a, b, c, d, e, f)
//initial 1 2 true 0 0
fmt.Println(g)
const s string = "constant"
const h = 50000000000 //常量定义
const l = 3e20 / h
fmt.Println(s, h, l, math.Sin(h), math.Sin(l))
}
3.4 基础语法 if-else
Go语言中的if-else语句与其他编程语言类似,用于根据条件执行不同的代码块。if-else语句的基本语法如下:
if condition {
// code to execute if condition is true
} else {
// code to execute if condition is false
}
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
3.5 基础语法-循环
for for循环结构,用于重复执行一段代码块。for循环的基本语法如下:
for initialization; condition; post {
// code to be executed
}
其中,initialization是循环变量的初始值;condition是循环条件,当条件为真时继续执行循环;post是每次循环后更新循环变量的操作。
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println("loop")
break
}
for j := 7; j < 9; j++ {
fmt.Println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}
}
3.6 switch分支
switch语句用于根据不同的条件执行不同的代码块。switch语句的基本语法如下:
switch expression {
case value1:
// code to be executed if expression equals value1
case value2:
// code to be executed if expression equals value2
...
default:
// code to be executed if none of the above cases are true
}
其中,expression是需要进行比较的表达式,value1、value2等是需要比较的值。如果expression等于某个值,则执行对应的代码块;如果没有任何一个值匹配,则执行default代码块。
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
3.7 数组
个人感觉像是java和C++的结合体 数组是一种固定长度、存储相同类型元素的数据结构。数组的定义方式如下:
var arr [n]T
其中,n表示数组的长度,T表示数组中元素的类型。例如,定义一个长度为5、元素类型为int的数组可以写成:
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println("get:", a[2])
fmt.Println("len:", len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
3.7.2 切片
感觉像java中的list和python数组的整合 切片(slice)是一种动态长度的数据结构,类似于动态数组。切片由三个部分组成:指向底层数组的指针、切片长度和切片容量。切片的定义方式如下:
var slice []T
其中,T表示切片中元素的类型。
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println(s) // [a b c d e f]
c := make([]string, len(s))
copy(c, s)
fmt.Println(c) // [a b c d e f]
fmt.Println(s[2:5]) // [c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[2:]) // [c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) // [g o o d]
}
2.8map
map是一种无序的键值对集合,类似于其他语言中的字典或哈希表。map中的键和值可以是任意类型,但需要满足以下条件:
- 键必须支持相等运算符(==、!=),例如基本类型、字符串、指针等。
- 值可以是任意类型。
map的定义方式如下:
var m map[keyType]valueType
其中,keyType表示键的类型,valueType表示值的类型。
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["unknow"]) // 0
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)
}
在遍历map时,元素的顺序是随机的。如果需要按照特定顺序遍历map,则需要先将key排序或者使用其他数据结构
3.9 函数
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
func main() {
res := add(1, 2)
fmt.Println(res) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
3.10 指针
初步支持,常用于对常用的数修改
指针是一种特殊的变量,它存储了一个变量的内存地址。指针变量的类型为*T,其中T表示指向的变量的类型。例如:
var p *int // 定义一个int类型的指针变量p
要获取一个变量的地址,可以使用取地址操作符(&)。例如:
var a int = 10
p = &a // 将a的地址赋值给p
要访问指针所指向的变量,可以使用解引用操作符(* )。例如:
fmt.Println(*p) // 输出10
在Go语言中,不支持指针运算和“->”操作符。
package main
import "fmt"
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
3.11 结构体
结构体是一种用户自定义的数据类型,它由一系列具有相同或不同类型的数据字段组成。结构体可以用来表示复杂的数据结构,例如一个人的信息、一本书的属性等等。
定义一个结构体可以使用type关键字和struct关键字。例如:
type Person struct {
name string
age int
}
上面的代码定义了一个名为Person的结构体,它有两个字段:name和age,分别表示人的姓名和年龄。
要创建一个结构体变量,可以使用以下语法:
var p Person // 定义一个Person类型的变量p
p.name = "Tom"
p.age = 18
也可以在定义时直接初始化:
var p Person = Person{"Tom", 18}
或者使用键值对初始化:
var p Person = Person{name: "Tom", age: 18}
访问结构体字段可以使用.操作符。例如:
fmt.Println(p.name, p.age) // 输出Tom 18
在Go语言中,还支持匿名结构体和嵌套结构体。匿名结构体没有类型名称,只能用于临时场景。嵌套结构体则是将一个结构体作为另一个结构体的字段。例如:
// 匿名结构体
var p struct {
name string
age int
} = struct {
name string
age int
}{name: "Tom", age: 18}
// 嵌套结构体
type Address struct {
city string
street string
}
type Person struct {
name string
age int
address Address // 嵌套结构体作为字段
}
var p Person = Person{
name: "Tom",
age: 18,
address: Address{
city: "Beijing",
street: "Chaoyang Road",
},
}
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
3.12 结构体方法
结构体可以定义方法。方法是一种与特定类型关联的函数,它可以访问该类型的数据。定义方法的语法如下:
func (receiver Type) methodName(parameters) (results) {
// 方法体
}
其中,receiver表示方法所属的类型,可以是结构体类型或非结构体类型;Type表示接收者的类型;methodName表示方法名;parameters表示参数列表;results表示返回值列表。
例如,下面定义了一个名为Person的结构体,并为其定义了一个名为SayHello()的方法:
type Person struct {
name string
age int
}
func (p Person) SayHello() {
fmt.Printf("Hello, my name is %s, I'm %d years old.\n", p.name, p.age)
}
在上面的代码中,(p Person)就是接收者,表示该方法属于Person类型。在方法内部,可以通过接收者访问该类型的数据。
要调用结构体方法,需要先创建一个结构体变量,并通过该变量调用相应的方法。例如:
var p Person = Person{name: "Tom", age: 18}
p.SayHello() // 输出:Hello, my name is Tom, I'm 18 years old.
在调用时,不需要显式地传递接收者参数,Go语言会自动将调用者作为接收者传递给方法。
除了普通方法外,在Go语言中还支持指针接收者方法。指针接收者方法可以修改接收者的值,而普通方法只能读取接收者的值。指针接收者方法的定义方式与普通方法类似,只是在接收者类型前加上*符号。例如:
func (p *Person) SetName(name string) {
p.name = name
}
在上面的代码中,(p *Person)就是指针接收者,表示该方法属于`*Person
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
3.13 错误处理
在Go语言中,错误处理是编程的重要方面。当一个函数遇到错误时,它可以返回一个错误值给调用者。调用者可以检查返回的值是否是一个错误,并相应地处理它。Go语言提供了一些内置函数和约定来帮助处理错误,例如使用error类型来表示错误,使用defer关键字来确保资源释放等
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
- 代码定义了一个
user结构体,包含用户名和密码两个字段。 - 然后定义了一个
findUser函数,该函数接收一个user类型的切片和一个用户名作为参数,返回找到的用户指针和错误信息。在函数内部,使用循环遍历切片中的每个用户,如果找到了与传入的用户名相同的用户,则返回该用户指针和空错误;否则返回空指针和自定义错误信息。 - 在主函数中,首先调用
findUser函数查找名为"wang"的用户,并将结果赋值给变量u和err。如果查找失败,则打印错误信息并退出程序;否则打印找到的用户的名字。 - 接着,在同一行中使用if语句调用了另一个查找用户的函数,并将结果赋值给变量u和err。如果查找失败,则打印错误信息并退出程序;否则打印找到的用户的名字。
- 需要注意的是,在第二次调用findUser时使用了短变量声明(:=)来声明u和err变量,这意味着它们只在if语句块中有效。因此,在if语句块外部无法访问这些变量。
3.14 字符串操作
GO语言中字符串的常用操作: 常用的字符串操作函数有:
- len(str):返回字符串的长度。
- strings.Contains(str, substr):判断字符串str是否包含子串substr,返回bool类型。
- strings.Index(str, substr):返回子串substr在字符串str中第一次出现的位置,如果没有找到则返回-1。
- strings.LastIndex(str, substr):返回子串substr在字符串str中最后一次出现的位置,如果没有找到则返回-1。
- strings.Replace(str, old, new, n):将字符串str中前n个old子串替换为new子串,并返回新的字符串。
- strings.Split(str, sep):将字符串str按照sep分割成多个子串,并返回一个切片。
- strings.ToLower(str):将字符串str转换为小写字母形式,并返回新的字符串。
- strings.ToUpper(str):将字符串str转换为大写字母形式,并返回新的字符串。
- strings.TrimSpace(str):去除字符串str首尾的空格,并返回新的字符串。
- strings.Trim(str, cutset):去除字符串str首尾包含在cutset中的字符,并返回新的字符串。
- strings.HasPrefix(str, prefix):判断字符串str是否以prefix开头,返回bool类型。
- strings.HasSuffix(str, suffix):判断字符串str是否以suffix结尾,返回bool类型。
- strings.Join(strs []string, sep string):将切片strs中的所有元素按照sep连接成一个字符串,并返回新的字符串。
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
}
3.15 JSON处理
Go语言中提供了内置的encoding/json包来处理JSON数据。该包提供了将JSON数据解析为Go语言中的结构体对象,以及将Go语言中的结构体对象转换为JSON格式数据的功能。
具体来说,该包提供了以下几个函数:
json.Marshal(v interface{}) ([]byte, error):将Go语言中的结构体对象v转换为JSON格式数据,并返回字节数组和错误信息。json.Unmarshal(data []byte, v interface{}) error:将JSON格式数据data解析为Go语言中的结构体对象v,并返回错误信息。
例如,假设我们有一个名为Person的结构体:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
我们可以使用json.Marshal()函数将该结构体转换为JSON格式数据:
p := Person{Name: "Alice", Age: 25}
data, err := json.Marshal(p)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(string(data)) // 输出:{"name":"Alice","age":25}
同样地,我们也可以使用json.Unmarshal()函数将JSON格式数据解析为Person结构体:
data := []byte(`{"name":"Bob","age":30}`)
var p Person
err := json.Unmarshal(data, &p)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(p.Name) // 输出:Bob
fmt.Println(p.Age) // 输出:30
需要注意的是,在使用json.Unmarshal()函数时,需要传入一个指向Person结构体的指针,否则无法将解析后的数据赋值给该结构体。
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
3.16 时间处理
Go语言中提供了内置的time包来处理时间。该包提供了表示时间的结构体类型、时间格式化、时间计算等功能。
以下是该包中常用的函数和结构体:
time.Now():获取当前本地时间。time.Parse(layout, value string) (Time, error):将字符串value按照layout指定的格式解析为Time类型的值。time.Time:表示时间的结构体类型,包含年月日时分秒等信息。Time.Format(layout string) string:将Time类型的值按照layout指定的格式转换为字符串。Time.Add(d Duration) Time:将Time类型的值加上Duration类型的值d,返回新的Time类型的值。
例如,我们可以使用以下代码获取当前本地时间并输出:
now := time.Now()
fmt.Println(now)
我们也可以使用time.Parse()函数将一个字符串解析为Time类型:
t, err := time.Parse("2006-01-02 15:04:05", "2022-01-01 00:00:00")
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(t)
在上面这个例子中,我们使用了一个特殊的日期格式"2006-01-02 15:04:05"作为layout参数。这是因为Go语言规定,在进行日期格式化时必须使用这个固定格式。
我们也可以使用Time.Format()函数将一个Time类型转换为字符串:
t := time.Date(2022, time.January, 1, 0, 0, 0, 0, time.Local)
fmt.Println(t.Format("2006-01-02 15:04:05"))
最后,我们可以使用Time.Add()函数对时间进行加减操作:
t := time.Now()
t2 := t.Add(24 * time.Hour)
fmt.Println(t2)
在上面这个例子中,我们将当前时间加上24小时,并输出结果。需要注意的是,在进行时间计算时,需要使用time.Duration类型表示时间
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080
}
3.16 数字解析
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
3.16 进程信息
一些运行环境、进程信息
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
// go run example/20-env/main.go a b c d
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
}