Golang基础知识(笔记迁移)

golang

变量作用域

  • 局部作用域:代码块、函数内的
  • 全局作用域:顶层作用域,代码块外的就是全局,如果变量名大写,则改变量整个程序都可以使用。

类型断言

golang的类型断言在变量后加上.(type),如果类型断言与实际类型不一致会返回第二个参数bool代表是否成功

func main(){var a inteface{}b := 1b = ac,ok := b.(boolean)if ok {... succcess}// 简写if c,ok := b.(boolean); ok {... succcess}
}

错误处理

默认情况发生错误panic程序会退出。golang中捕获错误采用如下方法,defer+recover

func tes(){defer func(){err := recover()if err != nil{// }}()fmt.Println(a+b)
}

自定义错误

golang中自定义错误采用errors.Newpanic函数

func main(){}

流程控制的细节

switch

  • 穿透:golang的switch默认不会穿透,不需要像js一样在case内加上break,如果需要穿透加上fallthrough
  • case后面可有多个值
func main(){a := 5switch a{case 1,2,3,4:println("ok1,2,3")case 5:println("ok5")default:println("default")}
}

可以将switch当作if else分支

func main(){a := 5switch {case a == 5:println("a = 5 true")}
}

Type Switch

switch可以用来判断某个变量的实际类型

var mapData map[string]interface{}
func main(){for k,v := range mapData{switch v.(type){case nil://case int://case float64://case bool,string://default://}}
}

函数

函数传递两种方式

  • 值传递
  • 引用传递。

引用传递效率高,因为只拷贝地址。对于引用类型,指针、slice切片、map、管道、chan、interface都属于引用类型。 值类型 int、float、bool、string、数组和结构体

init函数

每一个源文件都可以包含一个init函数,该函数将会在main函数之前被调用

defer关键字

defer使用场景主要是函数执行完毕后释放函数执行期间创建的资源

当go执行到defer时,会将defer后的代码压入栈中,当函数执行完毕后会从栈中取出语句执行(语句中引用的值会做一个拷贝)。

func sum(a int,b int)int{// 将println(a)   println(b)语句入栈,此时a,b值10,20被拷贝,defer println(a)  //10defer println(b)  //20a++  //11b++  //21res := a + breturn res
}
sum(10,20)

打印操作

golang打印需要导入fmt包,如果需要详细打印信息需要符号%#,使用%v打印任何值,%.2f可以打印小数并展示到小数后两位

s := "hello"
f := 3.1415926
fmt.Printf("s=%v",s)
fmt.Printf("%.2f",f)

new和make的区别

new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。举个例子:

func main() {a := new(int)b := new(bool)fmt.Printf("%T\n", a) // *intfmt.Printf("%T\n", b) // *boolfmt.Println(*a)       // 0fmt.Println(*b)       // false
}

var a *int只是声明了一个指针变量a但是没有初始化,指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值。应该按照如下方式使用内置的new函数对a进行初始化之后就可以正常对其赋值了:

func main() {var a *inta = new(int)*a = 10fmt.Println(*a)
}

make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

总结

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

结构体

结构体相当于java、js的class

type userInfo struct{name stringage inthobby []
}

定义结构体方法

func (u *userInfo) resetPassword(password string){u.password = password
}
func (u userInfo) checkPassword(password string) bool {return u.password == password
}

结构体的实例化

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

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

只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。

结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。

type person struct {name stringcity stringage  int8
}func main() {var p1 personp1.name = "pprof.cn"p1.city = "北京"p1.age = 18fmt.Printf("p1=%v\n", p1)  //p1={pprof.cn 北京 18}fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"pprof.cn", 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 = "博客"
p3.age = 30
p3.city = "成都"
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"博客", city:"成都", age:30}

结构体的初始化

type person struct {name stringcity stringage  int8
}func main() {var p4 personfmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0}
}

使用键值对对结构体进行初始化时,键对应结构体的字段,值对应该字段的初始值。

p5 := person{name: "pprof.cn",city: "北京",age:  18,
}
fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"pprof.cn", city:"北京", age:18}

也可以对结构体指针进行键值对初始化,例如:

p6 := &person{name: "pprof.cn",city: "北京",age:  18,
}
fmt.Printf("p6=%#v\n", p6) //p6=&main.person{name:"pprof.cn", city:"北京", age:18}

当某些字段没有初始值的时候,该字段可以不写。此时,没有指定初始值的字段的值就是该字段类型的零值。

p7 := &person{city: "北京",
}
fmt.Printf("p7=%#v\n", p7) //p7=&main.person{name:"", city:"北京", age:0}

使用值的列表初始化

初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:

p8 := &person{"pprof.cn","北京",18,
}
fmt.Printf("p8=%#v\n", p8) //p8=&main.person{name:"pprof.cn", city:"北京", age:18}

使用这种格式初始化时,需要注意:

    1.必须初始化结构体的所有字段。2.初始值的填充顺序必须与字段在结构体中的声明顺序一致。3.该方式不能和键值初始化方式混用。

结构体标签(Tag)

Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。

Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

    `key1:"value1" key2:"value2"`

结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔。 注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。

例如我们为Student结构体的每个字段定义json序列化时使用的Tag:

//Student 学生
type Student struct {ID     int    `json:"id"` //通过指定tag实现json序列化该字段时的keyGender string //json序列化是默认使用字段名作为keyname   string //私有不能被json包访问
}func main() {s1 := Student{ID:     1,Gender: "女",name:   "pprof",}data, err := json.Marshal(s1)if err != nil {fmt.Println("json marshal failed!")return}fmt.Printf("json str:%s\n", data) //json str:{"id":1,"Gender":"女"}
}

JSON操作

JSON的键需要大写,如果想小写,则需要加上tag,例如下面的json:age

type userInfo struct{Name stringAge int `json:age`Hobby []string
}

序列化和反序列化对象

import ("encoding/json""fmt"
)a:= userinfo{Name:"Yu",Age:25,Hobby:[]string{"Golang","TypeScript"} }
buf,err := json.Marshal(a)// 序列化后编程buf数组(16进制编码) 可以通过string(buf)转为字符串
string(buf)
var b userInfo 
err = json.Unmarshal(buf,&b)
if err!= nil{panic(err)
}

日期操作

  • 两个时间相减 调用Sub方法
  • 格式化时间不同于其他语言传入YYMM,需要传入固定时间2006-01-02 15:04:05进行格式化
package main
import ("fmt""time"
)
func main(){now := time.Now()// 获取现在时间t := time.Date(2023,3,27,1,25,36,0,time.UTC) // 2023-03-27 01:25:36 +0000 UTCt2 := time.Date(2023.3.27,2,30,36,0,time.UTC)fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute())//2023, March 27 1 25 dif := t2.Sub(t)// 时间相减t.Format("2006-01-02 15:04:05") // 格式化时间now.Unix() // 当前时间戳
}

切片

创建/初始化切片

func main() {//1.声明切片var s1 []intif 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创建切片  创建了一个长度为2容量为5的切片
var slice []int = make([]int,2,5)// 初始化切片
全局:
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[start:end] 
var slice1 []int = arr[:end]        
var slice2 []int = arr[start:]        
var slice3 []int = arr[:] 
var slice4 = arr[:len(arr)-1]      //去掉切片的最后一个元素
局部:
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[start:end]
slice6 := arr[:end]        
slice7 := arr[start:]     
slice8 := arr[:]  
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
操作含义
s[n]切片s中索引位置为n的项
s[:]从切片s的索引0到len(s)-1
s[start:]从start到len(s)-1
s[:end]从0到end处
s[start:end]startend
s[start:end:max]startend cap=max-low len=hight-low
len(s)切片长度
cap(s)切片

切片内存布局

读写操作实际目标是底层数组,只需注意索引号的差别

在这里插入图片描述

关于cap

:::tip

超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。

:::

package mainimport ("fmt"
)func main() {data := [...]int{0, 1, 2, 3, 4, 10: 0}s := data[:2:3]s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。fmt.Println(s, data)         // 重新分配底层数组,与原数组无关。fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。
}
// [0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
// 0xc4200160f0 0xc420070060

Go并发编程

go并发编程中有三个核心

  • Goroutine 协程 调用函数的时候在函数前加上go关键字即可
  • Channel
  • Sync

1.1协程之间通信Channel

提倡通过**通信共享内存(通道)**而不是共享内存实现通信(存在多个协程同时操作一个数据的情况)

在这里插入图片描述

1.2通道-通信共享内存

通道分为两类

  • 有缓冲通道make(chan int,通道大小example:2)类似于快递格子,如果满了就没法放快递等待格子腾出来才行,典型的生产消费模型。
  • 无缓冲通道make(chan int),也被称为同步通道
func CalSquare(){src := make(chan int)dest := make(chan int,3)go func(){defer close(src)for i:=0;i<10;i++ {src <- i}}()go func(){defer close(dest)for i:=range src {dest <- i * i}}()for i:= range dest {println(i)}
}
//0 1 4 9 16 25 36 49 64 81

1.3通信-共享内存

var (x int64lock sync.Mutex
)
func addWithLock(){for i := 0; i < 2000; i++ {lock.Lock()// 临界区资源上锁x += 1lock.Unlock()//释放临界区资源}
}
func addWithoutLock(){for i := 0; i < 2000; i++ {x += 1}
}
func Add(){x = 0for i := 0; i <5; i++ {go addWithLock()}time.Sleep(time.Second)println("addWithLock",x)x = 0for i := 0; i <5; i++ {go addWithoutLock()}time.Sleep(time.Second)println("addWithoutLock",x)
}
addWithLock 10000
addWithoutLock 6550
WaitGroup

之前防止主进程结束导致协程还没运行完就接触,我们采用time.Sleep,正确方式来了
在这里插入图片描述

func helloRoutine(i int) {println("hello" + fmt.Sprint(i))
}
func ManyGoWait(){var wg sync.WaitGroupwg.Add(5)for i := 0; i < 5; i++{go func(j int){defer wg.Done()helloRoutine(j)}(i)}wg.Wait()
}
func main(){ManyGoWait()
}

依赖管理

Go依赖管理的演进GOPATH->GO Vendor -》Go Module

GOPATH和GO Vendor 都是采用源码副本方式管理依赖没有版本规则

依赖管理三要素:

  • 配置文件,描述依赖 go.mod
  • 中心仓库管理依赖库 Proxy
  • 本地工具 go get/go mod

GOPATH

$GOPATH工作区

  1. bin 项目编译的二进制文件
  2. pkg 项目编译的中间产物,加速编译
  3. src 项目源码

这种管理方式,项目代码直接依赖src目录下的代码,然后通过go get下载最新包到src目录下

这种管理方式的缺点

本地两个项目A,B ;A和B依赖某一个PKG的不同版本,没法构建成功,无法实现多版本控制

Go Vendor

为了解决GOPATH的问题,该方式在项目目录下增加vendor文件,所有依赖包副本形式放在项目根目录下/vendor(如果没有找到就去GOPATH),每个项目都引入一份依赖的副本就解决了依赖同包不同版本的问题

Go Vendor问题

如果A项目依赖pkg B和pkg C,而B和C同时依赖了pkg D的不同版本,我们无法控制其版本选择问题
在这里插入图片描述

Go Module

通过go.mod文件管理依赖包版本,通过go get/go mod指令管理依赖包

go.mod的配置 类似于前端package.json

在这里插入图片描述

go mod工具

go mod init #初始化 创建go.mod文件,项目开始前的必备操作
go mod download #下载模块到本地
go mod tidy #增加需要的依赖,删除不需要的  每次提交代码前可以执行

依赖分发Proxy

由于依赖一般放在github等代码托管平台,就会存在一些问题

  • 作者可能增删改软件的版本,无法保证构建稳定性
  • 无法保证依赖可用性,作者可以删除
  • 代码托管平台负载问题

proxy会缓存原站的内容,版本也不会变。

当存在proxy项目依赖会先从proxy上查找,当proxy代理的地址没有时,就去direct源站

go项目初始化

go mod init 名字
go mod tidy

安装gorm和gin

go get -u github.com/gin-gonic/gin
go get -u gorm.io/gen
go get -u gorm.io/driver/mysql

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/286474.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怿星科技Neptune CHT-S测试系统,让智能座舱测试更加高效便捷

随着汽车“智能化”浪潮的推进&#xff0c;汽车的智能化水平正在持续刷新行业认知。在这股智能化潮流中&#xff0c;智能座舱作为客户体验最为直观的部分&#xff0c;其重要性不言而喻。倘若座舱设备出现死机、黑屏、卡顿等现象&#xff0c;都将对客户的使用体验产生非常大的影…

xmes前端问题,给form表单赋值后,再次从表单拿不到该值

xmes前端&#xff0c;给form表单赋值后&#xff0c;再次从表单拿不到该值&#xff0c;但页面可以展示 赋值 this.$[frm-main].$$([namefilm_num]).value filmNum ; 获取表单的值&#xff0c;这里拿不到之前赋的值 const reqData this.$[frm-main].serializeMyForm(); 原因&…

2.7、创建列表(List)

概述 列表是一种复杂的容器&#xff0c;当列表项达到一定数量&#xff0c;内容超过屏幕大小时&#xff0c;可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集&#xff0c;例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求&#xff08;如通讯录、…

Copilot for Microsoft365使用体验

注&#xff1a;本文来自粉丝投稿。 上周进行了留言抽奖&#xff0c;粉丝获得了一周体验资格&#xff0c;并写下了使用体验&#xff0c;特此赠送1个月copilot使用资格。 留言赠送copilot for Microsoft365一周体验卡 每周一Copilot for Microsoft 365留言赠送 上周一通过陈老…

【CPP】智能指针

引言 智能指针是RAII思想的体现&#xff0c;有时候程序抛异常导致指针指向的内存资源未释放&#xff0c;造成内存泄漏&#xff0c;这时就需要用到智能指针&#xff0c;它可以出作用域自动调用析构函数释放内存资源 内存泄漏 什么是内存泄漏 什么是内存泄漏&#xff1a;内存泄…

基于GA优化的CNN-LSTM-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 长短时记忆网络&#xff08;LSTM&#xff09;处理序列依赖关系 4.3 注意力机制&#xff08;Attention&#xff09; 5…

9.串口通信

串口基本认识 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&#x…

【网络爬虫】(2) requests模块,案例:网络图片爬取,附Python代码

1. 基本原理 1.1 requests 模块 requests 是 Python 中一个非常流行的 HTTP 客户端库&#xff0c;用于发送所有的 HTTP 请求类型。它基于 urllib&#xff0c;但比 urllib 更易用。 中文文档地址&#xff1a;Requests: 让 HTTP 服务人类 — Requests 2.18.1 文档 &#xff0…

cookie、localStorage、sessionStorage 详解

目录 cookie 是什么&#xff1f; cookie 不可以跨域请求 cookie 的属性 会话cookie & 永久性cookie cookie 禁用 cookie 的应用 sessionStorage 是什么&#xff1f; 失效时间 存储内容的类型 存储的大小 存储的位置 sessionStorage 的应用 localStorage 是什么…

GTC 2024 火线评论:DPU 重构文件存储访问

编者按&#xff1a;英伟达2024 GTC 大会上周在美国加州召开&#xff0c;星辰天合 CTO 王豪迈在大会现场参与了 GPU 与存储相关的最新技术讨论&#xff0c;继上一篇《GTC 2024 火线评论&#xff1a;GPU 的高效存储利用》之后&#xff0c;这是他发回的第二篇评论文章。 上一篇文章…

【ZZULI数据结构实验一】多项式的三则运算

【ZZULI数据结构实验一】多项式的四则运算 ♋ 结构设计♋ 方法声明♋ 方法实现&#x1f407; 定义一个多项式类型并初始化---CreateDataList&#x1f407; 增加节点---Getnewnode&#x1f407; 打印多项式类型的数据-- PrintPoly&#x1f407; 单链表的尾插--Listpush_back&…

Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️本系列源码仓库&#xff1a;多线程并发编程学习的多个代码片段(github) &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正…

【C语言】结构体

个人主页点这里~ 结构体 一、结构体类型的声明1、结构的声明2、结构体变量的创建和初始化3、声明时的特殊情况4、自引用 二、结构体内存对齐1、对齐规则2、存在内存对齐的原因3、修改默认对齐数 三、结构体传参四、结构体实现位段 一、结构体类型的声明 我们在指针终篇中提到过…

从零自制docker-5-【USER Namespace NETWORK Namespace】

文章目录 USER Namespace代码NETWORK Namespace代码块 USER Namespace 即进程运行在一个新的namespace中&#xff0c;且该namespace中的User ID和Group IDA在该namespace内外可以不同&#xff0c;可以实现在namspace的用户是root但是对应到宿主机并不是root Cloneflags增加一…

3款免费甘特图制作工具的比较和选择指南

GanntProject GanttProject https://www.ganttproject.biz/ 是一款项目管理和调度应用&#xff0c;适用于 Windows、macOS 和 Linux。它易于使用&#xff0c;无需任何设置&#xff0c;适用于个人用户和小型团队。该应用提供任务层次结构和依存关系、里程碑、基准行、Gantt 图表…

AI论文速读 | 具有时间动态的路网语义增强表示学习

论文标题&#xff1a; Semantic-Enhanced Representation Learning for Road Networks with Temporal Dynamics 作者&#xff1a; Yile Chen&#xff08;陈亦乐&#xff09; ; Xiucheng Li&#xff08;李修成&#xff09;; Gao Cong&#xff08;丛高&#xff09; ; Zhifeng Ba…

卓健易控zj-v8.0设备智能控费系统

卓健易控zj-v8.0设备智能控费系统 详细可联系&#xff1a;19138173009 在现今医疗技术日新月异、突飞猛进的时代&#xff0c;我院服务患者的实力与日俱增。随着先进辅助检查设备的不断完善和引进&#xff0c;医生们如同得到了得力助手&#xff0c;能够为患者做出更加精确的诊断…

TCP重传机制详解——04FACK

文章目录 TCP重传机制详解——04FACK什么是FACKFACK的发展为什么要引入FACK实战抓包讲解开启FACK场景&#xff0c;且达到dup ACK门限值开启FACK场景&#xff0c;未达到dup ACK门限值 为什么要淘汰FACK总结REF TCP重传机制详解——04FACK 什么是FACK FACK的全称是forward ackn…

JVM(二)——垃圾回收

三、垃圾回收 1、如何判断对象可以回收 1&#xff09;引用计数法 定义&#xff1a; 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是…

Java面试篇:Redis使用场景问题(缓存穿透,缓存击穿,缓存雪崩,双写一致性,Redis持久化,数据过期策略,数据淘汰策略)

目录 1.缓存穿透解决方案一:缓存空数据解决方案二&#xff1a;布隆过滤器 2.缓存击穿解决方案一:互斥锁解决方案二:设置当前key逻辑过期 3.缓存雪崩1.给不同的Key的TTL添加随机值2.利用Redis集群提高服务的可用性3.给缓存业务添加降级限流策略4.给业务添加多级缓存 4.双写一致性…