简介
是Google开发的一种计算机编程语言
优点
1)计算机硬件技术更新频繁,性能提高很快。目前的主流的编程语言发展明显落后于硬件,不能合理利用多核多CPU的优势
2)软件系统复杂度越来越高,维护成本越来越高,目前缺乏一个足够简洁高效的编程语言。
3)企业的运行维护很多C++/C的项目,C/C++程序运行速度很快,但是编程速度很慢,同时存在内存泄漏的一系列的困扰需要解决。
求职方向
-
Go服务器端/游戏软件工程师
-
Go分布式/云计算软件工程师
-
区块链工程师
安装后的目录
-
api 存放方法接口
-
bin go的命令
-
go.exe go语言编译器
-
gofmt.exe go语言格式化代码工具
-
开发环境
go 1.21.0
新版本的一些问题
1、go文件方法相互引用时路径找不到问题:
go在1.5之后,不用再配置gopath,在你使用的文件夹下面需要运行go mod init 文件夹名,从而时go能识别该路径。
2、不用导入fmt库,可以直接使用fmt库中的方法
命令
运行代码: go run go文件(从go.mod所在的文件夹开始算起)
入门
性质
-
go语言会自动给语句加分号
z = x + y //-> z = x + y;
z = x +
y //-> z = x + y;
z = x
+ y //×, 因为程序会默认z = x;
-
变量不能重复定义
var age int = 18
println(age)
var age int = 19 //×
println(age) //报错
-
如果不指定数据值,使用默认值
var age int
println(age) //0
-
自动类型推断
var age int
println(age) //0
-
支持一次性声明多个变量
var n1,n2,n3 int
println(n1)
println(n2)
println(n3)
var n1,n2,n3 = 10, "jack", 7, 8
n6, height := 6.9, "高" //string必须用双引号定义
-
一次性声明全局变量
var (
n1 = 1
n2 = "jack"
)
-
一旦声明必须使用
基本数据类型
-
两个int类型做运算结果为int类型
n3 := 10/3 //× n3 = 3
n3 := 10.0/3 //√ n3 = 3.33333333
-
自增,自减
var a int = 10
a++
a-- //自能单独这样写,不能套入运算中,且只能写在变量后
-
+=,-=,/=,%=,*=
num := 10
num += 10 // num = num + 10
-
&:返回变量的存储地址,*:取指针变量对应的数值
var age int = 18
fmt.Println("age对应的存储空间的地址为:", &age)
var ptr *int = &age //指针中存储age的地址
fmt.Println(ptr)
流程控制
-
if...else....
\\ 单分支
\\ if 后可以定义变量
if (count < 30) {
} \\()可以省略
if count:=20;count < 30 {
}
\\ else
if count < 30 {
} else{ \\注意else前不能换行
}
if count < 30 {
}
else{ \\× 不能这样写
}
\\ 多分支
if count < 30 {
} else if count = 30{ \\注意else前不能换行
} else {
}
-
switch .. case .. default...
\\ 单分支
\\ if 后可以定义变量
if (count < 30) {
} \\()可以省略
if count:=20;count < 30 {
}
\\ else
if count < 30 {
} else{ \\注意else前不能换行
}
if count < 30 {
}
else{ \\× 不能这样写
}
\\ 多分支
if count < 30 {
} else if count = 30{ \\注意else前不能换行
} else {
}
-
for
var sum int = 0
for i := 1; i <= 5; i++ { \\ for不能用var i int =1
sum += i
}
fmt.Println(sum)
函数
内存分析
package main
import "fmt"
// 函数首字母大写,表示函数可以被其他包使用,返回一个参数时,类型不用加()
func cal(num1 int, num2 int) int {
return num1 + num2
}
// 没有返回值时,不用写
func cal1(num1 int, num2 int) {
fmt.Println(num1 + num2)
}
// 两个返回值对应写两个类型,必须加()
func cal2(num1 int, num2 int) (int, int) {
sum := num1 + num2
result := num1 - num2
return sum, result
}
// go语言不支持重载(不可以函数名相同,形参列表不同)
// 支持不确定数量的返回值
// 可变参数
func cal3(args ...int) (int, int) {
sum := 0
// 函数内容处理可变参数的时候,将可变参数当做切片来处理
for i := 0; i < len(args); i++ {
sum += args[i]
fmt.Println(args[i])
}
return sum, len(args)
}
// 在函数内部改变函数外的变量值, *代表是指针
func cal4(num *int) {
// 修改指针所指的地址的值
*num = 30
}
func main() {
sum := cal(10, 20)
fmt.Println(sum)
sum1, _ := cal2(10, 20) // 可以利用“_”接受不用的返回值
fmt.Println(sum1)
sum, count := cal3(3, 4, 5, 7)
fmt.Println(sum, count) // 19 4
var num int = 10
// 传入num的地址
cal4(&num)
fmt.Println(num) // 30
}
-
go中函数也是一种数据类型,可以赋值给一个变量,通过变量去调用
a := cal //给函数赋值
a(10,20) // 调用函数
因此可以函数中传入函数变量
func test(num int, num2 float32, testFunc func(int)){
}
-
自定义数据类型 (给类型起别名)
type myInt int
var num1 myInt = 30
fmt.Println(num1)
var num2 int = 10
num2 = num1 // × 回报错误,两种不同的数据类型不能相互赋值
// 自定义函数类型,注意 func(int) 和func(int) int 不一样一个是有返回值的函数类型一个是没有的
type myFunc func(int)
func test1(num int) {
fmt.Println(num)
}
func test(num1 int, num2 int, m myFunc) {
fmt.Println(num1 + num2)
m(10)
}
a := test1
test(5, 9, a)
-
可以在函数中直接指定返回的变量,这样的话就可以不考虑return的先后顺序
func test02(num1 int, num2 int) (sum int, sub int){
sub = num1 + num2
sum = num1 + num2
return sub, sum
}
包的引入
-
相同函数名,不能写到同一包中
-
不同包下,调用函数和变量时,首字母要大写
-
原始的go语言需要指定GOPATH也就是go代码的工作区,而且写go代码只能在工作区中写,为了解决这个问题,新版本的go语言,使用模块去创建工作区,可以在任意的文件夹中写代码
-
通过go mod init 文件夹名来创建模块,之后会多出来一个go.mod文件,模块内的包可以相互引用
// main.go
package main
import (
"demo/demo1/utils" // 路径
"fmt"
)
func main() {
fmt.Println("Main 方法")
utils.GetConn() //utils是包名
}
\\ utils.go
package utils
import "fmt"
func GetConn() {
fmt.Println("GetConn方法")
}
-
同一文件夹下的同级源文件必须在同一个包下,也就是上面中utils文件夹中的go文件必须所属同一包
错误处理机制
-
错误处理:defer和recover
func test() int {
defer func() {
// 调用recover捕获错误
err := recover()
// 如果没有捕获错误,返回零值:nil
if err != nil {
fmt.Println("错误已经捕获")
fmt.Println("err是:", err)
}
}() //匿名函数的调用
num1 := 10
num2 := 0
result := num1 / num2 //发生错误后,还可以继续运用
fmt.Println(result)
return result
}
func main() {
// 异常捕获
test()
}
-
自定义处理 new
package main
import (
"errors"
"fmt"
"getcode/utils"
)
func test() (err error) {
num1 := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0") // 自定义错误
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
func main() {
fmt.Println("main")
utils.GetConn()
err := test() // 捕获错误,但是程序不会停止运行
if err != nil {
fmt.Println("自定义错误:", err) // 打印错误
panic(err) //出现错误后,程序停止运行
}
}
数组
// 定义
var scores [5]int
var arr1 [3]int = [3]int{1,2,3}
var arr2 = [3]int{1,2,3}
var arr3 = [...]int{1,2,3} // 长度不确定
var arr4 = [...]int{2:22,1:23,3:12} // 索引:值
// 赋值
scores[0] = 95
//内存分析
//数组中对应的每个元素的内存地址是连续的
// 优点:查询读取速度比较快
// 数据的迭代用法
for key,value := range scores {
fmt.Printf("第%d个学生的成绩为:%d\n", key,value)
}
// 全局数组如何在方法中修改元素值
package main
import "fmt"
func test(arr *[3]int) {
(*arr)[0] = 7
}
func main() {
var arr3 = [3]int{1, 2, 3}
test(&arr3)
fmt.Println("arr:", arr3)
}
// 二维数组
var arr [2][3]int = [2][3]int{{4, 5, 6}, {7, 8, 9}}
arr[1][1] = 12
fmt.Println(arr)
// 内存分析:内存arr[0][0]~arr[1][1]均占连续的2字节空间
// 遍历
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Println(arr[i][j])
}
}
for key, value := range arr {
for k, v := range value {
fmt.Printf("arr[%v][%v]=%v", key, k, v)
}
fmt.Println()
}
切片
是golang特有的数据类型
数组长度固定不可变,所以Go语言的代码里并不是特别常见。切片是建立在数组类型之上的抽象。一般切片用的比较常见一些
var intarr [6]int = [6]int{3, 4, 1, 2, 3, 5}
//定义切片
// 方式1:通过数组索引定义,包含1不包含3,左闭右开
slice1 := intarr[1:3] // 切片就是对数组连续片段的引用
fmt.Println(slice1)
// 方式2:通过make方法定义,他底层有一个原数组,这种方式无法访问原数组,对外不可操作
slice2 := make([]int, 4, 20) //类型、长度、容量
slice2[0] = 2
slice2[1] = 2
fmt.Println(slice2) // 赋值方式与数组相同
-
切片底层结构体保存的起始地址、切片长度、切片容量三部分信息,起始地区对应slice窃取的数组第一个位置的地址
-
索引不能越界
-
切片可以动态增长,append(slice, 88, 50) 会将88,50追加到数组中(底层其实是创建一个新数组,然后将这些值赋值到新数组中),所以append函数要写一个新的切片对象去接受,即slice2 = append(slice, 88, 50)
-
append(slice, slice2,...)切片拼接
slice3 := append(slice1, 99, 88) // 切片追加
fmt.Println(slice3)
slice := append(slice1, slice2...) // 切片拼接
fmt.Println(slice)
var a []int = []int{1, 3, 4, 5, 6, 8}
var b []int = make([]int, 10)
copy(b, a) //将a数组中的内容copy到b中
fmt.Println(b)
映射
基本语法
var mapName map[keyType] valueType
注意事项
-
keyType不能是slice、map、function
-
keyType通常是int、string,valueType通常是digital(int、float)、string、map、结构体
package main
import (
"fmt"
)
func main() {
// map定义
// 方式一
//只声明map内存是没有分配空间的
var a map[int]string
//使用map分配空间
a = make(map[int]string, 10)
a[1120220307] = "lzl"
fmt.Println(a)
// 方式二
b := make(map[int]string)
b[1120220307] = "lzl"
fmt.Println(b)
// 方式三
c := map[int]string{
1120220307: "张三",
}
fmt.Println(c)
// map删除
delete(b, 1120220307)
delete(b, 1120220306) //如果没有找到对应的key就不删除,也不报错
// map清空
// 1.遍历删除
// 2.重新map一个新的,map
// 查找
// value,bool = map[key]
value, flag := b[1120220307]
fmt.Println(value, flag)
// 遍历
for k, v := range b {
fmt.Printf("key为:%v value为%v \t", k, v)
}
// 深层map
d := make(map[string]map[int]string)
// 赋值
d["班级1"] = make(map[int]string, 3)
d["班级1"][2022] = "lzl"
for k1, v1 := range d {
for k2, v2 := range v1 {
fmt.Println(k1, k2, v2)
}
}
}
结构体
类似于java中的对象
-
定义
// 定义
type Teacher struct {
Name string
Age int
School string
}
// 方式一
var t1 Teacher
fmt.Println(t1) // 未赋值时默认是{0}
t1.Age = 22
t1.Name = "lzl"
t1.School = "DLMU"
fmt.Println(t1)
// 方式二
var t Teacher = Teacher{"lzl", 22, "DLMU"}
fmt.Println(t)
// 方式三
var t2 *Teacher = new(Teacher)
// t是指针,t其实指向的就是一个地址,应该给这个地址的指向的对象赋值
(*t2).Name = "lzl"
(*t2).Age = 33
t2.School = "DLMU" //这样写go语言底部还是使用(*t2).School = "DLMU"
fmt.Println(*t2)
// 方式四
var t3 *Teacher = &Teacher{"lzl", 22, "dlmu"}
fmt.Println(t3)
-
类型转换
// 不同对象间类型转换
var s1 Student = Student{19}
var p Person = Person{19}
s1 = Student(p) // 强制转换
fmt.Println(s1)
// 相同对象不同别名间的类型转换
var s Stu = Stu{19}
s1 = Student(s)
-
给结构体绑定方法
import "fmt"
type Person struct {
Age int
Name string
}
// 值传递,不改变对象的实际值
func (p Person) test() {
p.Name = "露露"
fmt.Println(p.Name) //露露
}
// 引用传递,改变对象的实际值
func (p *Person) test1() {
p.Name = "露露"
fmt.Println(p.Name) //露露
}
// 其实底层代码如下,go语言自动为我们加了&或*符号
// func (p *Person) test1() {
// (*p).Name = "露露"
// fmt.Println(p.Name) //露露
// }
func main() {
var p Person
p.Name = "lzl"
// 值传递
p.test()
fmt.Println(p.Name) // lzl
// 引用传递
p.test1() // 底层(&p).test1()
fmt.Println(p.Name) // 露露
}
-
封装基本数据类型,进行类方法绑定
type int integer
func (i integer) change(){
i = 30
fmt.Println(i)
}
func main(){
var i integer = 20
i.change() // 引用传递同上
}
补充:
fmt库
fmt.Printf("%T", a) //代表输出a的数据类型,%v输出a的值
fmt.scann(&scores[i]) //接受键盘输入值,并赋值给变量scores[i],注意传引用接受
fmt.Printf("%d", a) //代表输出a的数字digital,如int型