1 基础语法

1.1 变量与常量

//var 变量名 类型 = 表达式(在函数内部,可以使用更简略的 :=)
var m int = 100
m := 100
//const 常量名 类型 = 表达式(常量在定义的时候必须赋值)
const pi = 3.1415
  • 支持批量声明;
  • 变量的类型可以省略;
  • 在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量;
  • iota在const关键字出现时将被重置为0,const中每新增一行常量声明将使iota计数一次;


    1.2 基本数据类型与运算符

    1.2.1 常用类型

    | 类型 | | | —- | —- | | 整形 | 按长度划分为(无符号对应uint)
    int8、int16、int32、int64 | | 浮点型 | float32和float64 | | 布尔值 | 默认值为false,不允许整型强制转换为布尔型 | | 字符串 | Go语言中的字符串以原生数据类型 |

注:Go语言中只有强制类型转换,没有隐式类型转换。

1.2.2 运算符

运算符与其他语言基本一致

1.2.3类型别名与自定义类型

//将MyInt定义为int类型
type MyInt int
//系统默认的类型别名
type byte = uint8
type rune = int32

通过type关键字的定义,MyInt就是一种新的类型,它具有int的特性
类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。

1.3 流程控制

1.3.1 if

if条件判断与其他语言基本一致。
但有一种特殊的写法,在 if 表达式之前添加一个执行语句,再根据变量值进行判断。

a := 10
if  a >= 10 {
    fmt.Println("OK")
}    

//这里的a,if外获取不到
if a := 10; a >= 10 {
    fmt.Println("OK")
}

1.3.2 for

for循环的基本格式,只有循环体就相当与while(true)

for 初始语句;条件表达式;结束语句{
    循环体语句
}

for range(键值循环)

  1. 数组、切片、字符串返回索引和值。
  2. map返回键和值。
  3. 通道(channel)只返回通道内的值。

1.3.3 swith

Go语言规定每个switch只能有一个default分支;
fallthrough语法可以执行满足条件的case的下一个case;

1.4 数组

1.4.1 定义和初始化

//var 数组变量名 [元素数量]T

//会初始化为int类型的零值
var a [3]int
//使用指定的初始值完成初始化
var b = [3]int{1, 2}
//编译器会根据初始值的个数自行推断数组的长度
var numArray = [...]int{1, 2}
//指定索引值的方式来初始化数组
a := [...]int{1: 1, 3: 5}

注:数组是值类型

1.5 切片

1.5.1 定义和初始化

切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。切片是一个引用类型。

//不指定元素数量的数组就是切片
var slice []T

切片的定义与初始化与数组相似

1.5.2 切片表达式

切片表达式从字符串、数组、指向数组或切片的指针构造子字符串或切片。
切片表达式中的low和high表示一个索引范围(左包含,右不包含)

//简单的表达式可以省略max,容量等于得到的切片的底层数组的容量。
a[low : high : max]

1.6 Map

1.6.1 定义和初始化

//var Map变量名 map [key_data_type] value_data_type
//普通的声,会创建一个 nil map。nil map 不能用来存放键值对
var map_variable map[key_data_type]value_data_type
//使用make()函数来分配内存
map_variable := make(map[key_data_type]value_data_type)

1.6.2 判断是否存在某个键

//ok是看当前key是否存在返回布尔,value返回对应key的值
value, ok := map[key]

1.7 函数

1.7.1 定义

func 函数名(参数)(返回值){
    函数体
}

//凡是满足 func(int, int) int的函数都可以赋值给c
type calculation func(int, int) int
var c calculation
  • 函数的参数中如果相邻变量的类型相同,则可以省略类型
  • 函数支持多返回值
  • 函数可以定义为类型,赋值于某个变量
  • 函数可以做为参数,也可以做为返回值

1.7.2 匿名函数

func main() {
    // 将匿名函数保存到变量
    add := func(x, y int) {
        fmt.Println(x + y)
    }
    add(10, 20) // 通过变量调用匿名函数

    //自执行函数:匿名函数定义完加()直接执行
    func(x, y int) {
        fmt.Println(x + y)
    }(10, 20)
}

匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数

1.7.3 闭包

闭包=函数+引用环境

func add (x int) func(int) int {
    return func(y int) int {
        x += y
        return x
    }
}

1.7.4 内置函数

函数 作用
len 来求长度,比如string、array、slice、map、channel ,返回长度
cap 用于返回某个类型的最大容量(只能用于切片和 map)
append 用来追加元素到数组、slice中,返回修改后的数组、slice
delete 从map中删除key对应的value
close 主要用来关闭channel
panic/recover Go语言没有异常机制,配合defer用来做错误处理
make 用来分配内存,返回Type本身(只能应用于slice, map, channel)
new 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
init 不接收任何参数也没有任何返回值,我们也不能在代码中主动调用它。当程序启动的时候,init函数会按照它们声明的顺序自动执行。

1.8 指针

&(取地址)和(根据地址取值),取地址操作符&和取值操作符是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

func main() {
    a := 10
    b := &a
    fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
    fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
    fmt.Println(&b)                    // 0xc00000e018
    c := *b // 指针取值(根据指针去内存取值
    fmt.Println(c)     //c:10
}
  • 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

image.png

1.9 结构体

1.9.1 定义与实例化

//使用type和struct关键字来定义结构体
type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}
//使用var实例化,使用键值对对结构体进行初始化
func main() {
    var p 类型名

    var p2 类型名{
    字段名: 值,
    字段名: 值,
    字段名: 值,
    }

//通过.来访问结构体的字段(成员变量)
    p2.字段名
}

注:结构体中字段大写开头表示可公开访问,小写表示私有

1.9.2 方法和接收者

Go语言中的方法(Method)是一种作用于特定类型变量的函数。方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。

/*func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    函数体
}*/
//Person 结构体
type Person struct {
    name string
    age  int8
}

//NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
    return &Person{
        name: name,
        age:  age,
    }
}


//注意指针类型的接收者和值类型接收者的区别
func (p Person) methodA() {

}

func (p *Person) methodB() {

}

1.9.3 结构体的“继承”

go中没有继承,只能通过组合来实现继承。

  • 一个struct嵌套了另外一个匿名的struct从而实现了继承,嵌套多个匿名struct实现多重继承。
  • 一个struct嵌套了宁外一个struct的实例实现了组合。 ```go type Animal struct {

}

//继承 type Cat struct { //匿名 *Animail }

//组合 type Dog struct { animal Animal }

<a name="nrUzT"></a>
#### 
<a name="wbTcU"></a>
#### 1.9.4 结构体标签
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:
```go
type Student struct {
    ID     int    `json:"id"` //通过指定tag实现json序列化该字段时的key
    Gender string 
    name   string 
}

1.10 包

1.10.1 定义与引入

一个包可以简单理解为一个存放.go文件的文件夹。该文件夹下面的所有.go文件都要在非注释的第一行添加如下声明,声明该文件归属的包。
要在当前包中使用另外一个包的内容就需要使用import关键字引入这个包,并且import语句通常放在文件的开头,package声明语句的下方

package packagename

import importname "path/to/package"

importname 可以省略
importname 的包名如果为 _ 作为包名,那么这个包的引入方式就称为匿名引入。一个包被匿名引入的目的主要是为了加载这个包,从而使得这个包中的资源得以初始化。 被匿名引入的包中的init函数将被执行并且仅执行一遍。

1.10.2 go model

1.11 接口

1.11.1 接口的定义与实现

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

接口就是规定了一个需要实现的方法列表,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。

  • 空接口是指没有定义任何方法的接口类型。因此任何类型都可以视为实现了空接口。
  • 由于接口类型的值可以是任意一个实现了该接口的类型值,所以接口值除了需要记录具体之外,还需要记录这个值属于的类型

    1.11.2 ERROR接口

1.12 反射

1.12.1 reflect包

reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type

func reflectValue(x interface{}) {
    v := reflect.ValueOf(x)
    k := v.Kind()
    switch k {
    case reflect.Int64:
        // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
        fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
    case reflect.Float32:
        // v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
        fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
    case reflect.Float64:
        // v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
        fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
    }
}
func main() {
    var a float32 = 3.14
    var b int64 = 100
    reflectValue(a) // type is float32, value is 3.140000
    reflectValue(b) // type is int64, value is 100
    // 将int类型的原始值转换为reflect.Value类型
    c := reflect.ValueOf(10)
    fmt.Printf("type c :%T\n", c) // type c :reflect.Value
}

1.12.2 结构体反射

type student struct {
    Name  string `json:"name"`
    Score int    `json:"score"`
}

func main() {
    stu1 := student{
        Name:  "小王子",
        Score: 90,
    }

    t := reflect.TypeOf(stu1)
    fmt.Println(t.Name(), t.Kind()) // student struct
    // 通过for循环遍历结构体的所有字段信息
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
    }

    // 通过字段名获取指定结构体字段信息
    if scoreField, ok := t.FieldByName("Score"); ok {
        fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
    }
}