一. Go语言初识
1.1 什么是Go语言
特点:
- 高性能、高并发
- 语法简单
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
简单的文件服务器:
package main
import (
"net/http"
)
func main(){
http.Handle("/", http.FileServer(http.Dir(".")))
http.ListenAndServe(":8080", nil)
}
1.2 配置开发环境
安装Goland,安装完成后在终端查看版本号:
(base) ➜ ~ go version
go version go1.19 darwin/arm64
1.3 基础语法
1.3.1 Hello World
通过package main,导入fmt包,使用其Print函数输出:
package main
import (
"fmt"
)
func main() {
fmt.Print("hello world")
}
1.3.2 变量类型
Go与Java类似,是一门强变量类型语言。
字符串、整数、浮点数、布尔型
func main() {
var a = "test" // 字符串
var b, c int = 1, 2 // int
var d = false
var e float64
// := 运算符可以使变量在不声明的情况下直接被赋值使用
f := float32(e)
g := a + "go"
fmt.Println(a, b, c, d, e, f) // test 1 2 false 0 0
fmt.Println(g) // testgo
const s string = "constant"
const h = 5000 // 根据上下文自动确定类型
const i = 3e20
fmt.Println(s, h, i, math.Sin(h), math.Cos(i))
}
1.3.2 if-else
与C++类似,显著区别在于Go里的判断条件没有括号
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if num := 9; num < 0 {
fmt.Println("negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
运行结果:
7 is odd 9 has 1 digit
1.3.3 循环
与C++类似,可以使用continue、break等关键字
for循环:
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println("loop")
break
}
for i <= 4 {
fmt.Println(i)
i++
}
for j := 7; j < 9; j++ {
fmt.Println(j)
}
}
switch循环(与C++不同,不需要手动在条件里break):
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3, 4:
fmt.Println("three or four")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("before noon")
default:
fmt.Println("after noon")
}
}
1.3.4 数组
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println(a[4], 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(twoD)
}
1.3.5 切片
注意开闭区间:
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2])
fmt.Println("len", len(s))
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)
fmt.Println(s[2:5]) // 下标区间[2,5) -> [c d e]
fmt.Println(s[:5])
fmt.Println(s[2:])
good := []string{"abc", "def", "ghi"}
fmt.Println(good)
}
1.3.6 map
写法与Java差异较大,可以使用make创建空map,也可以直接用花括号填入值:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)
fmt.Println(len(m))
fmt.Println(m["one"])
r, ok := m["unknown"] // 是否存在
fmt.Println(r, ok) // 0 false
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
fmt.Println(m2)
}
1.3.7 range
可以用来简化遍历的循环代码,例如遍历数组或者map:
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num)
}
}
fmt.Println(sum)
m := map[string]string{"a": "A", "b": "B"}
for key, val := range m {
fmt.Println(key, val)
}
for key := range m {
fmt.Println(key)
}
}
1.3.8 函数
与Java或C++不同,Go的函数可以存在多个返回值:
package main
import "fmt"
func add(a int, 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(5, 9)
fmt.Println(res)
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // a true
}
1.3.9 指针
与C++相比,Go指针功能有限,下面以add函数的值传递为例:
package main
import "fmt"
func add(n int) {
n += 2 // 无效,因为n的值是拷贝
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 23
add(n)
fmt.Println(n) // 23
add2ptr(&n)
fmt.Println(n) //25
}
1.3.10 结构体
与C类似:
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "zhang", password: "123456"}
b := user{name: "yan", password: "2048"}
fmt.Println(a.password)
var c user
c.name = "james"
c.password = "1111"
fmt.Println(a, b, c) // {zhang 123456} {yan 2048} {james 1111}
}
1.3.11 错误处理
不同于Java的异常处理,Go能返回具体哪个函数出现错误:
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 { // _为占位符,本质上是返回key-value,把key忽略了
if u.name == name {
return &u, nil
}
}
return nil, errors.New("Not Found")
}
func main() {
if u, err := findUser([]user{{"james", "12345"}}, "james"); err != nil {
fmt.Println(err)
return
} else {
fmt.Println(u.name)
}
}
1.3.12 字符串操作
一些常用操作:
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, "lo")) // true
fmt.Println(strings.Index(a, "el")) // 1
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
}
格式化,用%v可以打印任何格式,%+v和%#v可以获取更详细格式:
package main
import (
"fmt"
)
type point struct {
x, y int
}
func main() {
a := "Hello"
b := 123
p := point{1, 3}
fmt.Printf("a=%v\n", a)
fmt.Printf("b=%v\n", b)
fmt.Printf("p=%v\n", p) // p={1 3}
fmt.Printf("p=%+v\n", p) // p={x:1 y:3}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:3}
}
1.3.13 JSON处理
通过Marshal和UnMarshal方法序列化、反序列化JSON字符串:
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "james", Age: 18, Hobby: []string{"Go", "Java"}}
buf, err := json.Marshal(a) // 序列化,Unmarshal可以反序列化到userInfo中
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // {"Name":"james","age":18,"Hobby":["Go","Java"]}
buff, err := json.MarshalIndent(a, "", "\t") // 更直观
if err != nil {
panic(err)
}
fmt.Println(string(buff))
}
1.3.14 时间处理
包括格式化时间,构造时间,获取时间等:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
t := time.Date(2023, 5, 13, 12, 59,
35, 0, time.UTC)
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())
fmt.Println(t.Format("2006-01-02 15:04:05")) // 只能用北美山地时间(MST:Mountain Standard Time)2006年1月2日下午(PM)3点4分5秒这个时间
t2 := time.Date(2024, 5, 13, 12, 59,
35, 0, time.UTC)
fmt.Println(t2.Sub(t).Hours()) // 8784
}
1.3.15 数字解析
包括进制转换,字符串与数字相互转换等:
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("1000", 16, 64)
n2, _ := strconv.ParseInt("0x1000", 0, 64) // 第二个参数为0,通过0x 0前缀判断
fmt.Println(n, n2) // 4096 4096
t, _ := strconv.Atoi("12390")
fmt.Println(t) // 12390
g := strconv.Itoa(214)
fmt.Println(g + "s") // "214s"
}
1.3.16 进程信息
例子:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
fmt.Println(os.Args)
fmt.Println(os.Getenv("PATH"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf))
}
1.3.17 小结
通过这一节课程,初识了Go基础语法,个人感觉Go有点像Java与Python的结合体,代码风格简洁度介于两者之间。有一些特性让我感觉编写代码更方便了,例如操作JSON序列化那一块,可以通过`符号定义序列化后key的值。