DAY ONE
go的特性
高性能、高并发
标准库
语法简洁
跨平台
快速编译
垃圾回收
开发环境配置
1、安装go语言
2、配置集成开发环境
运行程序:go run xxx.go
编译二进制使用 go build编译,使用./Xxx运行
go强类型语言
字符串、整数、浮点数、布尔型
字符串可以通过加号拼接,也能够直接用等号比较两个字符串
声明变量
var Xxx String(变量类型可以省略,自动推导变量类型) = " "
或者 变量 冒号:= 等于值
常量
const Xxx = , 常量没有确定类型,根据上下文自动确定类型
基础语法
if- else 特点if后没有(),如果写括号,保存时编译器会自动去掉。并且if后必须根大括号
循环:只有for循环,最简单的for循环是在for后面什么都不写代表死循环。在循环中可以使用break或者continue来跳出或继续循环。
Switch分支:Switch后的变量名不需要使用括号,并且不需要加break。可以使用任意的变量类型,还可以取代任意的if else语句。可以在Switch后面不加任何变量,然后在case里面写条件分支。
func main() {
//fmt.Println("hello world")
t := time.Now()
switch{
case t.Hour() < 12:
fmt.Println("Its before noon")
default:
fmt.Println("Its after noon")
}
}
数组:具有编号且长度固定的元素序列
func main() {
var a[5] int
a[4] = 100
fmt.Println(a[4],len(a))//数组可以很方便的取特定索引的值或往特定索引存储值
}
切片slice:不同于数组可以任意更改长度,使用make创建一个切片,像数组一样取值,使用append追加元素,使用append必须把append的结果赋值为原数组
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)
}
slice原理:是存储一个长度和一个容量,加一个指向一个数组的指针,在执行append操作时,如果容量不够的话,会扩容并且返回新的slice.
func main(){
c := make([]string,len(s))
copy(c,s)
fmt.Print(c)//[a b c d e f]
}
fmt.Println(s[2:5])//[c d e]取出s 从2到5中的元素不包括第五个
fmt.Println(s[:5])//[a b c d e]
fmt.Println(s[2:])//[c d e f]
map:哈希/字典,可以使用make创建一个空map,需要两个类型,第一个key,另一个是vlaue。可以从里面去存储或取出键值对,使用delete从里面删除键值对。
m := make(map[string] int)
m["one"] = 1
m["two"] = 2
fmt.Println(m)//map[one:1 two:2]
fmt.Println(len(m))//2
fmt.Println(m["one"])//1
fmt.Println(m["unkonw"])//0
r,ok := m["unknow"]//获取map中有没有这个key存在
fmt.Println(r,ok)// 0 false
delete(m,"one")
fmt.Println(m)//map[two:2]
go中map是完全无序的,遍历时不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序
range:对于一个slice或map,可以使用range来快速遍历,遍历时,对于数组会返回两个值,第一个是索引,第二个是对应位置的值,如果不需要索引时,可以用下划线来忽略。
nums := []int{2,3,4}
sum := 0
for i,num := range nums{
sum += num
if num == 2{
fmt.Println("index:",i,"num",num)//index: 0 num 2
}
}
fmt.Println(sum)//9
q := map[string] string{"a":"A","b":"B"}
for k,v :=range q {
fmt.Println(k,v)//a A b B
}
for k := range q {
fmt.Println("key",k)//key a key b
}
函数:变量类型后置,go中函数原生支持返回多个值,在实际业务逻辑代码中几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息
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
}
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(){
n := 5
//add2(n)
fmt.Println(n)
add2(&n)//为了类型匹配调用时需要加一个&符号
fmt.Println(n)//7
}
func add2(n *int){
*n += 2
}
结构体:是带类型字段的集合
package main
import "fmt"
type user struct{
name string
password string
}
func struct01(){
a := user{name:"wang",password: "44"}
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(ckeckPassword(a,"haa"))//false
fmt.Println(&a,"haha")//false
}
func ckeckPassword(u user,password string) bool{
return u.password == password
}
func checkPassword2(u *user,password string) bool{
return u.password == password
}
结构体支持指针,能够实现对于结构体的修改,也可以在某些情况下避免一些大结构体的拷贝开销
结构体方法:可以为结构体定义一些方法,有两种写法,一种是带指针,一种是不带指针。区别是如果带指针,可以对这个结构体做修改。不带指针,实际操作是一个拷贝,无法对结构体修改
func main(){
e := user{name: "wang",password: "1024"}
e.resetPassword("3333")
fmt.Println(e.checkPassword3("3333"))//true
}
func (u user) checkPassword3(password string) bool{
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
错误处理:使用一个单独的返回值来传递错误信息。可以清晰地知道哪个函数返回了错误,并且能够用简单的if else来处理错误。在函数中可以在函数的返回值类型后面加一个error,就代表这个函数可能返回错误,那么函数实现时,return需要同时return两个值,如果出现错误的话,可以return nil和一个error,如果没有,返回原来的结果和nil
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,error.new("not find")
}
}
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 find
return
}else{
fmt.Println(u.name)
}
}
字符串操作:在标准strings包中有很多字符串工具函数,例如contains判断一个字符串中是否包含里一个字符串,count字符串计数,index查找某个字符串的位置,join连接多个字符串,repeat重复多个字符串,replace替换字符串
package main
import (
"strings"
"fmt"
)
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,"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
}
字符串格式化:标准库fmt中有很多字符串格式相关的方法,可以使用%v打印任意类型的变量,而不需要区分数字字符串,也可以使用%+v打印详细结果,%#v打印更详细的结果
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
}
JOSN处理
对于一个已有的结构体,可以什么都不做,只要保证每个字段的第一个字母是大写,也就是公开字段,那么这个结构就能用JSON.marshaler去序列化,变成一个JSON字符串。序列化的字符串也能够用JSON.unmarshaler去反序列化到一个空的变量里面,这种默认序列化出来的字符串风格是大写字母开头,而不是下划线,可以使用json tag等语法来去修改输出JSON结果里面的字段
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct{
Name string
Age int "josn: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)
fmt.Println(string(buf))//强制类型转换,转成字符串
buf,err = json.MarshalIndent(a,"","\t")//带格式化的反序列化方法
if err != nil{
panic(err)
}
fmt.Print(string(buf))
var b userInfo
err = json.Unmarshal(buf,&b)
if err != nil{
panic(err)
}
fmt.Printf("%#v\n",b)
}
[123 34 78 97 109 101 34 58 34 119 97 110 103 34 44 34 65 103 101 34 58 49 56 44 34 72 111 98 98 121 34 58 91 34 71 111 108 97 110 103 34 44 34 84 121 112 101 83 99 114 105 112 116 34 93 125]
{"Name":"wang","Age":18,"Hobby":["Golang","TypeScript"]}
{
"Name": "wang",
"Age": 18,
"Hobby": [
"Golang",
"TypeScript"
]
}
main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
时间处理
最常用的time.now()来获取当前时间,使用time.date去构造一个带时区的时间
package main
import (
"time"
"fmt"
)
func main(){
now := time.Now()
fmt.Println(now)//2023-05-14 20:17:20.8239169 +0800 CST m=+0.005102001
t := time.Date(2022,3,27,1,25,36,0,time.UTC)
t2 := time.Date(2022,3,27,2,30,36,0,time.UTC)//构造带时区的时间
fmt.Println(t)//2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute())//2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05"))//2022-03-27 01:25:36 //格式化 特定时间
diff := t2.Sub(t)//两个时间相减
fmt.Println(diff)//1h5m0s
fmt.Println(diff.Minutes(),diff.Seconds())//65 3900
t3,err := time.Parse("2006-01-02 15:04:05","2022-03-27 01:25:36")
if err != nil{
panic(err)
}
fmt.Println(t3 == t)//true
fmt.Println(now.Unix())//1684066640 .UNix获取时间戳
}
数字解析
package main
import(
"fmt"
"strconv" //string convert
)
func main(){
f,_ := strconv.ParseFloat("1.234",64)
fmt.Println(f)//1.234
n,_ := strconv.ParseInt("111",10,64)//解析 字符串 进制 位
fmt.Println(n)//111
n1,_ := strconv.ParseInt("0x1000",0,64)
fmt.Println(n1)//4096
n2,_ := strconv.Atoi("123")//Atoi字符串转数字,itoA把数字转成字符串
fmt.Println(n2)//123
n2,err := strconv.Atoi("AAA")//输入不合法,返回error
fmt.Println(n2,err)//0 strconv.Atoi: parsing "AAA": invalid syntax
}
进程信息
package main
import (
"os"
"fmt"
"os/exec"
)
func main(){
fmt.Println(os.Args)//获取进程执行时的命令行参数
fmt.Println(os.Getenv("PATH"))//获取环境变量
fmt.Println(os.Setenv("AA","BB"))
buf,err := exec.Command("grep","127.0.0.1","/etc/hosts").CombinedOutput()//启动子进程,获取输入输出
if err != nil{
panic(err)
}
fmt.Println(string(buf))
}