go语言基础 | 青训营

135 阅读10分钟

go语言基础

go特点

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

go环境安装

  1. golang.google.cn/

  2. 将Go安装目录的bin目录添加到path中,以便执行go命令。

  3. 环境变量中新添加了GOROOT,默认值为安装目录

  4. 环境变量中新添加了GOPATH,我的默认值为C:\Users\Administrator\go,这个目录大家可以手动设置,并且可以设置多个值,用来存放go下载的包和命令,后续大家编写的包也要放在GOPATH指定的目录。

  5. 打开cmd输入代码更改依赖下载连接

    # 新版改成如下链接
    go env -w GO111MODULE=on
    go env -w GOPROXY=https://proxy.golang.com.cn,direct
    
  6. ctrl + shift + p 输入Go:Install/Updata Tool

  7. 全选下载

1.1fmt

导包

import (
    "fmt"
)

作用:打印字符串到控制台

func main() {
    fmt.printf("%v\n",)//只显示数值
    fmt.printf("%+v\n",)//显示数值和类型
    fmt.printf("%#v\n",)//显示结构体名类型和数值
    fmt.Println("hello world")
}
type point struct {
    x, y int
}
func main() {
    p := point{1, 2}
    fmt.Printf("p=%v\n", p)  // p={1 2}
    fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
    fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
}

1.2var

go语言是一门强类型语言,但可以不写类型自动判断

也可以加上var代表任意类型

1.3for

语法

 for init; condition; post { }
 for condition { }
 for { }

init: 一般为赋值表达式,给控制变量赋初值; condition: 关系表达式或逻辑表达式,循环控制条件; post: 一般为赋值表达式,给控制变量增量或减量。

for语句执行过程

  1. 先对表达式 init 赋初值;
  2. 判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。

1.4if

  1. 可省略条件表达式括号。
  2. 持初始化语句,可定义代码块局部变量。
  3. 代码块左 括号必须在条件表达式尾部。

语法

if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}

1.5switch

  1. Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
  2. 而如果switch没有表达式,它会匹配true。
  3. Go里面switch默认相当于每个case最后带有break,
  4. 匹配成功后不会自动向下执行其他case,而是跳出整个switch,
  5. 但是可以使用fallthrough强制执行后面的case代码。

代码

var x interface{}
//写法一:
//switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
switch i := x.(type) { // 带初始化语句
    case nil:
    fmt.Printf(" x 的类型 :%T\r\n", i)
    case int:
    fmt.Printf("x 是 int 型")
    case float64:
    fmt.Printf("x 是 float64 型")
    case func(int) float64:
    fmt.Printf("x 是 func(int) 型")
    case bool, string:
    fmt.Printf("x 是 bool 或 string 型")
    default:
    fmt.Printf("未知型")
}
//写法二
var j = 0
switch j {
    case 0:
case 1:
    fmt.Println("1")
    case 2:
    fmt.Println("2")
    default:
    fmt.Println("def")
}

1.6array

  1. 数组:是同一种数据类型的固定长度的序列。

  2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。

  3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。

  4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

    for i := 0; i < len(a); i++ {
    }
    for index, v := range a {
    }
    
  5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic

  6. 数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。

  7. 支持 "=="、"!=" 操作符,因为内存总是被初始化过的。

  8. 指针数组 [n]*T,数组指针 *[n]T。

多维数组与遍历

var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
     
for k1, v1 := range f {
    for k2, v2 := range v1 {
        fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
    }
    fmt.Println()
}

1.7切片slice

  1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
  2. 切片的长度可以改变,因此,切片是一个可变的数组。
  3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
  4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
  5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
  6. 如果 slice == nil,那么 len、cap 结果都等于 0。

创建切片的各种方式

//1.声明切片
var s1 []int
if s1 == nil {
    fmt.Println("是空")
} else {
    fmt.Println("不是空")
}
// 2.:=
s2 := []int{}
// 3.make()
var s3 []int = make([]int, 0)
fmt.Println(s1, s2, s3)
// 4.初始化赋值
var s4 []int = make([]int, 0, 0)
fmt.Println(s4)
s5 := []int{1, 2, 3}
fmt.Println(s5)
// 5.从数组切片
arr := [5]int{1, 2, 3, 4, 5}
var s6 []int
// 前包后不包
s6 = arr[1:4]
fmt.Println(s6)

通过make来创建切片

var slice []type = make([]type, len)
slice  := make([]type, len)
slice  := make([]type, len, cap)

用append内置函数操作切片(切片追加)

var a = []int{1, 2, 3}
fmt.Printf("slice a : %v\n", a)
var b = []int{4, 5, 6}
fmt.Printf("slice b : %v\n", b)
c := append(a, b...)
fmt.Printf("slice c : %v\n", c)
d := append(c, 7)
fmt.Printf("slice d : %v\n", d)
e := append(d, 8, 9, 10)
fmt.Printf("slice e : %v\n", e)

slice遍历

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
slice := data[:]
for index, value := range slice {
    fmt.Printf("inde : %v , value : %v\n", index, value)
}

1.8map

map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。

map的定义

map的定义语法如下

map[KeyType]ValueType

KeyType:表示键的类型。

ValueType:表示键对应的值的类型。

map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:

make(map[KeyType]ValueType, [cap])

其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量

1.9range

for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环

for key, value := range oldMap {
    newMap[key] = value
}

1.10func

  • 函数声明包含一个函数名,参数列表, 返回值列表和函数体。如果函数没有返回值,则返回列表可以省略。函数从第一条语句开始执行,直到执行return语句或者执行函数的最后一条语句。
  • 函数可以没有参数或接受多个参数。
  • 注意类型在变量名之后 。
  • 当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。
  • 函数可以返回任意数量的返回值。
  • 使用关键字 func 定义函数,左大括号依旧不能另起一行。
func test(x, y int, s string) (int, string) {
    // 类型相同的相邻参数,参数类型可合并。 多返回值必须用括号。
    n := x + y          
    return n, fmt.Sprintf(s, n)
}

1.11point

只需要知道三个概念: 指针地址, 指针类型, 指针取值

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:int、 int64、*string等。

取变量指针

ptr := &v    // v的类型为T
  • v:代表被取地址的变量,类型为T
  • ptr:用于接收地址的变量,ptr的类型就为T,称做T的指针类型。 代表指针。

指针取值

func main() {
    //指针取值
    a := 10
    b := &a // 取变量a的地址,将指针保存到b中
    fmt.Printf("type of b:%T\n", b)
    c := *b // 指针取值(根据指针去内存取值)
    fmt.Printf("type of c:%T\n", c)
    fmt.Printf("value of c:%v\n", c)
}
//总结: 取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
指针传值得例子:

new和make得区别

  1. 二者都是用来做内存分配的。
  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
  3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

1.12struct

结构体得定义

使用type和struct关键字来定义结构体

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}

其中:

  1. 类型名:标识自定义结构体的名称,在同一个包内不能重复。
  2. 字段名:表示结构体字段名。结构体中的字段名必须唯一。
  3. 字段类型:表示结构体字段的具体类型。
 type person1 struct {
     name, city string
     age        int8
 }

基本实例化

 type person struct {
     name string
     city string
     age  int8
 }
​
 func main() {
     var p1 person
     p1.name = "linxi"
     p1.city = "山东"
     p1.age = 18
     fmt.Printf("p1=%v\n", p1)  //p1={linxi 山东 18}
     fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"linxi", city:"山东", age:18}
 }
 //我们通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。

创建指针类型结构体

我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:

 var p2 = new(person)
 fmt.Printf("%T\n", p2)     //*main.person
 fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}

从打印的结果中我们可以看出p2是一个结构体指针。

需要注意的是在Go语言中支持对结构体指针直接使用.来访问结构体的成员。

 var p2 = new(person)
 p2.name = "linxi"
 p2.age = 18
 p2.city = "北京"
 fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"linxi", city:"北京", age:18}

取结构体的地址实例化

使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。

p3 := &person{}
fmt.Printf("%T\n", p3)     //*main.person
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}
p3.name = "linxi"
p3.age = 30
p3.city = "山东"
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"linxi", city:"山东", age:30}
p3.name = "com"

1.13struct-method

fun (对象名 结构体名) 方法名(方法参数列表){}
fun (对象名 *结构体名) 方法名(方法参数列表) (返回值列表){}

1.14error

导包

import (
    "errors"
)
errors.New("not found")
//返回错误信息

1.15string

方法介绍
len(str)求长度
+或fmt.Sprintf拼接字符串
strings.Split分割
strings.Contains判断是否包含
strings.HasPrefix,strings.HasSuffix前缀/后缀判断
strings.Index(),strings.LastIndex()子串出现的位置
strings.Join(a[]string, sep string)join操作

1.16json

导包

import (
    "encoding/json"
)

在结构体定义时加上

'json:"json名"'//变化时用JSON名:

方法

buf, err :=json.Marshal(a) //将a变为json字符串
string(buf)
​
buf, err = json.MarshalIndent(a, "", "\t")//将a变为json字符串,且换行
string(buf)

1.17time

导包

import (
    "time"
)

方法

  1. time.Now()返回当前时间
  2. time.Date(年,月, 日, 时, 分, 秒, 毫秒, time.UTC)返回规定时间
  3. 对象.Fromat("")
  4. 对象.Year(), 对象.Month(), 对象.Day(), 对象.Hour(), 对象.Minute(),对象.Minutes(), 对象.Seconds()

1.18strconv

进制转换

import (
  "strconv"
)
  1. strconv.ParseFloat("字符串",64)//将字符串变为float数字
  2. strconv.ParseInt("字符串",0,64)//将字符串根据进制变为int数字
  3. strconv.Atoi("字符串")//将字符串变为数字

1.19env

导包

import (
    "os"
    "os/exec"
)
  1. os.Args
  2. os.Getenv("PATH")//获取环境变量path的值
  3. os.Serenv("a","b")//设置环境变量a为b
  4. exec.Command("grep", "127.0.0.1", "路径").CombinedOutput()//寻找路径下的命令并输出