Go语言学习笔记(入门)

简介

是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型
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值