Go语言基础语法 | 青训营笔记

833 阅读11分钟

前言

概览Go语言基础之极速入门(包括Golang安装以及基础语法)

少废话,这次一定GO!

课程目录

  1. 简介
  2. 入门
    • 开发环境
    • 基础语言
    • 标准库

简介

Go语言的优点

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

多说无益,写个代码直接感受Go的魅力:

这个代码块使用的是内置库,实现了一个静态ftp的效果,访问localhost:8080,即可看到该程序

package main

import (
   "net/http"
)

func main() {
   // 处理http的/请求,映射到服务器的文件系统路径:"W:\",进入网页中可以到自己主机W盘的内容
   http.Handle("/", http.FileServer(http.Dir("W:\")))
   // 服务器启动,以localhost:8080访问
   http.ListenAndServe("localhost:8080", nil)
}

效果如下:

image.png

横向对比:Java(仅做个人学习Go,并巩固Java的框架,你可以直接跳到“ GO语言的市场占有 ”,如果还有意见,请关闭该文章,然后去喝一杯咖啡冷静一下。)

SpringBoot并未直接提供该方面的工具类,我的思路是:

  1. 使用SpringMVC返回文件的目录视图
@GetMapping("/")
public String home(Model model) throws IOException {
    // 获取W盘下的文件列表
    File file = new File("W://");
    File[] files = file.listFiles();
    model.addAttribute("files", files);
    return "home";
}
  1. 使用Thymeleaf去拼接出来目录。
  • 导入依赖
  • 配置application.properties
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
  • resources中创建templates目录,写一个home.html
  • 使用Thymeleaf的语法,拿到I/O传过来的文件对象。
<ul>
    <li th:each="file : ${files}">
        <a th:text="${file.getName()}"></a>
    </li>
</ul>
  • 这仅仅只有文件及文件夹的名字显示效果。需要做点击就需要在每个a标签中再处理一下链接请求,后端也要写相应的代码。

不得不说Go内置库在这方面的简易程度还是挺高的。

GO语言的市场占有

市场情况:大厂居多。

目前:字节跳动全面拥抱Go语言(使用率最多)。

配置GO的开发环境

安装

为了这次学习翻资料也将我第一次学习Go时的疑惑解开了!

本地安装

  1. 官网:The Go Programming Language (studygolang.com)
  2. 中文网:Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com)
  3. Go模块代理:七牛云 - Goproxy.cn
我的安装步骤
新的安装方式
  1. 选择中文网的包安装,我是Windows,下载的是msi安装包。
  2. GOROOT:自定义安装目录,例如我的是:U:\Program Files\Go,这个目录是我安装Go语言的目录。
  3. 测试:go version
  4. 找一个目录,执行go mod init 工程的名字,例如:go mod init top.chengyunlai/go-learn,开始学习。

什么,你要问go mod init是干啥的,我只能说就像webpack一样,这就像一个依赖管理文件,记录你的包管理。如果以后你拿功能或者别人拿你工程就能方便的下载对应的依赖了。

GO.16以前(了解即可,跳过吧!)

参考资料,Linux和Windows适用:

  1. 选择中文网的包安装,我是Windows,下载的是msi安装包。
  2. GOROOT:自定义安装目录,例如我的是:U:\Program Files\Go,这个目录是我安装Go语言的目录。
  3. GOPATH:准备一个工作区域,以后写代码用,例如我的是:W:\workSpace\go
    • 在这个工作目录中,新建三个目录:binpkgsrc
  4. 环境变量配置:
    • 在Windows中的系统变量中分别添加GOROOTGOPATH image.png
    • 在path中添加这两个目录的bin image.png
  5. 测试
C:\Users\12579>go version
go version go1.20.4 windows/amd64

解释:在进行Go语言开发的时候,我们的代码总是会保存在$GOPATH/src目录下(也就是我们指定的工作目录)。在工程经过go buildgo installgo get等指令后,会将下载的第三方包源代码文件放在$GOPATH/src目录下,产生的二进制可执行文件放在 $GOPATH/bin目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg 下。引自配置GOPATH - 地鼠文档 (topgoer.cn)

如果我们使用版本管理工具(Version Control SystemVCS。常用如Git)来管理我们的项目代码时,我们只需要添加$GOPATH/src目录的源代码即可。bin 和 pkg 目录的内容无需版本控制。

一图解惑,(图也是截自地鼠文档):

image.png

image.png

云运行

用Github账号登录。

Dashboard — Gitpod

工具选择

  1. VSCode
  2. GoLand(高校免费,如果你有魔法,你自己会就行)

基本语法

新手不一定友好,但课程很干货(无废话)。

资料推荐

这是我很爱的一个社区:

这是一个掘金小册,只是要收费,会员可以用借阅卡,我目前在温习:

这是我的专栏,你可以选择无视:

程序注释

领略go语言的魅力

package main

import (
   "net/http"
)

func main() {
   println("服务器启动")
   // 处理http的/请求,映射到服务器的文件系统路径:"W:\",进入网页中可以到自己主机W盘的内容
   http.Handle("/", http.FileServer(http.Dir("W:\")))
   // 服务器启动,以localhost:8080访问
   http.ListenAndServe("localhost:8080", nil)
}

/**
初探Go的魅力,访问localhost:8080,一个完整的静态资源ftp服务器展现在眼前
*/

hello world

package main  
  
import "fmt"  
  
func main() {  
    fmt.Println("hello world") // hello world  
  
    // 简易版  
    println("hello world") // hello world  
}  
  
/**  
go run [path] 直接运行程序  
go build [path] 将程序编译成二进制,会变成一个exe文件,通过.[path]执行该程序  
*/

类型

package main

func main() {
   // 变量声明的方式,以整型为例子
   var intNum1 int = 0
   var intNum2, intNum3 int = 1, 2
   var intNum4 = 3
   // 简易声明,类型自动推断
   intNum5 := 5

   println(intNum1)
   println(intNum2, intNum3)
   println(intNum4)
   println(intNum5)

   // 常量
   const PI float32 = 3.1415926
   println(PI)

   // 布尔
   flag := false
   println(flag)

   // 浮点型64
   var float64Num float64
   println(float64Num)

   // 浮点型32
   float32Num := float32(float64Num)
   println(float32Num)

   // 字符串
   str := "你好"
   str += "世界"
   println(str)
}

/**

Go是一门强类型的语言
*/

if-else

package main

func main() {
   // if - else
   if 7%2 == 0 {
      // 偶数
      println("7 is even")
   } else {
      // 奇数
      println("7 is odd")
   }

   // if - else if - else
   // num给其一个初始条件 9
   if num := 9; num < 0 { // 如果 num < 0
      println(num, "is negative") // 输出它是一个负数
   } else if num == 0 {
      println(num, "is zero") // 输出它是一个0
   } else {
      println(num, "is positive") // 输出它是一个正数
   }
}

循环

package main

func main() {
   // 死循环=while(true)
   for {
      println(1)
      break
   }

   // 打印0-9
   for i := 0; i < 10; i++ {
      println(i)
   }
}

/**
go没有while,没有do while,只有for循环
*/

switch

package main

import "time"

func main() {
   a := 2
   switch a {
   case 1:
      println("one")
   case 2:
      println("two")
   default:
      println("不知道")
   }

   t := time.Now()
   println(t)
   // 特殊用法,可以取代if - else
   switch {
   case t.Hour() < 12:
      println("早于中午")
   default:
      println("晚于中午")
   }
}

/**
switch自带一个break
*/

数组

package main

import "fmt"

func main() {
   var a [5]int
   a[0] = 100
   println(a[0], 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(twoD)

}

切片

package main

import "fmt"

func main() {
   // 用make创建切片,需要指定初始长度
   s := make([]string, 3)
   s[0] = "a"
   s[1] = "b"
   s[2] = "c"
   fmt.Println("get:", s[2])   // c
   fmt.Println("len:", len(s)) // 3

   // 新增,需要用一个切片变量接收,容量不够会扩容,返回一个新的slice
   s = append(s, "d")
   sNew := append(s, "e", "f")
   fmt.Println(sNew)

   // copy
   a := make([]string, len(sNew))
   copy(a, sNew)
   fmt.Println(a)

   // 切片操作
   fmt.Println(a[:])   // 所有
   fmt.Println(a[2:5]) // 区间[2,5)
   fmt.Println(s[:3])  // [0,3)
   fmt.Println(s[1:])  // 1到结尾

   // 简易定义
   good := []string{"g", "o", "o", "d"}
   fmt.Println(good)
   good = append(good, "!")
   fmt.Println(good)
   // 区别数组:   b := [5]int{1, 2, 3, 4, 5},是需要指定长度的
}

/**
切片是一个可变长数组,不用指定其长度。[]类型
*/

map

package main

import "fmt"

func main() {
   // key string, value int
   m := make(map[string]int)
   m["one"] = 1
   m["two"] = 2
   fmt.Println(m)
   fmt.Println(len(m))     //2
   fmt.Println(m["one"])   //1
   fmt.Println(m["two"])   //2
   fmt.Println(m["three"]) //0

   // ok 获取map中是否有这个key存在
   r, ok := m["three"]
   //fmt.Println(r)
   //fmt.Println(ok)
   fmt.Println(r, ok)

   // 删除key为one
   delete(m, "one")

   // 简易定义
   m2 := map[string]int{"one": 1, "two": 2}
   fmt.Println(m2)

   // 遍历map,同理可以遍历数组
   for key, value := range m {
      println(key, value) // key,value
   }
}

/**
map 是无序的
*/

rang遍历数组和map

package main

import "fmt"

func main() {
   arr := [3]int{}
   arr[0] = 1
   arr[1] = 2
   arr[2] = 3

   // index是数组下标,value对应的值
   for index, value := range arr {
      fmt.Println(index, value)
   }

   m := map[string]int{"one": 1, "two": 2}
   
   //key是map的key,value是map的value
   for key, value := range (m) {
      fmt.Println(key, value)
   }
}

函数

package main

func main() {
   println(add(1, 2))
   println(exists(map[string]int{"one": 1}, "two"))
}

// 函数名add,参数a,b均为int,返回值为int
func add(a, b int) int {
   return a + b
}

// 支持多值返回
func exists(m map[string]int, key string) (int, bool) {
   value, ok := m[key]
   return value, ok
}

指针

package main

func main() {
   n := 5
   add(n)
   println(n) // 5
   add2(&n)
   println(n) // 7
}

// 两个函数均没有返回值
func add(n int) {
   // 此时的n是add的局部变量,修改完后,当add函数执行完毕后,n消亡,不影响外部的变量值
   n += 2
}

func add2(n *int) {
   // 此时的n是一个指针,改动会改变原值
   *n += 2
}

/**
指针的主要用途,对传入的参数进行修改
*/

结构体 - 类 以及 方法

package main

import "fmt"

func main() {
   // 直接初始化时指定
   user := User{name: "chengyunlai", password: "root"}
   fmt.Println(user) // {chengyunlai root}
   // 修改名称
   user.name = "Cheng"
   fmt.Println(user) // {Cheng root}

   // 定义一个变量再指定属性
   user2 := User{}
   user2.name = "GoLang"
   fmt.Println(user2) //{GoLang }

   fmt.Println(checkPassword(&user, "123"))
   fmt.Println(user.checkPassword("123"))

}

type User struct {
   name     string
   password string
}

// 指针可以减少拷贝开销,也可以修改原值
func checkPassword(u *User, pass string) bool {
   return (*u).password == pass
}

// 结构体方法
func (u *User) resetPassword(password string) {
   u.password = password
}

func (u *User) resetUserName(userName string) {
   u.name = userName
}

func (u *User) checkPassword(pass string) bool {
   return (*u).password == pass
}

异常

package main

import (
   "errors"
   "fmt"
)

func main() {
   fmt.Println(isEquals("张三", "李四")) //false 不等
   fmt.Println(isEquals("张三", "张三")) //true <nil>

   value, err := isEquals("张三", "李四")
   if err != nil {
      // 打印错误消息
      fmt.Println(err)
   } else {
      fmt.Println(value)
   }
}

// 错误是error类型,通过errors.xx 创建相应的错误
func isEquals(name, input string) (bool, error) {
   if name != input {
      return false, errors.New("不等")
   }
   // nil 表示 null
   return true, nil
}

字符串工具

package main

import (
   "fmt"
   "strings"
)

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, "lo"))               // true
   fmt.Println(strings.Index(a, "ll"))                   // 2
   fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
   fmt.Println(strings.Repeat(a, 2))                     // hellohello
   //If n < 0, there is no limit on the number of replacements.所以1就是换一个,-1就是换全部
   fmt.Println(strings.Replace(a, "l", "L", 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

}

格式化打印

package main

import "fmt"

// 字符串格式化
func main() {
   // 打印多个变量
   fmt.Println(1, 2)

   // 万能的%v占位符
   s := "hello"
   n := 123
   arr := [3]int{1, 2}
   fmt.Printf("s=%v\n", s)
   fmt.Printf("s=%v\n", n)
   fmt.Printf("s=%v\n", arr)
   // %+v详细
   // %#v更详细

}

json处理

package main

import (
   "encoding/json"
   "fmt"
)

func main() {
   user := User{Name: "Chengyunlai", Age: 24}
   fmt.Println(user) //{Chengyunlai 24}
   // 转json
   res, err := json.Marshal(user)
   fmt.Println(res)         //[123 34 78 97 109 101 34 58 34 67 104 101 110 103 121 117 110 108 97 105 34 44 34 65 103 101 34 58 50 52 125]
   fmt.Println(string(res)) //{"Name":"Chengyunlai","Age":24}
   fmt.Println(err)
   // 反序列化
   user2 := User{}
   json.Unmarshal(res, &user2)
   fmt.Println(user2) //{Chengyunlai 24}

}

// 1 要求结构体的变量首字母为大写
type User struct {
   Name string `json:"name"` // 当转json时指定key的名称
   Age  int    `json:"age"`
}

时间处理

package main

import (
   "fmt"
   "time"
)

func main() {
   now := time.Now()
   fmt.Println(now) //2023-05-12 17:32:12.0876215 +0800 CST m=+0.005319101

   // 构造时间
   t1 := time.Date(2023, 05, 12, 17, 32, 0, 0, time.UTC)
   t2 := time.Date(2023, 05, 12, 18, 32, 0, 0, time.UTC)
   fmt.Println(t1) //2023-05-12 17:32:00 +0000 UTC

   // 方法
   fmt.Println(t1.Year(), t1.Month(), t1.Day(), t1.Hour(), t1.Minute())
   fmt.Println(t1.Format("2006-01-02 15:04:05")) //2023-05-12 17:32:00(无语这个格式。)
   diff := t2.Sub(t1)
   fmt.Println(diff)                           //1h0m0s
   fmt.Println(diff.Minutes(), diff.Seconds()) // 60 3600

   t3, err := time.Parse("2006-01-02 15:04:05", "2023-05-12 17:39:00")
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(t3)
   // 获取时间戳
   fmt.Println(now.Unix()) // 1683884463

}

数字字符串转数字

package main

import (
   "fmt"
   "strconv"
)

func main() {
   // 一个10进制字符串数字,转到X进制,精度是64位
   f, _ := strconv.ParseInt("111", 2, 64)
   fmt.Println(f) // 7

   n, _ := strconv.Atoi("123")
   fmt.Println(n) // 转数字
}

进程

package main

import (
   "fmt"
   "os"
   "os/exec"
)

func main() {
   // go run 这个程序 a b c d
   fmt.Println(os.Args) // [C:\Users\12579\AppData\Local\Temp\go-build3318116271\b001\exe\main.exe a b c d]

   fmt.Println(os.Getenv("PATH")) // 获取环境变量
   //fmt.Println(os.Setenv("AA","BB")) // 写入环境变量

   println("----")
   buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
   if err != nil {
      panic(err)
   }
   fmt.Println(string(buf))
}

/**
进程信息,目前不知道有什么作用
*/