热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Go基础笔记

Go语言基础
Go语言基础笔记

变量四种声明

var a int	// var a int = 0
var b int = 10
var c = 100
d := 1000 // 只能用在函数体内来声明

注意:全局变量只能用前三种方式,第四种方式只能用在函数体内来声明。

const

const 标识只读属性,可用于定义枚举类型。

// 可以在const() 添加一个关键字 iota,每行iota会累加1,第一行默认值为0
const (
    BEIJING = iota	// iota= 0
    SHANGHAI		// iota = 1
    GUANGZHOU		// iota = 2
    SHENZHEN		// iota = 3
)

函数返回值

// 无返回值
func foo0(a string, b int) {
	fmt.Println("-----foo0------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
}
// 一个返回值
func foo1(a string, b int) int {
	fmt.Println("-----foo1------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	c := 10
	return c
}
// 返回多个值,匿名
func foo2(a string, b int) (int, int) {
	fmt.Println("-----foo2------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	c := 10
	return c, 100
}
// 返回多个值,有形参名称
func foo3(a string, b int) (r1 int, r2 int) {
	fmt.Println("-----foo3------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	// r1 r2 属于形参,初始化值为0,作用域是foo3整个函数体 {} 空间
	fmt.Println("r1 = ", r1, ", r2 = ", r2)
	// 给有名返回值变量赋值
	r1 = 10
	r2 = 100
	return
}
// 返回值同类型
func foo4(a string, b int) (r1, r2 int) {
	fmt.Println("-----foo4------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	// 给有名返回值变量赋值
	r1 = 10
	r2 = 100
	return
}

init函数与import导包

注意:默认情况,导入包但是不使用会报错。

import (
    _ "mylib/lib1" // 匿名,不再报错,会执行 init()
	"mylib/lib2"
)
  • import导包方式
    • import _ "fmt" 给 fmt 包请一个别名,匿名,无法使用当前包的方法,但是会执行当前包内部的 init() 方法
    • impot aa "fmt" 给当前包起一个别名,aa,aa.Println() 形式来调用包的方法
    • import . "fmt" 将 fmt 包中的全部方法导入当前本包的作用中,fmt 的全部方法可以直接API调用,不需要 fmt.API (应避免使用)

指针

package main

import "fmt"

func changeValue1(val int)  {
	val = 10
}

func changeValue2(pval *int)  {
	*pval = 10
}

func main() {
	a := 1
	changeValue1(a)
	fmt.Println("changeValue1 a = ", a) // changeValue1 a =  1

	changeValue2(&a)
	fmt.Println("changeValue2 a = ", a) // changeValue2 a =  10
}

defer

当前函数生命周期结束执行。return 以后执行

数组和切片

数组

  • 声明方式

    var myArray1 [10]int
    myArray2 := [10]int{1, 2, 3, 4}	// 长度为10
    myArray3 := [4]int{1, 2, 3, 4}
    
  • 数组是固定长度的

  • 固定长度的数组在传参的时候,是严格匹配数组类型的

    func printArray(myArray [4]int)
    
  • 数组作为函数参数采用值传递

切片(动态数组)

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。

声明方式

// 1、声明切片,并初始化,长度为4
mySlice1 := []int{1, 2, 3, 4}
// 2、声明切片,并没有分配空间
var mySlice2 []int
mySlice2 = make([]int, 4) // 分配4个空间,初始化值为0
// 3、声明切片,分配空间,初始化为0
mySlice3 := make([]int, 4)

引用传递

动态数组在传参上是引用传递,而且不同元素长度的动态数组的形参是一致的

func printSlice(mySlice []int)

切片扩容

切片中添加元素,容量2倍增长

func main() {
    // 创建切片,len = 3, cap = 5
	var numbers = make([]int, 3, 5)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
	// 向切片追加一个元素
	numbers = append(numbers, 4)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

	// 向切片追加两个元素
	numbers = append(numbers, 5, 6)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
}

len = 3, cap = 5, slice = [0 0 0]
len = 4, cap = 5, slice = [0 0 0 4]
len = 6, cap = 10, slice = [0 0 0 4 5 6]

扩容源码$GOROOT/src/runtime/slice.go源码,其中扩容相关代码如下:

newcap := old.cap
doublecap := newcap + newcap
// 1、新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
if cap > doublecap {
    newcap = cap
} else {
    // 2、旧切片的长度小于1024,则最终容量 (newcap) 就是旧容量 (old.cap) 的两倍
    if old.cap <1024 {
        newcap = doublecap
    } else {
        // Check 0 
  1. 新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
  2. 否则 (cap <= 2 * old.cap) :
    1. 旧切片的长度小于1024,则最终容量 (newcap) 就是旧容量 (old.cap) 的两倍
    2. 旧切片长度大于等于1024:
      1. 最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,直到最终容量(newcap)大于等于新申请的容量
      2. 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)

切片截取

左闭右开区间 [startIndex:endIndex]

mySlice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println("origin\t", mySlice)
	fmt.Println("[:3]\t", mySlice[:3])		// [0, 3)
	fmt.Println("[1:4]\t", mySlice[1:4])	// [1, 4)
	fmt.Println("[2:]\t", mySlice[2:])		// [2, len(mySlice))
	fmt.Println("[:]\t\t", mySlice[:])		// [0, len(mySlice))

注意截取的切片底层是一致的,看起来就像浅拷贝一样

切片复制

copy(destSlice, srcSlice []T)
func main() {
	// copy()复制切片
	a := []int{1, 2, 3, 4, 5}
	b := make([]int, 5, 5)
	copy(b, a)     //使用copy()函数将切片a中的元素复制到切片b
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(b) //[1 2 3 4 5]
	b[0] = 1000
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(b) //[1000 2 3 4 5]
}

切片元素删除

要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

func main() {
	// 从切片中删除元素
	a := []int{30, 31, 32, 33, 34, 35, 36, 37}
	// 要删除索引为2的元素
	a = append(a[:2], a[3:]...)
	fmt.Println(a) //[30 31 33 34 35 36 37]
}

映射--map

map声明方式

  • 方式一:声明空map,使用前需要make分配数据空间

    //声明myMap1是一种map类型 key是string, value是string
    var myMap1 map[string]string
    if myMap1 == nil {
        fmt.Println("myMap1 是一个空map")
    }
    
    //在使用map前, 需要先用make给map分配数据空间
    myMap1 = make(map[string]string, 2)
    
    myMap1["one"] = "java"
    myMap1["two"] = "c++"
    myMap1["three"] = "python"
    
    fmt.Println(myMap1)
    
  • 方式二:声明并使用make

    myMap2 := make(map[int]string)
    
    myMap2[1] = "java"
    myMap2[2] = "c++"
    myMap2[3] = "python"
    
    fmt.Println(myMap2)
    
  • 方式三:不用make,直接创建并初始化

    myMap3 := map[string]string{
        "one":   "php",
        "two":   "c++",
        "three": "python",
    }
    fmt.Println(myMap3)
    

map使用

  • map 底层使用哈希,Go 采用的是数组 + 链地址法解决哈希冲突

  • 删除元素

    delete(myMap, key)
    
  • map做为形参,使用引用传递

OOP

struct

结构体作为参数,使用值传递

//声明一种行的数据类型 myint,是int的一个别名
type myint int

//定义一个结构体
type Book struct {
	title string
	auth  string
}

func changeBook(book Book) {
	//传递一个book的副本
	book.auth = "Jake"
}

func changeBook2(book *Book) {
	//指针传递
	book.auth = "Jake"
}

class

封装

  • 如果类名首字母大写,表示其他包也能够访问
  • 如果说类的属性首字母大写, 表示该属性是对外能够访问的,否则的话只能够类的内部访问
  • 方法首字母大写,其他包也能访问
//如果类名首字母大写,表示其他包也能够访问
type Hero struct {
	//如果说类的属性首字母大写, 表示该属性是对外能够访问的,否则的话只能够类的内部访问
	Name  string
	Ad    int
	level int
}

func (obj *Hero) GetName() string {
	return obj.Name
}

func (obj *Hero) SetName(newName string) {
	obj.Name = newName
}

继承

package main

import "fmt"

type Human struct {
	name string
	sex  string
}

func (obj *Human) Eat() {
	fmt.Println("Human.Eat()...")
}
func (obj *Human) Walk() {
	fmt.Println("Human.Walk()...")
}

//=================
type SuperMan struct {
	Human // SuperMan 类继承了 Human 类的方法
	level int
}

//重定义父类的方法Eat()
func (obj *SuperMan) Eat() {
	fmt.Println("SuperMan.Eat()...")
}
//子类的新方法
func (obj *SuperMan) Fly() {
	fmt.Println("SuperMan.Fly()...")
}

func main() {
	h := Human{"Jake", "male"}
	h.Eat()
	h.Walk()

	fmt.Println("----------------")
	//定义一个子类对象
	//s := SuperMan{Human{"Jake", "male"}, 666}
	var s SuperMan
	s.name = "Jake"
	s.sex = "male"
	s.level = 666

	s.Walk() //父类的方法
	s.Eat()  //子类的方法
	s.Fly()  //子类的方法

}

多态

通过接口实现,接口本质是一个指针

package main

import "fmt"

// 本质是一个指针
type AnimalIF interface {
	Sleep()
	GetColor() string // 获取动物的颜色
	GetType() string  // 获取动物的种类
}

// 具体的类
type Cat struct {
	color string // 猫的颜色
}
func (obj *Cat) Sleep() {
	fmt.Println("Cat is Sleep")
}
func (obj *Cat) GetColor() string {
	return obj.color
}
func (obj *Cat) GetType() string {
	return "Cat"
}

// 具体的类
type Dog struct {
	color string
}
func (obj *Dog) Sleep() {
	fmt.Println("Dog is Sleep")
}
func (obj *Dog) GetColor() string {
	return obj.color
}
func (obj *Dog) GetType() string {
	return "Dog"
}

func showAnimal(animal AnimalIF) {
	animal.Sleep() // 多态
	fmt.Printf("color = %s, kind = %s\n", animal.GetColor(), animal.GetType())
}

func main() {

	var animal AnimalIF // 接口的数据类型, 父类指针
	animal = &Cat{"Green"}
	animal.Sleep() // 调用的就是Cat的Sleep()方法, 多态的现象

	animal = &Dog{"Yellow"}
	animal.Sleep() // 调用Dog的Sleep方法,多态的现象

	cat := Cat{"Green"}
	dog := Dog{"Yellow"}

	showAnimal(&cat)
	showAnimal(&dog)
}

空接口 interface{} 是万能数据类型。interface{} 改如何区分此时引用的底层数据类型到底是什么?“类型断言” 的机制

package main

import "fmt"

// interface{} 是万能数据类型
func myFunc(arg interface{}) {
	// interface{} 改如何区分 此时引用的底层数据类型到底是什么?
	// 给 interface{} 提供 “类型断言” 的机制
	value, ok := arg.(string) // 判断是否是 string 类型
	if !ok {
		fmt.Printf("arg is not string type, type is %T\n", arg)
	} else {
		fmt.Println("arg is string type, value = ", value)
	}
}

func main() {
	myFunc(3.14)
	myFunc("abc")
}

反射

变量结构

var a string
// pair
a = "abcd"
// pair
var allType interface{}
//pair
allType = a

reflect包

reflect包文档:http://docscn.studygolang.com/pkg/reflect/

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
func (v Value) NumField() int
func (v Value) Field(i int) Value
func DoFiledAndMethod(input interface{}) {
	//获取input的type
	inputType := reflect.TypeOf(input)
	fmt.Println("inputType is :", inputType.Name())

	//获取input的value
	inputValue := reflect.ValueOf(input)
	fmt.Println("inputValue is:", inputValue)

	//通过type 获取里面的字段
	//1. 获取interface的reflect.Type,通过Type得到NumField ,进行遍历
	//2. 得到每个field,数据类型
	//3. 通过filed有一个Interface()方法等到 对应的value
	for i := 0; i 

结构体标签

type resume struct {
	Name string `info:"name" doc:"名字"`
	Sex  string `info:"sex"`
}

func findTag(str interface{}) {
	t := reflect.TypeOf(str).Elem()

	for i := 0; i 

info: name doc: 名字
info: sex doc:

结构体标签应用:

  1. json 编码
  2. orm 映射关系

Goroutine

  • Goroutine 就是协程
  • go func() 形式创建 Goroutine
  • main 退出所有协程都终止
  • runtime.Goexit() 退出当前 Goroutine
  • 无法通过返回获取结果

channel

  • channel 定义

    make(chan Type) // 等价于 make(chan Type, 0)
    make(chan Type, capacity)
    
  • channel 使用

    channel <- value	// 发送value到channel
    <-channel			// 接收并丢弃数据
    x := <-channel		// 从channel中接收数据,斌赋值给x
    x, ok := <-channel	// 从channel中接收数据,斌赋值给x,同时检查通道是否已关闭或者是否为空
    
  • 无缓冲channel

    用于读写的两个 Goroutine 都会阻塞等待对端操作(写Goroutine写入数据并等待读端读取,读Goroutine等待写端写入数据)

  • 有缓冲channel

    • 当channel已满,再向里面写数据,会阻塞
    • 当channel为空,从里面取数据,会阻塞
  • 关闭channel

    close(channel)
    
    • 关闭channel后,无法向channel再发送数据(向关闭后的channel发数据,会引发 panic 错误,导致接收立即返回零值)
    • 关闭channel后,可以继续从channel接收数据
    • 对于 nil channel,无论收发都会被阻塞
  • channel与range

    for {
        //ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
        if data, ok := <-c; ok {
            fmt.Println(data)
        } else {
            break
        }
    }
    // ========= 等价 ==============
    //可以使用range来迭代不断操作channel
    for data := range c {
        fmt.Println(data)
    }
    
  • channel与select

    单流程下,一个go同时只能监控一个channel的状态,select可以完成监控多个channel状态的任务

    select {
        case <- chan1:
        	// 如果chan1成功读到数据,则进行该处理流程
        case chan2 <- 1:
        	// 如果成功向chan2写入数据,则进行该处理流程
        default:
        	// 如果上面都没成功,则进行该处理流程
    }
    

Go modules

GOPATH弊端

  • 无版本控制概念
  • 无法同步一致第三方版本号
  • 无法指定当前项目引用的第三方版本号

go mod 命令

命令 功能
go mod init 生成 go.mod 文件
go mod download 下载 go.mod 文件中指明的所有依赖
go mod tidy 整理现有的依赖
go mod graph 查看现有的依赖结构
go mod edit 编辑 go.mod 文件
go mod vendor 导出项目所有的依赖到vendor目录
go mod verify 校验一个模块是否被篡改过
go mod why 查看为什么需要依赖某模块

go mod 环境变量

环境变量名 作用 补充
GO111MODULE 是否开启go modules模式 建议go V1.11之后,都设置为on
GOPROXY 1. 项目的第三方依赖库的下载源地址;
2. 建议设置国内的地址;
3. direct,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等)
阿里云:https://mirrors.aliyun.com/goproxy/
七牛云:https://goproxy.cn,direct
GOSUMDB 用来校验拉取的第三方库是否是完整的;默认也是国外的网站,如果设置了GOPROXY,这个就不用设置了
GONOPROXY 通过设置GOPRIVATE即可
GONOSUMDB 通过设置GOPRIVATE即可
GOPRIVATE 表示是私有仓库,不会进行GOPROXY下载和校验 go evn -w GOPRIVATE="*.example.com";表示所有模块路路径为example.com的子域名都不会进行GOPROXY下载和校验

Go Modules初始化项目

  1. 开启Go Modules模块

    GO111MODULE=on

  2. 初始化项目

    1. 任意文件夹创建一个项目(不要求在$GOPATH/src)

    2. 创建 go.mod 文件,同时起当前项目的模块名称

      go mod init github.com/xxx/module_test

      go.mod

      module github.com/xxx/module_test
      
      go 1.16
      
    3. 在该项目中编写源代码,源代码依赖某个库(假定为 github.com/gin-gonic/gin )

    4. 获取到依赖库,依赖库会下载到 $GOPATH/pkg/ 下

      1. 手动 down

        go get github.com/gin-gonic/gin

      2. 自动 down

      go.mod

      module github.com/xxx/module_test
      
      go 1.16
      
      require github.com/gin-gonic/gin v1.7.1 // indirect
      

      go.sum,罗列当前项目直接或间接的依赖所有模块版本,保证今后项目依赖的版本不会被篡改。

      • h1:hash :表示整体项目的zip文件打开之后的全部文件的校验和来生成的hash。如果不存在,可能表示依赖的库可能用不上。
      • xxx/go.mod h1:hash :表示go.mod文件做的hash
    5. 修改项目模块的版本依赖关系

      go mod edit -replace=被替换的版本=替换版本


推荐阅读
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
author-avatar
寡凫lo单鹄官方
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有