zoukankan      html  css  js  c++  java
  • Go语言基础

    GO开发

    • Golang被誉为21世纪的C语言

      • 2012.3 - 2020.2 1.0 - 1.14版本
    • 为什么选择GO

      • 继承python的简洁 & C语言的性能于一身
    • 环境搭建

    • 执行golang代码。

      • go run **.go
    • 或者go文件中 go build 会生成一个文件 在执行可执行文件

      • 再或者 go install。会将可执行文件放到bin目录
    • 创建目录

    目录结构如下:
    xxx 
    - bin
    - pkg 
    - src  //用于存放项目代码的目录
    
    • 环境变量
    GOROOT, GO编译器安装目录
    GOPATH, 用于存放项目代码, 编译后的可执行文件, 编译后的包文件(go 1.11版本后 ==> go.mod).
    GOBIN, 编译后的可执行的文件存放的目录
    
    • 开发工具

      • goland ide
      • vscode 编辑器
    • 配置

      • 字体
      • 参数提示
    • 项目开发

      • 新项目

      • 打开已经存在的项目

        注意: 项目放在$GOPATH/src目录。

    GO语法的使用

    • Go包管理初识 : 知道项目中文件和文件 文件和文件夹之间的关系

    • 输出 , 写代码 在go编译器运行时会在屏幕显示内容

    • Go的数据类型

      • 整型
      • 字符串
      • 布尔类型
    • 变量 & 常量。当作是昵称 别名

    • 输入

    • 条件语句 if else elif

    1. GO包管理

    • 一个文件夹可以称为一个包
    • 在文件夹(包)中可以创建多个文件
    • 在同一个包下的每个文件中必须指定包名称 且必须相同

    重点:

    - main包。如果是main包 必须写一个main函数  此函数就是项目的入口(main)    编译生成可生成可以执行进制文件
    - 非main包  
    

    2. 输出

    在终端将想要展示的数据显示出来, 例如欢迎登录, 请输入用户名等

    • 内置函数
      • print
      • println
    • fmt包(推荐使用)
      • fmt.print
      • fmt.println

    扩展: 进程中有 stdin/stdout/stderr

    fmt格式化输出
    
    fmt.printf()
    占位符
    %s 字符串
    %d 整型
    %f 小数
    %.2f 保留两位小数
    
    

    注释: 单行注释 // 多行注释 /**/

    3. 数据类型

    • 整型
    • 字符串
    • 布尔型 false true

    4. 变量var

    就是给各种类型的数据起别名 为了方便引用。还可以暂时存储数据

    • 变量名必须只包含:字母、数字、下划线

    • 变量不能以数字开头

    • 不能使用golang中的内置变量 关键字

    5. 输入

    fmt.scanf()

    6. 变量简写

    var name string = "ha"
    var name = "ha"
    name := "ha"
    
    
    
    因式分解
    var(
    	name = "zj"
      age = 18
      hobby = "sing"
    
    )
    
    go编译器会认为如果声明或者声明且赋值的变量没有进行引用, 就要被删除掉

    7. 变量作用域 (namespace名称空间)

    如果我们定义了大括号, 那么在大括号中定义的变量

    • 不能被上级使用
    • 可以在同级中引用使用
    • 子级可以引用上级的变量
    • 函数内的方法都是局部变量
    • 定义全局变量不能使用 := 简写

    全局变量和局部变量(都可以进行因式分解):

    • 全局变量: 在函数外的定义的非简写变量称为全局变量
    • 局部变量: 在{}内定义的变量为局部变量

    8. 变量的赋值及内存相关

    name := "zj"
    fmt.println(name, &name) //打印变量内容及内存地址
    

    9. 常量const

    不可修改的变量

    	// 常量 - 不可修改的变量
    	const age = 12
    	//age = 13
    	fmt.Println(age)
    
    	const (
    		va = 123
    		vb = "111"
    	)
    

    10. iota

    可有可无的东西 可以理解为计数器

    // iota 计数累加器 从0开始
    const (
    		v1 = iota
    		v2
    		v3
    		v4
    
    	)
    	fmt.Println(v1, v2, v3, v4)
    
    
    

    11. 输入

    // 让用户输入数据, 完成数据交互
    fmt.scanf
    fmt.scan
    fmt.scanln
    
    
    Scan
    	
    	var name string
    	var age int
    	fmt.Println("请输入用户名,姓名:")
    
    	_, error := fmt.Scan(&name, &age)  // _是count输入的总值 , error 是报错nil报错是输入争取 如果不是就是报错的
    	//fmt.Println(name, age)
    	//fmt.Println(error)
    	if error == nil {
    		fmt.Println(name, age)
    	} else {
    		fmt.Println("输入值错误", error)
    	}
    
    
    
    
    • 常用的为scanln
      • 但是scanln的问题在于如果输入的变量存在空格 默认取空格之前的问题 所以我们要使用os.stdin标准输入
    	// os.stdin标准输入
    	fmt.Println("请随便输入点东西:")
    	reader := bufio.NewReader(os.Stdin)
    	// line 从stdin中读取一行的数据 数据类型为byte 可以转换为字符串
    	// reader默认一次只能读取4096个字节  如果读取的字节数小于4096 isprefix=false
    	line, _, _ := reader.ReadLine()
    	// 通过string可以将byte类型的数据换换成字符串 类似于python的decode
    	fmt.Println(string(line))
    

    12. 条件语句

    • 最基本的条件语句

      if 条件{
        	条件成立执行语句
        
      } else {
        	条件不成立执行语句
        
      }
      
      
      // 例子
      	var (
      		username string
      		passwd string
      	)
      	fmt.Print("username:")
      
      	fmt.Scanln(&username)
      	fmt.Print("password:")
      	fmt.Scanln(&passwd)
      
      	if username == "zj" && passwd == "123456"{
      		fmt.Printf("用户名%s登录成功", username)
      	}else {
      		fmt.Println("用户名或密码错误")
      
      	}
      
    • 多条件判断

    if {
      
    } else if {
      
    } else {
      
    }
    
    • 嵌套
    if {
      if {
        
      } else {
        
      }
      
    } else {
      
    }
    
    • Switch case 语句 类似于shell中的switch case 语句

    • for循环语句

    • goto 语法 , 不建议使用

    • 字符串的格式化 ,

    • 运算符的优先级

    1.switch case 语句

    package main
    
    import "fmt"
    
    func main() {
    	var number int
    	_, err := fmt.Scanln(&number)
    	if err == nil {
    
    		switch number {
    		case 1 :
    			fmt.Println("1111")
    		case 2:
    			fmt.Println("2222")
    
    		case 3:
    			fmt.Println("3333")
    
    		default:
    			fmt.Println("无法识别你输入的内容")
    		}
    
    	}
    }
    
    
    switch 数据类型必须一致
    

    2.for 循环

    
    
    
    	//1.死循环 for 或者 for true
    	//fmt.Println("开始")
    	//for true {
    	//	fmt.Println("123")
    	//	time.Sleep(time.Second * 2) // 一秒为单位 time.second
    	//	break
    	//}
    	//fmt.Println("end")
    
    
    	number := 1
    
    	for number < 5 {
    		fmt.Println(number)
    		number += 1
    	}
    
    	fmt.Println("end")
    
    // for 循环条件判断 
    
    for i:=1; i<10{
      
    }
    
    // 循环10次 进行++
    	for i:=1; i<10; i++{
    		fmt.Println(i)
    
    	}
    
    
    
    
    
    
    
    	count := 0
    	for i:=1; i<100; i++{
    		fmt.Println(i)
    		count += i
    
    	}
    	print(count)
    
    • continue
    // 退出本次循环
    	for i := 1; i <= 10; i++ {
    		if i == 7 {
    			continue
    		}
    		fmt.Println(i)
    	}
    
    
    
    • break
    //跳出整个循环
    	for i := 1; i < 5; i++ {
    		if i == 4 {
    			fmt.Println("bye")
    			break
    		}
    		fmt.Println(i)
    
    	}
    
    
    
    *对 for 进行打标签, 然后可以通过break和continue就可以时间多层循环的跳出和中止
    
    test1:
    	for i := 1; i < 5; i++ {
    
    		for j := 1; j < 3; j++ {
    			if j == 2 {
    
    				//continue test1
    				break test1
    			}
    			fmt.Println(i, j)
    
    		}
    
    	}
    
    
    goto 语句
    //跳跃到指定的行向下执行代码
    package main
    
    import "fmt"
    
    func main() {
    
    shibai:
    	var name string
    	fmt.Print("请输入用户名:")
    
    	_, err := fmt.Scanln(&name)
    	fmt.Println(name)
    	if len(name) < 1 {
    		goto shibai
    	}
    	if err == nil {
    		if name == "zj" {
    			fmt.Println("牛皮")
    		} else {
    			fmt.Println("弟弟")
    			goto shibai
    		}
    
    	}
    
    }
    
    
    
    • 字符串格式化
    // 格式化字符串
    package main
    
    import "fmt"
    
    func main() {
    
    	var name string
    	var age int
    	var score float64
    	fmt.Println("please input name")
    	fmt.Scanln(&name)
    
    	fmt.Println("please input name")
    	fmt.Scanln(&age)
    
    	fmt.Println("please input name")
    	fmt.Scanln(&score)
    
    	result := fmt.Sprintf("name is %s, age is %d, score is %.2f", name, age, score)
    	fmt.Println(result)
    
    
    }
    
    
    
    
    

    必备基础知识

    
    - 进制
    - 单位
    - 字符编码 
    
    

    golang 数据类型

    1.整型
    var v1 int8 = 10
    var v2 int16 = 20 
    
    v3 := int16(v1) + v2
    
    注意: 
      - 低位转高位是没有问题
    	- 但是高位转成低位 是很有可能出问题的
    
    
    
    
    整型转换成字符串类型:Itoa
    	v1 := 19
    	result := strconv.Itoa(v1)
    	
    	fmt.Println(result, reflect.TypeOf(result))
    2.字符串转换成整形Atoi
    
    
    	v2 := "19"
    	res, err := strconv.Atoi(v2)
    	if err == nil {
    		fmt.Println(res)
    	} else {
    		fmt.Println("转换失败请检查原数据")
    	}
    
    • 进制转换

    go:

    	- 10进制是以整型的方式存在
    	- 其他进制 都是以字符串形式存在的
    
    • 整型 : 10进制数 转换成其他进制 (FormatInt)
    // 使用方法进行进制转换
    
    
    v1 := 9
    // 通过strconv.FormatInt方法进行进制数转换 int类型必须是int64 所以必须进行声明  第二个参数代表的是转换成几进制
    // 10进制转换成其他进制
    v2 := strconv.FormatInt(int64(v1), 8)
    fmt.Println(v2, reflect.TypeOf(v2))
    
    
    
    
    
    
    
    
    • 其他进制转换成10进制parseint
    	// 其他进制转换成整形=> 10进制
    	// parseint(其他进制数据, 原本的进制类型, 要转换成的进制类型 )
    	// 只要转换成功 转化出来的数据类型就是int64类型
    	data := "10001000"
    	res, err := strconv.ParseInt(data, 2, 10)
    	//fmt.Println(err)
    	if err == nil {
    		fmt.Println(res, reflect.TypeOf(res))
    	}
    
    
    - 自己出的小练习
    
    将2进制的10001000转换成8进制数
    思路:因为2进制和8进制代码层面不能相互转换 所以我们先将2进制转换成10进制 在将10进制装换成8进行
    
    
    	data := "10001000"
    	res, err := strconv.ParseInt(data, 2, 10)
    	//fmt.Println(err)
    	if err == nil {
    		fmt.Println(res, reflect.TypeOf(res))
    		test := strconv.FormatInt(int64(res), 8)
    		fmt.Println(test)
    	}
    
    
    

    ![image-20201228175014934](/Users/mac/Library/Application Support/typora-user-images/image-20201228175014934.png)

    常见的数据运算

    math 函数
    math abs 
    math.Pow(2 ,5)  // 2的五次方
    math.max(1, 2)  // 两个值相比较 取大值
    math.min(1, 2)  // 两个值相比较 取小值
    
    
    

    指针/nil/声明变量/ new

    • 声明变量
     
    ### 指针/nil/声明变量/new
    * 声明变量
            
            var v1 int
            v2 := 999
    
    
    * 指针 (作用就是节省内存)
            var v3 *int
            v4 := new(int)
                              nil  
                v3 ->      ^
                             0x0
                            0
                v4 ->    ^
                            0x0
    
    * new 关键字 
        * new用于创建内存并进行内部数据的初始化。并返回一个指针类型
    * nil
        * nil指go语言中的空值
        * var v6 *int
        * var v7 * int32
    
    
    
    * 超大整型
            var v1 big.int
            var v2 *big.int
            var new(big.int)
    
            v1.setint64(1000)
            
    
        
    var v1 big.Int
    v1.SetString("123456767898786898092355262738453726315", 2)
    fmt.Println(v1.String(), reflect.TypeOf(v1.String()))
    
    
    
    
    

    go Array数组

    数组是固定长度的特定类型元素组成的序列
    一个数字由零个或多个元素组成
    数组的长度是固定的,因此Go更常用slice(切片, 动态的增长或收缩序列)
    数组是值类型, 用索引下标访问每一个元素, 范围是0 - len-1, 访问超出数组长度范围 会panic异常
    
    
    
    // Go Array 数组中没有复制的数组 会有相应的默认值
    
    // 声明数组 , 并且个数组中的元素赋值
    var intArr [5]int
    fmt.Println(intArr)
    intArr[0] = 12
    intArr[1] = 34
    
    fmt.Println(intArr)
    
    
    
    // 声明数组 并且直接赋值 
    var namestr [5]string = [5]string{"1", "2"}
    fmt.Println(namestr)
    
    var namestr2 = [5]string{"1"}
    fmt.Println(namestr2)
    
    
    // 取数组最后一个元素  顺便展示指定索引赋值
    var namestr2 = [5]string{"1", 4: "124124"}
    fmt.Println(namestr2, namestr2[len(namestr2)-1])
    
    
    // 自适应数组大小[...]
    var namestr3 = [...]string{"zj", "zjj", "zjjj"}
    fmt.Println(namestr3)
    
    
    // 数据结构题类型数组
    
    var namestr5 = [...]struct{
      name string
      age int
    }{
      {"zj", 18},
      {"ccn", 18},
    }
    fmt.Println(namestr5)
    
    
    // 数组循环
    
    for i:=0; i < len(namestr3);i++{
      fmt.Println("for " + namestr3[i])
    }
    
    for index, value := range namestr3{
      fmt.Println(index, value)
    }
    
    
    
    ** 数组注意事项:
    	数组是多个相同数据的组合, 且长度固定, 无法扩容 [5]int
    
    数组使用步骤:
    	1.声明数组
    	2.给数组元素赋值
    	3.使用数组
    	4.数组索引从0开始 不能index of range
    	5.Go数组是值类型, 变量传递默认是值传递, 因此会进行值拷贝
    	6.修改原本的数组, 可以使用引用传递(指针)
    
    
    
    
    
    
    • 字符串的本质

    计算机中所有的操作和数据最终都是二进制 : 010101010...

    package main
    
    import (
    	"fmt"
    	"strconv"
    	"unicode/utf8"
    )
    
    func main() {
    	var name string = "张无忌"
    
    	fmt.Println(name[1], strconv.FormatInt(int64(name[1]), 2))
    
    	fmt.Println(len(name))
    
    	age := 123
    	res := strconv.Itoa(age)
    	// len字节长度
    	fmt.Println(len(res + ""))
    
    	// 字符串转换为一个 "字节集合"
    	byteset := []byte(name)
    	fmt.Println(byteset) // [229 188 160 230 151 160 229 191 140]
    
    	// 字节集合转换成字符串string
    	fmt.Println(string(byteset))
    
    	// rune集合
    	testName := "11223ddd"
    	runeset := []rune(testName)
    	fmt.Println(runeset,)
    	fmt.Println(string(runeset[:5]))
    
    	// 字符串长度获取
    	// len方法获取的是字节的长度
    	test2Name := "122333zghang张鉴"
    	runeset2 := []rune(test2Name)
    	fmt.Println(len(runeset2))
    	// or 使用utf8的方法获取长度
    
    	test2Namelength := utf8.RuneCountInString(test2Name)
    	fmt.Println(test2Namelength)
    }
    
    
    
    
    字符串常见的使用方法
    
    
    // 获取长度
    	var name string = "张无极"
    
    	fmt.Println(name)
    	fmt.Println(utf8.RuneCountInString(name))
    
    	// 是否以xx开头
    	name2 := "张无忌"
    	reslut := strings.HasPrefix(name2, "张")
    	fmt.Println(reslut)
    
    	// 是否以xx结尾
    	result := strings.HasSuffix(name2, "无忌")
    	fmt.Println(result)
    
    	// 是否包含 类似 python in
    	result2 := strings.Contains(name2, "无")
    	fmt.Println(result2)
    
    	fmt.Println("===============")
    	// 全变大写 类似 python upper
    	stringTest1 := "fff afa fUUUU 涨"
    	fmt.Println(strings.ToUpper(stringTest1))
    	// 全变小写
    	fmt.Println(strings.ToLower(stringTest1))
    
    	// 替换所有是-1  是左到右第一个 是1
    	fmt.Println(strings.Replace(stringTest1, "f", "", 1))
    	fmt.Println(strings.Replace(stringTest1, "f", "", 2))
    	fmt.Println(strings.Replace(stringTest1, "f", "", -1))
    
    	// 分割 split
    	splitTest := strings.Split(stringTest1, " ")
    	fmt.Println(splitTest[len(splitTest)-1])
    
    	// 拼接
    	mes := "你好" + "我好" // 不建议使用
    	fmt.Println(utf8.RuneCountInString(mes))
    
    	// 高效率的字符串拼接方法 非常推荐使用
    	var builder strings.Builder
    	builder.WriteString("我爱你")
    	builder.WriteString("中国")
    	value := builder.String()
    	fmt.Println(value, utf8.RuneCountInString(value))
    
    	// 字符串转换成int
    	var num int = 12
    	fmt.Println(strconv.Itoa(num), reflect.TypeOf(strconv.Itoa(num)))
    
    	textStr := "2141414141414"
    	fmt.Println(strconv.Atoi(textStr))
    
    	textStr2 := "0101010010"
    
    	fmt.Println(strconv.ParseInt(textStr2, 2 ,10))
    	fmt.Println(strconv.FormatInt(int64(num), 16))
    
    
    	// 字符串 转换
    	v1 := string(100)
    	fmt.Println(v1)
    	v2, size := utf8.DecodeRuneInString("d")
    	fmt.Println(v2, size)
    
    
    
    • 索引切片和循环
    
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    
    	var name3 string = "你好你好"
    	// 字节索引 切片
    	vs1 := name3[0:3]
    	fmt.Println(vs1)
    
    	// 手动循环所有的字符 range
    	for index, item := range name3 {
    		if index == 6 || index == 3 {
    			fmt.Println(index, string(item))
    		}
    		//fmt.Println(index, string(item))
    
    	}
    
    	// 转换成rune集合
    	dataList := []rune(name3)
    	fmt.Println(dataList, string(dataList))
    
    }
    
    
    • 数组

    数组, 定长且元素类型一致的数据集合 (类型必须相同)

    	// 声明加赋值自适应变量
    	var numbers = [...]int{1, 2, 3}
    	numbers[0] = 2
    	fmt.Println(numbers)
    
    	// 声明定长的数组 不足的用空格代替 len为5
    	var numbers = [5]string{"test", "alex", "didi"}	
    	fmt.Println(numbers)
    
    	
    
    	// 指定索引赋值
    	var namestr2 = [5]string{"1", 4: "124124"}
    	fmt.Println(namestr2, namestr2[len(namestr2)-1])
    
    
    	// 声明指针类型的数组 (指针类型, 不会开辟内存初始化数组中的值)
    	var numbers *[]int
    	
    	
    
    

    数组的特性(可变和拷贝)

    • 元素值是可以被修改的 但是数组一旦声明开辟了内存空间之后 数组的大小就不可以改变
    • 可以进行copy
    // 修改重新赋值
    var numbers = [...]int{1, 2, 3}
    numbers[0] = 2
    fmt.Println(numbers)
    
    
    
    • 长度切片[0:2]
    • 数组的嵌套
    
    二维数组
    // 含义为 数组中有3个元素 每个元素是一个含有2个int元素的数组
    var nestData [3][2]int 
    var nestData = [3][2]int{{1, 2}, {3, 4}}
    
    nestData[2] = [2]int{11, 22}
    nestData[2][1] = 6666
    
    fmt.Println(nestData)
    
    
    

    最后三种数据结构概览

    • 切片
    • 字典
    • 指针

    #切片

    切片, 动态数组

    切片是Go中重要的数据类型,每个切片对象内部都维护着 : 数组指针,切片长度,切片容量 三个数据

    • 创建切片
    // 每一个切片内部都存储了三个数据 
    数组指针*array 、 切片长度len 切片容量[:3]
    
    
    再向切片中追加数据个数大于容量时, 内部会自动扩容且每次扩容都是当前容量的2倍 [:3] -> [:6]
    
    
    // 创建切片
    var nums []int
    // 创建切片 并赋值
    var nums = []int{1,2,3}
    
    // 创建切片
    // make只用于 切片、字典、channel
    // 推荐使用
    int 类型 默认长度为2 容量为3 cap()
    var users = make([]int,2,3)
    
    
    
    
    
    • 自动扩容
    
    v1 := make([]int,1,3)
    v2 := append(v1, 123)
    注意: 扩容前和扩容后的切片内存地址 是不同的
    
    
    
    • 切片追加高级用法
    // 基本相当于python中的extend 扩展列表
    
    arrayList = append(arrayList, []int{100,200,300,400}...)
    fmt.Println(arrayList)
    
    
    
    
    • 切片删除
    // 切片数据实际上是没有删除操作的 我们可以使用append来拼接生成新的切片 代替删除
    newAarrayList := append(arrayList[:6])
    newAarrayList = append(newAarrayList, arrayList[7:]...)
    fmt.Println(newAarrayList)
    
    // or 
    
    newAarrayList := append(arrayList[:6], arrayList[7:]...)
    fmt.Println(newAarrayList)
    
    
    • 切片插入
    newAarrayList := append(arrayList[:6])
    newAarratList = append(arrayList, 123356)
    newAarrayList = append(newAarrayList, arrayList[7:]...)
    fmt.Println(newAarrayList)
    
    
    
    
    

    // 删除插入 效率低下 不使用。应该使用链表

    • 切片嵌套
    // 嵌套
    
    testList := [][]int{[]int{1, 2}, []int{66, 77, 99}}
    fmt.Println(testList)
    
    

    目前为止 上面所学的数据类型中, 在修改切片的内部元素时, 会造成所有的赋值的变量同时修改 (不扩容的前提下)

    字典类型(Map)

    • 任何编程语言中都会存在字典或者映射 , 同python中的字典 是以键值对的形式存在的数据集合
    {
      'name' : 'zj',
      
      'age': 15,
      
    }
    
    
    
    • 字典的特性map
      • 键不能重复
      • 键必须可hash(切片和map都不可hash所以不能做键)
      • 无序
    • map声明
    	
    // 第一个string代表键的类型 第二个string代表值的类型
    userInfo := map[string]string{
      "name": "zj",
      "age": "18",
    
    }
    fmt.Println(userInfo["name"])
    //or
    // dataInfo := make(map[string]string, 10)
    dataInfo := make(map[string]string)
    dataInfo["name"] = "ccn"
    fmt.Println(dataInfo["name"])
    
    
    
    
    • map 长度和容量
    len(userInfo)
    dataInfo := make(map[string]string, 10)
    // 根据穿参10 计算出合适的容量
    // 一个map 中会包含很多筒,每个筒可以存放8个键值对
    
    
    • map 增删改查
    userInfo := map[string]string{
      "name": "zj",
      "age": "18",
    
    }
    // 增
    userInfo["test"] = "123ff"
    // 改
    
    userInfo["name"] = "ccc"
    // 删
    delete(userInfo, "name")
    // 查
    for key, value := range userInfo {
      fmt.Println(key, value)
      
    }
    fmt.Println(userInfo["name"])
    
    
    
    • map变量赋值

    ...

    注意 : 无论是否存在扩容都指向同一个地址

    • map初始化详解
    info = make(map[int]int, 10)
    - 第一步 创建一个hmap的结构体对象
    - 第二部 生成一个hash因子 hash0 并赋值到hmap对象中(用于后续为key创建hash值)
    - 第三部 根据hint=10, 并根据算法来创建 2的B次方筒数,当前应该是1 所以就是创建两筒
    
    
    
    
    • map扩容原理
    再向map中添加数据时, 当达到某一个条件, 则会引发字典扩容
    扩容的条件:
    - map中数据总个数/筒个数 > 6.5时, 便会引发翻倍扩容
    
    
    

    指针数据

    什么是指针 一个指针变量指向了一个内存地址, 类似于变量和常量, 在使用指针前需要声明指针。指针声明格式如下

    // 指针类型生来就是用来节省内存的 
    
    // 声明指针类型的数据只需要在声明数据类型前加* 即可声明为指针类型
    package main
    
    import "fmt"
    
    func main() {
    	//var a int = 10
    	//fmt.Printf("%x" , &a)
    
    	var name string = "yunZhOngKeXin"
    	// 查看内存地址为&符号
    	fmt.Printf("name的内存地址: %v
    ", &name)
    
    	// 指针变量, 存的是内存地址
    	// ptr 指针变量指向变量name的内存地址
    	var ptr *string
    	ptr = &name
    	fmt.Printf("指针变量ptr的内存地址: %v 
    ", ptr)
    
    	// 获取指针变量的值, 用*ptr
    	// *ptr表示读取指针指向变量的值
    	// *ptr 代表真正的数据
    	fmt.Printf("指针变量ptr指向的值是: %v
    ", *ptr)
    
    }
    
    
    
    
    • 指针

    指针 , 是一种数据类型, 用于表示数据的内存地址。

    // 声明一个 字符串类型的变量 (默认初始化为空字符串)
    var v1 string 
    
    // 声明一个字符串的指针类型
    var v2 *string
    
    
    
    // 声明一个字符串类型的变量并赋值
    var name string = "zj"
    // 声明一个字符串的指针类型的变量, 值为name对应的内存地址
    var pointer *string = &name
    
    
    • 指针存在的意义

    相当于创建了一个**引用**, 以后可以根据这个引用来获取他里面的值 如果原本的数据发生变化引用也会发生变化,类似于软连接, 快捷方式这种

    v1 := "zh"
    v2 := &v1 
    fmt.println(v1, v2, *v2)
    
    
    • 使用指针的场景
    // 下边的这种函数传参的方式testData字符串会重新copy一份 
    func main {
      
      Test("zjjjj")
    }
    func Test(testData string) {
      testData = "hahah"
      fmt.println(testData)
    }
    
    
    
    // 
    func main {
      
      Test("zzjj")
      
    }
    func Test(ptr *string){
      ptr = "fffff"
      fmt.println(ptr) // 修改指针的内容上边的内容也会被修改 
      
    }
    
    
    
    
    • 指针的指针
    
    n1 := "zhjjj"
    
    n2 := &n1
    fmt.Println(n1, *n2)
    n3 := &n2
    fmt.Println(*n2, **n3)
    n4 := &n3
    fmt.Println(**n3, ***n4)
    
    
    
    • 指针的小高级操作
    // 指针的相加操作
    func Compl() {
    	// 指针的相加操作
    	// 定义一个int8数组
    	dataList := [3]int8{11, 22, 33}
    	// 取出第一个元素的内存地址
    	var firstData *int8 = &dataList[0]
    	// 将第一个铁元素的内存转化成pointer类型
    	ptr := unsafe.Pointer(firstData)
    	// pointer类型 +1
    	targetAddress := uintptr(ptr) + 1
    	// 相加之后重新转换成pointer类型
    	newPtr := unsafe.Pointer(targetAddress)
    	// 将pointer对象转换成int8 指针类型
    	value := (*int8)(newPtr)
    	// 使用指针类型获取数据
    	fmt.Println(*value)
    
    }
    
    
    

    结构体

    什么是结构体?

    | 结构体是一个复合类型, 用于表示一组数据

    | 结构体由一系列属性组成, 每个数据都有自己的类型和 值

    // 定义
    	type Person struct {
    		name string
    		age int
    		email string
    
    	}
    
    	//  初始化
    	var p1 = Person{"zh", 12, "153"}
    	fmt.Println(p1, reflect.TypeOf(p1))
    
    结构体基本格式 
    type 结构体名称 struct{
      字段 类型
      ...
      
    }
    
    
    • 结构体的定义
    type Person struct {
      name string
      age int
      hobby []string.  // 可以是切片哦
    }
    
    
    

    结构体

    
    package main
    import (
    "fmt"
    )
    
    func main() {
    // 定义结构体
    type Person1 struct {
    name, sex string
    age int
    }
    
    var p1 = Person1{"ccn", "sunhat", 18}
    fmt.Println(p1)
    // 结构体嵌套
    type Person2 struct {
    score int
    p1 Person1
    }
    v2 := Person2{99, Person1{"zj", "nan", 18}}
    fmt.Println(v2)
    //结构体匿名
    type Person3 struct {
    city string
    hobby string
    Person1 // 匿名结构体
    }
    v3 := Person3{city: "CN", hobby: "students", Person1:Person1{"11","nan",18}}
    // 由于结构体是匿名的所以可以直接取p1中的name 否则必须v3.变量名.name
    fmt.Println(v3.city, v3.name)
    // 注意结构体赋值的时候 数据会全部都的拷贝一份 无论什么类型
    // 指针结构体 是不拷贝的 软连接
    // 切片和map 数据拷贝的的时候可能会发现感觉并没有拷贝 但其实不是的是因为切片和map没有扩容的时候使用的都是同一块地址导致的 数据的特型
    }
    
    // 如果我们想要在赋值时不拷贝数据 我们可以将数据变成指针类型数据即可
    *[2]string
    *map[string]int
    
    
    
    • 结构体指针
    type Person2 struct {
       lambda string
       *Person
    }
    p3 := Person2{lambda: "这是一个匿名函数", Person: &Person{"Miho", "naming ", 12}}
    p4 := p3
    p3.name = "testzhizhen"
    fmt.Println(p3, *p3.Person, p4, *p4.Person)
    
    • 结构体标签
    	// 标签
    	type Person2 struct {
    		lambda  string "声明匿名"
    		*Person "我是Person"
    	}
    	p3 := Person2{lambda: "这是一个匿名函数", Person: &Person{"Miho", "naming ", 12}}
    	p4 := p3
    	p3.name = "testzhizhen"
    
    	// 标签1
    	testStruct := reflect.TypeOf(p3)
    	fmt.Println(testStruct.Field(1).Tag)
    	// 标签2
    	field, _ := testStruct.FieldByName("lambda")
    	fmt.Println(field.Tag)
    
    	// 循环获取tag
    	fieldNum := testStruct.NumField()
    	for i := 0; i < fieldNum; i++ {
    		fmt.Println(testStruct.Field(i).Tag)
    	}
    
    

    函数

    可以把函数当作一个公用的代码块,用于实现某一个功能. 并且提高代码的重用性和可读性(函数传参数据会重新拷贝 相当于赋值)

    // 函数使用方法
    
    func 函数名(参数 类型) 返回值类型 {
      函数执行体
    }
    
    
    
    package main
    
    import "fmt"
    
    // 定义了返回值的类型时必须有return , name为接收的参数  string 为返回值的类型
    func FirstFun(name string) string {
    	if len(name) > 2 {
    		//fmt.Println(name)
    		return name
    	}
    	return "error"
    
    }
    
    func main() {
    	
    	res := FirstFun("zjccn")
    	fmt.Println(res)
    }
    
    
    • 函数调用指针类型 (节省内存)
    package main
    
    import "fmt"
    
    func FirstFun(name *string) string {
    	*name = "123"
    	if len(*name) > 2 {
    		//fmt.Println(name)
    		return *name
    	}
    	return "error"
    
    }
    
    func main() {
    	name := "ZjCcn"
    	res := FirstFun(&name)
    	fmt.Println(res, name)
    }
    
    
    • 函数也可以作为参数
    func main() {
    
    	TestProxy(10, Add100)
    }
    
    func Add100(data int) (int, bool) {
    	return data + 100, true
    }
    
    func TestProxy(data int, function func(int) (int, bool)) string {
    	resData , flag := function(data)
    	if flag{
    		fmt.Println(resData, flag)
    	}else {
    		fmt.Println("error")
    	}
    	return "complete"
    
    }
    
    
    
    • 函数传递可变长参数 可以不确定传多少参数 (变长参数必须放在最后 类似 **kwargs 且只能存在一个)
    // 这个num的类型实际上是切片类型。可以任意扩容
    func Do(num ... int) int {
    	sum := 0 
    	for _, value := range num{
    		sum += value
    
    	}
    	return sum
    }
    
    func main() {
    	numcount := Do(1,2,2,2,2,2,2,2,2,3,3,3,3,3,3)
    	fmt.Println(numcount)
    }
    
    
    
    • 匿名函数
    func main() {
    
    	// 匿名函数
    	f1 := func(data int) int {
    		return data
    	}
    	fmt.Println(f1(11))
    	// 第二种匿名函数表达
    	f2 := func(data int) string {
    		return strconv.Itoa(data)
    	}(110)
    	fmt.Println(f2, reflect.TypeOf(f2))
    }
    
    
    • 函数不仅能做传递的参数 也可以作为返回值
    	test1 := Test1()
    	fmt.Println(test1(120))
    
    func Test1() func(t1 int) int  {
    	funtion := func(t1 int) int {
    		return t1
    	}
    	return funtion
    }
    
    
    
    • 闭包函数(通过一个函数将我们需要的数据固定包裹在一个包里)
    // 闭包函数的作用就是将生成好的函数封装好值也传好, 之后调用的函数的值返回值 都是定义好的无需再次传参 可直接调用
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	// 这个切片会存储着五个函数 分别打印 0 1 2 3 4
    	var funtionList []func()
    
    	for i := 0; i < 5; i++ {
    		funtion := func(data int) func(){
    			return func() {
    				fmt.Println(data)
    			}
    		}(i)
    		funtionList = append(funtionList, funtion)
    	}
    	// 运行封装好的包
    	funtionList[0]()
    	funtionList[2]()
    	funtionList[3]()
    
    }
    
    
    
    • defer

    用于在一个函数执行体完成之后自动触发的语句, 一般用于结束操作之后释放资源

    // go中称为延迟执行 可以随意定义位置 
    // 相当于python中的 __del__ 释放资源
    // 类似于堆栈。先进后出
    package main
    
    import "fmt"
    
    func main() {
    	TestDefer()
    }
    
    func TestDefer() {
    	fmt.Println("这是一次测试")
    	for i := 0; i < 10; i++ {
    		defer fmt.Println(i)
    
    	}
    }
    
    
    • 自执行函数 (类似于python init)
    // ~= python __init__
    // 自执行函数就是匿名函数
    
    func TestDefer() {
    	result := func(age int) int{
    		return age
    	}(18)
    	fmt.Println(result)
    
    }
    
    
    • 结构体做函数返回值
    type funstruct struct {
    	name string
    	age int
    }
    func StructTest(name string) (funstruct) {
    
    	return funstruct{name: name, age: 18}
    }
    func main(){
      
      t1 := StructTest("testzh")
      fmt.PrintLn(t1)
    }
    
    

    类型方法

    项目开发中可以为type声明的类型编写一些方法, 从而实现对象.方法的操作

    // 可以定义执行自己的代码的方法
    
    package main
    
    import "fmt"
    // 声明类型
    type myInt int
    
    func main() {
    
    	var testMy myInt = 110
    	res := testMy.Dosomething(1,2)
    
    	fmt.Println(res)
    }
    
    // 这个因为前边加上了(i *myInt) 所以他就不是函数了 而是自我定义的方法
    myInt可以是指针 也可以是正常的 为了节省内存我们是用指针
    func (i *myInt) Dosomething (data1 int , data2 int) int {
    	return data1+data2+int(*i)
    }
    
    
    
    • 方法继承(类似于面向对象的继承)
    
    type Base struct {
    	name string
    }
    type Son struct {
    	Base  // 匿名结构体
    	age int
    	test string
    }
    
    func (b1 *Base) Test1() int {
    	return 123
      // return b1.name   name: "张无忌"
    }
    func (s1 *Son) Test2() int  {
    	return 222
    }
    
    
    func main() {
    
    	son := Son{Base:Base{name: "张无忌"}, age:13, test:"测试"}
    	fmt.Println(son.Test1(), son.Test2())
    
    }
    >>>>>>>>>>>>>>>执行结果:
    123 222
    
    
    • 结构体工厂
    声明任何数据类型的时候。只要变量名称为小写 就是私有变量 大写就是公共变量。私有就是只有当前这个.go文件可以使用 公有都能使用
    
    
    • Go接口类型

    GO语言中的接口是一种特殊的数据类型, 定义格式如下:

    type name interface{

    方法名称() 返回值

    }

    例如:

    type Name interface {

    f1()

    f2() int

    f3() (int, bool)

    }

    定义接口的特点是 不需要写任何逻辑代码 只需要将方法的名和返回值传参数 写好即可

    • 接口的作用

      在程序开发中接口一般有两大作用 : 代指类型 和 约束。

    • 空接口, 代指任意类型

    package main
    
    import (
    "fmt"
    "reflect"
    )
    // 定义个空接口 空接口中可以传任意类型的值
    
    type base interface {
    
    }
    
    func main() {
    //dataList := make([]base, 0)
    // 上边这些接口定义可以简写为:
    
    dataList := make([]interface{}, 0)
    dataList = append(dataList, "test")
    dataList = append(dataList, 234)
    dataList = append(dataList, map[string]string{"key": "values"})
    fmt.Println(dataList[2], reflect.TypeOf(dataList))
    something("test")
    something("test")
    something("test")
    caseSth("haha")
    caseSth(123)
    caseSth([]string{"xii", "haha"})
    }
    
    // 像python一样 弱类型
    
    type person struct {
    name string
    }
    
    // 接收到数据类型都是接口类型 所以我们要使用.(类型或者类型结构体)转换成我们想要的格式
    
    func something(arg interface{}) {
    s,ok := arg.(string)
    if ok{
    fmt.Println(s, reflect.TypeOf(s))
    } else {
    fmt.Println("转换失败")
    }
    }
    func caseSth(arg interface{}) {
    
    switch data := arg.(type) {
    case string:
    fmt.Println(data)
    case int:
    fmt.Println(data)
    case []string:
    fmt.Println(data)
    default:
    fmt.Println("WOZHAOBUDAO")
    }
    }
    
    • 非空接口 规范约束
    type Ibase interface {
    
    test1 () int
    
    }
    
    type Person struct {
    name string
    age int
    }
    
    func (p *Person) test1() int {
    return 123
    }
    
    type User struct {
    username string
    password string
    }
    
    func (u *User) test1() int {
    return 567
    }
    
    func main() {
    p := Person{"zj", 18}
    //u := User{"1533333", "520cnn.."}
    p.test1()
    List := []Ibase{&Person{"COMPELTE",12}, &User{"完成", "LE"}}
    fmt.Println(List)
    for _ , value := range List{
    fmt.Println(value, reflect.TypeOf(value))
    }
    }
    
    
    
    • flag包

    基于os.Args可以获取传入的所有参数

    
    package main
    
    import (
    	"flag"
    	"fmt"
    )
    
    func main() {
    	//fmt.Println(os.Args)
    
    	/* >>>结果
    	mac$ ./main 213 123414155  5125
    	[./main 213 123414155 5125]
    	*/
    
    	// h 是命令行要-h 默认是127.0.0.1 后边是备注
    	host := flag.String("h", "127.0.0.1", "主机名")
    	port := flag.Int("p", 1234, "端口号")
    	//host := flag.String("h", "127.0.0.1", "主机名")
    	flag.Parse() // 必须parse 才能将命令行传入的数据传进来
    	fmt.Println(*host, *port)
    
    	/* >>结果
    	mac$ ./main -h 192.168.1.1 -p 1212
    	192.168.1.1 1212
    
    	*/
    }
    
    
    
    • Go regexp 正则表达式
    package main
    
    import (
    	"fmt"
    	"regexp"
    )
    
    func main() {
    	// 根据字符串匹配返回 是否成功
    	matchString, err := regexp.MatchString(".*?:(.*?)n.*?", "vaiuhguifg:xixizhangjiannihaonfaoghaiug")
    	if err == nil {
    		fmt.Println(matchString)  // true
    	}
    
    
    	// 根据匹配值字符串查找
    	reg1 := regexp.MustCompile(`d{11}`)
    	res1 := reg1.FindString("1242563837613")  // 结果 12425638376
    
    	fmt.Println(res1)
    	res2 := reg1.FindAllString("1241245569005837163414115", -1) // 结果就是找所有的11个数字的集合[12412455690 05837163414]
    
    	fmt.Println(res2)
    
    	// 获取分组信息
    }
    
    

    文件路径相关的内置包

    • 文件增删改查
    
    // 创建单级目录  付给权限
    os.Mkdir("testDir", 0755)
    
    // 创建多级目录
    os.MkdirAll("/test/123/code", 0755)
    
    // 删除文件或空文件夹, 文件夹下存在内容数据 就会报错
    os.Remove("testDir")
    
    // 删除文件或文件夹 (同时删除子目录中的数据)
    os.RemoveAll("test123")
    
    
    • 判断目录 或文件是否存在. os.IsNotExist(err)
    
    _, err := os.Stat("test1/test2/123.go")
    if err != nil {
      if os.IsNotExist(err) {
        fmt.Println("文件或文件夹不存在")
      }else{
        fmt.Println("存在")
      }
    }
    
    
    • 判断是否是文件夹IsDir()
    file, _ := os.Stat("test/123/")
    res := file.IsDir()
    
    
    • 获取绝对路径
    	abs, err := filepath.Abs("test/123/")
    	// 获得的结果就是绝对路径+test/123/
    
    // 获得绝对路径
    	abs, err := filepath.Abs(".")
    	if err == nil {
        // 获得上层路径
    		fmt.Println(filepath.Dir(abs))
    	}
    
    • 遍历目录下的文件
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"path/filepath"
    )
    
    func main() {
    	abs, _ := filepath.Abs(".")
    	dir, err := ioutil.ReadDir(abs)
    	fmt.Println(dir)
    	if err == nil {
    		for _, value :=range dir{
    			if !value.IsDir(){
    				fmt.Println(value.Name())
    			}
    		}
    
    	}
    }
    
    
    • walk
    // walk会深层递归的查询数据
    filepath.Walk(abs, func(path string, info os.FileInfo, err error) error {
    		fmt.Println(info.Name())
      	fmt.Println(path)
    		return nil
    	})
    
    
    
    • 路径拼接 or 文件扩展名
    
    // 拼接
    filePath := path.Join("1","2","3","7.dmg")
    
    // 文件扩展名
    ext := path.Ext("/fres/faf/xxx.txt")
    获取到的就是扩展名txt
    
  • 相关阅读:
    Your branch and 'origin/master' have diverged, and have # and # different commits each, respectively
    testng dataprovider 的几种用法以及Java中的二维数组
    python 类属性 实例属性 类方法 实例方法 静态方法(转载)
    Webdriver中PageFactory的正确用法
    Selenium webdriver在最开始打开的时候浏览器的地址栏会出现data的解决方法
    Selenium webdriver如何处理confirm对话框的问题
    SoapUI 引用第三方jar包和引用Groovy脚本
    git rebase -i 合并commit
    Git 撤销commit的注意事项
    单进程执行
  • 原文地址:https://www.cnblogs.com/zjaiccn/p/14667125.html
Copyright © 2011-2022 走看看