这是我参与「第五届青训营 」伴学笔记创作活动的第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的执行流程
- .go文件 --> go bulid --> 可执行文件(.exe) --> 运行
- .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())
以上内容若有不正之处,恳请您不吝指正!