Lesson1 Go语言基础|青训营笔记

88 阅读4分钟

一. Go语言初识

1.1 什么是Go语言

特点:

  1. 高性能、高并发
  2. 语法简单
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

简单的文件服务器:

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)                // testgoconst 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 falsedelete(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)200612日下午(PM)345秒这个时间
​
  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的值。