Go语言基础 | 青训营笔记
这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天,以下是我的课堂笔记
标题:走进 Go 语言基础语言 - 掘金
1.Go语言简介
Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。
语言特点:
- 高性能、高并发
- 语法简单
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2.语言入门
2.1 Go安装和开发环境
官网安装,配置好环境变量即可。主要在IDE"Goland"中学习
2.2 基础语法
1、Hello world!
package main
import "fmt"
func main() {
fmt.Println("hello world!")
}
2、变量声明
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 + "Abc"
fmt.Println(a, b, c, d, e, f)
fmt.Println(g)
const s string = "haha"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
变量声明可以用var name = xxx,可以不声明类型也可以声明类型,字符串可以直接做拼接,也可以用变量名 **:=**来声明
常量声明即const,const常量没有确定的类型会根据上下文自动确定类型
3、if-else
与C语言if-else不同的是if后面不再有括号了
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("num is de")
} else {
fmt.Println("num is ac")
}
}
我们看到 if后面的语句是可以声明变量的
4、循环:只有for!
直接上代码
package main
import "fmt"
func main() {
i := 1
for {//死循环
fmt.Println("loop")
break
}
for i := 0; i < 10; i++ {
fmt.Println(i)
}
for i <= 3 {
fmt.Println(i)
i += 1
}
}
我们看到第一个循环就是for后面不写,就是一个死循环,第二个是普通的for循环,第三个是相当于一个while
同样的可以用break或continue跳出
5、Switch分支
和c比较类似,同样的switch后面不需要括号
c中的case如果不break会默认按顺序跑完,但go中不会,go不用break也会跳出
go的Switch功能更加强大,可以使用各种类型作为case
package main
import (
"fmt"
"time"
)
func main() {
i := 1
switch i {
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
}
t := time.Now()
switch {//可以用if-else代替,但这样更加清晰
case t.Hour() < 12:
fmt.Println("its before noon")
case t.Hour() > 2:
fmt.Println("good")
default:
fmt.Println("its after noon")
}
}
6、数组
package main
import "fmt"
func main() {
var a [5]int
a[4] = 100
fmt.Println(a[4], len(a))
}
很简单,二维数组也一样
在真实和开发中我们很少用数组,因为其长度固定,我们用切片:
7、切片slice
可以任意时刻更改长度,用make创建
可以用append去往切片后面追加元素,但是必须赋值回去,因为可能发生扩容,切片实际上是存储了一个长度,容量,和一个指向数组的指针,当你操作append需要扩容,会返回一个新的切片
package main
import "fmt"
func main() {
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get", s[0])
fmt.Println("get", s[1])
s = append(s, "haha") //需要赋值回去
c := make([]string, len(s))
copy(c, s) //拷贝s到c
fmt.Println(s[1:3]) //取出位置1到3的元素,不包括3
fmt.Println(s[2:]) //2以后
fmt.Println(s[:2]) //2以前
}
8、map
同样用make创建,需要两个类型,一个是key的类型,一个是
value的类型,这里是int
package main
import "fmt"
func main() {
m := make(map[string]int, 3) //key的类型string,value类型int
m["one"] = 1
m["two"] = 2
fmt.Println("m["one"]:", m["one"]) //读取
delete(m, "one") //删除
r, ok := m["three"] //读取时可以加上OK去看有无这个key存在
fmt.Println(r, ok) //0,false
m2 := map[string]int{"one": 1, "two": 2}//创建时赋值
fmt.Println(m2)
}
map完全无序,遍历时随机顺序输出
9、range遍历
注意看注释
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
/*
遍历数组,相当于for后面为下标i,值num := range数组名,i和num就被声明了,可以在for循环体使用
*/
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v) // b 8; a A
}
for k := range m {
fmt.Println("key", k) // key a; key b
}
}
遍历map的语法结构和数组差不多
2.2.* 函数
声明一个函数
func name(参数) (返回值)
这里需要注意的是一般返回值会有多个,比如下面的代码中的exists方法
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
}
该方法中v string是真正的返回值,ok bool是检验
11、指针
使用并不广泛,主要用于方便改变func中传入的值,比如:
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
}
2.2.** 结构体
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
}
结构体的定义呢和c类似,在初始化时如果没赋值会初始化为0或nil(go中的null)
对于以结构体为参数的方法,你可以直接传入结构名,或者传入指针用法,如果使用指针用法就可以改变结构体
比如我们下面这个重置密码的方法
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
}
传入指针就可以改变结构体内部数据
2.2.*** 错误处理
不同于java中的错误处理机制,go可以清晰地返回错误并用简单的if-else去处理
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)
}
}
15、操作字符串的各种方法
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true 是否包含
fmt.Println(strings.Count(a, "l")) // 2 有几个“l”计数
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
}
字符串格式化:
package main
import "fmt"
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2},更加详细
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
}
2.2.**** json操作
结构体json操作很简单,需要保证结构体的变量名首字母大写即可
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {//首字母大写
Name string
Age int `json:"age"`//如果想让序列化后小写age就加后面这段
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)//解析成json
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"}}
}
18、时间操作
time包