go语言基础
go特点
- 高性能、高并发
- 语法简单,学习曲线平缓
- 丰富标准库
- 完善工具链
- 静态编译
- 快速编译
- 跨平台
- 垃圾回收
go环境安装
-
将Go安装目录的bin目录添加到path中,以便执行go命令。
-
环境变量中新添加了GOROOT,默认值为安装目录
-
环境变量中新添加了GOPATH,我的默认值为C:\Users\Administrator\go,这个目录大家可以手动设置,并且可以设置多个值,用来存放go下载的包和命令,后续大家编写的包也要放在GOPATH指定的目录。
-
打开cmd输入代码更改依赖下载连接
# 新版改成如下链接 go env -w GO111MODULE=on go env -w GOPROXY=https://proxy.golang.com.cn,direct -
ctrl + shift + p 输入Go:Install/Updata Tool
-
全选下载
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语句执行过程
- 先对表达式 init 赋初值;
- 判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
1.4if
- 可省略条件表达式括号。
- 持初始化语句,可定义代码块局部变量。
- 代码块左 括号必须在条件表达式尾部。
语法
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
1.5switch
- Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
- 而如果switch没有表达式,它会匹配true。
- Go里面switch默认相当于每个case最后带有break,
- 匹配成功后不会自动向下执行其他case,而是跳出整个switch,
- 但是可以使用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
-
数组:是同一种数据类型的固定长度的序列。
-
数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。
-
长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
-
数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
for i := 0; i < len(a); i++ { } for index, v := range a { } -
访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
-
数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
-
支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
-
指针数组 [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
- 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
- 切片的长度可以改变,因此,切片是一个可变的数组。
- 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
- cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
- 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
- 如果 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得区别
- 二者都是用来做内存分配的。
- make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
- 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
1.12struct
结构体得定义
使用type和struct关键字来定义结构体
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
其中:
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名。结构体中的字段名必须唯一。
- 字段类型:表示结构体字段的具体类型。
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"
)
方法
- time.Now()返回当前时间
- time.Date(年,月, 日, 时, 分, 秒, 毫秒, time.UTC)返回规定时间
- 对象.Fromat("")
- 对象.Year(), 对象.Month(), 对象.Day(), 对象.Hour(), 对象.Minute(),对象.Minutes(), 对象.Seconds()
1.18strconv
进制转换
import (
"strconv"
)
- strconv.ParseFloat("字符串",64)//将字符串变为float数字
- strconv.ParseInt("字符串",0,64)//将字符串根据进制变为int数字
- strconv.Atoi("字符串")//将字符串变为数字
1.19env
导包
import (
"os"
"os/exec"
)
- os.Args
- os.Getenv("PATH")//获取环境变量path的值
- os.Serenv("a","b")//设置环境变量a为b
- exec.Command("grep", "127.0.0.1", "路径").CombinedOutput()//寻找路径下的命令并输出