Go快速上手

Go介绍

基本介绍

​ Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。

优势

  • 可直接编译成机器码
  • 不依赖其他库
  • 直接运行即可部署
  • 语言层面的并发,能充分利用多核

GO项目

1、云计算基础设施领域
代表项目: dockerkubernetes、 etcd、 consul、 cloudflare CDN、七牛云存储等。
2、基础后端软件
代表项目: tidb、 influxdb、 cockroachdb等 。
3、微服务
代表项目: go-kit、micro、 monzo bank的typhon、bilbili等 。
4、互联网基础设施
代表项目:以太坊、hyperledger等 。

Go不足

  • 包管理,大部分包在github上

  • 所有Excepiton都用Error来处理(比较有争议)。

  • 对C的降级处理,并非无缝,没有C降级到asm那么完美(序列化问题)

环境安装

  • 基本环境

    • win10家庭版

    • IntelliJ IDEA 2021.3.2(GOLAND等也行)

  • go的环境

参考其中的 “环境准备” 章节即可

(3条消息) 以太坊源码阅读1——准备环境_mo_seele的博客-CSDN博客

HelloWorld

创建项目

  • 选择go类型

image-20220315164107754

  • 之后取个名下一步即可

代码

创建main包,在该包下创建 main.go文件

package main

import "fmt"

func main() {
   fmt.Println("hello World")
}

变量与常量

变量声明

  • 声明
var a int
  • 声明+赋值
var b int = 100
  • 省略类型的自动匹配
var c= 100
  • 直接自动匹配(最常用,但只能在函数体内用)
e := 100
  • 声明多个变量
var xx,yy int = 100, 200
  • 多行地声明多个变量
var(
	vv int = 100
    jj bool = true
)

常量声明

  • 常量(只读属性)
const length int = 10
  • const定义枚举
const(
	ONE = 1
    TWO = 2
)
  • iota+const

image-20220315174837044

注:可以在const()添加一个关键字iota,每行的iota都会累加1, 第1行的iota的默认值是0

变量内部结构

image-20220318134620065

函数

基本函数

  • 单返回值函数
func fun(a int, b int) int {
	return a + b
}
  • 多返回值函数
func fun1(a int, b int) (int, int, int) {
	return a, b, a + b
}
  • 带形参的多返回
func fun2(x int, y int) (a int, b int, c int) {
	c = x + y
	return
}
  • 形参简写的多返回
func fun3(x int, y int) (a, b, c int) {
	c = x + y
	return
}

函数调用顺序

image-20220315180128750

导包

  • import _ “fmt” :给fmt起一个别名,匿名,无法使用当前包的方法,但会执行当前的包的内部的init()方法
  • import aa “fmt” ; 给fmt包起一个别名,aa。Println()来调用
  • import . “fmt” 将当前fmt包中的全部方法,导入到当前本包中,可以直接调用

指针

image-20220315200019343

image-20220315200209484

数组

声明

  • 固定长度数组
var arr1 [10] int
myArray2 := [10] int{1,2,3,4}
  • 动态数组(切片)
myarr := []int{1,2,3,4}
  • 动态数组(切片),使用make
var slice[]int = make([]int,3)

遍历

  • 基本遍历(for)
for i := 0; i < len(myArray2); i++ {
   fmt.Println(myArray2[i])
}
  • 增强for
myArray2 := [10]int{1, 2, 3, 4}
for i, v := range myArray2 {
   fmt.Println(i, "", v)
}
  • 匿名遍历
myArray2 := [10]int{1, 2, 3, 4}
for _, v := range myArray2 {
   fmt.Println( v)
}

切片操作

  • 切片追加
myArray2 := make([]int, 3, 5) //3是长度(length),5是容量(cap)
myArray2 = append(myArray2, 1)
myArray2 = append(myArray2, 2)
myArray2 = append(myArray2, 3)
fmt.Println(myArray2)

当append 3 时,由于当前切片的容量不足,会自动追加大小为初始容量的空间

所以append 3 时,容量为10

其他

  • 查看数组类型
fnt.Printf("myArray3 types = T\n",myArray3)
  • 动态数组在传参上是引用传递,而且不同元素长度的动态数组他们的形参是一致。|

  • 判断一个slice为空

if slice == nil{
    。。。
}

其他

defer

会在函数返回后执行,当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出),下面的代码是将一系列的数值打印语句按顺序延迟处理,如下所示:

package main

import (
    "fmt"
)

func main() {

    fmt.Println("defer begin")

    // 将defer放入延迟调用栈
    defer fmt.Println(1)

    defer fmt.Println(2)

    // 最后一个放入, 位于栈顶, 最先调用
    defer fmt.Println(3)

    fmt.Println("defer end")
}
defer begin
defer end
3
2
1

map

创建

  • 声明创建
var myMap map[string]string = make(map[string]string, 10)
myMap["1"] = "one"
myMap["2"] = "two"
  • 省略创建
	myMap2 := make(map[string]string)
	myMap2["1"] = "one"
	myMap2["2"] = "two"
  • 初始化创建
myMap3 := map[string]string{
   "1": "one",
   "2": "two",
   "3": "three",
}

操作

  • 增加
myMap2["1"] = "one"
  • 修改
myMap2["1"] = "three"
  • 遍历
for key, val := range myMap2 {
   fmt.Println(key)
   fmt.Println(val)
}
  • 删除
delete(myMap2, "2")
  • map作为形参时,传递的是指针,是引用传递

对象

  • 基本使用
type Book struct {
   title string
   auth  string
}

func main() {
	var book Book
	book.title = "11"
	book.auth = "22"
	fmt.Println(book)
}
  • 结构体在传参时默认是传值,如果要传地址要用指针

  • 方法(与函数不同,可以理解为是绑定到结构体上的函数)

func (this Book) getTit() {
   fmt.Println(this.Title)
}

func main() {
   book := Book{Title: "11", Auth: "22"}
   book.getTit()
}
  • 当首字母大写时,表示在其他包也能使用,小写则只能在本包使用

  • 继承 (方法也会继承)

type Paper struct {
   Content string
}

type Book struct {
   Paper
   Title string
   Auth  string
}
  • 定义子类对象

image-20220317184923991

接口

  • 多态
type Anima interface {
   Sleep()
   getName() string
}

type Cat struct {
   color string
}

func (Cat Cat) Sleep() {
   fmt.Println("")
}

func (Cat Cat) getName() string {
   return Cat.color
}

func main() {
   var ani Anima = &Cat{color: "66"}
   fmt.Println(ani.getName())
}
  • 断言与万能数据类型
func Sleep(arg interface{}) interface{} {
   //类型断言
   value, ok := arg.(string)
   if ok {
      fmt.Println("ok")
   }
   return value
}

func main() {
   fmt.Println(Sleep("123"))
}

输出

ok
123

反射

  • 基本使用
type User struct {
   name string
   pwd  string
}
func ReflectTry(inter interface{}) {
  //获取类型
	interType := reflect.TypeOf(inter)
	fmt.Printf(interType.Name() + "\n")
	//获取value
	interVal := reflect.ValueOf(inter)
	fmt.Println(interVal)
	//获取属性
	field := interType.Field(0)
	fieldVal := interVal.Field(0)
	fmt.Println(field.Name, field.Type, fieldVal)
}

func main() {
   user := User{name: "123", pwd: "456"}
   ReflectTry(user)
}

输出

User
{123 456}      
name string 123
  • 获取方法
type User struct {
	name string `info:"remark" doc:"我的名字"`
	pwd  string `info:"password" `
}
func (this *User) UserTry() {
   fmt.Print(this)
}

func ReflectTry(inter interface{}) {
   //获取方法
   interType := reflect.TypeOf(inter)
   println(interType.NumMethod())
   println(interType.Method(0).Name)
}

func main() {
   user := User{name: "123", pwd: "456"}
   ReflectTry(&user)
}

输出

1
UserTry
  • Tag标签
type User struct {
   name string `info:"remark" doc:"我的名字"`
   pwd  string `info:"password" `
}
func main() {
   user := User{name: "123", pwd: "456"}

   ReflectTry(&user)
}
func ReflectTry(inter interface{}) {
   //获取类型
   interElem := reflect.TypeOf(inter).Elem()
   println(interElem.NumField())
   tagInfo := interElem.Field(0).Tag.Get("info")
   fmt.Println(tagInfo)
}

输出

2
remark
  • 使用tag进行json化
type User struct {
   Name string `json:"title" `
   Pwd  string `json:"password" `
}

func main() {
   user := User{"名字", "密码"}
   str, err := json.Marshal(user)
   if err != nil {
      return
   }
   fmt.Printf("%s", str)
}

结果

{"title":"名字","password":"密码"}

goroutine 多线程

基本

image-20220318161019489

缺点:

  • 创建、销毁、调度G都需要每个M获取锁,这就形成了激烈的锁竞争。
  • M转移G会造成延迟和额外的系统负载。
  • 系统调用(CPU在M之间的切换)导致频繁的线程阻塞和取消阻塞操作增加了系统开销。

后期修改:

image-20220318161310165

image-20220318161507290

  • 多线程代码
func mulTask() {
   for i := 0; i < 10; i++ {
      fmt.Printf("side---%d\n", i)
      time.Sleep(1 * time.Second)
   }
}

func main() {
   go mulTask()
   for i := 0; i < 10; i++ {
      fmt.Printf("main---%d\n", i)
      time.Sleep(1 * time.Second)
   }
}

输出

main---0
side---0
side---1
main---1
main---2
side---2
......

channel 通信

func mulTask() {
   c <- 666
}

func main() {
   c = make(chan int)
   go mulTask()
   num := <-c
   println(num)
}

image-20220318180427052

select

image-20220318181622772

Go Modules

介绍

Go modules是Go语言的依赖解决方案,发布于Go1.11,成长于Go1.12,丰富于Go1.13,正式于Go1.14推荐在生产上使用。Go moudles目前集成在Go的工具链中,只要安装了Go,自然而然也就可以使用Go moudles了,而Go modules的出现也解决了在Go1.11前的几个常见争议问题:

  1. Go语言长久以来的依赖管理问题。
  2. “淘汰"现有的GOPATH的使用模式。
  3. 统一社区中的其它的依赖管理工具(提供迁移功能)。

Go Path问题

  • 无版本控制概念

  • 无法同步一致第三方版本号

  • 无法指定当前项目引用的第三方版本号

Go mod使用

  • 命令

image-20220318200304110

go mod环境变量

可以通过go env命令来进行查看

image-20220318200417427

G0111MODULE

Go语言提供了G0111MODULE这个环境变量来作为Go modules的开关,其允许设置以下参数:

  • auto: 只要项目包含了go.mod文件的话启用Go modules, 目前在Go1.11至Go1.14中仍然是默认值。
  • or:启用Go modules, 推荐设置,将会是未来版本中的默认值。
  • off: 禁用Go modules, 不推荐设置。

GoProxy

这个环境变量主要是用于设置Go模块代理(Go module proxy) ,其作用是用于使Go在后续拉取模块版本时直接通过镜像站点来快速拉取。
GOPROXY的默认值是: https://proxy.golang.org direct proxy. golang. org国内访问不了,需要设置国内的代理.

  • 阿里云
    https:/ / mirors.aliyun.com/ goproxy/
  • 七牛云
    https:/ / goproxy.cn,direct

其他

image-20220318201949285

参考

8小时转职Golang工程师(如果你想低成本学习Go语言)_哔哩哔哩_bilibili

Logo

开源、云原生的融合云平台

更多推荐