Go学习笔记

举报
yd_256460632 发表于 2025/01/22 16:01:29 2025/01/22
【摘要】 概念基础07年出现go语言的优势:均衡(开发效率高,运行性能好),强大的并发能力go语言的优势:均衡(开发效率高,运行性能好),强大的并发能力开发语言:c c++ java go  python jsgoroot go安装路径gopath go代码目录go build (-o 文件名.exe)  编译成可执行程序/指定名称go run 文件名称  编译并执行 两种执行流程的方式区别1) 如...

概念基础

07年出现
go语言的优势:均衡(开发效率高,运行性能好),强大的并发能力go语言的优势:均衡(开发效率高,运行性能好),强大的并发能力
开发语言:c c++ java go  python js
goroot go安装路径
gopath go代码目录

go build (-o 文件名.exe)  编译成可执行程序/指定名称
go run 文件名称  编译并执行

 两种执行流程的方式区别

1) 如果我们先编译生成了可执行文件,那么我们可以将该可执行文件拷贝到没有 go 开发环境的机
器上,仍然可以运行
2) 如果我们是直接 go run go 源代码,那么如果要在另外一个机器上这么运行,也需要 go 开发
环境,否则无法执行。
3) 在编译时,编译器会将程序运行依赖的库文件包含在可执行文件中,所以,可执行文件变大了
很多。

快捷键                 作用

Shift +Alt+上/下键      快速复制
Ctrl + /             单行注释
Ctrl + Shift + /     多行注释
Ctrl + D             复制当前光标所在行
Ctrl + X             删除当前光标所在行
Ctrl + Alt + L         格式化代码
Ctrl + Shift +          方向键上或下 将光标所在的行进行上下移动(也可以使用 Alt+Shift+方向键上或下)
Ctrl + Alt +            left/right 返回至上次浏览的位置
Ctrl + R             替换
Ctrl + F             查找文本
Ctrl + Shift + F     全局查找

一、基础语法:

1.变量

先声明再赋值  var x int
声明并赋值  var y = "hello yuan!"
声明多个变量 var name,age = "yuan",22
// 声明多个相同类型变量
var x,y int  
// 声明多个不同类型变量
var (
        name      string
        age       int
        isMarried bool
    )
值拷贝  相互独立不受影响
匿名变量  

变量命名规则 

1、变量名称必须由数字、字母、下划线组成。
2、标识符开头不能是数字。
3、标识符不能是保留字和关键字

2.基本数据类型

整形   1字节=8位  ini8 -128至127 uint8 0至255
浮点型  float32  float64
布尔类型 true false(默认)
字符串  默认空值
索引 
切片左闭右开
转义符 \n 换行 
字符串的常用方法
方法 介绍
len(str) 求长度
strings.ToUpper,strings.ToLower 生成一个新的全部大写的字符串,生成一个新的全部小写的字符串
strings.ReplaceAll 生成一个新的原字符串被指定替换后的字符串
strings.Contains 判断是否包含
strings.HasPrefix,strings.HasSuffix 前缀/后缀判断
strings.Trim、 去除字符串两端匹配的内容
strings.Index(),strings.LastIndex() 子串出现的位置
strings.Split 分割,将字符串按指定的内容分割成数组
strings.Join(a[]string, sep string) join操作,将数组按指定的内容拼接成字符串

3.数据类型转化

3.1整形之间的转换  int8 int16 int32

3.2字符串与整形之间的转换 strconv

   strconv.Atoi 字符串-->整型
   strconv.Itoa 整型--> 字符串
   strconv.ParseInt 字符串-->任意整型
   strconv.ParseFloat字符串-->浮点型
   strconv.ParseBool  字符串-->布尔型
   
`strconv.Atoi` 函数只能将字符串转换为 `int` 类型,而 `strconv.ParseInt` 函数可以将字符串转换为任意整数类型,如 `int8`、`int16`、`int32`、`int64` 等。此外,`strconv.ParseInt` 还可以指定转换的进制数,而 `strconv.Atoi` 只能将字符串解析为十进制的整数。


4.运算符

4.1 算数运算符

+ - * %

4.2 关系运算符

== != > < >= <= 

4.3 逻辑运算符

   &&:与运算  真与真为 真   真与假为假  假与真为假 假与假为假
   ||:或运算  真或真为 真   真或假为真  假或者真为真 假或假为假
   !:非运算  非真为假 非假为真

4.4 赋值运算符

= 简单的赋值运算符,将一个表达式的值赋给一个左值
+= 相加后再赋值
-= 相减后再赋值 
++ 自增
-- 自减

4.5 运算符优先级

=最后级

5.输入输出函数

5.1 输出函数

5.1.1  Print 和Println 
       Print不换行 Println换行
5.1.2  格式化输出(Printf)    
fmt.Printf("姓名:%#v 年龄:%#v 婚否:%#v 薪资:%#v\n", name, age, isMarried, salary)
5.1.3 Sprintf  Printf和Sprintf都是替换字符串,Printf是直接输出到终端,Sprintf是不直接输出到终端,而是返回该字符串
%s  输出字符串
%d  输出不符号的整形
%+d 输出带符号的整形
%b  打印整形的二进制
%x  打印整形的十六进制
%o  打印不带0的八进制
%f  打印浮点型
%.2f  打印浮点型  保留2位小数
%T   打印类型
%t   打印布尔值
%#v   打印任意类型  原始形
%p    打印地址

5.2 输入函数

5.2.1 fmt.Scan  等待用户在命令输入一个值 fmt.Scan(&name, &age, &isMarried) // 输入类型不一致,按默认值
5.2.2 fmt.Scanln 换行输入
5.2.3 fmt.Scanf  按格式输入 fmt.Scanf("%d+%d", &a, &b)

二、流程控制语句

6.分支语句

6.1 单分支  if

    if username == "yuan" && password=="123"{
        fmt.Println("登录成功!")
    }

6.2 双分支 if else

    if age >= 18 {
        fmt.Println("恭喜,你已经成年,可以观看该影片!")
    } else {
        fmt.Println("抱歉,你还未成年,不宜观看该影片!")
    }     

6.3 if多分支语句  if...else if..else..

    if score > 100 && score < 0 {
        fmt.Println("输入数字应该在1-100之间")
    } else if score > 90 {
        fmt.Println("成绩优秀!")
    } else if score > 80 {
        fmt.Println("成绩良好!")
    } else if score > 60 {
        fmt.Println("成绩及格!")
    } else {
        fmt.Println("请输入一个数字!")
    }

6.4 switch多分支语句

if week > 0 && week <= 7 {
switch week {
case 1:
fmt.Println("星期一")
case 2:
fmt.Println("星期二")
case 3:
fmt.Println("星期三")
case 4:
fmt.Println("星期四")
case 5:
fmt.Println("星期五")
case 6:
fmt.Println("星期六")
case 7:
fmt.Println("星期日")
}
} else {
fmt.Println("输入不合法!")
}

6.5 分支嵌套

    var user string
    var pwd int
    fmt.Printf("请输入用户名:")
    fmt.Scanln(&user)
    fmt.Printf("请输入密码:")
    fmt.Scanln(&pwd)
    if user == "yuan" && pwd==123{
        var score int
        fmt.Printf("请输入成绩:")
        fmt.Scanln(&score)
        if score >= 90 && score<=100 {
            fmt.Println("成绩优秀!")
        } else if score >= 80 {
            fmt.Println("成绩良好!")
        } else if score >= 60 {
            fmt.Println("成绩及格")
        } else {
            fmt.Println("不及格!")
        }
    }else {
        fmt.Println("用户名或者密码错误!")
    } 

7 循环语句

7.1 简单循环案例:

count := 0   // 初始化语句
for count<10 {   // 条件判断
    fmt.Println(count)
    count ++  //  步进语句
}
7.2.1 无限循环案例:
for true{
      
    }

7.2.2 循环中嵌入分支语句:

func main() {
    fmt.Printf(`
    1、普通攻击
    2、超级攻击
    3、使用道具
    4、逃跑
`)
    for true {
        var choice int
        fmt.Printf("请输入选择:")
        fmt.Scanln(&choice)
        //fmt.Println(choice,reflect.TypeOf(choice))
        switch choice {
        case 1:
            fmt.Println("普通攻击")
        case 2:
            fmt.Println("超级攻击")
        case 3:
            fmt.Println("使用道具")
        case 4:
            fmt.Println("逃跑")
        default:
            fmt.Println("输入有误!")
        }
    }
}

7.3 三要素for循环(核心)

for init;condition;post { 
    // 循环体语句
}

for i := 1; i < 10; i++ {
        fmt.Println(i)
    }

7.4 遍历for循环

循环作用域:


7.5 退出循环

7.5.1 break  退出整个循环
7.5.2 continue 退出当前循环  后面有代码才有实际作用

7.6 循环嵌套

7.6.1  独立嵌套
7.6.2  关联嵌套


三,重要的数据类型

8.1指针类型(核心类型):接收地址值


var x = 100
// 取址符:&  取值符:*   *指针变量 不能接普通变量
fmt.Println("x的地址:",&x);
// 将地址值赋值给的变量称为指针变量
var p *int  = &x
fmt.Println("p的值:",p);
fmt.Println("p的地址",&p)
fmt.Println("p地址对应的值",*p)

8.2 new函数  

基本数据类型属于值类型
值类型(整型,浮点型,字符串,布尔类型,数组,结构体)的特点:当声明未赋值之前存在默认值。
引用类型(指针类型,切片,map,channel)的特点:当声明未赋值之前不存在默认值。
new函数 
var p *int
p = new(int)
fmt.Println(*p)
*p = 10
fmt.Println(*p)

8.3 数组

数组的特点:
一致性:数组只能保存相同的数据类型元素,元素的数据类型可以是任意相同的数据类型
有序性:数组中的元素是有序的,通过下标访问
不可变性:数组一旦初始化,则长度不可变 

// (1)先声明再赋值
// 数组的声明
// 数组必须限制长度
var arr [3]int
fmt.Println(arr) // [0 0 0]
// 赋值     数组[索引]
fmt.Println(arr[0])
fmt.Println(arr[1])
fmt.Println(arr[2])
// 索引赋值
arr[0] = 10
fmt.Println(arr) // [10 0 0 ]
arr[1] = 11
arr[2] = 12
fmt.Println(arr) // [10 11 12]

//(2)数组的声明并赋值
var names = [3]string{"yuan", "rain", "alvin"}
fmt.Println(names) // [yuan rain alvin]
var ages = [3]int{22, 23, 24}
fmt.Println(ages) // [22 23 24]

// (3) 省略长度赋值
var names2 = [...]string{"yuan", "rain", "alvin"}
fmt.Println(names2) // [yuan rain alvin]

// (4) 索引赋值
var names3 = [...]string{0: "yuan", 2: "rain"}
fmt.Println(names3) // [yuan  rain]

// go len函数:计算容器数据的长度
fmt.Println(len("hello"))
fmt.Println(len(names3))

数组操作:

var names = [3]string{"yuan", "rain", "alvin"}
// (1)索引操作 数组[索引]
fmt.Println(names[2])
names[2] = "Alvin"
fmt.Println(names)
// (2) 切片操作  数组[start索引:end索引]
var arr = [5]int{11, 12, 13, 14, 15}
s1 := arr[1:3] 
fmt.Println(s1, reflect.TypeOf(s1)) // [12 13]  []int
s2 := arr[1:]
fmt.Println(s2, reflect.TypeOf(s2))
s3 := arr[:3]
fmt.Println(s3, reflect.TypeOf(s3)) // [11 12 13] []int
s4 := arr[2:4]
fmt.Println(s4)

// (3) 遍历数组
//三要素for循环
var arr2 = [5]int{20, 21, 22, 23, 24}
for i := 0; i < len(arr2); i++ {
fmt.Println(i, arr2[i])
}

// range循环(首推,更好理解)
for _, v := range arr2 {
//fmt.Println(i, v)
v = 0
fmt.Println(v)
}
fmt.Println(arr2)

8.4 切片(slice)

切片是一个动态数组,因为数组的长度是固定的,所以操作起来很不方便。切片能更改长度。
切片是对数组的引用
每个切片存储3个值:起始地址 长度 容量
// 构建切片方式一:通过数组切片操作获得切片对象
/*var arr = [3]string{"rain", "eric", "alvin"}
fmt.Println(arr, reflect.TypeOf(arr))
s1 := arr[0:2]
fmt.Println(s1, reflect.TypeOf(s1)) // [rain eric] []string
s2 := arr[1:]
fmt.Println(s2, reflect.TypeOf(s2)) // [eric alvin] []string
*/
/*s2[0] = "yuan"
fmt.Println(s1)
fmt.Println(arr)*/

// (1) 切片是对数组的引用

/*var a = [5]int{1, 2, 3, 4, 5}
var slice = a[:] // 起始地址  长度  容量
fmt.Println(len(slice), cap(slice))
var slice2 = a[:2]
fmt.Println(len(slice2), cap(slice2))
fmt.Println(slice) // [1 2 3 4 5]
newSlice := slice[1:3]
fmt.Println(newSlice)
fmt.Println(len(newSlice), cap(newSlice))

newSlice[1] = 1000
fmt.Println(a)
fmt.Println(slice)
fmt.Println(slice2)
*/
// (2) 直接声明切片
//arr := [5]int{10, 11, 12, 13, 14}
//s := arr[:]

var s = []int{10, 11, 12, 13, 14}
s1 := s[1:4] // [11, 12, 13]
fmt.Println(len(s1), cap(s1))
s2 := s[3:]
fmt.Println(len(s2), cap(s2))
s3 := s1[1:2]
fmt.Println(len(s3), cap(s3))

//案例1
/*s1 := []int{1, 2, 3} // 切片 3 3  [1 2 3]
s2 := s1[1:]         // 2 2   [2 3]
s2[1] = 4
fmt.Println(s1)*/

// 案例2
var a = []int{1, 2, 3} // [1 2 3] 
b := a
c := a[1:]
a[0] = 100
fmt.Println(b)
fmt.Println(c)

 8.4 make函数

 如果需要动态地创建一个切片,可以使用 make() 内建函数,格式 make([]Type, size, cap)
 new函数仅仅返回地址
 make函数返回切片,即地址,长度,容量
 
// var s = []int{1,2,3,4,5}
// var s []int
//  初始化创建空间

var s = make([]int, 5, 10)
fmt.Println(len(s), cap(s))
fmt.Println(s)
s[0] = 100
fmt.Println(s)

// 练习题
a := make([]int, 5)
b := a[0:3]
a[0] = 100
fmt.Println(a) // [100 0 0 0 0]
fmt.Println(b) // [100 0 0]

append(重点)
片作为一个动态数组是可以添加元素的,添加方式为内建方法append。
对于容量不够的数组,会动态创建一个新的数组,双倍扩容
对于容量足够的数组,会在原来的数组上追加

    var s []int
    // 可以追加空切片一个值
    s1 := append(s, 0)
    fmt.Println(s1)
    // 可以追加多个值
    s2 := append(s1,1,2,3)
    fmt.Println(s2)
    // 可以追加一个切片
    s3 := append(s2,[]int{4,5,6}...)
    fmt.Println(s3)
 
a := []int{11, 22, 33}
fmt.Println(len(a), cap(a))

c := append(a, 44)
a[0] = 100
fmt.Println(a)
fmt.Println(c)

// make
/*a := make([]int, 3, 10)
fmt.Println(a)
b := append(a, 11, 22)
fmt.Println(a) // 小心a等于多少?
fmt.Println(b)
a[0] = 100
fmt.Println(a)
fmt.Println(b)*/

arr := [4]int{10, 20, 30, 40}
s1 := arr[0:2] // [10, 20]
s2 := s1       //  // [10, 20]
s3 := append(append(append(s1, 1), 2), 3)
s1[0] = 1000
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(arr)
append原理
每次 append 操作都会检查 slice 是否有足够的容量,如果足够会直接在原始数组上追加元素并返回一个新的 slice,底层数组不变,但是这种情况非常危险,极度容易产生 bug!而若容量不够,会创建一个新的容量足够的底层数组(双倍扩容),先将之前数组的元素复制过来,再将新元素追加到后面,然后返回新的 slice,底层数组改变而这里对新数组的进行扩容
经典面试题
var s = make([]int, 5, 10)
fmt.Println(len(s), cap(s))
fmt.Println(s)
s[0] = 100
fmt.Println(s)
append其他操作

/*var s = []int{1, 2, 3}
fmt.Printf("%p\n", &s)
s = append(s, 4)
fmt.Printf("%p\n", &s)*/

结果地址都是一样的,原理append时,即是重新给切片s赋值,其实就是给切片三要素重新赋值,初始地址,长度,容量都会改变,切片的结果会变,但是地址不变,内存地址还是原来的那块空间

// 向开头插入值或者切片
var a = []int{1, 2, 3}
fmt.Println(append([]int{0}, a...))
fmt.Println(append([]int{-1, -2, -3}, a...))

// 任意位置插入元素
var b = []int{1, 2, 3, 4, 5}
var i = 2
// var a []int
// a = append(a[:i], append([]int{x}, a[i:]...)...)       // 在第i个位置插入x
// a = append(a[:i], append([]int{1, 2, 3}, a[i:]...)...) // 在第i个位置插入切片
fmt.Println(append(b[:i], append([]int{100}, b[i:]...)...))

// 删除元素
var c = []int{1, 2, 3, 4, 5}
c = append(c[:2], c[2+1:]...)
fmt.Println(c)

8.5 map(映射)类型

var map_name map[key_type]value_type
     //声明
//var stus map[string]string
//fmt.Println(stus, reflect.TypeOf(stus))
// (1) 声明并初始化

// var x = []int{1, 2, 3} // 切片的声明并初始化

/*var stu01 = map[string]string{"name": "yuan", "age": "32"}
// 支持key查询  map对象[key]
fmt.Println(stu01) // map[age:32 name:yuan]
fmt.Println(stu01["name"])
fmt.Println(stu01["age"])
fmt.Println(len(stu01))
// 写入一个key-value
stu01["gender"] = "male"
fmt.Println(stu01) // map[age:32 gender:male name:yuan]
stu01["height"] = "180cm"
// 修改一个key-value
stu01["height"] = "190cm"
fmt.Println(stu01) // map[age:32 gender:male height:190cm name:yuan]
// 删除一个key-value
delete(stu01, "gender")
fmt.Println(stu01) // map[age:32 height:190cm name:yuan]

// (2) 基于make函数声明初始化

var p *int
p = new(int)
fmt.Println(*p)

//var s []int
s := make([]int, 3)
s[0] = 100

*/
//  遍历map对象
var s = []int{10, 11, 12, 13, 14}

for i, v := range s {
fmt.Println(i, v)
}

//var stu02 map[string]string // map引用类型

var stu02 = make(map[string]interface{})
stu02["name"] = "rain"
stu02["age"] = 30
stu02["gender"] = "male"
fmt.Println(stu02)

for k, v := range stu02 {
fmt.Println(k, v)
}

// 如何判断键是否存在
v, b := stu02["names"]
fmt.Println(v, b)

map拓展

(1)map嵌套slice
var data = make(map[string][]string)
data["beijing"] = []string{"朝阳", "海淀", "昌平"}
data["shandong"] = []string{"济南", "青岛", "威海"}
data["hebei"] = []string{"唐山", "石家庄", "保定"}
// fmt.Println(data) // map对象
(2)map嵌套map
/*stu01 := map[string]string{"name": "rain", "age": "32"}
stu02 := map[string]string{"name": "eric", "age": "33"}
stu03 := map[string]string{"name": "alvin", "age": "34"}
var stus = make(map[int]map[string]string)
stus[1001] = stu01
stus[1002] = stu02
stus[1003] = stu03
fmt.Println(stus)
(3)切片嵌套map
// 方式1
stu01 := map[string]string{"name": "rain", "age": "32"}
stu02 := map[string]string{"name": "eric", "age": "33"}
stu03 := map[string]string{"name": "alvin", "age": "34"}
// var stus = make([]map[string]string, 3)
var stus = []map[string]string{stu01, stu02, stu03}
// 方式2
//var stus = []map[string]string{{"name": "rain", "age": "32"}, {"name": "eric", "age": "32"}}

8.6 函数初识

函数功能:代码的一种组织形式,实现模块化,避免重复
声明函数:

    func 函数名(形式参数) (返回值) {
            函数体
            return 返回值   // 函数终止语句
    }
    
    函数调用:
    函数名()

变量 = 函数(实参)    // return 返回的值赋值给某个变量,程序就可以使用这个返回值了。
函数参数
    func printLing(n int) {// 形式参数 1个参数
     函数体
}

    func add(a, b int) { // 形式参数 2个参数
    fmt.Println(a + b)
    }
    
func add2(s ...int) { // 形式参数 多个参数
fmt.Println(s, reflect.TypeOf(s))
var ret = 0
for _, v := range s {
ret += v
}
函数返回值
    // 无返回值的情况
    func printLing(n int) {
         函数体
    }
    // 返回一个值的情况
    func add(a, b int) int { // 形式参数
    c := a + b
    return c // 函数的终止语句
    }
// 返回值命名
    func add2(s ...int) (z int) { // 形式参数
    fmt.Println(s, reflect.TypeOf(s))
    
    for _, v := range s {
    z += v
    }
    return
    
    }


    }
// 返回多个值的情况
    func login(user, pwd string) (bool, string) {
    if user == "root" && pwd == "123" {
    // 登陆成功
    return true, user
    } else {
    return false, ""
    }
    }
// 无返回值不能赋值给单独变量
//ret2 := printLing(6)
b, user := login("root", "123")
if b {
fmt.Println(user)
}

// 返回值命名
    func add2(s ...int) (z int) { // 形式参数 z声明时默认为0
    fmt.Println(s, reflect.TypeOf(s))
    
    for _, v := range s {
    z += v
    }
    return
    
    }

作用域
    变量根据所在位置的不同可以划分为全局变量和局部变量
局部变量 :写在{}中或者函数中或者函数的形参, 都是局部变量
全局变量 :函数外面的就是全局变量
:=只能用于局部变量, 不能用于全局变量
局部变量如果没有使用, 编译会报错。全局变量如果没有使用, 编译不会报错
局部变量的作用域是从定义的那一行开始, 直到遇到 } 结束或者遇到return为止
局部变量, 只有执行了才会分配存储空间, 只要离开作用域就会自动释放
全局变量的作用域是从定义的那一行开始, 直到文件末尾为止
全局变量, 只要程序一启动就会分配存储空间, 只有程序关闭才会释放存储空间

外层不能打印里层的变量,如果该函数层没有找到变量,需要向外扩展找变量,不能跨到其他函数找。
先找私域的变量,找不到才会去外层找。

x=20 和 var x=20 不一样
局部没有声明变量,会使用全局的变量,一旦使用 全局的变量值就会被改变,被重新赋值了

注意,if,for语句具备独立开辟作用域的能力,在{}里面



【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。