Golang基础知识(笔记迁移)

golang

变量作用域

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

类型断言

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

func main(){
    var a inteface{}
    b := 1
    b = a
    c,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 := 5
    switch a{
    	case 1,2,3,4:
        	println("ok1,2,3")
    	case 5:
        	println("ok5")
        default:
        	println("default")
    }
}

可以将switch当作if else分支

func main(){
    a := 5
    switch {
        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)  //10
    defer println(b)  //20
    a++  //11
    b++  //21
    res := a + b
    return 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) // *int
    fmt.Printf("%T\n", b) // *bool
    fmt.Println(*a)       // 0
    fmt.Println(*b)       // false
}

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

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

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

总结

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

结构体

结构体相当于java、js的class

type userInfo struct{
    name string
    age int
    hobby []
}

定义结构体方法

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.person
    fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}

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

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

type person struct {
    name string
    city string
    age  int8
}

func main() {
    var p1 person
    p1.name = "pprof.cn"
    p1.city = "北京"
    p1.age = 18
    fmt.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 string
    city string
    age  int8
}

func main() {
    var p4 person
    fmt.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序列化该字段时的key
    Gender string //json序列化是默认使用字段名作为key
    name   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 string
    Age 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 UTC
    t2 := 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 []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创建切片  创建了一个长度为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 main

import (
    "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 int64
	lock sync.Mutex
)
func addWithLock(){
	for i := 0; i < 2000; i++ {
		lock.Lock()// 临界区资源上锁
		x += 1
		lock.Unlock()//释放临界区资源
	}
}
func addWithoutLock(){
	for i := 0; i < 2000; i++ {
		x += 1
	}
}
func Add(){
	x = 0
	for i := 0; i <5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("addWithLock",x)
	x = 0
	for 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.WaitGroup
	wg.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.mfbz.cn/a/481029.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java面试必问题16:HashMap和HashTable区别 ConcurrentHashMap和HashMap的区别

HashMap和HashTable区别 ConcurrentHashMap和HashMap是Java中常用的两种Map实现&#xff0c;它们之间有以下几个区别&#xff1a; 线程安全性&#xff1a;ConcurrentHashMap是线程安全的&#xff0c;多个线程可以同时对其进行读写操作而不需要额外的同步措施&#xff1b;而Has…

ARM32day4

VID_20240319_210515 1.思维导图 2.实现三个LED灯亮灭 .text .global _start _start: 使能GPIO外设时钟 LDR R0,0x50000A28 LDR R1,[R0]使能GPIOE ORR R1,R1,#(0X1<<4)使能GPIOF ORR R1,R1,#(0X1<<5) STR R1,[R0]设置引脚状态 LDR R0,0X50006000 LDR R1,[R0…

Linux-生产者与消费者模型

文章目录 一、什么是生产者与消费者模型&#xff1f;二、示例模型示例模型介绍交易场所&#xff08;blockQueue&#xff09;消费者与生产者运行结果 总结 一、什么是生产者与消费者模型&#xff1f; 参照日常生活中&#xff0c;购买商品的人群可以被称之为消费者&#xff0c;生…

如果搭建axb回拨

AXB回拨技术是一种先进的电话通讯技术&#xff0c;它通过在A&#xff08;主叫方&#xff09;与B&#xff08;被叫方&#xff09;之间引入一个中间号码X&#xff0c;实现了双方的通话连接。这种技术可以有效避免直接拨打被叫方的电话号码&#xff0c;从而保护了用户的隐私。 具体…

《优化接口设计的思路》系列:第九篇—用好缓存,让你的接口速度飞起来

一、前言 大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 作为一名从业已达六年的老码农&#xff0c…

学习笔记Day14:Linux下软件安装

软件安装 Anaconda 所有语言的包(package)、依赖(dependency)和环境(environment)管理器&#xff0c;类似应用商店 Conda < Miniconda < Anaconda&#xff08;有交互界面&#xff09; Linux下Miniconda即可 安装Miniconda 搜索北外/清华miniconda镜像网站&#xff…

使用专属浏览器在国内直连GPT教程

Wildcard官方推特发文说他们最近推出了一款专门为访问OpenAI设计的浏览器。 根据官方消息&#xff0c;这是一款专门为访问OpenAI优选网络设计的浏览器&#xff0c;它通过为用户提供专用的家庭网络出口&#xff0c;确保了快速、稳定的连接。 用这个浏览器的最大好处就是直接用浏…

【前端寻宝之路】学习和总结HTML的标签属性

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-tgsZb9zTBxJHHYhD {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

蓝桥杯 2022 省B 李白打酒加强版

这题用递归暴力的方法如下&#xff1a; #include<iostream> #include<bits/stdc.h> using namespace std; int num; int N,M; void dfs(int now,int n,int m) {if(now<0 || n>N ||m>M)return ;if(nN && mM){if(now1)num1;return;}dfs(now-1,n,m1…

(一)、Doris安装使用(基于Doris 2.0.6)

第 1 章Doris简介 1.1、 Doris 概述 ​ Apache Doris由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018年贡献到 Apache 社区后&#xff0c;更名为 Doris&#xff09;&#xff0c;在百度内部&#xff0c;有超过200个产品线在使用&#xff0c;部署机器超过1000台…

【力扣白嫖日记】613.直线上的最近距离

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 613.直线上的最近距离 表&#xff1a;Point 列名类型xint 在SQL中&#xff0c;x是该表的主键列。该表的每一…

Redis 不再“开源”,对中国的影响及应对方案

Redis 不再“开源”&#xff0c;使用双许可证 3 月 20 号&#xff0c;Redis 的 CEO Rowan Trollope 在官网上宣布了《Redis 采用双源许可证》的消息。他表示&#xff0c;今后 Redis 的所有新版本都将使用开源代码可用的许可证&#xff0c;不再使用 BSD 协议&#xff0c;而是采用…

linux sh脚本编写

linux中bash Shell 是 Linux 的核心部分&#xff0c;它允许你使用各种诸如 cd、ls、cat 等的命令与 Linux 内核进行交互。Bash脚本和Shell脚本实际上是指同一种类型的脚本&#xff0c;只不过Bash是其中最常用的一种Shell。除了Bash之外&#xff0c;常见的Shell解释器还有C She…

【Django框架学习笔记】超详细的Python后端开发Django框架学习笔记

十二&#xff0c;Django框架 可以以下链接获取Django框架学习笔记,md文档和pdf文档 Django框架超详细的学习笔记&#xff0c;点击我获取 12.1 命令行操作 # 创建django项目 django-admin startproject aini# 启动项目 cd /mysite python3 manage.py runserver## 创建应用 …

BUUCTF---WEEK3(Rabin‘s RSA)

题目&#xff1a; from Crypto.Util.number import * from secret import flag p getPrime(64) q getPrime(64) assert p % 4 3 assert q % 4 3n p * qe 2 m bytes_to_long(flag)c pow(m,e,n)print(n , n) print(c , c)# n 201354090531918389422241515534761536573 …

MySQL面试题--事务

目录 1.什么是数据库事务&#xff1f;事务的特性是什么&#xff1f; 2.什么是ACID&#xff1f; 3.并发事务会有哪些问题&#xff1f; 4.什么是 脏读、丢失修改、不可重复读、幻读 5.不可重复读和幻读有什么区别&#xff1f; 6.Mysql是如何避免事务并发问题的&#xff1f; …

加一——大数据的应用

题目链接&#xff1a;66. 加一 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 先将输入的数组转换成整数&#xff0c;对整数进行加一操作&#xff0c;然后再转换回数组&#xff0c;这样就不用考虑加一进位和数位增加的问题&#xff0c;很简单的思路但是运行时间…

操作简单的城市内涝一维二维耦合模拟软件

原文链接&#xff1a;最简单的城市内涝一维二维耦合模拟软件https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247598401&idx3&sn0c4c86b3a5d09a75b8f07e6fad81aa9c&chksmfa8200a6cdf589b0970a6854869e8e3a9f132fe40a19977863c091cbcf6d9786f067e0c5651e&…

UE5 GameMode C++函数 学习

已经尝试&#xff0c;确实能重启游戏 类描述符加了noplaceable过后即使是Actor也不能放到场景中了&#xff0c;关卡蓝图&#xff0c;GameMode&#xff0c;GameState这些就不能放场景中了 UFUNCTION(exec)

【Python + Django】表结构创建

以员工管理系统为例。 事前呢&#xff0c;我们先把项目和app创建出来&#xff0c;详细步骤可以看我同栏目的第一篇、第二篇文章。 我知道你们是不会下来找的&#xff0c;就把链接贴在下面吧&#xff1a; 【Python Django】启动简单的文本页面-CSDN博客 【Python Django】…
最新文章