一天速转golang!

news/2024/11/20 21:15:55/

首先你需要会一门后端语言,以及要有基本的网络编程能力和并发思想

环境

本人在linux环境下下载的go1.23.3版本,使用Vim+go作为IDE

具体做法此处不再赘述,自行查阅其他博客

第一个Golang程序

  • package main ——定义声明包名(可以类比java来理解go)

​ 必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。

  • import "fmt" ——引入包

告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。(本例中的println函数)

四种变量的声明方式

省去var的方式只用于局部变量!

//声明全局变量 方法一二三都可以 方法四不可以!
func main(){//方法一:声明一个变量,用var int的话,不给他初始值,默认为0var a intfmt.Println("a = ", a) //a = 0//方法二:声明一个变量,给他一个初始值var b int = 100fmt.Println("b = ", b) //b = 100//方法三:初始化的时候,可以省去数据类型,通过值自动匹配当前变量的数据类型var c = 100fmt.Println("c = ", c) //c = 100fmt.Println("type of c = %T\n", c) //打印c的类型//方法四:(最常用)省去var关键字,直接自动匹配e := 100fmt.Println("e = ", e) //e = 100fmt.Println("type of e = %T\n", e) //打印c的类型
}
//声明多个变量
var xx, yy int = 100,200
var kk, ll = 100, "ABC"
var{vv int = 100jj bool = true
}

const与iota的注意事项

iota是逐行累加,一行再多变量,他们的iota也是一样的

iota的使用非常局限,只能配合const出现

//const来定义枚举变量
const{//可以在const()添加关键字iota,每行的iota都会累加1,第一行的iota默认0BEIJING = iotaSHANGHAIHAERBING
}
const{a, b = iota+1, iota+2 // iota = 0, a = iota+1, b = iota+2, a = 1, b = 2c, d                  // iota = 1, c = iota+1, d = iota+2, c = 2, d = 3e, f                  // iota = 2, e = iota+1, f = iota+2, e = 3, f = 4g, h = iota*2, iota*3 // iota = 3, e = iota*2, f = iota*3, e = 6, f = 9i, k                  // iota = 4, e = iota*2, f = iota*3, e = 8, f = 12
}
func main(){//const是常量,只读属性,不可修改const length int = 10fmt.Println("SHANGHAI = ", SHANGHAI) // 1fmt.Println("HAERBING = ", HAERBING) // 2
}

多返回值的三种写法

//返回一个返回值
func foo1(a string, b int) int {
...c := 100return c
}
//返回多个返回值,匿名
func foo2(a string, b int) (int, int){
...return 666,777
}
//返回多个返回值,有形参名
func foo3(a string, b int) (r1 int, r2 int){
...r1 = 1000r2 = 2000return
}
//返回多个返回值,有形参名,r1, r2 int也行,逆天语法
func foo4(a string, b int) (r1, r2 int){
...r1 = 1000r2 = 2000return
}

import相关

main,import以及init的流程:

import之后不会继续执行,而是解析包

现在自定义两个包,lib1.go和lib2.go,路径如下

//lib1.go
package lib1
import "fmt"
func Lib1Test(){fmt.Println("lib1Test()...")
}
func main(){fmt.Prinln("lib1.init()...")
}//lib2.go
package lib2
import "fmt"
func Lib21Test(){fmt.Println("lib2Test()...")
}
func main(){fmt.Prinln("lib2.init()...")
}//main.go
package mainimport("GolangStudy/5-init/lib1""GolangStudy/5-init/lib2"//必须写路径,不然找不到
)
func main(){lib1.Lib1Test() //go声明必须使用,这里每一行都不能少lib2.Lib2Test()
}

此外,import还有匿名导包和给包取别名的方式:

匿名导包用在,咱只需要这个包的某一个函数,但不想import这个包

_"GolangStudy/5-init/lib1"

加个下划线就行


给包起别名,比如包太长了,不愿意总写

mylib2 "GolangStudy/5-init/lib2"

前面加个mylib2即可!接下来调用这个包的函数就是->mylib2.Lib2Test()

或者:

. "GolangStudy/5-init/lib2"

用.去省略,接下来调用这个包的函数就是->Lib2Test()

.不要轻易使用!

defer的执行顺序

defer是在结束的时候执行,并且defer是栈的形式,第一个defer先入栈,后一个再入栈,那么后一个就是先出栈的

func main(){//写入defer关键字defer fmt.Println("main end1")defer fmt.Println("main end2")fmt.Println("main::hello go 1")fmt.Println("main::hello go 2")
}
/*
main::hello go 1
main::hello go 2
main end2
main end1
*/

defer和return是谁先谁后?结果是return快于defer,return会先被调用,defer是程序所有函数生命周期结束才被调用的!

切片slice相关

slice是动态数组的类型

定义遍历和打印:

//变长数组
func printArray(myArray []int){for _, value := range myArray{fmt.Println("value = ", value)}
}
func main(){myArray := []int{1,2,3,4}fmt.Print("myArray type is %T\n", myArray)
}

slice的4种声明:

func main(){//声明slice1是一个切片,并且初始化,默认值是1,2,3,长度是3slice1 := []int{1,2,3}//声明slice1是一个切片,但是并没有给slice1分配空间,也就是完全没有容量var slice1 []intslice1 = make([]int, 2)//先给slice1开辟空间//声明slice是一个切片,同时给slice1分配空间,3个空间,初始是0var slice1 []int = make([]int, 3)//通过:=推导出slice1是一个切片slice1 := make([]int, 3)//判断slice是否为0if slice == nil{fmt.Println("slice1是一个空切片")}else{fmt.Println("slice1是有空间的")}
}

切片容量的追加:

func main(){var numbers = make([]int, 3, 5)fmt.Println("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers))//cap求容量,打印结果:len = 3,cap = 5,slice = [0 0 0]

通过append追加值

//向numbers切片追加一个元素1,, numbers len = 4, [0 0 0 1], cap = 5
numbers = append(numbers, 1)

cap满了之后再append会怎样?底层会再次开辟一个cap空间

切片截取:

numbers := []int{0,1,2,3,4,5,6,7,8}
//切片截取
fmt.Println("numbers[1:4] ==", numbers[1:4]) // 打印子切片从索引1(包含)到索引4(不包含)
fmt.Println("numbers[:3] ==", numbers[:3]) // 默认下限为0
fmt.Println("numbers[4:] ==", numbers[4:]) //默认上限为len(s)

map相关

map的3种声明方式

//声明方式一
var myMap1 map[string]string
//使用map前,给他开辟空间
make(map[string]string, 10)
myMap2["one"] = "java"
myMap2["two"] = "c++"
myMap2["three"] = "python"//声明方式二
myMap2 := make(map[int]string)
myMap2[1] = "java"
myMap2[2] = "c++"
myMap2[3] = "python"//声明方式三
myMap3 := map[string]string{"one": "php","two": "c++","three": "python",
}

map的使用:

cityMap := make(map[string]string)
//添加
cityMap["China"] = "Beijing"
cityMap["Japan"] = "Tokyo"
cityMap["USA"] = "newYork"//遍历
for key, value := range cityMap{fmt.Println("key = ", key)fmt.Println("value = ", value)
}//删除
delete(cityMap, "China")//修改
cityMap["USA"] = "DC"

golang的面向对象

封装

type Hero struct{Name  stringAd    intLevel int
}
func (this *Hero) show(){fmt.Println("Name = ", this.Name)fmt.Println("Ad = ", this.Ad)fmt.Println("Level = ", this.level)
}
func (this *Hero) GetName() string{return this.Name
}
func (this *Hero) SetName(newName string){//this只是调用该方法的对象的一个副本this.Name = newName
}

继承

//父类
type Human struct{name stringsex  string
}
func (this *Human) Eat(){fmt.Println("Human.Eat()...")
}
func (this *Human) Walk(){fmt.Println("Human.Walk()...")
}//子类
type SuperMan struct{Human //SuperMan类继承了Human类的方法level int
}
//重新定义父类的方法Eat
func (this *SuperMan) Eat(){fmt.Println("SuperMan.Eat()...")
}
//子类新方法
func (this *SuperMan) Fly(){fmt.Println("SuperMan.Fly()...")
}
func (this *SuperMan) Print(){fmt.Println("name = ", this.name)fmt.Println("sex = ", this.sex)fmt.Println("level = ", this.level)
}
//定义一个子类对象
//s := SperMan{Human{"li4", "female"}, 88}
var s SuperMan
s.name = "li4"
s.sex = "male"
s.level = 88

多态

  • 多态(Polymorphism)是面向对象编程(OOP)中的一个重要概念。它指的是同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。简单来说,就是用同一种方式去调用不同类中的同名方法,这些方法在不同类中有不同的实现,从而展现出不同的行为。
  • 实现多态的条件
    • 要有继承关系。例如,有一个基类(父类)和一个或多个派生类(子类),子类继承父类的属性和方法。
    • 要有方法重写。子类重写(override)父类中的方法,即在子类中重新定义了与父类中同名、同参数列表(参数的类型、个数和顺序相同)的方法,并且方法体的内容不同。
package mainimport "fmt"// 定义一个接口,它有一个方法Sound
type Animal interface {Sound() string
}// 定义狗结构体
type Dog struct{}// 狗结构体实现Animal接口的Sound方法
func (d Dog) Sound() string {return "汪汪汪"
}// 定义猫结构体
type Cat struct{}// 猫结构体实现Animal接口的Sound方法
func (c Cat) Sound() string {return "喵喵喵"
}// 定义一个函数,它接受实现了Animal接口的对象,并调用其Sound方法
func MakeSound(a Animal) {fmt.Println(a.Sound())
}func main() {dog := Dog{}cat := Cat{}MakeSound(dog)MakeSound(cat)
}

interface{}是空接口,int、string、float32、float64、struct...都实现了interface{}。可以用interface{}类型,引用任意数据类型

func myFunc(arg interface{}){fmt.Println("myFunc is called...")fmt.Println(arg)
}
type Book struct{auth string
}
func main(){book := Book{"Golang"}myFunc(book)myFunc(3.14)myFunc("abc")
}
/*
打印输出:
Golang
3.14
abc
*/

那么interface{}是如何区分 此时引用的底层数据是什么类型?

->给interface{}提供"类型断言"机制

func myFunc(arg interface{}){fmt.Println("myFunc is called...")fmt.Println(arg)//给 interface{} 提供"类型断言"value, ok := arg.(string)if !ok {fmt.Println("arg is not string type")}else{fmt.Println("arg is string type, value = ", value)}
}

在代码 value, ok := arg.(string) 中,这是一个类型断言表达式,它尝试将接口类型 arg 断言为 string 类型,并且会返回两个值:

  • value

    • 如果接口 arg 确实是 string 类型,那么 value 就会被赋值为接口所包含的 string 值。例如,如果 arg 是通过传入一个字符串 "hello" 得到的接口值,那么当类型断言成功时,value 就会被赋值为 "hello"
    • 如果类型断言失败,value 的值是对应类型的零值。对于 string 类型来说,零值就是 ""(空字符串)。
  • ok

    • ok 是一个布尔值,它用于指示类型断言是否成功。
    • 如果 arg 确实是 string 类型,那么 ok 会被赋值为 true,表示类型断言成功,此时你可以放心地使用 value 变量,因为它已经正确获取到了接口中所包含的 string 值。
    • 如果 arg 不是 string 类型,那么 ok 会被赋值为 false,表示类型断言失败,此时 value 虽然也有值(对应类型的零值),但它并不是我们所期望的接口中原本的正确值,所以需要通过检查 ok 的值来判断是否可以正确使用 value 变量。

 反射

变量结构如下:

OpenFile返回了一个指针类型和一个error

//tty: pair<type:*os.File, value:"/dev/tty">
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

reflect包:

func ValueOf(i interface{}) Value {...}
// ValueOf用来获取输入参数接口中的数据的值,如果接口为空则返回0
func TypeOf(i interface{}) Type {...}
//TypeOf用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil

具体来说

package main
import ("fmt""reflect"
)
func reflectNum(arg interface{}){fmt.Println("type: ", reflect.TypeOf(arg)) //float64fmt.Println("value: ", reflect.ValueOf(arg)) //1.234
}
func main(){var num float64 = 1.234reflectNum(num)
}

结构体标签

package main
type resume struct{Name string    'info:"name" doc:"mine"'Sex  string    'info:"sex"'
}
func findTag(str interface{}){t := reflect.TypeOf(str).Elem()for i := 0; i < t.NumField(); i ++{taginfo := t.Field(i).Tag.Get("info")tagdoc := t.Field(i).Tag.Get("doc")fmt.Println("info:", taginfo, "doc:", tagdoc)}
}

结构体的标签引用在json编解码或者orm映射关系

goroutine

  • Goroutine 是 Go 语言特有的轻量级线程,它可以让一个函数在后台并发地执行。与传统的操作系统线程相比,Goroutine 非常轻量,一个 Go 程序可以轻松地创建成千上万个 Goroutine。
  • 例如,在一个 Web 服务器中,可以为每个传入的请求创建一个 Goroutine 来处理,这样就能够高效地处理多个请求,而不会因为创建大量线程导致系统资源耗尽。
  • 创建 Goroutine 非常简单,只需要在函数调用前加上go关键字。
go func() {defer fmt.Println("A.defer")//匿名函数func() {defer fmt.Println("B.defer")//退出当前goroutineruntime.Goexit()fmt.Println("8")}()for{time.Sleep(1*time.Second)}
}
/*输出
B
B.defer
A
A.defer
*/

channel

  1. 概念

    • 在 Go 语言中,channel是一种用于在不同的goroutine之间进行通信和同步的机制。它可以被看作是一个管道,数据可以通过这个管道在goroutine之间传递。
  2. 基本操作

    • 创建 channel:可以使用make函数来创建channelchannel有两种类型:无缓冲的channel和有缓冲的channel
      • 无缓冲的 channel
        • 示例:ch := make(chan int)。这创建了一个可以传输int类型数据的无缓冲channel。无缓冲channel在发送和接收数据时必须有对应的接收者和发送者同时准备好,否则会导致goroutine阻塞。
      • 有缓冲的 channel
        • 示例:ch := make(chan int, 3)这创建了一个可以传输int类型数据的有缓冲channel,缓冲大小为 3。有缓冲channel允许在没有接收者的情况下,发送者可以发送一定数量(缓冲大小)的数据,这些数据会存储在channel的缓冲区中,直到被接收。
    • 发送和接收数据
      • 发送数据:使用ch <- value的语法,其中chchannelvalue是要发送的数据。例如,ch := make(chan int); go func() { ch <- 5 }(),这里在一个goroutine中向channel发送了数字 5。
      • 接收数据:使用value := <-ch的语法来接收channel中的数据。例如,ch := make(chan int); go func() { value := <-ch; fmt.Println(value) }(),这个goroutine会从channel中接收数据并打印出来。
  3. 同步作用

    • channel除了用于数据传输,还具有同步goroutine的作用。
    • 例如,当一个goroutine向无缓冲channel发送数据时,如果没有其他goroutine来接收这个数据,发送数据的goroutine会阻塞,直到有接收者出现。同样,当一个goroutine试图从一个空的channel接收数据时,它也会阻塞,直到有数据被发送到这个channel
    • 这种阻塞机制可以确保goroutine之间的协调和同步。比如在一个并发计算任务中,一个goroutine完成部分计算后,通过channel发送结果给另一个goroutine,第二个goroutine在收到数据前会一直等待,这样就保证了数据的正确传递和任务的有序进行。
  1. 关闭 channel

    • 可以使用close(ch)来关闭channel。关闭channel后,再试图向其发送数据会引发panic,但仍然可以从已经关闭的channel中接收数据。
    • 接收已关闭channel的数据时,会正常接收缓冲区中剩余的数据,当缓冲区为空时,会得到对应类型的零值,并且可以通过一个额外的返回值来判断channel是否已经关闭。例如,value, ok := <-ch,如果okfalse,则表示channel已经关闭。
    • 关闭channel通常用于表示数据发送已经完成,让接收者知道不会再有新的数据发送过来了。例如,在一个生产者 - 消费者模型中,当生产者完成所有数据的生产后,可以关闭channel,消费者在接收完剩余数据后,通过检查channel是否关闭来结束接收操作。
func main() {c := make(chan int)go func(){defer fmt.Println("goroutine结束")fmt.Println("goroutine 正在运行...")c <- 666 //将666发送给c}()num := <-c //从c中接受数据,并赋值给numfmt.Println("num = ", num)fmt.Println("main goroutine 结束...")
}
/* 输出
goroutine 正在运行...
goroutine结束
num = 666
main goroutine 结束...
*/

 


http://www.ppmy.cn/news/1548578.html

相关文章

5.4.2-1 编写Java程序在HDFS上创建文件

本次实战涉及使用Java操作Hadoop HDFS&#xff0c;包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务&#xff0c;成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践&#xff0c;如检查文件存在性以避免重复创建&#xff0c;以及处理HDFS安全…

DAY120java审计第三方组件依赖库挖掘FastjsonShiroLog4jH2DB

组件漏洞判断插件 一、Tmall_demo-master&#xff08;fastjson&#xff09; 1、配置文件查找安装组件 1、JSON.parse(json) 2、JSON.parseObject 2、找可控的变量 3、利用组件漏洞 poc:propertyJson{"type":"java.net.Inet4Address","val":&q…

vue + axios config url 转码 空格转成 +(前端解决)

encodeURI 对一个完整的URI 进行编码&#xff0c;而encodeURIComponent对URI 的一个组件&#xff08;单个参数&#xff09;进行编码。 // 浏览器get请求 service.interceptors.request.use(config > { let url config.urlif (config.method get && config.params…

15分钟学 Go 实战项目六 :统计分析工具项目(30000字完整例子)

统计分析工具项目 1. 项目概述 功能模块说明难度数据收集CSV文件读取和解析★★☆☆☆数据分析基本统计和高级分析★★★☆☆可视化生成图表和报告★★★★☆导出功能支持多种格式导出★★☆☆☆Web界面交互式数据分析★★★★☆ 2. 项目架构 3. 核心代码实现 3.1 数据模型…

AI赋能电商:开启智慧零售新纪元

根据麦肯锡最新研究报告显示&#xff0c;到2025年&#xff0c;AI技术将为全球零售业带来4000-8000亿美元的价值增长。在中国&#xff0c;已有超过60%的电商平台开始应用AI技术优化运营流程。人工智能正在重塑电商行业的经营法则&#xff0c;带来前所未有的机遇与挑战。 一、智…

【FMC169】基于VITA57.1标准的4发4收射频子模块(基于ADRV9026)

产品概述 FMC169 是一款基于VITA57.1 标准规范&#xff0c;实现4 收4发的射频子模块&#xff0c;该板卡基于ADI的捷变收发器ADRV9026作为处理核心&#xff0c;射频工作范围为75MHz~6GHz频段&#xff0c;发射最大信号带宽450MHz&#xff0c;接收最大带宽200MHz&#xff0c;提供…

【MYSQL】什么是关系型数据库与非关系型数据库?

真正的让你快速理解什么是关系型数据库与非关系型数据库~ 主要是以查询语句&#xff0c;存储结构&#xff0c;拓展 性上的区别。 关系型数据库&#xff08;最经典就是mysql&#xff0c;oracle&#xff09;&#xff1a;它是支持SQL语言&#xff0c;并且关系型数据库大部分都支持…

时代变迁对传统机器人等方向课程的巨大撕裂

2020年之后&#xff0c;全面转型新质课程规划&#xff0c;传统课程规划全部转为经验。 农耕-代表性生产关系-封建分配制度主要生产力-人力工业-代表性生产关系-资本分配制度工业分为机械时代&#xff0c;电气时代&#xff0c;信息时代&#xff1b;主要生产力-人力转为人脑&…