第一行代码

vscode shift+alt+向下 当前行向下复制一行

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("Hello Albert")
  5. }

命令行运行

装完Golang.msi后

  1. go build -o albertmain.exe main.go //重命名编译
  2. go build main.go //编译成main.exe
  3. go run main.go //在后台编译,直接运行出结果

代码规范

  • 官方推荐使用行注释
  • tab向右,shift+tab向左

    目录结构

    GoPath—src—网站域名—作者/机构—项目名—模块
    image.png

    Dos常用指令

    1. Dos:Disk Operating System磁盘操作系统
    2. md pathname1 pathname2 pathname3 ... 创建目录
    3. rd pathname1 pathname2 删除空目录
    4. rd /q/s pathname 删除目录(不带询问)
    5. rd /s pathname 删除目录(带询问)
    6. dir
    7. cd ..
    8. cd \ 回到根目录
    9. echo filecontent > filename 内容写到文件中
    10. copy originfilename distfilename 拷贝文件
    11. move originfilename distfilename 移动文件(剪切文件)
    12. del filename 删除文件

    变量

    1. //标准声明
    2. var 变量名 变量类型 var strWorkItem string
    3. //批量声明
    4. var(
    5. strWorkItem1 string
    6. strWorkItem2 string
    7. intWorkItem int
    8. doubleWorkItem double
    9. )
    10. //类型推导
    11. var stringCityName = "NanJing"
    12. //短变量声明 := 只能在函数中使用
    13. stringName := "HelloGod"
    14. //匿名变量 一个下划线_
    15. x,_ := Foo() 匿名变量常用于占位,表示忽略值
    16. Func Foo()(int,string){
    17. }
    1. func DeclareVariable() {
    2. //批量声明变量
    3. var(
    4. name string = "Albert"
    5. age int = 25
    6. )
    7. fmt.Println(name)
    8. fmt.Println(age)
    9. }

    常量

    ```go const pi = 3.1415926

//批量声明常量时,如果没有赋值,默认和上一行一致 const( pi2 = 3.14 pi3 = 3.1415 )

  1. <a name="BiqI4"></a>
  2. # iota
  3. iota是golang的常量计数器,只能在常量的表达式中使用。<br />iota在const关键字出现时将被重置为0,const中每新增一行常量申明将使iota计数一次(iota可理解为const语句块中的行索引)。使用iota能简化定义,在定义枚举时很有用。
  4. ```go
  5. //实现类似枚举的效果
  6. const(
  7. n1 = iota //实现类似枚举的效果 0
  8. n2 //1
  9. n3 //2
  10. n4 //3
  11. )
  12. //跨行
  13. const(
  14. a1 = iota //0
  15. a2 = 100
  16. a3 = iota //2,上面新增了一行,计数加1,此处又加1为2
  17. a4
  18. )
  19. //多个常量声明在同一行
  20. const(
  21. b1,b2 = iota+1,iota+2 //此处iota都是0,没有新增一行,b1=1,b2=2
  22. b3,b4 = iota+1,iota+2 //b3=2,b4=3
  23. )
  24. //定义数量级
  25. const(
  26. _= iota
  27. KB = 1<<(10*iota) //1左移10位,2^10
  28. MB = 1<<(10*iota)
  29. GB = 1<<(10*iota)
  30. TB = 1<<(10*iota)
  31. PB = 1<<(10*iota)
  32. )

整型

在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int和uint。

浮点型

默认Golang中的小数都是float64类型
//PrintIn自带换行,但是不支持格式化输出字符串
//Print直接输出内容,Printf格式化输出字符串,Println会输出内容的结尾添加一个换行符
%T是查看类型 %v能查看默认值

  1. package main
  2. import "fmt"
  3. func main() {
  4. f1 := 1.725649
  5. fmt.Printf("此是f1的类型%T",f1)
  6. //fmt.Println("xxx is %f",f1)
  7. //PrintIn自带换行,但是不支持格式化输出字符串
  8. //Print直接输出内容,Printf格式化输出字符串,Println会输出内容的结尾添加一个换行符
  9. fmt.Printf("xxx is %f",f1)
  10. }

复数

complex64和complex128
complex64的实部和虚部为32位,complex128的实部和虚部为64位。

fmt总结

%T 查看类型
%v 查看值
%d 查看十进制
%b 查看二进制
%o 查看八进制
%x 查看十六进制
%s 字符串
%#v 会加一个描述符””

字符串

image.png
内部实现utf-8编码,支持中文。
格式化输出

  1. s1 := `
  2. 世情薄
  3. 人情恶
  4. 雨送黄昏花易落
  5. `
  6. fmt.Println(s1)
  7. fmt.Printf("s1的数据类型是%T",s1)

字符串常用函数

image.png

  • 字符串遍历 同时处理有中文的问题 r:= []rune(str) ```go package main

import ( “fmt” )

func main() { str := “Hello世界” r := []rune(str) for i := 0; i < len(r); i++ { fmt.Printf(“%c\n”,r[i]) } }

  1. - 字符串转整数: n,err := strconv.Atoi("12")
  2. - 整数转字符串: str := strconv.Itoa(1234)
  3. - 字符串转byte[]: var bytes = []byte("hello go") byte转字符串 var str = string([]byte)
  4. - 十进制转其他进制 str = strconv.FormatInt(121454,2) //2-->8,16
  5. - 查找子串是否在指定的字符串中 strings.Contains("seafood"."foo) //true
  6. - 统计一个字符串有几个指定的子串 strings.Count("cecheese","e") //4
  7. - 不区分大小写的字符串比较(==是区分字母大小写的) string.EqualFold("abc","Abc") //true
  8. - 返回子串在字符串第一次出现的index值,如果没有就返回-1 strings.Index("NLT_abc","abc") //4
  9. - 返回子串在字符串最后一次出现的index,如果没有就返回-1 strings.LastIndex("go golang","go") //3
  10. - 将指定的子串替换 strings.Replace("go go hello","go","golang",n) n可以指定你希望替换几个,如果n=-1表示全部替换
  11. ```go
  12. //替换字符串中
  13. str2:= "go go go to home"
  14. tempStr2 := strings.Replace(str2, "go", "golang", 2)
  15. println(tempStr2)
  • 按照指定的某个字符,分割字符串 strings.Split(“Hello,wrod”,”,”)
  • 将字符串的字母大小写转换 strings.ToLower(str) strings.ToUpper(str)
  • 将字符串左右两边的空格去掉 strings.TrimSpace(“ hello albert “) TrimLeft TrimRight

    1. //删除字符串首尾空格
    2. str3 := " hello albert "
    3. tempStr3 := strings.TrimSpace(str3)
    4. //%q可以用双引号将字符串引起来
    5. fmt.Printf("I'm tempStr3:%q", tempStr3)
  • 将字符串左右两边指定的字符去掉 strings.Trim(str,” xxx!”)去掉” “和xxx和!

  • 判断字符串是否以指定字符串开口/结束 strings.HasPrefix(str,”xxx”) strings.HasStufix(str,”xxx”) ```go //判断字符串是否以指定开头或结尾 str4:= “This is free city” fmt.Println(strings.HasPrefix(str4, “This”)) fmt.Println(strings.HasSuffix(str4, “city”))
  1. <a name="hSPcW"></a>
  2. # 数组-值类型
  3. 1. 声明方式: var arrName [len]T (var arrAge [3]int)
  4. 1. 数组初始化
  5. - var testArr [3]int 默认值
  6. - var numArr = [3]int{1,2}
  7. - var cityArr = [...]string{"shanghai","beijing"}
  8. - var stuArr = [...]int{1:3,3:5}
  9. 和C#不同点C#的引用类型:类、接口、委托、预定义类型(object&string&dynamic)、array 值类型:简单类型、枚举、结构体
  10. <a name="K9GgO"></a>
  11. # Slice
  12. Slice是一个拥有相同类型元素的可变长度的序列。引用类型,只能和nil(null)比较<br />声明 var name []T<br />**切片表达式左闭右开**<br />a := [5]int{1,2,3,4,5} 等价于 var a = [5]int{1,2,3,4,5}<br />s := a[1:3] [1,3)索引的值被拿到切片s中<br />fmt.Println(len(s),cap(s)) len(s):2 cap(s) <br />s5 := a[:4] 1,2,3 -->[0:4) <br />**切片的容量是底层数组的容量(长度)**
  13. ```go
  14. func main() {
  15. //切片声明1
  16. var stuList []string
  17. //切片插值
  18. stuList = append(stuList, "AlbertZhao", "AlbertChen")
  19. stuList = append(stuList, "AlbertZhao", "AlbertChen")
  20. //切片遍历,index为索引,可以用_来占位 v是元素,range后是遍历的对象
  21. for index, v := range stuList {
  22. fmt.Println(index, v)
  23. }
  24. //切片声明2
  25. teaList := make([]string, 3, 10)
  26. for _, v := range teaList {
  27. fmt.Println(v)
  28. }
  29. fmt.Println(len(teaList))
  30. //连接切片
  31. totalList := append(stuList, teaList...)
  32. fmt.Println(len(totalList))
  33. fmt.Println("===========")
  34. //切片的赋值拷贝,copy(dst,src []Type) 会拷贝源目标的最小长度到目的的最小长度,最小长度由dst决定
  35. //min(len(dst),len(src))
  36. copytotalList := make([]string, 5, 10)
  37. copy(copytotalList, totalList)
  38. fmt.Println(totalList)
  39. fmt.Println(copytotalList)
  40. for _, v := range copytotalList {
  41. fmt.Println(v)
  42. }
  43. //从切片中删除元素 删除切片index元素,a = append(a[:index],a[index+1:]...)
  44. //删除copytotalList第1个元素AlbertChen
  45. copytotalList = append(copytotalList[:1], copytotalList[2:]...)
  46. fmt.Println(copytotalList)
  47. //排序,排序这种长度不定的数组要先转换为切片再进行排序
  48. var arrAge = [...]int{3, 7, 5, 6, 9, 5, 4, 1}
  49. sort.Ints(arrAge[:])
  50. fmt.Println(arrAge)
  51. for i := 0; i < len(arrAge); i++ {
  52. fmt.Println(arrAge[i])
  53. }
  54. }

指针

Go语言中不存在指针操作,只需要记住两个符号:

  1. & 取地址
    • 根据地址取值

      1. //关于Go中,指针只起到取地址和取值两个功能,前者&,后者*
      2. n:= 18
      3. fmt.Println(&n)
      4. fmt.Println(*(&n))
      5. //Go中空指针赋值问题
      6. //var a *int //没有初始化,对应的地址为nil,*a毫无意义,如何避免new关键字
      7. //*a = 100
      8. //fmt.Println(*a)
      9. //new函数申请一个内存地址
      10. var a = new(int)
      11. *a = 100
      12. fmt.Println(*a)

      new()

      用于值类型变量申请空间

      1. func main(){
      2. a := new(int) //不可以写成 var a *int 然后*a = 100这样申请的是a是nil
      3. *a = 100
      4. fmt.PrintIn(*a) //100输出结果
      5. }

      make()

      用于引用类型的空间申请 slice\map\chan的内存创建

      1. func main(){
      2. //make也是用于内存分配,区别于new,只用于slice、map以及chan的内存创建,而且它返回的类型就是
      3. //这三个类型本身,而不是他们的指针类型,因为这三种类型是引用类型,所以没必要返回他们的指针。
      4. //make函数是无法替代的,在初始化那三个类型,然后才可以对其进行操作。
      5. sourceSlice := make([]string,5,10)
      6. sourceSlice[0] = "ClaireLi"
      7. fmt.Println(sourceSlice)
      8. sourceMap := make(map[string]int,10)
      9. sourceMap["ClaireLi"] = 10
      10. fmt.Println(sourceMap)
      11. }

      map

      Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。C#中即是字典。
      定义:map[KeyType]ValueType map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:make(map[KeyType]ValueType, [cap])
      var 元素为map类型的切片 var mapSlice = make([]map[string]string,3) ```go //声明map字典 scoreMap := make(map[string]int, 10) //最好预估一个容量,避免在程序运行期间再动态扩容 scoreMap[“小明”] = 100 scoreMap[“张三”] = 99 fmt.Println(scoreMap) fmt.Println(scoreMap[“小明”])

      //map的初始化,声明时填充元素 userInfo := map[string]string{“username”:”AlbertZhao”,”password”:”albertzhao”} fmt.Println(userInfo) fmt.Println(userInfo[“username”])

      //判断键值是否存在 value,ok :=userInfo[“username”] //ok是golang中约定的 if ok { fmt.Println(“键值存在”) fmt.Println(value) }

      //map的遍历 for key, value := range userInfo { fmt.Printf(“键为%v,值为%v\n”,key,value) } //只取map中的key值 for key := range userInfo { fmt.Printf(“键为%v\n”,key) } //删除map中的元素 delete(map,key) //即使key不存在也不会报错 delete(userInfo,”username”) fmt.Println(userInfo)

      //对字典进行排序 dicStuManager := make(map[string]int,6) dicStuManager[“AlbertZhao”] = 15 dicStuManager[“AlbertLi”] = 14 dicStuManager[“AlbertChen”] = 13 dicStuManager[“AlbertHuang”] = 12

      listStuNames := make([]string,10,20) for key := range dicStuManager { listStuNames = append(listStuNames, key) }

      sort.Strings(listStuNames) for _, key := range listStuNames { fmt.Println(key,dicStuManager[key]) }

      //写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。 words := “how do you do” splits := strings.Split(words, “ “) dicWords := make(map[string]int,5) for , v := range splits { ,ok := dicWords[v] if !ok {

      1. dicWords[v] = 1

      }else{

      1. dicWords[v]++

      } } fmt.Println(dicWords)

  1. <a name="VjN8v"></a>
  2. # 包
  3. go的每一个文件都属于一个包,go是以包的形式来管理文件和项目目录结构的<br />文件包名通常和文件所在的文件夹名一致,一般为小写字母。 <br />在import包时,路径从$GOPATH的src下开始(不使用go mod inti 统御名称)<br />go mod init xxx <br />go mod tidy<br />import("<br />别名 "路径"<br />)<br />main包只能有一个,可执行程序。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/957395/1637302127781-b1eb32b7-6569-476a-a62a-0bf548a5070b.png#clientId=u3cf6511f-45de-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=479&id=u5faa5f74&margin=%5Bobject%20Object%5D&name=image.png&originHeight=479&originWidth=751&originalType=binary&ratio=1&rotation=0&showTitle=false&size=42304&status=done&style=none&taskId=u5e162175-33f3-41e0-bc56-c4cdd353945&title=&width=751)
  4. <a name="xS1aJ"></a>
  5. # 函数
  6. <a name="td0vT"></a>
  7. ## 判断if switch
  8. else if必须跟在}后面
  9. ```go
  10. //strings.Contains
  11. var cmd = "git commit"
  12. if strings.Contains(cmd, "commit") {
  13. fmt.Println("包含-commit")
  14. } else if strings.Contains(cmd, "-m") {
  15. fmt.Println("包含-m")
  16. } else {
  17. fmt.Println("不包含")
  18. }
  19. switch cmd {
  20. case "git commit -m":
  21. fmt.Println("相等")
  22. case "git commit":
  23. fmt.Println("不相等啊")
  24. fallthrough
  25. default:
  26. fmt.Println("不相等")
  27. }

函数声明及使用

  1. package main
  2. import(
  3. "fmt"
  4. )
  5. func main() {
  6. fmt.Println(sum(6,7))
  7. }
  8. func sum(x int,y int)(ref int) {
  9. return x+y
  10. }
  11. //可变参数:Go语言中的可变参数通过在参数名后加...来标识。
  12. 注意:可变参数通常要作为函数的最后一个参数。
  13. func intSum2(x ...int) int {
  14. fmt.Println(x) //x是一个切片
  15. sum := 0
  16. for _, v := range x {
  17. sum = sum + v
  18. }
  19. return sum
  20. }
  21. ret1 := intSum2()
  22. ret2 := intSum2(10)
  23. ret3 := intSum2(10, 20)
  24. ret4 := intSum2(10, 20, 30)
  25. fmt.Println(ret1, ret2, ret3, ret4) //0 10 30 60

defer

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:
image.png
image.png

函数参数的传递方式

  • 基本数据类型和数组默认都是值传递,即进行值拷贝。在函数内修改,也不会影响到原来的值。
  • 如果希望函数内的变量能够修改函数外的变量,可以传入变量的地址&,函数内以指针指针的方式操作变量。从效果上看类似引用。
  • Go函数不支持重载。

image.png

委托

sensor := 函数A
sensor()执行A
sensor = 函数B
sensor()执行B

闭包

可以这样裂解:闭包是类,函数是操作,n是字段。函数和它使用到的n构成闭包。这个对象并没有被销毁,而是一直累加。
一直在调用匿名函数(相当于get;set;方法)
image.png
闭包的最佳实践

时间和日期函数-time包

  • time.Time类型 用于表示时间 ```go func ShowTime() { //打印当前时间 now := time.Now() fmt.Printf(“now is %v, now type is %T”, now,now) }

//now is 2021-11-22 16:49:00.5368526 +0800 CST m=+0.001499601, now type is time.Time

  1. - 格式化日期和时间:
  2. dataStr := fmt.Sprintf("xxx") Sprintf直接拿到打印的字符串<br />fmt.Printf(now.Format("2006/01/02 15:04:05"))
  3. - 休眠
  4. time.Sleep(100*time.Millisecond)
  5. ```go
  6. //休眠打印
  7. for i := 0; i < 10; i++ {
  8. println(i)
  9. time.Sleep(time.Second)
  10. }
  • 获取当前Unix时间戳和Unixnao时间戳(可以获取随机数字)

rand.Seed(time.Now().Unix())

错误处理

go中错误处理的方式: defer panic recover

defer+recover错误异常处理

  1. defer func(){
  2. err := recover()
  3. if err!=nil{
  4. printIn(err)
  5. //这里可以将错误信息发送给管理员
  6. }
  7. }
  8. //if前也可以有语句
  9. defer func(){
  10. if err := recover();err!=nil{
  11. printIn(err)
  12. //这里可以将错误信息发送给管理员
  13. }
  14. }

自定义错误

Go中程序中,也支持自定义错误,使用errors.New和panic内置函数。

  • errors.New(“错误说明’),会返回一个error类型的值,表示一个错误
  • panic内置函数,接收一个interface{}类型的值(也就是任何值)作为参数。可以接收error类型的变量,输出错误信息,并退出程序
  • image.png

    面向对象编程(OOP)

  • Golang支持面向对象编程特性

  • Golang中没有class,结构体struct和其他语言的类class有同等的地位
  • Golang面向对象去掉了传统OOP语言的继承、方法重载、构造函数和析构函数、隐藏this指针等等
  • Golang中实现方式和其他OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名字段来实现
  • Golang通过接口关联,降低耦合性

    结构体-值类型

    值拷贝,复制不影响原先变量
    image.png
    声明:
    type Person struct{
    }

  • var person Person

  • var person Person = Person{}
  • var person Person = new(Person) (person).Name = “Albert” person.Name = “Json”
  • var person *Person = &Person{} person是一个指针,取地址找到结构体

使用注意事项及细节:

  • 结构体的所有字段
  • struct的每一个字段上,都可以写一个tag,该tag可以通过反射机制获取,常见的场景就是序列化和反序列化。 ``go //定义一个Cat结构体 //打Tag进行小写的转换 type Cat struct{ Name stringjson:”nAme”SubName stringjson:”subname”Age int Color stringjson:”color”` }

// //创建Cat变量 var catOne Cat; catOne.Name = “Xiaochen” catOne.SubName = “XiaochenMiao” catOne.Color = “White” catOne.Age = 2 fmt.Println(catOne)

data,err := json.Marshal(catOne)
if err!=nil{
    defer func(){
        err2 := recover()
        if err2!=nil{
            fmt.Println(err2)
            //这里可以将错误信息发送给管理员
        }
    }()
    fmt.Println("json encoding err  ",err)        
}
fmt.Println("Json后的数据",string(data)) //这里的data是byte[]类型
<a name="G97ZW"></a>
## 结构体方法
表示A结构体有一方法 Test()
```go
type Student struct{
    Name string
    Age int
}


//结构体方法  func (a *A) Test(xxx Type) (xxx Type){}

// *A的好处是不会进行值拷贝,而是地址
func (stu Student)PrintStudentInfo(){
    stu.Name = "AlbertZhao"
    stu.Age = 25
    println(stu.Name+"-"+strconv.Itoa(stu.Age))    
}

image.png
image.png
调用String()方法 &stu
image.png

工厂模式

Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
类比C#中字段private 属性get set方法public

type student struct{
    Name string
    Score float64
}

//函数,构造函数
func NewStudent(n string,s float64) *student{
    return &student{
        Name:n,
        Score:s
    }
}
//结构体字段方法
func (s *student)GetScore() float64{
    return s.score
}

继承、封装、多态

image.png
image.png
image.png
多重继承:如果同名,需要 c.A.Name c.B.Name单独赋值
image.png

接口Interface

image.png
实现多接口
image.png
image.png

接口的最佳实践

实现对Hero结构体切片的排序 sort.Sort(data interface)

//声明Hero结构体
type Hero struct{
    Name string
    Age int
}

//声明一个Hero结构体切片类型
type HeroSlice []Hero

//实现接口Interface
func (hs HeroSlice)Len() int{
    return len(hs)
}

func (hs HeroSlice)Less(i,j int) bool{
    return hs[i].Age <hs[j].Age
}

func (hs HeroSlice)Swap(i,j int){
    //hs[i],hs[j] = hs[j]:hs[i]这句话等价下面的
    temp := hs[i]
    hs[i] = hs[j]
    hs[j] = temp
}

断言

image.png

type Point struct{
    x int
    y int
}

var a interface{}
var point Point = Point{1,2}
a = point
var b Point
b,ok = a.(Point) //断言
if ok == true

文件操作 os.File

image.png

文件常规操作

带缓冲区的方式读取

package main

import (
    "io"
    "bufio"
    "fmt"
    "os"
)

func main() {
    //打开一个文件
    file, err := os.Open("F:\\Repo\\GetStartedWithGolang\\Day1124\\InterfacePractise\\main.go")
    if err != nil {
        fmt.Println("open file err=", err)
    }

    fmt.Println(file)
    //当函数退出时,要及时关闭file,否则会有内存泄漏
    defer file.Close()

    //创建一个*Reader,带缓冲的,读一部分处理一部分缓冲的好处,可以处理比较大的文件
    //默认缓冲大小是4096
    reader := bufio.NewReader(file)
    for{
        str,err := reader.ReadString('\n') //每次读取一行,读到一个换行就结束
        //io.EOF表示文件末尾
        if err == io.EOF{
            fmt.Println("已经读到文件末尾啦,我跳出去啦")
            break;
        }
        //输出内容
        fmt.Print(str)
    }
    fmt.Println("文件读取结束....")
}

小文件一次性读取(使用ioutil一次将整个文件读入到内存中),适用于文件不大的情况。(ioutil.ReadFile)

//小文件读取
func ReadFileOne(path string){
    //一次性读取
    content,err := ioutil.ReadFile(path)  //content []byte
    if err!=nil{
        fmt.Println("Read file err=",err)
    }

    fmt.Println(string(content))  //[]byte转string   string转[]byte  []byte(string)
}

写文件

FileMode在Linux和Unix下使用,Windows不生效。
image.png