Golang¶
导包¶
Go
import (
"fmt"
// "strconv"
"gomod-code/v1/mod/publicfunc"
// 导入自定义的包,前面需加上 go.mod 内的路径
"errors"
)
// 标志符的使用
// 下划线 "_" 本身在 Go 中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但是它对应的值会被忽略(比如: 忽略某个返回值)。所以仅能被作为占位符使用,不能单独作为标识符使用。
// 定义的变量和包如果没有使用,编译会报错
// 不支持单行{}
// fun{
// body
// }
// 起名规则
// (1)包名: 尽量保持 package 的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,和标准库不要冲突
// 1.为什么之前在定义源文件的时候,一般我们都用 package main 包?
// main 包是一个程序的入口包,所以你 main 函数它所在的包建议定义为 main 包,如果不定义为 main 包,那么就不能得到可执行文件。
// 2.尽量保持 package 的名字和目录保持一致
// (2)变量名、函数名、常量名 : 采用驼峰法。
// 就是单词按照大写区分开
// var stuNameDetail string = 'lili'
// (3)如果变量名、函数名、常量名首字母大写,则可以被其他的包访问;
// 如果首字母小写,则只能在本包中使用 (利用首字母大写小写完成权限控制)
变量¶
Go
// 全局变量的声明 --> 全局变量可以声明不使用,不会报错
// 1.一个一个声明
var glo1 int
var glo2 int
// 2.一次声明多个 --> var(变量声明)
var(
glo3 int
glo4 int
)
func main() {
// 1.变量--> var 名字 类型
// 2.变量不能重复声明
var str string = "hello world"
println(str)
// 3.如果变量不初始化,则默认值为 0/空/false
var age int
var str1 string
println("age =", age)
println("str1 =", str1)
// 4.变量声明如果不写类型,则会自动判断类型
var str2 = "haihaihai"
println(str2)
// 5.一次声明多个变量
var n1, n2, n3 int = 10, 20, 30
println(n1, n2, n3)
var m1, m2, m3 = 1, "haha", 3
println(m1, m2, m3)
}
数据类型¶
基本数据类型¶
Go
// 数据类型--> 基本数据类型和复杂数据类型
// 基本数据类型
// 整形 -->
// 有符号:int8,int16,int32,int64
// 无符号:uint8,uint16.uint32,uint64
// 其他整数类型--> byte==uint8 rune==int32
// 浮点型 -->
// float32, float64
// 浮点数可以用 10 进制表示,也可用科学计数法表示
// float64 精度更大,推荐使用 float64
var ch1 byte = 'a'
println(ch1) // 打印出的是 ASCII 码
// 字符是按 ASCII 码存储
var ch2 int = '中'
// 汉字为 Unicode 编码存储,故不能用 byte
println(ch2)
fmt.Printf("ch1 =%c\n", ch1) // 格式化输出
// bool 类型 --> true/false
// 字符串类型 --> 不可变序列(指不能改变序列中单个字符)
var s2 string = "abc"
// s2 [0] ='e' // 会报错
s2 = "efg" // 不会报错
fmt.Println("s2 =", s2)
// 字符串的表示形式--> 双引号和反引号
// 1.没有特殊符号--> 双引号
// 2.有特殊符号--> 反引号
var s3 string = `
abdakdcba)&U*&
acbuaibcxaicsxa
|||||||||||
cxacxbakjcx`
fmt.Println(s3)
// 字符串的拼接 --> +
fmt.Println(s2 + s3)
// 基本数据类型转换 --> go 只有显式转换
// 向上转换不会报错,但会发生数据截断
// 运算时,最终结果类型以参与运算的数据类型最大的为主
var f float64 = 5.022
var fi int64 = int64(f)
fmt.Println(fi)
// 基本数据类型与字符串相互转换
// 基本数据类型 --> string
// 1.fmt.Sprintf 方法 --> 更常见
var i1 int = 18
var str3 string = fmt.Sprintf("%d", i1) // 前面是要转化的数据格式化
fmt.Printf("%T, str3 =%q\n", str3, str3) // %T 为输出数据类型
// 2.strconv 包方法
var str4 string = strconv.FormatInt(int64(i1), 10)
fmt.Printf("%T, str4 =%q\n", str4, str4)
// string --> 基本数据类型
//string --> bool
var s1 string = "true"
var b bool
// ParseBool 这个函数的返回值有两个:(value bool, err error)
// value 就是我们得到的布尔类型的数据,err 出现的错误
// 我们只关注得到的布尔类型的数据,err 可以用_直接忽略
b , _ = strconv.ParseBool(s1)
fmt.Printf("b 的类型是:%T, b =%v \n", b, b)
// string --> int64
var s2 string = "19"
var num1 int64
num1,_ = strconv.ParseInt(s2,10,64)
fmt.Printf("num1 的类型是:%T, num1 =%v \n", num1, num1)
// string --> float32/float64
var s3 string = "3.14"
var f1 float64
f1,_ = strconv.ParseFloat(s3,64)
fmt.Printf("f1 的类型是:%T, f1 =%v \n", f1, f1)
// 注意:string 向基本数据类型转换的时候,一定要确保 string 类型能够转成有效的数据类型,否则最后得到的结果就是按照对应类型的默认值输出
复杂数据类型¶
Go
// 复杂数据类型
// 指针
// 指针的声明
var ptr *int = &i1
fmt.Println(ptr) //输出 i1 地址
fmt.Println(*ptr) //输出 ptr 对应地址的值
获取用户输入¶
Go
// 获取用户输入
// 1.scanln
fmt.Println("please input name:")
var name string
fmt.Scanln(&name) //传入一个地址
fmt.Println("your name is ", name)
// 2.scanf
fmt.Println("please input your id:")
var id int
fmt.Scanf("%d", &id)
fmt.Println("your id is ", id)
分支结构¶
if-else if-else¶
Go
// 分支结构
// if else(if else if else)
var num int = 10
if num > 100{
fmt.Println("num > 100")
}else{
// else 不能另起一行
fmt.Println(" num <= 100 ")
}
if num < 20 && num > 10{
fmt.Println("num < 20 && num > 10")
}else if num > 0 && num < 10{
fmt.Println(" num > 0 && num < 10 ")
}else{
fmt.Println("num")
}
switch¶
Go
switch
// 给出一个学生分数:
var score int = 187
// 根据分数判断等级:
// switch 后面是一个表达式,这个表达式的结果依次跟 case 进行比较,满足结果的话就执行冒号后面的代码。
// default 是用来“兜底”的一个分支,其它 case 分支都不走的情况下就会走 default 分支
// default 分支可以放在任意位置上,不一定非要放在最后。
// 不用像 java 一样每个分支后面都加入 break 关键字
switch score/10 {
case 10 :
fmt.Println("您的等级为 A 级")
case 9 :
fmt.Println("您的等级为 A 级")
case 8 :
fmt.Println("您的等级为 B 级")
case 7 :
fmt.Println("您的等级为 C 级")
case 6 :
fmt.Println("您的等级为 D 级")
case 5 :
fmt.Println("您的等级为 E 级")
case 4 :
fmt.Println("您的等级为 E 级")
case 3 :
fmt.Println("您的等级为 E 级")
case 2 :
fmt.Println("您的等级为 E 级")
case 1 :
fmt.Println("您的等级为 E 级")
case 0 :
fmt.Println("您的等级为 E 级")
default:
fmt.Println("您的成绩有误")
}
循环结构¶
Go
// 循环结构
1.普通 for 循环
// 实现一个功能:求和: 1+2+3+4+5:
// 求和:
// 利用 for 循环来解决问题:
var sum int = 0
for i := 1; i <= 5; i++ {
sum += i
}
// 输出结果:
fmt.Println(sum)
// for 循环的语法格式:
// for 初始表达式; 布尔表达式(条件判断); 迭代因子 {
// 循环体;--> 反复重复执行的内容
// }
// 注意:for 的初始表达式 不能用 var 定义变量的形式,要用:=
// for range
// 定义一个字符串:
var str string = "hello golang 你好"
// for range
for i , value := range str {
fmt.Printf("索引为:%d, 具体的值为:%c \n", i, value)
}
// 对 str 进行遍历,遍历的每个结果的索引值被 i 接收,每个结果的具体数值被 value 接收
// 遍历对字符进行遍历的
函数¶
Go
// 函数
// func 函数名(形参列表)(返回值类型列表){
// 执行语句..
// return + 返回值列表
// }
// 自定义函数:功能:两个数相加:
func cal (num1 int, num2 int) (int) { //如果返回值类型就一个的话,那么()是可以省略不写的
var sum int = 0
sum += num1
sum += num2
return sum
}
// 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
// 以值传递方式的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果来看类似引用传递。
// 可变参数
// 定义一个函数,函数的参数为:可变参数 ... 参数的数量可变
// args...int 可以传入任意多个数量的 int 类型的数据 传入 0 个,1 个,,,,n 个
func test (args...int)(){
// 函数内部处理可变参数的时候,将可变参数当做切片来处理
// 遍历可变参数:
for i := 0; i < len(args); i++ {
fmt.Println(args [i])
}
}
// 在 Go 中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
func testvar(num int)(){
fmt.Println("testvar func", num)
}
// 函数作为形参
// 定义一个函数,把另一个函数作为形参:
func test02 (num1 int , num2 float32, testFunc func(int)){
fmt.Println("-----test02")
}
// init 函数:初始化函数,可以用来进行一些初始化的操作
// 每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用。
func init(){
fmt.Println("this is init func")
}
var a = testvar
fmt.Printf("the type is %T\n", a) //打印 a 的类型--> func(int)
a(10) //调用函数
fmt.Printf("the feibonacc result is %d\n", publicfunc.Feibonacc(10))
// chufa(10,0)
ChufaTest(10,0)
错误捕获机制¶
Go
// defer 关键字
// defer 关键字的作用:
// 在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go 的设计者提供 defer 关键字
// 程序在碰到 defer 关键字后不会立即执行后面的语句,而是会把语句推入一个栈中,继续执行之后的代码
// defer 语句执行顺序:按照栈的特点--> 先进后出
// 注:defer 将语句压入栈也会将相关的值同时拷贝入栈中,不会随着函数后面的变化而变化。
// 错误捕获机制 --> defer+recover: defer 加上匿名函数使用
func chufa(a int, b int)(int){
defer func(){
// recover 函数捕获错误--> 没有捕获到错误会返回 nil
var err = recover()
if err!= nil{
fmt.Println("err is ", err)
}
}()
return a/b
}
// 自定义错误
func ChufaTest(a int, b int)(int){
if b==0{
var err = errors.New("除数不能为 0")
// errors.New()自定义错误
fmt.Println("the error is ", err)
return 0
}else{
return a/b
}
}
数组¶
Go
// 数组:
// 实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
// 给出五个学生的成绩:---> 数组存储:
// 1.定义一个数组:
var scores [5] int
// 2.数组的初始化
// 第一种:
var arr1 [3] int = [3] int{3,6,9}
fmt.Println(arr1)
// 第二种:
var arr2 = [3] int{1,4,7}
fmt.Println(arr2)
// 第三种:
var arr3 = [...] int{4,5,6,7}
fmt.Println(arr3)
// 第四种:
var arr4 = [...] int{2: 66,0: 33,1: 99,3: 88}
fmt.Println(arr4)
// 3.数组的赋值与读取
// 将成绩存入数组:(循环 + 终端输入)
for i := 0; i < len(scores); i++ { //i:数组的下标
fmt.Printf("请录入第个%d 学生的成绩", i + 1)
fmt.Scanln(&scores [i])
}
// 展示一下班级的每个学生的成绩:(数组进行遍历)
// 方式 1:普通 for 循环:
for i := 0; i < len(scores); i++ {
fmt.Printf("第%d 个学生的成绩为:%d\n", i+1, scores [i])
}
fmt.Println("-------------------------------")
// 方式 2:for-range 循环
for key, value := range scores {
fmt.Printf("第%d 个学生的成绩为:%d\n", key + 1, value)
}
// 5.Go 中数组属值类型,在默认情况下是值传递,因此会进行值拷贝。
// 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)。
// 二维数组
// 定义二维数组:
var arr [3][3] int = [3][3] int{{1,4,7},{2,5,8},{3,6,9}}
fmt.Println(arr)
fmt.Println("------------------------")
// 方式 1:普通 for 循环:
for i := 0; i < len(arr); i++{
for j := 0; j < len(arr[i]); j++ {
fmt.Print(arr [i][j], "\t")
}
fmt.Println()
}
fmt.Println("------------------------")
// 方式 2:for range 循环:
for key, value := range arr {
for k, v := range value {
fmt.Printf("arr [%v][%v] =%v\t", key, k, v)
}
fmt.Println()
}
切片¶
Go
// 切片--> 和 python 类似
// 定义数组:
var intarr [6] int = [6] int{3,6,9,1,4,7}
// 切片构建在数组之上:
// 定义一个切片名字为 slice,[]动态变化的数组长度不写,int 类型,intarr 是原数组
// [ 1:3 ]切片 —切出的一段片段 —索引:从 1 开始,到 3 结束(不包含 3)—[ 1,3)
// 切片初始化方式
// 1.数组截取
// var slice [] int = intarr [1: 3]
slice := intarr [1: 3]
// 输出数组:
fmt.Println("'intarr:", intarr)
// 输出切片:
fmt.Println("slice:", slice)
// 2.make 函数
// 定义切片:make 函数的三个参数:1 .切片类型 2 .切片长度 3 .切片的容量 slice := make([] int,4,20)
// fmt.Print1n(slice)///[0 0 0 0]
fmt.Println("切片的长度:", len(slice))
fmt.Println("切片的容量:", cap(slice))
slice [0] = 66
slice [1] = 88
fmt.Println(slice)
// 切片的动态增长--> 也可追加切片
fmt.Println(len(slice))
slice2 := append(slice,88,50)
fmt.Println(slice2) //[4 7 3 88 50]
fmt.Println(slice)
// 底层原理:
// 1.底层追加元素的时候对数组进行扩容,老数组扩容为新数组:
// 2.创建一个新数组,将老数组中的 4,7,3 复制到新数组中,在新数组中追加 88,50
// 3.slice2 底层数组的指向 指向的是新数组
// 4.往往我们在使用追加的时候其实想要做的效果给 slice 追加:
slice = append(slice,88,50)
fmt.Println(slice)
// 5.底层的新数组 不能直接维护,需要通过切片间接维护操作。
// 切片的拷贝
// 定义切片:
var a [] int = [] int{1,4,7,3,6,9}
// 再定义一个切片:
var b [] int = make([] int,10)
// 拷贝:
copy(b, a) //将 a 中对应数组中元素内容复制到 b 中对应的数组中
fmt.Println(b)