标准库文档

标识符&关键字

标识符

关键字

变量

var 变量名 变量类型 or var (变量名1 变量类型 \n 变量名2 变量类型)

变量声明未被使用,则会编译不过

  1. 变量声明
  2. 变量批量声明
  3. 局部变量
  4. 匿名变量

常量

const

  1. 批量声明

iota

常量计数器

数组

同一种数据类型的集合,大小在声明时确定,数组是类型

数组声明

  1. var 数组名 [数组长度]数据类型
  2. 例子:var al [3]bool //[false,false,false]
  3. var a12 [行数量][列数量] int

数组初始化

不够的数量用相应的类型数据填充

  1. a1 = [3]bool{true, true}
  2. a1 = [...]bool{true,true,true} //自动判断数组长度
  3. a1 = [5]{0:1,4:2}//特定数组长度设置值
  4. a12 = [3]int{
  5. [2]int{1,2},
  6. [2]int{3,4},
  7. [2]int{5,6},
  8. }

数组的遍历

  1. a1 := [3]string{"胡", "加", "加"}
  2. for i := 0; i < len(a1); i++ {
  3. fmt.Println(a1[i])
  4. }
  1. a1 := [3]string{"胡", "加", "加"}
  2. for i, v := range a1 {
  3. fmt.Println(i, v)
  4. }
  5. for _, v := range a1 {
  6. fmt.Println(i, v)
  7. }

切片(slice)

拥有相同类型数据可变长度的数据序列 ,长度可变,引用类型,判断slice是空的,用len(slice)==0判断,不用nil;

定义

  1. var 数组名 []数据类型
  2. 例子: var al []int

初始化

  1. a1 := []int{1,2,3}
  2. // 由数组创建切片
  3. a2 :=[5]int{1,2,3,4,5}
  4. a3 :=a1[1:4]
  5. // 由切片创建切片
  6. a4 :=a3[0:2]
  7. fmt.Println(a2, a3, a4) => 输出 [1 2 3 4 5] [2 3 4] [2 3]

切片容量&长度

make 函数创建切片

make([]数据类型,长度,容量)

  1. a1 := make([]int,5,10)
  2. fmt.Println(len(a1), cap(a1))

append 切片追加元素

当容量 不够时,底层会进行扩容处理,扩容策略会根据当前值大小,数据类型等做出相应的扩容处理!!!!

  1. a1 := make([]int, 2, 2)
  2. fmt.Println(len(a1), cap(a1), a1)
  3. a1 = append(a1, 3)
  4. fmt.Println(len(a1), cap(a1), a1)
  5. a1 = append(a1, 4, 5)
  6. fmt.Println(len(a1), cap(a1), a1)
  7. a2 := make([]int ,1,2)
  8. a1 = append(a1,a2...)

扩容策略源码见 /src/runtime/slice.go growslice方法。go版本 “go1.15.6 windows/amd64”

  1. newcap := old.cap
  2. doublecap := newcap + newcap
  3. if cap > doublecap {
  4. newcap = cap
  5. } else {
  6. if old.len < 1024 {
  7. newcap = doublecap
  8. } else {
  9. // Check 0 < newcap to detect overflow
  10. // and prevent an infinite loop.
  11. for 0 < newcap && newcap < cap {
  12. newcap += newcap / 4
  13. }
  14. // Set newcap to the requested cap when
  15. // the newcap calculation overflowed.
  16. if newcap <= 0 {
  17. newcap = cap
  18. }
  19. }
  20. }

复制切片copy

copy 复制,值赋值

  1. copy(目标切片,源切片)

指针

&=》 取地址
*=》 根据地址取值

  1. var a1 *int // =>指针指向nil
  2. *a1 = 100 // 会报错的
  3. var a2 = new(int) 开辟一个空间

map

无序的key-value数据结构。引用类型。类似js中的object

  1. 声明 名字 map[key类型] value类型
  2. var mapname map[string] int ;
  3. 初始化 使用make
  4. mapname =make(map[string]int,size) // 估算map容量,避免动态扩容
  5. // 设置值
  6. mapname["key"] = 2
  7. // 获取值
  8. value ,isbe := mapname["key2"]
  9. //map遍历
  10. for key, value := range mapname {
  11. fmt.Printf(key, v)
  12. }
  13. // map 删除

函数

函数的定义

  1. func1 functionname(参数1 参数类型1,参数2 参数类型2)(返回值1 返回类型1){
  2. return 返回类型1的数据
  3. }
  4. func1 functionname(参数1 参数类型1,参数2 参数类型2) 返回类型1{
  5. return 返回类型1的数据
  6. }
  7. func functionname(){
  8. }

其中func1 ,相当于已经在函数内部声明了返回值1;

defer

延迟操作,延迟后面的语句

  1. defer fmt.Printf("start 1 => ")
  2. defer fmt.Printf("start 2 => ")
  3. defer fmt.Printf("start 3 => ")
  4. fmt.Printf("start 4 => ")

结果 start 4 => start 3 => start 2 => start 1 =>
defer执行契机:
image.png如果延迟执行内部有需要执行的条件,需要先满足执行条件

  1. func() {
  2. a := 1
  3. b := 2
  4. defer callbac("1", a, callbac("10", a, b))
  5. a = 0
  6. defer callbac("2", a, callbac("20", a, b))
  7. b = 1
  8. }()
  9. run ==>
  10. 10 1 2 3
  11. 20 0 2 2
  12. 2 0 2 2
  13. 1 1 3 4

函数作为参数

函数可以作为一种类型,可以作为参数传递

  1. func f3(x func() int) {
  2. result := x()
  3. fmt.Printf("%T\n", result)
  4. }

函数可以作为一个返回值

  1. func f3(x func() int) func() {
  2. result := x()
  3. return result
  4. }
  5. func f3(x func() int) func(int ,int ) int {
  6. result := x()
  7. return result
  8. }

实际上也还是函数的样子。参数的变量,变量类型。 返回值,返回类型。

匿名函数

没有名字的函数,一般写在函数内,作为代码块。或者立即执行此函数

  1. f1 := func() {
  2. fmt.Println("这是匿名函数")
  3. }
  4. f1()
  5. func() {
  6. fmt.Println("这是匿名函数立即执行")
  7. }()

闭包

和js 类似,将变量的”生命”延长了,本来变量随着函数的生命周期的结束而结束,但是闭包将需要使用的变量生命延长至使用处。这个不需要过度的研究闭包。本身这个名字就很怪,非常怪,这概念在js中也是令人头痛。

内置函数

close 主要关闭channel
len string,array,slice,map,channel 长度
new 分配值类型 内存,比如int,struct
make 分配引用类型内存,chan.map,slice
append 追加元素到array,slice
panic& recover 错误处理

panic&recover

panic 在程序的任何地方都可能出现会导致程序崩溃,recover 在defer 中才能使用,用来补救函数.

  1. 在某些资源被打开之后,程序panic .这是补救就是,在defer 下释放资源。
  2. func test() {
  3. defer func() {
  4. err := recover()
  5. fmt.Println(err)
  6. println("兜底关闭了文件")
  7. }()
  8. println("打开了文件")
  9. panic("程序崩溃了")
  10. }

defer 补救在可能出现错误之前定义

结构体

可以说在go 语言中实现“对象”

类型别名

  1. type 类型别名 = 类型 // 类型别名,在程序编译的时候是原型类型

自定义类型

  1. type 自定义类型 类型 // 自定义类型,编译之后是一定义类型

定义声明

  1. type 结构体名字 struct{
  2. 属性名字1 属性类型1
  3. 属性名字2 属性类型2
  4. }

匿名结构体

  1. var dd struct {
  2. 属性名字1 属性类型1
  3. 属性名字2 属性类型2
  4. }

结构体指针

  1. type person struct{
  2. name string
  3. age int
  4. }
  5. per := person{
  6. name :"名字",
  7. age:1
  8. }
  9. per:= new(person)

内存分布

结构体中数据的内存是连续的,即初始化时分配了连续的内存

仿造构造函数

  1. type person struct {
  2. name string
  3. age int
  4. }
  5. func newperson(name string, age int) person {
  6. return person{
  7. name: name,
  8. age: age,
  9. }
  10. }
  11. // 或者
  12. func newperson(name string, age int) *person {
  13. return &person{
  14. name: name,
  15. age: age,
  16. }
  17. }

仿造“对象”的方法

限定方法的时候对象

  1. func (per person) hold(){
  2. fmt.Printf("人类对象实例",per.name) // 此时per和外部的不是一个东西
  3. }
  4. func (per *person) hold(){
  5. per.age ++;
  6. fmt.Printf("人类对象实例",per.name) // 传外部指针
  7. }
  8. var pers = new(person)
  9. pers.hold()

此操作可以给自定义类型进行扩展

  1. type myInt int
  2. func (myint myInt)hello(){
  3. fmt.Println("这是自定义类型")
  4. }
  5. func main(){
  6. test := myInt(100)
  7. test.hello()
  8. }

结构体匿名字段

字段类型当作 key

  1. type person struct{
  2. string
  3. int
  4. }

结构体嵌套

结构体组成了结构体

  1. type address struct {
  2. city string
  3. }
  4. type person struct {
  5. name string
  6. age int
  7. address address
  8. }
  9. //type person struct { 匿名嵌套结构体
  10. // name string
  11. // age int
  12. // address
  13. //}
  14. func main (){
  15. per :=person{
  16. name:"名字"
  17. age:1,
  18. address:{
  19. city :"城市"
  20. }
  21. }
  22. fmt.Println(per.name,per.address.city);
  23. }

仿造“对象”继承

利用嵌套结构体,将父级属性嵌入.

json于结构体的转换

结构体中属性名需要大写才可以被其他包识别
序列化 && 反序列化

  1. per := person{
  2. Name: "名字", Age: 1,
  3. }
  4. // 序列化
  5. p, err := json.Marshal(per)
  6. if err != nil {
  7. fmt.Println("序列化错误")
  8. return
  9. }
  10. fmt.Println("序列化结果", string(p))
  11. str := `{"Name":"理想","age":18}`
  12. var paf person
  13. json.Unmarshal([]byte(str), &paf)
  14. fmt.Printf("反序列化结果%#v\n", paf)

接口

interface, 一种类型,但是不能被实例化,他的作用呢?可以说是一些方法定义的集合体。任何实现了这些方法一个或者多个的数据类型都是这个接口的“实现”。这意味着可以通过一个统一的渠道来执行这些方法!!!

package

  • 一个文件夹下面只能有一个包
  • 一个程序有且只有一个main 方法,入口方法
  • 引入包,以$GOPATH/src 为起始路径
  • 包中标识符(变量名/接口/方法/结构体)首字母小写,表示私有,其他包不能使用.
  • 给引入的包设置别名

    1. import(
    2. myself "com.yourself.code.project.module"
    3. )
  • 匿名导入.导入了但是没有使用其中方法.(会执行init),比如数据库操作

    1. impoert _ "com.yourself.code.project.module"

    image.png

文件操作

读取文件

  1. filrobj, err := os.Open("./main.go") // 打开文件
  2. if err != nil {
  3. fmt.Println("open file error : %v", err)
  4. return
  5. }
  6. defer filrobj.Close()
  7. for {
  8. var tmp [128]byte
  9. filedata, err := filrobj.Read(tmp[:]) //从文件对象拿数据
  10. if err != nil {
  11. fmt.Println("read file error,errr:%v", err)
  12. return
  13. }
  14. fmt.Println("读取了字节数", filedata)
  15. fmt.Println(string(tmp[:filedata]))
  16. if filedata < 128 { // 代表数据读取完毕
  17. return
  18. }
  19. }

使用包ioutil读取文件

  1. ret, err := ioutil.ReadFile("./main.go")
  2. if err != nil {
  3. fmt.Println("读取文件错误%v", err)
  4. return
  5. }
  6. fmt.Println(string(ret))

写入文件

文档链接

  1. file, err := os.OpenFile("./main.go", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
  2. if err != nil {
  3. fmt.Println("写文件打开错误%v", err)
  4. }
  5. defer file.Close()
  6. file.WriteString("//测试") == >实际上是对Write 的封装
  7. file.Write([]byte("//测试"))

使用bufio 写文件

  1. file, err := os.OpenFile("./res.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
  2. if err != nil {
  3. fmt.Println("写文件打开错误%v", err)
  4. }
  5. defer file.Close()
  6. // file.WriteString("//测试")
  7. // 使用bufio
  8. wr := bufio.NewWriter(file)
  9. wr.WriteString("使用bufio 写数据到缓冲,不过记得将缓冲数据刷新到文件内")
  10. wr.Flush()

获取用户控制台输入

  1. var s string
  2. reader := bufio.NewReader(os.Stdin)
  3. s, _ = reader.ReadString('\n') // 回车终止
  4. fmt.Println("用户输入的数据库%s", s)

反射

json与结构体的转换应用了反射,GO 是强类型的语言,基本上在编译阶段就能确定程序的类型,但是有的情况是在编译时不知道的,这时候使用反射,在程序运行的时候再去获取变量类型.比如orm库.配置文件等等

reflect.TypeOf

  1. func reflectType(x interface{}) {
  2. v := reflect.TypeOf(x)
  3. fmt.Printf("类型:%v\n", v)
  4. }
  5. func main() {
  6. var a float32 = 3.14
  7. reflectType(a) // == > 类型:float32
  8. }

获取任意值的类型对象

类型种类(kind)&&名称(Name)

  1. type Dog struct {
  2. }
  3. func reflectType(x interface{}) {
  4. v := reflect.TypeOf(x)
  5. fmt.Printf("type:%v kind :%v ", v.Name(), v.Kind())
  6. }
  7. func main() {
  8. var dog = Dog{}
  9. reflectType(dog)
  10. }

reflect.ValueOf

获取任意值的类型值

值的种类

  1. func reflectValue(x interface{}) {
  2. v := reflect.ValueOf(x)
  3. k := v.Kind()
  4. switch k {
  5. case reflect.Int64:
  6. fmt.Printf("type is int64 ,value is %d\n ", int64(v.Int()))
  7. case reflect.Float32:
  8. fmt.Printf("type is float32 ,value is %f\n ", float32(v.Float()))//=>type is float32 ,value is 3.140000
  9. }
  10. }
  11. func main() {
  12. var a float32 = 3.14
  13. reflectValue(a)
  14. }

反射设置值

函数传值在函数或方法体内是操作值拷贝.所以基本传指针.否则通过反射修改值会引发panic.通过Elem 获取指针的值进行修改操作

  1. // 指针传递
  2. func reflectSetValue(x interface{}) {
  3. v := reflect.ValueOf(x)
  4. if v.Elem().Kind() == reflect.Float32 {
  5. v.Elem().SetFloat(3.222)
  6. }
  7. }
  8. // 非指针传递
  9. func reflectSetValuePinc(x interface{}) {
  10. v := reflect.ValueOf(x)
  11. if v.Elem().Kind() == reflect.Float32 {
  12. v.Elem().SetFloat(3.111)
  13. }
  14. }
  15. func main() {
  16. var a float32 = 3.14
  17. reflectSetValue(&a)
  18. // reflectSetValuePinc(a)
  19. fmt.Print("修改之后的值是", a)
  20. // var dog = Dog{}
  21. // reflectType(&dog)
  22. }

IsValid&& IsNil

IsValid 判定返回值是否有效,IsNil 判定指针是否为空

  1. func IsNilIsValid() {
  2. var a *int
  3. fmt.Println("指针空", reflect.ValueOf(a).IsNil())
  4. fmt.Println("nil is Valid", reflect.ValueOf(nil).IsValid())
  5. b := struct{}{}
  6. fmt.Println("结构体中是否存在某个属性", reflect.ValueOf(b).FieldByName("属性名字在").IsValid())
  7. fmt.Println("结构体中是否存在某个方法", reflect.ValueOf(b).MethodByName("方法名字").IsValid())
  8. mapobj := map[string]int{}
  9. fmt.Println("map中不存在的key", reflect.ValueOf(mapobj).MapIndex(reflect.ValueOf("搞钱")).IsValid())
  10. }
  11. //指针空 true
  12. //nil is Valid false
  13. //结构体中是否存在某个属性 false
  14. //结构体中是否存在某个方法 false
  15. //map中不存在的key false

结构体反射

结构体和反射信息的相关操作

StructField

structfield 用来描述结构体中一个字段信息,其拥有修改获取方法. 官方

  1. type StructField struct {
  2. // Name is the field name.
  3. Name string
  4. // PkgPath is the package path that qualifies a lower case (unexported)
  5. // field name. It is empty for upper case (exported) field names.
  6. // See https://golang.org/ref/spec#Uniqueness_of_identifiers
  7. PkgPath string
  8. Type Type // field type 字段类型
  9. Tag StructTag // field tag string 字段标签
  10. Offset uintptr // offset within struct, in bytes 字段在结构体中字节偏移量
  11. Index []int // index sequence for Type.FieldByIndex 索引切片
  12. Anonymous bool // is an embedded field 是否是匿名字段
  13. }

exapmle

  1. type student struct {
  2. Name string `json:"name" yml:"servename"`
  3. Age int `json:"age"`
  4. }
  5. func readstructField() {
  6. stu := student{
  7. Name: "护甲问",
  8. Age: 28,
  9. }
  10. typ := reflect.TypeOf(stu)
  11. for i := 0; i < typ.NumField(); i++ {
  12. field := typ.Field(i)
  13. fmt.Printf("name:%s index:%d type:%v tag:%v \n", field.Name, field.Index, field.Type, field.Tag.Get("yml"))
  14. }
  15. if scoreField, ok := typ.FieldByName("Name"); ok {
  16. fmt.Printf("name:%s index:%d type:%v tag:%v \n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("yml"))
  17. }
  18. }
  19. name:Name index:[0] type:string tag:servename
  20. name:Age index:[1] type:int tag:
  21. name:Name index:[0] type:string tag:servename

缺点

  1. 反射类型错误,在运行时才会出错,意味着错误延后性.
  2. 性能低下
  3. 代码缺少联系性,增加阅读难度

练习

分金币

文件操作-日志库

  1. 日志分级
  2. 可以w

    利用反射解析config.ini 文件