Go 语言上手 - 基础语言 | 青训营笔记

75 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。

go语言特点

1.高性能,高并发

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

3.丰富的标准库

4.完善的工具链

5.静态链接

6.快速编译

7.跨平台

8.垃圾回收

基础语法

变量

import{
    "fmt"
}
var a = "initial"
var b,c int = 1,2
var d = true
var e float64
g := a + "foo"
fmt.println(a,b,c,d,e,g)
const s = 1

go语言里面变量的声明有两种方法,一种是通过var name string=""这种方式来声明,声明变量的时候,一般会自动去推导变量的类型,如果有需要,也可以显示写出变量类型。另一种声明变量的方式是:使用变量 := 等于值

if-else

import "fmt"
func main(){
    if 7%2 == 0{
        fmt.println("")
    }else{
        fmt.println("")
    }

for循环

import "fmt"
func main(){
    i := 1
    for j := 7;j<9;j++{
        fmt.println("")
    }
    for i<=3{
        fmt.println("")
        i = i+1
    }
}

switch

switch a{
case 1:
    fmt.println("")
case 2:
    fmt.println("")
default:
}

数组

var a [5]int
b := [5]int{1,2,3,4,5}
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)

切片

s := make([]string,3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.println("len:",len(s))
s = append(s,"d")
c := make([]string,len(s))
copy(c,s)
fmt.println(s[2:5]) //左闭右开
good := []string{"g","o","o","d"}
fmt.println(good) //{g o o d}

map

m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.println(m) //map[one:1 tow:2]
fmt.println(m["one"]) //1
fmt.println(m["unknow"]) //0
delete(m,"one")
m2 := map[string]int{"one":1,"two":2}
var m3 = map[string]int{"one":1,"two":2}

第一个是key的类型,第二个是value的类型,map是完全无序的

range

对于一个slice或者一个map的话,可以用range来快速遍历,这样代码能够更加简洁。range遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值。如果不需要索引的话,可以用下划线来忽略

nums := []int{2,3,4}
sum := 0
for i,num := range nums{
    sum += num
    if num==2{
        fmt.println("index:",i,"num:",num)
    }
}
m := map[string]string{"a":"A","b":"B"}
for k := range m{
    fmt.println("key",k) //key a;key b
}

函数

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(1,2)
    v,ok := exists(map[string]string{"a":"A"},"a")
    fmt.println(v,ok) //A True

golang和其他很多语言不一样的是,变量类型是后置的

golang里面的函数原生支持返回多个值,在实际的业务逻辑代码里几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息

指针

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
}

go里面的指针主要用途是对传入参数进行修改。

函数add2试图把一个变量+2,但单纯这种写法是无效的,因为传入函数的参数实际上是一个拷贝,拷贝+2,并不起作用。如果需要起作用的话,要把那个类型写成指针类型,那么为了类型匹配,调用的时候会加一个&符号。

结构体

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

结构体方法

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

在golang里面可以为结构体去定义一些方法,类似于其它语言里的类成员函数,结构体函数可以使用xx.xx("xx")这样去调用,具体的代码修改就是把第一个参数加上括号写到函数名称前面。

在实现结构体的方法的时候也有两种写法,一种是带指针,一种是不带指针,区别就是如果你带指针的话就可以去对这个结构体做修改,如果不带指针的话,实际上操作的是一个拷贝,就无法对结构体进行修改

错误处理

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
    }else{
        fmt.println(u.name)
    }
}

在go语言里面符合语言习惯的做法就是使用一个单独的返回值来传递错误信息。

不同于Java使用的异常,go语言的处理方式,能够清晰的知道哪个函数返回了错误,并且能用简单的if else来处理错误。在函数里面,可以在那个函数的返回值类型里面,后面加一个error,就代表这个函数可能会返回错误。那么在函数实现的时候,return需要同时return两个值,要么就是如果出现错误的话,那么可以return nil和一个error。如果没有的话,就返回原来的结果和nil

字符串操作

import(
    "fmt"
    "strings"
)
func main(){
    a := "hello"
    strings.Contains(a,"ll") //true
    strings.Count(a,"l") //2
    strings.HasPrefix(a,"he") //true
    strings.HasSuffix(a,"llo") //true
    strings.Index(a,"ll") //2
    strings.join([]string{"he","llo"},"-") //he-llo
    strings.Repeat(a,2) //hellohello
    strings.Replace(a,"e","E",-1)) //hEllo
    strings.Split("a-b-c","-") //[a b c]
    strings.ToLower(a) //hello
    strings.ToUpper(a) //HELLO

字符串格式化

type point struct{
    x,y int
}
func main(){
    p := point{1,2}
    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}
}

在标准化的FMT包里有很多字符串格式相关的方法,比如printf,在go语言里可以用%v来打印任意类型的变量,而不需要区分数字字符串。也可以用%+v打印详细结果,%#v则更加详细

JSON处理

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","type"}}
    buf,err := json.Marshal(a)
    if err!=nil{
        panic(err)
    }
    fmt.Println(buf) //{123 34...}
    fmt.Println(string(buf)) //{"Name":"Wang","age":...}

go语言里面的json操作非常简单,对于一个已有的结构体,可以什么都不做,只要保证每个字段的第一个字母是大写,也就是公开字段。那么这个结构体就能用JSON.marshal去序列化,变成一个JSON的字符串。序列化之后的字符串也能用JSON.unmarshal去反序列化到一个空的变量里面。这样默认序列化出来的字符串的话,他的风格是大写字母开头,而不是下划线。可以在后面用json tag等语法来修改输出JSON结果里面的字段名

时间处理

import (
    "fmt"
    "time"
)
func main(){
    now := time.Now()
    t := time.Date(2022,3,27,1,25,36,0,time.UTC) //2022-03-27 01:25:36 +0000 UTC
    

在go语言里最常用的就是time.now()来获取当前时间,也可以用time.date去构造一个带时区的时间,可以用.UNIX来获取时间戳

数字解析

import (
    "fmt"
    "strconv"
)
func main(){
    f,_ := strconv.ParseFloat("1.234",64) //1.234
    n,_ := strconv.ParseInt("111",10,64) //111
    n2,_ := strconv.Atoi("123") //123
}

关于字符串和数字类型之间的转换都在STR conv这个包下,这个包是string convert这两个单词的缩写。如果输入不合法,那么这些函数都会返回error