Go语言快速入门| 青训营笔记

238 阅读6分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第1天。

Go语言简介与背景

Go是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。

Go(又称 Golang)是 Google 的 Rob Pike(罗勃.派克),Ken Thompson(肯·汤普逊)和Robert Griesemer(罗伯特.格瑞史莫)开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。

Go语言最开始是谷歌工程师20%时间的产物(谷歌的“20%时间”工作方式,允许工程师拿出20%的时间来研究自己喜欢的项目)。一直到了 2009 年11月,Go 正式开源了。

Go语言的特点

Go语言被称为"21世纪的c语言",个人感觉是既有c的运行速度,又有Python的开发效率,是动态语言与静态语言的部分优点的集合。

1.高性能、高性能

内嵌库支持高并发,性能媲美C++,易开发高并发应用程序

2.语法简单、学习曲线平缓

语法结构类似c语言,但更为简洁,去除了分号与部分语句的括号等

3.丰富的标准库

可以在不借助第三库情况下稳定、兼容的开发出很多应用程序

4.完善的工具链

编译、代码格式化、错误检查、包管理等,内置了单元测试框架

5.静态链接

go中所有编译结构默认是静态链接的,只需要拷贝编译之后的唯一一个可执行文件就能部署运行,在线上的容器环境下运行镜像体积可以控制的非常小

6.快速编译

拥有静态语言中几乎最快的编译速度

7.跨平台

能在各OS上运行,包括android和Ios、路由器和树莓板

8.垃圾回收

无需考虑内存的分配释放,可以专注于业务逻辑

go的执行流程

  1. .go文件 --> go bulid --> 可执行文件(.exe) --> 运行
  2. .go文件 --> go run --> 运行
    区别:1.生成了可执行文件的话就可以在没有go开发环境上运行,go run则要求必须要有sdk环境。
    2.编译时编译器会将运行依赖的库文件包含在可执行文件中,所以可执行文件要大很多。

Go的变量声明

go语言是一门强类型语言,每一个变量都有自己的类型。

变量注意点:
1.变量表示内存中的一个存储区域
2.该区域有自己的名称(变量名)和类型(数据类型)
3.go变量使用的三种方式:
第一种:指定变量类型,声明后若不赋值,使用默认值
var i int
第二种:根据值自行判定变量类型(类型推导)
var num = 12.12
第三种:省略var,注意: :=左侧的变量不应该是已经声明过的
name := "tom"
4.该区域的数据值可以在同一类型范围内不断变化

var i int = 10
i = 30
i = 50

但不能:i = 21.12 因为申请的数据类型已经确定了
5.变量在同一个作用域内不能重名
6.变量=变量名+值+数据类型
7.go的变量如果没有赋初值,编译器会用默认值,如int为0,string默认值为空串
8.多变量定义:

   var n1 , name, n2 = 100, "tom", 299
   n1 , name, n2 := 100, "tom", 299

Go的部分存储结构

数组

声明方式如:

var a[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的切片
s := make([] string, 3)
// 加入元素
s = append(s, "d")
s = append(s, "e", "f")

注意这里必须要把append的结果赋值给原切片,因为slice的原理是存储了一个长度和一个容量和一个指向数组的指针,如果容量不够,则会扩容并返回一个新的slice。

c := make([] string, len(s))
copy(c,s)  // 拷贝切片s到c

整体来说,Go的切片操作与Python的切片操作类似。

Map

   // 创建空map, key类型为string, value类型为int
   m := make(map[string]int)
   // 写入键值对
   m["one"] = 1
   m["two"] = 2

   // 直接创建有键值对的map
   m2 := map[string]int{"one": 1, "two": 2}
   fmt.Printf(fmt.Sprintf("%d", m2["one"]))

   // 删除键值对
   delete(m, "one")

注意:go的map是完全无序的,即输出时会基本无序输出

// 判断一个键值对是否存在
   word, number := m["one"]
   fmt.Println(word, number)

指针

指针的主要用途是对传入的变量进行修改,用法与c语言类似。

1.对于基本数据类型:变量存的就是值,也叫值类型
2.获取变量的地址,用&,比如:var num int,获取num的地址:&num
3.对于指针类型:变量存的是一个地址,这个地址指向的空间存的才是值,比如:var pointer int = &num
4.获取指针类型所指向的值,使用:
,比如:var pointer int,使用pointer获取pointer所指向的值
注意点:
1.pointer是一个指针变量
2.pointer的类型是 *int
3.pointer本身的值是 &i
4.创建指针时要注意基本类型要匹配

值类型都有对应的指针类型,形式为:*数据类型

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
}

结构体

结构体为: 带类型的字段的集合,初始化字段都为空值

结构体方法: 类似于其他语言的类成员方法
例如如下代码:

package main

import "fmt"

type user struct {
name     string
password string
}

func main() {
a := user{name: "jack", password: "123"}
b := user{"rose", "456"}
c := user{name: "durant"}
c.password = "789"

var d user
d.name = "curry"
d.password = "1234"

fmt.Println(a, b, c, d)
}

func (u user) checkPassword(password string) bool {
return u.password == password
}

// 带指针则可以对结构体进行修改
func (u *user) resetPassword(password string) {
u.password = password
}

Go的部分标准库

错误处理

package main

import (
   "errors"
   "fmt"
)

type user struct {
   name     string
   password string
}
// 判断user是否存在
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)
   }
}

JSON处理

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"}}
}

要保证结构体的字段的第一个字母大写,即为空开,类似java的public。
主要记忆:用json.Marshal序列化

时间处理

// 当前时间
now := time.Now()
fmt.Println(now)

//构造带时区的时间
t := time.Date(2023, 1, 15, 12, 25,20, 0, time.UTC)
t2 := time.Date(2023, 1, 15, 16, 25,20, 0, time.UTC)
fmt.Println(t)

// 获取时间点信息
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour())

// 格式化时间字符串到时间
fmt.Println(t.Format("2006-01-02 15:04:05"))

// 获取两个时间的差值
diff := t2.Sub(t)
fmt.Println(diff)
// 指定单位
fmt.Println(diff.Minutes(), diff.Seconds())

// 获取时间戳
fmt.Println(now.Unix())

以上内容若有不正之处,恳请您不吝指正!