goang基础知识小全

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: goang基础知识小全

1、基础知识:

1.1 命名、变量、操作符、基本输入输出、程序结构等

Go语言的主要特征:

1.自动立即回收。
2.更丰富的内置类型。
3.函数多返回值。
4.错误处理。
5.匿名函数和闭包。
6.类型和接口。
7.并发编程。
8.反射。
9.语言交互性。

Go语言命名

1)首字符可以是任意的Unicode字符或者下划线
2)剩余字符可以是Unicode字符、下划线、数字
3)字符长度不限

25个关键字

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

37个保留字

Constants:    true  false  iota  nil
Types:    int  int8  int16  int32  int64  
          uint  uint8  uint16  uint32  uint64  uintptr
          float32  float64  complex128  complex64
          bool  byte  rune  string  error
Functions:   make  len  cap  new  append  copy  close  delete
             complex  real  imag
             panic  recover

可见性

1)声明在函数内部,是函数的本地值,类似private
2)声明在函数外部,是对当前包可见(包内所有.go文件都可见)的全局值,类似protect
3)声明在函数外部且首字母大写是所有包可见的全局值,类似public

命名

命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。 Go语言从语法层面进行了以下限定:任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。

当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Analysize,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);

命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )

一、变量命名规范

变量命名一般采用驼峰式,当遇到特有名词(缩写或简称,如DNS)的时候,特有名词根据是否私有全部大写或小写。例子:

若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头

var apiClient
var URLString
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
二、常量命名规范

同变量规则,力求语义表达完整清楚,不要嫌名字长。

如果模块复杂,为避免混淆,可按功能统一定义在package下的一个文件中。

const todayNews = "Hello"
// 如果超过了一个常量应该用括号的方法来组织
const (
  systemName = "What"
  sysVal = "dasdsada"
)

常量均需使用全部大写字母组成,并使用下划线分词

const APP_URL = "https://www.baidu.com"

如果是枚举类型的常量,需要先创建相应类型:

type Scheme string
const (
    HTTP  Scheme = "http"
    HTTPS Scheme = "https"
)
三、接口命名规范

单个函数的接口名以 er 为后缀

type Reader interface {
    Read(p []byte) (n int, err error)
}
两个函数的接口名综合两个函数名,如:
type WriteFlusher interface {
    Write([]byte) (int, error)
    Flush() error
}
三个以上函数的接口名类似于结构体名,如:
type Car interface {
    Start() 
    Stop()
    Drive()
}
四、结构体命名规范

结构体名应该是名词或名词短语,如Account,Book,避免使用Manager这样的。如果该数据结构需要序列化,如json, 则首字母大写, 包括里面的字段。

采用驼峰命名法,首字母根据访问控制大写或者小写

struct 申明和初始化格式采用多行,例如下面:

type MainConfig struct {
    Port string `json:"port"`
    Address string `json:"address"`
}
config := MainConfig{"1234", "123.221.134"}
五、receiver命名规范

golang 中存在receiver 的概念 Receiver 的名称应该缩写,应该尽量保持一致, 避免this, super,等其他语言的一些语义关键字如下

type A struct{}
func (a *A) methodA() {
}
func (a *A) methodB() {
  a.methodA()
}
六、函数/方法命名规范

由于Golang的特殊性(用大小写来控制函数的可见性),除特殊的性能测试与单元测试函数之外, 都应该遵循如下原则

  • 采用驼峰式。将功能及必要的参数体现在名字中, 不要嫌长, 如updateById,getUserInfo.
  • 如果包外不需要访问请用小写开头的函数
  • 如果需要暴露出去给包外访问需要使用大写开头的函数名称

一个典型的函数命名方法如下:

// 注释一律使用双斜线, 对象暴露的方法
func (*fileDao) AddFile(file *model.File) bool {
  result := db.NewRecord(*file)
  if result {
   db.Create(file)
  }
  return result
}
// 不需要给包外访问的函数如下
func removeCommaAndQuote(content string) string {
  re, _ := regexp.Compile("[\\`\\,]+")
  return strings.TrimSpace(re.ReplaceAllString(content, ""))
}

变量

变量声明

Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。并且Go语言的变量声明后必须使用。

标准声明

Go语言的变量声明格式为:

var 变量名 变量类型

变量声明以关键字var开头变量类型放在变量的后面行尾无需分号。 举个例子:

var name string
var age int
var isOk bool
批量声明

每声明一个变量就需要写var关键字会比较繁琐,go语言中还支持批量变量声明:

var (
    a string
    b int
    c bool
    d float32
)
变量的初始化

Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如:

  • 整型和浮点型变量的默认值为0。
  • 字符串变量的默认值为空字符串。
  • 布尔型变量默认为false。
  • 切片、函数、指针变量的默认为nil。

当然我们也可在声明变量的时候为其指定初始值。变量初始化的标准格式如下:

var 变量名 类型 = 表达式

举个例子:

var name string = "pprof.cn"
var sex int = 1

或者一次初始化多个变量

var name, sex = "pprof.cn", 1
类型推导

有时候我们会将变量的类型省略,这个时候编译器会根据等号右边的值来推导变量的类型完成初始化。

var name = "pprof.cn"
var sex = 1
短变量声明

在函数内部,可以使用更简略的 := 方式声明并初始化变量

package main
import (
    "fmt"
)
// 全局变量m
var m = 100
func main() {
    n := 10
    m := 200 // 此处声明局部变量m
    fmt.Println(m, n)
}
匿名变量

在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。 匿名变量用一个下划线_表示,例如:

func foo() (int, string) {

return 10, “Q1mi”

}

func main() {

x, _ := foo()

_, y := foo()

fmt.Println(“x=”, x)

fmt.Println(“y=”, y)

}

匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。 (在Lua等编程语言里,匿名变量也被叫做哑元变量。)

注意事项:

函数外的每个语句都必须以关键字开始(var、const、func等)
:=不能使用在函数外。
_多用于占位,表示忽略值。

常量

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。

const pi = 3.1415
const e = 2.7182

声明了pi和e这两个常量之后,在整个程序运行期间它们的值都不能再发生变化了。

多个常量也可以一起声明:

const (
    pi = 3.1415
    e = 2.7182
)

const同时声明多个常量时,如果省略了值则表示和上面一行的值相同。 例如:

const (
    n1 = 100
    n2
    n3
)

上面示例中,常量n1、n2、n3的值都是100。

iota

iota是go语言的常量计数器,只能在常量的表达式中使用。

iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。

使用iota能简化定义,在定义枚举时很有用。

举个例子:

const (
        n1 = iota //0
        n2        //1
        n3        //2
        n4        //3
    )

几个常见的iota示例:

使用_跳过某些值

const (
        n1 = iota //0
        n2        //1
        _
        n4        //3
    )

iota声明中间插队

const (
        n1 = iota //0
        n2 = 100  //100
        n3 = iota //2
        n4        //3
    )
const n5 = iota //0
定义数量级 、

(这里的<<表示左移操作,1<<10表示将1的二进制表示向左移10位,也就是由1变成了10000000000,也就是十进制的1024。同理2<<2表示将2的二进制表示向左移2位,也就是由10变成了1000,也就是十进制的8。)

const (
        _  = iota
        KB = 1 << (10 * iota)
        MB = 1 << (10 * iota)
        GB = 1 << (10 * iota)
        TB = 1 << (10 * iota)
        PB = 1 << (10 * iota)
    )

多个iota定义在一行

const (
        a, b = iota + 1, iota + 2 //1,2
        c, d                      //2,3
        e, f                      //3,4
    )

操作符

运算符

Go 语言内置的运算符有:

算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
算数运算符

运算符

+ 相加
  - 相减
  * 相乘
  / 相除
  % 求余

注意: ++(自增)和–(自减)在Go语言中是单独的语句,并不是运算符。

关系运算符

运算符

==  检查两个值是否相等,如果相等返回 True 否则返回 False。
!=  检查两个值是否不相等,如果不相等返回 True 否则返回 False。
> 检查左边值是否大于右边值,如果是返回 True 否则返回 False。
>=  检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
< 检查左边值是否小于右边值,如果是返回 True 否则返回 False。
<=  检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。
逻辑运算符

运算符

&&  逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。
! 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。
位运算符

位运算符对整数在内存中的二进制位进行操作。

运算符

& 参与运算的两数各对应的二进位相与。(两位均为1才为1)
  l 参与运算的两数各对应的二进位相或。(两位有一个为1就为1)
  ^ 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。(两位不一样则为1)
  <<  左移n位就是乘以2的n次方。“a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。
  >>  右移n位就是除以2的n次方。“a>>b”是把a的各二进位全部右移b位。
赋值运算符

运算符

= 简单的赋值运算符,将一个表达式的值赋给一个左值
  +=  相加后再赋值
  -=  相减后再赋值
  *=  相乘后再赋值
  /=  相除后再赋值
  %=  求余后再赋值
  <<= 左移后赋值
  >>= 右移后赋值
  &=  按位与后赋值
  l=  按位或后赋值
  ^=  按位异或后赋值

基本输入输出

01、获取输入

Scan、Scanf和Scanln从标准输入os.Stdin读取文本;用于获取用户输入的数据

1、fmt.Scan
func Scan(a ...interface{}) (n int, err error)

fmt.Scan从标准输入中扫描用户输入的数据,将以空白符分隔的数据分别存入指定的参数。

fmt.Scan在没有扫描完所有变量之前是不会结束扫描的

2、fmt.Scanf
func Scanf(format string, a ...interface{}) (n int, err error)

要加上格式

fmt.Scanf类似于C语言中的Scanf,格式化输入,函数参数前面是格式字符,后面是变量地址。

fmt.Scanf不同于fmt.Scan简单的以空格作为输入数据的分隔符,fmt.Scanf通过格式空字符%为输入数据指定了具体的输入内容格式,只有按照格式输入数据才会被扫描并存入对应变量。

3、fmt.Scanln
func Scanln(a ...interface{}) (n int, err error)

Scanln类似Scan,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。

本函数返回成功扫描的数据个数和遇到的任何错误。

fmt.Scanln遇到换行即结束扫描,如果还有没输入的变量值,该变量会按默认值处理

func main() {
  var (
    name    string
    age     int
    married bool
  )
  fmt.Scanln(&name, &age, &married)
  fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}
张三 19 false
扫描结果 name:张三 age:19 isMale:false
bufio.NewReader

输入的内容包含空格,但是前面三种输入的方式都将空格视为分隔符,无法输入包含空格的字符串。

os.Stdin:

os.Stdin指向标准输入文件/dev/stdin,即os.Stdin是标准输入文件/dev/stdin的指针
func main() {
   reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
   fmt.Print("请输入内容:")
   text, _ := reader.ReadString('\n') // 读到换行
   text = strings.TrimSpace(text)
   fmt.Printf("%#v\n", text)
}
5、Fscan系列

这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从io.Reader中读取数据。

func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
6、Sscan系列

这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从指定字符串中读取数据。

func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
02、输出数据
1、fmt.Print
func Print(a ...interface{}) (n int, err error)
2、fmt.Printf
3、fmt.Println
4、Fprint

Fprint系列函数会将内容输出到一个io.Writer接口类型的变量w中,我们通常用这个函数往文件中写入内容。

func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
5、Sprint

Sprint系列函数会把传入的数据生成并返回一个字符串。

func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string
s1 := fmt.Sprint("沙河小王子")
name := "沙河小王子"
age := 18
s2 := fmt.Sprintf("name:%s,age:%d", name, age)
s3 := fmt.Sprintln("沙河小王子")
fmt.Println(s1, s2, s3)

拼接

6、Errorf

Errorf函数根据format参数生成格式化字符串并返回一个包含该字符串的错误。

03、格式字符

占位符 说明

%v  值的默认格式表示
  %+v 类似%v,但输出结构体时会添加字段名
  %#v 值的Go语法表示
  %T  打印值的类型
  %%  百分号
fmt.Printf("%v\n", 100)
  fmt.Printf("%v\n", false)
  o := struct{ name string }{"小王子"}
  fmt.Printf("%v\n", o)
  fmt.Printf("%+v\n", o)
  fmt.Printf("%#v\n", o)
  fmt.Printf("%T\n", o)
  fmt.Printf("100%%\n")
100
false
{小王子}
{name:小王子}
struct { name string }{name:"小王子"}
struct { name string }
100%
1、布尔型

占位符

%t  true或false
2、整型

占位符 说明

%b 表示为二进制

%c 该值对应的unicode码值

%d 表示为十进制

%o 表示为八进制

%x 表示为十六进制,使用a-f

%X 表示为十六进制,使用A-F

%U 表示为Unicode格式:U+1234,等价于”U+%04X”

%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示

n := 65
fmt.Printf("%b\n", n)
fmt.Printf("%c\n", n)
fmt.Printf("%d\n", n)
fmt.Printf("%o\n", n)
fmt.Printf("%x\n", n)
fmt.Printf("%X\n", n)
1000001
A
65
101
41
41
3、浮点数与复数

占位符 说明

%b  无小数部分、二进制指数的科学计数法,如-123456p-78
    %e  科学计数法,如-1234.456e+78
    %E  科学计数法,如-1234.456E+78
    %f  有小数部分但无指数部分,如123.456
    %F  等价于%f
    %g  根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
    %G  根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
f := 12.34
fmt.Printf("%b\n", f)
fmt.Printf("%e\n", f)
fmt.Printf("%E\n", f)
fmt.Printf("%f\n", f)
fmt.Printf("%g\n", f)
fmt.Printf("%G\n", f)
6946802425218990p-49
1.234000e+01
1.234000E+01
12.340000
12.34
12.34
4、字符串和[]byte
%s  直接输出字符串或者[]byte
%q  该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x  每个字节用两字符十六进制数表示(使用a-f)
%X  每个字节用两字符十六进制数表示(使用A-F)
s := "小王子"
fmt.Printf("%s\n", s)
fmt.Printf("%q\n", s)
fmt.Printf("%x\n", s)
fmt.Printf("%X\n", s)
小王子
"小王子"
e5b08fe78e8be5ad90
E5B08FE78E8BE5AD90
5、指针

占位符 说明

%p  表示为十六进制,并加上前导的0x
6、宽度标识符
占位符 说明
%f  默认宽度,默认精度
%9f 宽度9,默认精度
%.2f  默认宽度,精度2
%9.2f 宽度9,精度2
%9.f  宽度9,精度0
7、其他flag

占位符 说明

’+’ 总是输出数值的正负号;对%q(%+q)会生成全部是ASCII字符的输出(通过转义);
      ’ ‘ 对数值,正数前加空格而负数前加负号;对字符串采用%x或%X时(% x或% X)会给各打印的字节之间加空格
      ’-’ 在输出右边填充空白而不是默认的左边(即从默认的右对齐切换为左对齐);
      ’#’ 八进制数前加0(%#o),十六进制数前加0x(%#x)或0X(%#X),指针去掉前面的0x(%#p)对%q(%#q),对%U(%#U)会输出空格和单引号括起来的go字面值;
      ‘0’ 使用0而不是空格填充,对于数值类型会把填充的0放在正负号后面;

程序结构

// 当前程序的包名
package main
// 导入其他包
import . "fmt"
// 常量定义
const PI = 3.14
// 全局变量的声明和赋值
var name = "gopher"
// 一般类型声明
type newType int
// 结构的声明
type gopher struct{}
// 接口的声明
type golang interface{}
// 由main函数作为程序入口点启动
func main() {
    Println("Hello World!")
}

原始基本数据类型

bool
    string
    Numeric Types
    int8,int16,int32,int64,int
    uint8,uint16,uint32,uint64,uint
    float32,float64
    complex64,complex128
    byte
    rune

(1)布尔型bool

布尔型的值只可以是常量true或者false。

(2)数值类型

整数

int8 有符引8位整型(-128到127) 长度:8bit
      int16 有符号16位整型(-32768到32767)
      int32 有符号32位整型(-2147483648到2147483647)
      int64 有符号64位整型(-9223372036854775808到9223372036854775807)
      uint8 无符号8位整型(0到255) 8位都用于表示数值
      uint16 无符号16位整型(O到65535)
      uint32 无符号32位整型(0到4294967295)
      uint64 无符号64位整型(O到18446744073709551615)

浮点型

float32 IEEE-75432位浮点型数
      float64 lEEE-75464位浮点型数

复数

complex64 32位虚数和实数
      complex128 64位虚数和实数

其他类型

byte 类似uint8
      rune 类似int32
      uint 32位或64位
    uintptr 无符号整型,用于存放一个指针

字符串

字符串就是一串固定长度的字符(byte)连接起来的字符序列。

Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本

单引号和双引号的区别:

比如说’A’,那么该类型是int32,表示的是ASCII码 65;

"A"的类型是string,就是代表字符串A、

转义字符: \

\n 换行 \t制表符

特殊符号,单引号和双引号嵌套使用

(3)数据类型转换:Type Convert

语法格式: Type(Value)

常数:在有需要的时候,会自动转型

变量:需要手动转型 T(V)

注意:兼容类型可以转换

浮点类型在转成整数类型时只是保留了整数部分,不存在四舍五入。

原始复合数据类型

Go语言口基本的复合数据类型有指针、数组、切片、字典、通道、结构、接口

1、数组

● 数组: 是指一系列同一类型数据的集合。是在内存中连续存储的多个相同类型的数据集合。

● 数组是值类型

● 数组支持==与!=

● 在数组中找具体元素使用下标,下标是从0始的,最大为数组个数减一。

● 定义格式:

var arr [10]int

● 初始化 ①:

var arr [10]int 
var arr [10]int = [10]int{1,2,3,4}//(初始化部分元素值,其他值均为0)
arr := [10]int{1,2,3,4}
arr := [10]int{1,2,3,4,6:8}//(6:8 表示下标为6的赋值为8)
arr := [...]int{1,2}
fmt.Println(arr)//[1 2]  //(使用自动类型推导可以根据元素个数创建数组)
fmt.Printf("%T",arr)//[2]int

1.1、len(参 数)作用

● 获取字符串有效长度

● 获取不定参函数参数个数

● 计算数组元素个数

1.2、注意

● 数组在定义后元素个数就已经固定不能添加。

● 小于0个或者大于定义个数的数组下标,都会引发数组越界

数组是一个常量,不允许赋值(可以给数组中的每一个元素赋值),数组名代表整个数组,只能允许相同类型进行赋值(即相同数组长度)。

● 数组名也可以表示数组的首地址,即 &arr == &arr[0]。

● 注意: 数组作为函数参数是 值传递 的(不是 地址 传递)

1.3、二维数组

var 数组名 [行个数][列个数]数据类型

var a [3][2]int
a = [3][2]int{[2]int{1, 2}, [2]int{3, 4}, [2]int{5, 6}}

2、随机数

import (
  "math/rand"
  "time"
)
rand.Seed(time.Now().UnixNano())
for i:=0;i<10;i++{
  fmt.Println(rand.Intn(123))
}

3、切片

切片: 长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,所以可以将切片理解“动态数组”,但它不是数组,切片存在堆区。切片不保存具体的值,像一个框,底层是数组。

注意: 切片之间是不能比较的,不能使用 == 操作符来判断两个切片是否含有全部相等元素。切片唯一合法的比较操作是和nil比较。一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是不能说一个长度和容量都是0的切片一定是nil

定义:var 切片名 []数据类型

var s []int

定义指定长度的切片(make()函数创建)

make([]T,size,cap)

T:切片的元素类型,size:切片中元素的数量,cap:切片的容量

a := make([]int, 8,10)

简单赋值:

a := make([]int, 8)
a[0] = 1

切片的追加、拷贝、删除注意事项

追加:

● 切片的每一次追加数据,可能会导致切片的首地址发生变化。(如果容量扩充导致输出存储溢出切片会自动找寻新的空间存储数据。)

● 调用append函数必须用原来的切片变量接收返回值append追加元素,原来的底层数组放不下的时候,Go底层就会把底层数组换一个。所以需要接收返回值。

● 切片中,把一个切片中的内容append都另一个切片,用 … 拆分

s1 := []string{"北京", "上海", "深圳"}
    //s1[3] = "广州" //错误的写法会导致编译错误:索引越界
    ss := []string{"武汉","西安","苏州"}
    s1 = append(s1, ss...)//...表示拆开

拷贝:

拷贝后的元素是新的,修改后不会改变原来切片的值

删除:

Go语言中没有专门的删除操作,可以利用切片特性,自己封装。

// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
//要删除索引为3的元素
a = append(a[:3], a[4:]...)
fmt.Println(a) //[30 31 32 34 35 36 37]

切片做为函数参数和返回值

作为参数

形参和实参使用的是同一内存地址空间,传递时是地址传递

引用计数

在函数内部append切片

注意:如果使用append操作切片可能改变切片的地址,需要使用返回值给实参赋值。

func test111(s []int){
  s=append(s, 4,5,6)
  fmt .Printf( "%p\n",s)//0xc0000c2060
  fmt .Println(s)//[1 2 3 4 5 6]
}
func main(){
  s:=[]int{1,2,3}
  fmt.Printf("%p\n",s)//0xc0000ae090
  test111(s)
  fmt.Println(s)//[1 2 3]
}

所以要特别的注意,传递slice的时候,最好是返回值安全一些。

常用操作

① append()添加元素(在长度后添加):

s = append(s, 2,3,4)
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)//[0 0 0 0 0 1 2 3]

② len()查询切片使用的长度

b := len(a)

③ cap()查询切片整体容量(容量大于等于使用长度)

容量扩展规则:

★ 如果新容量大于原来的2倍,则现容量就是新容量

★ 如果不超过1024字节,每次扩展都是上次的2倍

★ 如果超过,每次扩展是上次的四分之一

fmt.Println(cap(s))

④ copy(切片1,切片2)复制切片:将s1切片中的数据拷贝到s2中,s2中要有足够的容量

s1:=[]int{1,2,3,4,5}
s2:=make([]int,5)
copy(s2,s1)
copy(s2,s1[1:3])//如果想拷贝切片中的片段,需要使用截取

⑤ for遍历

for i, v := range s {
     fmt.Println(i, v)
  }

切片的注意事项

var a = make([]int, 5, 10) //创建切片,长度为5,容量为10
fmt.Println(a)//[0 0 0 0 0]
for i := 0; i < 10; i++ {
  a = append(a, i)
}
fmt.Println(a) //[0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]

4、Map

字典结构(建议读map类型)

定义格式

由键值对构成无序的,内部由hash实现的数据结构,是引用类型。

● 定义字典结构使用map关键字,[]中指定的是键(key)的类型后面紧跟着的是值的类型。

键的类型,必须是支持==和!=操作符的类型,切片、函数以及包含切片的结构类型不能作为字典的键,使用这些类型会造成编译错。

● 值的类型,任意都可以。

● map类型的长度是自动扩容的,长度和容量始终保持相等。

● map中的数据存储是无序的,所以遍历用rang遍历。

● map 中的键必须是唯一的,在定义时不唯一会报错。

● map 中的键没有的时候,取值会得到对应类型的默认值

map[keyType]valueTye
var  ml map[int]string
m :=make(map[int]string,50)//尽量预估数量,避免动态扩容
n := map[string]int{"赵四": 10}

对map元素的操作

ok(自己定义的,约定用Ok接收返回bool值)判断值存在与否

m := map[int]string{1: "hell", 2: "o", 3: "word"}
value, ok := m[1]
if ok == true {
  fmt.Println("m[1] = ", value)
} else {
  fmt.Println("key不存在")
}

rang遍历map

m := map[int]string{1: "hell", 2: "o", 3: "word"}
for k,v:=range m{
  fmt.Println(k,v)
}

delete删除元素:注意,delete删除map中的元素的时候,没有相应键是不会报错的

m := map[int]string{1: "hell", 2: "o", 3: "word"}
delete(m,1)

map作为函数参数

Map做为函数参数是引用传递(地址传递)

Map在函数中添加数据是允许的,实参也会被影响。

func demo(m map[int]string) {
  m[102] = "李四"
  m[103] = "王五"
  delete(m, 101)
}
func main() {
  m := make(map[int]string, 1)
  m[101] = "张三"
  demo(m)
  fmt.Println(m)//map[102:李四 103:王五]
}

练习

统计一串字符(20个)的每个的单词数目

var arr [20]byte
for i := 0; i < len(arr); i++ {
  fmt.Scanf("%c", &arr[i])
}
m := make(map[byte]int)
for i := 0; i < len(arr); i++ {
  m[arr[i]] ++
}
for k, v := range m {
  if v > 0 {
   fmt.Printf("%c %d\n", k, v)
  }
}

将map里面的值按照键的前后顺序排序

rand.Seed(time.Now().UnixNano())
var scoreMap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
  key := fmt.Sprintf("stu%02d", i)  //生成stu开头的字符串
  value := rand.Intn(100)      //生成0~99的随机整数
  scoreMap[key] = value
}
fmt.Println(scoreMap)
//取出map中的所有key存入切片keys
var keys = make([]string, 0, 280)
for key := range scoreMap {
  keys = append(keys, key)
}
//对切片进行排序
sort.Strings(keys)
//按照排序后的key遍历map
for _,key := range keys {
  fmt.Println(key, scoreMap[key])
}

5、结构体

Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct 。

也就是我们可以通过struct 来定义自己的类型了。

Go语言中通过struct 来实现面向对象。

结构体是值类型。

5.1、定义结构体

type Student struct{
id int
name string
}

type后面跟着的是结构体的名字Student, struct表示定义的是-一个结构体。

大括号中是结构体的成员,注意在定义结构体成员时,不要加var。

type Student struct{
  id int
  name,sex string
}
func main(){
  var s Student
  s.name="张三"
  s.id = 1
  s.sex="男"
  var s1 Student = Student{1,"李四"}
  s2:=Student{id:10,name:"李四"}
}

5.2、结构体初始化

//类构造函数初始化:约定成俗,用new开头
type person struct {
  name string
  age  int
}
//类似构造函数 返回值是结构体 
func newPerson(name string, age int) person {
  return person{
   name: name,
   age:  age,
  }
}
//类似构造函数 返回值是指针
func newPerson2(name string, age int) *person {
  return &person{
   name: name,
   age:  age,
  }
}
p1 := newPerson("张三", 18)
p2 := newPerson2("李四", 9000)
fmt.Println(p1, p2)//{张三 18} &{李四 9000}

对于函数返回值是结构体还是结构体指针看具体的结构体内的成员:

  • 当结构体内变量少时,选哪个都无所谓。
  • 当结构体内变量多时,尽量选结构体指针。
  • 为的是减少程序运行时候的内存开销。

5.3、赋值与比较

赋值(值赋值,两个不会相互影响

s2:=Student{id:10,name:"李四"}
s3 :=s2

比较,结构体可以使用 == 或 != 来进行成员的值的比较操作

也可以用 > < 来比较成员(int 或 bool)

s3 :=s2
fmt.Println(s2==s3)//true

5.4、结构体数组/切片

var 结构体数组名 [元素个数]结构体类型

var ss [8]Student
ss[0].id=1
ss[0].name = "王五"
var ss []Student//切片

● 结构体数组可以进行排序(根据结构体成员)。

● 结构体数组中的元素允许相互赋值交换位置。

● 结构体切片添加信息

var s5 []Student
s5 = append(s5,Student{10,"赵四"})

5.5、结构体作为map中的值

m := make(map[int]Student)
m[101] = Student{101, "张三"}
m[102] = Student{102, "李四"}

Map和结构体切片

m := make(map[int][]Student)
m[101] = append(m[101],Student{101, "张"}
          ,Student{102, "王五"})
m[101] = append(m[101],Student{103, "李四"})
fmt.Println(len(m[101]))//3
fmt.Println(m[101])//[{101 张三} {102 王五} {103 李四}]

5.6、结构体作为函数参数

结构体作为参数传递属于值传递

func test123( student Student){
  student.name= "李逵"
}
func main() {
  stu:=Student{101,"宋江"}
  test123(stu)
  fmt.Println(stu)//{101 宋江}
}

5.7、创建结构体类型的指针

p1 := new(Student)
p1.name = "张三"  //go语言语法糖,会转化成下面那种格式
(*p1).id = 123
fmt.Println(p1)//&{123 张三}
var p2 = &Student{
  name: "李四",
  id:  111,
}
fmt.Println(p2)//&{111 李四}
var p3 = &Student{
  1234,
  "王五",
}
fmt.Println(p3)//&{1234 王五}

5.8、匿名结构体

//匿名结构体
var s struct {
  name string
  age  int
}

5.9、结构体内存布局

结构体内的字段是连续的

type x struct {
  a int8 // 8bit 一》1byte
  b int8
  c int8
}
func main() {
  m := x{
   a: int8(18),
   b: int8(20),
   c: int8(30),
  }
  fmt.Printf("%p\n", &(m.a))
  fmt.Printf("%p\n", &(m.b))
  fmt.Printf("%p\n", &(m.c))
}

5.10、结构体匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。

type people struct {
  string
  int
}
func main() {
  var p = people{"张三", 11}
  fmt.Println(p.string)//张三
  fmt.Println(p.int)//11
  fmt.Println(p)//{张三 11}
}

5.11、结构体嵌套

type people struct {
  name string
  age  int
  addr address
}
type address struct {
  province string
  city   string
}
func main() {
  var p = people{
   "张三",
   11,
   address{
     "湖北",
     "武汉",
   }}
  fmt.Println(p) //{张三 11 {湖北 武汉}}
}

对于嵌套的结构体,Go语言的语法糖

type people struct {
  name string
  age  int
  address//将结构体写成匿名嵌套形式,就可以将结构体内的成员当成本结构体的成员直接访问
}
type address struct {
  province string
  city   string
}
func main() {
  var p = people{
   "张三",
   11,
   address{
     "湖北",
     "武汉",
   }}
  fmt.Println(p.province) //湖北 先在自己结构体找这个字段,找不到就去匿名嵌套的结构体中查找该字段
  fmt.Println(p) //{张三 11 {湖北 武汉}}
}

当匿名嵌套结构体字段冲突时,只能全写,不能省略

5.12、结构体模拟“继承”

//动物的结构体(模拟动物类)
type animal struct {
  name string
}
//给animal结构体实现一个移动的方法
func (a animal) move() {
  fmt.Printf("%s在动!\n", a.name)
}
//狗的结构体(模拟狗类)
type dog struct {
  id uint8
  animal //anima拥有的方法,dog也有了
}
//给dog结构体实现一个汪汪汪的方法
func (d dog) wang() {
  fmt.Printf("%s叫:汪汪汪\n", d.name)
}
func main() {
  var d = dog{
   1,
   animal{
     "小李子",
   },
  }
  fmt.Println(d.id) //1
  fmt.Println(d.name) //小李子
  fmt.Println(d)    //{1 {小李子}}
  d.move()       //小李子在动!
  d.wang()       //小李子叫:汪汪汪
}

5.13、结构体与JSON

type people1 struct {
  name string
  age  int
  address1//将结构体写成匿名嵌套形式,就可以将结构体内的成员当成本结构体的成员直接访问
}
type address1 struct {
  province string
  city   string
}
func main() {
  var p = people1{
   name: "张单",
   age:  18,
   address1: address1{
     "山东",
     "菏泽",
   },
  }
  fmt.Println(p)//{张单 18 {山东 菏泽}}
  marshal,err := json.Marshal(p)
  fmt.Printf("%#v\n",string(marshal))//"{}"
  fmt.Println(string(marshal))//{}
  fmt.Println(marshal)//[123 125]
  fmt.Println(err)//<nil>
}

必须将其中的字段首字母大写,才能在所有包中被访问到

type people1 struct {
  Name string
  Age  int
  address1//将结构体写成匿名嵌套形式,就可以将结构体内的成员当成本结构体的成员直接访问
}
type address1 struct {
  Province string
  city   string
}
func main() {
  var p = people1{
   Name: "张单",
   Age:  18,
   address1: address1{
     "山东",
     "菏泽",
   },
  }
  fmt.Println(p)//{张单 18 {山东 菏泽}}
  marshal,err := json.Marshal(p)
  fmt.Printf("%#v\n",string(marshal))//"{\"Name\":\"张单\",\"Age\":18,\"Province\":\"山东\"}"
  fmt.Println(string(marshal))//{"Name":"张单","Age":18,"Province":"山东"}
  fmt.Println(marshal)//[123 34 78 97 109 101 34 58 34 229 188 160 229 141 149 34 44 34 65 103 101 34 58 49 56 44 34 80 114 111 118 105 110 99 101 34 58 34 229 177 177 228 184 156 34 125]
  fmt.Println(err)//<nil>
}

当需求是,字段必须是小写的时候:

type people1 struct {
  Name string `json:"name" db:"name" xml:"name" ini:"name"`
  Age  int `json:"age"`
  address1//将结构体写成匿名嵌套形式,就可以将结构体内的成员当成本结构体的成员直接访问
}
type address1 struct {
  Province string `json:"province"`
  City   string `json:"city"`
}
func main() {
  var p = people1{
   Name: "张单",
   Age:  18,
   address1: address1{
     "山东",
     "菏泽",
   },
  }
  fmt.Println(p)//{张单 18 {山东 菏泽}}
  marshal,err := json.Marshal(p)
  fmt.Printf("%#v\n",string(marshal))//"{\"name\":\"张单\",\"age\":18,\"province\":\"山东\",\"city\":\"菏泽\"}"
  fmt.Println(string(marshal))//{"name":"张单","age":18,"province":"山东","city":"菏泽"}
  fmt.Println(marshal)//[123 34 110 97 109 101 34 58 34 229 188 160 229 141 149 34 44 34 97 103 101 34 58 49 56 44 34 112 114 111 118 105 110 99 101 34 58 34 229 177 177 228 184 156 34 44 34 99 105 116 121 34 58 34 232 143 143 230 179 189 34 125]
  fmt.Println(err)//<nil>
}

反序列化:JSON转换成Go语言的结构体

type people1 struct {
  Name   string `json:"name" db:"name" xml:"name" ini:"name"`
  Age    int   `json:"age" `
  address1 `:"address_1"` //将结构体写成匿名嵌套形式,就可以将结构体内的成员当成本结构体的成员直接访问
}
type address1 struct {
  Province string `json:"province"`
  City   string `json:"city"`
}
func main() {
  str :=`{"name":"张单","age":18,"province":"山东","city":"菏泽"}`
  var p people1
  json.Unmarshal([]byte(str),&p)
  fmt.Println(p)//{张单 18 {山东 菏泽}}
  fmt.Printf("%#v\n",p)//main.people1{Name:"张单", Age:18, address1:main.address1{Province:"山东", City:"菏泽"}}
}

5.14、结构体练习

定义结构体存储5名学生三门成绩求出每名学生的总成绩和平均成绩

type student struct {
  id    int
  name  string
  score  [3]int
}
func main() {
  arr := []student{student{101, "小明", [3]int{100, 99, 88}},
   student{102, "小红", [3]int{88, 56, 83}},
   student{103, "小刚", [3]int{18, 57, 81}},
   student{104, "小强", [3]int{48, 66, 93}},
   student{105, "小花", [3]int{98, 50, 89}}}
  //五名学生
  for i := 0; i < len(arr); i++ {
   //三门成绩
   sum := 0
   for j := 0; j < len(arr[1].score); j++ {
     sum += arr[i].score[j]
   }
   fmt.Printf("第%d名学生\t总成绩为:%d\t平均成绩:%d\n", i+1, sum, sum/len(arr[i].score))
  }
}

6、指针

* :取值运算符(也可以用于定义指针类型)
    & :取地址运算符(与变量一起用时)

● 对变量进行取地址( &)操作,可以获得这个变量的指针变量。

● 指针变量的值是指针地址。

● 对指针变量进行取值 (* )操作,可以获得指针变星指向的原变量的值。

6.1、定义

指针类型就是在基本类型前加*表示

var p *int//声明了一个指针默认值为nil (空指针 值为0)指向了内存地址编号为0的空间
//*p =100 0- 255为系统占用不允许用户进行读写操作 空指针是不能直接赋值的
var a int
p =&a
*p =555 // 可以通过指针间接修改变星对应的内存空间
fmt.Println(*p)

● 空指针:指向了内存地址编号为0的空间

● 野指针:指针变量指向了一个未知的空间

● 注意:Go语言访问野指针与空指针对应的内存空间都会报错

6.2、创建内存空间

格式 :

指针变量 = new(数据类型)
var p *int
p = new (int)//与定义的类型必须一至
*p =555 // 可以通过指针间接修改变星对应的内存空
fmt.Println(*p)

注意:

在Go语言中只需要开辟空间,不需要管理空间的释放。(GC)

6.3、make和new的区别

● make和new都是用来申请内存的。

● new很少用,一般用来给基本数据类型申请内存,返回的是对应类型的指针

● make区别于new,它只用于slice、map、chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。

● make函数是无可替代的,在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

函数入参类型,传值,传指针,变长入参

Go里面就是没有引用传递,严格地说,go方法或函数只有一种传递方式,那就是值传递。每次将一个变量作为参数传递时,都会创建一个新的变量副本并将其传递给所调用的函数或方法。副本分配在不同的内存地址。

在指针传递变量的情况下,将创建指向相同内存地址的新副本。为了感受它们之间的差异,我们来看看它是如何工作的。

顾名思义,变长参数是指参数的数量不固定,可以是 0 个,1 个或多个。变长参数的格式是 …T,在参数的类型前面有 3 个 .,表示该参数是变长参数。

该使用场景是通过调用下游方法,输入用户的个人资料。但是,后期调用的下游方法的入参发生变化,新增了一个或多个请求参数。

如果不使用变长参数,我们原来调用该下游方法的代码都需要随之修改。以下是示例代码:

变更前的示例代码:

func CallUserCenter(name string, age int, gender string) (detail *User, err error) {
 detail, err = NewUserUsecase().Create(name, age, gender)
 if err != nil {
  return
 }
 return
}
func CallUserCenter(name string, age int, gender string, args ...interface{}) (detail *User, err error) {
 if len(args) == 0 {
  detail, err = NewUserUsecase().Create(name, age, gender)
 } else {
  detail, err = NewUserUsecase().Create(name, age, gender, args[0])
 }
 if err != nil {
  return
 }
 return
}

结构体的声明与使用

前面复合类型讲了

流程控制语句

条件语句if

条件语句switch

switch匹配成功后就不会执行其他的case,如果我们需要无条件的强制执行后面的case,需要使用fallthrough关键字。

条件语句select

循环语句for

循环语句range

循环控制Goto、Break、Continue

函数/方法的声明实现与使用

1 语法

func (recevier type) methodName(参数列表) (返回值列表){
    方法体
    return 返回值
}

2 说明

a 参数列表:表示方法的输入。
b recevier type: 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型。
c receiver type: type 可以是结构体,也可以其它的自定义类型。
d receiver: 就是 type 类型的一个变量(实例)。比如:Person 结构体的一个变量(实例)。
e 返回值列表: 表示返回的值,可以有多个。
f 方法主体: 表示为了实现某一功能代码块。
g return 语句不是必须的。

方法的注意事项和细节

1 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

2 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理

3 Golang 中的方法作用在指定的数据类型上的,和指定的数据类型绑定。因此自定义类型,都可以有方法,而不仅仅是 struct。比如 int、float32 等都可以有方法

4 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问

5 如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出

Interface的声明实现与使用

基础认识

interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现。

请注意:此处限定是一组方法,既然是方法,就不能是变量;而且是一组,表明可以有多个方法。再多声明一点,interface本质上是一种类型,确切的说,是指针类型,此处暂且不多表,后文中自然能体会到。

interface是为实现多态功能,多态是指代码可以根据类型的具体实现采取不同行为的能力。如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。

type  接口名称 interface {
    method1(参数列表) 返回值列表
    method2(参数列表) 返回值列表
    ...
    methodn(参数列表) 返回值列表
}

接口通常以er作为名称后缀,方法名是声明组成部分,但参数名可不同或省略。如果接口没有任何方法声明,那么就是一个空接口(interface{}),它的用途类似面向对象里的根类型Object,可被赋值为任何类型的对象。

接口变量默认值是nil。如果实现接口的类型支持,可做相等运算

此外,还可以像匿名字段那样,嵌入其他接口。目标类型方法集中必须拥有包含嵌入接口方法在内的全部方法才算实现了该接口。

type stringer interface{ 
   string()string
} 
type tester interface{ 
   stringer               // 嵌入其他接口 
   test() 
}

interface应用场景

类型转换


实现多态

包的设计、组织与使用

包和闭包

Goroutines、Channels和同步

错误处理

defer recover pannic

Init、defer、panic、recover

1

init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。init 函数通常被用来:

对变量进行初始化
检查/修复程序的状态
注册
运行一次计算

为了使用导入的包,首先必须将其初始化。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。这通过Golang的运行时系统控制,如下图所示:

初始化导入的包(递归导入)
对包块中声明的变量进行计算和分配初始值
执行包中的init函数

当我们导入其他包时,

先初始化导入的包

初始化包时,会先加载全局变量

而后从上到下加载init()函数

当被导入的包的init()函数执行完毕后

执行调用方的全局变量加载

init()函数的顺序加载,

之后执行main()函数。

初始化顺序规则: (1)引入的包 (2) 当前包中的变量常量 (3) 当前包的init (4)main函数

2

注意:

0. 当前go源文件中, 每一个被Import的包, 按其在源文件中出现顺序初始化。
1. 如果当前包有多个init在不同的源文件中, 则按源文件名以字典序从小到大排序,小的先被执行到, 同一包且同一源文件中的init,则按其出现在文件中的先后顺序依次初始化; 
    当前包的package level变量常量也遵循这个规则; 
    其实准确来说,应是按提交给编译器的源文件名顺序为准,只是在提交编译器之前, go命令行工具对源文件名按字典序排序了。
2. init只可以由go runtine自已调用, 我们在代码中不可以显示调用,也不可以被引用,如赋给a function variable。
3. 包A 引入包B , 包B又引入包C, 则包的初始化顺序为: C -> B -> A
4. 引入包,必须避免死循环,如 A 引 B , B引C, C引A.
5. 一个包被其它多个包引入,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次。
6. 另一个大原则, 被依赖的总是先被初始化,当然呀。
7. main包总是被最后一个初始化,这很好理解,因为它总是依赖别的包。

3

首先给出结论:

在同一个package中,可以多个文件中定义init方法

在同一个go文件中,可以重复定义init方法

在同一个package中,不同文件中的init方法的执行按照文件名先后执行各个文件中的init方法

在同一个文件中的多个init方法,按照在代码中编写的顺序依次执行不同的init方法

4

开始是先在main包下:

如果引入了其他包,就跳到其他包下 初始化常量,变量,init()方法,

如果又引入了其他包就又优先再其他包下按这个顺序初始化,以此类推。

还要说明的是,如果导入的两个包中又都引入的第三个包,第三个包不会初始化两次,只会一次。

初始化常量、全局变量、init函数、自己的按照这个顺序

常用工具

build\clean\install\run\test\fmt\vet\get\list\pprof\got tool(addr2line,nm,objdump)等

$ go
...
    build            compile packages and dependencies
    clean            remove object files
    doc              show documentation for package or symbol
    env              print Go environment information
    fmt              run gofmt on package sources
    get              download and install packages and dependencies
    install          compile and install packages and dependencies
    list             list packages
    run              compile and run Go program
    test             test packages
    version          print Go version
    vet              run go tool vet on packages
Use "go help [command]" for more information about a command.
...

Go官方的工具可以使用go help xxx命令查看帮助文档,比如查看go get的参数标记和使用文档:

go help get

可以参考Golang官方的文档:https://golang.google.cn/cmd/go/

一、G0官方工具

(一)go get

该命令可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,下载后自动编译,一般引用依赖用go get就可以了。

参考:

go get -u "github.com/VictoriaMetrics/fastcache"

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261349

(二)go build

该命令用于编译我们指定的源码文件或代码包以及它们的依赖包。命令的常用标记说明如下:

编译过程输出到文件:go build -x > result 2>&1,因为go build -x 最终是将日志写到标准错误流当中。

如果只在编译特定包时需要指定参数,可以参考包名=参数列表的格式,比如go build -gcflags=‘log=-N -l’ main.go

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261347

(三)go install

该命令用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令会先去处理依赖包。

与go build命令一样,传给go install命令的代码包参数应该以导入路径的形式提供。

并且,go build命令的绝大多数标记也都可以用于go install命令。

实际上,go install命令只比go build命令多做了一件事,即:安装编译后的结果文件到指定目录。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261348

(四)go fmt和gofmt

Golang的开发团队制定了统一的官方代码风格,并且推出了gofmt工具(gofmt或go fmt)来帮助开发者格式化他们的代码到统一的风格。

gofmt是一个cli程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有.go文件,如果不传参数,会格式化当前目录下的所有.go文件。

gofmt默认不对代码进行简化,使用-s参数可以开启简化代码功能

gofmt是一个独立的cli程序,而go中还有一个go fmt命令,go fmt命令是gofmt的简单封装。go fmt在调用gofmt时添加了-l -w参数,相当于执行了gofmt -l -w

参考:

https://blog.csdn.net/whatday/article/details/97682094

(五)go env

该命令用于打印Go语言的环境信息,常见的通用环境信息如下:

设置或修改环境变量值:

go env -w GOPROXY="https://goproxy.com,direct"

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261359

(六)go run

该命令可以运行命令源码文件,只能接受一个命令源码文件以及若干个库源码文件(必须同属于main包)作为文件参数,且不能接受测试源码文件。它在执行时会检查源码文件的类型。如果参数中有多个或者没有命令源码文件,那么go run命令就只会打印错误提示信息并退出,而不会继续执行。

在通过参数检查后,go run命令会将编译参数中的命令源码文件,并把编译后的可执行文件存放到临时工作目录中。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261352

(七)go test

该命令用于对Go语言编写的程序进行测试,这种测试是以代码包为单位的,命令会自动测试每一个指定的代码包。当然,前提是指定的代码包中存在测试源码文件。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261353

(八)go clean

该命令会删除掉执行其它命令时产生的一些文件和目录。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261350

(九)go list

该命令的作用是列出指定的代码包的信息。与其他命令相同,我们需要以代码包导入路径的方式给定代码包。被给定的代码包可以有多个。这些代码包对应的目录中必须直接保存有Go语言源码文件,其子目录中的文件不算在内。

标记-e的作用是以容错模式加载和分析指定的代码包。在这种情况下,命令程序如果在加载或分析的过程中遇到错误只会在内部记录一下,而不会直接把错误信息打印出来。

为了看到错误信息可以使用-json标记。这个标记的作用是把代码包的结构体实例用JSON的样式打印出来。-m标记可以打印出modules而不是package。

# cd yky-sys-backend/cmd/bidengine
# go list -json -m
{
        "Path": "github.com/Project/test",
        "Main": true,
        "Dir": "/data/test",
        "GoMod": "/data/test/go.mod",
        "GoVersion": "1.15"
}
# 进入到.go文件的目录下,可以把文件依赖打印出来
go list -m -json

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261354

(十)go mod xxx

go mod init

该命令初始化并写入一个新的go.mod至当前目录中,实际上是创建一个以当前目录为根的新模块。文件go.mod必须不存在。如果可能,init会从import注释(参阅“go help importpath”)或从版本控制配置猜测模块路径。要覆盖此猜测,提供模块路径作为参数 module为当前项目名。比如:

go mod init demo

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod tidy

该命令确保go.mod与模块中的源代码一致。它添加构建当前模块的包和依赖所必须的任何缺少的模块,删除不提供任何有价值的包的未使用的模块。它也会添加任何缺少的条目至go.mod并删除任何不需要的条目。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod vendor

该命令重置主模块的vendor目录,使其包含构建和测试所有主模块的包所需要的所有包。不包括vendor中的包的测试代码。即将GOPATH或GOROOT下载的包拷贝到项目下的vendor目录,如果不使用vendor隔离项目的依赖,则不需要使用该命令拷贝依赖。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod download

该命令下载指定名字的模块,可为选择主模块依赖的模块匹配模式,或path@version形式的模块查询。如果download不带参数则代表是主模块的所有依赖。download的只会下载依赖,不会编译依赖,和get是有区别的。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod edit

该命令提供一个编辑go.mod的命令行接口,主要提供给工具或脚本使用。它只读取go.mod;不查找涉及模块的信息。默认情况下,edit读写主模块的go.mod文件,但也可以在标志后指定不同的目标文件。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod graph

该命令以文本形式打印模块间的依赖关系图。输出的每一行行有两个字段(通过空格分割);模块和其所有依赖中的一个。每个模块都被标记为path@version形式的字符串(除了主模块,因其没有@version后缀)。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod verify

该命令查存储在本地下载源代码缓存中的当前模块的依赖,是否自从下载之后未被修改。如果所有模块都未被修改,打印“all modules verified”。否则,报告哪个模块已经被修改并令“go mod”以非0状态退出。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

go mod why

该命令输出每个包或者模块的引用块,每个块以注释行“# package”或“# module”开头,给出目标包或模块。随后的行通过导入图给出路径,一个包一行。每个块之间通过一个空行分割,如果包或模块没有被主模块引用,该小节将显示单独一个带圆括号的提示信息来表明该事实。

参考:

https://www.jianshu.com/p/f6d2d6db2bca

(十一)go tool xxx

go tool的可执行文件在GOROOT或GOPATH的pkg/tool目录。go doc cmd可以查看具体cmd的使用说明,比如:

go doc pprof

go tool pprof

在Golang中,可以通过pprof工具对应于程序的运行时进行性能分析,包括CPU、内存、Goroutine等实时信息。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261357

go tool trace

该命令可以追踪请求链路,清晰的了解整个程序的调用栈,可以通过追踪器捕获大量信息。

参考:

https://zhuanlan.zhihu.com/p/410590497

go tool compile

该命令可以编译Go文件生成汇编代码,-N参数表示禁止编译优化, -l表示禁止内联,-S表示打印汇编,比如

会生成main.o的汇编文件

go tool compile -S main.go

go tool vet和go vet

该命令是一个用于检查Go语言源码中静态错误的简单工具。

go vet命令是go tool vet命令的简单封装。它会首先载入和分析指定的代码包,并把指定代码包中的所有Go语言源码文件和以“.s”结尾的文件的相对路径作为参数传递给go tool vet命令。

其中,以“.s”结尾的文件是汇编语言的源码文件。如果go vet命令的参数是Go语言源码文件的路径,则会直接将这些参数传递给go tool vet命令。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261356

go tool doc和go doc(写好注释,直接生成)

**该命令可以打印附于Go语言程序实体上的文档。**我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。

所谓Go语言的程序实体,是指变量、常量、函数、结构体以及接口。而程序实体的标识符即是代表它们的名称。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261351

go tool addr2line

该命令可以调用栈的地址转化为文件和行号。

Usage:

go tool addr2line binary

Addr2line reads hexadecimal addresses, one per line and with optional 0x

prefix, from standard input. For each input address, addr2line prints two

output lines, first the name of the function containing the address and

second the file:line of the source code corresponding to that address.

This tool is intended for use only by pprof; its interface may change or it

may be deleted entirely in future releases.

go tool asm

该命令可以将汇编文件编译成一个.o文件,后续这个.o文件可以用于生成.a归档文件,命令的file参数必须是汇编文件。

Usage:

go tool asm [flags] file

The specified file must be a Go assembly file. The same assembler is used

for all target operating systems and architectures. The GOOS and GOARCH

environment variables set the desired target.

go tool buildid

每一个 Go 二进制文件内,都有一个独一无二的 Build ID,详情参考 src/cmd/go/internal/work/buildid.go 。Go Build ID 可以用以下命令来查看:

go tool buildid

参考:

https://www.anquanke.com/post/id/215419

go tool cgo

该命令可以使我们创建能够调用C语言代码的Go语言源码文件。这使得我们可以使用Go语言代码去封装一些C语言的代码库,并提供给Go语言代码或项目使用。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261358

go tool cover

该命令对单元测试过程中生成的代码覆盖率统计生成html文件,可以本地打开展示。

go test -coverprofile=a.out

go tool cover -html=a.out -o coverage.html

覆盖度工具不仅可以记录分支是否被执行,还可以记录分支被执行了多少次。

go test -covermode=set|count|atomic: -covermode:

set: 默认模式,统计是否执行

count: 计数

atomic: count的并发安全版本,仅当需要精确统计时使用

通过go tool cover -func=count.out查看每个函数的覆盖度。

参考:

https://blog.csdn.net/xhdxhdxhd/article/details/120424848

go tool dist

dist工具是属于go的一个引导工具,它负责构建C程序(如Go编译器)和Go工具的初始引导副本。它也可以作为一个包罗万象用shell脚本替换以前完成的零工。通过“go tool dist”命令可以操作该工具。

go tool dist

usage: go tool dist [command]

Commands are:

banner print installation banner

bootstrap rebuild everything

clean deletes all built files

env [-p] print environment (-p: include $PATH)

install [dir] install individual directory

list [-json] list all supported platforms

test [-h] run Go test(s)

version print Go version

使用go tool dist list可以输出当前安装Go版本所支持的操作系统与架构。

参考:https://blog.csdn.net/byxiaoyuonly/article/details/112492264

go tool fix和go fix

**该命令会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。**这里所说的版本即Go语言的版本。代码包的所有Go语言源码文件不包括其子代码包(如果有的话)中的文件。修正操作包括把对旧程序调用的代码更换为对新程序调用的代码、把旧的语法更换为新的语法等等。

这个工具其实非常有用。**在编程语言的升级和演进的过程中,**难免会对过时的和不够优秀的语法及标准库进行改进。

参考:

https://www.kancloud.cn/cattong/go_command_tutorial/261355

go tool link

该命令链接Go的归档文件比如静态库,以及链接其所有依赖,生成一个可执行文件(含main package)。

go tool link [flags] main.a

Flags:

-B note
    Add an ELF_NT_GNU_BUILD_ID note when using ELF.
    The value should start with 0x and be an even number of hex digits.
-D address
    Set data segment address.
-E entry
    Set entry symbol name.
-H type
    Set executable format type.
    The default format is inferred from GOOS and GOARCH.
    On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary."
-I interpreter
    Set the ELF dynamic linker to use.
-L dir1 -L dir2
    Search for imported packages in dir1, dir2, etc,
    after consulting $GOROOT/pkg/$GOOS_$GOARCH.
-R quantum
    Set address rounding quantum.
-T address
    Set text segment address.
-V
    Print linker version and exit.

go tool compile -o calc.o -I pkg/linux_amd64 src/calc/calc.go

go tool link -o bin/calc -L pkg/linux_amd64 calc.o

参考:

http://cache.baiducontent.com/

go tool nm

该命令可以查看符号表的命令,等同于系统的nm命令,非常有用。在断点的时候,如果你不知道断点的函数符号,那么用这个命令查一下就知道了(命令处理的是二进制程序文件),第一列是地址,第二列是类型,第三列是符号。等同于nm命令。

参考:

https://studygolang.com/articles/29906

go tool objdump

该命令可以反汇编二进制的工具,等同于系统objdump,命令解析的是二进制格式的程序文件。

go tool objdump example.o

go tool objdump -s DoFunc example.o // 反汇编具体函数

参考:

https://studygolang.com/articles/29906

go tool pack

该命令把二进制文件打包成静态库。

go tool pack op file.a [name…]

参数op

c append files (from the file system) to a new archive

p print files from the archive

r append files (from the file system) to the archive

t list files from the archive

x extract files from the archive

go tool compile -o simplemath.o src/simplemath/add.go src/simplemath/sqrt.go

go tool pack c pkg/linux_amd64/simplemath.a simplemath.o

参考:

http://cache.baiducontent.com/

go tool test2json

该命令用于把测试可执行文件转化可读的json格式。

// 生成测试文件的可执行文件

go test string_concat_test.go -o string_concat.test

go tool test2json ./string_concat.test -test.v

// 输出

{“Action”:“output”,“Output”:“testing: warning: no tests to run\n”}

{“Action”:“output”,“Output”:“PASS\n”}

{“Action”:“pass”}

参考:

https://blog.csdn.net/weixin_33772442/article/details/112098085

godoc

**该命令可以生成一个Go官方文档的本地http服务,**可以在线查看标准库和第三方库文档,以及项目文档,但是需要按照一定的格式去写注释。

// 安装godoc

go get -v golang.org/x/tools/cmd/godoc

// 方式一

godoc -http=:6060

// 方式二 :-play可以使用playground运行Example代码

godoc -http=:6060 -play

// 查看自己的项目文档,需要将其它路径下的项目代码目录建一个软链到GOROOT的src目录下,如cd $GOROOT/src; ln -s /data/my-project ./,在浏览器后面输入项目名即可

http://127.0.0.1:6060/pkg/my-project/

Web页面如下:

图片

参考:https://www.fujieace.com/golang/godoc.html

二、第三方工具

Go工具和组件汇总项目:

https://github.com/avelino/awesome-go

(一)delve

本地代码调试工具

参考:https://github.com/go-delve/delve

(二)goconvey

goconvey是一款针对Golang的测试框架,可以管理和运行测试用例,同时提供了丰富的断言函数,并支持很多Web界面特性。

参考:https://github.com/smartystreets/goconvey

(三)goleak

本地排查内存泄露的工具

参考:https://github.com/uber-go/goleak

(四)go-wrk

Go接口压测工具

参考:https://github.com/adjust/go-wrk

(五)golint

代码风格检查

参考:https://github.com/golang/lint

(六)revive

代码风格检查,比golint速度更快

参考:https://github.com/mgechev/revive

(七)gocode

代码自动补全工具,可以在vim中使用

参考:https://github.com/nsf/gocode

(八)godoctor

代码重构工具

参考:https://github.com/godoctor/godoctor

(九)gops

查看go进程和相关信息的工具,用于诊断线上服务。

参考:https://github.com/google/gops

(十)goreplay

GoReplay是一个开源网络监控工具,可以将实时HTTP流量捕获并重放到测试环境。

参考:https://github.com/buger/goreplay

https://blog.51cto.com/axzxs/5102596

(十一)depth

一个有用的Golang工具,Depth可帮助Web开发人员检索和可视化Go源代码依赖关系树。它可以用作独立的命令行应用程序或作为项目中的特定包。你可以通过在解析之前在Tree上设置相应的标志来添加自定义。

参考:https://github.com/KyleBanks/depth

(十二)go-swagger

该工具包包括各种功能和功能。Go-Swagger是Swagger 2.0的一个实现,可以序列化和反序列化swagger规范。它是RESTful API简约但强大的代表。

通过Go-Swagger,你可以swagger规范文档,验证JSON模式以及其他额外的规则。其他功能包括代码生成,基于swagger规范的API生成,基于代码的规范文档生成,扩展了的字符串格式,等等。

(有时候一份清晰明了的接口文档能够极大地提高前后端双方的沟通效率和开发效率。如何使用swagger生成接口文档。)

参考:https://github.com/go-swagger/go-swagger

(十三)gox

交叉编译工具,可以并行编译多个平台。

参考:https://github.com/mitchellh/gox

(十四)gocyclo

gocyclo用来检查函数的复杂度。

# 列出了所有复杂度大于20的函数
gocyclo -over 20 $(ls -d */ | grep -v vendor)
# 列出最复杂的5个
gocyclo -top 5 $(ls -d */ | grep -v vendor)

参考:https://github.com/fzipp/gocyclo

(十五)deadcode

deadcode会告诉你哪些代码片段根本没用。

find . -type d -not -path "./vendor/*" | xargs deadcode

参考:https://github.com/tsenart/deadcode

(十六)gotype

gotype会对go文件和包进行语义(semantic)和句法(syntactic)的分析,这是google提供的一个工具。

find . -name "*.go" -not -path "./vendor/*" -not -path ".git/*" -print | xargs gotype -a

参考:https://golang.org/x/tools/cmd/gotype

(十七)misspell

misspell用来拼写检查,对国内英语不太熟练的同学很有帮助。

find . -type f -not -path "./vendor/*" -print | xargs misspell

参考:https://github.com/client9/misspell

(十八)staticcheck

staticcheck是一个超牛的工具,提供了巨多的静态检查,就像C#生态圈的 ReSharper一样

# 安装:
go install honnef.co/go/tools/cmd/staticcheck@latest
# 使用:
staticcheck main.go

参考:

https://staticcheck.io/docs/

https://github.com/dominikh/go-tools/tree/master/staticcheck

(十九)goconst

goconst会查找重复的字符串,这些字符串可以抽取成常量。

goconst ./… | grep -v vendor

常用包

io/bufio/ioutil,bytes,flag,strings,strconv,errors,regexp,json,net,os,sync,time,context,encoding,html,template,atomic,debug.crypto,syscall

1) fmt

fmt 包实现了格式化的标准输入输出,这与C语言中的 printf 和 scanf 类似。其中的 fmt.Printf() 和 fmt.Println() 是开发者使用最为频繁的函数。

格式化短语派生于C语言,一些短语(%- 序列)是这样使用:

%v:默认格式的值。当打印结构时,加号(%+v)会增加字段名;

%#v:Go样式的值表达;

%T:带有类型的 Go 样式的值表达。

2) io

这个包提供了原始的 I/O 操作界面。它主要的任务是对 os 包这样的原始的 I/O 进行封装,增加一些其他相关,使其具有抽象功能用在公共的接口上。

3) bufio

bufio 包通过对 io 包的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。

在 bufio 各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据,只有当缓冲区没有数据时,才会从数据源获取数据更新缓冲。

4) sort

sort 包提供了用于对切片和用户定义的集合进行排序的功能。

5) strconv

strconv 包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串的功能。

6) os

os 包提供了不依赖平台的操作系统函数接口,设计像 Unix 风格,但错误处理是 go 风格,当 os 包使用时,如果失败后返回错误类型而不是错误数量。

7) sync

sync 包实现多线程中锁机制以及其他同步互斥机制。

8) flag

flag 包提供命令行参数的规则定义和传入参数解析的功能。绝大部分的命令行程序都需要用到这个包。

9) encoding/json

JSON 目前广泛用做网络程序中的通信格式。encoding/json 包提供了对 JSON 的基本支持,比如从一个对象序列化为 JSON 字符串,或者从 JSON 字符串反序列化出一个具体的对象等。

10) html/template

主要实现了 web 开发中生成 html 的 template 的一些函数。

11) net/http

net/http 包提供 HTTP 相关服务,主要包括 http 请求、响应和 URL 的解析,以及基本的 http 客户端和扩展的 http 服务。

通过 net/http 包,只需要数行代码,即可实现一个爬虫或者一个 Web 服务器,这在传统语言中是无法想象的。

12) reflect

reflect 包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类型 interface{} 的值,并且通过 Typeof 解析出其动态类型信息,通常会返回一个有接口类型 Type 的对象。

13) os/exec

os/exec 包提供了执行自定义 linux 命令的相关实现。

14) strings

strings 包主要是处理字符串的一些函数集合,包括合并、查找、分割、比较、后缀检查、索引、大小写处理等等。

strings 包与 bytes 包的函数接口功能基本一致。

15) bytes

bytes 包提供了对字节切片进行读写操作的一系列函数。字节切片处理的函数比较多,分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等。

16) log

log 包主要用于在程序中输出日志。

log 包中提供了三类日志输出接口,Print、Fatal 和 Panic。

Print 是普通输出;

Fatal 是在执行完 Print 后,执行 os.Exit(1);

Panic 是在执行完 Print 后调用 panic() 方法。

参考资料

基础知识

https://blog.51cto.com/u_13919520/3152161
https://blog.csdn.net/qq_52648173/article/details/124653298
https://blog.csdn.net/z318913/article/details/123005086
https://blog.csdn.net/mocoll/article/details/119427874
https://www.jb51.net/article/255592.htm
https://blog.csdn.net/m0_54130475/article/details/125586782
https://blog.csdn.net/chengqiuming/article/details/115915109
https://www.jianshu.com/p/b62adfaaefc3
https://golang.google.cn/cmd/go/
https://mp.weixin.qq.com/s?__biz=MzI2NDU4OTExOQ==&mid=2247540970&idx=1&sn=2df664485056860e6af54e6120414734&chksm=eaa83abadddfb3ac301e1a1766f7bdc4ce3c2edd0029279ece81c6abe85fcd639a8559be4192&scene=27
https://blog.csdn.net/htyu_0203_39/article/details/50948193
https://blog.csdn.net/zhuxinquan61/article/details/73712251
目录
相关文章
|
存储 编解码 监控
视频基础知识 3
视频基础知识
156 0
|
4月前
|
存储 监控 安全
安全基础知识
【8月更文挑战第9天】
43 1
|
7月前
|
机器学习/深度学习 人工智能 大数据
一些基础知识
了解人工智能基础,探索AI系统如何结合算法、机器学习和数据分析模拟人类智能。从 Siri 和 Alexa 的语音助手到 Netflix 的个性化推荐,AI已深入日常生活中。查阅《人工智能入门手册(AI101)》深入学习,同时参考《抖音百科》和《大数据》拓宽知识领域。
|
存储 编解码 算法
视频基础知识 1
视频基础知识
217 0
|
存储 Web App开发 SQL
基础知识
基础知识
112 0
|
编译器 C++
C++模板基础知识
本文将讲述C++模板初阶的相关知识。
148 1
前后台交互基础知识
在别人造的轮子上飞奔的时候,轮子背后的前后台交互如何进行一脸懵$.
75 0
|
安全 编译器 程序员
【C++】C++基础知识
其中 { } 内容即为命名空间的成员,注意最后右花括号后不用加分号结尾。
|
边缘计算 网络协议 测试技术
DoIP基础知识
DoIP基础知识
DoIP基础知识

热门文章

最新文章