✅作者简介:CSDN内容合伙人、信息安全专业在校大学生🏆
🔥系列专栏 :
📃新人博主 :欢迎点赞收藏关注,会回访!
💬舞台再大,你不上台,永远是个观众。平台再好,你不参与,永远是局外人。能力再大,你不行动,只能看别人成功!没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷。
文章目录
环境配置
下载安装Go
- 无脑安装就行,地址:https://studygolang.com/dl
- 查看安装是否成功
- 设置环境变量
下面这个是存放默认项目文件和第三方库文件的地址? 随便新建一个目录就行(最好是的你的代码目录)
注意下面这个用弄两边:
然后在gopath
目录下新建三个空文件夹:
- 检验:
下载安装GoLand
https://www.jetbrains.com/go/download/download-thanks.html
无脑安装
测试
go run HelloWorld.go
package main
import "fmt"
func main(){
fmt.Println("Hello World!")
}
- Goland
基础语法
注释
// 我是单行注释
/*
我是多行注释
我是多行注释
*/
变量
- 定义风格
var name type
//定义一个字符串变量 name
var name String
//定义一个数字类型变量 int
var age int
// 批量定义
var (
name string
age int
addr string
)
- 短变量声明并初始化
// 这是Go语言的推导声明写法,编译器会自动根据右值的类型推断出左值的类型
// 因为简洁和灵活的特点,简短变量声明被广泛用于大部分的局部变量的声明和初始化
// 注意:由于使用了 := ,而不是赋值的 = ,因此推导声明写法的左值必须是没有被定义过的,若定义过,将会发生编译错误
name := "kuangshen"
age := 3
fmt.Printf("name:%s,age:%d",name,age)
- 打印变量类型和内存地址
fmt.Printf("%T,%T", name, age) // string,int
var num int = 5
fmt.Printf("num:%d的内存地址为:%p\n", num, &num) //num:5的内存地址为:0xc0000a6058
num = 100
fmt.Printf("num:%d的内存地址为:%p\n", num, &num) //num:100的内存地址为:0xc0000a6058
- 变量交换和匿名变量(类似于python)
var a int = 100
var b int = 200
b, a = a, b
a, _ = b, a
- 变量的作用域
- 局部变量
- 全局变量 ( 不包含这个全局变量的源文件需要使用"import"关键字引入全局变量所在的源文件之后才能使用这个全局变量。全局变量声明必须以var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。 )
- 常量
const URL string = "192.168.1.1" //显式定义
const URL2 = "192.168.1.1" //隐式定义
const a, b, c = 3.14, "www.baidu.com", true //同时定义
/*
iota,特殊常量,可以认为是一个可以被编译器修改的常量。iota是go语言的常量计数器
iota在const关键字出现时将被重置为0(const内部的第一行之前),const 中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。
iota可以被用作枚举值:
*/
const (
a = iota
b = iota
c = iota
)
fmt.Println(a, b, c) // 0 1 2
const (
a = iota
b
c
d = "haha"
e
f = 100
g
h = iota
i
)
const (
j = iota
k
)
fmt.Println(a, b, c, d, e, f, g, h, i) // 0 1 2 haha haha 100 100 7 8 0 1 默认继承上一个元素的值
基本数据类型
布尔型
var flag bool = true
数字型
func main() {
var i1 int8
var i2 uint8
i1, i2 = 100, 200
fmt.Println(i1)
fmt.Println(i2)
}
- 浮点型,Go语言中默认是float64
- 常见的别名
字符与字符串
字符时 rune(通用一点) 或者 byte
func main() {
var str string
str = "Hello,Go"
fmt.Printf("%T,%s\n", str, str)
v1 := 'A'
v2 := "A"
fmt.Printf("%T,%s\n", v1, v1)
fmt.Printf("%T,%s\n", v2, v2)
}
/*
string,Hello,Go
int32,%!s(int32=65)//可以看到单引号定义的变量实际是int32类型,实际上打印的是对应ASCII表中的十进制值
string,A
*/
- 部分用法
func main() {
// 拼接
fmt.Printf("aaa" + "bbb\n") // aaabbb
// 转义
fmt.Printf("hello\"bbb\n") // hello"bbb
// 换行
fmt.Printf("hello\nbbb") // hello \n bbb
// Tab
fmt.Printf("hello\tbbb") // hello bbb
}
数据类型的装换
由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明。
func main() {
a := 3.14
b := int(a)
fmt.Println(a) // 3.14
fmt.Println(b) // 3
}
运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
流程控制
if
/*
if 判断条件 { //判断条件的括号可加可不加
满足条件执行的代码
} else {
不满足执行的代码
}
*/
switch
和fallthrough
switch语句的执行从上至下,直到找到匹配项,找到之后也不需要再加break,switch默认情况下case最后自带break语句。
package main
import "fmt"
func main() {
score := 90
switch score {
case 90:
fmt.Println("A")
fallthrough // fallthrough 会无视下一个case的条件,直接执行
case 80:
fmt.Println("B")
case 60,70:
fmt.Println("B")
default:
fmt.Println("D)
}
}
/*
A
B
*/
for
func main() {
for i := 1; i <= 9; i++ { //打印99乘法表
for j := 1; j <= i; j++ {
fmt.Printf("%d*%d=%d\t", j, i, j*i)
}
fmt.Println()
}
}
2. 可以当做while语句使用
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
brake
与continue
遍历字符串
package main
import "fmt"
func main() {
score := 90
name := "zhuwenjie"
println(len(name)) // len(str) 获得字符串的长度
fmt.Printf("%c,%d\n", name[1], name[1])
for i := 0; i < len(name); i++ {
fmt.Printf("%c,%d\n", name[i], name[i])
}
// i是下标,v是对应元素 类似于python的enum那个穷举类型
for i, v := range name { // for i, v := range name 其中i是下标,v是该下标对应的值
fmt.Printf("%d,%c,%d\n", i, v, v)
}
}
函数
声明
func function_name( [parameter list] ) [return_types] {
函数体
}
1. 无参无返回值函数
func printInfo() {
println("我执行了printInfo函数")
}
2. 有一个参数的函数
func printInfo(a int) {
println(a)
}
3. 有两个参数的函数
func add(a int, b int) int {
c := a + b
return c
}
4. 有多个返回值的函数
c, d = addAndDel(a, b)
func addAndDel(a int, b int) (int, int) {
c := a + b
d := a - b
return c, d
}
形参和实参
package main
func main() {
// 形参与实参要一一对应,包括类型和顺序
add, del := addAndDel(1, 2)
println(add, del)
}
// addAndDel 计算两个数相加和相减的结果
// 形式参数: a b 定义函数时,用来接收外部传入数据的参数
// 实际参数: 1 2 调用函数时,传给形参的实际数据
func addAndDel(a int, b int) (int, int) {
c := a + b
d := a - b
//一个函数在定义时如果有返回值,那么函数中必须使用return语句
return c, d
}
可变参数
- 如果一个函数的参数是可变参数,同时还有其他参数,可变参数要放在参数列表的最后
func myfunc(arg ...int){}
// arg ...int 告诉编译器这个函数接收不定数量的参数,类型全是int
package main
func main() {
println(getSum(1, 3, 5, 7, 9))
}
func getSum(nums ...int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
参数传递
按照数据的存储特点来分:
- 值类型的数据:操作的是数据本身,基础数据类型int、bool、array、struct…
- 引用类型的数据:操作的是数据的地址,slice、map、chan…
值传递
package main
import "fmt"
func main() {
//值传递 定义一个array数组 固定大小的
arr := [4]int{1, 2, 3, 4}
fmt.Println(arr)
update(arr, 1, 100)
fmt.Println(arr)
}
// 更改数组中某个元素的值
func update(arr [4]int, j int, target int) {
arr[j] = target
fmt.Println(arr)
}
/*
[1 2 3 4] //未修改前数组的值
[1 100 3 4] //update方法里修改后的值
[1 2 3 4] //修改后数组的值
*/
引用传递
package main
import "fmt"
func main() {
//引用传递 定义一个arr切片 没写大小 说明可以变
arr := []int{1, 2, 3, 4}
fmt.Println(arr)
update(arr, 1, 100)
fmt.Println(arr)
}
// 更改切片中某个元素的值
func update(arr []int, j int, target int) {
arr[j] = target
fmt.Println(arr)
}
/*
[1 2 3 4]
[1 100 3 4]
[1 100 3 4]
*/
defer
defer语义:推迟、延迟
在Go语言中,使用defer关键字来延迟一个函数或者方法的执行。
package main
import "fmt"
func main() {
f("1")
fmt.Println("2")
defer f("3")
fmt.Println("4")
}
func f(s string) {
fmt.Println(s)
}
/*
1
2
4
3
*/
defer函数或方法:该函数或方法的执行被延迟
如果一个函数中添加了多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当进行一些打开资源的操作时,遇到错误需要提前返回,在返回前需要关闭相应的资源,不然容易造成资源泄露问题。
- 如果有很多调用defer,那么defer用的是后进先出(栈)模式。
- 需要注意的是,在执行到defer语句的时候,函数里的形参就已经传递进去了,只是函数被延迟执行了。
常用场景:
对象.close() 临时文件的删除
文件.open
defer 文件.close
对文件进行操作
Go语言中关于异常的处理,使用panic()和recover()
panic 函数用于引发恐慌,导致函数中断执行
recover 函数用于恢复程序的执行,recover() 语法上要求必须在 defer 中执行
函数本质的探究
- 函数本身也是有数据类型的
func main() {
fmt.Printf("%T", fs) // func()
}
func fs() {}
- 既然函数本身有数据类型,那么它就可以作为变量,可以赋值。
func main() {
fmt.Printf("%T\n", fs) // func(int, int)
//定义函数类型的变量
var ff func(int, int) = fs
ff(1, 2) // 3 这一步等于 fs(1,2)
//看看ff和fs是否相同
fmt.Println(ff) //0x107fe40
fmt.Println(fs) //0x107fe40
}
func fs(a int, b int) {
fmt.Println(a + b)
}
匿名函数推导
func main() {
// 函数正常执行
f1()
// 匿名函数就是没有名字的函数
f2 := f1
f2()
// 匿名函数,函数体后增加一个()执行,通常只能执行一次
func() {
fmt.Println("我是一个匿名函数")
}()
// 将匿名函数赋值,单独进行调用
f3 := func() {
fmt.Println("我是一个匿名函数...")
}
f3()
// 定义带参数的匿名函数
func(a int, b int) {
fmt.Println(a, b)
}(1, 2)
// 定义带返回值的匿名函数
r1 := func(a int, b int) int {
return a + b
}(10, 20) //带了()就是函数调用
fmt.Println(r1)
}
func f1() {
fmt.Println("我是f1函数")
}
/*
我是f1函数
我是f1函数
我是一个匿名函数
我是一个匿名函数...
1 2
30
*/
回调函数
func main() {
// 正常执行
r1 := add(1, 2)
fmt.Println(r1)
// 封装
r2 := oper(3, 4, add) //加
fmt.Println(r2)
r3 := oper(8, 4, sub) //减
fmt.Println(r3)
r4 := oper(8, 4, func(i int, i2 int) int { //乘
return i * i2
})
fmt.Println(r4)
r5 := oper(8, 4, func(i int, i2 int) int { //除
if i2 == 0 {
fmt.Println("除数不能为0")
return 0
}
return i / i2
})
fmt.Println(r5)
}
// 高阶函数
func oper(a int, b int, fun func(int, int) int) int {
r := fun(a, b)
return r
}
func add(a int, b int) int {
return a + b
}
func sub(a int, b int) int {
return a - b
}
/*
3
7
4
32
2
*/
闭包的理解
/*
一个外层函数中,有内层函数,这个内层函数会操作外层函数的局部变量
且该外层函数的返回值就是该内层函数
这个内层函数和外层函数的局部变量,统称为闭包结构
此时局部变量的声明周期就会发生变化,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包中外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
*/
func main() {
r1 := increment()
fmt.Println(r1)
fmt.Println(r1())
v2 := r1()
fmt.Println(v2)
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
r2 := increment()
fmt.Println(r2())
}
func increment() func() int {
// 局部变量i
i := 0
// 定义一个匿名函数,给变量自增并返回
fun := func() int {
i++
return i
}
return fun
}
/*
0x8ae800
1
2
3
4
5
1
*/